diff options
author | Branden Robinson <branden@debian.org> | 2006-05-14 22:21:50 -0700 |
---|---|---|
committer | Branden Robinson <branden@debian.org> | 2006-05-14 22:21:50 -0700 |
commit | 19b0c5e0755e213172625e814ef089398abd7de1 (patch) | |
tree | ec96bd66d3cf9fd4b1b37cb92a62c22e2b134e5b |
Import xtrs_4.9c.orig.tar.gz
[dgit import orig xtrs_4.9c.orig.tar.gz]
-rw-r--r-- | ChangeLog | 1129 | ||||
-rw-r--r-- | Makefile | 224 | ||||
-rw-r--r-- | Makefile.local | 110 | ||||
-rw-r--r-- | README | 227 | ||||
-rwxr-xr-x | cassette | 149 | ||||
-rw-r--r-- | cassette.man | 148 | ||||
-rwxr-xr-x | cassette.sh | 127 | ||||
-rw-r--r-- | cassette.txt | 144 | ||||
-rw-r--r-- | cd.ccc | 76 | ||||
-rw-r--r-- | cd.cmd | bin | 0 -> 6109 bytes | |||
-rw-r--r-- | cd6.cmd | bin | 0 -> 6086 bytes | |||
-rw-r--r-- | cmd.c | 77 | ||||
-rw-r--r-- | cmd.h | 24 | ||||
-rw-r--r-- | cmddump.c | 178 | ||||
-rw-r--r-- | cmddump.man | 52 | ||||
-rw-r--r-- | cmddump.txt | 52 | ||||
-rw-r--r-- | compile_rom.c | 140 | ||||
-rw-r--r-- | config.h | 27 | ||||
-rw-r--r-- | cpmutil.dsk | bin | 0 -> 213504 bytes | |||
-rw-r--r-- | cpmutil.html | 224 | ||||
-rw-r--r-- | cpmutil.tgz | bin | 0 -> 23163 bytes | |||
-rw-r--r-- | crc.c | 93 | ||||
-rw-r--r-- | debug.c | 943 | ||||
-rw-r--r-- | dis.c | 2127 | ||||
-rw-r--r-- | do6.jcl | 13 | ||||
-rw-r--r-- | dskspec.html | 590 | ||||
-rw-r--r-- | error.c | 63 | ||||
-rw-r--r-- | expall.bas | 22 | ||||
-rw-r--r-- | export.bas | 33 | ||||
-rw-r--r-- | export.cmd | bin | 0 -> 634 bytes | |||
-rw-r--r-- | export.lst | 480 | ||||
-rw-r--r-- | export.z80 | 408 | ||||
-rw-r--r-- | fakerom.hex | 11 | ||||
-rw-r--r-- | fakerom.lst | 79 | ||||
-rw-r--r-- | fakerom.z80 | 42 | ||||
-rw-r--r-- | hex2cmd.c | 54 | ||||
-rw-r--r-- | hex2cmd.man | 23 | ||||
-rw-r--r-- | hex2cmd.txt | 31 | ||||
-rw-r--r-- | import.bas | 27 | ||||
-rw-r--r-- | import.cmd | bin | 0 -> 620 bytes | |||
-rw-r--r-- | import.lst | 478 | ||||
-rw-r--r-- | import.z80 | 407 | ||||
-rw-r--r-- | load_cmd.c | 331 | ||||
-rw-r--r-- | load_cmd.h | 70 | ||||
-rw-r--r-- | load_hex.c | 93 | ||||
-rw-r--r-- | m1format.fix | 11 | ||||
-rw-r--r-- | main.c | 140 | ||||
-rw-r--r-- | mkdisk.c | 395 | ||||
-rw-r--r-- | mkdisk.man | 254 | ||||
-rw-r--r-- | mkdisk.txt | 255 | ||||
-rw-r--r-- | mount.ccc | 104 | ||||
-rw-r--r-- | mount.cmd | bin | 0 -> 6798 bytes | |||
-rw-r--r-- | mount6.cmd | bin | 0 -> 6775 bytes | |||
-rw-r--r-- | pwd.ccc | 43 | ||||
-rw-r--r-- | pwd.cmd | bin | 0 -> 5559 bytes | |||
-rw-r--r-- | pwd6.cmd | bin | 0 -> 5536 bytes | |||
-rw-r--r-- | reed.h | 47 | ||||
-rw-r--r-- | settime.ccc | 30 | ||||
-rw-r--r-- | settime.cmd | bin | 0 -> 235 bytes | |||
-rw-r--r-- | settime.lst | 224 | ||||
-rw-r--r-- | settime.z80 | 201 | ||||
-rw-r--r-- | trs.h | 173 | ||||
-rw-r--r-- | trs_cassette.c | 1479 | ||||
-rw-r--r-- | trs_chars.c | 2301 | ||||
-rw-r--r-- | trs_disk.c | 3565 | ||||
-rw-r--r-- | trs_disk.h | 214 | ||||
-rw-r--r-- | trs_hard.c | 495 | ||||
-rw-r--r-- | trs_hard.h | 210 | ||||
-rw-r--r-- | trs_imp_exp.c | 682 | ||||
-rw-r--r-- | trs_imp_exp.h | 267 | ||||
-rw-r--r-- | trs_interrupt.c | 501 | ||||
-rw-r--r-- | trs_io.c | 413 | ||||
-rw-r--r-- | trs_iodefs.h | 19 | ||||
-rw-r--r-- | trs_keyboard.c | 1014 | ||||
-rw-r--r-- | trs_memory.c | 535 | ||||
-rw-r--r-- | trs_printer.c | 34 | ||||
-rw-r--r-- | trs_uart.c | 421 | ||||
-rw-r--r-- | trs_uart.h | 89 | ||||
-rw-r--r-- | trs_xinterface.c | 2035 | ||||
-rw-r--r-- | truedam.ccc | 45 | ||||
-rw-r--r-- | truedam.cmd | bin | 0 -> 6137 bytes | |||
-rw-r--r-- | truedam6.cmd | bin | 0 -> 6114 bytes | |||
-rw-r--r-- | umount.ccc | 63 | ||||
-rw-r--r-- | umount.cmd | bin | 0 -> 5970 bytes | |||
-rw-r--r-- | umount6.cmd | bin | 0 -> 5951 bytes | |||
-rw-r--r-- | unix.ccc | 80 | ||||
-rw-r--r-- | unix.cmd | bin | 0 -> 6306 bytes | |||
-rw-r--r-- | unix6.cmd | bin | 0 -> 6279 bytes | |||
-rw-r--r-- | utility.dsk | bin | 0 -> 213504 bytes | |||
-rw-r--r-- | utility.jcl | 38 | ||||
-rw-r--r-- | xtrs.man | 1067 | ||||
-rw-r--r-- | xtrs.txt | 1054 | ||||
-rw-r--r-- | xtrs8.dct | bin | 0 -> 910 bytes | |||
-rw-r--r-- | xtrs8.lst | 547 | ||||
-rw-r--r-- | xtrs8.z80 | 453 | ||||
-rw-r--r-- | xtrsemt.ccc | 496 | ||||
-rw-r--r-- | xtrsemt.h | 73 | ||||
-rw-r--r-- | xtrshard.dct | bin | 0 -> 1425 bytes | |||
-rw-r--r-- | xtrshard.lst | 919 | ||||
-rw-r--r-- | xtrshard.z80 | 800 | ||||
-rw-r--r-- | xtrsmous.cmd | bin | 0 -> 433 bytes | |||
-rw-r--r-- | xtrsmous.lst | 323 | ||||
-rw-r--r-- | xtrsmous.z80 | 256 | ||||
-rw-r--r-- | xtrsrom4p.README | 89 | ||||
-rw-r--r-- | xtrsrom4p.hex | 33 | ||||
-rw-r--r-- | xtrsrom4p.lst | 361 | ||||
-rw-r--r-- | xtrsrom4p.z80 | 287 | ||||
-rw-r--r-- | z80.c | 4370 | ||||
-rw-r--r-- | z80.h | 248 |
109 files changed, 37279 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..4f3a36a --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1129 @@ +4.9c -- Sun May 14 17:54:25 PDT 2006 -- Tim Mann + +* Fixed the new -e flag on import/cmd and export/cmd to actually +work. Oops. Tested on Model I DOSPLUS 3.5. + +4.9b -- Sat May 13 11:54:26 PDT 2006 -- Tim Mann + +* Bundled in the latest version of Roland Gerlach's CP/M utilties for +xtrs and their documentation (as of 5/13/2006). + +* Added -emtsafe feature to turn off ability for emts to write to +unexpected places in the host filesystem. Thanks to Joe Peterson. + +* Added -e flag to import/cmd and export/cmd. Dropped DOSPLUS patches +for those programs, which were outdated anyway. + +* Dropped old method of importing/exporting files using fake I/O +ports and deleted import/bas and export/bas. + +* Small cleanups in load_cmd.c. + +* Renamed Z-80 source files to .z80 instead of .z to prevent Linux +utilities from thinking they're compressed files. + +* Moved key queuing code from trs_interface.c to trs_keyboard.c and +cleaned it up a little. + +* Applied patches from Branden Robinson to add watchpoints and the +zbxinfo command to the debugger and clean up a few things. Thanks! + +* Added some signed/unsigned type casts; thanks to Andreas Jochens. + +4.9a -- Mon Apr 25 22:35:51 PDT 2005 -- Tim Mann + +* Changed the DISKDEBUG options in trs_disk.c to be runtime flag bits +instead of #ifdefs. Added diskdebug command in zbx to set the bits, +with terse documentation in zbx's help command. 2 (FDC commands) is +the most useful. 1 (FDC register I/O) is extremely verbose but +sometimes useful. + +* Added code to export.cmd and import.cmd to ignore the TRSDOS/LDOS 6 +advisory error code when we open a file with the "wrong" LRL. + +* Fixed a typo in the usage message for mkdisk. + +* Increased the maximum accepted track length for DMK to the hard +limit of 0x4000. Previously it had been 0x2940, but cw2dmk is writing +longer tracks than that by default for 8" disks now! Oops. + +* Small cleanup patches from Branden Robinson for cassette.sh. + +4.9 -- Sat May 11 13:42:06 PDT 2002 Tim Mann + +This release has various bug fixes and tiny features that have +accumulated since the last one. + +* Added a hint about floppy drive BIOS settings to the man page. + +* Fixed a bug in the cassette shell script. The cassette image type +wasn't being determined from the file extension due to a shell +programming error. + +* Got rid of #ifdef HRG1B" and just put all the code for that in-line. +Moved documentation to where most of the implementation is. + +* Added -iconic option. Thanks to Jean-Marc Le Peuvedic. + +* Bugfix: check_endian would crash if it failed; it was calling +fatal() before program_name was set. Thanks to Sot Aliadis for the +bug report. + +* Bugfix: the SIGUSR1 feature wasn't turning off the needchange flag +after doing the disk change, so it would do the change over and over. +Thanks to Jean-Marc Le Peuvedic for the bug report. + +* Bugfix: The change that was made in version 4.6 to get the 500bps +loader that came on a 250bps tape to work turned out to break writing +1500bps tapes. The tape-based 500bps loader needed a special +heuristic, because it does a write just after turning on the motor but +before its first read. But the 1500bps writer does a read after all +its writes but before turning off the motor. This was erroneously +activating the heuristic for the loader case, causing us to switch to +read mode without flushing the last transition from the write. The +heuristic is now changed so that it won't activate if more than one +write has been done since the motor was turned on. This makes both +cases work. + +* Changed the sound code to be less picky about sample format on +/dev/dsp. Needed because some hardware doesn't do the 8-bit formats. +We still prefer to use 8-bit mono for game sound and 8-bit stereo for +Orchestra-85/90, but we can also use 16-bit signed little endian for +either, and stereo when we want mono (but not vice versa). + +* Fixed a bug in FDC emulation that was stopping CopyCat from booting. +After starting a read, the CopyCat boot loader reads from the data +register once immediately, before DRQ has come high. This read must +not capture the first byte of data from the sector. + +* Fixed a bug in FDC emulation that was keeping CopyCat from seeing +sectors on a track other than the first. When client software used +the Model III/4/4P feature that lets you put the Z-80 into a wait +state until the FDC raises DRQ, the amount of time waited was not +being added to the T-state counter. Thus repeated Read Address +commands that used the wait state feature would read the same sector +ID over and over instead of rotating to the next one. + +* The change in version 4.2 to wait a few T-states after a floppy Read +or Read Address command before raising DRQ was only implemented for +floppy image files. It's now implemented for real floppies too. + +* The change in version 4.2 to set the track and sector registers on a +Read Address to the track and sector numbers read from the ID field +was implemented only for real floppies. It's now implemented for +floppy image files too. + +* A few minor cleanups to floppy emulation code. + +* Bugfix: When a real Z-80 takes an interrupt or NMI from a halt +instruction, it pushes the address of the next instruction after the +halt as the return address (and later returns there, thus breaking out +of the halt). Previously xtrs was erroneously pushing the address of +the halt instruction instead. If no interrupt or NMI is taken, xtrs +still repeatedly executes the halt instruction; you can see this if +you use zbx to single-step or trace a halt. A real Z-80 supposedly +executes nops internally instead of re-executing the halt, but the +difference is not visible to Z-80 programs running in xtrs. + +4.8 -- Fri Aug 31 11:39:49 PDT 2001 Tim Mann + +* This release has quick fixes for some troublesome bugs. Better +fixes may be forthcoming in a later version. Thanks to Jean-Marc Le +Peuvedic and Frederic Vecoven for problem reports and suggestions. + +* Changed my email address, as I have left Compaq. + +* Bug fix: Output to the X server was not always being flushed in a +timely manner. It is now flushed at the frequency of the timer +interrupt on the machine being emulated (30 to 60 times per second). +This fixes problems with the display not being updated when it should +have been in certain games and operating systems. + +* Added a heuristic to flush the key transition queue if the Z-80 +program does not poll the keyboard often enough. Right now this is +set to go off if more than 2 timer interrupt ticks go by without a +poll. This change should get rid of the unwanted typeahead effect +previously seen in certain situations, such as when a game does some +screen flashing or sound effects between levels without polling the +keyboard. + +* Fixed a compile error on non-Linux platforms. + +4.7 -- Sun Jul 8 16:37:14 PDT 2001 Tim Mann + +* Fixed a silly bug that caused a crash if no -diskdir was specified. +I had inadvertently deleted the initialization. + +4.6 -- Sat Jul 7 13:56:33 PDT 2001 Tim Mann + +* The value of -diskdir can now begin with "~/" (or just be "~") to +make it relative to the user's home directory. Suggested by Jean-Marc +Le Peuvedic. + +* Fixed the heuristic that decides whether the TRS-80 is loading or +saving to tape to work with the 500bps loader that came on a 250bps +cassette with the tape Editor/Assembler. This code does an OUT to the +cassette port first thing after turning on the motor, which was +confusing us. Now if the TRS-80 does an IN from the cassette port +with the motor on, we switch to reading even if we initially saw an +OUT and guessed it was writing. + +* Added feature: sending SIGUSR1 to xtrs causes a disk change (as if +F7 were pressed). This is for use by external disk-manipulating +programs. Suggested by Jean-Marc Le Peuvedic. + +* Changed to a new keystretch algorithm that stretches for a fixed +number of T-states instead of a fixed number of keyboard matrix reads. +This should hopefully eliminate the need to tweak the keystretch +setting to different values for games. Inspired by suggestions from +Jean-Marc Le Peuvedic. To make this work, also had to change the +KBWAIT algorithm to wait only after the ROM wait-for-key routine has +been called several times with no keyboard state change. + +* I noticed that the X server on my development machine is now +reporting Shift+F1 as F13 instead of F11, so I changed the table in +trs_keyboard.c to match, but I left an #if setting that can +accommodate the old behavior. It looks like newer versions of XFree86 +don't do either translation, thankfully. + +4.5a -- Fri May 4 23:17:46 PDT 2001 Tim Mann + +* Corrected some typos in xtrs.man and Makefile. Thanks to Branden +Robinson. + +4.5 -- Wed May 2 20:48:10 PDT 2001 Tim Mann + +* All undocumented instructions and almost all undocumented flag +settings from http://www.msxnet.org/tech/Z80/z80undoc.txt are now +emulated. + +* Added code to parse resources from $HOME/.Xdefaults, $HOME/Xtrs, and +/usr/X11/lib/X11/app-defaults/Xtrs. Normally Xt would handle this, +but xtrs doesn't use Xt. Branden Robinson noted the absence of this +feature back in 1998; it's finally fixed. + +* Added -shiftbracket command line option, inspired by mail from +Jean-Marc Le Peuvedic. Defaults to true on Model 4/4P. + +* Added man pages for hex2cmd and cmddump; thanks to Branden Robinson. + +* Raised JV1/JV3 cylinder limit from 96 to 255 after someone sent me a +JV1 with 143 cylinders. I wonder which DOS that was made with! + +* Fixed syntax errors in debug.c that gcc had been allowing through. + +* Corrected some man page errors and unclear passages. + +4.4 -- Fri Feb 16 23:52:32 PST 2001 Tim Mann + +* Added some DOSPLUS patches for import/cmd and export/cmd from +Knut Roll-Lund. + +* Removed kludge from MSM5832 clock chip emulation; now it provides a +plain 2-digit year (i.e., "01" for 2001) as the real hardware did. + +* Cleaned up a bit and eliminated C compiler warnings. + +* Added break command to debugger as a synonym for "stop at". + +* Modified the debugger's reset and run commands, and added softreset. +Now reset does a powerup initialization of the Z-80 and all devices, +while softreset just pushes the TRS-80's reset button. The run +command does a hard reset and starts execution at address 0000. On +the Model I and III, softreset resets external devices and gives the +Z-80 a nonmaskable interrupt, but on the Model 4 and 4P, it is the +same as reset. + +* Added emulation of the Alpha Products joystick, mapped at port 0 +only. For the moment it doesn't work with a real joystick; instead, +it uses the numeric keypad keys with NumLock off. Keys 2, 4, 6, 8 +(KP_Down, KP_Left, KP_Right, KP_Up) are the main directions; keys 1, +3, 7, and 9 (KP_End, KP_Page_Down, KP_Home, KP_Page_Up) work as +diagonal directions by activating two main directions at once; and key +0 (KP_Insert) or 5 (KP_Begin) is the fire button. At some point I may +acquire a real PC joystick, read about the X input extension, and make +that work too. + +4.3 -- Fri Jul 21 20:44:48 PDT 2000 Tim Mann + +* Added emulation of the serial port for all models. Note: Model I +LDOS 5.3.1 has a bug in the RS232 driver (RS232R/DVR). You must set +the break parameter to a nonzero value for it to work. Try "SET *CL +RS232R (BREAK=1)", for example. + +* Added emulation of the Radio Shack graphics card for Model III. +This is just like the Model 4 card, except that there is no overlay +mode, there are no scrolling registers, and the graphics are always +640x240 (never 512x192). Changed default graphics card from +-microlabs to -nomicrolabs. Changed the -resize flag to be meaningful +in Model III mode, and changed the default to be off for Model III +mode, but still on for Model 4/4P mode. + +* Added emulation of the Micro Labs graphics card for Model III. This +is quite different from the Model 4 card. I deduced how it works by +looking at what the software for it was trying to do, so there could +be bugs in the emulation. + +* Enable the hard disk controller at powerup. NEWDOS is said to need this. + +* Minor changes to compile on Solaris. Thanks to Frederic Vecoven. + +* Bugfix: The MSM5832 clock chip returns a leap year indication in +bit 2 of port base+8. + +* Bugfix: When running an emulated model 4/4P in model 3 mode, characters +were being drawn only 10 scaled pixels high instead of 12. + +* Added a unified debug() routine to print all debugging messages. + +* Patch to fix zbx listing up to 0xffff, from Ulrich Mueller. + +* Patch to correct disassembly of DD and FD prefixes that have no effect, +from Ulrich Mueller. + +* Patches to remove unneeded cast and fix indentation, from Ulrich Mueller. + +4.2 -- Sat Jun 24 01:05:13 PDT 2000 Tim Mann + +* Emulation of the Model I HRG1B 384*192 graphics card, sold in +Germany for Model I and Video Genie by RB-Elektronik. Contributed by +Ulrich Mueller. + +* Fixed a bug that would crash xtrs if the Z-80 SP pointed into +keyboard memory. Contributed by Ulrich Mueller. + +* The -scale command line option now allows independent scaling in the +X and Y dimensions. The scaling mechanism is also used to eliminate +the need for the bitmaps in trs_chars.c to be double-height, and to +eliminate the need for the separate double-width bitmaps formerly in +trs_wide_chars.c. The German characters in trs_chars.c were corrected +to make this work. Contributed by Ulrich Mueller. + +* Fixed an obscure bug in the heuristic to identify whether a disk +image is JV1, JV3, or DMK. + +* In trs_disk.c, reading a sector now waits a while before raising +DRQ. The old behavior broke the boot loader for self-booting Zork. + +* In trs_disk.c, Read Address now sets the track register to the track +number read and the sector register to the sector number read. The +1771 data sheet says that Read Address sets the sector register to the +sector number, but nothing about the track register. The 179x data +sheet says that Read Address sets the sector register to the track +number, and nothing about the track register, which seems wrong. "The +Institute" game loader assumes that Read Address will set the track +register to the track number on a 1771. + +* Minor accuracy tweaks to trs_hard.[ch]. + +4.1 -- Fri May 19 00:40:29 PDT 2000 Tim Mann + +* Fixed a Y2K problem in settime/cmd (as assembled from settime.z). +For some random reason, Model I/III LDOS 5.3.1 stores a 2-digit year, +so 2000 => 0, but Model 4 LS-DOS 6.3.1 stores an offset from 1900, +so 2000 => 100. Previously we were getting it right for 5.3.1 but +wrong for 6.3.1. + +* Fixed a bug in the floppy drive motor timeout interrupt. It was not +being cancelled immediately when a drive was reselected. This +sometimes made the floppy driver in the Montezuma CP/M boot sector +fail, and could have caused problems in other software too. + +* Now only Models I/III/4, not Model 4P, are emulated as having no +floppy controller when they have no disks mounted. A Model 4P always +has an FDC, and it can boot off the hard drive with suitable software +(such as Montezuma CP/M), so it is possible to boot an operating +system with no floppies mounted, then mount and access some later. +This isn't possible on the other models. + +* Bugfix: If a JV3 or DMK floppy was not writable, but did not have +its internal write-protected flag set, we were not emulating a write +protect tab as intended. + +* F9 will now get you into the debugger (zbx) even if you didn't start +xtrs with the -debug flag. Also, starting with -debug no longer +causes the emulator to run more slowly; it runs at full speed as long +as no traps are set. + +* Formerly, executing a halt instruction caused entry into the +debugger (zbx) if xtrs had been started with the -debug flag. This +feature has been removed. As a replacement, executing the new +emt_debug emulator trap (ed2f) always causes the program to enter the +debugger. + +* Fixed the debugger reset and run commands to reset the whole +machine, not just the processor. + +* Installed a small patch that makes the emulator control its window +size in the correct way, by passing a size hint to the window manager +instead of noticing when its size has changed and changing it back. +Thanks to Joseph Wang. + +* Added emulation of real Radio Shack hard drive controllers (WD1010). + +* Changed xtrshard/dct to set the directory cylinder field in the Reed +header automagically. (Heuristic: when a byte is written to cylinder +0, sector 0, byte 2, assume that's the right value.) The WD1010 +emulation does this too. There is now no need for mkdisk to have a +flag to set this field, or for the user to remember to use the right +value with FORMAT, so that stuff is deleted from the man pages. + +* Deleted references to cassette "autodetect" format, since I'm not +going to get around to implementing it. The current autodetection +in the cassette shell script based on file extension works well +enough. Rephrased the caveats about reading and writing real +cassettes, since I just tried it again and got fairly good results +this time. + +4.0 -- Sat Apr 1 20:52:53 PST 2000 Tim Mann + +* Fixed bugs in switching between 80x24 and 64x16 screen sizes in +-usefont mode. Thanks to Al Petrofsky for noticing back in 1998 that +the code looked wrong; I finally got around to cleaning it up for 4.0 +and found the bugs. + +* Worked around an X problem. If you start xtrs with the -resize flag +and a Model 4 operating system is already mounted, the window gets +resized very soon after being created. This was triggering some +sort of X race condition that would make the window get confused about +how big it was. + +* Fixed obscure bugs in the Model 4 hi-res graphics card emulation. + +* Added -scale option. Thanks to Denis Leconte for some of the code. + +* Emulate software-switchable 3.54Mhz clock speedup kit on Model I. +Useful with Orchestra-85 emulation. + +* Changed -samplerate default to 44,100 for better sound. + +* Added emulation of Orchestra-85 and Orchestra-90. Thanks to David +Keil for some information on how Orchestra-90 works. + +* Fixed a bug that sometimes caused the F10 reboot key to hang the +emulator until the next X event. + +* Added emulation of TimeDate80, TChron1, TRSWatch, and T-Timer, based +on code from Joe Peterson and information in LDOS Quarterly 1-6. I'm +not sure it's completely right; see the comments in trs_io.c. If you +have software that uses these devices, let me know how well it works, +especially with regard to the Y2K issue. + +* Added support for Level 1 Basic .cas files. + +* Worked around a problem that kept Model I Level 2 Basic from being +able to read .cas files. When Basic records a 500bps cassette, it +puts an extra delay after the initial 0xA5 sync byte. This delay is +required when reading a cassette because Basic executes quite a bit of +code right after reading the sync byte. As a kludge, I put in an extra +delay whenever a 0xA5 is read from a 500bps cassette; ugly, but it +works. + +* Updated documentation for EXPORT.COM in xtrs.man; this change should +have been in 3.9. + +3.9a -- Sun Nov 28 14:17:49 PST 1999 Tim Mann + +* Fixed a bug in the GENIE support and added one more GENIE feature: +printer interface at port 0xFD. + +3.9 -- Fri Nov 26 11:07:24 PST 1999 Tim Mann + +* Fixed a problem in the sound support which (for reasons I don't +fully understand) was making xtrs hang when playing sound on Linux +kernel 2.2.12. Thanks to Roland Gerlach for the bug report. Version +3.8 of xtrs worked on kernel 2.2.5, but not 2.2.12. Also fixed a bug +where too many samples could be generated if the Z-80 program +repeatedly wrote the same value to the sound/cassette port; this makes +the CP/M bell sound much better, and also seems to fix the remaining +problem with the Talking Adventure 1 title screen. + +* Corrected a small error in emulating Model I floppy disk controller +interrupts (a feature that's unused by any known software anyway!). + +* Installed a pile of changes from Branden Robinson, which he says are +"mostly to satisfy the vagaries of Debian's packaging policy." They +are as follows: + + * main.c: canonicalize program_name here, not in trs_xinterface.c, so + that error messages are consistent + * Makefile: + - clean rule deletes compile_rom binary, and keeps deroffed manpages + - veryclean rule deletes deroffed manpages + - use Debian-canonical file modes when installing + - install cmddump, hex2cmd, and mkdisk + * Makefile.local: + - compile-in expected paths for ROM files + - build using readline and ncurses (for zbx internal debugger) + - Debian-canonical flags to cc + - LDFLAGS and IFLAGS that make sense with our X directories + - create a PREFIX variable that gets prepended to BINDIR and MANDIR + * trs_xinterface.c: + - remove canonicalization of program_name, this is now in main.c + - change logic for finding romfiles; check command line options, X + resources, compiled-in path to rom files, then finally fall back on a + built-in ROM image + * cassette.sh: rewrote csh cassette script in Bourne + +I undid or modified a few of Branden's changes: I left the old +cassette script as the default since I'm not confident that every Unix +system has a new enough version of /bin/sh to support cassette.sh. I +left -Wall out of the C flags, since I see no reason to worry users +with the small number of harmless warnings I've left in the code. I +put the default installation locations back to places that are +appropriate for add-on code in most non-Debian systems: /usr/local/bin +and /usr/local/man. + +* Merged in support for the GENIE, a German Model I clone, from Jenz +Guenther. I simplified it a bit along the way; I hope I haven't left +out anything essential. + +* Fetched updated cpmutil.dsk from Roland Gerlach's Web page, +http://www.rkga.com.au/~roland/trs-80/cpm-xtrs/. + +3.8 -- Sat Jul 17 19:25:57 PDT 1999 Tim Mann + +* Made several tweaks to the sound support. The title screen of +"Talking Adventure 1: Forbidden Planet" for Model III now works +somewhat better, at least at -samplerate 44100, but there still seems +to be a problem. Also, it still looks like the speed boost when a +sound begins (trs_suspend_delay) may be making games run too fast when +a sound plays. + +* Lost Data is now set if a read or write is not completed in 500ms. +This is not an exact emulation, but is enough to make DOSPLUS 3.3 and +Floppy Doctor work. + +* Seeking and stepping slowed down a little to make Floppy Doctor +notice that the Busy bit does get set. + +* Head Engaged bit emulated correctly now. Was needed for Talking +Adventure loader (!). + +* DMK writing emulation more accurate, handles case of writing to a +sector that was formatted with no DAM. Thanks to David Keil. + +* zbx "dump" command prints T-state counter and delay value. +"diskdump" prints T-states to next motor timeout, not T-state counter +value at timeout. + +3.7 -- Sat Jul 3 15:57:04 PDT 1999 Tim Mann + +* Completely rewrote the cassette support. Instead of trapping the +Model I ROM routines, it now observes I/O requests to the cassette +port and uses xtrs's T-state counter to measure (or generate) the +correct signal timing. Real cassettes can even be read/written using +the OSS sound driver or .wav files, though this could use some work. + +* Completely rewrote the sound support to use the OSS sound driver. +Most code is shared with the cassette support. The old code is still +there too, but #if'ed out. + +* Modified import/cmd and export/cmd to work on Model III TRSDOS 1.3. +Tested and verified that setdate/cmd works there too. + +* Recompiled cd/ccc, mount/ccc, pwd/ccc, truedam/ccc, umount/ccc, and +unix/ccc with the LDOS I/O redirection option turned off. This makes +them work on Model I/III TRSDOS and NEWDOS too. + +* Added -l (force lowercase) to cd/cmd, mount/cmd, and unix/cmd. +Useful with Model I/III TRSDOS and NEWDOS. + +* Fixed a bug in printing exit status from unix/cmd. + +* Added -p/-u flags to mkdisk for turning write protection on/off. + +* Added display of IFF1, IFF2, IM, and undocumented F register bits to +the zbx dump command. + +* Fixed a 1-byte memory smash in emt_opendisk. + +* Fixed a bug that could crash xtrs when accessing an empty real +floppy drive. + +* Made error() and fatal() more capable and cleaned up some stray +places where fprintf(stderr, ...) was being used instead. + +3.6 -- Sun Jun 20 16:13:05 PDT 1999 Tim Mann + +* Implemented the DMK emulated floppy format. This format supports +essentially everything the original hardware could do, including +"protected" disk formats, non-IBM sectors on the Model I, partially +reformatting a track to achieve mixed density, Read Track, and more. +It is compatible with David Keil's Model 4 emulator; thanks to David +for designing and documenting it. + +* Added support for the Motor Off NMI on Model III/4. + +* Added emulated CRCs on Read Address. + +* Added diskdump command to zbx; dumps the state of the emulated +floppy disk controller and drives. + +3.5 -- Sun May 16 21:55:38 PDT 1999 Tim Mann + +* Changed physical disk handling to report the correct WD17xx error +bits upon reading (or writing) a sector with a CRC error in the ID +field. In this case both CRC and RNF bits should be set. Fixing this +lets us boot physical Scott Adams Adventure disks that use the Kim +Watt loader. Unfortunately, the WD17xx behavior with "read address" +on such a sector cannot be emulated, because a 765-style FDC's "read +id" command ignores IDs with CRC errors rather than returning an error +when it sees one. Also, of course, .DSK format has no way to +represent a sector with a CRC error in the ID field. + +* Always use a recalibrate if seeking a real disk to track 0. This +should help us recover from confusion about what track the disk is +really on. Such confusion should not arise unless two copies of xtrs +are both seeking the disk, or the disk is mapped twice to the same +xtrs and both virtual drives seek -- but there are sometimes reasons +for doing that. + +* Updated xtrsrom4p.README file with material inadvertently left out +in 3.4 release. + +3.4 -- Sun Apr 4 22:27:46 PDT 1999 Tim Mann + +* Corrected the Model 4 reset switch to send RESET instead of NMI. +Thanks to Lamar Owen for pointing out the error. + +* Corrected the default charset for Model III to be katakana. Thanks +to Gary Shanafelt and others for pointing out the error. + +* Fixed minor bugs in cassette support. F10 (Reset button) now gets +you out of cloading something that isn't there, even if you aren't +using the SIGIO support. Also, autodelay measurement is turned off +while cloading. + +* Added a free minimal Model 4P ROM, written by Pete Cervasio (with a +few hacks of my own). Thanks, Pete! The free ROM can boot only Model +4 mode operating systems; it doesn't know how to load MODELA/III. + +3.3 -- Sat Jan 23 15:42:36 PST 1999 Tim Mann + +* Added an emt_misc code to query/set the -truedam flag, and added a +Misosys C program to invoke it. Made -truedam more rigorous for real +floppies, and clarified (I hope) DAM documentation in the man page. + +* Much improved behavior when accessing a real floppy drive with no +disk in it. Previously this would generate a raft of error messages +to stderr, subsequent accessses to the drive would not work until the +user pressed F7 to reset the error condition, and the TRS-80 program +would always see the problem as a sector not found or a write fault +instead of an empty drive. Now we check whether a disk is present +whenever the drive is selected, unless we have already checked in the +last T seconds (including the implicit check that occurs when you read +or write to the drive). If the drive is empty, the TRS-80 program +sees this correctly, there are no stderr messages, and F7 is not +needed. F7 will still force a check before the timeout T. T is +currently set to 3 seconds. + +* Drive motor timeout is now emulated. Formerly drive motors were +emulated as never timing out; now they time out after two seconds +(measured in T-states). This is useful for a few programs that fail +to handle error cases properly unless the motor eventually times out +and causes Not Ready to be asserted. Model I SuperUtility has been +observed to do this, at least in some unusual cases. Minor bug: the +T-state counter doesn't run during HALT instructions or when the +emulator has detected that the TRS-80 program is blocked waiting for +keyboard input (but it does run while the TRS-80 program is handling +timer interrupts out of those states). + +* Fixed bugs in maintaining the track position on real floppies. Part +of the fix causes a restore (recalibrate) on each real floppy drive +when xtrs is first started, and when an emulated drive is changed from +a .dsk file to a real drive and F7 is pressed. The other part avoids +forgetting the head positions when F7 is pressed. + +* Detect attempts to format a track with a sector ID that is not +followed by any sector data (i.e., no data address mark). We can't +represent this on either a .dsk file or a real floppy, so we print an +error message. In the JV3 .dsk file case, we record it as an +intentional CRC error instead, even though that is not the same thing. + +* Made -truedam affect reading real floppies. Without -truedam, +single density F8 is translated to FA upon reading; with it, F8 reads +as F8. On writing to real floppies, it is still the case that all +non-FB DAMs are written as F8, but with -truedam you now get an +non-fatal error message for F9 and FA. + +* Changed delayed event scheduler to work in terms of T-states, not +instructions. + +* Used delayed events to emulate semi-correct timing for the Read +Address command on emulated floppies. Unfortunately, there were so +many problems making this work with real floppies that I had to give +up. The timing emulation does make the HyperZap "A" command produce +reasonable-looking results on emulated floppies, for what that's worth. + +* Tweaked the FDC emulation so that the method for formatting mixed +density tracks that Model III and 4 Super Utility uses will work on +emulated floppies (but not real floppies) -- they toggle the MFM input +to the FDC during formatting. I had no idea that would work on real +hardware, but evidently it does. The method that Model I SU and +HyperZap use still does not work -- they format the track first in one +density with a very large gap at the beginning, then reformat over the +gap in the other density, aborting the format in the middle using the +Force Interrupt command to avoid overwriting the first set of sectors. +It's hard to see how to do this reliably with a .DSK file, which +carries no information about gaps; how do you decide which sectors (if +any) from the old track should remain valid? It also would be a pain +to implement. Neither method works on real floppies; it does not seem +feasible to implement mixed density formatting on a PC-style FDC, +especially not working at arm's length through the Linux driver. + +* Fixed a few small problems with FDC emulation that were revealed by +HyperZap. I still can't really say that xtrs supports HyperZap well +enough to be useful. + +3.2 -- Sat Jan 2 20:43:42 PST 1999 Tim Mann + +* Reworked JV3 DAM support to be more accurate by default, or fully +accurate with the new -truedam flag. + +3.1 -- Tue Dec 15 19:37:30 PST 1998 Tim Mann + +* Added cd/cmd, pwd/cmd, unix/cmd, mount/cmd, and umount/cmd. They +are compiled with Misosys C and therefore probably work only on LDOS +and LS-DOS. (They definitely don't work on Model I TRSDOS 2.3 or +Model I Newdos/80.) + +* Changed import/cmd and export/cmd to default the destination +filename. + +* Added emt_getddir and emt_setddir. Changed MC names of some emts in +xtrsemt.h because MC's linker requires names to be unique in the first +seven characters. Fixed several bugs in xtrsemt.h and xtrsemt.ccc. + +3.0 -- Sat Dec 12 11:19:58 PST 1998 Tim Mann + +* Fixed a bug in the retry loop that's used to read real floppies with +sector sizes other than 256. It would screw up if the sector could +not be read at all. + +* Fixed a bug in reflecting CRC errors on real floppies to the +emulated controller. Formerly this was done in an incorrect way that +would typically hang the floppy driver in the Z-80 DOS. + +* Minor portability changes. + +2.9 -- Fri Nov 6 23:14:20 PST 1998 Tim Mann + +* Fixed a nasty bug in formatting emulated floppies with sector sizes +other than 256 bytes. Sector data would be written to the wrong +offset in the file during the format and during any subsequent writes, +until the next F7 disk change or the next time the emulator was run. + +* New version of cpmutil.dsk from Roland Gerlach with a bug fix. +IMPORT.COM would crash if the CP/M filename you specified already +existed and you gave a different name in response to the error +message. + +2.8 -- Sat Oct 31 15:00:52 PST 1998 Tim Mann + +* The default XFree86 keymap reports the shifted F1-F10 keys as +keysyms F11-F20, so I changed xtrs's mapping from keysym to TRS-80 key +to compensate. Shift+F6 now works as TRS-80 Shift+0, as documented. +The remapping entailed some small user-visible changes to seldom used +keys: (1) F11-F14 no longer have distinct functions of their own. (2) +Home is no longer a synonym for Clear; instead it takes over the +obscure duty of letting you activate the Model III/4 RightShift +position even if you're in Model I mode. + +* Replaced the incorrect DAA implementation with a correct one, fixed +a few obscure bugs in documented Z-80 flag settings, and implemented +all the undocumented flag settings. I used a test program included +with the yaze CP/M emulator to check for errors, and I read the yaze +code for guidance on fixing them. Thanks to the author of yaze for +checking his implementation so carefully and making his code available +to others under the GPL. + +* Bug fix: due to a race condition between the main thread and the +SIGALRM handler, autodelay could cause xtrs to hang when the delay +changed from nonzero to zero. + +* Bug fix: trying to do a Write Track on an emulated drive with no +disk in it could crash xtrs. Some other operations also failed to +check for an empty drive, but with less dire consequences. + +* We now compute a reasonable gap3 size on real floppies instead of +using a small constant value (0x0c). The small gap3 seemed to cause +problems at times. The formulas might still need tweaking. + +* Added code to measure the gap sizes that Z-80 software is trying to +format. These are not actually used for anything, but can be printed +if debugging is turned on. It's not possible to feed them directly to +a PC FDC due to interface differences. + +* Formatting a real floppy now tries to use the fill data byte given +by the Z-80 program. Since the WD17xx let you fill with (almost) any +data, but PC FDCs let you choose only one byte, we can't fully emulate +this. We just pick the last fill byte given in the last sector and use +that throughout. This is needed for CP/M, which looks for its E5 fill +byte to find free directory space! + +* Changed the real floppy sector-not-found error handling heuristic +for reads to try each possible sector size once internally before +returning an error to the Z-80 program. Of necessity, however, a +sector-not-found error on write is still returned immediately to the +Z-80 program; on the retry with a different sector size, we will +need to run around the Z-80 program's loop more or fewer times to pick +up the number of bytes we are trying with. + +* Fixed a bug in reading/writing 1024-byte sectors on real floppies: +the error handling heuristic was not trying that size. + +* Minor bug fix: executing a HALT instruction would pause the emulator +even if a disk interrupt was scheduled to happen a few cycles +later. We now check the event schedule before pausing. + +* Fixed a bug in doing inverse video with -usefont. + +* Changed the default font for -usefont to have fewer *'s, to eliminate +problems where the wide version didn't scale the intended font. + +2.7 -- Sat Oct 24 16:21:17 PDT 1998 Tim Mann + +* Fixed a bug that would have prevented the Z-80 emulation from working +on machines where char is unsigned by default. + +* Implemented the alternate characters for Model III/4, corrected all the +character generator fonts and added several options. Decommissioned the +.bdf fonts; they are inaccurate and (for unknown reasons) they don't work +with the font scaler in XFree86. Decommissioned the -trsfont feature; +now graphics blocks are always generated at runtime, not taken from the +font. Thanks to Todd Cromwell III for supplying real Model III character +generator ROM data obtained using a ROM reader. + +* Added a fake boot ROM to use as a default. It just prints a message that +you don't have a ROM installed for this TRS-80 model. Maybe in the future +it could be beefed up to do more: the Model 4 (when not in Model III mode) +and the Model 4P don't require a great deal from the ROM. + +* Updated xtrsemt.h and xtrsemt.ccc. + +* Added a bunch more functions to emt_misc. + +* Cleaned up code to remove most warnings. + +* Added -sizemap and -stepmap. + +* Added signal blocking around calls to readline in debug.c, allowing it +to work again. See Makefile for details. + +2.6 -- Wed Oct 14 23:05:01 PDT 1998 Tim Mann + +* Sector sizes other than 256 are now supported! 128, 256, 512, and +1024 all work, on both JV3 floppy images and real floppies. This +feature uses a backward compatible extension to the JV3 format; xtrs +JV3 floppy images that use only 256-byte sectors will still work with +other emulators. I could find very little TRS-80 software to test this +feature with. Super Utility crashes when reading sectors larger than +256 bytes. Trakcess works well, though. + +* Fixed a bug in delivering FDC completion INTRQs. Previously an +interrupt could be lost if Z-80 software read the status register just +at the moment the interrupt was to be delivered. + +* Fixed bugs in the FDC "read address" command on real floppy drives. + +* Added the -doubler option, and added a more accurate emulation of +having two separate FDC chips in Model I mode. Super Utility now +works properly with double density if -doubler is set to either +"percom" or "radioshack" (not the default value "both"). + +* Bug fix: The floppy controller's BUSY bit was not set while the FDC +completion INTRQ was being delayed. That was the real reason why +increasing the delay caused problems (as noted under 2.5 below). + +2.5 -- Mon Oct 12 14:59:14 PDT 1998 Tim Mann + +* Fixed a bug in graphics-only mode. When there is no text overlay, +graphics should always be 640x240. Graphics should be 512x192 only +when 64x16 text is actually overlayed. Thanks to Mark McDougall for +the bug report. + +* In the fix for CP/M described under 2.3 below, I increased the delay +for disk INTRQ in too many cases. The increased delay for seek +commands was causing Model III NEWDOS/80 to fail to boot. For now, I +reduced the delay on seek (and related commands) back to the pre-2.3 +level, while leaving the increased delay for reads on a blank track +(and related commands) in place. Thanks to Mark McDougall for the bug +report. + +2.4 -- Sun Oct 11 08:14:36 PDT 1998 Tim Mann + +* Oops. The fixes for Montezuma CP/M mentioned below were inadvertently +left out. They are included in this version. + +2.3 -- Sun Oct 11 01:05:17 PDT 1998 Tim Mann + +* Added import and export utilities for Montezuma CP/M, written by Roland +Gerlach. Thanks! + +* Fixed bugs in 512x192 graphics mode, reported by Mark McDougall. + +* Fixed a problem that prevented Montezuma CP/M from formatting +diskettes. The number of instructions of delay between trying to read +a sector on a blank track and getting a disk INTRQ interrupt was too +short. Also, reading the disk status register would cancel a pending +interrupt even if it was still being delayed (i.e., had not yet +actually happened). + +* Fixed bug in Model 4 @ICNFG chaining in xtrshard/dct. If you have +the old version of xtrshard sysgened, you should remove your +config.sys and re-sysgen with the new version. The bug could prevent +other sysgened drivers from initializing at boot time, and could even +cause crashes while booting due to an address being executed as +instructions. + +* Fixed Expose event support to do fewer redraws. This reduces the +problem of xtrs hanging for a long time (doing repeated redraws) after +the window is moved, which shows up on window managers that display +the coordinates on top of the window while it's moving. + +* Fixed bugs in -borderwidth, and changed default to 2. + +* Added emulation of the Radio Shack hi-res graphics card, including +undocumented ports. This card is similar to the Micro Labs card but +not identical. + +* Added mouse support and a mouse driver. + +2.2 -- Sun Sep 27 19:59:00 PDT 1998 Tim Mann + +* Added hints on configuring for games to the man page. + +* Improved the -autodelay algorithm. It now finds the correct speed +much faster. + +2.1 -- Sat Sep 26 14:06:49 PDT 1998 Tim Mann + +* Added emulation of the Micro Labs Grafyx Solution Model 4 hi-res +graphics card. Thanks to Mark McDougall for typing up a summary of the +documentation for me! I checked the behavior on doubtful points +against Matthew Reed's emulator -- thanks to Matthew for pioneering +here. + +* Added -autodelay. This dynamically adjusts the -delay setting to try +to make programs run at about the speed they would have on a real +machine. It works by monitoring how many T-states are executed during +each heartbeat clock period and adjusting the delay value up or down +by one on each tick. It doesn't track very precisely, but is good +enough to make realtime games a lot more playable. + +* Deleted the -spinfast option, which is no longer needed. + +* Disk index holes are now emulated in virtual time, locked to the +rate at which T-states would be executed by a real TRS-80. Thus +programs that measure the disk rotational speed with timing loops will +get the answers they expect. In particular, this will correct problems +with the FORMAT programs in TRSDOS 2.3, VTOS 3.0, and at least some +NEWDOS versions. + +* Added a T-state counter. + +* Keyboard repeat is automatically turned off when focus enters the +window and restored to the previous state when it leaves. Thanks to +Marcelo Fernandes Vianna for the idea and an initial version of the +code. + +* Partial support for 16-byte non-IBM sectors in the 1771 FDC +emulation, just enough to support VTOS 3.0's copy protection +scheme. Bit position 0x04 in the JV3 sector flags field is now used to +indicate a non-IBM sector. Thanks to D. J. Dubay for inspiration. + +* Parameters to the keystretch algorithm can now be controlled from +the command line. Tweaking them might be of help in getting games or +other software that polls the keyboard in unusual ways to work well. + +* Bugfix: in Model 4 mode, we could try to access the SoundBlaster +hardware even when the -sb switch was not given, typically resulting +in a crash. Thanks to Roland Gerlach for the bug report. + +2.0 -- Sun May 24 18:14:52 PDT 1998 Tim Mann + +* Added -delay flag, a crude speed control. + +* Added Model 4P emulation. Please don't pirate Model 4P ROMs. Frank +Durda IV has expressed interest in creating a freely redistributable, +simplified 4P ROM for use in emulators, but it is not yet available. + +* Added a somewhat more general mechanism for scheduling events to happen +several instructions in the future. This supports software that sometimes +starts an I/O operation that will trigger a completion interrupt in the +near future, but relies on the fact that the real I/O device could not +interrupt until the CPU had had time to execute a few more instructions. +Previously I had a more kludgey case-by-case solution for this problem, +but that method didn't work for the Model 4P ROM. + +* The cmddump program (a TRS-80 DOS load module analyzer) is now included +in the release, but there is no man page. See the top of cmddump.c for +brief documentation. + +* Added ability to use a TRS-80 load module file directly as the ROM. +In particular, the MODELA/III file is in this format. + +* mkdisk now puts the original filename in the header of a .hdv file. + +* Added missing code to initialize the SoundBlaster DSP and turn on the +DAC speaker. This was required to make sound work properly on my machine, +which has the ESS1688 AudioDrive sound chip, a SoundBlaster clone. It's +probably needed for older true SoundBlaster models too. + +* Changed sound resource to have same name ("sb") as command line option. + +* Moved sound init code to fix bug I introduced in 1.10. + +* Older Linux kernels don't have <sys/io.h>; 2.0.29 does not, 2.0.32 does. +Added comment saying to delete the include if it causes an error. + +* Fixed bugs in assembly version of settime: it did not work on an +emulated Model III/4/4P in Model III mode using MODELA/III as its ROM, and +it caused JCLs to abort on Model 4 LDOS. + +* Fixed bug in emulating the SLA instruction: the Z flag was not getting +set when 0x80 was shifted left to yield zero. The bug was affecting the +MultiDOS 4 keyboard driver, causing problems with key repeat. + +1.10 -- Thu Apr 16 19:20:30 PDT 1998 Tim Mann + +* Fixed bug in setting data rate for real floppy drives that spin at 300 +RPM (3.5-inch and 360KB 5-inch). This bug caused disks written by xtrs in +such drives to be unreadable on a real TRS-80 (and vice versa). Disks +written by old xtrs versions in 1.2MB 5-inch drives (which spin at 360 +RPM) are OK. + +* Finished implementing emulation of 8-inch floppy drives. Added +xtrs8/dct, an LDOS/LS-DOS driver for the emulated 8-inch floppy hardware. +Corrected index hole emulation for 8-inch drives to run at 360 RPM. Added +support for real 8-inch drives (untested) and emulation of 8-inch drives +by real 5.25-inch and 3.5-inch drives (tested and working). + +* Added emt_system. Updated xtrsemt.ccc. + +* Added sound emulation based on code from Fabio Ferrari. Linux with +SoundBlaster only. + +* Fixed bug in returning error (or success) code from emt_strerror. +Reported by Roland Gerlach. + +* Give an error message and don't crash if diskM-U is linked to a floppy +device that we don't have read/write permission for. + +* Undocumented equivalents for documented instructions are now commented +in disassemblies. + +* import.z and export.z modified to also work with Newdos/80. settime.ccc +rewritten in assembly language and made to also work on Newdos/80. Thanks +to Ulrich Mueller for this code! + +* Corrected disassembly of ld a,r. Added borderwidth resource. Minor fix +to import.bas for Newdos/80. Thanks to Ulrich Mueller for this code! + +1.9 -- Thu Feb 12 20:44:02 PST 1998 Tim Mann + +* Added a hard disk emulation driver, xtrshard/dct. It basically uses the +plain file I/O emts, but I added minor variants of open and close to allow +the names to be relative to -diskdir and keep rebooting from causing file +descriptor leaks. Renamed mkfloppy to mkdisk and added support for making +an empty hard disk. Hard disk format is compatible with Matthew Reed's +emulators; thanks to Matthew for supplying documentation on his format. + +* Distinguished between fatal and nonfatal errors. Now an unimplemented +opcode is a nonfatal error. It's treated as a no-op. + +* Added the raw code bytes to disassembler printout. Fixed bugs in +disassembling ld (ix+dd),nn and ld (iy+dd),nn. Corrected disassembly +of rlca and rrca. + +* Fixed a fatal bug in emt_lseek. Added emt_ftruncate. Fixed portability +bug in emt_open that affected import/cmd and export/cmd. Added disk +change count on emt_misc. + +* "next" in debugger now will step over RSTs. Note this won't work +right with the funny RST 8 calling convention in the Model I/III Basic +ROM, which puts a 1-byte argument after the instruction. + +* Added import, export, and settime for Model 4 TRSDOS/LDOS 6. + +* Fixed problems compiling trs_disk.c on some Linux versions. + +1.8 -- Mon Dec 1 16:00:52 PST 1997 Tim Mann + +* Moved queueing of keys to after the translation from X keysyms to +TRS-80 keyboard transitions. This lets shift transitions that are +forced by other keystrokes (where X and TRS-80 keyboards have opposite +shift states to get the same character) be queued separately and thus +separated in time from the basic key transitions. This separation is +required by the Montezuma CP/M keyboard driver, which sometimes misses +a shift transition if another transition occurs at exactly the same instant. + +* Bug fix: an FDC restore command was erroneously zeroing the sector +register instead of just the track register. Montezuma CP/M tickled this +bug. + +* Bug fix to mem_bank from Roland Gerlach. Switching banks in the +low-order half of the address space was switching in the wrong memory. +Montezuma CP/M tickled this bug and crashed. + +* Removed use of pre-POSIX signal code; thanks to Al Petrofsky. + +* Added the delayed_intrq_kludge in more cases in trs_disk.c. This fixes +additional problems with SuperUtility and Trakcess that I didn't notice +earlier. Also fixed some problems with error handling on real floppies. + +1.0.tpm1 to 1.7 -- Tim Mann + +* Added floppy disk emulation, timer interrupt emulation, a kludge to +import and export data to the host system, Model III mode, Model 4 mode, a +rewrite of the keyboard handling, support for most of the undocumented +Z-80 instructions, and a bunch of bug fixes and minor improvements. + +* Fixed bugs in the instructions rrc, inc ix, inc iy, sra, and neg. I +didn't notice the sra bug until I tried to get Model III mode working; +the Model III LDOS disk driver uses it. The neg bug was keeping the +break key from working right under LDOS in xtrs-1.3 and earlier. + +* Got the emulator running on both Linux and Digital Unix. Now it +might have minor problems elsewhere, on less ANSI- and POSIX-compliant +systems. + +* Fixed two bugs in the disassembler. The ld (bc),a instruction was +erroneously disassembled as ld bc,a, and instructions that start "dd +cb" or "fd cb" were not disassembled; for example: bit 7,(iy+03h). + +* Fixed some bugs in emulated cassettes. Formerly if you tried to +append more stuff to an emulated cassette, it would smash whatever was +already there. If you tried to read from a cassette with nothing on +it, it would lock up so hard that even the emulated boot button +wouldn't work. Now at least the boot button works. + +* SIGIO is now used in a much safer way than before, and the emulator +will work without it if needed. + +* Reworked the keyboard support substantially. A full state vector is +now kept, including all 64 possible TRS-80 keys. Key queueing is now +always used, and a "stretching" feature prevents the emulated keyboard +state from changing too often, to avoid losing keystrokes. Keyboard +response is quite snappy and reliable now. I added enough key +mappings that it should be possible to do about everything you could +do with a real TRS-80 keyboard (and more than on a Model I, because +modern keyboards have N-key rollover). More key mappings are easily +added by changing tables; there is very little special-case code. + +* Did some work on the KBWAIT feature, which tries to avoid burning +host CPU cycles when the emulated program is sitting in a loop polling +the keyboard. It now works in a lot more cases. + +* More accurate character bitmaps are now used, contributed by Al +Petrofsky. The Model I bitmaps are still not the real ones, which +used a 5x7 matrix, not 7x8. + +* The halt instruction is now emulated correctly. Formerly it caused xtrs +to exit. + +1.0 -- David Gingold and Alec Wolman + +* Initial release. Emulated a TRS-80 Model I with 48K of RAM, cassette +drive, and uppercase-only screen. See README. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..94d8ab2 --- /dev/null +++ b/Makefile @@ -0,0 +1,224 @@ + +# +# Makefile for xtrs, the TRS-80 emulator. +# + +OBJECTS = \ + z80.o \ + main.o \ + load_cmd.o \ + load_hex.o \ + trs_memory.o \ + trs_keyboard.o \ + error.o \ + debug.o \ + dis.o \ + trs_io.o \ + trs_cassette.o \ + trs_xinterface.o \ + trs_chars.o \ + trs_printer.o \ + trs_rom1.o \ + trs_rom3.o \ + trs_rom4p.o \ + trs_disk.o \ + trs_interrupt.o \ + trs_imp_exp.o \ + trs_hard.o \ + trs_uart.o + +CR_OBJECTS = \ + compile_rom.o \ + error.o \ + load_cmd.o \ + load_hex.o + +MD_OBJECTS = \ + mkdisk.o + +HC_OBJECTS = \ + cmd.o \ + error.o \ + load_hex.o \ + hex2cmd.o + +CD_OBJECTS = \ + cmddump.o \ + load_cmd.o + +SOURCES = \ + cmd.c \ + cmddump.c \ + compile_rom.c \ + debug.c \ + dis.c \ + error.c \ + hex2cmd.c \ + load_cmd.c \ + load_hex.c \ + main.c \ + mkdisk.c \ + trs_cassette.c \ + trs_chars.c \ + trs_disk.c \ + trs_hard.c \ + trs_imp_exp.c \ + trs_interrupt.c \ + trs_io.c \ + trs_keyboard.c \ + trs_memory.c \ + trs_printer.c \ + trs_uart.c \ + trs_xinterface.c \ + z80.c + +HEADERS = \ + cmd.h \ + config.h \ + reed.h \ + trs.h \ + trs_disk.h \ + trs_hard.h \ + trs_imp_exp.h \ + trs_iodefs.h \ + trs_uart.h \ + z80.h + +MISC = \ + ChangeLog \ + Makefile \ + Makefile.local \ + README \ + cassette \ + cassette.txt \ + cmddump.txt \ + export.cmd \ + export.lst \ + export.z80 \ + hardfmt.txt \ + hex2cmd.txt \ + import.cmd \ + import.lst \ + import.z80 \ + m1format.fix \ + mkdisk.txt \ + settime.ccc \ + settime.cmd \ + settime.lst \ + settime.z80 \ + utility.dsk \ + utility.jcl \ + cpmutil.dsk \ + xtrs.txt \ + xtrsemt.ccc \ + xtrsemt.h \ + xtrshard.dct \ + xtrshard.lst \ + xtrshard.z80 \ + xtrsmous.cmd \ + xtrsmous.lst \ + xtrsmous.z80 + +Z80CODE = export.cmd import.cmd settime.cmd xtrsmous.cmd \ + xtrs8.dct xtrshard.dct \ + fakerom.hex xtrsrom4p.hex + +MANSOURCES = cassette.man \ + cmddump.man \ + hex2cmd.man \ + mkdisk.man \ + xtrs.man + +MANPAGES = xtrs.txt mkdisk.txt cassette.txt cmddump.txt hex2cmd.txt + +PROGS = xtrs mkdisk hex2cmd cmddump + +default: xtrs mkdisk hex2cmd cmddump manpages + +manpages: $(MANPAGES) + +z80code: $(Z80CODE) + +# Local customizations for make variables are done in Makefile.local: +include Makefile.local + +CFLAGS = $(DEBUG) $(ENDIAN) $(DEFAULT_ROM) $(READLINE) $(DISKDIR) $(IFLAGS) \ + $(APPDEFAULTS) -DKBWAIT -DHAVE_SIGIO +LIBS = $(XLIB) $(READLINELIBS) $(EXTRALIBS) + +ZMACFLAGS = -h +.SUFFIXES: .z80 .cmd .dct .man .txt .hex +.z80.cmd: + zmac $(ZMACFLAGS) $< + hex2cmd $*.hex > $*.cmd + rm -f $*.hex +.z80.dct: + zmac $(ZMACFLAGS) $< + hex2cmd $*.hex > $*.dct + rm -f $*.hex +.z80.hex: + zmac $(ZMACFLAGS) $< +.man.txt: + nroff -man -c -Tascii $< | colcrt - | cat -s > $*.txt + +xtrs: $(OBJECTS) + $(CC) $(LDFLAGS) -o xtrs $(OBJECTS) $(LIBS) + +compile_rom: $(CR_OBJECTS) + $(CC) -o compile_rom $(CR_OBJECTS) + +trs_rom1.c: compile_rom $(BUILT_IN_ROM) + ./compile_rom 1 $(BUILT_IN_ROM) > trs_rom1.c + +trs_rom3.c: compile_rom $(BUILT_IN_ROM3) + ./compile_rom 3 $(BUILT_IN_ROM3) > trs_rom3.c + +trs_rom4p.c: compile_rom $(BUILT_IN_ROM4P) + ./compile_rom 4p $(BUILT_IN_ROM4P) > trs_rom4p.c + +mkdisk: $(MD_OBJECTS) + $(CC) -o mkdisk $(MD_OBJECTS) + +hex2cmd: $(HC_OBJECTS) + $(CC) -o hex2cmd $(HC_OBJECTS) + +cmddump: $(CD_OBJECTS) + $(CC) -o cmddump $(CD_OBJECTS) + +saber_src: + #ignore SIGIO + #load $(LDFLAGS) $(CFLAGS) $(SOURCES) $(LIBS) + +tar: $(SOURCES) $(HEADERS) + tar cvf xtrs.tar $(SOURCES) $(HEADERS) $(MANSOURCES) $(MISC) + rm -f xtrs.tar.Z + compress xtrs.tar + +clean: + rm -f $(OBJECTS) $(MD_OBJECTS) $(CR_OBJECTS) $(HC_OBJECTS) \ + $(CD_OBJECTS) trs_rom*.c *~ \ + $(PROGS) compile_rom + +veryclean: clean + rm -f $(Z80CODE) $(MANPAGES) *.lst + +link: + rm -f xtrs + make xtrs + +install: install-progs install-man + +install-progs: $(PROGS) + $(INSTALL) -c -m 755 $(PROGS) $(BINDIR) + +install-man: $(MANPAGES) + $(INSTALL) -c -m 644 xtrs.man $(MANDIR)/man1/xtrs.1 + $(INSTALL) -c -m 644 cassette.man $(MANDIR)/man1/cassette.1 + $(INSTALL) -c -m 644 mkdisk.man $(MANDIR)/man1/mkdisk.1 + $(INSTALL) -c -m 644 cmddump.man $(MANDIR)/man1/cmddump.1 + $(INSTALL) -c -m 644 hex2cmd.man $(MANDIR)/man1/hex2cmd.1 + +depend: + makedepend -- $(CFLAGS) -- $(SOURCES) + +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/Makefile.local b/Makefile.local new file mode 100644 index 0000000..091457f --- /dev/null +++ b/Makefile.local @@ -0,0 +1,110 @@ + +# +# Makefile.local: local customizations for the xtrs Makefile +# + +# If your machine is not a sun, vax, or DEC MIPS, uncomment the line below +# if the processor has big-endian byte ordering. If you're not sure what +# to do, don't worry -- the program will complain if it's not right. + +# ENDIAN = -Dbig_endian + +# If you would like the TRS-80 ROM to be built into the application, +# use the following lines (with the appropriate file names). You can +# get a ROM image from a real TRS-80 with moderate effort, if you have +# one. There are also Web sites with TRS-80 ROMs that can be +# downloaded, but none are included with this package due to copyright +# concerns. As another alternative, for -model III or 4, you can +# export the MODELA/III file from a Model III or 4 TRSDOS or +# LDOS/LS-DOS diskette and use it as BUILT_IN_ROM3. + +# The default ROM for -model I, III and 4 (fakerom.hex) just prints a +# message saying that you don't have a ROM. The default ROM for +# -model 4P (xtrsrom4p.hex) is a free minimal ROM that can boot a +# Model 4 mode operating system (such as TRSDOS/LDOS 6); source code +# is included in this package. This ROM cannot boot a Model III mode +# operating system. + +# for -model I +BUILT_IN_ROM = fakerom.hex +# for -model III and -model 4 +BUILT_IN_ROM3 = fakerom.hex +# for -model 4P +BUILT_IN_ROM4P = xtrsrom4p.hex + +# If you would like the application to load a default ROM file at startup +# time, use these lines (with the appropriate file names). The default file +# name is ignored and can be omitted if the ROM for that model is built in. + +DEFAULT_ROM = -DDEFAULT_ROM='"/usr/local/lib/xtrs/level2rom.hex"' \ + -DDEFAULT_ROM3='"/usr/local/lib/xtrs/romimage.m3"' \ + -DDEFAULT_ROM4P='"/usr/local/lib/xtrs/romimage.m4p"' + +# If you would like to change where xtrs looks for disk?-? files, edit +# this line. "." of course means the current working directory. + +DISKDIR = -DDISKDIR='"."' + +# If you have the GNU readline package (the README file tells you where to +# get it) and would like to use it with the built-in Z-80 debugger, use +# these lines. As of xtrs 2.7, it is now OK to use this feature again. + +READLINE = -DREADLINE +READLINELIBS = -lreadline -lncurses + +# If you want to debug the emulator, use the -g flag instead: + +DEBUG = -O2 -g +#DEBUG = -g -DKBDEBUG -DXDEBUG +#DEBUG = -g +#DEBUG = -Wall + +# If you have gcc, and you want to use it: + +#CC = gcc + +# If you need a different path for libraries: + +LDFLAGS = -L/usr/X11R6/lib +#LDFLAGS = -non_shared -L/usr/X11/lib + +# If you need a different path for include files: + +IFLAGS = -I/usr/include/X11 + +# Re-define this if your X library is strangely named: + +XLIB = -lX11 + +# Use this if you need yet more libraries: + +#EXTRALIBS = -ldnet +#EXTRALIBS = -lots +#EXTRALIBS = -ldnet_stub + +# If you want xtrs to look for a global app-defaults file +# at runtime in $APPDEFAULTS/Xtrs: + +APPDEFAULTS = -DAPPDEFAULTS='"/usr/X11/lib/X11/app-defaults"' + +# prefix directory + +PREFIX=/usr/local +#If included in distribution: PREFIX=/usr + +# Set these to where you want installed stuff to go, if you install them. + +BINDIR = $(PREFIX)/bin +MANDIR = $(PREFIX)/share/man + +# Change this if you have a BSD-compatible install program that is +# not the first program named "install" on your $PATH + +INSTALL = install + +# If you are building in a subdirectory: +#vpath %.c .. +#vpath %.h .. +#vpath %.man .. +#old way: +#VPATH = .. @@ -0,0 +1,227 @@ +xtrs README +Tim Mann +http://tim-mann.org/ +1/12/98 + +As computer scientists, we always long for something bigger and better +than what we have now. Give us a megabyte, and we want a gigabyte. +Give us an emulated TRS-80 Model I with Level II BASIC and cassette, +and we long for the Expansion Interface and floppy disk drives. Give +us that, and we want a Model III. Give us a Model III, and we want a +Model 4! + +This is a snapshot of the work I've been doing on xtrs to add these +niceties. My main additions over xtrs-1.0 have been floppy disk and +hard disk emulation, timer interrupt emulation, a kludge to import and +export data to the host system, Model III mode, Model 4 mode, a +rewrite of the keyboard handling, support for most of the undocumented +Z-80 instructions, and a bunch of bug fixes and minor improvements. + +This project has been a real (48KB?) trip down memory lane for me. I +worked as a programmer for Logical Systems, the company that produced +LDOS, during 1980-81 before I went to grad school. I no longer own a +TRS-80, but I recently found a box in my storage closet containing all +my Model I hardware manuals, schematics, ROM disassemblies, and even +the assembly listing for a version of the LDOS Percom Doubler driver +that I helped write, giving me enough information to get my additions +to xtrs off the ground. + +Since then I've done much more. Check out my TRS-80 Web page at +http://www.tim-mann.org/trs80.html for details! + +************************************************************************* + +Below is the original README file from xtrs 1.0. It is outdated, but +I've kept it for historical reasons and to credit the original authors. + + + XTRS + + A TRS-80 Model I Emulator for X Windows + + by David Gingold <gingold@think.com> + and Alec Wolman <wolman@crl.dec.com> + + Version 1.0 + + April 1, 1992 + + + +WHAT IS XTRS? + +As computer scientists, we and our colleagues now and then experience a +strange yet powerful desire to write some BASIC code on the computer system +that was for many of us the first we owned or programmed: the TRS-80 Model +I. In our modern systems of networked workstations and window systems, +this capability has been strangely neglected. Xtrs is our attempt to fill +this important void in our computing environment. + +Xtrs is an X Windows client which runs under Unix and emulates a TRS-80 +Model I. Version 1.0 is our first release of this software; it provides a +simple yet powerful Level II BASIC environment and cassette emulation. We +expect to provide some more sophisticated functionality in future releases. + + +OBTAINING XTRS: + +If you have Internet access, you can get a copy of the latest xtrs software +via anonymous FTP: + + Host: think.com + Directory: /users/gingold/xtrs + File: xtrs-1.0.tar.Z + +We will also be posting the sources to alt.sources and perhaps to +comp.sources.x. + + +OBTAINING THE ROM: + +You will need a copy of the Level II ROM software in order to use xtrs. + +The Level II ROM software may be covered by copyrights; for this reason we +are not distributing it with our xtrs software. However, if you already +own the Level II ROM software, then you can make yourself a copy of this +for use with xtrs (as long as you obey the legal restrictions for its fair +use). You can get such a copy via anonymous FTP. + +Please do not make illegal copies of the ROM software. + +You already own a legal copy of the Level II ROM software if: +- You own a TRS-80 Model I with Level II Basic, +- You own a TRSDOS disk which has a copy of the ROM image on it, or +- You own a copy of Farvour's "Microsoft Basic Decoded" which contains the + ROM code listing. + +If you have Internet access, you can get a copy of the ROM software via +anonymous ftp: + + Host: think.com + Directory: /users/gingold/xtrs/rom + File: level2rom.hex + +If you are obtaining the ROM software through other means (such as getting +it directly out of your TRS-80), you'll need to end up with an ASCII file +which is in what we believe to be an "Intel Hex" format -- this is a format +used with PROM programmers. + +Our disclaimer: We have neither sought nor received permission from anybody +to distribute copies of the ROM software. We discourage your making +illegal copies of this or any software. Neither we nor our employers will +accept any sort of responsibility if you illegaly copy the ROM software. +We are not lawyers, and are in no position to advise you on what is legal. + +(For the legally curious: the TRS-80 we own has a copyright notice on the +ROM PC board, but none on the ROMs themselves or within the software. We +don't know if Farvour had permission to publish the ROM code.) + + +BUILDING THE PROGRAM: + +You can uncompress and extract the software from the tar file as follows: + + % uncompress xtrs-1.0.tar.Z + % tar xvf xtrs-1.0.tar + +You can then build the program using "make". You will probably need to +edit the file "Makefile.local" in order to compile the program properly. +Notes in that file explain how to do this. + +It is possible either to compile the ROM image into the program or to keep +the rom file separate, optionally specified on the command line. The +"Makefile.local" describes how to do this. + +If you would like to use the Gnu "readline" facility with the our built-in +Z-80 debugger, you can get this software via anonymous FTP: + + Host: athena-dist.mit.edu + File: /pub/gnu/readline-1.1.tar.Z + + +RUNNING THE PROGRAM: + +See the manual page. + + +DETAILS: + +Xtrs is built on top of a (mostly) full Z-80 emulator, with added routines +to support keyboard and video I/O through an X Windows interface. The +hardware emulation is based on the TRS-80 Model I design. + +Xtrs supports 48K of RAM. There is support for a rudimentary cassette I/O +emulation which uses files for cassette tapes. A printer is emulated by +sending its output to standard output. There is no support (yet) for a +disk or a serial port. + +The speed of the emulator is pretty good. On a decent Sun-4 compiled with +gcc, it computes a little faster than the real thing. Some operations +(such as writing to the screen) will naturally be slower. + +The Z-80 emulator is written to be portable to most C environments. It is +possible to build a faster emulator by sacrificing this portability (such +emulators have been built for 80x86 and 680x0 machines), but that wasn't +our goal. Memory accesses are handled through a function call interface, +allowing us to cleanly emulate memory-mapped devices. + +The Z-80 emulator has a way-cool debugger called zbx. It works sort of +like dbx. If you run with the -debug switch you'll enter the debugger, and +you can type "help" for more information. + +Some Z-80 things are not supported: interrupts other than the non-maskable +interrupt, other esoteric signals not used by the TRS-80, and any of the +"undocumented" Z-80 instructions. Reading the memory refresh register +gives a pseudo-random value. The execution speed of instructions bears no +relationship to actual Z-80 timings. + +Special support in the emulator allows the program to block when waiting +for information from the keyboard. This will only work for programs which +get keyboard input via the standard ROM calls. We do similar tricks to +make the cassette I/O work. + + +BUGS: + +The manual page lists a few known bugs. + +If you discover bugs (or write fixes for any of these), please let us know +(you can send us mail at the above addresses). We expect to incorporate +fixes into future releases. + + +FUTURE ENHANCEMENTS: + +Here are some features we are thinking of adding to xtrs in the future: + +- A better name for the application. We're not too happy with "xtrs". +- An emulator for disk drives. +- A mechanism for digitizing cassette tapes. +- A nicer mechanism for controlling the cassette emulator. +- A cut and paste mechanism in the X window. +- An emulator for the serial port. +- A Macintosh implementation. + + +CREDITS: + +David Gingold <gingold@think.com> wrote the Z-80 emulator, zbx, and much of +the TRS-80 emulation. + +Alec Wolman <wolman@crl.dec.com> wrote the X windows interface and some of +the associated routines. + +Bruce Norskog is the suspected author of a program called "zdis" which was +incorporated into the debugger to disassemble Z-80 instructions. + +Rodnay Zach's "Programming the Z-80" was the reference for implementing the +Z-80 emulator. + +James Farvour's "Microsoft Basic Decoded and Other Mysteries" and George +Blank's "Pathways through the ROM" helped us to debug the emulator and +figure out how to make the gnarly keyboard and cassette hacks work. Thanks +go to David Librik and Marty Brilliant for providing these books. + +Jim Cathey, Charlie Gibbs, and Willi Kusche wrote SimCPM, a CPM simulator +for the Amiga. This was used (don't ask how) to help debug our Z-80 +emulator. diff --git a/cassette b/cassette new file mode 100755 index 0000000..e60189c --- /dev/null +++ b/cassette @@ -0,0 +1,149 @@ +#!/bin/csh -f +# Try cassette.sh instead if you do not have /bin/csh. + +set done = 0 +set control_file = '.cassette.ctl' +set default_format = '1' +set format_name = ( 'cas' 'cpt' 'wav' 'direct' 'debug' ) + +if(! -e $control_file) then + echo "Creating" $control_file + echo "cassette.$format_name[$default_format] 0 $default_format" \ + > $control_file +endif + +while($done != 1) + set control = `cat $control_file` + set filename = $control[1] + set position = $control[2] + if (${#control} < 3) then + set format = $default_format + else + set format = $control[3] + endif + if ({ test -e $filename }) then + set isnew = "" + else + set isnew = " (new)" + endif + echo "" + echo "Tape loaded: " $filename$isnew + echo "Type: " $format_name[$format] + echo "Position: " $position + echo "" + echo -n "Command: " + set command = "$<" + set command = ( $command ) + + if($#command < 1) then + set command = "help" + endif + switch($command[1]) + + case "pos": + breaksw + + case "load": + case "file": + if($#command != 2) then + echo "Must specify a file name" + else + switch($command[2]) + case *.cas: + case *.bin: + set format = 1 + breaksw + case *.cpt: + set format = 2 + breaksw + case *.wav: + set format = 3 + breaksw + case "/dev/dsp*": + set format = 4 + breaksw + case *.debug: + set format = 5 + breaksw + default: + set format = $default_format + breaksw + endsw + echo $command[2] 0 $format > $control_file + endif + breaksw + + case "type": + if($#command != 2) then + echo "Types are:" + echo " " $format_name + else + switch($command[2]) + case "cas": + set format = 1 + breaksw + case "cpt": + set format = 2 + breaksw + case "wav": + set format = 3 + breaksw + case "direct": + set format = 4 + set filename = "/dev/dsp" + set position = 0 + breaksw + case "debug": + set format = 5 + breaksw + default: + echo "Types are:" + echo " " $format_name + breaksw + endsw + echo $filename $position $format > $control_file + endif + breaksw + + case "rew": + if($#command == 2) then + @ position = $command[2] + else + @ position = 0 + endif + + echo $filename $position $format > $control_file + breaksw + + case "ff": + if($#command == 2) then + @ position = $command[2] + else + set wcout = `wc -c $filename` + @ position = $wcout[1] + endif + + echo $filename $position $format > $control_file + breaksw + + case "quit": + case "exit": + case "done": + set done = 1 + breaksw + + case "help": + default: + echo "Commands are:" + echo " pos" + echo " load filename" + echo " type {$format_name}" + echo " rew [position]" + echo " ff [position]" + echo " quit" + breaksw + + endsw +end + +exit 0 diff --git a/cassette.man b/cassette.man new file mode 100644 index 0000000..f226f2e --- /dev/null +++ b/cassette.man @@ -0,0 +1,148 @@ +.TH cassette 1 +.SH Name +cassette \- data cassette image manipulator for xtrs TRS-80 emulator +.SH Syntax +.B cassette +.SH Description +To control the emulated cassette used by \fBxtrs\fP, a file called +".cassette.ctl" in the current directory keeps track of what file is +currently "loaded" as the cassette tape and the current position within +that file. The \fBcassette\fP shell script provides a way to manipulate +this file; typing "help" at its prompt shows its commands. You may use +this script to load and position cassette tape files. The operation +works very much like an actual tape recorder. + +This manual page also describes the image formats that the emulator +supports and their limitations. + +In this release, two cassette programs are supplied. The original +\fBcassette\fP is a C-shell script; it should work with most versions +of /bin/csh. If you do not have /bin/csh installed, you can use +\fBcassette.sh\fP, which is a Bourne shell script. It requires a +modern version of the Bourne shell that supports user-defined functions, +so it may not work on older Unix systems. + +.SH Commands +.B pos +generates a status message including the filename being used as the +cassette image and the current position within the image, in bytes. + +.B load +.I [filename] +changes the cassette image currently being used to the file specified, and +resets the position counter to zero. + +.B type +.I typename +tells the emulator what type of image is loaded. Usually this is +detected from the file extension, but you can override the detected +value with this command. The supported types are listed in the next section. + +.B rew +.I [position] +changes the position counter to the position specified. +If no position is given, the counter is reset to zero. + +.B ff +.I [position] +changes the position counter to the position specified. +If no position is given, the counter is set to the end of the file. + +.B quit +exits the \fBcassette\fP shell script. +.SH Types +\fBxtrs\fP supports several different types of cassette images, +each of which represents cassette data in a different format. + +.I cas +format is fairly compact and is compatible with other TRS-80 emulators +that have cassette support. This format represents the bit stream +that (the emulator thinks) the TRS-80 cassette routines were trying to +save to the tape, not the actual electrical signals on the tape. + +On writing, the emulator monitors the values that the TRS-80 software is +sending to the cassette port and their timing, auto-recognizes whether +a 250-bps, 500-bps, or 1500-bps format is being written, decodes the +signals into a string of 0 and 1 bits, packs the bits into bytes, and +writes them to the cas file. On reading, the emulator auto-detects +whether software is trying to read at 250, 500, or 1500 bps and +encodes the 0's and 1's back into the signals that the TRS-80 software +is expecting. This somewhat roundabout method should work with most +TRS-80 cassette routines that read and write signals compatible with the +ROM cassette routines, but it may fail with custom routines that are +too different. + +Note that generally nothing useful will happen if you try to write a +cas image at one speed and read it at another. There are differences +in the actual bit streams that standard TRS-80 software records at +each of the three different speeds, not just differences in encoding +the electrical signals on the tape. Thus an incoming bit stream that +was originally recorded at one speed will not be understood when read +back in at a different speed. For example, Level 2 Basic programs are +tokenized, while Level 1 Basic programs are not, and the two Basic +implementations record different binary information at the start of +the program and between lines. Also, when a file is saved at 1500 +bps, standard TRS-80 software puts an extra 0 bit after every 8 data +bits, and these extra bits are packed into the cas file along with the +data bits. + +.I cpt +format (for "cassette pulse train") encodes the exact values and +timing of the signals that the TRS-80 cassette routine sends to the +cassette output port to be recorded on the tape. Timing is to the +nearest microsecond. This format emulates a perfect, noise-free +cassette, so any cassette routines that even halfway worked on real +hardware should work with it. + +.I wav +format is a standard sound file format. The wav format is +intermediate in emulation accuracy between cas and cpt. It does +represent actual signals, not decoded bits, but its timing precision +is limited by the sample rate used. The default rate for new wav +files is 44,100 Hz; you can change this with the -samplerate command +line option to \fBxtrs\fP. + +You can play wav files written by \fBxtrs\fP through your sound card +and hear roughly what a real TRS-80 cassette sounds like. A real +TRS-80 should be able to read wav files written by \fBxtrs\fP if you +copy them to a cassette or connect the TRS-80 directly to the sound +card's output. This feature has not been tested extensively, but it +does seem to work, at least for short programs. + +\fBxtrs\fP can also read wav files. It can read back the wav files +that it writes without error. Reading wav files sampled from real +cassettes is more difficult because of the noise introduced, but in +brief testing it does seem to work. The signal processing algorithms +used are very crude, and better ones could probably do a better job +of reading old, noisy cassettes, but I don't have any such cassettes +to test with (and I don't know much about signal processing!). Help +in this area would be welcome. + +The wav file parsing code has several limitations. Samples must be +8-bit mono, and the wav file must contain only one data chunk and no +extra optional RIFF chunks in the header. If you have a wav file +whose header xtrs rejects, try using sox(1) to convert it to a more +vanilla format. + +.I direct +format is similar to wav format, except that the samples go to (or +come from) your sound card directly, not a wav file. Direct format +requires the Open Sound System /dev/dsp device. Extending the code +to work with other sound interfaces would probably not be +hard, but is left as an exercise for the reader. Please send me +the changes if you do this. + +.I debug +format is the same as cpt format except that the data is written in +human-readable ASCII. The cassette output is assumed to be 0 +initially. Each line of output gives a new value (0, 1, or 2), and +the amount of time (in microseconds) to wait before changing the +output to this value. + +.SH Authors +\fBxtrs\fP 1.0 was written by David Gingold and Alec Wolman. +The current version was revised and much extended by Timothy Mann +(see http://tim-mann.org/). +An initial version of this man page, and +the translation from C-shell (cassette) to Bourne shell (cassette.sh), +are due to Branden Robinson. diff --git a/cassette.sh b/cassette.sh new file mode 100755 index 0000000..27cc812 --- /dev/null +++ b/cassette.sh @@ -0,0 +1,127 @@ +#!/bin/sh + +arrayitem () { + INDEX=$1 + shift + eval echo \$$INDEX; +} + +arraycount () { + echo $#; +} + +DONE=no +CONTROL_FILE=.cassette.ctl +DEFAULT_FORMAT=1 +FORMAT_NAME='cas cpt wav direct debug' + +if [ ! -e "$CONTROL_FILE" ]; then + echo Creating $CONTROL_FILE + echo "cassette.$(arrayitem $DEFAULT_FORMAT $FORMAT_NAME) 0 $DEFAULT_FORMAT" > \ + $CONTROL_FILE +fi + +while [ "$DONE" != "yes" ]; do + CONTROL=$(cat $CONTROL_FILE) + FILENAME=$(arrayitem 1 $CONTROL) + POSITION=$(arrayitem 2 $CONTROL) + if [ $(arraycount $CONTROL) -lt 3 ]; then + FORMAT=$DEFAULT_FORMAT + else + FORMAT=$(arrayitem 3 $CONTROL) + fi + if [ -e "$FILENAME" ]; then + ISNEW= + else + ISNEW=" (new)" + fi + echo + echo "Tape loaded: $FILENAME$ISNEW" + echo 'Type: '$(arrayitem $FORMAT $FORMAT_NAME) + echo 'Position: '$POSITION + echo + echo -n 'Command: ' + read COMMAND + + if [ $(arraycount $COMMAND) -lt 1 ]; then + COMMAND=help + fi + + TOKEN1="$(arrayitem 1 $COMMAND)" + + case $TOKEN1 in + pos) ;; + load|file) + if [ $(arraycount $COMMAND) -ne 2 ]; then + echo "Must specify a file name" + else + TOKEN2="$(arrayitem 2 $COMMAND)" + case $TOKEN2 in + *.cas|*.bin) + FORMAT=1 ;; + *.cpt) + FORMAT=2 ;; + *.wav) + FORMAT=3 ;; + /dev/dsp*) + FORMAT=4 ;; + *.debug) + FORMAT=5 ;; + *) + FORMAT=1 ;; + esac + echo "$(arrayitem 2 $COMMAND) $FORMAT" > $CONTROL_FILE + fi ;; + type) + if [ $(arraycount $COMMAND) -ne 2 ]; then + echo Types are: + echo ' '$FORMAT_NAME + else + TOKEN2="$(arrayitem 2 $COMMAND)" + case $TOKEN2 in + cas) + FORMAT=1 ;; + cpt) + FORMAT=2 ;; + wav) + FORMAT=3 ;; + direct) + FORMAT=4 + FILENAME=/dev/dsp + POSITION=0 ;; + debug) + FORMAT=5 ;; + *) + echo Types are: + echo ' '$FORMAT_NAME ;; + esac + echo "$FILENAME $POSITION $FORMAT" > $CONTROL_FILE + fi ;; + rew) + if [ $(arraycount $COMMAND) -lt 2 ]; then + POSITION=0 + else + POSITION=$(arrayitem 2 $COMMAND) + fi + echo "$FILENAME $POSITION $FORMAT" > $CONTROL_FILE ;; + ff) + if [ $(arraycount $COMMAND) -lt 2 ]; then + POSITION=$(wc -c $FILENAME) + else + POSITION=$(arrayitem 2 $COMMAND) + fi + echo "$FILENAME $POSITION $FORMAT" > $CONTROL_FILE ;; + quit|exit|done) + DONE=yes ;; + *) + echo Commands are: + echo ' 'pos + echo ' 'load filename + echo ' 'type {$FORMAT_NAME} + echo ' 'rew [position] + echo ' 'ff [position] + echo ' 'quit ;; + esac +done + +exit diff --git a/cassette.txt b/cassette.txt new file mode 100644 index 0000000..ccab8bf --- /dev/null +++ b/cassette.txt @@ -0,0 +1,144 @@ +cassette(1) cassette(1) + + + +[1mName[0m + cassette - data cassette image manipulator for xtrs TRS-80 emulator + +[1mSyntax[0m + [1mcassette[0m + +[1mDescription[0m + To control the emulated cassette used by [1mxtrs[22m, a file called ".cas- + sette.ctl" in the current directory keeps track of what file is cur- + rently "loaded" as the cassette tape and the current position within + that file. The [1mcassette [22mshell script provides a way to manipulate this + file; typing "help" at its prompt shows its commands. You may use this + script to load and position cassette tape files. The operation works + very much like an actual tape recorder. + + This manual page also describes the image formats that the emulator + supports and their limitations. + + In this release, two cassette programs are supplied. The original [1mcas-[0m + [1msette [22mis a C-shell script; it should work with most versions of + /bin/csh. If you do not have /bin/csh installed, you can use [1mcas-[0m + [1msette.sh[22m, which is a Bourne shell script. It requires a modern version + of the Bourne shell that supports user-defined functions, so it may not + work on older Unix systems. + + +[1mCommands[0m + [1mpos [22mgenerates a status message including the filename being used as the + cassette image and the current position within the image, in bytes. + + [1mload [4m[22m[filename][24m changes the cassette image currently being used to the + file specified, and resets the position counter to zero. + + [1mtype [4m[22mtypename[24m tells the emulator what type of image is loaded. Usually + this is detected from the file extension, but you can override the + detected value with this command. The supported types are listed in + the next section. + + [1mrew [4m[22m[position][24m changes the position counter to the position specified. + If no position is given, the counter is reset to zero. + + [1mff [4m[22m[position][24m changes the position counter to the position specified. + If no position is given, the counter is set to the end of the file. + + [1mquit [22mexits the [1mcassette [22mshell script. + +[1mTypes[0m + [1mxtrs [22msupports several different types of cassette images, each of which + represents cassette data in a different format. + + [4mcas[24m format is fairly compact and is compatible with other TRS-80 emula- + tors that have cassette support. This format represents the bit stream + that (the emulator thinks) the TRS-80 cassette routines were trying to + save to the tape, not the actual electrical signals on the tape. + + On writing, the emulator monitors the values that the TRS-80 software + is sending to the cassette port and their timing, auto-recognizes + whether a 250-bps, 500-bps, or 1500-bps format is being written, + decodes the signals into a string of 0 and 1 bits, packs the bits into + bytes, and writes them to the cas file. On reading, the emulator auto- + detects whether software is trying to read at 250, 500, or 1500 bps and + encodes the 0's and 1's back into the signals that the TRS-80 software + is expecting. This somewhat roundabout method should work with most + TRS-80 cassette routines that read and write signals compatible with + the ROM cassette routines, but it may fail with custom routines that + are too different. + + Note that generally nothing useful will happen if you try to write a + cas image at one speed and read it at another. There are differences + in the actual bit streams that standard TRS-80 software records at each + of the three different speeds, not just differences in encoding the + electrical signals on the tape. Thus an incoming bit stream that was + originally recorded at one speed will not be understood when read back + in at a different speed. For example, Level 2 Basic programs are tok- + enized, while Level 1 Basic programs are not, and the two Basic imple- + mentations record different binary information at the start of the pro- + gram and between lines. Also, when a file is saved at 1500 bps, stan- + dard TRS-80 software puts an extra 0 bit after every 8 data bits, and + these extra bits are packed into the cas file along with the data bits. + + [4mcpt[24m format (for "cassette pulse train") encodes the exact values and + timing of the signals that the TRS-80 cassette routine sends to the + cassette output port to be recorded on the tape. Timing is to the + nearest microsecond. This format emulates a perfect, noise-free cas- + sette, so any cassette routines that even halfway worked on real hard- + ware should work with it. + + [4mwav[24m format is a standard sound file format. The wav format is interme- + diate in emulation accuracy between cas and cpt. It does represent + actual signals, not decoded bits, but its timing precision is limited + by the sample rate used. The default rate for new wav files is 44,100 + Hz; you can change this with the -samplerate command line option to + [1mxtrs[22m. + + You can play wav files written by [1mxtrs [22mthrough your sound card and hear + roughly what a real TRS-80 cassette sounds like. A real TRS-80 should + be able to read wav files written by [1mxtrs [22mif you copy them to a cas- + sette or connect the TRS-80 directly to the sound card's output. This + feature has not been tested extensively, but it does seem to work, at + least for short programs. + + [1mxtrs [22mcan also read wav files. It can read back the wav files that it + writes without error. Reading wav files sampled from real cassettes is + more difficult because of the noise introduced, but in brief testing it + does seem to work. The signal processing algorithms used are very + crude, and better ones could probably do a better job of reading old, + noisy cassettes, but I don't have any such cassettes to test with (and + I don't know much about signal processing!). Help in this area would + be welcome. + + The wav file parsing code has several limitations. Samples must be + 8-bit mono, and the wav file must contain only one data chunk and no + extra optional RIFF chunks in the header. If you have a wav file whose + header xtrs rejects, try using sox(1) to convert it to a more vanilla + format. + + [4mdirect[24m format is similar to wav format, except that the samples go to + (or come from) your sound card directly, not a wav file. Direct format + requires the Open Sound System /dev/dsp device. Extending the code to + work with other sound interfaces would probably not be hard, but is + left as an exercise for the reader. Please send me the changes if you + do this. + + [4mdebug[24m format is the same as cpt format except that the data is written + in human-readable ASCII. The cassette output is assumed to be 0 ini- + tially. Each line of output gives a new value (0, 1, or 2), and the + amount of time (in microseconds) to wait before changing the output to + this value. + + +[1mAuthors[0m + [1mxtrs [22m1.0 was written by David Gingold and Alec Wolman. The current + version was revised and much extended by Timothy Mann (see http://tim- + mann.org/). An initial version of this man page, and the translation + from C-shell (cassette) to Bourne shell (cassette.sh), are due to Bran- + den Robinson. + + + + cassette(1) @@ -0,0 +1,76 @@ +/* cd.ccc -- Misosys C program to change Unix working directory on xtrs */ +/* Copyright (c) 1998, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 16:59:03 PST 1998 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +usage() +{ + fprintf(stderr, "usage: cd [-l] unixdir\n"); + exit(1); +} + +#define ERRBUFSIZE 256 +#define NAMEBUFSIZE 256 + +int main(argc, argv) + int argc; + char **argv; +{ + int ret, lower, i; + char errbuf[ERRBUFSIZE]; + char namebuf[NAMEBUFSIZE]; + char *p, *q; + + if (argc < 2) { + usage(); + } + i = 1; + if (argv[i][0] == '-') { + if (tolower(argv[i][1]) != 'l') { + usage(); + } + i++; + lower = 1; + } else { + lower = 0; + } + if (argc - i != 1) { + usage(); + } + + p = namebuf; + q = argv[i]; + if (lower) { + while (*q) { + if (*q == '[' && *(q+1)) { + q++; + *p++ = *q++; + } else if (isalpha(*q)) { + *p++ = tolower(*q++); + } else { + *p++ = *q++; + } + } + } else { + while (*q) *p++ = *q++; + } + *p = '\000'; + + ret = emt_chdir(namebuf); + if (ret != 0) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "%s: %s\n", namebuf, errbuf); + exit(1); + } + exit(0); +} + Binary files differBinary files differ@@ -0,0 +1,77 @@ +/* Routines to write a TRS-80 DOS "/cmd" file */ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Wed Aug 13 19:46:49 PDT 1997 by mann */ + +#include <stdio.h> + +static int last_address, block_address; +static int block_size, got_transfer; +static unsigned char block[256]; +static FILE* file; + +void +cmd_init(FILE* f) +{ + last_address = block_address = -2; + block_size = 0; + got_transfer = 0; + file = f; +} + +void +cmd_data(int address, int value) +{ + unsigned char* p; + + if (value < 0 || address != last_address + 1 || block_size >= 256) { + if (block_size > 0) { + /* close off current block */ + putc(1, file); + putc((block_size + 2) & 0xff, file); + putc(block_address & 0xff, file); + putc((block_address >> 8) & 0xff, file); + p = block; + while (block_size) { + putc(*p++, file); + block_size--; + } + } + last_address = block_address = address; + if (value == -2) { + /* transfer address */ + putc(2, file); + putc(2, file); + putc(block_address & 0xff, file); + putc((block_address >> 8) & 0xff, file); + return; + } + if (value == -3) { + /* eof, no transfer address */ + putc(3, file); + return; + } + } + /* continue current block */ + block[block_size++] = value; + last_address = address; + return; +} + +void +cmd_transfer_address(int address) +{ + cmd_data(address, -2); + got_transfer = 1; +} + +void +cmd_end_of_file() +{ + if (!got_transfer) cmd_data(0, -3); +} @@ -0,0 +1,24 @@ +/* Routines to write a TRS-80 DOS "/cmd" file */ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Wed Apr 29 23:30:52 PDT 1998 by mann */ + +extern void cmd_init(FILE *outf); +extern void cmd_data(int address, int value); +extern void cmd_transfer_address(int address); +extern void cmd_end_of_file(void); + + + + + + + + + + diff --git a/cmddump.c b/cmddump.c new file mode 100644 index 0000000..dfab877 --- /dev/null +++ b/cmddump.c @@ -0,0 +1,178 @@ +/* Copyright (c) 1996-98, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Thu Apr 23 23:58:52 PDT 1998 by mann */ + +/* Simulated TRS-80 DOS /cmd file loader. + * + * See the LDOS Quarterly, April 1, 1982 (Vol 1, No 4), for documentation + * of the TRS-80 DOS /cmd file format. + * + * Usage: cmddump [-flags] infile [outfile startbyte nbytes] + * If the optional arguments are given, the given byte range is dumped + * from the simulated memory after loading. + * + * Flags: -q quiet: turn off -t, -m, -d, -s (later flags can override). + * -t print text of module headers, pds headers, + * patch names, and copyright notices. + * -m print running load map as file is parsed, + * coalescing adjacent blocks (implies -t). (default) + * -d print detailed map; same as -m, but don't coalesce. + * -s print summary load map after file is parsed. + * -i n select ISAM entry n (0x notation OK) + * -p foo select PDS entry "foo" (padded to 8 bytes with spaces) + * -x ignore anything after the first xfer address + */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include "load_cmd.h" + +unsigned char memory[64*1024] = { 0 }; +unsigned char loadmap[64*1024]; + +#define ARGS "qtmdsi:p:x" + +int +main(int argc, char* argv[]) +{ + FILE* f; + int verbosity = VERBOSITY_MAP; + int isam = -1; + char *pds = NULL; + int xferaddr, stopxfer = 0; + int print_summary = 0; + char pdsbuf[9]; + int res; + int c, errflg = 0; + + optarg = NULL; + while (!errflg && (c = getopt(argc, argv, ARGS)) != -1) { + switch (c) { + case 'q': + verbosity = VERBOSITY_QUIET; + break; + case 't': + verbosity = VERBOSITY_TEXT; + break; + case 'm': + verbosity = VERBOSITY_MAP; + break; + case 'd': + verbosity = VERBOSITY_DETAILED; + break; + case 's': + print_summary = 1; + break; + case 'i': + isam = strtol(optarg, NULL, 0); + break; + case 'p': + strncpy(pdsbuf, optarg, 8); + pdsbuf[8] = '\000'; + strncat(pdsbuf, " ", 8 - strlen(pdsbuf)); + pds = pdsbuf; + break; + case 'x': + stopxfer = 1; + break; + default: + errflg++; + break; + } + } + + if (errflg || !(argc == optind + 1 || argc == optind + 4)) { + fprintf(stderr, + "Usage: %s [-%s] infile [outfile startbyte nbytes]\n", + argv[0], ARGS); + exit(1); + } + + f = fopen(argv[optind], "r"); + if (f == NULL) { + perror(argv[optind]); + exit(1); + } + + res = load_cmd(f, memory, print_summary ? loadmap : NULL, + verbosity, stdout, isam, pds, &xferaddr, stopxfer); + + switch (res) { + case LOAD_CMD_OK: + if (isam != -1) { + fprintf(stderr, "warning: not ISAM file but -i flag given\n"); + } else if (pds) { + fprintf(stderr, "warning: not PDS file but -p flag given\n"); + } + break; + case LOAD_CMD_EOF: + fprintf(stderr, "premature end of file\n"); + exit(1); + case LOAD_CMD_NOT_FOUND: + if (pds) { + fprintf(stderr, "PDS entry \"%s\" not found\n", pds); + } else { + fprintf(stderr, "ISAM entry 0x%02x not found\n", isam); + } + exit(1); + case LOAD_CMD_ISAM: + if (isam == -1) { + fprintf(stderr, "warning: ISAM file but -i flag not given\n"); + } + break; + case LOAD_CMD_PDS: + if (pds == NULL) { + fprintf(stderr, "warning: PDS file but -p flag not given\n"); + } + break; + default: + fprintf(stderr, "load file format error, bad block type 0x%02x\n", res); + break; + } + + if (print_summary) { + /* Print load map */ + int lastaddr = -1; + int lastcount = -1; + int addr; + for (addr = 0; addr < sizeof(memory); addr++) { + if (loadmap[addr] != lastcount) { + if (lastcount == 1) { + printf("loaded 0x%04x - 0x%04x\n", + lastaddr, addr-1); + } else if (lastcount > 1) { + printf("loaded 0x%04x - 0x%04x (%d times!)\n", + lastaddr, addr-1, lastcount); + } + lastaddr = addr; + lastcount = loadmap[addr]; + } + } + printf("transfer address = 0x%04x\n", xferaddr); + } + + /* Dump memory - optional */ + if (argc == optind + 4) { + FILE* outf; + int addr, count; + outf = fopen(argv[optind + 1], "w"); + if (outf == NULL) { + perror(argv[optind + 1]); + exit(1); + } + addr = strtol(argv[optind + 2], (char**)NULL, 0); + count = strtol(argv[optind + 3], (char**)NULL, 0); + + while (count-- > 0) { + putc(memory[addr++], outf); + } + fclose(outf); + } + return 0; +} diff --git a/cmddump.man b/cmddump.man new file mode 100644 index 0000000..21bba87 --- /dev/null +++ b/cmddump.man @@ -0,0 +1,52 @@ +.TH cmddump 1 2001-02-22 +.SH Name +cmddump \- simulated TRS-80 CMD file loader +.SH Syntax +\fBcmddump\fP \fI[flags] infile [outfile startbyte nbytes]\fP +.SH Description +.B cmddump +displays information about TRS-80 DOS binary (command) files. It takes +an optional set of \fIflags\fP (described below), an input /cmd file, and +an optional \fIoutfile\fP, an optional starting offset of \fIstartbyte\fP +into the /cmd file, and an optional \fInbytes\fP number of bytes to dump. +Non-flag arguments must be given in the order shown. If the optional +arguments are given, the given byte range is dumped from the simulated +memory after loading. +.SH Options +.TP +\fB-d\fP +print detailed map; same as -m, but don't coalesce +.TP +\fB-i\fP \fIn\fP +select ISAM entry \fIn\fP (0x notation OK) +.TP +\fB-m\fP +print running load map as file is parsed, coalescing adjacent blocks +(implies -t) (default) +.TP +\fB-p\fP \fIfoo\fP +select PDS entry \fIfoo\fP (padded to 8 bytes with spaces) +.TP +\fB-q\fP +quiet; turns off -t, -m, -d, -s (later flags can override) +.TP +\fB-s\fP +print summary load map after file is parsed +.TP +\fB-t\fP +print text of module headers, pds headers, patch names, and copyright +notices +.TP +\fB-x\fP +ignore anything after the first transfer address +.SH Author +.B cmddump +was written by Timothy Mann. +This man page was generated by Branden Robinson from comments in the +source code. +.SH See also +.IR xtrs (1) +.PP +See the LDOS Quarterly, April 1, 1982 (Vol 1, No 4), for documentation of +the TRS-80 DOS /cmd file format. +It is available on the Web at http://www.tim-mann.org/misosys.html. diff --git a/cmddump.txt b/cmddump.txt new file mode 100644 index 0000000..af13679 --- /dev/null +++ b/cmddump.txt @@ -0,0 +1,52 @@ +cmddump(1) cmddump(1) + + + +[1mName[0m + cmddump - simulated TRS-80 CMD file loader + +[1mSyntax[0m + [1mcmddump [4m[22m[flags][24m [4minfile[24m [4m[outfile[24m [4mstartbyte[24m [4mnbytes][0m + +[1mDescription[0m + [1mcmddump [22mdisplays information about TRS-80 DOS binary (command) files. + It takes an optional set of [4mflags[24m (described below), an input /cmd + file, and an optional [4moutfile[24m, an optional starting offset of [4mstartbyte[0m + into the /cmd file, and an optional [4mnbytes[24m number of bytes to dump. + Non-flag arguments must be given in the order shown. If the optional + arguments are given, the given byte range is dumped from the simulated + memory after loading. + +[1mOptions[0m + [1m-d [22mprint detailed map; same as -m, but don't coalesce + + [1m-i [4m[22mn[24m select ISAM entry [4mn[24m (0x notation OK) + + [1m-m [22mprint running load map as file is parsed, coalescing adjacent + blocks (implies -t) (default) + + [1m-p [4m[22mfoo[24m select PDS entry [4mfoo[24m (padded to 8 bytes with spaces) + + [1m-q [22mquiet; turns off -t, -m, -d, -s (later flags can override) + + [1m-s [22mprint summary load map after file is parsed + + [1m-t [22mprint text of module headers, pds headers, patch names, and + copyright notices + + [1m-x [22mignore anything after the first transfer address + +[1mAuthor[0m + [1mcmddump [22mwas written by Timothy Mann. This man page was generated by + Branden Robinson from comments in the source code. + +[1mSee also[0m + [4mxtrs[24m(1) + + See the LDOS Quarterly, April 1, 1982 (Vol 1, No 4), for documentation + of the TRS-80 DOS /cmd file format. It is available on the Web at + http://www.tim-mann.org/misosys.html. + + + + 2001-02-22 cmddump(1) diff --git a/compile_rom.c b/compile_rom.c new file mode 100644 index 0000000..7740784 --- /dev/null +++ b/compile_rom.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Sat Apr 25 00:57:31 PDT 1998 by mann +*/ + +#include "z80.h" +#include "load_cmd.h" + +char *program_name; +static int highest_address = 0; +static Uchar memory[Z80_ADDRESS_LIMIT]; +static Uchar loadmap[Z80_ADDRESS_LIMIT]; + +/* Called by load_hex */ +void hex_data(int address, int value) +{ + address &= 0xffff; + + memory[address] = value; + if(highest_address < address) + highest_address = address; +} + +void hex_transfer_address(int address) +{ + /* Ignore */ +} + +static void load_rom(char *filename) +{ + FILE *program; + int c, a; + + if((program = fopen(filename, "r")) == NULL) + { + char message[100]; + sprintf(message, "could not read %s", filename); + fatal(message); + } + c = getc(program); + if (c == ':') { + /* Assume Intel hex format */ + rewind(program); + load_hex(program); + fclose(program); + return; + } else if (c == 1 || c == 5) { + /* Assume MODELA/III file */ + int res; + rewind(program); + res = load_cmd(program, memory, loadmap, 0, NULL, -1, NULL, NULL, 1); + if (res == LOAD_CMD_OK) { + highest_address = Z80_ADDRESS_LIMIT; + while (highest_address > 0) { + if (loadmap[--highest_address] != 0) { + break; + } + } + fclose(program); + return; + } else { + /* Guess it wasn't one */ + rewind(program); + c = getc(program); + } + } + a = 0; + while (c != EOF) { + hex_data(a++, c); + c = getc(program); + } +} + +static void write_output(char *which) +{ + int address = 0; + int i; + + highest_address++; + + printf("int trs_rom%s_size = %d;\n", which, highest_address); + printf("unsigned char trs_rom%s[%d] = \n{\n", which, highest_address); + + while(address < highest_address) + { + printf(" "); + for(i = 0; i < 8; ++i) + { + printf("0x%.2x,", memory[address++]); + + if(address == highest_address) + break; + } + printf("\n"); + } + printf("};\n"); +} + +static void write_norom_output(char *which) +{ + printf("int trs_rom%s_size = -1;\n", which); + printf("unsigned char trs_rom%s[1];\n", which); +} + +int main(int argc, char *argv[]) +{ + program_name = argv[0]; + if(argc == 2) + { + fprintf(stderr, + "%s: no specified ROM file, ROM %s will not be built into program.\n", + program_name, argv[1]); + write_norom_output(argv[1]); + } + else if(argc != 3) + { + fprintf(stderr, "Usage: %s model hexfile", program_name); + } + else + { + load_rom(argv[2]); + write_output(argv[1]); + } + return 0; +} diff --git a/config.h b/config.h new file mode 100644 index 0000000..62dc758 --- /dev/null +++ b/config.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +#if defined(sun) && !defined(i386) +#define big_endian +#endif + +#ifdef vax +#undef big_endian +#endif + +#if defined(mips) && defined(ultrix) +#undef big_endian +#endif + diff --git a/cpmutil.dsk b/cpmutil.dsk Binary files differnew file mode 100644 index 0000000..e469125 --- /dev/null +++ b/cpmutil.dsk diff --git a/cpmutil.html b/cpmutil.html new file mode 100644 index 0000000..502bf29 --- /dev/null +++ b/cpmutil.html @@ -0,0 +1,224 @@ +<html><head><title>Roland Gerlach : TRS-80 : CP/M Utilities for the xtrs Emulator</title> + + + + <meta name="Author" content="Roland Gerlach"> + <link href="cpmutil_files/style.css" title="compact" rel="stylesheet" type="text/css"> + <meta name="description" content="Roland Gerlachs's CP/M Utilities for xtrs"> + <meta name="keywords" content="TRS-80, TRS80, CP/M, Montezuma, UNIX, xtrs"></head><body> +<!-- navigation begin --> +<script language="JavaScript"><!-- +if (document.images) { + home1 = new Image(19,19); home1.src = "../home.png"; home2 = new Image(19,19); home2.src = "../home-on.png"; + up1 = new Image(19,19); up1.src = "../up.png"; up2 = new Image(19,19); up2.src = "../up-on.png"; +} +function hiLite(imgName,imgObjName) { +if (document.images) { + document.images[imgName].src = eval(imgObjName + ".src"); +}} +//--></script> +<table align="right"><tbody><tr><td> +<a href="http://members.optusnet.com.au/%7Ergerlach/trs-80/" onmouseover="hiLite('up','up2')" onmouseout="hiLite('up','up1')"><img src="cpmutil_files/up.png" alt="UP" name="up" border="0" height="19" width="19"></a> +<a href="http://members.optusnet.com.au/%7Ergerlach/" onmouseover="hiLite('home','home2')" onmouseout="hiLite('home','home1')"><img src="cpmutil_files/home.png" alt="HOME" name="home" border="0" height="19" width="19"></a> +</td></tr></tbody></table> +<!-- navigation end --> + +<h1>CP/M Utilities for the xtrs Emulator<hr></h1> + +<!-- toc begin --> +<h2>Contents</h2> +<dl> + <dt><a href="#programs">Programs</a> + </dt><dl> + <dt><a href="#export">EXPORT</a> + </dt><dt><a href="#import">IMPORT</a> + </dt><dt><a href="#xtrs">XTRS</a> + </dt></dl> + <dt><a href="#bugs">Bug Reports</a> + </dt><dt><a href="#download">Download</a> +</dt></dl> +<!-- toc end --> + +<h2><a name="programs">Programs</a></h2> + +<p>These programs were written for CP/M and make use of the emulator traps in +<a href="http://www.tim-mann.org/xtrs.html">Tim Mann's xtrs Emulator</a>. +</p> + +<p>IMPORT and EXPORT also work (with the exception of the -H option) when running CP/M on +<a href="http://www.discover-net.net/%7Edmkeil/trs80/model4.htm">David Keil's Model 4/4P Emulator</a>. +</p> + + +<h3><a name="export">EXPORT</a></h3> + +<p>Export CP/M file(s) to the host operating system.</p> + +<h4>Usage</h4> +<pre>Usage: EXPORT [-H][-L][-T][-V] cpmfileref [hostfilename] + +Where: cpmfileref is the local file to export + (use "?" and "*" to specify multiple files) + hostfilename is the name of the file to create on the host + (converted to uppercase by default), use -L to convert + to lowercase and [ to toggle case of next character. + -H switches emulator to high speed and restores speed when done. + -L converts hostfilename to lowercase. + -T export text file (CR LF becomes LF, stop at SUB character). + -V (verbose) display "r" for block read, "w" for block written. +</pre> + +<h4>Notes</h4> + +<p>CP/M does not keep an accurate file size. Binary files are always +multiples of 128 byte blocks; text files end at the first sub +character. + +</p><p>The CP/M CCP converts all command line parameters to uppercase, +hence the need of the -L option and the use of [ to toggle the case of +the next character within the hostfilename. + +</p><h4>Examples</h4> +<dl> +<dt><code>EXPORT -LT HELPFILE.TXT README.TXT</code> +</dt><dd>Export local file "HELPFILE.TXT" as a text file to "readme.txt" on the host. +<p> +</p></dd><dt><code>EXPORT RUNME.COM</code> +</dt><dd>Export local file "RUNME.COM" as a binary file to "RUNME.COM" on the host. +</dd></dl> + + +<h3><a name="import">IMPORT</a></h3> + +<p>Imports a file from the host operating system to CP/M.</p> + +<h4>Usage</h4> +<pre>Usage: IMPORT [-F][-H][-L][-T][-V] hostfilename [cpmfilename] + +Where: hostfilename is the name of the file on the host + (converted to uppercase by default), use -L to convert + to lowercase and [ to toggle case of next character. + cpmfilename is the name of the CP/M file to create, + existing files will not be overwritten unless -F is used. + -F overwrite existing files. + -H switches emulator to high speed and restores speed when done. + -L convert hostfilename to lowercase. + -T import text file (LF becomes CR LF, add SUB at end of file). + -V (verbose) display "r" for block read, "w" for block written. +</pre> + +<h4>Notes</h4> + +<p>CP/M does not keep an accurate file size, both binary and text +files will be padded to multiples of 128 byte blocks. + +</p><p>The CP/M CCP converts all command line parameters to uppercase, +hence the need of the -L option and the use of [ to toggle the case of +the next character within the hostfilename. + +</p><h4>Examples</h4> +<dl> +<dt><code>IMPORT -LT README.TXT HELPFILE.TXT</code> +</dt><dd>Import "readme.txt" from the host as a text file to "HELPFILE.TXT". +<p> +</p></dd><dt><code>IMPORT RUNME.COM</code> +</dt><dd>Import "RUNME.COM" from the host as a binary file to "RUNME.COM". +</dd></dl> + + +<h3><a name="xtrs">XTRS</a></h3> + +<p>Controls miscellaneous functions of the xtrs emulator.</p> + +<h4>Usage</h4> +<pre>Usage: XTRS action [parameters] + +Where: action is one of the following: + BOOT - reboot emulator + CHANGE - signal disk change + DEBUG - enter debugger + EXIT - end emulator + HIGHSPEED - high speed (autodelay off) + MOUNT [-L] hostfilename disknum - mount disk + NORMALSPEED - normal speed (autodelay on) + REPORT - report status + SYSTEM command - execute command on host + UMOUNT disknum - umount disk + hostfilename is the name of the virtual disk file on the host + (converted to uppercase by default), use -L to convert to + lowercase and [ to toggle case of next character. + command is the command (and parameters) to execute on the host + (converted to lowercase by default), use [ to toggle case. + Note output from command is NOT displayed in the XTRS window. + disknum is disk drive number (between 0 and 3 inclusive). + -L converts hostfilename to lowercase. +</pre> + + +<h4>Warning</h4> + +<p>The XTRS program assumes that the emulator's virtual disk files - +diskM-U (where M is the TRS-80 model and U is the drive number) - are +symbolic links. For example: +</p><pre>$ ls -l +-rw-r--r-- 1 user group 213504 Mar 21 15:24 cpmutil.dsk +lrwxrwxrwx 1 user group 10 Mar 5 12:40 disk4p-0 -> system.dsk +lrwxrwxrwx 1 user group 11 Mar 7 13:49 disk4p-3 -> cpmutil.dsk +-rw-r--r-- 1 user group 745984 Mar 24 17:38 system.dsk +</pre> + + +<p>The MOUNT action deletes the diskM-U file, replaces it with a +symbolic link to the given filename and signals a disk change (as if +F7 had been pressed). + +</p><p>The UMOUNT action deletes the diskM-U file and signals a disk +change. + +</p><h4>Notes</h4> + +<p>When changing disks, remember that CP/M must also be told of the +disk change. This is usually done by pressing ctrl-C at the prompt. + +</p><h4>Examples</h4> +<dl> +<dt><code>XTRS MOUNT -L DATA.DSK 1</code> +</dt><dd>Mount the virtual disk file "data.dsk" on drive 1. +The actual command that would be executed on the host is:<br> +<code>rm -f disk4p-1;test -f data.dsk && ln -s data.disk disk4p-1</code> +<p> +</p></dd><dt><code>XTRS UMOUNT 1</code> +</dt><dd>Unmount the virtual disk file associated with drive 1. +The actual command that would be executed on the host is:<br> +<code>rm -f disk4p-1</code> + +</dd></dl> + +<h2><a name="bugs">Bug Reports</a></h2> + +<p>Send Bug Reports and comments to +<a href="http://members.optusnet.com.au/%7Ergerlach/contact.html">Roland Gerlach</a>.</p> + +<h2><a name="download">Download</a></h2> + +<dl> + +<dt><a href="http://members.optusnet.com.au/%7Ergerlach/trs-80/mine/cpmutil.tgz">cpmutil.tgz</a> +</dt><dd>Source code and programs in a gzipped tar file. + +<p> +</p></dd><dt><a href="http://members.optusnet.com.au/%7Ergerlach/trs-80/mine/cpmutil.dsk.gz">cpmutil.dsk.gz</a> +</dt><dd>All of these programs (including source code) on a virtual disk +formatted as a "Montezuma Micro Standard DATA disk (40T, SS, DD, +200K)" with 512-byte sectors. Be careful to configure CP/M with the +proper disk format and drive parameters (40 tracks, not 80 tracks), or +you will have problems reading this disk. + +</dd></dl> + +<center> +<p></p><hr>Comments and suggestions are welcome, send them to +<a href="http://members.optusnet.com.au/%7Ergerlach/contact.html">Roland Gerlach</a>.<p></p> +</center> + +</body></html>
\ No newline at end of file diff --git a/cpmutil.tgz b/cpmutil.tgz Binary files differnew file mode 100644 index 0000000..f87fa1a --- /dev/null +++ b/cpmutil.tgz @@ -0,0 +1,93 @@ +/* crc.c + Compute CCITT CRC-16 using the correct bit order for floppy disks. +*/ + +/* Accelerator table to compute the CRC eight bits at a time */ +unsigned short const crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +/* Slow way, not using table */ +unsigned short CALC_CRC1a(unsigned short crc, unsigned char byte) +{ + int i = 8; + unsigned short b = byte << 8; + while (i--) { + crc = (crc << 1) ^ (((crc ^ b) & 0x8000) ? 0x1021 : 0); + b <<= 1; + } + return crc; +} + +/* Fast way, using table */ +#define CALC_CRC1b(crc, c) (((crc) << 8) ^ crc16_table[((crc) >> 8) ^ (c)]) + +#ifndef calc_crc1 +#define calc_crc1 CALC_CRC1b +#endif + +/* Recompute the CRC with len bytes appended. */ +unsigned short calc_crc(unsigned short crc, + unsigned char const *buf, int len) +{ + while (len--) { + crc = calc_crc1(crc, *buf++); + } + return crc; +} + +#if TEST +#include <stdio.h> +int +main(int argc, char **argv) +{ + char buf[2048]; + int count, c, res; + unsigned short preset; + + if (argc > 1) { + preset = strtol(argv[1], NULL, 0); + } else { + preset = 0xffff; + } + count = 0; + for (;;) { + res = scanf("%2x", &c); + if (res != 1) break; + buf[count++] = c; + } + printf("\n%04x\n", calc_crc(preset, buf, count)); + return 0; +} +#endif @@ -0,0 +1,943 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Tue May 1 17:52:20 PDT 2001 by mann +*/ + +#include "z80.h" +#include "trs.h" + +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include <string.h> + +#ifdef READLINE +#include <readline/readline.h> +#include <readline/history.h> +#endif + +/*SUPPRESS 112*/ + +#define MAXLINE (256) +#define ADDRESS_SPACE (0x10000) +#define MAX_TRAPS (100) + +#define BREAKPOINT_FLAG (0x1) +#define TRACE_FLAG (0x2) +#define DISASSEMBLE_ON_FLAG (0x4) +#define DISASSEMBLE_OFF_FLAG (0x8) +#define BREAK_ONCE_FLAG (0x10) +#define WATCHPOINT_FLAG (0x20) + +static Uchar *traps; +static int num_traps; +static int print_instructions; +static int stop_signaled; +static unsigned int num_watchpoints = 0; + +static char help_message[] = + +"(zbx) commands:\n\ +\n\ +Running:\n\ + run\n\ + Hard reset the Z-80 and devices and commence execution.\n\ + cont\n\ + Continue execution.\n\ + step\n\ + Execute one instruction, disallowing all interrupts.\n\ + stepint\n\ + Execute one instruction, allowing an interrupt afterwards.\n\ + next\n\ + nextint\n\ + Execute one instruction. If the instruction is a CALL, continue\n\ + until the return. Interrupts are always allowed inside the call,\n\ + but only the nextint form allows an interrupt afterwards.\n\ + reset\n\ + Hard reset the Z-80 and devices.\n\ + softreset\n\ + Press the system reset button. On Model I/III, softreset resets the\n\ + devices and posts a nonmaskable interrupt to the CPU; on Model 4/4P,\n\ + softreset is the same as hard reset.\n\ +Printing:\n\ + dump\n\ + Print the values of the Z-80 registers.\n\ + list\n\ + list <addr>\n\ + list <start addr> , <end addr>\n\ + Disassemble 10 instructions at the current pc, 10 instructions at\n\ + the specified hex address, or the instructions in the range of hex\n\ + addresses.\n\ + <start addr> , <end addr> /\n\ + <start addr> / <num bytes>\n\ + <addr> =\n\ + Print the memory values in the specified range. All values are hex.\n\ + traceon\n\ + Enable tracing of all instructions.\n\ + traceoff\n\ + Disable tracing.\n\ + diskdump\n\ + Print the state of the floppy disk controller emulation.\n\ +Traps:\n\ + status\n\ + Show all traps (breakpoints, trace points).\n\ + clear\n\ + Delete the trap at the current address.\n\ + delete <n>\n\ + delete *\n\ + Delete trap n, or all traps.\n\ + stop at <address>\n\ + break <address>\n\ + Set a breakpoint at the specified hex address.\n\ + trace <address>\n\ + Set a trap to trace execution at the specified hex address.\n\ + traceon at <address>\n\ + Set a trap to enable tracing at the specified hex address.\n\ + traceoff at <address>\n\ + Set a trap to disable tracing at the specified hex address.\n\ + watch <address>\n\ + Set a trap to watch specified hex address for changes.\n\ +Miscellaneous:\n\ + assign $<reg> = <value>\n\ + assign <addr> = <value>\n\ + Change the value of a register, register pair, or memory byte.\n\ + timeroff\n\ + timeron\n\ + Disable/enable the emulated TRS-80 real time clock interrupt.\n\ + diskdebug <hexval>\n\ + Set floppy disk controller debug flags to hexval.\n\ + 1=FDC register I/O, 2=FDC commands, 4=VTOS 3.0 JV3 kludges, 8=Gaps,\n\ + 10=Phys sector sizes, 20=Readadr timing, 40=DMK, 80=ioctl errors.\n\ + zbxinfo\n\ + Display information about this debugger.\n\ + help\n\ + ?\n\ + Print this message.\n\ + quit\n\ + Exit from xtrs.\n"; + +static struct +{ + int valid; + int address; + int flag; + Uchar byte; /* used only by watchpoints */ +} trap_table[MAX_TRAPS]; + +static char *trap_name(int flag) +{ + switch(flag) + { + case BREAKPOINT_FLAG: + return "breakpoint"; + case TRACE_FLAG: + return "trace"; + case DISASSEMBLE_ON_FLAG: + return "traceon"; + case DISASSEMBLE_OFF_FLAG: + return "traceoff"; + case BREAK_ONCE_FLAG: + return "temporary breakpoint"; + case WATCHPOINT_FLAG: + return "watchpoint"; + default: + return "unknown trap"; + } +} + +static void show_zbxinfo() +{ + printf("zbx: Z-80 debugger by David Gingold, Alex Wolman, and Timothy" + " Mann\n"); + printf("\n"); + printf("Traps set: %d (maximum %d)\n", num_traps, MAX_TRAPS); + printf("Size of address space: 0x%x\n", ADDRESS_SPACE); + printf("Maximum length of command line: %d\n", MAXLINE); +#ifdef READLINE + printf("GNU Readline library support enabled.\n"); +#else + printf("GNU Readline library support disabled.\n"); +#endif +} + +static void clear_all_traps() +{ + int i; + for(i = 0; i < MAX_TRAPS; ++i) + { + if(trap_table[i].valid) + { + traps[trap_table[i].address] &= ~(trap_table[i].flag); + trap_table[i].valid = 0; + } + } + num_traps = 0; +} + +static void print_traps() +{ + int i; + + if(num_traps) + { + for(i = 0; i < MAX_TRAPS; ++i) + { + if(trap_table[i].valid) + { + printf("[%d] %.4x (%s)\n", i, trap_table[i].address, + trap_name(trap_table[i].flag)); + } + } + } + else + { + printf("No traps are set.\n"); + } +} + +static void set_trap(int address, int flag) +{ + int i; + + if(num_traps == MAX_TRAPS) + { + printf("Cannot set more than %d traps.\n", MAX_TRAPS); + } + else + { + i = 0; + while(trap_table[i].valid) ++i; + + trap_table[i].valid = 1; + trap_table[i].address = address; + trap_table[i].flag = flag; + if (trap_table[i].flag == WATCHPOINT_FLAG) { + /* Initialize the byte field to current memory contents. */ + trap_table[i].byte = mem_read(address); + /* Increment number of set watchpoints. */ + num_watchpoints++; + } + traps[address] |= flag; + num_traps++; + + printf("Set %s [%d] at %.4x\n", trap_name(flag), i, address); + } +} + +static void clear_trap(int i) +{ + if((i < 0) || (i > MAX_TRAPS) || !trap_table[i].valid) + { + printf("[%d] is not a valid trap.\n", i); + } + else + { + traps[trap_table[i].address] &= ~(trap_table[i].flag); + trap_table[i].valid = 0; + if (trap_table[i].flag == WATCHPOINT_FLAG) { + /* Decrement number of set watchpoints. */ + num_watchpoints--; + } + num_traps--; + printf("Cleared %s [%d] at %.4x\n", + trap_name(trap_table[i].flag), i, trap_table[i].address); + } +} + +static void clear_trap_address(int address, int flag) +{ + int i; + for(i = 0; i < MAX_TRAPS; ++i) + { + if(trap_table[i].valid && (trap_table[i].address == address) + && ((flag == 0) || (trap_table[i].flag == flag))) + { + clear_trap(i); + } + } +} + +void debug_print_registers() +{ + printf("\n S Z - H - PV N C IFF1 IFF2 IM\n"); + printf("Flags: %d %d %d %d %d %d %d %d %d %d %d\n\n", + (SIGN_FLAG != 0), + (ZERO_FLAG != 0), + (REG_F & UNDOC5_MASK) != 0, + (HALF_CARRY_FLAG != 0), + (REG_F & UNDOC3_MASK) != 0, + (OVERFLOW_FLAG != 0), + (SUBTRACT_FLAG != 0), + (CARRY_FLAG != 0), + z80_state.iff1, z80_state.iff2, z80_state.interrupt_mode); + + printf("A F: %.2x %.2x IX: %.4x AF': %.4x\n", + REG_A, REG_F, REG_IX, REG_AF_PRIME); + printf("B C: %.2x %.2x IY: %.4x BC': %.4x\n", + REG_B, REG_C, REG_IY, REG_BC_PRIME); + printf("D E: %.2x %.2x PC: %.4x DE': %.4x\n", + REG_D, REG_E, REG_PC, REG_DE_PRIME); + printf("H L: %.2x %.2x SP: %.4x HL': %.4x\n", + REG_H, REG_L, REG_SP, REG_HL_PRIME); + + printf("\nT-state counter: %" TSTATE_T_LEN " ", z80_state.t_count); + printf("Delay setting: %d (%s)\n", + z80_state.delay, trs_autodelay ? "auto" : "fixed"); +} + + +static void signal_handler() +{ + stop_signaled = 1; + if (trs_continuous > 0) trs_continuous = 0; +} + +void trs_debug() +{ + stop_signaled = 1; + if (trs_continuous > 0) trs_continuous = 0; +} + +void debug_init() +{ + int i; + + traps = (Uchar *) malloc(ADDRESS_SPACE * sizeof(Uchar)); + bzero(traps, ADDRESS_SPACE * sizeof(Uchar)); + + for(i = 0; i < MAX_TRAPS; ++i) trap_table[i].valid = 0; + + printf("Type \"help\" for a list of commands.\n"); +} + +static void print_memory(Ushort address, int num_bytes) +{ + int bytes_to_print, i; + int byte; + + while(num_bytes > 0) + { + bytes_to_print = 16; + if(bytes_to_print > num_bytes) bytes_to_print = num_bytes; + + printf("%.4x:\t", address); + for(i = 0; i < bytes_to_print; ++i) + { + printf("%.2x ", mem_read((address + i) & 0xffff)); + } + for(i = bytes_to_print; i < 16; ++i) + { + printf(" "); + } + printf(" "); + for(i = 0; i < bytes_to_print; ++i) + { + byte = mem_read((address + i) & 0xffff); + if(isprint(byte)) + { + printf("%c", byte); + } + else + { + printf("."); + } + } + printf("\n"); + num_bytes -= bytes_to_print; + address += bytes_to_print; + } +} + +static void debug_run() +{ + void (*old_signal_handler)(); + Uchar t; + Uchar byte; + int continuous; + int i; + int watch_triggered = 0; + + /* catch control-c signal */ + old_signal_handler = signal(SIGINT, signal_handler); + + stop_signaled = 0; + + t = traps[REG_PC]; + while(!stop_signaled) + { + if(t) + { + if(t & TRACE_FLAG) + { + printf("Trace: "); + disassemble(REG_PC); + } + if(t & DISASSEMBLE_ON_FLAG) + { + print_instructions = 1; + } + if(t & DISASSEMBLE_OFF_FLAG) + { + print_instructions = 0; + } + } + + if(print_instructions) disassemble(REG_PC); + + continuous = (!print_instructions && num_traps == 0); + if (z80_run(continuous)) { + printf("emt_debug instruction executed.\n"); + stop_signaled = 1; + } + + t = traps[REG_PC]; + if(t & BREAKPOINT_FLAG) + { + stop_signaled = 1; + } + if(t & BREAK_ONCE_FLAG) + { + stop_signaled = 1; + clear_trap_address(REG_PC, BREAK_ONCE_FLAG); + } + + /* + * Iterate over the trap list looking for watchpoints only if we + * know there are any to be found. + */ + if (num_watchpoints) + { + for (i = 0; i < MAX_TRAPS; ++i) + { + if (trap_table[i].flag == WATCHPOINT_FLAG) + { + byte = mem_read(trap_table[i].address); + if (byte != trap_table[i].byte) + { + /* + * If a watched memory location has changed, report + * it, update the watch entry in the trap table to + * reflect the new value, and set the + * watch_triggered flag so that we stop after all + * watchpoints have been processed. + */ + printf("Memory location 0x%.4x changed value from " + "0x%.2x to 0x%.2x.\n", trap_table[i].address, + trap_table[i].byte, byte); + trap_table[i].byte = byte; + watch_triggered = 1; + } + } + } + if (watch_triggered) + { + stop_signaled = 1; + } + } + + } + signal(SIGINT, old_signal_handler); + printf("Stopped at %.4x\n", REG_PC); +} + +void debug_shell() +{ + char input[MAXLINE]; + char command[MAXLINE]; + int done = 0; + sigset_t set, oldset; + +#ifdef READLINE + char *line; + char history_file[MAXLINE]; + char *home = (char *)getenv ("HOME"); + if (!home) home = "."; + sprintf (history_file, "%s/.zbx-history", home); + read_history(history_file); +#endif + + while(!done) + { + printf("\n"); + disassemble(REG_PC); + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + +#ifdef READLINE + /* + * Use the way cool gnu readline() utility. Get completion, + * history, way way cool. + */ + { + + line = readline("(zbx) "); + if(line) + { + if(strlen(line) > 0) + { + add_history(line); + } + strncpy(input, line, MAXLINE - 1); + free(line); + } + else + { + break; + } + } +#else + printf("(zbx) "); fflush(stdout); + + if (fgets(input, MAXLINE, stdin) == NULL) break; +#endif + + sigprocmask(SIG_SETMASK, &oldset, NULL); + + if(sscanf(input, "%s", command)) + { + if(!strcmp(command, "help") || !strcmp(command, "?")) + { + printf(help_message); + } + else if (!strcmp(command, "zbxinfo")) + { + show_zbxinfo(); + } + else if(!strcmp(command, "clear")) + { + clear_trap_address(REG_PC, 0); + } + else if(!strcmp(command, "cont")) + { + debug_run(); + } + else if(!strcmp(command, "dump")) + { + debug_print_registers(); + } + else if(!strcmp(command, "delete")) + { + int i; + + if(!strncmp(input, "delete *", 8)) + { + clear_all_traps(); + } + else if(sscanf(input, "delete %d", &i) != 1) + { + printf("A trap must be specified.\n"); + } + else + { + clear_trap(i); + } + } + else if(!strcmp(command, "list")) + { + int x, y; + Ushort start, old_start; + int bytes = 0; + int lines = 0; + + if(sscanf(input, "list %x , %x", &x, &y) == 2) + { + start = x; + bytes = (y - x) & 0xffff; + } + else if(sscanf(input, "list %x", &x) == 1) + { + start = x; + lines = 10; + } + else + { + start = REG_PC; + lines = 10; + } + + if(lines) + { + while(lines--) + { + start = disassemble(start); + } + } + else + { + while (bytes >= 0) { + start = disassemble(old_start = start); + bytes -= (start - old_start) & 0xffff; + } + } + } + else if(!strcmp(command, "next") || !strcmp(command, "nextint")) + { + int is_call = 0, is_rst = 0; + switch(mem_read(REG_PC)) { + case 0xCD: /* call address */ + is_call = 1; + break; + case 0xC4: /* call nz, address */ + is_call = !ZERO_FLAG; + break; + case 0xCC: /* call z, address */ + is_call = ZERO_FLAG; + break; + case 0xD4: /* call nc, address */ + is_call = !CARRY_FLAG; + break; + case 0xDC: /* call c, address */ + is_call = CARRY_FLAG; + break; + case 0xE4: /* call po, address */ + is_call = !PARITY_FLAG; + break; + case 0xEC: /* call pe, address */ + is_call = PARITY_FLAG; + break; + case 0xF4: /* call p, address */ + is_call = !SIGN_FLAG; + break; + case 0xFC: /* call m, address */ + is_call = SIGN_FLAG; + break; + case 0xC7: + case 0xCF: + case 0xD7: + case 0xDF: + case 0xE7: + case 0xEF: + case 0xF7: + case 0xFF: + is_rst = 1; + break; + default: + break; + } + if (is_call) { + set_trap((REG_PC + 3) % ADDRESS_SPACE, BREAK_ONCE_FLAG); + debug_run(); + } else if (is_rst) { + set_trap((REG_PC + 1) % ADDRESS_SPACE, BREAK_ONCE_FLAG); + debug_run(); + } else { + z80_run((!strcmp(command, "nextint")) ? 0 : -1); + } + } + else if(!strcmp(command, "quit")) + { + done = 1; + } + else if(!strcmp(command, "reset")) + { + printf("Performing hard reset."); + trs_reset(1); + } + else if(!strcmp(command, "softreset")) + { + printf("Pressing reset button."); + trs_reset(0); + } + else if(!strcmp(command, "run")) + { + printf("Performing hard reset and running.\n"); + trs_reset(1); + debug_run(); + } + else if(!strcmp(command, "status")) + { + print_traps(); + } + else if(!strcmp(command, "set") || !strcmp(command, "assign")) + { + char regname[MAXLINE]; + int addr, value; + + if(sscanf(input, "%*s $%[a-zA-Z] = %x", regname, &value) == 2) + { + if(!strcasecmp(regname, "a")) { + REG_A = value; + } else if(!strcasecmp(regname, "f")) { + REG_F = value; + } else if(!strcasecmp(regname, "b")) { + REG_B = value; + } else if(!strcasecmp(regname, "c")) { + REG_C = value; + } else if(!strcasecmp(regname, "d")) { + REG_D = value; + } else if(!strcasecmp(regname, "e")) { + REG_E = value; + } else if(!strcasecmp(regname, "h")) { + REG_H = value; + } else if(!strcasecmp(regname, "l")) { + REG_L = value; + } else if(!strcasecmp(regname, "sp")) { + REG_SP = value; + } else if(!strcasecmp(regname, "pc")) { + REG_PC = value; + } else if(!strcasecmp(regname, "af")) { + REG_AF = value; + } else if(!strcasecmp(regname, "bc")) { + REG_BC = value; + } else if(!strcasecmp(regname, "de")) { + REG_DE = value; + } else if(!strcasecmp(regname, "hl")) { + REG_HL = value; + } else if(!strcasecmp(regname, "af'")) { + REG_AF_PRIME = value; + } else if(!strcasecmp(regname, "bc'")) { + REG_BC_PRIME = value; + } else if(!strcasecmp(regname, "de'")) { + REG_DE_PRIME = value; + } else if(!strcasecmp(regname, "hl'")) { + REG_HL_PRIME = value; + } else if(!strcasecmp(regname, "ix")) { + REG_IX = value; + } else if(!strcasecmp(regname, "iy")) { + REG_IY = value; + } else if(!strcasecmp(regname, "i")) { + REG_I = value; + } else { + printf("Unrecognized register name %s.\n", regname); + } + } + else if(sscanf(input, "%*s %x = %x", &addr, &value) == 2) + { + mem_write(addr, value); + } + else + { + printf("Syntax error. (Type \"help\" for commands.)\n"); + } + } + else if(!strcmp(command, "step")) + { + z80_run(-1); + } + else if(!strcmp(command, "stepint")) + { + z80_run(0); + } + else if(!strcmp(command, "stop") || !strcmp(command, "break")) + { + int address; + + if(sscanf(input, "stop at %x", &address) != 1 && + sscanf(input, "break %x", &address) != 1) + { + address = REG_PC; + } + address %= ADDRESS_SPACE; + set_trap(address, BREAKPOINT_FLAG); + } + else if(!strcmp(command, "trace")) + { + int address; + + if(sscanf(input, "trace %x", &address) != 1) + { + address = REG_PC; + } + address %= ADDRESS_SPACE; + set_trap(address, TRACE_FLAG); + } + else if(!strcmp(command, "untrace")) + { + printf("Untrace not implemented.\n"); + } + else if(!strcmp(command, "traceon")) + { + int address; + + if(sscanf(input, "traceon at %x", &address) == 1) + { + set_trap(address, DISASSEMBLE_ON_FLAG); + } + else + { + print_instructions = 1; + printf("Tracing enabled.\n"); + } + } + else if(!strcmp(command, "traceoff")) + { + int address; + + if(sscanf(input, "traceoff at %x", &address) == 1) + { + set_trap(address, DISASSEMBLE_OFF_FLAG); + } + else + { + print_instructions = 0; + printf("Tracing disabled.\n"); + } + } + else if(!strcmp(command, "watch")) + { + int address; + + if(sscanf(input, "watch %x", &address) == 1) + { + address %= ADDRESS_SPACE; + set_trap(address, WATCHPOINT_FLAG); + } + } + else if(!strcmp(command, "timeroff")) + { + /* Turn off emulated real time clock interrupt */ + trs_timer_off(); + } + else if(!strcmp(command, "timeron")) + { + /* Turn off emulated real time clock interrupt */ + trs_timer_on(); + } + else if(!strcmp(command, "diskdump")) + { + trs_disk_debug(); + } + else if(!strcmp(command, "diskdebug")) + { + trs_disk_debug_flags = 0; + sscanf(input, "diskdebug %x", &trs_disk_debug_flags); + } + else + { + int start_address, end_address, num_bytes; + + if(sscanf(input, "%x , %x / ", &start_address, &end_address) == 2) + { + print_memory(start_address, end_address - start_address); + } + else if(sscanf(input, "%x / %x ", &start_address, &num_bytes) == 2) + { + print_memory(start_address, num_bytes); + } + else if(sscanf(input, "%x = ", &start_address) == 1) + { + print_memory(start_address, 1); + } + else + { + printf("Syntax error. (Type \"help\" for commands.)\n"); + } + } + } + } +#ifdef READLINE + write_history(history_file); +#endif +} + +#ifdef NEVER + +/* + * Test code: + */ + +carry_in(a, b, r) +{ + return (a ^ b ^ r); +} + +carry_out(a, b, r) +{ + return (a & b) | ((a | b) & ~r); +} + +overflow(a, b, r) +{ + return carry_in(a, b, r) ^ carry_out(a, b, r); +} + +test_add(int a, int b) +{ + Uchar result; + Uchar flags; + + result = a + b; + + flags = 0; + + if(result & 0x80) flags |= SIGN_MASK; + + if(result == 0) flags |= ZERO_MASK; + + if(carry_out(a, b, result) & 0x8) flags |= HALF_CARRY_MASK; + + if(overflow(a, b, result) & 0x80) flags |= OVERFLOW_MASK; + + if(carry_out(a, b, result) & 0x80) flags |= CARRY_MASK; + + do_add_flags(a, b, result); + if(REG_F != flags) + { + printf("error: %d + %d = %d, expected %.2x, got %.2x\n", + a, b, result, flags, REG_F); + } +} + +test_sub(int a, int b) +{ + Uchar result; + Uchar flags; + + result = a - b; + + flags = 0; + + if(result & 0x80) flags |= SIGN_MASK; + + if(result == 0) flags |= ZERO_MASK; + + if(carry_out(a, - b, result) & 0x8) flags |= HALF_CARRY_MASK; + + if(overflow(a, - b, result) & 0x80) flags |= OVERFLOW_MASK; + + flags |= SUBTRACT_MASK; + + if(carry_out(a, - b, result) & 0x80) flags |= CARRY_MASK; + + do_sub_flags(a, b, result); + + if(REG_F != flags) + { + printf("error: %d - %d = %d, expected %.2x, got %.2x\n", + a, b, result, flags, REG_F); + } +} + +test_all() +{ + int a, b; + + for(a = 0; a < 256; ++a) + { + for(b = 0; b < 256; ++b) + { + test_add(a, b); + test_sub(a, b); + } + } +} + +#endif @@ -0,0 +1,2127 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + * dis.c -- a hacked version of "zdis" which can print out instructions + * as they are executed. + */ + +#include "z80.h" + +/* Argument printing */ +#define A_0 0 /* No arguments */ +#define A_8 1 /* 8-bit number */ +#define A_16 2 /* 16-bit number */ +#define A_0B 0xff /* No arguments, backskip over last opcode byte */ +#define A_8P 0x100 /* 8-bit number preceding last opcode byte */ +#define A_8R 0x101 /* 8-bit relative address */ +#define A_8X2 0x102 /* Two 8-bit numbers */ + +#define arglen(a) ((signed char)((a)&0xff)) + +static char undefined[] = "undefined"; + +struct opcode { + char *name; + int args; +}; + +static struct opcode major[256] = { + { "nop", A_0 }, /* 00 */ + { "ld bc,%02x%02xh", A_16 }, /* 01 */ + { "ld (bc),a", A_0 }, /* 02 */ + { "inc bc", A_0 }, /* 03 */ + { "inc b", A_0 }, /* 04 */ + { "dec b", A_0 }, /* 05 */ + { "ld b,%02xh", A_8 }, /* 06 */ + { "rlca", A_0 }, /* 07 */ + + { "ex af,af'", A_0 }, /* 08 */ + { "add hl,bc", A_0 }, /* 09 */ + { "ld a,(bc)", A_0 }, /* 0a */ + { "dec bc", A_0 }, /* 0b */ + { "inc c", A_0 }, /* 0c */ + { "dec c", A_0 }, /* 0d */ + { "ld c,%02xh", A_8 }, /* 0e */ + { "rrca", A_0 }, /* 0f */ + + { "djnz %04xh", A_8R }, /* 10 */ + { "ld de,%02x%02xh", A_16 }, /* 11 */ + { "ld (de),a", A_0 }, /* 12 */ + { "inc de", A_0 }, /* 13 */ + { "inc d", A_0 }, /* 14 */ + { "dec d", A_0 }, /* 15 */ + { "ld d,%02xh", A_8 }, /* 16 */ + { "rla", A_0 }, /* 17 */ + + { "jr %04xh", A_8R }, /* 18 */ + { "add hl,de", A_0 }, /* 19 */ + { "ld a,(de)", A_0 }, /* 1a */ + { "dec de", A_0 }, /* 1b */ + { "inc e", A_0 }, /* 1c */ + { "dec e", A_0 }, /* 1d */ + { "ld e,%02xh", A_8 }, /* 1e */ + { "rra", A_0 }, /* 1f */ + + { "jr nz,%04xh", A_8R }, /* 20 */ + { "ld hl,%02x%02xh", A_16 }, /* 21 */ + { "ld (%02x%02xh),hl",A_16 }, /* 22 */ + { "inc hl", A_0 }, /* 23 */ + { "inc h", A_0 }, /* 24 */ + { "dec h", A_0 }, /* 25 */ + { "ld h,%02xh", A_8 }, /* 26 */ + { "daa", A_0 }, /* 27 */ + + { "jr z,%04xh", A_8R }, /* 28 */ + { "add hl,hl", A_0 }, /* 29 */ + { "ld hl,(%02x%02xh)",A_16 }, /* 2a */ + { "dec hl", A_0 }, /* 2b */ + { "inc l", A_0 }, /* 2c */ + { "dec l", A_0 }, /* 2d */ + { "ld l,%02xh", A_8 }, /* 2e */ + { "cpl", A_0 }, /* 2f */ + + { "jr nc,%04xh", A_8R }, /* 30 */ + { "ld sp,%02x%02xh", A_16 }, /* 31 */ + { "ld (%02x%02xh),a", A_16 }, /* 32 */ + { "inc sp", A_0 }, /* 33 */ + { "inc (hl)", A_0 }, /* 34 */ + { "dec (hl)", A_0 }, /* 35 */ + { "ld (hl),%02xh", A_8 }, /* 36 */ + { "scf", A_0 }, /* 37 */ + + { "jr c,%04xh", A_8R }, /* 38 */ + { "add hl,sp", A_0 }, /* 39 */ + { "ld a,(%02x%02xh)", A_16 }, /* 3a */ + { "dec sp", A_0 }, /* 3b */ + { "inc a", A_0 }, /* 3c */ + { "dec a", A_0 }, /* 3d */ + { "ld a,%02xh", A_8 }, /* 3e */ + { "ccf", A_0 }, /* 3f */ + + { "ld b,b", A_0 }, /* 40 */ + { "ld b,c", A_0 }, /* 41 */ + { "ld b,d", A_0 }, /* 42 */ + { "ld b,e", A_0 }, /* 43 */ + { "ld b,h", A_0 }, /* 44 */ + { "ld b,l", A_0 }, /* 45 */ + { "ld b,(hl)", A_0 }, /* 46 */ + { "ld b,a", A_0 }, /* 47 */ + + { "ld c,b", A_0 }, /* 48 */ + { "ld c,c", A_0 }, /* 49 */ + { "ld c,d", A_0 }, /* 4a */ + { "ld c,e", A_0 }, /* 4b */ + { "ld c,h", A_0 }, /* 4c */ + { "ld c,l", A_0 }, /* 4d */ + { "ld c,(hl)", A_0 }, /* 4e */ + { "ld c,a", A_0 }, /* 4f */ + + { "ld d,b", A_0 }, /* 50 */ + { "ld d,c", A_0 }, /* 51 */ + { "ld d,d", A_0 }, /* 52 */ + { "ld d,e", A_0 }, /* 53 */ + { "ld d,h", A_0 }, /* 54 */ + { "ld d,l", A_0 }, /* 55 */ + { "ld d,(hl)", A_0 }, /* 56 */ + { "ld d,a", A_0 }, /* 57 */ + + { "ld e,b", A_0 }, /* 58 */ + { "ld e,c", A_0 }, /* 59 */ + { "ld e,d", A_0 }, /* 5a */ + { "ld e,e", A_0 }, /* 5b */ + { "ld e,h", A_0 }, /* 5c */ + { "ld e,l", A_0 }, /* 5d */ + { "ld e,(hl)", A_0 }, /* 5e */ + { "ld e,a", A_0 }, /* 5f */ + + { "ld h,b", A_0 }, /* 60 */ + { "ld h,c", A_0 }, /* 61 */ + { "ld h,d", A_0 }, /* 62 */ + { "ld h,e", A_0 }, /* 63 */ + { "ld h,h", A_0 }, /* 64 */ + { "ld h,l", A_0 }, /* 65 */ + { "ld h,(hl)", A_0 }, /* 66 */ + { "ld h,a", A_0 }, /* 67 */ + + { "ld l,b", A_0 }, /* 68 */ + { "ld l,c", A_0 }, /* 69 */ + { "ld l,d", A_0 }, /* 6a */ + { "ld l,e", A_0 }, /* 6b */ + { "ld l,h", A_0 }, /* 6c */ + { "ld l,l", A_0 }, /* 6d */ + { "ld l,(hl)", A_0 }, /* 6e */ + { "ld l,a", A_0 }, /* 6f */ + + { "ld (hl),b", A_0 }, /* 70 */ + { "ld (hl),c", A_0 }, /* 71 */ + { "ld (hl),d", A_0 }, /* 72 */ + { "ld (hl),e", A_0 }, /* 73 */ + { "ld (hl),h", A_0 }, /* 74 */ + { "ld (hl),l", A_0 }, /* 75 */ + { "halt", A_0 }, /* 76 */ + { "ld (hl),a", A_0 }, /* 77 */ + + { "ld a,b", A_0 }, /* 78 */ + { "ld a,c", A_0 }, /* 79 */ + { "ld a,d", A_0 }, /* 7a */ + { "ld a,e", A_0 }, /* 7b */ + { "ld a,h", A_0 }, /* 7c */ + { "ld a,l", A_0 }, /* 7d */ + { "ld a,(hl)", A_0 }, /* 7e */ + { "ld a,a", A_0 }, /* 7f */ + + { "add a,b", A_0 }, /* 80 */ + { "add a,c", A_0 }, /* 81 */ + { "add a,d", A_0 }, /* 82 */ + { "add a,e", A_0 }, /* 83 */ + { "add a,h", A_0 }, /* 84 */ + { "add a,l", A_0 }, /* 85 */ + { "add a,(hl)", A_0 }, /* 86 */ + { "add a,a", A_0 }, /* 87 */ + + { "adc a,b", A_0 }, /* 88 */ + { "adc a,c", A_0 }, /* 89 */ + { "adc a,d", A_0 }, /* 8a */ + { "adc a,e", A_0 }, /* 8b */ + { "adc a,h", A_0 }, /* 8c */ + { "adc a,l", A_0 }, /* 8d */ + { "adc a,(hl)", A_0 }, /* 8e */ + { "adc a,a", A_0 }, /* 8f */ + + { "sub b", A_0 }, /* 90 */ + { "sub c", A_0 }, /* 91 */ + { "sub d", A_0 }, /* 92 */ + { "sub e", A_0 }, /* 93 */ + { "sub h", A_0 }, /* 94 */ + { "sub l", A_0 }, /* 95 */ + { "sub (hl)", A_0 }, /* 96 */ + { "sub a", A_0 }, /* 97 */ + + { "sbc a,b", A_0 }, /* 98 */ + { "sbc a,c", A_0 }, /* 99 */ + { "sbc a,d", A_0 }, /* 9a */ + { "sbc a,e", A_0 }, /* 9b */ + { "sbc a,h", A_0 }, /* 9c */ + { "sbc a,l", A_0 }, /* 9d */ + { "sbc a,(hl)", A_0 }, /* 9e */ + { "sbc a,a", A_0 }, /* 9f */ + + { "and b", A_0 }, /* a0 */ + { "and c", A_0 }, /* a1 */ + { "and d", A_0 }, /* a2 */ + { "and e", A_0 }, /* a3 */ + { "and h", A_0 }, /* a4 */ + { "and l", A_0 }, /* a5 */ + { "and (hl)", A_0 }, /* a6 */ + { "and a", A_0 }, /* a7 */ + + { "xor b", A_0 }, /* a8 */ + { "xor c", A_0 }, /* a9 */ + { "xor d", A_0 }, /* aa */ + { "xor e", A_0 }, /* ab */ + { "xor h", A_0 }, /* ac */ + { "xor l", A_0 }, /* ad */ + { "xor (hl)", A_0 }, /* ae */ + { "xor a", A_0 }, /* af */ + + { "or b", A_0 }, /* b0 */ + { "or c", A_0 }, /* b1 */ + { "or d", A_0 }, /* b2 */ + { "or e", A_0 }, /* b3 */ + { "or h", A_0 }, /* b4 */ + { "or l", A_0 }, /* b5 */ + { "or (hl)", A_0 }, /* b6 */ + { "or a", A_0 }, /* b7 */ + + { "cp b", A_0 }, /* b8 */ + { "cp c", A_0 }, /* b9 */ + { "cp d", A_0 }, /* ba */ + { "cp e", A_0 }, /* bb */ + { "cp h", A_0 }, /* bc */ + { "cp l", A_0 }, /* bd */ + { "cp (hl)", A_0 }, /* be */ + { "cp a", A_0 }, /* bf */ + + { "ret nz", A_0 }, /* c0 */ + { "pop bc", A_0 }, /* c1 */ + { "jp nz,%02x%02xh", A_16 }, /* c2 */ + { "jp %02x%02xh", A_16 }, /* c3 */ + { "call nz,%02x%02xh", A_16 }, /* c4 */ + { "push bc", A_0 }, /* c5 */ + { "add a,%02xh", A_8 }, /* c6 */ + { "rst 0", A_0 }, /* c7 */ + + { "ret z", A_0 }, /* c8 */ + { "ret", A_0 }, /* c9 */ + { "jp z,%02x%02xh", A_16 }, /* ca */ + { 0, 0 }, /* cb */ + { "call z,%02x%02xh", A_16 }, /* cc */ + { "call %02x%02xh", A_16 }, /* cd */ + { "adc a,%02xh", A_8 }, /* ce */ + { "rst 8", A_0 }, /* cf */ + + { "ret nc", A_0 }, /* d0 */ + { "pop de", A_0 }, /* d1 */ + { "jp nc,%02x%02xh", A_16 }, /* d2 */ + { "out (%02xh),a", A_8 }, /* d3 */ + { "call nc,%02x%02xh", A_16 }, /* d4 */ + { "push de", A_0 }, /* d5 */ + { "sub %02xh", A_8 }, /* d6 */ + { "rst 10h", A_0 }, /* d7 */ + + { "ret c", A_0 }, /* d8 */ + { "exx", A_0 }, /* d9 */ + { "jp c,%02x%02xh", A_16 }, /* da */ + { "in a,(%02xh)", A_8 }, /* db */ + { "call c,%02x%02xh", A_16 }, /* dc */ + { 0, 1 }, /* dd */ + { "sbc a,%02xh", A_8 }, /* de */ + { "rst 18h", A_0 }, /* df */ + + { "ret po", A_0 }, /* e0 */ + { "pop hl", A_0 }, /* e1 */ + { "jp po,%02x%02xh", A_16 }, /* e2 */ + { "ex (sp),hl", A_0 }, /* e3 */ + { "call po,%02x%02xh", A_16 }, /* e4 */ + { "push hl", A_0 }, /* e5 */ + { "and %02xh", A_8 }, /* e6 */ + { "rst 20h", A_0 }, /* e7 */ + + { "ret pe", A_0 }, /* e8 */ + { "jp (hl)", A_0 }, /* e9 */ + { "jp pe,%02x%02xh", A_16 }, /* ea */ + { "ex de,hl", A_0 }, /* eb */ + { "call pe,%02x%02xh", A_16 }, /* ec */ + { 0, 2 }, /* ed */ + { "xor %02xh", A_8 }, /* ee */ + { "rst 28h", A_0 }, /* ef */ + + { "ret p", A_0 }, /* f0 */ + { "pop af", A_0 }, /* f1 */ + { "jp p,%02x%02xh", A_16 }, /* f2 */ + { "di", A_0 }, /* f3 */ + { "call p,%02x%02xh", A_16 }, /* f4 */ + { "push af", A_0 }, /* f5 */ + { "or %02xh", A_8 }, /* f6 */ + { "rst 30h", A_0 }, /* f7 */ + + { "ret m", A_0 }, /* f8 */ + { "ld sp,hl", A_0 }, /* f9 */ + { "jp m,%02x%02xh", A_16 }, /* fa */ + { "ei", A_0 }, /* fb */ + { "call m,%02x%02xh", A_16 }, /* fc */ + { 0, 3 }, /* fd */ + { "cp %02xh", A_8 }, /* fe */ + { "rst 38h", A_0 }, /* ff */ +}; + +static struct opcode minor[6][256] = { + { /* cb */ + { "rlc b", A_0 }, /* cb00 */ + { "rlc c", A_0 }, /* cb01 */ + { "rlc d", A_0 }, /* cb02 */ + { "rlc e", A_0 }, /* cb03 */ + { "rlc h", A_0 }, /* cb04 */ + { "rlc l", A_0 }, /* cb05 */ + { "rlc (hl)", A_0 }, /* cb06 */ + { "rlc a", A_0 }, /* cb07 */ + + { "rrc b", A_0 }, /* cb08 */ + { "rrc c", A_0 }, /* cb09 */ + { "rrc d", A_0 }, /* cb0a */ + { "rrc e", A_0 }, /* cb0b */ + { "rrc h", A_0 }, /* cb0c */ + { "rrc l", A_0 }, /* cb0d */ + { "rrc (hl)", A_0 }, /* cb0e */ + { "rrc a", A_0 }, /* cb0f */ + + { "rl b", A_0 }, /* cb10 */ + { "rl c", A_0 }, /* cb11 */ + { "rl d", A_0 }, /* cb12 */ + { "rl e", A_0 }, /* cb13 */ + { "rl h", A_0 }, /* cb14 */ + { "rl l", A_0 }, /* cb15 */ + { "rl (hl)", A_0 }, /* cb16 */ + { "rl a", A_0 }, /* cb17 */ + + { "rr b", A_0 }, /* cb18 */ + { "rr c", A_0 }, /* cb19 */ + { "rr d", A_0 }, /* cb1a */ + { "rr e", A_0 }, /* cb1b */ + { "rr h", A_0 }, /* cb1c */ + { "rr l", A_0 }, /* cb1d */ + { "rr (hl)", A_0 }, /* cb1e */ + { "rr a", A_0 }, /* cb1f */ + + { "sla b", A_0 }, /* cb20 */ + { "sla c", A_0 }, /* cb21 */ + { "sla d", A_0 }, /* cb22 */ + { "sla e", A_0 }, /* cb23 */ + { "sla h", A_0 }, /* cb24 */ + { "sla l", A_0 }, /* cb25 */ + { "sla (hl)", A_0 }, /* cb26 */ + { "sla a", A_0 }, /* cb27 */ + + { "sra b", A_0 }, /* cb28 */ + { "sra c", A_0 }, /* cb29 */ + { "sra d", A_0 }, /* cb2a */ + { "sra e", A_0 }, /* cb2b */ + { "sra h", A_0 }, /* cb2c */ + { "sra l", A_0 }, /* cb2d */ + { "sra (hl)", A_0 }, /* cb2e */ + { "sra a", A_0 }, /* cb2f */ + + { "slia b", A_0 }, /* cb30 [undoc] */ + { "slia c", A_0 }, /* cb31 [undoc] */ + { "slia d", A_0 }, /* cb32 [undoc] */ + { "slia e", A_0 }, /* cb33 [undoc] */ + { "slia h", A_0 }, /* cb34 [undoc] */ + { "slia l", A_0 }, /* cb35 [undoc] */ + { "slia (hl)", A_0 }, /* cb36 [undoc] */ + { "slia a", A_0 }, /* cb37 [undoc] */ + + { "srl b", A_0 }, /* cb38 */ + { "srl c", A_0 }, /* cb39 */ + { "srl d", A_0 }, /* cb3a */ + { "srl e", A_0 }, /* cb3b */ + { "srl h", A_0 }, /* cb3c */ + { "srl l", A_0 }, /* cb3d */ + { "srl (hl)", A_0 }, /* cb3e */ + { "srl a", A_0 }, /* cb3f */ + + { "bit 0,b", A_0 }, /* cb40 */ + { "bit 0,c", A_0 }, /* cb41 */ + { "bit 0,d", A_0 }, /* cb42 */ + { "bit 0,e", A_0 }, /* cb43 */ + { "bit 0,h", A_0 }, /* cb44 */ + { "bit 0,l", A_0 }, /* cb45 */ + { "bit 0,(hl)", A_0 }, /* cb46 */ + { "bit 0,a", A_0 }, /* cb47 */ + + { "bit 1,b", A_0 }, /* cb48 */ + { "bit 1,c", A_0 }, /* cb49 */ + { "bit 1,d", A_0 }, /* cb4a */ + { "bit 1,e", A_0 }, /* cb4b */ + { "bit 1,h", A_0 }, /* cb4c */ + { "bit 1,l", A_0 }, /* cb4d */ + { "bit 1,(hl)", A_0 }, /* cb4e */ + { "bit 1,a", A_0 }, /* cb4f */ + + { "bit 2,b", A_0 }, /* cb50 */ + { "bit 2,c", A_0 }, /* cb51 */ + { "bit 2,d", A_0 }, /* cb52 */ + { "bit 2,e", A_0 }, /* cb53 */ + { "bit 2,h", A_0 }, /* cb54 */ + { "bit 2,l", A_0 }, /* cb55 */ + { "bit 2,(hl)", A_0 }, /* cb56 */ + { "bit 2,a", A_0 }, /* cb57 */ + + { "bit 3,b", A_0 }, /* cb58 */ + { "bit 3,c", A_0 }, /* cb59 */ + { "bit 3,d", A_0 }, /* cb5a */ + { "bit 3,e", A_0 }, /* cb5b */ + { "bit 3,h", A_0 }, /* cb5c */ + { "bit 3,l", A_0 }, /* cb5d */ + { "bit 3,(hl)", A_0 }, /* cb5e */ + { "bit 3,a", A_0 }, /* cb5f */ + + { "bit 4,b", A_0 }, /* cb60 */ + { "bit 4,c", A_0 }, /* cb61 */ + { "bit 4,d", A_0 }, /* cb62 */ + { "bit 4,e", A_0 }, /* cb63 */ + { "bit 4,h", A_0 }, /* cb64 */ + { "bit 4,l", A_0 }, /* cb65 */ + { "bit 4,(hl)", A_0 }, /* cb66 */ + { "bit 4,a", A_0 }, /* cb67 */ + + { "bit 5,b", A_0 }, /* cb68 */ + { "bit 5,c", A_0 }, /* cb69 */ + { "bit 5,d", A_0 }, /* cb6a */ + { "bit 5,e", A_0 }, /* cb6b */ + { "bit 5,h", A_0 }, /* cb6c */ + { "bit 5,l", A_0 }, /* cb6d */ + { "bit 5,(hl)", A_0 }, /* cb6e */ + { "bit 5,a", A_0 }, /* cb6f */ + + { "bit 6,b", A_0 }, /* cb70 */ + { "bit 6,c", A_0 }, /* cb71 */ + { "bit 6,d", A_0 }, /* cb72 */ + { "bit 6,e", A_0 }, /* cb73 */ + { "bit 6,h", A_0 }, /* cb74 */ + { "bit 6,l", A_0 }, /* cb75 */ + { "bit 6,(hl)", A_0 }, /* cb76 */ + { "bit 6,a", A_0 }, /* cb77 */ + + { "bit 7,b", A_0 }, /* cb78 */ + { "bit 7,c", A_0 }, /* cb79 */ + { "bit 7,d", A_0 }, /* cb7a */ + { "bit 7,e", A_0 }, /* cb7b */ + { "bit 7,h", A_0 }, /* cb7c */ + { "bit 7,l", A_0 }, /* cb7d */ + { "bit 7,(hl)", A_0 }, /* cb7e */ + { "bit 7,a", A_0 }, /* cb7f */ + + { "res 0,b", A_0 }, /* cb80 */ + { "res 0,c", A_0 }, /* cb81 */ + { "res 0,d", A_0 }, /* cb82 */ + { "res 0,e", A_0 }, /* cb83 */ + { "res 0,h", A_0 }, /* cb84 */ + { "res 0,l", A_0 }, /* cb85 */ + { "res 0,(hl)", A_0 }, /* cb86 */ + { "res 0,a", A_0 }, /* cb87 */ + + { "res 1,b", A_0 }, /* cb88 */ + { "res 1,c", A_0 }, /* cb89 */ + { "res 1,d", A_0 }, /* cb8a */ + { "res 1,e", A_0 }, /* cb8b */ + { "res 1,h", A_0 }, /* cb8c */ + { "res 1,l", A_0 }, /* cb8d */ + { "res 1,(hl)", A_0 }, /* cb8e */ + { "res 1,a", A_0 }, /* cb8f */ + + { "res 2,b", A_0 }, /* cb90 */ + { "res 2,c", A_0 }, /* cb91 */ + { "res 2,d", A_0 }, /* cb92 */ + { "res 2,e", A_0 }, /* cb93 */ + { "res 2,h", A_0 }, /* cb94 */ + { "res 2,l", A_0 }, /* cb95 */ + { "res 2,(hl)", A_0 }, /* cb96 */ + { "res 2,a", A_0 }, /* cb97 */ + + { "res 3,b", A_0 }, /* cb98 */ + { "res 3,c", A_0 }, /* cb99 */ + { "res 3,d", A_0 }, /* cb9a */ + { "res 3,e", A_0 }, /* cb9b */ + { "res 3,h", A_0 }, /* cb9c */ + { "res 3,l", A_0 }, /* cb9d */ + { "res 3,(hl)", A_0 }, /* cb9e */ + { "res 3,a", A_0 }, /* cb9f */ + + { "res 4,b", A_0 }, /* cba0 */ + { "res 4,c", A_0 }, /* cba1 */ + { "res 4,d", A_0 }, /* cba2 */ + { "res 4,e", A_0 }, /* cba3 */ + { "res 4,h", A_0 }, /* cba4 */ + { "res 4,l", A_0 }, /* cba5 */ + { "res 4,(hl)", A_0 }, /* cba6 */ + { "res 4,a", A_0 }, /* cba7 */ + + { "res 5,b", A_0 }, /* cba8 */ + { "res 5,c", A_0 }, /* cba9 */ + { "res 5,d", A_0 }, /* cbaa */ + { "res 5,e", A_0 }, /* cbab */ + { "res 5,h", A_0 }, /* cbac */ + { "res 5,l", A_0 }, /* cbad */ + { "res 5,(hl)", A_0 }, /* cbae */ + { "res 5,a", A_0 }, /* cbaf */ + + { "res 6,b", A_0 }, /* cbb0 */ + { "res 6,c", A_0 }, /* cbb1 */ + { "res 6,d", A_0 }, /* cbb2 */ + { "res 6,e", A_0 }, /* cbb3 */ + { "res 6,h", A_0 }, /* cbb4 */ + { "res 6,l", A_0 }, /* cbb5 */ + { "res 6,(hl)", A_0 }, /* cbb6 */ + { "res 6,a", A_0 }, /* cbb7 */ + + { "res 7,b", A_0 }, /* cbb8 */ + { "res 7,c", A_0 }, /* cbb9 */ + { "res 7,d", A_0 }, /* cbba */ + { "res 7,e", A_0 }, /* cbbb */ + { "res 7,h", A_0 }, /* cbbc */ + { "res 7,l", A_0 }, /* cbbd */ + { "res 7,(hl)", A_0 }, /* cbbe */ + { "res 7,a", A_0 }, /* cbbf */ + + { "set 0,b", A_0 }, /* cbc0 */ + { "set 0,c", A_0 }, /* cbc1 */ + { "set 0,d", A_0 }, /* cbc2 */ + { "set 0,e", A_0 }, /* cbc3 */ + { "set 0,h", A_0 }, /* cbc4 */ + { "set 0,l", A_0 }, /* cbc5 */ + { "set 0,(hl)", A_0 }, /* cbc6 */ + { "set 0,a", A_0 }, /* cbc7 */ + + { "set 1,b", A_0 }, /* cbc8 */ + { "set 1,c", A_0 }, /* cbc9 */ + { "set 1,d", A_0 }, /* cbca */ + { "set 1,e", A_0 }, /* cbcb */ + { "set 1,h", A_0 }, /* cbcc */ + { "set 1,l", A_0 }, /* cbcd */ + { "set 1,(hl)", A_0 }, /* cbce */ + { "set 1,a", A_0 }, /* cbcf */ + + { "set 2,b", A_0 }, /* cbd0 */ + { "set 2,c", A_0 }, /* cbd1 */ + { "set 2,d", A_0 }, /* cbd2 */ + { "set 2,e", A_0 }, /* cbd3 */ + { "set 2,h", A_0 }, /* cbd4 */ + { "set 2,l", A_0 }, /* cbd5 */ + { "set 2,(hl)", A_0 }, /* cbd6 */ + { "set 2,a", A_0 }, /* cbd7 */ + + { "set 3,b", A_0 }, /* cbd8 */ + { "set 3,c", A_0 }, /* cbd9 */ + { "set 3,d", A_0 }, /* cbda */ + { "set 3,e", A_0 }, /* cbdb */ + { "set 3,h", A_0 }, /* cbdc */ + { "set 3,l", A_0 }, /* cbdd */ + { "set 3,(hl)", A_0 }, /* cbde */ + { "set 3,a", A_0 }, /* cbdf */ + + { "set 4,b", A_0 }, /* cbe0 */ + { "set 4,c", A_0 }, /* cbe1 */ + { "set 4,d", A_0 }, /* cbe2 */ + { "set 4,e", A_0 }, /* cbe3 */ + { "set 4,h", A_0 }, /* cbe4 */ + { "set 4,l", A_0 }, /* cbe5 */ + { "set 4,(hl)", A_0 }, /* cbe6 */ + { "set 4,a", A_0 }, /* cbe7 */ + + { "set 5,b", A_0 }, /* cbe8 */ + { "set 5,c", A_0 }, /* cbe9 */ + { "set 5,d", A_0 }, /* cbea */ + { "set 5,e", A_0 }, /* cbeb */ + { "set 5,h", A_0 }, /* cbec */ + { "set 5,l", A_0 }, /* cbed */ + { "set 5,(hl)", A_0 }, /* cbee */ + { "set 5,a", A_0 }, /* cbef */ + + { "set 6,b", A_0 }, /* cbf0 */ + { "set 6,c", A_0 }, /* cbf1 */ + { "set 6,d", A_0 }, /* cbf2 */ + { "set 6,e", A_0 }, /* cbf3 */ + { "set 6,h", A_0 }, /* cbf4 */ + { "set 6,l", A_0 }, /* cbf5 */ + { "set 6,(hl)", A_0 }, /* cbf6 */ + { "set 6,a", A_0 }, /* cbf7 */ + + { "set 7,b", A_0 }, /* cbf8 */ + { "set 7,c", A_0 }, /* cbf9 */ + { "set 7,d", A_0 }, /* cbfa */ + { "set 7,e", A_0 }, /* cbfb */ + { "set 7,h", A_0 }, /* cbfc */ + { "set 7,l", A_0 }, /* cbfd */ + { "set 7,(hl)", A_0 }, /* cbfe */ + { "set 7,a", A_0 }, /* cbff */ + }, + { /* dd */ + { undefined, A_0B }, /* dd00 */ + { undefined, A_0B }, /* dd01 */ + { undefined, A_0B }, /* dd02 */ + { undefined, A_0B }, /* dd03 */ + { undefined, A_0B }, /* dd04 */ + { undefined, A_0B }, /* dd05 */ + { undefined, A_0B }, /* dd06 */ + { undefined, A_0B }, /* dd07 */ + + { undefined, A_0B }, /* dd08 */ + { "add ix,bc", A_0 }, /* dd09 */ + { undefined, A_0B }, /* dd0a */ + { undefined, A_0B }, /* dd0b */ + { undefined, A_0B }, /* dd0c */ + { undefined, A_0B }, /* dd0d */ + { undefined, A_0B }, /* dd0e */ + { undefined, A_0B }, /* dd0f */ + + { undefined, A_0B }, /* dd10 */ + { undefined, A_0B }, /* dd11 */ + { undefined, A_0B }, /* dd12 */ + { undefined, A_0B }, /* dd13 */ + { undefined, A_0B }, /* dd14 */ + { undefined, A_0B }, /* dd15 */ + { undefined, A_0B }, /* dd16 */ + { undefined, A_0B }, /* dd17 */ + + { undefined, A_0B }, /* dd18 */ + { "add ix,de", A_0 }, /* dd19 */ + { undefined, A_0B }, /* dd1a */ + { undefined, A_0B }, /* dd1b */ + { undefined, A_0B }, /* dd1c */ + { undefined, A_0B }, /* dd1d */ + { undefined, A_0B }, /* dd1e */ + { undefined, A_0B }, /* dd1f */ + + { undefined, A_0B }, /* dd20 */ + { "ld ix,%02x%02xh", A_16 }, /* dd21 */ + { "ld (%02x%02xh),ix",A_16 }, /* dd22 */ + { "inc ix", A_0 }, /* dd23 */ + { "inc ixh", A_0 }, /* dd24 [undoc] */ + { "dec ixh", A_0 }, /* dd25 [undoc] */ + { "ld ixh,%02xh", A_8 }, /* dd26 [undoc] */ + { undefined, A_0B }, /* dd27 */ + + { undefined, A_0B }, /* dd28 */ + { "add ix,ix", A_0 }, /* dd29 */ + { "ld ix,(%02x%02xh)",A_16 }, /* dd2a */ + { "dec ix", A_0 }, /* dd2b */ + { "inc ixl", A_0 }, /* dd2c [undoc] */ + { "dec ixl", A_0 }, /* dd2d [undoc] */ + { "ld ixl,%02xh", A_8 }, /* dd2e [undoc] */ + { undefined, A_0B }, /* dd2f */ + + { undefined, A_0B }, /* dd30 */ + { undefined, A_0B }, /* dd31 */ + { undefined, A_0B }, /* dd32 */ + { undefined, A_0B }, /* dd33 */ + { "inc (ix+%02xh)", A_8 }, /* dd34 */ + { "dec (ix+%02xh)", A_8 }, /* dd35 */ + { "ld (ix+%02xh),%02xh",A_8X2 }, /* dd36 */ + { undefined, A_0B }, /* dd37 */ + + { undefined, A_0B }, /* dd38 */ + { "add ix,sp", A_0 }, /* dd39 */ + { undefined, A_0B }, /* dd3a */ + { undefined, A_0B }, /* dd3b */ + { undefined, A_0B }, /* dd3c */ + { undefined, A_0B }, /* dd3d */ + { undefined, A_0B }, /* dd3e */ + { undefined, A_0B }, /* dd3f */ + + { undefined, A_0B }, /* dd40 */ + { undefined, A_0B }, /* dd41 */ + { undefined, A_0B }, /* dd42 */ + { undefined, A_0B }, /* dd43 */ + { "ld b,ixh", A_0 }, /* dd44 [undoc] */ + { "ld b,ixl", A_0 }, /* dd45 [undoc] */ + { "ld b,(ix+%02xh)", A_8 }, /* dd46 */ + { undefined, A_0B }, /* dd47 */ + + { undefined, A_0B }, /* dd48 */ + { undefined, A_0B }, /* dd49 */ + { undefined, A_0B }, /* dd4a */ + { undefined, A_0B }, /* dd4b */ + { "ld c,ixh", A_0 }, /* dd4c [undoc] */ + { "ld c,ixl", A_0 }, /* dd4d [undoc] */ + { "ld c,(ix+%02xh)", A_8 }, /* dd4e */ + { undefined, A_0B }, /* dd4f */ + + { undefined, A_0B }, /* dd50 */ + { undefined, A_0B }, /* dd51 */ + { undefined, A_0B }, /* dd52 */ + { undefined, A_0B }, /* dd53 */ + { "ld d,ixh", A_0 }, /* dd54 [undoc] */ + { "ld d,ixl", A_0 }, /* dd55 [undoc] */ + { "ld d,(ix+%02xh)", A_8 }, /* dd56 */ + { undefined, A_0B }, /* dd57 */ + + { undefined, A_0B }, /* dd58 */ + { undefined, A_0B }, /* dd59 */ + { undefined, A_0B }, /* dd5a */ + { undefined, A_0B }, /* dd5b */ + { "ld e,ixh", A_0 }, /* dd5c [undoc] */ + { "ld e,ixl", A_0 }, /* dd5d [undoc] */ + { "ld e,(ix+%02xh)", A_8 }, /* dd5e */ + { undefined, A_0B }, /* dd5f */ + + { "ld ixh,b", A_0 }, /* dd60 [undoc] */ + { "ld ixh,c", A_0 }, /* dd61 [undoc] */ + { "ld ixh,d", A_0 }, /* dd62 [undoc] */ + { "ld ixh,e", A_0 }, /* dd63 [undoc] */ + { "ld ixh,ixh", A_0 }, /* dd64 [undoc] */ + { "ld ixh,ixl", A_0 }, /* dd65 [undoc] */ + { "ld h,(ix+%02xh)", A_8 }, /* dd66 */ + { "ld ixh,a", A_0 }, /* dd67 [undoc] */ + + { "ld ixl,b", A_0 }, /* dd68 [undoc] */ + { "ld ixl,c", A_0 }, /* dd69 [undoc] */ + { "ld ixl,d", A_0 }, /* dd6a [undoc] */ + { "ld ixl,e", A_0 }, /* dd6b [undoc] */ + { "ld ixl,ixh", A_0 }, /* dd6c [undoc] */ + { "ld ixl,ixl", A_0 }, /* dd6d [undoc] */ + { "ld l,(ix+%02xh)", A_8 }, /* dd6e */ + { "ld ixl,a", A_0 }, /* dd6f [undoc] */ + + { "ld (ix+%02xh),b", A_8 }, /* dd70 */ + { "ld (ix+%02xh),c", A_8 }, /* dd71 */ + { "ld (ix+%02xh),d", A_8 }, /* dd72 */ + { "ld (ix+%02xh),e", A_8 }, /* dd73 */ + { "ld (ix+%02xh),h", A_8 }, /* dd74 */ + { "ld (ix+%02xh),l", A_8 }, /* dd75 */ + { undefined, A_0B }, /* dd76 */ + { "ld (ix+%02xh),a", A_8 }, /* dd77 */ + + { undefined, A_0B }, /* dd78 */ + { undefined, A_0B }, /* dd79 */ + { undefined, A_0B }, /* dd7a */ + { undefined, A_0B }, /* dd7b */ + { "ld a,ixh", A_0 }, /* dd7c [undoc] */ + { "ld a,ixl", A_0 }, /* dd7d [undoc] */ + { "ld a,(ix+%02xh)", A_8 }, /* dd7e */ + { undefined, A_0B }, /* dd7f */ + + { undefined, A_0B }, /* dd80 */ + { undefined, A_0B }, /* dd81 */ + { undefined, A_0B }, /* dd82 */ + { undefined, A_0B }, /* dd83 */ + { "add a,ixh", A_0 }, /* dd84 [undoc] */ + { "add a,ixl", A_0 }, /* dd85 [undoc] */ + { "add a,(ix+%02xh)", A_8 }, /* dd86 */ + { undefined, A_0B }, /* dd87 */ + + { undefined, A_0B }, /* dd88 */ + { undefined, A_0B }, /* dd89 */ + { undefined, A_0B }, /* dd8a */ + { undefined, A_0B }, /* dd8b */ + { "adc a,ixh", A_0 }, /* dd8c [undoc] */ + { "adc a,ixl", A_0 }, /* dd8d [undoc] */ + { "adc a,(ix+%02xh)", A_8 }, /* dd8e */ + { undefined, A_0B }, /* dd8f */ + + { undefined, A_0B }, /* dd90 */ + { undefined, A_0B }, /* dd91 */ + { undefined, A_0B }, /* dd92 */ + { undefined, A_0B }, /* dd93 */ + { "sub ixh", A_0 }, /* dd94 [undoc] */ + { "sub ixl", A_0 }, /* dd95 [undoc] */ + { "sub (ix+%02xh)", A_8 }, /* dd96 */ + { undefined, A_0B }, /* dd97 */ + + { undefined, A_0B }, /* dd98 */ + { undefined, A_0B }, /* dd99 */ + { undefined, A_0B }, /* dd9a */ + { undefined, A_0B }, /* dd9b */ + { "sbc ixh", A_0 }, /* dd9c [undoc] */ + { "sbc ixl", A_0 }, /* dd9d [undoc] */ + { "sbc a,(ix+%02xh)", A_8 }, /* dd9e */ + { undefined, A_0B }, /* dd9f */ + + { undefined, A_0B }, /* dda0 */ + { undefined, A_0B }, /* dda1 */ + { undefined, A_0B }, /* dda2 */ + { undefined, A_0B }, /* dda3 */ + { "and ixh", A_0 }, /* dda4 [undoc] */ + { "and ixl", A_0 }, /* dda5 [undoc] */ + { "and (ix+%02xh)", A_8 }, /* dda6 */ + { undefined, A_0B }, /* dda7 */ + + { undefined, A_0B }, /* dda8 */ + { undefined, A_0B }, /* dda9 */ + { undefined, A_0B }, /* ddaa */ + { undefined, A_0B }, /* ddab */ + { "xor ixh", A_0 }, /* ddac [undoc] */ + { "xor ixl", A_0 }, /* ddad [undoc] */ + { "xor (ix+%02xh)", A_8 }, /* ddae */ + { undefined, A_0B }, /* ddaf */ + + { undefined, A_0B }, /* ddb0 */ + { undefined, A_0B }, /* ddb1 */ + { undefined, A_0B }, /* ddb2 */ + { undefined, A_0B }, /* ddb3 */ + { "or ixh", A_0 }, /* ddb4 [undoc] */ + { "or ixl", A_0 }, /* ddb5 [undoc] */ + { "or (ix+%02xh)", A_8 }, /* ddb6 */ + { undefined, A_0B }, /* ddb7 */ + + { undefined, A_0B }, /* ddb8 */ + { undefined, A_0B }, /* ddb9 */ + { undefined, A_0B }, /* ddba */ + { undefined, A_0B }, /* ddbb */ + { "cp ixh", A_0 }, /* ddbc [undoc] */ + { "cp ixl", A_0 }, /* ddbd [undoc] */ + { "cp (ix+%02xh)", A_8 }, /* ddbe */ + { undefined, A_0B }, /* ddbf */ + + { undefined, A_0B }, /* ddc0 */ + { undefined, A_0B }, /* ddc1 */ + { undefined, A_0B }, /* ddc2 */ + { undefined, A_0B }, /* ddc3 */ + { undefined, A_0B }, /* ddc4 */ + { undefined, A_0B }, /* ddc5 */ + { undefined, A_0B }, /* ddc6 */ + { undefined, A_0B }, /* ddc7 */ + + { undefined, A_0B }, /* ddc8 */ + { undefined, A_0B }, /* ddc9 */ + { undefined, A_0B }, /* ddca */ + { 0, 4 }, /* ddcb */ + { undefined, A_0B }, /* ddcc */ + { undefined, A_0B }, /* ddcd */ + { undefined, A_0B }, /* ddce */ + { undefined, A_0B }, /* ddcf */ + + { undefined, A_0B }, /* ddd0 */ + { undefined, A_0B }, /* ddd1 */ + { undefined, A_0B }, /* ddd2 */ + { undefined, A_0B }, /* ddd3 */ + { undefined, A_0B }, /* ddd4 */ + { undefined, A_0B }, /* ddd5 */ + { undefined, A_0B }, /* ddd6 */ + { undefined, A_0B }, /* ddd7 */ + + { undefined, A_0B }, /* ddd8 */ + { undefined, A_0B }, /* ddd9 */ + { undefined, A_0B }, /* ddda */ + { undefined, A_0B }, /* dddb */ + { undefined, A_0B }, /* dddc */ + { undefined, A_0B }, /* dddd */ + { undefined, A_0B }, /* ddde */ + { undefined, A_0B }, /* dddf */ + + { undefined, A_0B }, /* dde0 */ + { "pop ix", A_0 }, /* dde1 */ + { undefined, A_0B }, /* dde2 */ + { "ex (sp),ix", A_0 }, /* dde3 */ + { undefined, A_0B }, /* dde4 */ + { "push ix", A_0 }, /* dde5 */ + { undefined, A_0B }, /* dde6 */ + { undefined, A_0B }, /* dde7 */ + + { undefined, A_0B }, /* dde8 */ + { "jp (ix)", A_0 }, /* dde9 */ + { undefined, A_0B }, /* ddea */ + { undefined, A_0B }, /* ddeb */ + { undefined, A_0B }, /* ddec */ + { undefined, A_0B }, /* dded */ + { undefined, A_0B }, /* ddee */ + { undefined, A_0B }, /* ddef */ + + { undefined, A_0B }, /* ddf0 */ + { undefined, A_0B }, /* ddf1 */ + { undefined, A_0B }, /* ddf2 */ + { undefined, A_0B }, /* ddf3 */ + { undefined, A_0B }, /* ddf4 */ + { undefined, A_0B }, /* ddf5 */ + { undefined, A_0B }, /* ddf6 */ + { undefined, A_0B }, /* ddf7 */ + + { undefined, A_0B }, /* ddf8 */ + { "ld sp,ix", A_0 }, /* ddf9 */ + { undefined, A_0B }, /* ddfa */ + { undefined, A_0B }, /* ddfb */ + { undefined, A_0B }, /* ddfc */ + { undefined, A_0B }, /* ddfd */ + { undefined, A_0B }, /* ddfe */ + { undefined, A_0B }, /* ddff */ + }, + { /* ed */ + { undefined, A_0 }, /* ed00 */ + { undefined, A_0 }, /* ed01 */ + { undefined, A_0 }, /* ed02 */ + { undefined, A_0 }, /* ed03 */ + { undefined, A_0 }, /* ed04 */ + { undefined, A_0 }, /* ed05 */ + { undefined, A_0 }, /* ed06 */ + { undefined, A_0 }, /* ed07 */ + + { undefined, A_0 }, /* ed08 */ + { undefined, A_0 }, /* ed09 */ + { undefined, A_0 }, /* ed0a */ + { undefined, A_0 }, /* ed0b */ + { undefined, A_0 }, /* ed0c */ + { undefined, A_0 }, /* ed0d */ + { undefined, A_0 }, /* ed0e */ + { undefined, A_0 }, /* ed0f */ + + { undefined, A_0 }, /* ed10 */ + { undefined, A_0 }, /* ed11 */ + { undefined, A_0 }, /* ed12 */ + { undefined, A_0 }, /* ed13 */ + { undefined, A_0 }, /* ed14 */ + { undefined, A_0 }, /* ed15 */ + { undefined, A_0 }, /* ed16 */ + { undefined, A_0 }, /* ed17 */ + + { undefined, A_0 }, /* ed18 */ + { undefined, A_0 }, /* ed19 */ + { undefined, A_0 }, /* ed1a */ + { undefined, A_0 }, /* ed1b */ + { undefined, A_0 }, /* ed1c */ + { undefined, A_0 }, /* ed1d */ + { undefined, A_0 }, /* ed1e */ + { undefined, A_0 }, /* ed1f */ + + { undefined, A_0 }, /* ed20 [vavasour emt] */ + { undefined, A_0 }, /* ed21 [vavasour emt] */ + { undefined, A_0 }, /* ed22 */ + { undefined, A_0 }, /* ed23 */ + { undefined, A_0 }, /* ed24 */ + { undefined, A_0 }, /* ed25 */ + { undefined, A_0 }, /* ed26 */ + { undefined, A_0 }, /* ed27 */ + + /* xtrs emulator traps; not real Z80 instructions */ + { "emt_system", A_0 }, /* ed28 */ + { "emt_mouse", A_0 }, /* ed29 */ + { "emt_getdir", A_0 }, /* ed2a */ + { "emt_setdir", A_0 }, /* ed2b */ + { undefined, A_0 }, /* ed2c */ + { undefined, A_0 }, /* ed2d */ + { undefined, A_0 }, /* ed2e */ + { "emt_debug", A_0 }, /* ed2f */ + + { "emt_open", A_0 }, /* ed30 */ + { "emt_close", A_0 }, /* ed31 */ + { "emt_read", A_0 }, /* ed32 */ + { "emt_write", A_0 }, /* ed33 */ + { "emt_lseek", A_0 }, /* ed34 */ + { "emt_strerror", A_0 }, /* ed35 */ + { "emt_time", A_0 }, /* ed36 */ + { "emt_opendir", A_0 }, /* ed37 */ + + { "emt_closedir", A_0 }, /* ed38 */ + { "emt_readdir", A_0 }, /* ed39 */ + { "emt_chdir", A_0 }, /* ed3a */ + { "emt_getcwd", A_0 }, /* ed3b */ + { "emt_misc", A_0 }, /* ed3c */ + { "emt_ftruncate", A_0 }, /* ed3d */ + { "emt_opendisk", A_0 }, /* ed3e */ + { "emt_closedisk", A_0 }, /* ed3f */ + /* end xtrs emulator traps */ + + { "in b,(c)", A_0 }, /* ed40 */ + { "out (c),b", A_0 }, /* ed41 */ + { "sbc hl,bc", A_0 }, /* ed42 */ + { "ld (%02x%02xh),bc",A_16 }, /* ed43 */ + { "neg", A_0 }, /* ed44 */ + { "retn", A_0 }, /* ed45 */ + { "im 0", A_0 }, /* ed46 */ + { "ld i,a", A_0 }, /* ed47 */ + + { "in c,(c)", A_0 }, /* ed48 */ + { "out (c),c", A_0 }, /* ed49 */ + { "adc hl,bc", A_0 }, /* ed4a */ + { "ld bc,(%02x%02xh)",A_16 }, /* ed4b */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed4c [undoc] */ + { "reti", A_0 }, /* ed4d */ + { undefined, A_0 }, /* ed4e */ + { "ld r,a", A_0 }, /* ed4f */ + + { "in d,(c)", A_0 }, /* ed50 */ + { "out (c),d", A_0 }, /* ed51 */ + { "sbc hl,de", A_0 }, /* ed52 */ + { "ld (%02x%02xh),de",A_16 }, /* ed53 */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed54 [undoc] */ + { "ret\t\t\t;undoc equiv",A_0 }, /* ed55 [undoc] */ + { "im 1", A_0 }, /* ed56 */ + { "ld a,i", A_0 }, /* ed57 */ + + { "in e,(c)", A_0 }, /* ed58 */ + { "out (c),e", A_0 }, /* ed59 */ + { "adc hl,de", A_0 }, /* ed5a */ + { "ld de,(%02x%02xh)",A_16 }, /* ed5b */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed5c [undoc] */ + { "ret\t\t\t;undoc equiv",A_0 }, /* ed5d [undoc] */ + { "im 2", A_0 }, /* ed5e */ + { "ld a,r", A_0 }, /* ed5f */ + + { "in h,(c)", A_0 }, /* ed60 */ + { "out (c),h", A_0 }, /* ed61 */ + { "sbc hl,hl", A_0 }, /* ed62 */ + { "ld (%02x%02xh),hl\t;undoc equiv",A_16 }, + /* ed63 [semi-documented] */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed64 [undoc] */ + { "ret\t\t\t;undoc equiv",A_0 }, /* ed65 [undoc] */ + { "im\t0\t\t;undoc equiv",A_0 }, /* ed66 [undoc] */ + { "rrd", A_0 }, /* ed67 */ + + { "in l,(c)", A_0 }, /* ed68 */ + { "out (c),l", A_0 }, /* ed69 */ + { "adc hl,hl", A_0 }, /* ed6a */ + { "ld hl,(%02x%02xh)\t;undoc equiv",A_16 }, + /* ed6b [semi-documented] */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed6c [undoc] */ + { "ret\t\t\t;undoc equiv",A_0 }, /* ed6d [undoc] */ + { undefined, A_0 }, /* ed6e */ + { "rld", A_0 }, /* ed6f */ + + { "in (c)", A_0 }, /* ed70 [undoc] */ + { "out (c),0", A_0 }, /* ed71 [undoc] */ + { "sbc hl,sp", A_0 }, /* ed72 */ + { "ld (%02x%02xh),sp",A_16 }, /* ed73 */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed74 [undoc] */ + { "ret\t\t\t;undoc equiv",A_0 }, /* ed75 [undoc] */ + { "im\t1\t\t;undoc equiv",A_0 }, /* ed76 [undoc] */ + { undefined, A_0 }, /* ed77 */ + + { "in a,(c)", A_0 }, /* ed78 */ + { "out (c),a", A_0 }, /* ed79 */ + { "adc hl,sp", A_0 }, /* ed7a */ + { "ld sp,(%02x%02xh)",A_16 }, /* ed7b */ + { "neg\t\t\t;undoc equiv",A_0 }, /* ed7c [undoc] */ + { "ret\t\t\t;undoc equiv",A_0 }, /* ed7d [undoc] */ + { "im\t2\t\t;undoc equiv",A_0 }, /* ed7e [undoc] */ + { undefined, A_0 }, /* ed7f */ + + { undefined, A_0 }, /* ed80 */ + { undefined, A_0 }, /* ed81 */ + { undefined, A_0 }, /* ed82 */ + { undefined, A_0 }, /* ed83 */ + { undefined, A_0 }, /* ed84 */ + { undefined, A_0 }, /* ed85 */ + { undefined, A_0 }, /* ed86 */ + { undefined, A_0 }, /* ed87 */ + + { undefined, A_0 }, /* ed88 */ + { undefined, A_0 }, /* ed89 */ + { undefined, A_0 }, /* ed8a */ + { undefined, A_0 }, /* ed8b */ + { undefined, A_0 }, /* ed8c */ + { undefined, A_0 }, /* ed8d */ + { undefined, A_0 }, /* ed8e */ + { undefined, A_0 }, /* ed8f */ + + { undefined, A_0 }, /* ed90 */ + { undefined, A_0 }, /* ed91 */ + { undefined, A_0 }, /* ed92 */ + { undefined, A_0 }, /* ed93 */ + { undefined, A_0 }, /* ed94 */ + { undefined, A_0 }, /* ed95 */ + { undefined, A_0 }, /* ed96 */ + { undefined, A_0 }, /* ed97 */ + + { undefined, A_0 }, /* ed98 */ + { undefined, A_0 }, /* ed99 */ + { undefined, A_0 }, /* ed9a */ + { undefined, A_0 }, /* ed9b */ + { undefined, A_0 }, /* ed9c */ + { undefined, A_0 }, /* ed9d */ + { undefined, A_0 }, /* ed9e */ + { undefined, A_0 }, /* ed9f */ + + { "ldi", A_0 }, /* eda0 */ + { "cpi", A_0 }, /* eda1 */ + { "ini", A_0 }, /* eda2 */ + { "outi", A_0 }, /* eda3 */ + { undefined, A_0 }, /* eda4 */ + { undefined, A_0 }, /* eda5 */ + { undefined, A_0 }, /* eda6 */ + { undefined, A_0 }, /* eda7 */ + + { "ldd", A_0 }, /* eda8 */ + { "cpd", A_0 }, /* eda9 */ + { "ind", A_0 }, /* edaa */ + { "outd", A_0 }, /* edab */ + { undefined, A_0 }, /* edac */ + { undefined, A_0 }, /* edad */ + { undefined, A_0 }, /* edae */ + { undefined, A_0 }, /* edaf */ + + { "ldir", A_0 }, /* edb0 */ + { "cpir", A_0 }, /* edb1 */ + { "inir", A_0 }, /* edb2 */ + { "otir", A_0 }, /* edb3 */ + { undefined, A_0 }, /* edb4 */ + { undefined, A_0 }, /* edb5 */ + { undefined, A_0 }, /* edb6 */ + { undefined, A_0 }, /* edb7 */ + + { "lddr", A_0 }, /* edb8 */ + { "cpdr", A_0 }, /* edb9 */ + { "indr", A_0 }, /* edba */ + { "otdr", A_0 }, /* edbb */ + { undefined, A_0 }, /* edbc */ + { undefined, A_0 }, /* edbd */ + { undefined, A_0 }, /* edbe */ + { undefined, A_0 }, /* edbf */ + + { undefined, A_0 }, /* edc0 */ + { undefined, A_0 }, /* edc1 */ + { undefined, A_0 }, /* edc2 */ + { undefined, A_0 }, /* edc3 */ + { undefined, A_0 }, /* edc4 */ + { undefined, A_0 }, /* edc5 */ + { undefined, A_0 }, /* edc6 */ + { undefined, A_0 }, /* edc7 */ + + { undefined, A_0 }, /* edc8 */ + { undefined, A_0 }, /* edc9 */ + { undefined, A_0 }, /* edca */ + { undefined, A_0 }, /* edcb */ + { undefined, A_0 }, /* edcc */ + { undefined, A_0 }, /* edcd */ + { undefined, A_0 }, /* edce */ + { undefined, A_0 }, /* edcf */ + + { undefined, A_0 }, /* edd0 */ + { undefined, A_0 }, /* edd1 */ + { undefined, A_0 }, /* edd2 */ + { undefined, A_0 }, /* edd3 */ + { undefined, A_0 }, /* edd4 */ + { undefined, A_0 }, /* edd5 */ + { undefined, A_0 }, /* edd6 */ + { undefined, A_0 }, /* edd7 */ + + { undefined, A_0 }, /* edd8 */ + { undefined, A_0 }, /* edd9 */ + { undefined, A_0 }, /* edda */ + { undefined, A_0 }, /* eddb */ + { undefined, A_0 }, /* eddc */ + { undefined, A_0 }, /* eddd */ + { undefined, A_0 }, /* edde */ + { undefined, A_0 }, /* eddf */ + + { undefined, A_0 }, /* ede0 */ + { undefined, A_0 }, /* ede1 */ + { undefined, A_0 }, /* ede2 */ + { undefined, A_0 }, /* ede3 */ + { undefined, A_0 }, /* ede4 */ + { undefined, A_0 }, /* ede5 */ + { undefined, A_0 }, /* ede6 */ + { undefined, A_0 }, /* ede7 */ + + { undefined, A_0 }, /* ede8 */ + { undefined, A_0 }, /* ede9 */ + { undefined, A_0 }, /* edea */ + { undefined, A_0 }, /* edeb */ + { undefined, A_0 }, /* edec */ + { undefined, A_0 }, /* eded */ + { undefined, A_0 }, /* edee */ + { undefined, A_0 }, /* edef */ + + { undefined, A_0 }, /* edf0 */ + { undefined, A_0 }, /* edf1 */ + { undefined, A_0 }, /* edf2 */ + { undefined, A_0 }, /* edf3 */ + { undefined, A_0 }, /* edf4 */ + { undefined, A_0 }, /* edf5 */ + { undefined, A_0 }, /* edf6 */ + { undefined, A_0 }, /* edf7 */ + + { undefined, A_0 }, /* edf8 */ + { undefined, A_0 }, /* edf9 */ + { undefined, A_0 }, /* edfa */ + { undefined, A_0 }, /* edfb */ + { undefined, A_0 }, /* edfc */ + { undefined, A_0 }, /* edfd */ + { undefined, A_0 }, /* edfe */ + { undefined, A_0 }, /* edff */ + }, + { /* fd */ + { undefined, A_0B }, /* fd00 */ + { undefined, A_0B }, /* fd01 */ + { undefined, A_0B }, /* fd02 */ + { undefined, A_0B }, /* fd03 */ + { undefined, A_0B }, /* fd04 */ + { undefined, A_0B }, /* fd05 */ + { undefined, A_0B }, /* fd06 */ + { undefined, A_0B }, /* fd07 */ + + { undefined, A_0B }, /* fd08 */ + { "add iy,bc", A_0 }, /* fd09 */ + { undefined, A_0B }, /* fd0a */ + { undefined, A_0B }, /* fd0b */ + { undefined, A_0B }, /* fd0c */ + { undefined, A_0B }, /* fd0d */ + { undefined, A_0B }, /* fd0e */ + { undefined, A_0B }, /* fd0f */ + + { undefined, A_0B }, /* fd10 */ + { undefined, A_0B }, /* fd11 */ + { undefined, A_0B }, /* fd12 */ + { undefined, A_0B }, /* fd13 */ + { undefined, A_0B }, /* fd14 */ + { undefined, A_0B }, /* fd15 */ + { undefined, A_0B }, /* fd16 */ + { undefined, A_0B }, /* fd17 */ + + { undefined, A_0B }, /* fd18 */ + { "add iy,de", A_0 }, /* fd19 */ + { undefined, A_0B }, /* fd1a */ + { undefined, A_0B }, /* fd1b */ + { undefined, A_0B }, /* fd1c */ + { undefined, A_0B }, /* fd1d */ + { undefined, A_0B }, /* fd1e */ + { undefined, A_0B }, /* fd1f */ + + { undefined, A_0B }, /* fd20 */ + { "ld iy,%02x%02xh", A_16 }, /* fd21 */ + { "ld (%02x%02xh),iy",A_16 }, /* fd22 */ + { "inc iy", A_0 }, /* fd23 */ + { undefined, A_0B }, /* fd24 */ + { undefined, A_0B }, /* fd25 */ + { undefined, A_0B }, /* fd26 */ + { undefined, A_0B }, /* fd27 */ + + { undefined, A_0B }, /* fd28 */ + { "add iy,iy", A_0 }, /* fd29 */ + { "ld iy,(%02x%02xh)",A_16 }, /* fd2a */ + { "dec iy", A_0 }, /* fd2b */ + { undefined, A_0B }, /* fd2c */ + { undefined, A_0B }, /* fd2d */ + { undefined, A_0B }, /* fd2e */ + { undefined, A_0B }, /* fd2f */ + + { undefined, A_0B }, /* fd30 */ + { undefined, A_0B }, /* fd31 */ + { undefined, A_0B }, /* fd32 */ + { undefined, A_0B }, /* fd33 */ + { "inc (iy+%02xh)", A_8 }, /* fd34 */ + { "dec (iy+%02xh)", A_8 }, /* fd35 */ + { "ld (iy+%02xh),%02xh",A_8X2 }, /* fd36 */ + { undefined, A_0B }, /* fd37 */ + + { undefined, A_0B }, /* fd38 */ + { "add iy,sp", A_0 }, /* fd39 */ + { undefined, A_0B }, /* fd3a */ + { undefined, A_0B }, /* fd3b */ + { undefined, A_0B }, /* fd3c */ + { undefined, A_0B }, /* fd3d */ + { undefined, A_0B }, /* fd3e */ + { undefined, A_0B }, /* fd3f */ + + { undefined, A_0B }, /* fd40 */ + { undefined, A_0B }, /* fd41 */ + { undefined, A_0B }, /* fd42 */ + { undefined, A_0B }, /* fd43 */ + { undefined, A_0B }, /* fd44 */ + { undefined, A_0B }, /* fd45 */ + { "ld b,(iy+%02xh)", A_8 }, /* fd46 */ + { undefined, A_0B }, /* fd47 */ + + { undefined, A_0B }, /* fd48 */ + { undefined, A_0B }, /* fd49 */ + { undefined, A_0B }, /* fd4a */ + { undefined, A_0B }, /* fd4b */ + { undefined, A_0B }, /* fd4c */ + { undefined, A_0B }, /* fd4d */ + { "ld c,(iy+%02xh)", A_8 }, /* fd4e */ + { undefined, A_0B }, /* fd4f */ + + { undefined, A_0B }, /* fd50 */ + { undefined, A_0B }, /* fd51 */ + { undefined, A_0B }, /* fd52 */ + { undefined, A_0B }, /* fd53 */ + { undefined, A_0B }, /* fd54 */ + { undefined, A_0B }, /* fd55 */ + { "ld d,(iy+%02xh)", A_8 }, /* fd56 */ + { undefined, A_0B }, /* fd57 */ + + { undefined, A_0B }, /* fd58 */ + { undefined, A_0B }, /* fd59 */ + { undefined, A_0B }, /* fd5a */ + { undefined, A_0B }, /* fd5b */ + { undefined, A_0B }, /* fd5c */ + { undefined, A_0B }, /* fd5d */ + { "ld e,(iy+%02xh)", A_8 }, /* fd5e */ + { undefined, A_0B }, /* fd5f */ + + { undefined, A_0B }, /* fd60 */ + { undefined, A_0B }, /* fd61 */ + { undefined, A_0B }, /* fd62 */ + { undefined, A_0B }, /* fd63 */ + { undefined, A_0B }, /* fd64 */ + { undefined, A_0B }, /* fd65 */ + { "ld h,(iy+%02xh)", A_8 }, /* fd66 */ + { undefined, A_0B }, /* fd67 */ + + { undefined, A_0B }, /* fd68 */ + { undefined, A_0B }, /* fd69 */ + { undefined, A_0B }, /* fd6a */ + { undefined, A_0B }, /* fd6b */ + { undefined, A_0B }, /* fd6c */ + { undefined, A_0B }, /* fd6d */ + { "ld l,(iy+%02xh)", A_8 }, /* fd6e */ + { undefined, A_0B }, /* fd6f */ + + { "ld (iy+%02xh),b", A_8 }, /* fd70 */ + { "ld (iy+%02xh),c", A_8 }, /* fd71 */ + { "ld (iy+%02xh),d", A_8 }, /* fd72 */ + { "ld (iy+%02xh),e", A_8 }, /* fd73 */ + { "ld (iy+%02xh),h", A_8 }, /* fd74 */ + { "ld (iy+%02xh),l", A_8 }, /* fd75 */ + { undefined, A_0B }, /* fd76 */ + { "ld (iy+%02xh),a", A_8 }, /* fd77 */ + + { undefined, A_0B }, /* fd78 */ + { undefined, A_0B }, /* fd79 */ + { undefined, A_0B }, /* fd7a */ + { undefined, A_0B }, /* fd7b */ + { undefined, A_0B }, /* fd7c */ + { undefined, A_0B }, /* fd7d */ + { "ld a,(iy+%02xh)", A_8 }, /* fd7e */ + { undefined, A_0B }, /* fd7f */ + + { undefined, A_0B }, /* fd80 */ + { undefined, A_0B }, /* fd81 */ + { undefined, A_0B }, /* fd82 */ + { undefined, A_0B }, /* fd83 */ + { undefined, A_0B }, /* fd84 */ + { undefined, A_0B }, /* fd85 */ + { "add a,(iy+%02xh)", A_8 }, /* fd86 */ + { undefined, A_0B }, /* fd87 */ + + { undefined, A_0B }, /* fd88 */ + { undefined, A_0B }, /* fd89 */ + { undefined, A_0B }, /* fd8a */ + { undefined, A_0B }, /* fd8b */ + { undefined, A_0B }, /* fd8c */ + { undefined, A_0B }, /* fd8d */ + { "adc a,(iy+%02xh)", A_8 }, /* fd8e */ + { undefined, A_0B }, /* fd8f */ + + { undefined, A_0B }, /* fd90 */ + { undefined, A_0B }, /* fd91 */ + { undefined, A_0B }, /* fd92 */ + { undefined, A_0B }, /* fd93 */ + { undefined, A_0B }, /* fd94 */ + { undefined, A_0B }, /* fd95 */ + { "sub (iy+%02xh)", A_8 }, /* fd96 */ + { undefined, A_0B }, /* fd97 */ + + { undefined, A_0B }, /* fd98 */ + { undefined, A_0B }, /* fd99 */ + { undefined, A_0B }, /* fd9a */ + { undefined, A_0B }, /* fd9b */ + { undefined, A_0B }, /* fd9c */ + { undefined, A_0B }, /* fd9d */ + { "sbc a,(iy+%02xh)", A_8 }, /* fd9e */ + { undefined, A_0B }, /* fd9f */ + + { undefined, A_0B }, /* fda0 */ + { undefined, A_0B }, /* fda1 */ + { undefined, A_0B }, /* fda2 */ + { undefined, A_0B }, /* fda3 */ + { undefined, A_0B }, /* fda4 */ + { undefined, A_0B }, /* fda5 */ + { "and (iy+%02xh)", A_8 }, /* fda6 */ + { undefined, A_0B }, /* fda7 */ + + { undefined, A_0B }, /* fda8 */ + { undefined, A_0B }, /* fda9 */ + { undefined, A_0B }, /* fdaa */ + { undefined, A_0B }, /* fdab */ + { undefined, A_0B }, /* fdac */ + { undefined, A_0B }, /* fdad */ + { "xor (iy+%02xh)", A_8 }, /* fdae */ + { undefined, A_0B }, /* fdaf */ + + { undefined, A_0B }, /* fdb0 */ + { undefined, A_0B }, /* fdb1 */ + { undefined, A_0B }, /* fdb2 */ + { undefined, A_0B }, /* fdb3 */ + { undefined, A_0B }, /* fdb4 */ + { undefined, A_0B }, /* fdb5 */ + { "or (iy+%02xh)", A_8 }, /* fdb6 */ + { undefined, A_0B }, /* fdb7 */ + + { undefined, A_0B }, /* fdb8 */ + { undefined, A_0B }, /* fdb9 */ + { undefined, A_0B }, /* fdba */ + { undefined, A_0B }, /* fdbb */ + { undefined, A_0B }, /* fdbc */ + { undefined, A_0B }, /* fdbd */ + { "cp (iy+%02xh)", A_8 }, /* fdbe */ + { undefined, A_0B }, /* fdbf */ + + { undefined, A_0B }, /* fdc0 */ + { undefined, A_0B }, /* fdc1 */ + { undefined, A_0B }, /* fdc2 */ + { undefined, A_0B }, /* fdc3 */ + { undefined, A_0B }, /* fdc4 */ + { undefined, A_0B }, /* fdc5 */ + { undefined, A_0B }, /* fdc6 */ + { undefined, A_0B }, /* fdc7 */ + + { undefined, A_0B }, /* fdc8 */ + { undefined, A_0B }, /* fdc9 */ + { undefined, A_0B }, /* fdca */ + { 0, 5 }, /* fdcb */ + { undefined, A_0B }, /* fdcc */ + { undefined, A_0B }, /* fdcd */ + { undefined, A_0B }, /* fdce */ + { undefined, A_0B }, /* fdcf */ + + { undefined, A_0B }, /* fdd0 */ + { undefined, A_0B }, /* fdd1 */ + { undefined, A_0B }, /* fdd2 */ + { undefined, A_0B }, /* fdd3 */ + { undefined, A_0B }, /* fdd4 */ + { undefined, A_0B }, /* fdd5 */ + { undefined, A_0B }, /* fdd6 */ + { undefined, A_0B }, /* fdd7 */ + + { undefined, A_0B }, /* fdd8 */ + { undefined, A_0B }, /* fdd9 */ + { undefined, A_0B }, /* fdda */ + { undefined, A_0B }, /* fddb */ + { undefined, A_0B }, /* fddc */ + { undefined, A_0B }, /* fddd */ + { undefined, A_0B }, /* fdde */ + { undefined, A_0B }, /* fddf */ + + { undefined, A_0B }, /* fde0 */ + { "pop iy", A_0 }, /* fde1 */ + { undefined, A_0B }, /* fde2 */ + { "ex (sp),iy", A_0 }, /* fde3 */ + { undefined, A_0B }, /* fde4 */ + { "push iy", A_0 }, /* fde5 */ + { undefined, A_0B }, /* fde6 */ + { undefined, A_0B }, /* fde7 */ + + { undefined, A_0B }, /* fde8 */ + { "jp (iy)", A_0 }, /* fde9 */ + { undefined, A_0B }, /* fdea */ + { undefined, A_0B }, /* fdeb */ + { undefined, A_0B }, /* fdec */ + { undefined, A_0B }, /* fded */ + { undefined, A_0B }, /* fdee */ + { undefined, A_0B }, /* fdef */ + + { undefined, A_0B }, /* fdf0 */ + { undefined, A_0B }, /* fdf1 */ + { undefined, A_0B }, /* fdf2 */ + { undefined, A_0B }, /* fdf3 */ + { undefined, A_0B }, /* fdf4 */ + { undefined, A_0B }, /* fdf5 */ + { undefined, A_0B }, /* fdf6 */ + { undefined, A_0B }, /* fdf7 */ + + { undefined, A_0B }, /* fdf8 */ + { "ld sp,iy", A_0 }, /* fdf9 */ + { undefined, A_0B }, /* fdfa */ + { undefined, A_0B }, /* fdfb */ + { undefined, A_0B }, /* fdfc */ + { undefined, A_0B }, /* fdfd */ + { undefined, A_0B }, /* fdfe */ + { undefined, A_0B }, /* fdff */ + }, + { /* dd cb */ + { "ld b,rlc (ix+%02xh)", A_8P }, /* ddcb..00 [undoc] */ + { "ld c,rlc (ix+%02xh)", A_8P }, /* ddcb..01 [undoc] */ + { "ld d,rlc (ix+%02xh)", A_8P }, /* ddcb..02 [undoc] */ + { "ld e,rlc (ix+%02xh)", A_8P }, /* ddcb..03 [undoc] */ + { "ld h,rlc (ix+%02xh)", A_8P }, /* ddcb..04 [undoc] */ + { "ld l,rlc (ix+%02xh)", A_8P }, /* ddcb..05 [undoc] */ + { "rlc (ix+%02xh)", A_8P }, /* ddcb..06 */ + { "ld a,rlc (ix+%02xh)", A_8P }, /* ddcb..07 [undoc] */ + + { "ld b,rrc (ix+%02xh)", A_8P }, /* ddcb..08 [undoc] */ + { "ld c,rrc (ix+%02xh)", A_8P }, /* ddcb..09 [undoc] */ + { "ld d,rrc (ix+%02xh)", A_8P }, /* ddcb..0a [undoc] */ + { "ld e,rrc (ix+%02xh)", A_8P }, /* ddcb..0b [undoc] */ + { "ld h,rrc (ix+%02xh)", A_8P }, /* ddcb..0c [undoc] */ + { "ld l,rrc (ix+%02xh)", A_8P }, /* ddcb..0d [undoc] */ + { "rrc (ix+%02xh)", A_8P }, /* ddcb..0e */ + { "ld a,rrc (ix+%02xh)", A_8P }, /* ddcb..0f [undoc] */ + + { "ld b,rl (ix+%02xh)", A_8P }, /* ddcb..10 [undoc] */ + { "ld c,rl (ix+%02xh)", A_8P }, /* ddcb..11 [undoc] */ + { "ld d,rl (ix+%02xh)", A_8P }, /* ddcb..12 [undoc] */ + { "ld e,rl (ix+%02xh)", A_8P }, /* ddcb..13 [undoc] */ + { "ld h,rl (ix+%02xh)", A_8P }, /* ddcb..14 [undoc] */ + { "ld l,rl (ix+%02xh)", A_8P }, /* ddcb..15 [undoc] */ + { "rl (ix+%02xh)", A_8P }, /* ddcb..16 */ + { "ld a,rl (ix+%02xh)", A_8P }, /* ddcb..17 [undoc] */ + + { "ld b,rr (ix+%02xh)", A_8P }, /* ddcb..18 [undoc] */ + { "ld c,rr (ix+%02xh)", A_8P }, /* ddcb..19 [undoc] */ + { "ld d,rr (ix+%02xh)", A_8P }, /* ddcb..1a [undoc] */ + { "ld e,rr (ix+%02xh)", A_8P }, /* ddcb..1b [undoc] */ + { "ld h,rr (ix+%02xh)", A_8P }, /* ddcb..1c [undoc] */ + { "ld l,rr (ix+%02xh)", A_8P }, /* ddcb..1d [undoc] */ + { "rr (ix+%02xh)", A_8P }, /* ddcb..1e */ + { "ld a,rr (ix+%02xh)", A_8P }, /* ddcb..1f [undoc] */ + + { "ld b,sla (ix+%02xh)", A_8P }, /* ddcb..20 [undoc] */ + { "ld c,sla (ix+%02xh)", A_8P }, /* ddcb..21 [undoc] */ + { "ld d,sla (ix+%02xh)", A_8P }, /* ddcb..22 [undoc] */ + { "ld e,sla (ix+%02xh)", A_8P }, /* ddcb..23 [undoc] */ + { "ld h,sla (ix+%02xh)", A_8P }, /* ddcb..24 [undoc] */ + { "ld l,sla (ix+%02xh)", A_8P }, /* ddcb..25 [undoc] */ + { "sla (ix+%02xh)", A_8P }, /* ddcb..26 */ + { "ld a,sla (ix+%02xh)", A_8P }, /* ddcb..27 [undoc] */ + + { "ld b,sra (ix+%02xh)", A_8P }, /* ddcb..28 [undoc] */ + { "ld c,sra (ix+%02xh)", A_8P }, /* ddcb..29 [undoc] */ + { "ld d,sra (ix+%02xh)", A_8P }, /* ddcb..2a [undoc] */ + { "ld e,sra (ix+%02xh)", A_8P }, /* ddcb..2b [undoc] */ + { "ld h,sra (ix+%02xh)", A_8P }, /* ddcb..2c [undoc] */ + { "ld l,sra (ix+%02xh)", A_8P }, /* ddcb..2d [undoc] */ + { "sra (ix+%02xh)", A_8P }, /* ddcb..2e */ + { "ld a,sra (ix+%02xh)", A_8P }, /* ddcb..2f [undoc] */ + + { "ld b,slia (ix+%02xh)", A_8P }, /* ddcb..30 [undoc] */ + { "ld c,slia (ix+%02xh)", A_8P }, /* ddcb..31 [undoc] */ + { "ld d,slia (ix+%02xh)", A_8P }, /* ddcb..32 [undoc] */ + { "ld e,slia (ix+%02xh)", A_8P }, /* ddcb..33 [undoc] */ + { "ld h,slia (ix+%02xh)", A_8P }, /* ddcb..34 [undoc] */ + { "ld l,slia (ix+%02xh)", A_8P }, /* ddcb..35 [undoc] */ + { "slia (ix+%02xh)", A_8P }, /* ddcb..36 [undoc] */ + { "ld a,slia (ix+%02xh)", A_8P }, /* ddcb..37 [undoc] */ + + { "ld b,srl (ix+%02xh)", A_8P }, /* ddcb..38 [undoc] */ + { "ld c,srl (ix+%02xh)", A_8P }, /* ddcb..39 [undoc] */ + { "ld d,srl (ix+%02xh)", A_8P }, /* ddcb..3a [undoc] */ + { "ld e,srl (ix+%02xh)", A_8P }, /* ddcb..3b [undoc] */ + { "ld h,srl (ix+%02xh)", A_8P }, /* ddcb..3c [undoc] */ + { "ld l,srl (ix+%02xh)", A_8P }, /* ddcb..3d [undoc] */ + { "srl (ix+%02xh)", A_8P }, /* ddcb..3e */ + { "ld a,srl (ix+%02xh)", A_8P }, /* ddcb..3f [undoc] */ + + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..40 [undoc] */ + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..41 [undoc] */ + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..42 [undoc] */ + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..43 [undoc] */ + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..44 [undoc] */ + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..45 [undoc] */ + { "bit 0,(ix+%02xh)", A_8P }, /* ddcb..46 */ + { "bit 0,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..47 [undoc] */ + + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..48 [undoc] */ + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..49 [undoc] */ + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..4a [undoc] */ + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..4b [undoc] */ + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..4c [undoc] */ + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..4d [undoc] */ + { "bit 1,(ix+%02xh)", A_8P }, /* ddcb..4e */ + { "bit 1,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..4f [undoc] */ + + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..50 [undoc] */ + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..51 [undoc] */ + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..52 [undoc] */ + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..53 [undoc] */ + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..54 [undoc] */ + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..55 [undoc] */ + { "bit 2,(ix+%02xh)", A_8P }, /* ddcb..56 */ + { "bit 2,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..57 [undoc] */ + + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..58 [undoc] */ + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..59 [undoc] */ + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..5a [undoc] */ + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..5b [undoc] */ + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..5c [undoc] */ + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..5d [undoc] */ + { "bit 3,(ix+%02xh)", A_8P }, /* ddcb..5e */ + { "bit 3,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..5f [undoc] */ + + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..60 [undoc] */ + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..61 [undoc] */ + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..62 [undoc] */ + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..63 [undoc] */ + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..64 [undoc] */ + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..65 [undoc] */ + { "bit 4,(ix+%02xh)", A_8P }, /* ddcb..66 */ + { "bit 4,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..67 [undoc] */ + + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..68 [undoc] */ + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..69 [undoc] */ + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..6a [undoc] */ + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..6b [undoc] */ + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..6c [undoc] */ + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..6d [undoc] */ + { "bit 5,(ix+%02xh)", A_8P }, /* ddcb..6e */ + { "bit 5,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..6f [undoc] */ + + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..70 [undoc] */ + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..71 [undoc] */ + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..72 [undoc] */ + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..73 [undoc] */ + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..74 [undoc] */ + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..75 [undoc] */ + { "bit 6,(ix+%02xh)", A_8P }, /* ddcb..76 */ + { "bit 6,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..77 [undoc] */ + + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..78 [undoc] */ + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..79 [undoc] */ + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..7a [undoc] */ + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..7b [undoc] */ + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..7c [undoc] */ + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..7d [undoc] */ + { "bit 7,(ix+%02xh)", A_8P }, /* ddcb..7e */ + { "bit 7,(ix+%02xh)\t;undoc equiv", A_8P }, /* ddcb..7f [undoc] */ + + { "ld b,res 0,(ix+%02xh)", A_8P }, /* ddcb..80 [undoc] */ + { "ld c,res 0,(ix+%02xh)", A_8P }, /* ddcb..81 [undoc] */ + { "ld d,res 0,(ix+%02xh)", A_8P }, /* ddcb..82 [undoc] */ + { "ld e,res 0,(ix+%02xh)", A_8P }, /* ddcb..83 [undoc] */ + { "ld h,res 0,(ix+%02xh)", A_8P }, /* ddcb..84 [undoc] */ + { "ld l,res 0,(ix+%02xh)", A_8P }, /* ddcb..85 [undoc] */ + { "res 0,(ix+%02xh)", A_8P }, /* ddcb..86 */ + { "ld a,res 0,(ix+%02xh)", A_8P }, /* ddcb..87 [undoc] */ + + { "ld b,res 1,(ix+%02xh)", A_8P }, /* ddcb..88 [undoc] */ + { "ld c,res 1,(ix+%02xh)", A_8P }, /* ddcb..89 [undoc] */ + { "ld d,res 1,(ix+%02xh)", A_8P }, /* ddcb..8a [undoc] */ + { "ld e,res 1,(ix+%02xh)", A_8P }, /* ddcb..8b [undoc] */ + { "ld h,res 1,(ix+%02xh)", A_8P }, /* ddcb..8c [undoc] */ + { "ld l,res 1,(ix+%02xh)", A_8P }, /* ddcb..8d [undoc] */ + { "res 1,(ix+%02xh)", A_8P }, /* ddcb..8e */ + { "ld a,res 1,(ix+%02xh)", A_8P }, /* ddcb..8f [undoc] */ + + { "ld b,res 2,(ix+%02xh)", A_8P }, /* ddcb..90 [undoc] */ + { "ld c,res 2,(ix+%02xh)", A_8P }, /* ddcb..91 [undoc] */ + { "ld d,res 2,(ix+%02xh)", A_8P }, /* ddcb..92 [undoc] */ + { "ld e,res 2,(ix+%02xh)", A_8P }, /* ddcb..93 [undoc] */ + { "ld h,res 2,(ix+%02xh)", A_8P }, /* ddcb..94 [undoc] */ + { "ld l,res 2,(ix+%02xh)", A_8P }, /* ddcb..95 [undoc] */ + { "res 2,(ix+%02xh)", A_8P }, /* ddcb..96 */ + { "ld a,res 2,(ix+%02xh)", A_8P }, /* ddcb..97 [undoc] */ + + { "ld b,res 3,(ix+%02xh)", A_8P }, /* ddcb..98 [undoc] */ + { "ld c,res 3,(ix+%02xh)", A_8P }, /* ddcb..99 [undoc] */ + { "ld d,res 3,(ix+%02xh)", A_8P }, /* ddcb..9a [undoc] */ + { "ld e,res 3,(ix+%02xh)", A_8P }, /* ddcb..9b [undoc] */ + { "ld h,res 3,(ix+%02xh)", A_8P }, /* ddcb..9c [undoc] */ + { "ld l,res 3,(ix+%02xh)", A_8P }, /* ddcb..9d [undoc] */ + { "res 3,(ix+%02xh)", A_8P }, /* ddcb..9e */ + { "ld a,res 3,(ix+%02xh)", A_8P }, /* ddcb..9f [undoc] */ + + { "ld b,res 4,(ix+%02xh)", A_8P }, /* ddcb..a0 [undoc] */ + { "ld c,res 4,(ix+%02xh)", A_8P }, /* ddcb..a1 [undoc] */ + { "ld d,res 4,(ix+%02xh)", A_8P }, /* ddcb..a2 [undoc] */ + { "ld e,res 4,(ix+%02xh)", A_8P }, /* ddcb..a3 [undoc] */ + { "ld h,res 4,(ix+%02xh)", A_8P }, /* ddcb..a4 [undoc] */ + { "ld l,res 4,(ix+%02xh)", A_8P }, /* ddcb..a5 [undoc] */ + { "res 4,(ix+%02xh)", A_8P }, /* ddcb..a6 */ + { "ld a,res 4,(ix+%02xh)", A_8P }, /* ddcb..a7 [undoc] */ + + { "ld b,res 5,(ix+%02xh)", A_8P }, /* ddcb..a8 [undoc] */ + { "ld c,res 5,(ix+%02xh)", A_8P }, /* ddcb..a9 [undoc] */ + { "ld d,res 5,(ix+%02xh)", A_8P }, /* ddcb..aa [undoc] */ + { "ld e,res 5,(ix+%02xh)", A_8P }, /* ddcb..ab [undoc] */ + { "ld h,res 5,(ix+%02xh)", A_8P }, /* ddcb..ac [undoc] */ + { "ld l,res 5,(ix+%02xh)", A_8P }, /* ddcb..ad [undoc] */ + { "res 5,(ix+%02xh)", A_8P }, /* ddcb..ae */ + { "ld a,res 5,(ix+%02xh)", A_8P }, /* ddcb..af [undoc] */ + + { "ld b,res 6,(ix+%02xh)", A_8P }, /* ddcb..b0 [undoc] */ + { "ld c,res 6,(ix+%02xh)", A_8P }, /* ddcb..b1 [undoc] */ + { "ld d,res 6,(ix+%02xh)", A_8P }, /* ddcb..b2 [undoc] */ + { "ld e,res 6,(ix+%02xh)", A_8P }, /* ddcb..b3 [undoc] */ + { "ld h,res 6,(ix+%02xh)", A_8P }, /* ddcb..b4 [undoc] */ + { "ld l,res 6,(ix+%02xh)", A_8P }, /* ddcb..b5 [undoc] */ + { "res 6,(ix+%02xh)", A_8P }, /* ddcb..b6 */ + { "ld a,res 6,(ix+%02xh)", A_8P }, /* ddcb..b7 [undoc] */ + + { "ld b,res 7,(ix+%02xh)", A_8P }, /* ddcb..b8 [undoc] */ + { "ld c,res 7,(ix+%02xh)", A_8P }, /* ddcb..b9 [undoc] */ + { "ld d,res 7,(ix+%02xh)", A_8P }, /* ddcb..ba [undoc] */ + { "ld e,res 7,(ix+%02xh)", A_8P }, /* ddcb..bb [undoc] */ + { "ld h,res 7,(ix+%02xh)", A_8P }, /* ddcb..bc [undoc] */ + { "ld l,res 7,(ix+%02xh)", A_8P }, /* ddcb..bd [undoc] */ + { "res 7,(ix+%02xh)", A_8P }, /* ddcb..be */ + { "ld a,res 7,(ix+%02xh)", A_8P }, /* ddcb..bf [undoc] */ + + { "ld b,set 0,(ix+%02xh)", A_8P }, /* ddcb..c0 [undoc] */ + { "ld c,set 0,(ix+%02xh)", A_8P }, /* ddcb..c1 [undoc] */ + { "ld d,set 0,(ix+%02xh)", A_8P }, /* ddcb..c2 [undoc] */ + { "ld e,set 0,(ix+%02xh)", A_8P }, /* ddcb..c3 [undoc] */ + { "ld h,set 0,(ix+%02xh)", A_8P }, /* ddcb..c4 [undoc] */ + { "ld l,set 0,(ix+%02xh)", A_8P }, /* ddcb..c5 [undoc] */ + { "set 0,(ix+%02xh)", A_8P }, /* ddcb..c6 */ + { "ld a,set 0,(ix+%02xh)", A_8P }, /* ddcb..c7 [undoc] */ + + { "ld b,set 1,(ix+%02xh)", A_8P }, /* ddcb..c8 [undoc] */ + { "ld c,set 1,(ix+%02xh)", A_8P }, /* ddcb..c9 [undoc] */ + { "ld d,set 1,(ix+%02xh)", A_8P }, /* ddcb..ca [undoc] */ + { "ld e,set 1,(ix+%02xh)", A_8P }, /* ddcb..cb [undoc] */ + { "ld h,set 1,(ix+%02xh)", A_8P }, /* ddcb..cc [undoc] */ + { "ld l,set 1,(ix+%02xh)", A_8P }, /* ddcb..cd [undoc] */ + { "set 1,(ix+%02xh)", A_8P }, /* ddcb..ce */ + { "ld a,set 1,(ix+%02xh)", A_8P }, /* ddcb..cf [undoc] */ + + { "ld b,set 2,(ix+%02xh)", A_8P }, /* ddcb..d0 [undoc] */ + { "ld c,set 2,(ix+%02xh)", A_8P }, /* ddcb..d1 [undoc] */ + { "ld d,set 2,(ix+%02xh)", A_8P }, /* ddcb..d2 [undoc] */ + { "ld e,set 2,(ix+%02xh)", A_8P }, /* ddcb..d3 [undoc] */ + { "ld h,set 2,(ix+%02xh)", A_8P }, /* ddcb..d4 [undoc] */ + { "ld l,set 2,(ix+%02xh)", A_8P }, /* ddcb..d5 [undoc] */ + { "set 2,(ix+%02xh)", A_8P }, /* ddcb..d6 */ + { "ld a,set 2,(ix+%02xh)", A_8P }, /* ddcb..d7 [undoc] */ + + { "ld b,set 3,(ix+%02xh)", A_8P }, /* ddcb..d8 [undoc] */ + { "ld c,set 3,(ix+%02xh)", A_8P }, /* ddcb..d9 [undoc] */ + { "ld d,set 3,(ix+%02xh)", A_8P }, /* ddcb..da [undoc] */ + { "ld e,set 3,(ix+%02xh)", A_8P }, /* ddcb..db [undoc] */ + { "ld h,set 3,(ix+%02xh)", A_8P }, /* ddcb..dc [undoc] */ + { "ld l,set 3,(ix+%02xh)", A_8P }, /* ddcb..dd [undoc] */ + { "set 3,(ix+%02xh)", A_8P }, /* ddcb..de */ + { "ld a,set 3,(ix+%02xh)", A_8P }, /* ddcb..df [undoc] */ + + { "ld b,set 4,(ix+%02xh)", A_8P }, /* ddcb..e0 [undoc] */ + { "ld c,set 4,(ix+%02xh)", A_8P }, /* ddcb..e1 [undoc] */ + { "ld d,set 4,(ix+%02xh)", A_8P }, /* ddcb..e2 [undoc] */ + { "ld e,set 4,(ix+%02xh)", A_8P }, /* ddcb..e3 [undoc] */ + { "ld h,set 4,(ix+%02xh)", A_8P }, /* ddcb..e4 [undoc] */ + { "ld l,set 4,(ix+%02xh)", A_8P }, /* ddcb..e5 [undoc] */ + { "set 4,(ix+%02xh)", A_8P }, /* ddcb..e6 */ + { "ld a,set 4,(ix+%02xh)", A_8P }, /* ddcb..e7 [undoc] */ + + { "ld b,set 5,(ix+%02xh)", A_8P }, /* ddcb..e8 [undoc] */ + { "ld c,set 5,(ix+%02xh)", A_8P }, /* ddcb..e9 [undoc] */ + { "ld d,set 5,(ix+%02xh)", A_8P }, /* ddcb..ea [undoc] */ + { "ld e,set 5,(ix+%02xh)", A_8P }, /* ddcb..eb [undoc] */ + { "ld h,set 5,(ix+%02xh)", A_8P }, /* ddcb..ec [undoc] */ + { "ld l,set 5,(ix+%02xh)", A_8P }, /* ddcb..ed [undoc] */ + { "set 5,(ix+%02xh)", A_8P }, /* ddcb..ee */ + { "ld a,set 5,(ix+%02xh)", A_8P }, /* ddcb..ef [undoc] */ + + { "ld b,set 6,(ix+%02xh)", A_8P }, /* ddcb..f0 [undoc] */ + { "ld c,set 6,(ix+%02xh)", A_8P }, /* ddcb..f1 [undoc] */ + { "ld d,set 6,(ix+%02xh)", A_8P }, /* ddcb..f2 [undoc] */ + { "ld e,set 6,(ix+%02xh)", A_8P }, /* ddcb..f3 [undoc] */ + { "ld h,set 6,(ix+%02xh)", A_8P }, /* ddcb..f4 [undoc] */ + { "ld l,set 6,(ix+%02xh)", A_8P }, /* ddcb..f5 [undoc] */ + { "set 6,(ix+%02xh)", A_8P }, /* ddcb..f6 */ + { "ld a,set 6,(ix+%02xh)", A_8P }, /* ddcb..f7 [undoc] */ + + { "ld b,set 7,(ix+%02xh)", A_8P }, /* ddcb..f8 [undoc] */ + { "ld c,set 7,(ix+%02xh)", A_8P }, /* ddcb..f9 [undoc] */ + { "ld d,set 7,(ix+%02xh)", A_8P }, /* ddcb..fa [undoc] */ + { "ld e,set 7,(ix+%02xh)", A_8P }, /* ddcb..fb [undoc] */ + { "ld h,set 7,(ix+%02xh)", A_8P }, /* ddcb..fc [undoc] */ + { "ld l,set 7,(ix+%02xh)", A_8P }, /* ddcb..fd [undoc] */ + { "set 7,(ix+%02xh)", A_8P }, /* ddcb..fe */ + { "ld a,set 7,(ix+%02xh)", A_8P }, /* ddcb..ff [undoc] */ + }, + { /* fd cb */ + { "ld b,rlc (iy+%02xh)", A_8P }, /* fdcb..00 [undoc] */ + { "ld c,rlc (iy+%02xh)", A_8P }, /* fdcb..01 [undoc] */ + { "ld d,rlc (iy+%02xh)", A_8P }, /* fdcb..02 [undoc] */ + { "ld e,rlc (iy+%02xh)", A_8P }, /* fdcb..03 [undoc] */ + { "ld h,rlc (iy+%02xh)", A_8P }, /* fdcb..04 [undoc] */ + { "ld l,rlc (iy+%02xh)", A_8P }, /* fdcb..05 [undoc] */ + { "rlc (iy+%02xh)", A_8P }, /* fdcb..06 */ + { "ld a,rlc (iy+%02xh)", A_8P }, /* fdcb..07 [undoc] */ + + { "ld b,rrc (iy+%02xh)", A_8P }, /* fdcb..08 [undoc] */ + { "ld c,rrc (iy+%02xh)", A_8P }, /* fdcb..09 [undoc] */ + { "ld d,rrc (iy+%02xh)", A_8P }, /* fdcb..0a [undoc] */ + { "ld e,rrc (iy+%02xh)", A_8P }, /* fdcb..0b [undoc] */ + { "ld h,rrc (iy+%02xh)", A_8P }, /* fdcb..0c [undoc] */ + { "ld l,rrc (iy+%02xh)", A_8P }, /* fdcb..0d [undoc] */ + { "rrc (iy+%02xh)", A_8P }, /* fdcb..0e */ + { "ld a,rrc (iy+%02xh)", A_8P }, /* fdcb..0f [undoc] */ + + { "ld b,rl (iy+%02xh)", A_8P }, /* fdcb..10 [undoc] */ + { "ld c,rl (iy+%02xh)", A_8P }, /* fdcb..11 [undoc] */ + { "ld d,rl (iy+%02xh)", A_8P }, /* fdcb..12 [undoc] */ + { "ld e,rl (iy+%02xh)", A_8P }, /* fdcb..13 [undoc] */ + { "ld h,rl (iy+%02xh)", A_8P }, /* fdcb..14 [undoc] */ + { "ld l,rl (iy+%02xh)", A_8P }, /* fdcb..15 [undoc] */ + { "rl (iy+%02xh)", A_8P }, /* fdcb..16 */ + { "ld a,rl (iy+%02xh)", A_8P }, /* fdcb..17 [undoc] */ + + { "ld b,rr (iy+%02xh)", A_8P }, /* fdcb..18 [undoc] */ + { "ld c,rr (iy+%02xh)", A_8P }, /* fdcb..19 [undoc] */ + { "ld d,rr (iy+%02xh)", A_8P }, /* fdcb..1a [undoc] */ + { "ld e,rr (iy+%02xh)", A_8P }, /* fdcb..1b [undoc] */ + { "ld h,rr (iy+%02xh)", A_8P }, /* fdcb..1c [undoc] */ + { "ld l,rr (iy+%02xh)", A_8P }, /* fdcb..1d [undoc] */ + { "rr (iy+%02xh)", A_8P }, /* fdcb..1e */ + { "ld a,rr (iy+%02xh)", A_8P }, /* fdcb..1f [undoc] */ + + { "ld b,sla (iy+%02xh)", A_8P }, /* fdcb..20 [undoc] */ + { "ld c,sla (iy+%02xh)", A_8P }, /* fdcb..21 [undoc] */ + { "ld d,sla (iy+%02xh)", A_8P }, /* fdcb..22 [undoc] */ + { "ld e,sla (iy+%02xh)", A_8P }, /* fdcb..23 [undoc] */ + { "ld h,sla (iy+%02xh)", A_8P }, /* fdcb..24 [undoc] */ + { "ld l,sla (iy+%02xh)", A_8P }, /* fdcb..25 [undoc] */ + { "sla (iy+%02xh)", A_8P }, /* fdcb..26 */ + { "ld a,sla (iy+%02xh)", A_8P }, /* fdcb..27 [undoc] */ + + { "ld b,sra (iy+%02xh)", A_8P }, /* fdcb..28 [undoc] */ + { "ld c,sra (iy+%02xh)", A_8P }, /* fdcb..29 [undoc] */ + { "ld d,sra (iy+%02xh)", A_8P }, /* fdcb..2a [undoc] */ + { "ld e,sra (iy+%02xh)", A_8P }, /* fdcb..2b [undoc] */ + { "ld h,sra (iy+%02xh)", A_8P }, /* fdcb..2c [undoc] */ + { "ld l,sra (iy+%02xh)", A_8P }, /* fdcb..2d [undoc] */ + { "sra (iy+%02xh)", A_8P }, /* fdcb..2e */ + { "ld a,sra (iy+%02xh)", A_8P }, /* fdcb..2f [undoc] */ + + { "ld b,slia (iy+%02xh)", A_8P }, /* fdcb..30 [undoc] */ + { "ld c,slia (iy+%02xh)", A_8P }, /* fdcb..31 [undoc] */ + { "ld d,slia (iy+%02xh)", A_8P }, /* fdcb..32 [undoc] */ + { "ld e,slia (iy+%02xh)", A_8P }, /* fdcb..33 [undoc] */ + { "ld h,slia (iy+%02xh)", A_8P }, /* fdcb..34 [undoc] */ + { "ld l,slia (iy+%02xh)", A_8P }, /* fdcb..35 [undoc] */ + { "slia (iy+%02xh)", A_8P }, /* fdcb..36 [undoc] */ + { "ld a,slia (iy+%02xh)", A_8P }, /* fdcb..37 [undoc] */ + + { "ld b,srl (iy+%02xh)", A_8P }, /* fdcb..38 [undoc] */ + { "ld c,srl (iy+%02xh)", A_8P }, /* fdcb..39 [undoc] */ + { "ld d,srl (iy+%02xh)", A_8P }, /* fdcb..3a [undoc] */ + { "ld e,srl (iy+%02xh)", A_8P }, /* fdcb..3b [undoc] */ + { "ld h,srl (iy+%02xh)", A_8P }, /* fdcb..3c [undoc] */ + { "ld l,srl (iy+%02xh)", A_8P }, /* fdcb..3d [undoc] */ + { "srl (iy+%02xh)", A_8P }, /* fdcb..3e */ + { "ld a,srl (iy+%02xh)", A_8P }, /* fdcb..3f [undoc] */ + + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..40 [undoc] */ + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..41 [undoc] */ + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..42 [undoc] */ + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..43 [undoc] */ + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..44 [undoc] */ + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..45 [undoc] */ + { "bit 0,(iy+%02xh)", A_8P }, /* fdcb..46 */ + { "bit 0,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..47 [undoc] */ + + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..48 [undoc] */ + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..49 [undoc] */ + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..4a [undoc] */ + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..4b [undoc] */ + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..4c [undoc] */ + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..4d [undoc] */ + { "bit 1,(iy+%02xh)", A_8P }, /* fdcb..4e */ + { "bit 1,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..4f [undoc] */ + + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..50 [undoc] */ + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..51 [undoc] */ + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..52 [undoc] */ + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..53 [undoc] */ + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..54 [undoc] */ + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..55 [undoc] */ + { "bit 2,(iy+%02xh)", A_8P }, /* fdcb..56 */ + { "bit 2,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..57 [undoc] */ + + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..58 [undoc] */ + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..59 [undoc] */ + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..5a [undoc] */ + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..5b [undoc] */ + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..5c [undoc] */ + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..5d [undoc] */ + { "bit 3,(iy+%02xh)", A_8P }, /* fdcb..5e */ + { "bit 3,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..5f [undoc] */ + + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..60 [undoc] */ + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..61 [undoc] */ + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..62 [undoc] */ + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..63 [undoc] */ + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..64 [undoc] */ + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..65 [undoc] */ + { "bit 4,(iy+%02xh)", A_8P }, /* fdcb..66 */ + { "bit 4,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..67 [undoc] */ + + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..68 [undoc] */ + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..69 [undoc] */ + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..6a [undoc] */ + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..6b [undoc] */ + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..6c [undoc] */ + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..6d [undoc] */ + { "bit 5,(iy+%02xh)", A_8P }, /* fdcb..6e */ + { "bit 5,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..6f [undoc] */ + + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..70 [undoc] */ + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..71 [undoc] */ + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..72 [undoc] */ + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..73 [undoc] */ + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..74 [undoc] */ + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..75 [undoc] */ + { "bit 6,(iy+%02xh)", A_8P }, /* fdcb..76 */ + { "bit 6,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..77 [undoc] */ + + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..78 [undoc] */ + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..79 [undoc] */ + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..7a [undoc] */ + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..7b [undoc] */ + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..7c [undoc] */ + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..7d [undoc] */ + { "bit 7,(iy+%02xh)", A_8P }, /* fdcb..7e */ + { "bit 7,(iy+%02xh)\t;undoc equiv", A_8P }, /* fdcb..7f [undoc] */ + + { "ld b,res 0,(iy+%02xh)", A_8P }, /* fdcb..80 [undoc] */ + { "ld c,res 0,(iy+%02xh)", A_8P }, /* fdcb..81 [undoc] */ + { "ld d,res 0,(iy+%02xh)", A_8P }, /* fdcb..82 [undoc] */ + { "ld e,res 0,(iy+%02xh)", A_8P }, /* fdcb..83 [undoc] */ + { "ld h,res 0,(iy+%02xh)", A_8P }, /* fdcb..84 [undoc] */ + { "ld l,res 0,(iy+%02xh)", A_8P }, /* fdcb..85 [undoc] */ + { "res 0,(iy+%02xh)", A_8P }, /* fdcb..86 */ + { "ld a,res 0,(iy+%02xh)", A_8P }, /* fdcb..87 [undoc] */ + + { "ld b,res 1,(iy+%02xh)", A_8P }, /* fdcb..88 [undoc] */ + { "ld c,res 1,(iy+%02xh)", A_8P }, /* fdcb..89 [undoc] */ + { "ld d,res 1,(iy+%02xh)", A_8P }, /* fdcb..8a [undoc] */ + { "ld e,res 1,(iy+%02xh)", A_8P }, /* fdcb..8b [undoc] */ + { "ld h,res 1,(iy+%02xh)", A_8P }, /* fdcb..8c [undoc] */ + { "ld l,res 1,(iy+%02xh)", A_8P }, /* fdcb..8d [undoc] */ + { "res 1,(iy+%02xh)", A_8P }, /* fdcb..8e */ + { "ld a,res 1,(iy+%02xh)", A_8P }, /* fdcb..8f [undoc] */ + + { "ld b,res 2,(iy+%02xh)", A_8P }, /* fdcb..90 [undoc] */ + { "ld c,res 2,(iy+%02xh)", A_8P }, /* fdcb..91 [undoc] */ + { "ld d,res 2,(iy+%02xh)", A_8P }, /* fdcb..92 [undoc] */ + { "ld e,res 2,(iy+%02xh)", A_8P }, /* fdcb..93 [undoc] */ + { "ld h,res 2,(iy+%02xh)", A_8P }, /* fdcb..94 [undoc] */ + { "ld l,res 2,(iy+%02xh)", A_8P }, /* fdcb..95 [undoc] */ + { "res 2,(iy+%02xh)", A_8P }, /* fdcb..96 */ + { "ld a,res 2,(iy+%02xh)", A_8P }, /* fdcb..97 [undoc] */ + + { "ld b,res 3,(iy+%02xh)", A_8P }, /* fdcb..98 [undoc] */ + { "ld c,res 3,(iy+%02xh)", A_8P }, /* fdcb..99 [undoc] */ + { "ld d,res 3,(iy+%02xh)", A_8P }, /* fdcb..9a [undoc] */ + { "ld e,res 3,(iy+%02xh)", A_8P }, /* fdcb..9b [undoc] */ + { "ld h,res 3,(iy+%02xh)", A_8P }, /* fdcb..9c [undoc] */ + { "ld l,res 3,(iy+%02xh)", A_8P }, /* fdcb..9d [undoc] */ + { "res 3,(iy+%02xh)", A_8P }, /* fdcb..9e */ + { "ld a,res 3,(iy+%02xh)", A_8P }, /* fdcb..9f [undoc] */ + + { "ld b,res 4,(iy+%02xh)", A_8P }, /* fdcb..a0 [undoc] */ + { "ld c,res 4,(iy+%02xh)", A_8P }, /* fdcb..a1 [undoc] */ + { "ld d,res 4,(iy+%02xh)", A_8P }, /* fdcb..a2 [undoc] */ + { "ld e,res 4,(iy+%02xh)", A_8P }, /* fdcb..a3 [undoc] */ + { "ld h,res 4,(iy+%02xh)", A_8P }, /* fdcb..a4 [undoc] */ + { "ld l,res 4,(iy+%02xh)", A_8P }, /* fdcb..a5 [undoc] */ + { "res 4,(iy+%02xh)", A_8P }, /* fdcb..a6 */ + { "ld a,res 4,(iy+%02xh)", A_8P }, /* fdcb..a7 [undoc] */ + + { "ld b,res 5,(iy+%02xh)", A_8P }, /* fdcb..a8 [undoc] */ + { "ld c,res 5,(iy+%02xh)", A_8P }, /* fdcb..a9 [undoc] */ + { "ld d,res 5,(iy+%02xh)", A_8P }, /* fdcb..aa [undoc] */ + { "ld e,res 5,(iy+%02xh)", A_8P }, /* fdcb..ab [undoc] */ + { "ld h,res 5,(iy+%02xh)", A_8P }, /* fdcb..ac [undoc] */ + { "ld l,res 5,(iy+%02xh)", A_8P }, /* fdcb..ad [undoc] */ + { "res 5,(iy+%02xh)", A_8P }, /* fdcb..ae */ + { "ld a,res 5,(iy+%02xh)", A_8P }, /* fdcb..af [undoc] */ + + { "ld b,res 6,(iy+%02xh)", A_8P }, /* fdcb..b0 [undoc] */ + { "ld c,res 6,(iy+%02xh)", A_8P }, /* fdcb..b1 [undoc] */ + { "ld d,res 6,(iy+%02xh)", A_8P }, /* fdcb..b2 [undoc] */ + { "ld e,res 6,(iy+%02xh)", A_8P }, /* fdcb..b3 [undoc] */ + { "ld h,res 6,(iy+%02xh)", A_8P }, /* fdcb..b4 [undoc] */ + { "ld l,res 6,(iy+%02xh)", A_8P }, /* fdcb..b5 [undoc] */ + { "res 6,(iy+%02xh)", A_8P }, /* fdcb..b6 */ + { "ld a,res 6,(iy+%02xh)", A_8P }, /* fdcb..b7 [undoc] */ + + { "ld b,res 7,(iy+%02xh)", A_8P }, /* fdcb..b8 [undoc] */ + { "ld c,res 7,(iy+%02xh)", A_8P }, /* fdcb..b9 [undoc] */ + { "ld d,res 7,(iy+%02xh)", A_8P }, /* fdcb..ba [undoc] */ + { "ld e,res 7,(iy+%02xh)", A_8P }, /* fdcb..bb [undoc] */ + { "ld h,res 7,(iy+%02xh)", A_8P }, /* fdcb..bc [undoc] */ + { "ld l,res 7,(iy+%02xh)", A_8P }, /* fdcb..bd [undoc] */ + { "res 7,(iy+%02xh)", A_8P }, /* fdcb..be */ + { "ld a,res 7,(iy+%02xh)", A_8P }, /* fdcb..bf [undoc] */ + + { "ld b,set 0,(iy+%02xh)", A_8P }, /* fdcb..c0 [undoc] */ + { "ld c,set 0,(iy+%02xh)", A_8P }, /* fdcb..c1 [undoc] */ + { "ld d,set 0,(iy+%02xh)", A_8P }, /* fdcb..c2 [undoc] */ + { "ld e,set 0,(iy+%02xh)", A_8P }, /* fdcb..c3 [undoc] */ + { "ld h,set 0,(iy+%02xh)", A_8P }, /* fdcb..c4 [undoc] */ + { "ld l,set 0,(iy+%02xh)", A_8P }, /* fdcb..c5 [undoc] */ + { "set 0,(iy+%02xh)", A_8P }, /* fdcb..c6 */ + { "ld a,set 0,(iy+%02xh)", A_8P }, /* fdcb..c7 [undoc] */ + + { "ld b,set 1,(iy+%02xh)", A_8P }, /* fdcb..c8 [undoc] */ + { "ld c,set 1,(iy+%02xh)", A_8P }, /* fdcb..c9 [undoc] */ + { "ld d,set 1,(iy+%02xh)", A_8P }, /* fdcb..ca [undoc] */ + { "ld e,set 1,(iy+%02xh)", A_8P }, /* fdcb..cb [undoc] */ + { "ld h,set 1,(iy+%02xh)", A_8P }, /* fdcb..cc [undoc] */ + { "ld l,set 1,(iy+%02xh)", A_8P }, /* fdcb..cd [undoc] */ + { "set 1,(iy+%02xh)", A_8P }, /* fdcb..ce */ + { "ld a,set 1,(iy+%02xh)", A_8P }, /* fdcb..cf [undoc] */ + + { "ld b,set 2,(iy+%02xh)", A_8P }, /* fdcb..d0 [undoc] */ + { "ld c,set 2,(iy+%02xh)", A_8P }, /* fdcb..d1 [undoc] */ + { "ld d,set 2,(iy+%02xh)", A_8P }, /* fdcb..d2 [undoc] */ + { "ld e,set 2,(iy+%02xh)", A_8P }, /* fdcb..d3 [undoc] */ + { "ld h,set 2,(iy+%02xh)", A_8P }, /* fdcb..d4 [undoc] */ + { "ld l,set 2,(iy+%02xh)", A_8P }, /* fdcb..d5 [undoc] */ + { "set 2,(iy+%02xh)", A_8P }, /* fdcb..d6 */ + { "ld a,set 2,(iy+%02xh)", A_8P }, /* fdcb..d7 [undoc] */ + + { "ld b,set 3,(iy+%02xh)", A_8P }, /* fdcb..d8 [undoc] */ + { "ld c,set 3,(iy+%02xh)", A_8P }, /* fdcb..d9 [undoc] */ + { "ld d,set 3,(iy+%02xh)", A_8P }, /* fdcb..da [undoc] */ + { "ld e,set 3,(iy+%02xh)", A_8P }, /* fdcb..db [undoc] */ + { "ld h,set 3,(iy+%02xh)", A_8P }, /* fdcb..dc [undoc] */ + { "ld l,set 3,(iy+%02xh)", A_8P }, /* fdcb..dd [undoc] */ + { "set 3,(iy+%02xh)", A_8P }, /* fdcb..de */ + { "ld a,set 3,(iy+%02xh)", A_8P }, /* fdcb..df [undoc] */ + + { "ld b,set 4,(iy+%02xh)", A_8P }, /* fdcb..e0 [undoc] */ + { "ld c,set 4,(iy+%02xh)", A_8P }, /* fdcb..e1 [undoc] */ + { "ld d,set 4,(iy+%02xh)", A_8P }, /* fdcb..e2 [undoc] */ + { "ld e,set 4,(iy+%02xh)", A_8P }, /* fdcb..e3 [undoc] */ + { "ld h,set 4,(iy+%02xh)", A_8P }, /* fdcb..e4 [undoc] */ + { "ld l,set 4,(iy+%02xh)", A_8P }, /* fdcb..e5 [undoc] */ + { "set 4,(iy+%02xh)", A_8P }, /* fdcb..e6 */ + { "ld a,set 4,(iy+%02xh)", A_8P }, /* fdcb..e7 [undoc] */ + + { "ld b,set 5,(iy+%02xh)", A_8P }, /* fdcb..e8 [undoc] */ + { "ld c,set 5,(iy+%02xh)", A_8P }, /* fdcb..e9 [undoc] */ + { "ld d,set 5,(iy+%02xh)", A_8P }, /* fdcb..ea [undoc] */ + { "ld e,set 5,(iy+%02xh)", A_8P }, /* fdcb..eb [undoc] */ + { "ld h,set 5,(iy+%02xh)", A_8P }, /* fdcb..ec [undoc] */ + { "ld l,set 5,(iy+%02xh)", A_8P }, /* fdcb..ed [undoc] */ + { "set 5,(iy+%02xh)", A_8P }, /* fdcb..ee */ + { "ld a,set 5,(iy+%02xh)", A_8P }, /* fdcb..ef [undoc] */ + + { "ld b,set 6,(iy+%02xh)", A_8P }, /* fdcb..f0 [undoc] */ + { "ld c,set 6,(iy+%02xh)", A_8P }, /* fdcb..f1 [undoc] */ + { "ld d,set 6,(iy+%02xh)", A_8P }, /* fdcb..f2 [undoc] */ + { "ld e,set 6,(iy+%02xh)", A_8P }, /* fdcb..f3 [undoc] */ + { "ld h,set 6,(iy+%02xh)", A_8P }, /* fdcb..f4 [undoc] */ + { "ld l,set 6,(iy+%02xh)", A_8P }, /* fdcb..f5 [undoc] */ + { "set 6,(iy+%02xh)", A_8P }, /* fdcb..f6 */ + { "ld a,set 6,(iy+%02xh)", A_8P }, /* fdcb..f7 [undoc] */ + + { "ld b,set 7,(iy+%02xh)", A_8P }, /* fdcb..f8 [undoc] */ + { "ld c,set 7,(iy+%02xh)", A_8P }, /* fdcb..f9 [undoc] */ + { "ld d,set 7,(iy+%02xh)", A_8P }, /* fdcb..fa [undoc] */ + { "ld e,set 7,(iy+%02xh)", A_8P }, /* fdcb..fb [undoc] */ + { "ld h,set 7,(iy+%02xh)", A_8P }, /* fdcb..fc [undoc] */ + { "ld l,set 7,(iy+%02xh)", A_8P }, /* fdcb..fd [undoc] */ + { "set 7,(iy+%02xh)", A_8P }, /* fdcb..fe */ + { "ld a,set 7,(iy+%02xh)", A_8P }, /* fdcb..ff [undoc] */ + } +}; + +int disassemble(unsigned short pc) +{ + int i, j; + struct opcode *code; + int addr; + + addr = pc; + i = mem_read(pc++); + if (!major[i].name) + { + j = major[i].args; + i = mem_read(pc++); + if (!minor[j][i].name) + { + /* dd cb or fd cb; offset comes *before* instruction */ + j = minor[j][i].args; + pc++; /* skip over offset */ + i = mem_read(pc++); + } + code = &minor[j][i]; + } + else + { + code = &major[i]; + } + printf ("%04x ", addr); + for (i = 0; i < ((pc + arglen(code->args) - addr) & 0xffff); i++) + printf("%02x ", mem_read((addr + i) & 0xffff)); + for (; i < 4; i++) + printf(" "); + printf(" "); + switch (code->args) { + case A_16: /* 16-bit number */ + printf (code->name, mem_read((pc + 1) & 0xffff), mem_read(pc)); + break; + case A_8X2: /* Two 8-bit numbers */ + printf (code->name, mem_read(pc), mem_read((pc + 1) & 0xffff)); + break; + case A_8: /* One 8-bit number */ + printf (code->name, mem_read(pc)); + break; + case A_8P: /* One 8-bit number before last opcode byte */ + printf (code->name, mem_read((pc - 2) & 0xffff)); + break; + case A_0: /* No args */ + case A_0B: /* No args, backskip over last opcode byte */ + printf (code->name); + break; + case A_8R: /* One 8-bit relative address */ + printf (code->name, (pc + 1 + (char) mem_read(pc)) & 0xffff); + break; + } + putchar ('\n'); + pc += arglen(code->args); + return pc; /* return the location of the next instruction */ +} @@ -0,0 +1,13 @@ +. Avoid running Model I/III-only xtrs utilities on Model 4 +remove cd/cmd:#D# +remove pwd/cmd:#D# +remove unix/cmd:#D# +remove mount/cmd:#D# +remove umount/cmd:#D# +remove truedam/cmd:#D# +rename cd6/cmd:#D# cd/cmd:#D# +rename pwd6/cmd:#D# pwd/cmd:#D# +rename unix6/cmd:#D# unix/cmd:#D# +rename mount6/cmd:#D# mount/cmd:#D# +rename umount6/cmd:#D# umount/cmd:#D# +rename truedam6/cmd:#D# truedam/cmd:#D# diff --git a/dskspec.html b/dskspec.html new file mode 100644 index 0000000..d9347a6 --- /dev/null +++ b/dskspec.html @@ -0,0 +1,590 @@ +<HTML> +<HEAD> + <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> + <TITLE>Common File Formats for Emulated TRS-80 Floppy Disks</TITLE> +</HEAD> +<BODY> + +<H1> +Common File Formats for Emulated TRS-80 Floppy Disks</H1> +Tim Mann +<BR><A HREF="http://tim-mann.org/">http://tim-mann.org</a> +<BR>Last revised 18-Jul-99 + +<P>Most current TRS-80 Model I/III/4 emulators use one of three common +file formats for emulated floppy disk media. The extension .DSK is +usually used for all three formats, and some emulators transparently +support two or three of them, while others support only one. The two +most common formats both originated with emulators written by Jeff +Vavasour, while the third originated with an emulator written by David Keil, +so I'll take the liberty of giving the formats names that use their initials. + +<ul> +<li>The JV1 format originated in Jeff's Model I emulator for MS-DOS. It's +a very simple, limited format that can represent only single-density media +with 256 byte sectors and a Model I directory on track 17.<p> + +<li>The JV3 format originated in Jeff's Model III/4 emulator for MS-DOS. +It is much more flexible than JV1, but still cannot represent everything +the real hardware can do, so some copy-protected disks cannot be represented. +The format has been extended to support 8-inch floppies and sector sizes of +other than 256 bytes, but not all emulators support these extensions. I +indicate below which features are extensions and which emulators are known +to support which extensions.<p> + +<li>The DMK format originated in David's Model III/4 emulator for Windows. +Unlike JV1 and JV3, DMK is able +to represent essentially everything that a real TRS-80 floppy disk +controller could do, including copy protected TRS-80 disks. +</ul> + +<p>This document describes the JV1 and JV3 formats in complete detail and +indicates (where known) which emulators support which. +DMK is quite different from JV1 and JV3 and is not described +in this document, but there is +<a href= +"http://discover-net.net/~dmkeil/trs80/trstech.htm#Technical-DMK-disks" +>a description on David Keil's Web page</a>. +You probably +don't need any of this information unless you are writing an emulator +or working with unusual disks. + +<H2> +The JV1 Format</H2> +The JV1 format is simply an array of 256-byte sectors stored in a file. +Byte 0 of the file is byte 0 of track 0, sector 0; byte 256 is byte 0 of +track 0, sector 1, and so forth. There are 10 sectors per track (i.e., +single density), numbered 0 through 9, and only one side. Tracks are numbered +starting at 0. All sectors on track 17 are formatted with the nonstandard +data address mark 0xFA, indicating a TRSDOS 2.3 directory. All other sectors +are formatted with the standard data address mark 0xFB. + +<P>Note: In emulations of the WD1791/3 floppy disk controller used in the +Model III and 4, it is best to present track 17 as being formatted with +the standard deleted data address mark 0xF8. The WD1791/3 cannot write +0xFA, and upon reading, returns it as 0xFB. So Model III/4 operating systems +use 0xF8 on the directory track instead. +<H2> +The JV3 Format</H2> +The JV3 format consists of a fixed-size array of <I>sector headers</I>, +followed by an area containing the data for each sector. As an extension +to allow for more sectors, a second block of sector headers and data can +follow. Here is the format in pseudo-C notation. The length of each data +area depends on the content of the headers, as described below. +<PRE>typedef struct { + SectorHeader headers1[2901]; + unsigned char writeprot; + unsigned char data1[]; + SectorHeader headers2[2901]; + unsigned char padding; + unsigned char data2[]; +} JV3;</PRE> + +<H3> +Write Protect and Padding</H3> +The field <I>writeprot</I> should normally contain 0xFF. As an extension, +a value of 0x00 in his field indicates that the disk is write-protected +(i.e., it is not writable). + +<P>The field <I>padding</I> is currently unused. It should contain 0xFF. +<H3> +The Sector Header</H3> +Each sector in a JV3 file is described by a three-byte header. The first +byte gives the sector's track number, the second gives its sector number +on the track, and the third is a set of flags. +<PRE>typedef struct { + unsigned char track; + unsigned char sector; + unsigned char flags; +} SectorHeader; + +#define JV3_DENSITY 0x80 /* 1=dden, 0=sden */ +#define JV3_DAM 0x60 /* data address mark code; see below */ +#define JV3_SIDE 0x10 /* 0=side 0, 1=side 1 */ +#define JV3_ERROR 0x08 /* 0=ok, 1=CRC error */ +#define JV3_NONIBM 0x04 /* 0=normal, 1=short */ +#define JV3_SIZE 0x03 /* in used sectors: 0=256,1=128,2=1024,3=512 + in free sectors: 0=512,1=1024,2=128,3=256 */ + +#define JV3_FREE 0xFF /* in track and sector fields of free sectors */ +#define JV3_FREEF 0xFC /* in flags field, or'd with size code */ +</PRE> + +<p>The <I>track</I> field gives both the physical track number on which the +sector lies and the logical track number that is formatted in the sector's +ID. Numbering starts from 0. Thus it is not possible to represent a copy-protected +disk where sectors were deliberately formatted with incorrect track numbers. +The format allows for 255 tracks (numbered 0 through 0xFE), but emulators +may impose lower limits. You can expect at least 80 tracks to be supported; +xtrs currently allows up to 96. + +<P>The <I>sector</I> field gives the logical sector number that is formatted +into the sector's ID field. The physical order of sectors on the track +is not explicitly represented, but xtrs (and perhaps other emulators) present +them in the order they are recorded in the JV3 file; thus when a TRS-80 +program formats a track with interleave and reads back the sector ids, +they come back in the same interleaved order. + +<P>The <I>flags</I> field packs in a lot of information: +<UL> +<LI> +If JV3_DENSITY is set, the sector was formatted in double density (MFM); +if not, it was formatted in single density (FM).<p></LI> + +<LI> +The 2-bit JV3_DAM field encodes the sector's data address mark. Here are +the basic meanings of the codes: + +<table border cols=3 width="90%" nosave> +<tr> +<td>JV3_DAM value</td> +<td>Single density</td> +<td>Double density</td> +</tr> +<tr> +<td>0x00</td> +<td>0xFB (Normal)</td> +<td>0xFB (Normal)</td> +</tr> +<tr> +<td>0x20</td> +<td>0xFA (User-defined)</td> +<td>0xF8 (Deleted)</td> +</tr> +<tr> +<td>0x40</td> +<td>0xF9 (User-defined)</td> +<td>Invalid; unused</td> +</tr> +<tr> +<td>0x60</td> +<td>0xF8 (Deleted)</td> +<td>Invalid; unused</td> +</tr> +</table> + +<p>The treatment of single density data address marks by the Western +Digital 1771 and 179x controllers used in original TRS-80 hardware is +rather inconvenient. The WD1771 (used in the TRS-80 Model I) is capable +of writing any of the four DAMs shown above, and can distinguish +amongst all four on reading. The WD179x (used in the Model III and 4), +however, can write only 0xFB or 0xF8, and on reading, it cannot +distinguish between 0xFB and 0xFA, or between 0xF8 and 0xF9. + +<p>TRS-80 operating systems differentiate directory sectors from +ordinary data sectors by giving them different data address marks. +Unfortunately, Model I TRSDOS uses 0xFA on directory sectors, which a +WD179x cannot write and cannot distinguish from the 0xFB used on data +sectors. Other Model I operating systems (such as LDOS) typically +write 0xF8 on directory sectors but accept either 0xFA or 0xF8 when +reading them, allowing them to read TRSDOS disks. Compatibility +problems remain when attempting to use Model I TRSDOS disks on +a Model III or 4, and when attempting to use LDOS disks with Model I +TRSDOS. + +<p>An emulator can easily paper over these compatibility problems by +behaving differently from the real hardware. All currently known +emulators do so, making (at least) the following changes to strictly +correct behavior: (1) If TRS-80 software attempts to write or format a +0xF8 DAM in single density, it is instead recorded in the JV3 header +as 0xFA. (2) If TRS-80 software reads a single density sector that +has the 0xFA DAM using an emulated WD179x, it is returned as 0xF8 +instead. These changes hide all the DOS compatibility problems +described in the previous paragraph. + +<p>It might be worthwhile to make precisely correct behavior available +as a run-time emulator option, as this might allow more types of +"protected" self-booting TRS-80 disks to work correctly. This option +is available in xtrs version 3.2 and later, but not in any other known +emulators. + +<p>The following tables summarize the DAM behavior of +both the WD1771 and the WD179x. On the WD1771, read status bits 6,5 are +used to report the DAM of the sector just read, while write command +bits 1,0 select the DAM to write; on the WD179x, only read status bit 5 +and write command bit 0 are significant: read status bit 6 is always +0, while write command bit 1 is used for a function unrelated to DAM +selection. The behavior of these bits on the actual hardware is shown +first in each cell; the modifications typically made by emulators +follow in parentheses. It should be emphasized that WD1771 behavior +shown here correctly describes the actual hardware, even though it +contradicts the Western Digital data sheet; the data sheet erroneously +transposes the column headings for status bits 6 and 5. As an aside, +it seems likely that the engineers who designed the WD179x were misled +by this error, as the value that the WD179x returns in bit 5 is the +value that the WD1771 was documented as returning in bit 5, but that it +actually returns in bit 6. + +<p>Read status bits 6,5: +<table border cols=4 width="90%" nosave> +<tr> +<td>DAM</td> +<td>WD1771</td> +<td>WD179x single</td> +<td>WD179x double</td> +</tr> +<tr> +<td>0xFB</td> +<td>0,0</td> +<td>0,0</td> +<td>0,0</td> +</tr> +<tr> +<td>0xFA</td> +<td>0,1</td> +<td>0,0 (0,1)</td> +<td>impossible</td> +</tr> +<tr> +<td>0xF9</td> +<td>1,0</td> +<td>0,1</td> +<td>impossible</td> +</tr> +<tr> +<td>0xF8</td> +<td>1,1</td> +<td>0,1</td> +<td>0,1</td> +</tr> +</table> + +<p>Write command bits 1,0: +<table border cols=4 width="90%" nosave> +<tr> +<td>Bits</td> +<td>WD1771</td> +<td>WD179x single</td> +<td>WD179x double</td> +</tr> +<tr> +<td>0,0</td> +<td>0xFB</td> +<td>0xFB</td> +<td>0xFB</td> +</tr> +<tr> +<td>0,1</td> +<td>0xFA</td> +<td>0xF8 (0xFA)</td> +<td>0xF8</td> +</tr> +<tr> +<td>1,0</td> +<td>0xF9</td> +<td>0xFB</td> +<td>0xFB</td> +</tr> +<tr> +<td>1,1</td> +<td>0xF8 (0xFA)</td> +<td>0xF8 (0xFA)</td> +<td>0xF8</td> +</tr> +</table> + +<p></LI> + +<LI> +JV3_SIDE reflects both the physical side on which the sector is recorded +and the side number formatted in the sector's ID. Thus it is not possible +to represent a copy-protected disk where sectors were formatted with incorrect +side numbers or with values other than 0 or 1 in the sector ID's 8-bit +side field.<p></LI> + +<LI> +JV3_ERROR, if set, indicates that the sector should show a data CRC error +when read. The actual value of the two CRC bytes is not represented.<p></LI> + +<LI> +JV3_NONIBM is an xtrs-specific extension that partially supports the WD1771's +feature of "non-IBM" sector sizes. Only a small subset of the functionality +is supported, with some special kludges specifically to make the VTOS 3.0 +copy protection scheme work. It is probably best to treat this bit as a +"must be zero" field in other emulators; see the xtrs source code if +you are really sure you want to know how it works, and keep your barf bag +handy.<p></LI> + +<LI> +JV3_SIZE is an extension to support sector sizes of other than 256 bytes. +This field is encoded differently depending on whether the sector +is in use or free (not in use): + +<p><table border cols=4 width="90%" nosave> +<tr> +<td>Size</td> +<td>IBM size code in sector ID</td> +<td>JV3_SIZE field value if in use</td> +<td>JV3_SIZE field value if free</td> +</tr> +<tr> +<td>128</td> +<td>00</td> +<td>1</td> +<td>2</td> +</tr> +<tr> +<td>256</td> +<td>01</td> +<td>0</td> +<td>3</td> +</tr> +<tr> +<td>512</td> +<td>02</td> +<td>3</td> +<td>0</td> +</tr> +<tr> +<td>1024</td> +<td>03</td> +<td>2</td> +<td>1</td> +</tr> +</table> + +<p>Thus, if a sector is in use, xor'ing its JV3_SIZE field with 1 +gives the IBM size code that appears in its sector ID size field. +If a sector is free, xor'ing its JV3_SIZE field with 2 gives its IBM +size code. This representation +was chosen because the original JV3 format supports only 256-byte sectors, +always places zeros in the JV3_SIZE field for sectors that are in use, +and always places ones in the field for sectors that are free.</LI> +</UL> + +<p>If a sector header is not in use (free), its track and sector fields +must contain 0xFF. Its flags field must contain 0xFC plus the +appropriate free JV3_SIZE field value given above. + +<H3> +The Data Blocks</H3> +For each sector header, there is one data block. The blocks are placed +in the same order as the headers that describe them, and are tightly packed. +Thus to find the data block for the <I>n</I>th header, you need to know +the total size of data blocks 0 to <I>n </I>- 1. A sector header marked +as free <I>does</I> have a corresponding data block, of the size indicated +in its size field. However, the file can (and in fact should; see below) +end immediately after the last in-use data block. +<H3> +The Second Header Block</H3> +The second header block is present if and only if the file is long enough +to contain it. It starts immediately after the last data block described +by the first header block. Thus to find the second header block, you need +to know the total size of all data blocks described by the first header +block. + +<P>It's obvious, by the way, how to extend the format to more than two +header blocks, but no known emulators support more than two. Having more +than two is not useful to describe any floppy format that was supported +by any real TRS-80. One block is sufficient for 5.25-inch DD drives, +and two are sufficient for 8-inch drives, 5.25-inch HD drives, +or 3.5-inch HD drives. +<H3> +Allocating and Freeing Sectors</H3> +Allocating and freeing sectors is a little tricky (if you support the extension +to sizes other than 256 bytes). When a new emulated disk is first created, +it should have one header block, filled with free 256-byte sectors. When +a new sector is formatted, if a free sector of the correct size exists, +it can be reused; otherwise, the next sector after the highest in-use sector +can be chosen and changed to the correct size. In the latter case, since +there is no data after the sector that is being resized, resizing it does +not require anything to move. When a sector is erased (because the track +it is on is being reformatted), it should normally be freed without changing +its size, so that the data of later sectors need not be moved. + +<P>If the highest in-use sector is freed, the emulator should search backward +for the new highest in-use sector, and should truncate the file to eliminate +any sector data for trailing free sectors, as well as the second header +block if it is no longer needed. Why do this? If the user reformats +some sectors to be shorter or longer than they used to be, the nominal +start of the second header block (as defined above) will shift. If you +have garbage at the end of the file, and the block shifts to start somewhere +in the garbage, then when you read the file back in again later, you'll +have a second header block full of garbage that will confuse you. +<H2> +Support</H2> +Here is a summary of which emulators support which formats and which features. +The data is believed current as of this document's revision +date. Future versions of some of +these emulators may support more (or fewer!) features. If an emulator does +not support a feature, it will still work with disks created by emulators +that do support the feature, as long as those disks didn't require that +feature when they were created. For example, an emulator that supports +the second header block will not create a second header block unless you +format more that 2901 sectors on one disk; note that an 80 track, double +sided, double density 5.25-inch disk contains less than 2901 sectors. +<BR> +<TABLE BORDER COLS=8 WIDTH="100%" NOSAVE > +<TR> +<TD> </TD> + +<TD>JV1</TD> + +<TD>JV3 Basic features</TD> + +<TD>JV3 Write protect</TD> + +<TD>JV3 Second header block</TD> + +<TD>JV3 Sizes other than 256</TD> + +<TD>JV3 Non-IBM kludge</TD> + +<TD>DMK</TD> +</TR> + +<TR> +<TD>Jeff Vavasour Model I v3.02u</TD> + +<TD>Yes</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> +</TR> + +<TR> +<TD>Jeff Vavasour Model III/4 v2.3</TD> + +<TD>No</TD> + +<TD>Yes</TD> + +<TD>No (future)</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> +</TR> + +<TR> +<TD>Matthew Reed Model I/III v1.10</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>No (future)</TD> + +<TD>No (future)</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> +</TR> + +<TR> +<TD>Matthew Reed Model 4 v1.01</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> +</TR> + +<TR> +<TD>xtrs Model I/III/4 v3.7</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> +</TR> + +<TR> +<TD>WinTRS80 Model I/III/4 v1.02</TD> + +<TD>Yes</TD> + +<TD>Yes</TD> + +<TD>Respected but not generated</TD> + +<TD>Yes</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> +</TR> + +<TR> +<TD>Yves Lempereur Model I</TD> + +<TD>Yes</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> + +<TD>No</TD> +</TR> + +<TR> +<TD>David Keil Model III/4</TD> + +<TD>Yes</TD> + +<TD>Yes, with some limitations</TD> + +<TD>Respected but not generated</TD> + +<TD>No</TD> + +<TD>Yes</TD> + +<TD>No</TD> + +<TD>Yes</TD> +</TR> + + +</TABLE> + +<p>Additional information or corrections for this table are welcome. + +<HR> +<p><a href="../trs80.html">Back to the TRS-80 Page</a> | +<a href="../">My home page</a> + +</BODY> +</HTML> @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +#include "z80.h" +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> + +extern char *program_name; + +void debug(const char *fmt, ...) +{ + va_list args; + char xfmt[2048]; + + strcpy(xfmt, "debug: "); + strcat(xfmt, fmt); + /*strcat(xfmt, "\n");*/ + va_start(args, fmt); + vfprintf(stderr, xfmt, args); + va_end(args); +} + +void error(const char *fmt, ...) +{ + va_list args; + char xfmt[2048]; + + strcpy(xfmt, program_name); + strcat(xfmt, " error: "); + strcat(xfmt, fmt); + strcat(xfmt, "\n"); + va_start(args, fmt); + vfprintf(stderr, xfmt, args); + va_end(args); +} + +void fatal(const char *fmt, ...) +{ + va_list args; + char xfmt[2048]; + + strcpy(xfmt, program_name); + strcat(xfmt, " fatal error: "); + strcat(xfmt, fmt); + strcat(xfmt, "\n"); + va_start(args, fmt); + vfprintf(stderr, xfmt, args); + va_end(args); + exit(1); +} diff --git a/expall.bas b/expall.bas new file mode 100644 index 0000000..dfd5962 --- /dev/null +++ b/expall.bas @@ -0,0 +1,22 @@ +20 CLEAR 1000 +40 REM-- List an LDOS directory by opening and reading DIR/SYS +50 REM-- and use the xtrs export program to export all files +60 LINE INPUT "Drive? "; DR$ +80 OPEN "ri", 1, "dir/sys.rs0lt0ff:" + DR$, 32 +100 FIELD 1, 1 AS AT$, 1 AS FL$, 1 AS DT$, 1 AS EF$, 1 AS RL$, 8 AS NM$, 3 AS NX$, 2 AS OP$, 2 AS UP$, 2 AS ER$, 10 AS XT$ +120 GET 1, 16 +140 N = 0 +160 IF EOF(1) THEN END +180 N = N + 1 +200 GET 1 +220 IF (ASC(AT$) AND 16) = 0 THEN GOTO 160 +240 I=INSTR(NM$, " ") +260 IF I THEN NH$ = LEFT$(NM$, I-1) ELSE NH$ = NM$ +280 I = INSTR(NX$, " ") +300 IF I THEN NT$ = LEFT$(NX$, I-1) ELSE NT$ = NX$ +320 REM print n; " "; +340 NF$ = NH$ + "/" + NT$ +360 PRINT NF$ +380 UF$ = NH$ + "." + NT$ +400 CMD "export -l " + NF$ + ".rs0lt0ff:" + DR$ + " " + UF$ +420 GOTO 160 diff --git a/export.bas b/export.bas new file mode 100644 index 0000000..804f63f --- /dev/null +++ b/export.bas @@ -0,0 +1,33 @@ +20 REM-- EXPORT/BAS +40 REM-- Uses xtrs 1.3 export feature to copy a TRS-80 +60 REM-- file to a Unix file. +70 REM-- Timothy Mann, 10/30/96 +75 REM-- Last modified on Wed Oct 1 17:37:25 PDT 1997 by mann +80 CLEAR 5000 +120 LINE INPUT "TRS-80 input file? ";F$ +130 LINE INPUT "Unix output file? ";C$ +140 OPEN"RO",1,F$,1 +160 FIELD #1,1 AS B$ +180 INPUT "Convert newlines (y/n)";CN$ +200 CN% = LEFT$(CN$,1) = "y" OR LEFT$(CN$,1) = "Y" +205 OUT &HD0,0 +210 OUT &HD0,2 +220 FOR I% = 1 TO LEN(C$) +240 OUT &HD1, ASC(MID$(C$, I%)) +260 NEXT I% +270 OUT &HD1,0 +300 S% = INP(&HD0) +320 IF S% <> 0 THEN 520 +340 IF LOC(1) >= LOF(1) THEN 440 +360 GET#1 +380 B% = ASC(B$) +400 IF CN% THEN IF B% = &H0D THEN B% = &H0A +420 OUT &HD1, B%:GOTO 340 +440 CLOSE #1 +445 S% = INP(&HD0) +450 IF S% <> 0 THEN 520 +460 OUT &HD0,3 +480 S% = INP(&HD0) +500 IF S% = 0 THEN 540 +520 PRINT:PRINT "Error ";S% +540 CLOSE diff --git a/export.cmd b/export.cmd Binary files differnew file mode 100644 index 0000000..0581db5 --- /dev/null +++ b/export.cmd diff --git a/export.lst b/export.lst new file mode 100644 index 0000000..6dc11c7 --- /dev/null +++ b/export.lst @@ -0,0 +1,480 @@ + 1: ;; export.z + 2: ;; Timothy Mann, 8/24/97 + 3: ;; $Date: 2006/05/15 00:48:57 $ + 4: ;; + 5: ;; Copyright (c) 1997, Timothy Mann + 6: ;; + 7: ;; This software may be copied, modified, and used for any + 8: ;; purpose without fee, provided that (1) the above copyright + 9: ;; notice is retained, and (2) modified versions are clearly + 10: ;; marked as having been modified, with the modifier's name and + 11: ;; the date included. + 12: ;; + 13: ;; Use xtrs emulator traps to copy a file from TRS-80 to Unix + 14: ;; Usage: EXPORT [-lne] fromfile [unixfile] + 15: ;; Parameter -l will convert the Unix file to lower case. + 16: ;; (Needed for NEWDOS/80. They insist on uppercasing the command line.) + 17: ;; If the -n parameter is given, each carriage return ('\r') + 18: ;; in the TRS-80 file is converted to a newline ('\n') in the Unix file. + 19: ;; The program tries to determine what DOS it is running on and use + 20: ;; the correct FCB end of file convention, but this works only on + 21: ;; TRSDOS, LDOS, and NEWDOS/80. For other DOSes that use the + 22: ;; NEWDOS/80 convention (such as DOSPLUS), give the -e paramter. + 23: ;; If the unixfile parameter is omitted, the fromfile parameter is used, + 24: ;; with '/' changed to '.'. + 25: + 26: ;; Model I/III addresses + 27: 441C @fspec equ 441ch + 28: 4420 @init equ 4420h + 29: 4424 @open equ 4424h + 30: 4428 @close equ 4428h + 31: 4436 @read equ 4436h + 32: 4439 @write equ 4439h + 33: 4409 @error equ 4409h + 34: 402D @exit equ 402dh + 35: 4030 @abort equ 4030h + 36: 001B @put equ 001bh + 37: 401D dodcb$ equ 401dh + 38: + 39: ;; Model 4 SVCs + 40: 0028 @svc equ 40 ; rst address for SVCs + 41: ;@svc equ 5 ; older zmac requires 8080-style "rst 5" + 42: 004E @fspec6 equ 78 + 43: 003A @init6 equ 58 + 44: 003B @open6 equ 59 + 45: 003C @close6 equ 60 + 46: 0043 @read6 equ 67 + 47: 004B @write6 equ 75 + 48: 001A @error6 equ 26 + 49: 0016 @exit6 equ 22 + 50: 0015 @abort6 equ 21 + 51: 000A @dsply6 equ 10 + 52: + 53: ;; Model 4 only: file init or open with wrong LRL. Can be ignored. + 54: 002A lrlerr equ 42 + 55: + 56: 5200 org 5200h + 57: + 58: ;; Jump tables for OS independence + 59: 5200 startj: + 60: 5200 CD1C44 fspec: call @fspec + 61: 5203 C9 ret + 62: 5204 CD2044 init: call @init + 63: 5207 C9 ret + 64: 5208 CD2444 open: call @open + 65: 520B C9 ret + 66: 520C CD2844 close: call @close + 67: 520F C9 ret + 68: 5210 CD3644 reed: call @read + 69: 5213 C9 ret + 70: 5214 CD3944 write: call @write + 71: 5217 C9 ret + 72: 5218 CD0944 error: call @error + 73: 521B C9 ret + 74: 521C CD2D40 exit: call @exit + 75: 521F C9 ret + 76: 5220 CD3040 abort: call @abort + 77: 5223 C9 ret + 78: 5224 CDB553 dsply: call dsply5 + 79: 5227 C9 ret + 80: 5228 CDE753 getern: call getern5 + 81: 522B C9 ret + 82: 522C endj: + 83: + 84: ; Model 4 + 85: 522C startj6: + 86: 522C 3E4E ld a, @fspec6 + 87: 522E EF rst @svc + 88: 522F C9 ret + 89: 5230 3E3A ld a, @init6 + 90: 5232 EF rst @svc + 91: 5233 C9 ret + 92: 5234 3E3B ld a, @open6 + 93: 5236 EF rst @svc + 94: 5237 C9 ret + 95: 5238 3E3C ld a, @close6 + 96: 523A EF rst @svc + 97: 523B C9 ret + 98: 523C 3E43 ld a, @read6 + 99: 523E EF rst @svc + 100: 523F C9 ret + 101: 5240 3E4B ld a, @write6 + 102: 5242 EF rst @svc + 103: 5243 C9 ret + 104: 5244 3E1A ld a, @error6 + 105: 5246 EF rst @svc + 106: 5247 C9 ret + 107: 5248 3E16 ld a, @exit6 + 108: 524A EF rst @svc + 109: 524B C9 ret + 110: 524C 3E15 ld a, @abort6 + 111: 524E EF rst @svc + 112: 524F C9 ret + 113: 5250 3E0A ld a, @dsply6 + 114: 5252 EF rst @svc + 115: 5253 C9 ret + 116: 5254 CDF753 call getern6 + 117: 5257 C9 ret + 118: + 119: ; Nonzero for LDOS ern convention + 120: 5258 01 ernldos: db 1 + 121: + 122: ; Emulator trap instructions, byte-reversed for use in defw: + 123: 30ED emt_open equ 30edh + 124: 31ED emt_close equ 31edh + 125: 32ED emt_read equ 32edh + 126: 33ED emt_write equ 33edh + 127: 34ED emt_lseek equ 34edh + 128: 35ED emt_strerror equ 35edh + 129: + 130: 0003 EO_ACCMODE equ 3q + 131: 0000 EO_RDONLY equ 0q + 132: 0001 EO_WRONLY equ 1q + 133: 0002 EO_RDWR equ 2q + 134: 0040 EO_CREAT equ 100q + 135: 0080 EO_EXCL equ 200q + 136: 0200 EO_TRUNC equ 1000q + 137: 0400 EO_APPEND equ 2000q + 138: + 139: 5259 export: + 140: 5259 3A0A00 ld a, (000ah) ; Model 4? + 141: 525C FE40 cp 40h + 142: 525E 280D jr z, not4 + 143: 5260 E5 push hl + 144: 5261 110052 ld de, startj + 145: 5264 212C52 ld hl, startj6 + 146: 5267 012C00 ld bc, endj - startj + 147: 526A EDB0 ldir + 148: 526C E1 pop hl + 149: 526D not4: + 150: 526D 3A2744 ld a, (4427h) ; system id for Newdos/80... + 151: 5270 D682 sub 82h ; ...should be 82h (v2.0) + 152: 5272 2805 jr z, gotid + 153: 5274 3A1F44 ld a, (441fh) ; system version number for most other DOSes + 154: 5277 D613 sub 13h ; TRSDOS 1.3? + 155: 5279 325852 gotid: ld (ernldos), a + 156: + 157: 527C 7E flag0: ld a, (hl) ; look for flags + 158: 527D FE20 cp ' ' + 159: 527F DA9953 jp c, usage ; error if line ends here + 160: 5282 2003 jr nz, flag1 + 161: 5284 23 inc hl + 162: 5285 18F5 jr flag0 + 163: 5287 FE2D flag1: cp '-' + 164: 5289 202C jr nz, fromf + 165: 528B 23 inc hl + 166: 528C 7E ld a, (hl) + 167: 528D F620 flag3: or 20h + 168: 528F FE65 cp 'e' + 169: 5291 2006 jr nz, flagl + 170: 5293 97 sub a + 171: 5294 325852 ld (ernldos), a + 172: 5297 1815 jr flag2 + 173: 5299 FE6C flagl: cp 'l' + 174: 529B 2007 jr nz, flagn ; check for next flag + 175: 529D 3E01 ld a, 1 + 176: 529F 32FC53 ld (lflag), a + 177: 52A2 180A jr flag2 + 178: 52A4 FE6E flagn: cp 'n' + 179: 52A6 C29953 jp nz, usage ; unknown flag + 180: 52A9 3E01 ld a, 1 + 181: 52AB 32FD53 ld (nflag), a + 182: 52AE 23 flag2: inc hl + 183: 52AF 7E ld a, (hl) + 184: 52B0 FE20 cp ' ' + 185: 52B2 20D9 jr nz, flag3 ; another flag follows + 186: 52B4 23 inc hl + 187: 52B5 18C5 jr flag0 + 188: + 189: 52B7 116A54 fromf: ld de, dcb ; ready to get LDOS filename from (HL) + 190: 52BA 22FE53 ld (lfname), hl ; save if needed to default Unix name + 191: 52BD CD0052 call fspec + 192: 52C0 C29953 jp nz, usage + 193: + 194: 52C3 7E unix0: ld a, (hl) ; scan over Unix filename + 195: 52C4 FE20 cp ' ' ; first skip spaces + 196: 52C6 3814 jr c, usetrs ; if no Unix name, use translated TRS name + 197: 52C8 2003 jr nz, unix1 + 198: 52CA 23 inc hl + 199: 52CB 18F6 jr unix0 + 200: 52CD 119A54 unix1: ld de, iobuf ; copy Unix filename + 201: 52D0 3E20 ld a, ' ' + 202: 52D2 BE unix2: cp (hl) + 203: 52D3 EDA0 ldi + 204: 52D5 38FB jr c, unix2 + 205: 52D7 1B dec de + 206: 52D8 97 sub a + 207: 52D9 12 ld (de), a ; NUL terminate Unix name + 208: 52DA 181C jr gotu + 209: + 210: 52DC 2AFE53 usetrs: ld hl, (lfname) ; translate TRS-80 name to Unix + 211: 52DF 119A54 ld de, iobuf + 212: 52E2 7E ut1: ld a, (hl) + 213: 52E3 FE3A cp ':' ; drivespec? + 214: 52E5 280F jr z, utdone ; done if so + 215: 52E7 FE21 cp ' '+1 ; end of line? + 216: 52E9 380B jr c, utdone ; done if so + 217: 52EB FE2F cp '/' ; change '/' to '.' for extension + 218: 52ED 2002 jr nz, notsl + 219: 52EF 3E2E ld a, '.' + 220: 52F1 12 notsl: ld (de), a + 221: 52F2 23 inc hl + 222: 52F3 13 inc de + 223: 52F4 18EC jr ut1 + 224: 52F6 97 utdone: sub a ; NUL-terminate Unix name + 225: 52F7 12 ld (de), a + 226: + 227: 52F8 219A54 gotu: ld hl, iobuf + 228: 52FB 116A54 ld de, dcb + 229: 52FE 0600 ld b, 0 + 230: 5300 CD0852 call open ; open the TRS-80 file + 231: 5303 E1 pop hl + 232: 5304 280B jr z, uname + 233: 5306 FE2A cp lrlerr + 234: 5308 2807 jr z, uname + 235: 530A 4F ld c, a + 236: 530B CD1852 call error + 237: 530E C32052 jp abort + 238: + 239: 5311 219A54 uname: ld hl, iobuf ; path + 240: 5314 3AFC53 ld a, (lflag) + 241: 5317 B7 or a + 242: 5318 C4CA53 call nz, lcconv ; convert filename to lower case + 243: 531B 014102 ld bc, EO_WRONLY|EO_CREAT|EO_TRUNC + 244: 531E 11B601 ld de, 0666q ; mode + 245: 5321 ED30 defw emt_open ; open the Unix file + 246: 5323 2806 jr z, opn2ok ; go if OK + 247: 5325 212954 ld hl, uopner ; error message and exit + 248: 5328 C3A253 jp uerror + 249: + 250: ;; Read + 251: 532B CD2852 opn2ok: call getern ; count down records in bc + 252: + 253: 532E D5 loop: push de ; save fd + 254: 532F 116A54 ld de, dcb + 255: 5332 CD1052 call reed ; read 256 bytes from file + 256: 5335 D1 pop de + 257: 5336 280B jr z, rdok ; got a full 256 bytes + 258: 5338 FE1C cp 28 ; eof? + 259: 533A 283F jr z, closit ; yes, OK + 260: 533C 4F ld c, a + 261: 533D CD1852 call error ; oops, i/o error + 262: 5340 C32052 jp abort + 263: 5343 0B rdok: dec bc + 264: + 265: ;; Translate + 266: 5344 C5 push bc ; save record count + 267: 5345 3AFD53 ld a, (nflag) ; check for NL feature + 268: 5348 A7 and a + 269: 5349 280F jr z, nlfals + 270: 534B 219A54 ld hl, iobuf + 271: 534E 3E0D ld a, 0dh + 272: 5350 010A00 ld bc, 000ah ; b := 0, c := 0ah + 273: 5353 BE tloop: cp (hl) + 274: 5354 2001 jr nz, notlf + 275: 5356 71 ld (hl), c + 276: 5357 23 notlf: inc hl + 277: 5358 10F9 djnz tloop + 278: 535A C1 nlfals: pop bc ; restore record count + 279: + 280: ;; Write + 281: 535B 79 ld a, c + 282: 535C B0 or b ; last record? + 283: 535D C5 push bc ; save record count + 284: 535E 010001 ld bc, 0100h ; byte count + 285: 5361 2007 jr nz, notlst + 286: 5363 47 ld b, a + 287: 5364 3A7254 ld a, (dcb+8) + 288: 5367 4F ld c, a + 289: 5368 0D dec c ; EOF offset 0: write 256 bytes + 290: 5369 03 inc bc + 291: 536A notlst: + 292: 536A 219A54 ld hl, iobuf + 293: 536D ED33 defw emt_write + 294: 536F C1 pop bc + 295: 5370 2805 jr z, wrok + 296: 5372 213E54 ld hl, uwrer ; write error + 297: 5375 182B jr uerror + 298: 5377 79 wrok: ld a, c + 299: 5378 B0 or b + 300: 5379 20B3 jr nz, loop + 301: + 302: ;; Close + 303: 537B ED31 closit: defw emt_close ; close Unix file + 304: 537D 2805 jr z, closok + 305: 537F 215454 ld hl, uclser ; close error + 306: 5382 181E jr uerror + 307: 5384 116A54 closok: ld de, dcb + 308: 5387 CD0C52 call close ; close the TRS-80 file + 309: 538A 2807 jr z, cls2ok + 310: 538C 4F ld c, a + 311: 538D CD1852 call error ; oops, i/o error + 312: 5390 C32052 jp abort + 313: 5393 210000 cls2ok: ld hl, 0 ; all is well + 314: 5396 C31C52 jp exit + 315: + 316: ;; Usage message + 317: 5399 210054 usage: ld hl, usager ; error message and exit + 318: 539C CD2452 call dsply + 319: 539F C32052 jp abort + 320: + 321: ;; Unix error, msg in hl, errno in a + 322: 53A2 F5 uerror: push af + 323: 53A3 CD2452 call dsply + 324: 53A6 F1 pop af + 325: 53A7 219A54 ld hl, iobuf + 326: 53AA 010001 ld bc, 256 + 327: 53AD ED35 defw emt_strerror + 328: 53AF CD2452 call dsply + 329: 53B2 C32052 jp abort + 330: + 331: ;; Display message in HL. 03h terminate, 0dh newline and terminate. + 332: 53B5 111D40 dsply5: ld de, dodcb$ + 333: 53B8 E5 push hl + 334: 53B9 7E dsply0: ld a, (hl) + 335: 53BA FE03 cp 03h + 336: 53BC 280A jr z, dsply1 + 337: 53BE F5 push af + 338: 53BF CD1B00 call @put + 339: 53C2 F1 pop af + 340: 53C3 23 inc hl + 341: 53C4 FE0D cp 0dh + 342: 53C6 20F1 jr nz, dsply0 + 343: 53C8 E1 dsply1: pop hl + 344: 53C9 C9 ret + 345: + 346: ;; Convert (NUL terminated) string in HL to lower case. + 347: 53CA E5 lcconv: push hl + 348: 53CB 54 ld d, h + 349: 53CC 5D ld e, l + 350: 53CD 7E lcloop: ld a, (hl) + 351: 53CE FE5B cp 5bh ; use '[' or uparrow as escape + 352: 53D0 2004 jr nz, lconv1 + 353: 53D2 23 inc hl + 354: 53D3 7E ld a, (hl) + 355: 53D4 1809 jr lconv2 ; char after esc: don't convert + 356: 53D6 D641 lconv1: sub 'A' + 357: 53D8 FE1A cp 26 + 358: 53DA 7E ld a, (hl) + 359: 53DB 3002 jr nc, lconv2 + 360: 53DD F620 or 20h ; convert to lower case + 361: 53DF 12 lconv2: ld (de), a + 362: 53E0 23 inc hl + 363: 53E1 13 inc de + 364: 53E2 B7 or a ; NUL terminator? + 365: 53E3 20E8 jr nz, lcloop + 366: 53E5 E1 pop hl + 367: 53E6 C9 ret + 368: + 369: ;; EOF handling differs between TRS-80 DOSes: + 370: ;; For TRSDOS 2.3 and LDOS, word (dcb+12) contains the number of + 371: ;; 256 byte records in the file, byte (dcb+8) contains the EOF + 372: ;; offset in the last record (0=256). + 373: ;; For NEWDOS/80 and TRSDOS 1.3, byte (dcb+8) and word (dcb+12) + 374: ;; form a 24 bit number containing the relative byte address of EOF. + 375: ;; Thus (dcb+12) differs by one if the file length is not a + 376: ;; multiple of 256 bytes. DOSPLUS also uses this convention, + 377: ;; and NEWDOS 2.1 probably does too (not checked). + 378: + 379: ; Returns number of (partial or full) records in BC, destroys A + 380: 53E7 getern5: + 381: 53E7 ED4B7654 ld bc, (dcb+12) + 382: 53EB 3A5852 ld a, (ernldos) ; get ERN convention + 383: 53EE A7 and a + 384: 53EF C0 ret nz ; done if TRSDOS 2.3/LDOS convention + 385: 53F0 3A7254 ld a, (dcb+8) ; length multiple of 256 bytes? + 386: 53F3 A7 and a + 387: 53F4 C8 ret z ; done if so + 388: 53F5 03 inc bc ; no, # of records = last full record + 1 + 389: 53F6 C9 ret + 390: + 391: ; All Model 4 mode operating systems should be TRSDOS/LS-DOS 6.x compatible + 392: 53F7 getern6: + 393: 53F7 ED4B7654 ld bc, (dcb+12) + 394: 53FB C9 ret + 395: + 396: 53FC 00 lflag: defb 0 + 397: 53FD 00 nflag: defb 0 + 398: 53FE 0000 lfname: defw 0 + 399: + 400: 5400 55736167 usager: defb 'Usage: EXPORT [-lne] fromfile [unixfile]', 0dh + 653A2045 + 58504F52 + 54205B2D + 6C6E655D + 2066726F + 6D66696C + 65205B75 + 6E697866 + 696C655D + 0D + 401: 5429 4572726F uopner: defb 'Error in Unix open: ', 03h + 7220696E + 20556E69 + 78206F70 + 656E3A20 + 03 + 402: 543E 4572726F uwrer: defb 'Error in Unix write: ', 03h + 7220696E + 20556E69 + 78207772 + 6974653A + 2003 + 403: 5454 4572726F uclser: defb 'Error in Unix close: ', 03h + 7220696E + 20556E69 + 7820636C + 6F73653A + 2003 + 404: + 405: 546A dcb: defs 48 ; 48 for Model III TRSDOS 1.3 + 406: 549A iobuf: defs 256 + 407: + 408: 5259 end export + + + +Statistics: + + 102 symbols + 618 bytes + + + +Symbol Table: + +@abort =4030 emt_read =32ed+ lflag 53fc +@abort6 = 15 emt_strerror =35ed lfname 53fe +@close =4428 emt_write =33ed loop 532e +@close6 = 3c endj 522c lrlerr = 2a +@dsply6 = a eo_accmode = 3+ nflag 53fd +@error =4409 eo_append = 400+ nlfals 535a +@error6 = 1a eo_creat = 40 not4 526d +@exit =402d eo_excl = 80+ notlf 5357 +@exit6 = 16 eo_rdonly = 0+ notlst 536a +@fspec =441c eo_rdwr = 2+ notsl 52f1 +@fspec6 = 4e eo_trunc = 200 open 5208 +@init =4420 eo_wronly = 1 opn2ok 532b +@init6 = 3a ernldos 5258 rdok 5343 +@open =4424 error 5218 reed 5210 +@open6 = 3b exit 521c startj 5200 +@put = 1b export 5259 startj6 522c +@read =4436 flag0 527c tloop 5353 +@read6 = 43 flag1 5287 uclser 5454 +@svc = 28 flag2 52ae uerror 53a2 +@write =4439 flag3 528d uname 5311 +@write6 = 4b flagl 5299 unix0 52c3 +abort 5220 flagn 52a4 unix1 52cd +close 520c fromf 52b7 unix2 52d2 +closit 537b fspec 5200 uopner 5429 +closok 5384 getern 5228 usage 5399 +cls2ok 5393 getern5 53e7 usager 5400 +dcb 546a getern6 53f7 usetrs 52dc +dodcb =401d gotid 5279 ut1 52e2 +dsply 5224 gotu 52f8 utdone 52f6 +dsply0 53b9 init 5204+ uwrer 543e +dsply1 53c8 iobuf 549a write 5214+ +dsply5 53b5 lcconv 53ca wrok 5377 +emt_close =31ed lcloop 53cd +emt_lseek =34ed+ lconv1 53d6 +emt_open =30ed lconv2 53df diff --git a/export.z80 b/export.z80 new file mode 100644 index 0000000..bb46f63 --- /dev/null +++ b/export.z80 @@ -0,0 +1,408 @@ +;; export.z +;; Timothy Mann, 8/24/97 +;; $Date: 2006/05/15 00:48:57 $ +;; +;; Copyright (c) 1997, Timothy Mann +;; +;; This software may be copied, modified, and used for any +;; purpose without fee, provided that (1) the above copyright +;; notice is retained, and (2) modified versions are clearly +;; marked as having been modified, with the modifier's name and +;; the date included. +;; +;; Use xtrs emulator traps to copy a file from TRS-80 to Unix +;; Usage: EXPORT [-lne] fromfile [unixfile] +;; Parameter -l will convert the Unix file to lower case. +;; (Needed for NEWDOS/80. They insist on uppercasing the command line.) +;; If the -n parameter is given, each carriage return ('\r') +;; in the TRS-80 file is converted to a newline ('\n') in the Unix file. +;; The program tries to determine what DOS it is running on and use +;; the correct FCB end of file convention, but this works only on +;; TRSDOS, LDOS, and NEWDOS/80. For other DOSes that use the +;; NEWDOS/80 convention (such as DOSPLUS), give the -e paramter. +;; If the unixfile parameter is omitted, the fromfile parameter is used, +;; with '/' changed to '.'. + +;; Model I/III addresses +@fspec equ 441ch +@init equ 4420h +@open equ 4424h +@close equ 4428h +@read equ 4436h +@write equ 4439h +@error equ 4409h +@exit equ 402dh +@abort equ 4030h +@put equ 001bh +dodcb$ equ 401dh + +;; Model 4 SVCs +@svc equ 40 ; rst address for SVCs +;@svc equ 5 ; older zmac requires 8080-style "rst 5" +@fspec6 equ 78 +@init6 equ 58 +@open6 equ 59 +@close6 equ 60 +@read6 equ 67 +@write6 equ 75 +@error6 equ 26 +@exit6 equ 22 +@abort6 equ 21 +@dsply6 equ 10 + +;; Model 4 only: file init or open with wrong LRL. Can be ignored. +lrlerr equ 42 + + org 5200h + +;; Jump tables for OS independence +startj: +fspec: call @fspec + ret +init: call @init + ret +open: call @open + ret +close: call @close + ret +reed: call @read + ret +write: call @write + ret +error: call @error + ret +exit: call @exit + ret +abort: call @abort + ret +dsply: call dsply5 + ret +getern: call getern5 + ret +endj: + +; Model 4 +startj6: + ld a, @fspec6 + rst @svc + ret + ld a, @init6 + rst @svc + ret + ld a, @open6 + rst @svc + ret + ld a, @close6 + rst @svc + ret + ld a, @read6 + rst @svc + ret + ld a, @write6 + rst @svc + ret + ld a, @error6 + rst @svc + ret + ld a, @exit6 + rst @svc + ret + ld a, @abort6 + rst @svc + ret + ld a, @dsply6 + rst @svc + ret + call getern6 + ret + +; Nonzero for LDOS ern convention +ernldos: db 1 + +; Emulator trap instructions, byte-reversed for use in defw: +emt_open equ 30edh +emt_close equ 31edh +emt_read equ 32edh +emt_write equ 33edh +emt_lseek equ 34edh +emt_strerror equ 35edh + +EO_ACCMODE equ 3q +EO_RDONLY equ 0q +EO_WRONLY equ 1q +EO_RDWR equ 2q +EO_CREAT equ 100q +EO_EXCL equ 200q +EO_TRUNC equ 1000q +EO_APPEND equ 2000q + +export: + ld a, (000ah) ; Model 4? + cp 40h + jr z, not4 + push hl + ld de, startj + ld hl, startj6 + ld bc, endj - startj + ldir + pop hl +not4: + ld a, (4427h) ; system id for Newdos/80... + sub 82h ; ...should be 82h (v2.0) + jr z, gotid + ld a, (441fh) ; system version number for most other DOSes + sub 13h ; TRSDOS 1.3? +gotid: ld (ernldos), a + +flag0: ld a, (hl) ; look for flags + cp ' ' + jp c, usage ; error if line ends here + jr nz, flag1 + inc hl + jr flag0 +flag1: cp '-' + jr nz, fromf + inc hl + ld a, (hl) +flag3: or 20h + cp 'e' + jr nz, flagl + sub a + ld (ernldos), a + jr flag2 +flagl: cp 'l' + jr nz, flagn ; check for next flag + ld a, 1 + ld (lflag), a + jr flag2 +flagn: cp 'n' + jp nz, usage ; unknown flag + ld a, 1 + ld (nflag), a +flag2: inc hl + ld a, (hl) + cp ' ' + jr nz, flag3 ; another flag follows + inc hl + jr flag0 + +fromf: ld de, dcb ; ready to get LDOS filename from (HL) + ld (lfname), hl ; save if needed to default Unix name + call fspec + jp nz, usage + +unix0: ld a, (hl) ; scan over Unix filename + cp ' ' ; first skip spaces + jr c, usetrs ; if no Unix name, use translated TRS name + jr nz, unix1 + inc hl + jr unix0 +unix1: ld de, iobuf ; copy Unix filename + ld a, ' ' +unix2: cp (hl) + ldi + jr c, unix2 + dec de + sub a + ld (de), a ; NUL terminate Unix name + jr gotu + +usetrs: ld hl, (lfname) ; translate TRS-80 name to Unix + ld de, iobuf +ut1: ld a, (hl) + cp ':' ; drivespec? + jr z, utdone ; done if so + cp ' '+1 ; end of line? + jr c, utdone ; done if so + cp '/' ; change '/' to '.' for extension + jr nz, notsl + ld a, '.' +notsl: ld (de), a + inc hl + inc de + jr ut1 +utdone: sub a ; NUL-terminate Unix name + ld (de), a + +gotu: ld hl, iobuf + ld de, dcb + ld b, 0 + call open ; open the TRS-80 file + pop hl + jr z, uname + cp lrlerr + jr z, uname + ld c, a + call error + jp abort + +uname: ld hl, iobuf ; path + ld a, (lflag) + or a + call nz, lcconv ; convert filename to lower case + ld bc, EO_WRONLY|EO_CREAT|EO_TRUNC + ld de, 0666q ; mode + defw emt_open ; open the Unix file + jr z, opn2ok ; go if OK + ld hl, uopner ; error message and exit + jp uerror + +;; Read +opn2ok: call getern ; count down records in bc + +loop: push de ; save fd + ld de, dcb + call reed ; read 256 bytes from file + pop de + jr z, rdok ; got a full 256 bytes + cp 28 ; eof? + jr z, closit ; yes, OK + ld c, a + call error ; oops, i/o error + jp abort +rdok: dec bc + +;; Translate + push bc ; save record count + ld a, (nflag) ; check for NL feature + and a + jr z, nlfals + ld hl, iobuf + ld a, 0dh + ld bc, 000ah ; b := 0, c := 0ah +tloop: cp (hl) + jr nz, notlf + ld (hl), c +notlf: inc hl + djnz tloop +nlfals: pop bc ; restore record count + +;; Write + ld a, c + or b ; last record? + push bc ; save record count + ld bc, 0100h ; byte count + jr nz, notlst + ld b, a + ld a, (dcb+8) + ld c, a + dec c ; EOF offset 0: write 256 bytes + inc bc +notlst: + ld hl, iobuf + defw emt_write + pop bc + jr z, wrok + ld hl, uwrer ; write error + jr uerror +wrok: ld a, c + or b + jr nz, loop + +;; Close +closit: defw emt_close ; close Unix file + jr z, closok + ld hl, uclser ; close error + jr uerror +closok: ld de, dcb + call close ; close the TRS-80 file + jr z, cls2ok + ld c, a + call error ; oops, i/o error + jp abort +cls2ok: ld hl, 0 ; all is well + jp exit + +;; Usage message +usage: ld hl, usager ; error message and exit + call dsply + jp abort + +;; Unix error, msg in hl, errno in a +uerror: push af + call dsply + pop af + ld hl, iobuf + ld bc, 256 + defw emt_strerror + call dsply + jp abort + +;; Display message in HL. 03h terminate, 0dh newline and terminate. +dsply5: ld de, dodcb$ + push hl +dsply0: ld a, (hl) + cp 03h + jr z, dsply1 + push af + call @put + pop af + inc hl + cp 0dh + jr nz, dsply0 +dsply1: pop hl + ret + +;; Convert (NUL terminated) string in HL to lower case. +lcconv: push hl + ld d, h + ld e, l +lcloop: ld a, (hl) + cp 5bh ; use '[' or uparrow as escape + jr nz, lconv1 + inc hl + ld a, (hl) + jr lconv2 ; char after esc: don't convert +lconv1: sub 'A' + cp 26 + ld a, (hl) + jr nc, lconv2 + or 20h ; convert to lower case +lconv2: ld (de), a + inc hl + inc de + or a ; NUL terminator? + jr nz, lcloop + pop hl + ret + +;; EOF handling differs between TRS-80 DOSes: +;; For TRSDOS 2.3 and LDOS, word (dcb+12) contains the number of +;; 256 byte records in the file, byte (dcb+8) contains the EOF +;; offset in the last record (0=256). +;; For NEWDOS/80 and TRSDOS 1.3, byte (dcb+8) and word (dcb+12) +;; form a 24 bit number containing the relative byte address of EOF. +;; Thus (dcb+12) differs by one if the file length is not a +;; multiple of 256 bytes. DOSPLUS also uses this convention, +;; and NEWDOS 2.1 probably does too (not checked). + +; Returns number of (partial or full) records in BC, destroys A +getern5: + ld bc, (dcb+12) + ld a, (ernldos) ; get ERN convention + and a + ret nz ; done if TRSDOS 2.3/LDOS convention + ld a, (dcb+8) ; length multiple of 256 bytes? + and a + ret z ; done if so + inc bc ; no, # of records = last full record + 1 + ret + +; All Model 4 mode operating systems should be TRSDOS/LS-DOS 6.x compatible +getern6: + ld bc, (dcb+12) + ret + +lflag: defb 0 +nflag: defb 0 +lfname: defw 0 + +usager: defb 'Usage: EXPORT [-lne] fromfile [unixfile]', 0dh +uopner: defb 'Error in Unix open: ', 03h +uwrer: defb 'Error in Unix write: ', 03h +uclser: defb 'Error in Unix close: ', 03h + +dcb: defs 48 ; 48 for Model III TRSDOS 1.3 +iobuf: defs 256 + + end export diff --git a/fakerom.hex b/fakerom.hex new file mode 100644 index 0000000..822696c --- /dev/null +++ b/fakerom.hex @@ -0,0 +1,11 @@ +:10000000F321370011C03D013000EDB03E05ED3C5D +:100010003E3085FE35281632F03DFE34280218FEAB +:1000200021670011003E012B00EDB018FE21F03DCC +:10003000363423365018FE596F7520646F206E6F6A +:10004000742068617665206120524F4D20696D6192 +:10005000676520696E7374616C6C656420666F728D +:10006000204D6F64656C20284D6F64656C203420D2 +:100070006D6F6465207265717569726573206120AA +:100080004D6F64656C203320524F4D20696D616760 +:020090006529E0 +:00000001FF diff --git a/fakerom.lst b/fakerom.lst new file mode 100644 index 0000000..d29ab0a --- /dev/null +++ b/fakerom.lst @@ -0,0 +1,79 @@ + 1: ; + 2: ; Fake ROM for xtrs, initial hack + 3: ; + 4: + 5: 3DC0 video equ 3c00h+7*64 + 6: + 7: 0000 org 0 + 8: 0000 F3 start: di + 9: 0001 213700 ld hl,fakemsg + 10: 0004 11C03D ld de,video + 11: 0007 013000 ld bc,fmend-fakemsg + 12: 000A EDB0 ldir + 13: 000C 3E05 ld a,5 ;query model + 14: 000E ED3C defw 3cedh ;emt_misc + 15: 0010 3E30 ld a,'0' + 16: 0012 85 add a,l + 17: 0013 FE35 cp '5' + 18: 0015 2816 jr z,mod4p + 19: 0017 32F03D ld (model-fakemsg+video),a + 20: 001A FE34 cp '4' + 21: 001C 2802 jr z,mod4 + 22: 001E 18FE jr $ + 23: + 24: 0020 216700 mod4: ld hl,m4msg + 25: 0023 11003E ld de,video+64 + 26: 0026 012B00 ld bc,m4end-m4msg + 27: 0029 EDB0 ldir + 28: 002B 18FE jr $ + 29: + 30: 002D 21F03D mod4p: ld hl,model-fakemsg+video + 31: 0030 3634 ld (hl),'4' + 32: 0032 23 inc hl + 33: 0033 3650 ld (hl),'P' + 34: 0035 18FE jr $ + 35: + 36: 0037 596F7520 fakemsg:defb 'You do not have a ROM image installed for Model ' + 646F206E + 6F742068 + 61766520 + 6120524F + 4D20696D + 61676520 + 696E7374 + 616C6C65 + 6420666F + 72204D6F + 64656C20 + 37: 0067 model equ $ + 38: 0067 fmend equ $ + 39: 0067 284D6F64 m4msg: defb '(Model 4 mode requires a Model 3 ROM image)' + 656C2034 + 206D6F64 + 65207265 + 71756972 + 65732061 + 204D6F64 + 656C2033 + 20524F4D + 20696D61 + 676529 + 40: 0092 m4end equ $ + 41: + 42: 0000 end start + + + +Statistics: + + 9 symbols + 146 bytes + + + +Symbol Table: + +fakemsg 37 mod4 20 video =3dc0 +fmend = 67 mod4p 2d +m4end = 92 model = 67 +m4msg 67 start 0 diff --git a/fakerom.z80 b/fakerom.z80 new file mode 100644 index 0000000..dfeab3f --- /dev/null +++ b/fakerom.z80 @@ -0,0 +1,42 @@ +; +; Fake ROM for xtrs, initial hack +; + +video equ 3c00h+7*64 + + org 0 +start: di + ld hl,fakemsg + ld de,video + ld bc,fmend-fakemsg + ldir + ld a,5 ;query model + defw 3cedh ;emt_misc + ld a,'0' + add a,l + cp '5' + jr z,mod4p + ld (model-fakemsg+video),a + cp '4' + jr z,mod4 + jr $ + +mod4: ld hl,m4msg + ld de,video+64 + ld bc,m4end-m4msg + ldir + jr $ + +mod4p: ld hl,model-fakemsg+video + ld (hl),'4' + inc hl + ld (hl),'P' + jr $ + +fakemsg:defb 'You do not have a ROM image installed for Model ' +model equ $ +fmend equ $ +m4msg: defb '(Model 4 mode requires a Model 3 ROM image)' +m4end equ $ + + end start diff --git a/hex2cmd.c b/hex2cmd.c new file mode 100644 index 0000000..86ad393 --- /dev/null +++ b/hex2cmd.c @@ -0,0 +1,54 @@ +/* Convert Intel Hex format to TRS-80 CMD format */ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Mon Jan 12 15:44:48 PST 1998 by mann */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include "cmd.h" +#include "z80.h" + +char *program_name; + +/* Called by load_hex */ +void +hex_data(int address, int value) +{ + cmd_data(address, value); +} + +void +hex_transfer_address(int address) +{ + cmd_transfer_address(address); +} + +int +main(int argc, char *argv[]) +{ + FILE *f; + program_name = argv[0]; + cmd_init(stdout); + if (argc == 1) { + f = stdin; + } else if (argc == 2) { + f = fopen(argv[1], "r"); + if (f == NULL) { + perror(argv[1]); + exit(1); + } + } else { + fprintf(stderr, "Usage: %s [<] file.hex > file.cmd\n", + program_name); + exit(2); + } + load_hex(f); + cmd_end_of_file(); + return 0; +} diff --git a/hex2cmd.man b/hex2cmd.man new file mode 100644 index 0000000..46eb82f --- /dev/null +++ b/hex2cmd.man @@ -0,0 +1,23 @@ +.TH hex2cmd 1 2001-02-22 +.SH Name +hex2cmd \- convert Intel hex format to TRS-80 CMD format +.SH Syntax +\fBhex2cmd\fP \fI[infile]\fP +.SH Description +.B hex2cmd +reads the specified \fIinfile\fP (or standard input if none is given) +in Intel hex format (also known as S-record format), and writes a +TRS-80 CMD file to standard output. +An S-record that asks for 0 bytes to be loaded at address A +sets the transfer address (entry point) of the CMD file to A; otherwise +the CMD file is given no transfer address. +.SH Author +.B hex2cmd +was written by Timothy Mann. +This man page was created by Branden Robinson. +.SH See also +.IR xtrs (1) +.PP +See the LDOS Quarterly, April 1, 1982 (Vol 1, No 4), for documentation of +the TRS-80 DOS /cmd file format. +It is available on the Web at http://www.tim-mann.org/misosys.html. diff --git a/hex2cmd.txt b/hex2cmd.txt new file mode 100644 index 0000000..c7ded18 --- /dev/null +++ b/hex2cmd.txt @@ -0,0 +1,31 @@ +hex2cmd(1) hex2cmd(1) + + + +[1mName[0m + hex2cmd - convert Intel hex format to TRS-80 CMD format + +[1mSyntax[0m + [1mhex2cmd [4m[22m[infile][0m + +[1mDescription[0m + [1mhex2cmd [22mreads the specified [4minfile[24m (or standard input if none is given) + in Intel hex format (also known as S-record format), and writes a + TRS-80 CMD file to standard output. An S-record that asks for 0 bytes + to be loaded at address A sets the transfer address (entry point) of + the CMD file to A; otherwise the CMD file is given no transfer address. + +[1mAuthor[0m + [1mhex2cmd [22mwas written by Timothy Mann. This man page was created by + Branden Robinson. + +[1mSee also[0m + [4mxtrs[24m(1) + + See the LDOS Quarterly, April 1, 1982 (Vol 1, No 4), for documentation + of the TRS-80 DOS /cmd file format. It is available on the Web at + http://www.tim-mann.org/misosys.html. + + + + 2001-02-22 hex2cmd(1) diff --git a/import.bas b/import.bas new file mode 100644 index 0000000..9cae720 --- /dev/null +++ b/import.bas @@ -0,0 +1,27 @@ +20 REM-- IMPORT/BAS +40 REM-- Uses xtrs 1.3 import feature to read a Unix file +50 REM-- and display or save the results. +60 REM-- Timothy Mann, 10/30/96 +70 REM-- Last modified on Fri Feb 13 12:18:22 PST 1998 by mann +80 CLEAR 5000 +100 LINE INPUT "Unix input file? ";C$ +120 LINE INPUT "TRS-80 output file? (*DO to display) ";F$ +140 OPEN "O",1,F$ +160 INPUT "Convert newlines (y/n)";CN$ +180 CN%=LEFT$(CN$,1)="y" OR LEFT$(CN$,1)="Y" +200 OUT &HD0,0 +220 OUT &HD0,1 +240 FOR I%=1 TO LEN(C$) +260 OUT &HD1, ASC(MID$(C$, I%)) +280 NEXT I% +300 OUT &HD1,0 +320 S%=INP(&HD0) +340 IF S% <> 0 THEN 480 +360 B%=INP(&HD1) +380 IF CN% THEN IF B% = &H0A THEN B% =&H0D +400 IF B% <> 0 THEN PRINT#1,CHR$(B%);:GOTO 360 +420 S%=INP(&HD0) +440 IF S% = &HFF THEN PRINT#1,CHR$(B%);:GOTO 360 +460 IF S%=0 THEN 500 +480 PRINT:PRINT "Error ";S% +500 CLOSE diff --git a/import.cmd b/import.cmd Binary files differnew file mode 100644 index 0000000..9abc34e --- /dev/null +++ b/import.cmd diff --git a/import.lst b/import.lst new file mode 100644 index 0000000..881bd0b --- /dev/null +++ b/import.lst @@ -0,0 +1,478 @@ + 1: ;; import.z + 2: ;; Timothy Mann, 8/24/97 + 3: ;; $Date: 2006/05/15 00:49:19 $ + 4: ;; + 5: ;; Copyright (c) 1997, Timothy Mann + 6: ;; + 7: ;; This software may be copied, modified, and used for any + 8: ;; purpose without fee, provided that (1) the above copyright + 9: ;; notice is retained, and (2) modified versions are clearly + 10: ;; marked as having been modified, with the modifier's name and + 11: ;; the date included. + 12: ;; + 13: ;; Use xtrs emulator traps to copy a file from Unix to TRS-80 + 14: ;; Usage: IMPORT [-lne] unixfile [tofile] + 15: ;; Parameter -l will convert the Unix file to lower case. + 16: ;; (Needed for NEWDOS/80. They insist on uppercasing the + 17: ;; command line.) + 18: ;; If the -n parameter is given, each newline ('\n') in the Unix + 19: ;; file is converted to a carriage return ('\r'), the TRS-80 end of + 20: ;; line character. + 21: ;; The program tries to determine what DOS it is running on and use + 22: ;; the correct FCB end of file convention, but this works only on + 23: ;; TRSDOS, LDOS, and NEWDOS/80. For other DOSes that use the + 24: ;; NEWDOS/80 convention (such as DOSPLUS), give the -e paramter. + 25: ;; If the tofile parameter is omitted, the last component of the + 26: ;; Unix pathname is used, with '.' changed to '/'. If this is + 27: ;; not a legal TRS-80 filename, you get an error message. + 28: + 29: ;; Model I/III addresses + 30: 441C @fspec equ 441ch + 31: 4420 @init equ 4420h + 32: 4424 @open equ 4424h + 33: 4428 @close equ 4428h + 34: 4436 @read equ 4436h + 35: 4439 @write equ 4439h + 36: 4409 @error equ 4409h + 37: 402D @exit equ 402dh + 38: 4030 @abort equ 4030h + 39: 001B @put equ 001bh + 40: 401D dodcb$ equ 401dh + 41: + 42: ;; Model 4 SVCs + 43: 0028 @svc equ 40 ; rst address for SVCs + 44: ;@svc equ 5 ; older zmac requires 8080-style "rst 5" + 45: 004E @fspec6 equ 78 + 46: 003A @init6 equ 58 + 47: 003B @open6 equ 59 + 48: 003C @close6 equ 60 + 49: 0043 @read6 equ 67 + 50: 004B @write6 equ 75 + 51: 001A @error6 equ 26 + 52: 0016 @exit6 equ 22 + 53: 0015 @abort6 equ 21 + 54: 000A @dsply6 equ 10 + 55: + 56: ;; Model 4 only: file init or open with wrong LRL. Can be ignored. + 57: 002A lrlerr equ 42 + 58: + 59: 5200 org 5200h + 60: + 61: ;; Jump tables for OS independence + 62: 5200 startj: + 63: 5200 CD1C44 fspec: call @fspec + 64: 5203 C9 ret + 65: 5204 CD2044 init: call @init + 66: 5207 C9 ret + 67: 5208 CD2444 open: call @open + 68: 520B C9 ret + 69: 520C CD2844 close: call @close + 70: 520F C9 ret + 71: 5210 CD3644 reed: call @read + 72: 5213 C9 ret + 73: 5214 CD3944 write: call @write + 74: 5217 C9 ret + 75: 5218 CD0944 error: call @error + 76: 521B C9 ret + 77: 521C CD2D40 exit: call @exit + 78: 521F C9 ret + 79: 5220 CD3040 abort: call @abort + 80: 5223 C9 ret + 81: 5224 CDA953 dsply: call dsply5 + 82: 5227 C9 ret + 83: 5228 CDDB53 setern: call setern5 + 84: 522B C9 ret + 85: 522C endj: + 86: + 87: ; Model 4 + 88: 522C startj6: + 89: 522C 3E4E ld a, @fspec6 + 90: 522E EF rst @svc + 91: 522F C9 ret + 92: 5230 3E3A ld a, @init6 + 93: 5232 EF rst @svc + 94: 5233 C9 ret + 95: 5234 3E3B ld a, @open6 + 96: 5236 EF rst @svc + 97: 5237 C9 ret + 98: 5238 3E3C ld a, @close6 + 99: 523A EF rst @svc + 100: 523B C9 ret + 101: 523C 3E43 ld a, @read6 + 102: 523E EF rst @svc + 103: 523F C9 ret + 104: 5240 3E4B ld a, @write6 + 105: 5242 EF rst @svc + 106: 5243 C9 ret + 107: 5244 3E1A ld a, @error6 + 108: 5246 EF rst @svc + 109: 5247 C9 ret + 110: 5248 3E16 ld a, @exit6 + 111: 524A EF rst @svc + 112: 524B C9 ret + 113: 524C 3E15 ld a, @abort6 + 114: 524E EF rst @svc + 115: 524F C9 ret + 116: 5250 3E0A ld a, @dsply6 + 117: 5252 EF rst @svc + 118: 5253 C9 ret + 119: 5254 CDEC53 call setern6 + 120: 5257 C9 ret + 121: + 122: ; Nonzero for LDOS ern convention + 123: 5258 01 ernldos: db 1 + 124: + 125: ; Emulator trap instructions, byte-reversed for use in defw: + 126: 30ED emt_open equ 30edh + 127: 31ED emt_close equ 31edh + 128: 32ED emt_read equ 32edh + 129: 33ED emt_write equ 33edh + 130: 34ED emt_lseek equ 34edh + 131: 35ED emt_strerror equ 35edh + 132: + 133: 0003 EO_ACCMODE equ 3q + 134: 0000 EO_RDONLY equ 0q + 135: 0001 EO_WRONLY equ 1q + 136: 0002 EO_RDWR equ 2q + 137: 0040 EO_CREAT equ 100q + 138: 0080 EO_EXCL equ 200q + 139: 0200 EO_TRUNC equ 1000q + 140: 0400 EO_APPEND equ 2000q + 141: + 142: 2000 iobsize equ 8192 ; must be divisible by 256 + 143: + 144: 5259 import: + 145: 5259 3A0A00 ld a, (000ah) ; Model 4? + 146: 525C FE40 cp 40h + 147: 525E 280D jr z, not4 + 148: 5260 E5 push hl + 149: 5261 110052 ld de, startj + 150: 5264 212C52 ld hl, startj6 + 151: 5267 012C00 ld bc, endj - startj + 152: 526A EDB0 ldir + 153: 526C E1 pop hl + 154: 526D not4: + 155: 526D 3A2744 ld a, (4427h) ; system id for Newdos/80... + 156: 5270 D682 sub 82h ; ...should be 82h (v2.0) + 157: 5272 2805 jr z, gotid + 158: 5274 3A1F44 ld a, (441fh) ; system version number for most other DOSes + 159: 5277 D613 sub 13h ; TRSDOS 1.3? + 160: 5279 325852 gotid: ld (ernldos), a + 161: + 162: 527C 7E flag0: ld a, (hl) ; look for flags + 163: 527D FE20 cp ' ' + 164: 527F DA1453 jp c, usage ; error if line ends here + 165: 5282 2003 jr nz, flag1 + 166: 5284 23 inc hl + 167: 5285 18F5 jr flag0 + 168: 5287 FE2D flag1: cp '-' + 169: 5289 202B jr nz, unix1 + 170: 528B 23 inc hl + 171: 528C 7E ld a, (hl) + 172: 528D F620 flag3: or 20h + 173: 528F FE65 cp 'e' + 174: 5291 2006 jr nz, flagl + 175: 5293 97 sub a + 176: 5294 325852 ld (ernldos), a + 177: 5297 1814 jr flag2 + 178: 5299 FE6C flagl: cp 'l' + 179: 529B 2007 jr nz, flagn ; check for next flag + 180: 529D 3E01 ld a, 1 + 181: 529F 32F353 ld (lflag), a + 182: 52A2 1809 jr flag2 + 183: 52A4 FE6E flagn: cp 'n' + 184: 52A6 206C jr nz, usage ; unknown flag + 185: 52A8 3E01 ld a, 1 + 186: 52AA 32F453 ld (nflag), a + 187: 52AD 23 flag2: inc hl + 188: 52AE 7E ld a, (hl) + 189: 52AF FE20 cp ' ' + 190: 52B1 20DA jr nz, flag3 ; another flag follows + 191: 52B3 23 inc hl + 192: 52B4 18C6 jr flag0 + 193: + 194: 52B6 118C54 unix1: ld de, iobuf ; copy Unix filename + 195: 52B9 3E20 ld a, ' ' + 196: 52BB BE unix2: cp (hl) + 197: 52BC EDA0 ldi + 198: 52BE 38FB jr c, unix2 + 199: 52C0 1B dec de ; NUL terminate Unix name + 200: 52C1 3E00 ld a, 0 + 201: 52C3 12 ld (de), a + 202: 52C4 2812 jr z, trs80 ; go if two names given + 203: + 204: ;; Translate last component of Unix name to TRS-80 name + 205: 52C6 2B dec hl ; back up to terminator + 206: 52C7 2B unix3: dec hl ; back up to last byte of name + 207: 52C8 7E ld a, (hl) + 208: 52C9 FE2E cp '.' ; change '.' to '/' + 209: 52CB 2002 jr nz, notdot + 210: 52CD 362F ld (hl), '/' + 211: 52CF FE2F notdot: cp '/' + 212: 52D1 2804 jr z, unix4 + 213: 52D3 FE20 cp ' ' + 214: 52D5 20F0 jr nz, unix3 + 215: 52D7 23 unix4: inc hl ; point to start of modified last component + 216: + 217: 52D8 115C54 trs80: ld de, dcb ; ready to get TRS-80 filename from (HL) + 218: 52DB CD0052 call fspec + 219: 52DE 2034 jr nz, usage + 220: 52E0 218C54 ld hl, iobuf ; Unix path + 221: 52E3 3AF353 ld a, (lflag) + 222: 52E6 B7 or a + 223: 52E7 C4BE53 call nz, lcconv ; convert path to lower case + 224: 52EA 010000 ld bc, EO_RDONLY + 225: 52ED 110000 ld de, 0 ; mode (ignored) + 226: 52F0 ED30 defw emt_open + 227: 52F2 2806 jr z, openok ; go if OK + 228: 52F4 211C54 ld hl, uopner ; error message and exit + 229: 52F7 C39653 jp uerror + 230: + 231: 52FA D5 openok: push de ; save fd + 232: 52FB 218C54 ld hl, iobuf + 233: 52FE 115C54 ld de, dcb + 234: 5301 0600 ld b, 0 + 235: 5303 CD0452 call init ; open the file + 236: 5306 D1 pop de + 237: 5307 2814 jr z, opn2ok + 238: 5309 FE2A cp lrlerr + 239: 530B 2810 jr z, opn2ok + 240: 530D 4F ld c, a + 241: 530E CD1852 call error + 242: 5311 C32052 jp abort + 243: 5314 21F553 usage: ld hl, usager ; error message and exit + 244: 5317 CD2452 call dsply + 245: 531A C32052 jp abort + 246: + 247: ;; Read + 248: 531D rloop: + 249: 531D 218C54 opn2ok: ld hl, iobuf ; read a buffer + 250: 5320 010020 ld bc, iobsize + 251: 5323 ED32 defw emt_read + 252: 5325 2805 jr z, readok + 253: 5327 213154 ld hl, urder ; read error (!!code in A) + 254: 532A 186A jr uerror + 255: 532C D5 readok: push de ; save fd + 256: + 257: ;; Translate + 258: 532D 3AF453 ld a, (nflag) ; check for NL feature + 259: 5330 A7 and a + 260: 5331 2817 jr z, nlfals + 261: 5333 218C54 ld hl, iobuf + 262: 5336 C5 push bc ; save byte count + 263: 5337 3E0A ld a, 0ah + 264: 5339 160D ld d, 0dh + 265: 533B 0C inc c ; deal with b=0 and/ c=0 + 266: 533C 04 inc b + 267: 533D 1805 jr tstrt + 268: 533F BE tloop: cp (hl) + 269: 5340 2001 jr nz, notcr + 270: 5342 72 ld (hl), d + 271: 5343 23 notcr: inc hl + 272: 5344 0D tstrt: dec c + 273: 5345 20F8 jr nz, tloop + 274: 5347 10F6 djnz tloop + 275: 5349 C1 pop bc ; restore byte count + 276: + 277: ;; Write + 278: 534A C5 nlfals: push bc ; save byte count + 279: 534B 218C54 ld hl, iobuf + 280: 534E 115C54 ld de, dcb + 281: 5351 04 inc b ; deal with b=0 and/or c=0 + 282: 5352 79 ld a, c + 283: 5353 A7 and a + 284: 5354 2810 jr z, wstrt + 285: 5356 225F54 wloop: ld (dcb+3), hl + 286: 5359 CD1452 call write ; write 256 bytes to file + 287: 535C 2807 jr z, wrok + 288: 535E 4F ld c, a + 289: 535F CD1852 call error ; oops, i/o error + 290: 5362 C32052 jp abort + 291: 5365 24 wrok: inc h + 292: 5366 10EE wstrt: djnz wloop + 293: 5368 C1 pop bc ; restore byte count + 294: + 295: ;; Done? + 296: 5369 D1 pop de ; restore fd + 297: 536A 79 ld a, c + 298: 536B A7 and a + 299: 536C 2003 jr nz, closit ; done for sure + 300: 536E B8 cp b + 301: 536F 20AC jr nz, rloop ; maybe not done (sloppy) + 302: + 303: 5371 ED31 closit: defw emt_close ; close Unix file + 304: 5373 2805 jr z, closok + 305: 5375 214654 ld hl, uclser ; close error (!!code in A) + 306: 5378 181C jr uerror + 307: 537A 79 closok: ld a, c + 308: 537B 326454 ld (dcb+8), a ; set EOF offset + 309: 537E CD2852 call setern ; set ERN (in case shortening file) + 310: 5381 115C54 ld de, dcb + 311: 5384 CD0C52 call close ; close the TRS-80 file + 312: 5387 2807 jr z, cls2ok + 313: 5389 4F ld c, a + 314: 538A CD1852 call error ; oops, i/o error + 315: 538D C32052 jp abort + 316: 5390 210000 cls2ok: ld hl, 0 ; all is well + 317: 5393 C31C52 jp exit + 318: + 319: ;; Unix error, msg in hl, errno in a + 320: 5396 F5 uerror: push af + 321: 5397 CD2452 call dsply + 322: 539A F1 pop af + 323: 539B 218C54 ld hl, iobuf + 324: 539E 010001 ld bc, 256 + 325: 53A1 ED35 defw emt_strerror + 326: 53A3 CD2452 call dsply + 327: 53A6 C32052 jp abort + 328: + 329: ;; Display message in HL. 03h terminate, 0dh newline and terminate. + 330: 53A9 111D40 dsply5: ld de, dodcb$ + 331: 53AC E5 push hl + 332: 53AD 7E dsply0: ld a, (hl) + 333: 53AE FE03 cp 03h + 334: 53B0 280A jr z, dsply1 + 335: 53B2 F5 push af + 336: 53B3 CD1B00 call @put + 337: 53B6 F1 pop af + 338: 53B7 23 inc hl + 339: 53B8 FE0D cp 0dh + 340: 53BA 20F1 jr nz, dsply0 + 341: 53BC E1 dsply1: pop hl + 342: 53BD C9 ret + 343: + 344: ;; Convert (NUL terminated) string in HL to lower case. + 345: 53BE E5 lcconv: push hl + 346: 53BF 54 ld d, h + 347: 53C0 5D ld e, l + 348: 53C1 7E lcloop: ld a, (hl) + 349: 53C2 FE5B cp 5bh ; use '[' or uparrow as escape + 350: 53C4 2004 jr nz, lconv1 + 351: 53C6 23 inc hl + 352: 53C7 7E ld a, (hl) + 353: 53C8 1809 jr lconv2 ; char after esc: don't convert + 354: 53CA D641 lconv1: sub 'A' + 355: 53CC FE1A cp 26 + 356: 53CE 7E ld a, (hl) + 357: 53CF 3002 jr nc, lconv2 + 358: 53D1 F620 or 20h ; convert to lower case + 359: 53D3 12 lconv2: ld (de), a + 360: 53D4 23 inc hl + 361: 53D5 13 inc de + 362: 53D6 B7 or a ; NUL terminator? + 363: 53D7 20E8 jr nz, lcloop + 364: 53D9 E1 pop hl + 365: 53DA C9 ret + 366: + 367: ;; EOF handling differs between TRS-80 DOSes: + 368: ;; For TRSDOS 2.3 and LDOS, word (dcb+12) contains the number of + 369: ;; 256 byte records in the file, byte (dcb+8) contains the EOF + 370: ;; offset in the last record (0=256). + 371: ;; For NEWDOS/80 and TRSDOS 1.3, byte (dcb+8) and word (dcb+12) + 372: ;; form a 24 bit number containing the relative byte address of EOF. + 373: ;; Thus (dcb+12) differs by one if the file length is not a + 374: ;; multiple of 256 bytes. DOSPLUS also uses this convention, + 375: ;; and NEWDOS 2.1 probably does too (not checked). + 376: + 377: ; Set ending record number of file to current position + 378: ; EOF offset in C; destroys A, HL + 379: 53DB setern5: + 380: 53DB 2A6654 ld hl, (dcb+10) ; current record number + 381: 53DE 3A5852 ld a, (ernldos) ; get ERN convention + 382: 53E1 B7 or a + 383: 53E2 2004 jr nz, noadj ; go if TRSDOS 2.3/LDOS convention + 384: 53E4 B1 adj: or c ; length multiple of 256 bytes? + 385: 53E5 2801 jr z, noadj ; go if so + 386: 53E7 2B dec hl ; no, # of records - 1 + 387: 53E8 226854 noadj: ld (dcb+12), hl + 388: 53EB C9 ret + 389: + 390: ; All Model 4 mode operating systems should be TRSDOS/LS-DOS 6.x compatible + 391: 53EC setern6: + 392: 53EC 2A6654 ld hl, (dcb+10) + 393: 53EF 226854 ld (dcb+12), hl + 394: 53F2 C9 ret + 395: + 396: 53F3 00 lflag: defb 0 + 397: 53F4 00 nflag: defb 0 + 398: + 399: 53F5 55736167 usager: defb 'Usage: IMPORT [-lne] unixfile [tofile]', 0dh + 653A2049 + 4D504F52 + 54205B2D + 6C6E655D + 20756E69 + 7866696C + 65205B74 + 6F66696C + 655D0D + 400: 541C 4572726F uopner: defb 'Error in Unix open: ', 03h + 7220696E + 20556E69 + 78206F70 + 656E3A20 + 03 + 401: 5431 4572726F urder: defb 'Error in Unix read: ', 03h + 7220696E + 20556E69 + 78207265 + 61643A20 + 03 + 402: 5446 4572726F uclser: defb 'Error in Unix close: ', 03h + 7220696E + 20556E69 + 7820636C + 6F73653A + 2003 + 403: + 404: 545C dcb: defs 48 ; 48 for Model III TRSDOS 1.3 + 405: 548C iobuf: defs iobsize + 406: + 407: 5259 end import + + + +Statistics: + + 103 symbols + 604 bytes + + + +Symbol Table: + +@abort =4030 emt_open =30ed nlfals 534a +@abort6 = 15 emt_read =32ed noadj 53e8 +@close =4428 emt_strerror =35ed not4 526d +@close6 = 3c emt_write =33ed+ notcr 5343 +@dsply6 = a endj 522c notdot 52cf +@error =4409 eo_accmode = 3+ open 5208+ +@error6 = 1a eo_append = 400+ openok 52fa +@exit =402d eo_creat = 40+ opn2ok 531d +@exit6 = 16 eo_excl = 80+ readok 532c +@fspec =441c eo_rdonly = 0 reed 5210+ +@fspec6 = 4e eo_rdwr = 2+ rloop 531d +@init =4420 eo_trunc = 200+ setern 5228 +@init6 = 3a eo_wronly = 1+ setern5 53db +@open =4424 ernldos 5258 setern6 53ec +@open6 = 3b error 5218 startj 5200 +@put = 1b exit 521c startj6 522c +@read =4436 flag0 527c tloop 533f +@read6 = 43 flag1 5287 trs80 52d8 +@svc = 28 flag2 52ad tstrt 5344 +@write =4439 flag3 528d uclser 5446 +@write6 = 4b flagl 5299 uerror 5396 +abort 5220 flagn 52a4 unix1 52b6 +adj 53e4+ fspec 5200 unix2 52bb +close 520c gotid 5279 unix3 52c7 +closit 5371 import 5259 unix4 52d7 +closok 537a init 5204 uopner 541c +cls2ok 5390 iobsize =2000 urder 5431 +dcb 545c iobuf 548c usage 5314 +dodcb =401d lcconv 53be usager 53f5 +dsply 5224 lcloop 53c1 wloop 5356 +dsply0 53ad lconv1 53ca write 5214 +dsply1 53bc lconv2 53d3 wrok 5365 +dsply5 53a9 lflag 53f3 wstrt 5366 +emt_close =31ed lrlerr = 2a +emt_lseek =34ed+ nflag 53f4 diff --git a/import.z80 b/import.z80 new file mode 100644 index 0000000..4afa8ad --- /dev/null +++ b/import.z80 @@ -0,0 +1,407 @@ +;; import.z +;; Timothy Mann, 8/24/97 +;; $Date: 2006/05/15 00:49:19 $ +;; +;; Copyright (c) 1997, Timothy Mann +;; +;; This software may be copied, modified, and used for any +;; purpose without fee, provided that (1) the above copyright +;; notice is retained, and (2) modified versions are clearly +;; marked as having been modified, with the modifier's name and +;; the date included. +;; +;; Use xtrs emulator traps to copy a file from Unix to TRS-80 +;; Usage: IMPORT [-lne] unixfile [tofile] +;; Parameter -l will convert the Unix file to lower case. +;; (Needed for NEWDOS/80. They insist on uppercasing the +;; command line.) +;; If the -n parameter is given, each newline ('\n') in the Unix +;; file is converted to a carriage return ('\r'), the TRS-80 end of +;; line character. +;; The program tries to determine what DOS it is running on and use +;; the correct FCB end of file convention, but this works only on +;; TRSDOS, LDOS, and NEWDOS/80. For other DOSes that use the +;; NEWDOS/80 convention (such as DOSPLUS), give the -e paramter. +;; If the tofile parameter is omitted, the last component of the +;; Unix pathname is used, with '.' changed to '/'. If this is +;; not a legal TRS-80 filename, you get an error message. + +;; Model I/III addresses +@fspec equ 441ch +@init equ 4420h +@open equ 4424h +@close equ 4428h +@read equ 4436h +@write equ 4439h +@error equ 4409h +@exit equ 402dh +@abort equ 4030h +@put equ 001bh +dodcb$ equ 401dh + +;; Model 4 SVCs +@svc equ 40 ; rst address for SVCs +;@svc equ 5 ; older zmac requires 8080-style "rst 5" +@fspec6 equ 78 +@init6 equ 58 +@open6 equ 59 +@close6 equ 60 +@read6 equ 67 +@write6 equ 75 +@error6 equ 26 +@exit6 equ 22 +@abort6 equ 21 +@dsply6 equ 10 + +;; Model 4 only: file init or open with wrong LRL. Can be ignored. +lrlerr equ 42 + + org 5200h + +;; Jump tables for OS independence +startj: +fspec: call @fspec + ret +init: call @init + ret +open: call @open + ret +close: call @close + ret +reed: call @read + ret +write: call @write + ret +error: call @error + ret +exit: call @exit + ret +abort: call @abort + ret +dsply: call dsply5 + ret +setern: call setern5 + ret +endj: + +; Model 4 +startj6: + ld a, @fspec6 + rst @svc + ret + ld a, @init6 + rst @svc + ret + ld a, @open6 + rst @svc + ret + ld a, @close6 + rst @svc + ret + ld a, @read6 + rst @svc + ret + ld a, @write6 + rst @svc + ret + ld a, @error6 + rst @svc + ret + ld a, @exit6 + rst @svc + ret + ld a, @abort6 + rst @svc + ret + ld a, @dsply6 + rst @svc + ret + call setern6 + ret + +; Nonzero for LDOS ern convention +ernldos: db 1 + +; Emulator trap instructions, byte-reversed for use in defw: +emt_open equ 30edh +emt_close equ 31edh +emt_read equ 32edh +emt_write equ 33edh +emt_lseek equ 34edh +emt_strerror equ 35edh + +EO_ACCMODE equ 3q +EO_RDONLY equ 0q +EO_WRONLY equ 1q +EO_RDWR equ 2q +EO_CREAT equ 100q +EO_EXCL equ 200q +EO_TRUNC equ 1000q +EO_APPEND equ 2000q + +iobsize equ 8192 ; must be divisible by 256 + +import: + ld a, (000ah) ; Model 4? + cp 40h + jr z, not4 + push hl + ld de, startj + ld hl, startj6 + ld bc, endj - startj + ldir + pop hl +not4: + ld a, (4427h) ; system id for Newdos/80... + sub 82h ; ...should be 82h (v2.0) + jr z, gotid + ld a, (441fh) ; system version number for most other DOSes + sub 13h ; TRSDOS 1.3? +gotid: ld (ernldos), a + +flag0: ld a, (hl) ; look for flags + cp ' ' + jp c, usage ; error if line ends here + jr nz, flag1 + inc hl + jr flag0 +flag1: cp '-' + jr nz, unix1 + inc hl + ld a, (hl) +flag3: or 20h + cp 'e' + jr nz, flagl + sub a + ld (ernldos), a + jr flag2 +flagl: cp 'l' + jr nz, flagn ; check for next flag + ld a, 1 + ld (lflag), a + jr flag2 +flagn: cp 'n' + jr nz, usage ; unknown flag + ld a, 1 + ld (nflag), a +flag2: inc hl + ld a, (hl) + cp ' ' + jr nz, flag3 ; another flag follows + inc hl + jr flag0 + +unix1: ld de, iobuf ; copy Unix filename + ld a, ' ' +unix2: cp (hl) + ldi + jr c, unix2 + dec de ; NUL terminate Unix name + ld a, 0 + ld (de), a + jr z, trs80 ; go if two names given + +;; Translate last component of Unix name to TRS-80 name + dec hl ; back up to terminator +unix3: dec hl ; back up to last byte of name + ld a, (hl) + cp '.' ; change '.' to '/' + jr nz, notdot + ld (hl), '/' +notdot: cp '/' + jr z, unix4 + cp ' ' + jr nz, unix3 +unix4: inc hl ; point to start of modified last component + +trs80: ld de, dcb ; ready to get TRS-80 filename from (HL) + call fspec + jr nz, usage + ld hl, iobuf ; Unix path + ld a, (lflag) + or a + call nz, lcconv ; convert path to lower case + ld bc, EO_RDONLY + ld de, 0 ; mode (ignored) + defw emt_open + jr z, openok ; go if OK + ld hl, uopner ; error message and exit + jp uerror + +openok: push de ; save fd + ld hl, iobuf + ld de, dcb + ld b, 0 + call init ; open the file + pop de + jr z, opn2ok + cp lrlerr + jr z, opn2ok + ld c, a + call error + jp abort +usage: ld hl, usager ; error message and exit + call dsply + jp abort + +;; Read +rloop: +opn2ok: ld hl, iobuf ; read a buffer + ld bc, iobsize + defw emt_read + jr z, readok + ld hl, urder ; read error (!!code in A) + jr uerror +readok: push de ; save fd + +;; Translate + ld a, (nflag) ; check for NL feature + and a + jr z, nlfals + ld hl, iobuf + push bc ; save byte count + ld a, 0ah + ld d, 0dh + inc c ; deal with b=0 and/ c=0 + inc b + jr tstrt +tloop: cp (hl) + jr nz, notcr + ld (hl), d +notcr: inc hl +tstrt: dec c + jr nz, tloop + djnz tloop + pop bc ; restore byte count + +;; Write +nlfals: push bc ; save byte count + ld hl, iobuf + ld de, dcb + inc b ; deal with b=0 and/or c=0 + ld a, c + and a + jr z, wstrt +wloop: ld (dcb+3), hl + call write ; write 256 bytes to file + jr z, wrok + ld c, a + call error ; oops, i/o error + jp abort +wrok: inc h +wstrt: djnz wloop + pop bc ; restore byte count + +;; Done? + pop de ; restore fd + ld a, c + and a + jr nz, closit ; done for sure + cp b + jr nz, rloop ; maybe not done (sloppy) + +closit: defw emt_close ; close Unix file + jr z, closok + ld hl, uclser ; close error (!!code in A) + jr uerror +closok: ld a, c + ld (dcb+8), a ; set EOF offset + call setern ; set ERN (in case shortening file) + ld de, dcb + call close ; close the TRS-80 file + jr z, cls2ok + ld c, a + call error ; oops, i/o error + jp abort +cls2ok: ld hl, 0 ; all is well + jp exit + +;; Unix error, msg in hl, errno in a +uerror: push af + call dsply + pop af + ld hl, iobuf + ld bc, 256 + defw emt_strerror + call dsply + jp abort + +;; Display message in HL. 03h terminate, 0dh newline and terminate. +dsply5: ld de, dodcb$ + push hl +dsply0: ld a, (hl) + cp 03h + jr z, dsply1 + push af + call @put + pop af + inc hl + cp 0dh + jr nz, dsply0 +dsply1: pop hl + ret + +;; Convert (NUL terminated) string in HL to lower case. +lcconv: push hl + ld d, h + ld e, l +lcloop: ld a, (hl) + cp 5bh ; use '[' or uparrow as escape + jr nz, lconv1 + inc hl + ld a, (hl) + jr lconv2 ; char after esc: don't convert +lconv1: sub 'A' + cp 26 + ld a, (hl) + jr nc, lconv2 + or 20h ; convert to lower case +lconv2: ld (de), a + inc hl + inc de + or a ; NUL terminator? + jr nz, lcloop + pop hl + ret + +;; EOF handling differs between TRS-80 DOSes: +;; For TRSDOS 2.3 and LDOS, word (dcb+12) contains the number of +;; 256 byte records in the file, byte (dcb+8) contains the EOF +;; offset in the last record (0=256). +;; For NEWDOS/80 and TRSDOS 1.3, byte (dcb+8) and word (dcb+12) +;; form a 24 bit number containing the relative byte address of EOF. +;; Thus (dcb+12) differs by one if the file length is not a +;; multiple of 256 bytes. DOSPLUS also uses this convention, +;; and NEWDOS 2.1 probably does too (not checked). + +; Set ending record number of file to current position +; EOF offset in C; destroys A, HL +setern5: + ld hl, (dcb+10) ; current record number + ld a, (ernldos) ; get ERN convention + or a + jr nz, noadj ; go if TRSDOS 2.3/LDOS convention +adj: or c ; length multiple of 256 bytes? + jr z, noadj ; go if so + dec hl ; no, # of records - 1 +noadj: ld (dcb+12), hl + ret + +; All Model 4 mode operating systems should be TRSDOS/LS-DOS 6.x compatible +setern6: + ld hl, (dcb+10) + ld (dcb+12), hl + ret + +lflag: defb 0 +nflag: defb 0 + +usager: defb 'Usage: IMPORT [-lne] unixfile [tofile]', 0dh +uopner: defb 'Error in Unix open: ', 03h +urder: defb 'Error in Unix read: ', 03h +uclser: defb 'Error in Unix close: ', 03h + +dcb: defs 48 ; 48 for Model III TRSDOS 1.3 +iobuf: defs iobsize + + end import diff --git a/load_cmd.c b/load_cmd.c new file mode 100644 index 0000000..fc1b03a --- /dev/null +++ b/load_cmd.c @@ -0,0 +1,331 @@ +/* Copyright (c) 1996-98, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* + * Last modified on Fri Apr 24 03:52:04 PDT 1998 by mann + */ + +/* TRS-80 DOS /cmd file loader. + * + * See the LDOS Quarterly, April 1, 1982 (Vol 1, No 4), for documentation + * of the TRS-80 DOS /cmd file format. + */ + +#include <stdio.h> +#include <string.h> +#include "load_cmd.h" + +int +load_cmd(FILE* f, unsigned char memory[1<<16], + unsigned char* loadmap, int verbosity, FILE* outf, + int isam, char* pds, int* xferaddr, int stopxfer) +{ + int lastaddr = -1, lastcount = -1; + int c, a1, a2, a3, v, count, dummy, i; + int pflags1, pflags2; + char pentry[9]; + int ientry, iaddr, iseek, ilen; + int status = LOAD_CMD_OK; + unsigned short addr; /* wrap at 2**16 */ + + if (loadmap) { + memset(loadmap, 0, 1<<16); + } + if (xferaddr) { + *xferaddr = -1; + } else { + xferaddr = &dummy; + } + + for (;;) { + c = getc(f); /* get block type code */ + if (c != 1 /* not loading bytes into memory */) { + if (verbosity >= VERBOSITY_MAP) { + if (lastaddr != -1) { + fprintf(outf, "loaded 0x%04x - 0x%04x\n", + lastaddr, lastaddr + lastcount - 1); + } + lastaddr = -1; + } + } + if (c == EOF) return status; + count = getc(f); + if (count == EOF) { + if (c == 3) return status; + return LOAD_CMD_EOF; + } + if (count == 0) count = 256; + + switch (c) { + case 0: /* skip (used by Model I TRSDOS BOOT/SYS easter egg) */ + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "skip block, %d bytes\n", count); + } + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + break; + + case 1: /* load bytes into memory */ + a1 = getc(f); + if (a1 == EOF) return LOAD_CMD_EOF; + a2 = getc(f); + if (a2 == EOF) return LOAD_CMD_EOF; + addr = a1 + a2 * 256; + count -= 2; + if (count <= 0) count += 256; + if (verbosity >= VERBOSITY_MAP) { + if (verbosity >= VERBOSITY_DETAILED || + addr != lastaddr + lastcount) { + if (lastaddr != -1) { + fprintf(outf, "loaded 0x%04x - 0x%04x\n", + lastaddr, lastaddr + lastcount - 1); + } + lastaddr = addr; + lastcount = count; + } else { + lastcount += count; + } + } + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + /* Don't load PDS front end if specific member requested */ + if (!(pds && isam == -1)) { + memory[addr] = v; + if (loadmap) loadmap[addr]++; + } + addr++; + } + break; + + case 2: /* transfer address and end of file */ + if (count != 2) return c; + a1 = getc(f); + if (a1 == EOF) return LOAD_CMD_EOF; + a2 = getc(f); + if (a2 == EOF) return LOAD_CMD_EOF; + *xferaddr = a1 + a2 * 256; + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "transfer address = 0x%04x\n", *xferaddr); + } + if (stopxfer) return status; + break; + + case 3: /* end of file with no transfer address */ + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + return status; + + case 4: /* end of ISAM/PDS member */ + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "end of ISAM/PDS member\n"); + } + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + if (isam != -1) return status; + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "seek ptr = 0x%06lx\n", ftell(f)); + } + break; + + case 5: /* module header */ + if (verbosity >= VERBOSITY_TEXT) { + fprintf(outf, "module header = \""); + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + putc(v, outf); + } + fprintf(outf, "\"\n"); + } else { + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + } + break; + + case 6: /* PDS header */ + if (verbosity >= VERBOSITY_TEXT) { + fprintf(outf, "PDS header = \""); + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + putc(v, outf); + } + fprintf(outf, "\"\n"); + } else { + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + } + break; + + case 7: /* Patch name header */ + if (verbosity >= VERBOSITY_TEXT) { + fprintf(outf, "patch name = \""); + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + putc(v, outf); + } + fprintf(outf, "\"\n"); + } else { + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + } + break; + + case 8: /* ISAM directory entry */ + if (status == LOAD_CMD_OK) status = LOAD_CMD_ISAM; + if (count == 6 || count == 9) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + ientry = v; + a1 = getc(f); + if (a1 == EOF) return LOAD_CMD_EOF; + a2 = getc(f); + if (a2 == EOF) return LOAD_CMD_EOF; + iaddr = a1 + a2 * 256; + a1 = getc(f); + if (a1 == EOF) return LOAD_CMD_EOF; + a2 = getc(f); + if (a2 == EOF) return LOAD_CMD_EOF; + a3 = getc(f); + if (a3 == EOF) return LOAD_CMD_EOF; + iseek = (a2 << 16) + (a1 << 8) + a3; + } else { + return c; + } + if (count == 9) { + /* Is this right? */ + a1 = getc(f); + if (a1 == EOF) return LOAD_CMD_EOF; + a2 = getc(f); + if (a2 == EOF) return LOAD_CMD_EOF; + a3 = getc(f); + if (a3 == EOF) return LOAD_CMD_EOF; + ilen = (a2 << 16) + (a1 << 8) + a3; /*???*/ + } else { + ilen = -1; + } + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "ISAM entry 0x%02x, transfer 0x%04x, seek ptr 0x%06x", + ientry, iaddr, iseek); + if (ilen != -1) { + fprintf(outf, ", length 0x%06x\n", ilen); + } else { + fprintf(outf, "\n"); + } + } + if (ientry == isam) { + fseek(f, iseek, 0); + *xferaddr = iaddr; + } + break; + + case 0x0a: /* end of ISAM directory */ + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "end of ISAM directory\n"); + } + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + if (isam != -1) return LOAD_CMD_NOT_FOUND; + break; + + case 0x0c: /* PDS directory entry */ + status = LOAD_CMD_PDS; + if (count != 11) { + return c; + } + for (i=0; i<8; i++) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + pentry[i] = v; + } + pentry[8] = '\000'; + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + ientry = v; + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + pflags1 = v; + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + pflags2 = v; + if (pds && strcmp(pds, pentry) == 0) { + isam = ientry; + } + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "PDS entry \"%s\", ISAM 0x%02x, flags 0x%02x 0x%02x\n", + pentry, ientry, pflags1, pflags2); + } + break; + + case 0x0e: /* end of PDS directory */ + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "end of PDS directory\n"); + } + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + if (pds != NULL && isam == -1) return LOAD_CMD_NOT_FOUND; + break; + + case 0x10: /* yanked load block */ + a1 = getc(f); + if (a1 == EOF) return LOAD_CMD_EOF; + a2 = getc(f); + if (a2 == EOF) return LOAD_CMD_EOF; + addr = a1 + a2 * 256; + count -= 2; + if (count <= 0) count += 256; + if (verbosity >= VERBOSITY_MAP) { + fprintf(outf, "yanked load block 0x%04x - 0x%04x\n", + addr, addr + count - 1); + } + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + break; + + case 0x1f: /* copyright block */ + if (verbosity >= VERBOSITY_TEXT) { + fprintf(outf, "====copyright block====\n"); + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + putc(v, outf); + } + fprintf(outf, "\n=======================\n"); + } else { + while (count-- > 0) { + v = getc(f); + if (v == EOF) return LOAD_CMD_EOF; + } + } + break; + + default: + return c; + } + } + return status; +} diff --git a/load_cmd.h b/load_cmd.h new file mode 100644 index 0000000..74a8c83 --- /dev/null +++ b/load_cmd.h @@ -0,0 +1,70 @@ +/* Copyright (c) 1996-98, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* + * Last modified on Thu Apr 23 00:36:11 PDT 1998 by mann + */ + +/* TRS-80 DOS /cmd file loader */ + +#define LOAD_CMD_OK 0 +#define LOAD_CMD_EOF -1 +#define LOAD_CMD_NOT_FOUND -2 +#define LOAD_CMD_ISAM -3 +#define LOAD_CMD_PDS -4 + +#define VERBOSITY_QUIET 0 +#define VERBOSITY_TEXT 1 +#define VERBOSITY_MAP 2 +#define VERBOSITY_DETAILED 3 + +#define ISAM_NONE -1 + +/* Load the /cmd file f into the given memory, optionally selecting + * out an ISAM or PDS member. Return LOAD_CMD_OK for success if f was a + * normal /cmd file, LOAD_CMD_ISAM for success if it was an ISAM file, + * LOAD_CMD_PDS for success if this was a PDS file, LOAD_CMD_EOF for + * failure due to premature end of file, LOAD_CMD_NOT_FOUND for ISAM + * or PDF member not found, or a positive number B for an unknown or + * badly formatted load block of typecode B (load file format error). + * + * Optional flags: + * + * If loadmap is not NULL, it must point to an array of 2**16 + * bytes. Each byte in the return value will have a count (mod 256) + * of the number of times that memory location was loaded. Usually each + * count will be 0 or 1, of course. + * + * If verbosity is VERBOSITY_QUIET, print nothing. If verbosity is + * VERBOSITY_TEXT, print module headers, PDS headers, patch names, and + * copyright notices. If verbosity is VERBOSITY_MAP, also print load + * map information as we go along, but coalesce adjacent blocks that + * load contiguously into memory. If verbosity is VERBOSITY_DETAILED, + * don't coalesce. + * + * If isam is not -1, search for the given isam member number and load + * it instead of loading the whole file. + * + * If pds is not NULL, search for the given pds member name and + * load it instead of loading the whole file. isam and pds cannot + * both be used. + * + * If xferaddr is not NULL, return the transfer address there, or if + * there is no transfer address, return -1. + * + * If stopxfer is 1, stop loading when a transfer address is seen; if + * 0, continue loading. stopxfer = 0 is needed if you want to parse + * a PDS file with a front end loader. stopxfer = 1 is useful to deal + * with ordinary /cmd files that have extra garbage at the end, as + * sometimes happens. stopxfer = 0 should be considered the default. + */ + + +int +load_cmd(FILE* f, unsigned char memory[65536], + unsigned char* loadmap, int verbosity, FILE* outf, + int isam, char* pds, int* xferaddr, int stopxfer); diff --git a/load_hex.c b/load_hex.c new file mode 100644 index 0000000..066aa48 --- /dev/null +++ b/load_hex.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Sat Apr 25 00:58:56 PDT 1998 by mann +*/ + +#include "z80.h" +#include <stdlib.h> + +#define BUFFER_SIZE 256 + +extern void hex_transfer_address(int address); +extern void hex_data(int address, int value); + +static int hex_byte(char *string) +{ + char buf[3]; + + buf[0] = string[0]; + buf[1] = string[1]; + buf[2] = '\0'; + + return(strtol(buf, (char **)NULL, 16)); +} + +int load_hex(FILE *file) +{ + char buffer[BUFFER_SIZE]; + char *b; + int num_bytes; + int address; + int check; + int value; + int high = 0; + + while(fgets(buffer, BUFFER_SIZE, file)) + { + if(buffer[0] == ':') + { + /* colon */ + b = buffer + 1; + + /* number of bytes on the line */ + num_bytes = hex_byte(b); b += 2; + check = num_bytes; + + /* the starting address */ + address = hex_byte(b) << 8; b += 2; + address |= hex_byte(b); b+= 2; + check += (address >> 8) + (address & 0xff); + + /* a zero? */ + b += 2; + + /* the data */ + if(num_bytes == 0) + { + /* Transfer address */ + hex_transfer_address(address); + } else { + while(num_bytes--) + { + value = hex_byte(b); b += 2; + hex_data(address++, value); + check += value; + } + if (address > high) high = address; + + /* the checksum */ + value = hex_byte(b); + if(((0x100 - check) & 0xff) != value) + { + fatal("bad checksum from hex file"); + } + } + } + } + return high; +} diff --git a/m1format.fix b/m1format.fix new file mode 100644 index 0000000..c70b8b8 --- /dev/null +++ b/m1format.fix @@ -0,0 +1,11 @@ +. M1FORMAT/FIX - 01/24/98 - Tim Mann +. Patch to Model I LDOS 5.3.1 FORMAT to allow formatting a hard +. drive. NOPs out some buggy code that makes it fail. Odd +. that this bug is still present; README/TXT suggests it was +. fixed. This patch is required to format an emulated hard +. drive with XTRSHARD/DCT on an emulated Model I. +. +. Apply with PATCH FORMAT/CMD.RS0LT0FF USING M1FORMAT +. Do not apply to Model III LDOS or Model 4 LS-DOS! +. +X'630B'=00 00 @@ -0,0 +1,140 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Wed May 17 02:10:24 PDT 2000 by mann +*/ + +#include <string.h> +#include <stdlib.h> + +#include "z80.h" +#include "trs.h" +#include "trs_disk.h" +#include "load_cmd.h" + +int trs_model = 1; +int trs_paused = 1; +int trs_autodelay = 0; +char *program_name; + +static void check_endian() +{ + wordregister x; + x.byte.low = 1; + x.byte.high = 0; + if(x.word != 1) + { + fatal("Program compiled with wrong ENDIAN value -- adjust the Makefile.local, type \"rm *.o\", recompile, and try again."); + } +} + +void trs_load_rom(char *filename) +{ + FILE *program; + int c; + + if((program = fopen(filename, "r")) == NULL) + { + char message[100]; + sprintf(message, "could not read %s", filename); + fatal(message); + } + c = getc(program); + if (c == ':') { + /* Assume Intel hex format */ + rewind(program); + trs_rom_size = load_hex(program); + fclose(program); + return; + } else if (c == 1 || c == 5) { + /* Assume MODELA/III file */ + int res; + extern Uchar *rom; /*!! fixme*/ + Uchar loadmap[Z80_ADDRESS_LIMIT]; + rewind(program); + res = load_cmd(program, rom, loadmap, 0, NULL, -1, NULL, NULL, 1); + if (res == LOAD_CMD_OK) { + trs_rom_size = Z80_ADDRESS_LIMIT; + while (trs_rom_size > 0) { + if (loadmap[--trs_rom_size] != 0) { + trs_rom_size++; + break; + } + } + fclose(program); + return; + } else { + /* Guess it wasn't one */ + rewind(program); + c = getc(program); + } + } + trs_rom_size = 0; + while (c != EOF) { + mem_write_rom(trs_rom_size++, c); + c = getc(program); + } +} + +void trs_load_compiled_rom(int size, unsigned char rom[]) +{ + int i; + + trs_rom_size = size; + for(i = 0; i < size; ++i) + { + mem_write_rom(i, rom[i]); + } +} + +int main(int argc, char *argv[]) +{ + int debug = FALSE; + + /* program_name must be set first because the error + * printing routines use it. */ + program_name = strrchr(argv[0], '/'); + if (program_name == NULL) { + program_name = argv[0]; + } else { + program_name++; + } + + check_endian(); + + argc = trs_parse_command_line(argc, argv, &debug); + if (argc > 1) { + fprintf(stderr, "%s: erroneous argument %s\n", program_name, argv[1]); + exit(1); + } + mem_init(); + trs_disk_init(0); + trs_screen_init(); + trs_timer_init(); + + z80_reset(); + if (!debug) { + /* Run continuously until exit or request to enter debugger */ + z80_run(TRUE); + } + printf("Entering debugger.\n"); + debug_init(); + debug_shell(); + printf("Quitting.\n"); + exit(0); +} + diff --git a/mkdisk.c b/mkdisk.c new file mode 100644 index 0000000..7833642 --- /dev/null +++ b/mkdisk.c @@ -0,0 +1,395 @@ +/* Copyright (c) 1996-98, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue May 16 19:54:51 PDT 2000 by mann */ + +/* + * mkdisk.c + * Make a blank (unformatted) emulated floppy or hard drive in a file, + * or write protect/unprotect an existing one. + */ + +#include <stdio.h> +#include <unistd.h> +#include <time.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> + +typedef unsigned char Uchar; +#include "reed.h" +ReedHardHeader rhh; + +void Usage(char *progname) +{ + fprintf(stderr, + "Usage:\t%s -1 file\n" + "\t%s [-3] file\n" + "\t%s -k [-s sides] [-d density] [-8] [-i] file\n" + "\t%s -h [-c cyl] [-s sec] [-g gran] file\n" + "\t%s {-p|-u} {-1|-3|-k|-h} file\n", + progname, progname, progname, progname, progname); + exit(2); +} + +int +main(int argc, char *argv[]) +{ + int jv1 = 0, jv3 = 0, dmk = 0, hard = 0; + int cyl = -1, sec = -1, gran = -1, dir = -1, eight = 0, ignden = 0; + int writeprot = 0, unprot = 0; + int i, c, oumask; + char *fname; + FILE *f; + + opterr = 0; + for (;;) { + c = getopt(argc, argv, "13khc:s:g:d:8ipu"); + if (c == -1) break; + switch (c) { + case '1': + jv1 = 1; + break; + case '3': + jv3 = 1; + break; + case 'k': + dmk = 1; + break; + case 'h': + hard = 1; + break; + case 'c': + cyl = atoi(optarg); + break; + case 's': + sec = atoi(optarg); + break; + case 'g': + gran = atoi(optarg); + break; + case 'd': + dir = atoi(optarg); + break; + case '8': + eight = 1; + break; + case 'i': + ignden = 1; + break; + case 'p': + writeprot = 1; + break; + case 'u': + unprot = 1; + break; + case '?': + default: + Usage(argv[0]); + break; + } + } + + if (argc - optind != 1) Usage(argv[0]); + + fname = argv[optind]; + + if (writeprot || unprot) { + /* Completely different functionality here */ + struct stat st; + int newmode; + + if (writeprot && unprot) { + fprintf(stderr, + "%s: -p and -u are mutually exclusive\n", argv[0]); + exit(2); + } + + if (jv1 + jv3 + dmk + hard != 1) { + fprintf(stderr, + "%s: %s requires exactly one of -1, -3, -k, or -h\n", + argv[0], writeprot ? "-p" : "-u"); + exit(2); + } + + if (stat(fname, &st) < 0) { + perror(fname); + exit(1); + } + + /* Make writable so we can poke inside if need be */ + if (chmod(fname, st.st_mode | (S_IWUSR|S_IWGRP|S_IWOTH)) < 0) { + perror(fname); + exit(1); + } + + f = fopen(fname, "r+"); + if (f == NULL) { + perror(fname); + exit(1); + } + + /* Poke inside */ + if (jv1) { + /* No indication inside; nothing to do here */ + + } else if (jv3) { + /* Set the magic byte */ + fseek(f, 256*34-1, 0); + putc(writeprot ? 0 : 0xff, f); + + } else if (dmk) { + /* Set the magic byte */ + putc(writeprot ? 0xff : 0, f); + + } else if (hard) { + /* Set the magic bit */ + fseek(f, 7, 0); + newmode = getc(f); + if (newmode == EOF) { + perror(fname); + exit(1); + } + newmode = (newmode & 0x7f) | (writeprot ? 0x80 : 0); + fseek(f, 7, 0); + putc(newmode, f); + + } + + /* Finish by chmoding the file appropriately */ + if (writeprot) { + newmode = st.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH); + } else { + oumask = umask(0); + umask(oumask); + newmode = st.st_mode | + (( (st.st_mode & (S_IRUSR|S_IRGRP|S_IROTH)) >> 1 ) & ~oumask); + } + if (fchmod(fileno(f), newmode)) { + perror(fname); + exit(1); + } + fclose(f); + exit(0); + } + + switch (jv1 + jv3 + dmk + hard) { + case 0: + jv3 = 1; + break; + case 1: + break; + default: + fprintf(stderr, + "%s: -1, -3, -k, and -h are mutually exclusive\n", argv[0]); + exit(2); + } + + if ((jv1 || jv3) && (cyl >= 0 || sec >= 0 || gran >= 0 || dir >= 0)) { + fprintf(stderr, + "%s: -c, -s, -g, -d are not meaningful with -1 or -3\n", argv[0]); + exit(2); + } + + if (dmk && (cyl >= 0 || gran >= 0)) { + fprintf(stderr, "%s: -c and -g are not meaningful with -k\n", argv[0]); + exit(2); + } + + if (!dmk && (eight || ignden)) { + fprintf(stderr, "%s: -8 and -i are only meaningful with -k\n", argv[0]); + exit(2); + } + + if (jv1) { + /* Unformatted JV1 disk - just an empty file! */ + f = fopen(fname, "w"); + if (f == NULL) { + perror(fname); + exit(1); + } + + } else if (jv3) { + /* Unformatted JV3 disk. */ + f = fopen(fname, "w"); + if (f == NULL) { + perror(fname); + exit(1); + } + for (i=0; i<(256*34); i++) { + putc(0xff, f); + } + + } else if (dmk) { + /* Unformatted DMK disk */ + /* Reuse flag letters s, d */ +#define sides sec +#define density dir + if (sides == -1) sides = 2; + if (density == -1) density = 2; + + if (sides != 1 && sides != 2) { + fprintf(stderr, "%s error: sides must be 1 or 2\n", argv[0]); + exit(2); + } + if (density < 1 || density > 2) { + fprintf(stderr, "%s error: density must be 1 or 2\n", argv[0]); + exit(2); + } + + f = fopen(fname, "w"); + putc(0, f); /* 0: not write protected */ + putc(0, f); /* 1: initially zero tracks */ + if (eight) { + if (density == 1) + i = 0x14e0; + else + i = 0x2940; + } else { + if (density == 1) + i = 0x0cc0; + else + i = 0x1900; + } + putc(i & 0xff, f); /* 2: LSB of track length */ + putc(i >> 8, f); /* 3: MSB of track length */ + i = 0; + if (sides == 1) i |= 0x10; + if (density == 1) i |= 0x40; + if (ignden) i |= 0x80; + putc(i, f); /* 4: options */ + putc(0, f); /* 5: reserved */ + putc(0, f); /* 6: reserved */ + putc(0, f); /* 7: reserved */ + putc(0, f); /* 8: reserved */ + putc(0, f); /* 9: reserved */ + putc(0, f); /* a: reserved */ + putc(0, f); /* b: reserved */ + putc(0, f); /* c: MBZ */ + putc(0, f); /* d: MBZ */ + putc(0, f); /* e: MBZ */ + putc(0, f); /* f: MBZ */ + + } else /* hard */ { + /* Unformatted hard disk */ + /* We don't care about most of this header, but we generate + it just in case some user wants to exchange hard drives with + Matthew Reed's emulator or with Pete Cervasio's port of + xtrshard/dct to Jeff Vavasour's Model III/4 emulator. + */ + time_t tt = time(0); + struct tm *lt = localtime(&tt); + Uchar *rhhp; + int cksum; + + if (cyl == -1) cyl = 202; + if (sec == -1) sec = 256; + if (gran == -1) gran = 8; + if (dir == -1) dir = 1; + + if (cyl < 3) { + fprintf(stderr, "%s error: cyl < 3\n", argv[0]); + exit(2); + } + if (cyl > 256) { + fprintf(stderr, "%s error: cyl > 256\n", argv[0]); + exit(2); + } + if (cyl > 203) { + fprintf(stderr, + "%s warning: cyl > 203 is incompatible with XTRSHARD/DCT\n", + argv[0]); + } + if (sec < 4) { + fprintf(stderr, "%s error: sec < 4\n", argv[0]); + exit(2); + } + if (sec > 256) { + fprintf(stderr, "%s error: sec > 256\n", argv[0]); + exit(2); + } + if ((sec % 32) != 0) { + fprintf(stderr, "%s warning: %s\n", argv[0], + "(sec % 32) != 0 is incompatible with WD1000/1010 emulation"); + if (sec > 32) { + fprintf(stderr, "%s warning: %s\n", argv[0], + "(sec % 32) != 0 and sec > 32 " + "is incompatible with Matthew Reed's emulators"); + } + } + if (gran < 1) { + fprintf(stderr, "%s error: gran < 1\n", argv[0]); + exit(2); + } + if (gran > 8) { + fprintf(stderr, "%s error: gran > 8\n", argv[0]); + exit(2); + } + if (sec < gran) { + fprintf(stderr, "%s error: sec < gran\n", argv[0]); + exit(2); + } + if (sec % gran != 0) { + fprintf(stderr, "%s error: sec %% gran != 0\n", argv[0]); + exit(2); + } + if (sec / gran > 32) { + fprintf(stderr, "%s error: sec / gran > 32\n", argv[0]); + exit(2); + } + if (dir < 1) { + fprintf(stderr, "%s error: dir < 1\n", argv[0]); + exit(2); + } + if (dir >= cyl) { + fprintf(stderr, "%s error: dir >= cyl\n", argv[0]); + exit(2); + } + + rhh.id1 = 0x56; + rhh.id2 = 0xcb; + rhh.ver = 0x10; + rhh.cksum = 0; /* init for cksum computation */ + rhh.blks = 1; + rhh.mb4 = 4; + rhh.media = 0; + rhh.flag1 = 0; + rhh.flag2 = rhh.flag3 = 0; + rhh.crtr = 0x42; + rhh.dfmt = 0; + rhh.mm = lt->tm_mon + 1; + rhh.dd = lt->tm_mday; + rhh.yy = lt->tm_year; + rhh.dparm = 0; + rhh.cyl = cyl; + rhh.sec = sec; + rhh.gran = gran; + rhh.dcyl = dir; + strcpy(rhh.label, "xtrshard"); + strcpy(rhh.filename, fname); /* note we don't limit to 8 chars */ + + cksum = 0; + rhhp = (Uchar *) &rhh; + for (i=0; i<=31; i++) { + cksum += rhhp[i]; + } + rhh.cksum = ((Uchar) cksum) ^ 0x4c; + + f = fopen(fname, "w"); + if (f == NULL) { + perror(fname); + exit(1); + } + fwrite(&rhh, sizeof(rhh), 1, f); + } + fclose(f); + return 0; +} + + + diff --git a/mkdisk.man b/mkdisk.man new file mode 100644 index 0000000..688081e --- /dev/null +++ b/mkdisk.man @@ -0,0 +1,254 @@ +.TH mkdisk 1 +.SH Name +mkdisk \- Make a blank emulated floppy or hard disk for xtrs, +or add/remove an emulated write protect tab +.SH Syntax +.B mkdisk -1 filename +.br +.B mkdisk [-3] filename +.br +.B mkdisk -k [-s sides] [-d density] [-8] [-i] filename +.br +.B mkdisk -h [-c cyl] [-s sec] [-g gran] filename +.br +.B mkdisk {-p|-u} {-1|-3|-k|-h} filename +.SH Description +The mkdisk program is part of the \fBxtrs\fP(1) package. It has two +distinct functions: (1) It can make a +blank (unformatted) emulated floppy or hard drive in a file. +(2) With the -p or -u flag, it can +turn the write protect flag on or off for an existing emulated floppy or hard +drive file. See the xtrs man page for background information. + +The conventional file extensions are .dsk for emulated floppies +and .hdv for emulated hard drives, but \fBmkdisk\fP does not enforce this +convention; you can use any filename. Other extensions sometimes used +for emulated floppies are .jv1, .jv3, .8in, and .dmk. +.SH Making Emulated Floppies +With the -1 flag, \fBmkdisk\fP makes an unformatted emulated floppy of +type JV1. No additional flags are accepted. + +With the -3 flag (which is the default and should normally +be used), \fBmkdisk\fP makes an unformatted emulated floppy of type +JV3. No additional flags are accepted. + +With the -k flag, \fBmkdisk\fP makes an unformatted emulated floppy of +type DMK. With -k, the optional flags -s, -d, -8, and -i can be used +to give the emulated floppy special properties. Specifying -s1 +limits the floppy to one side; with -s2 (the default), the floppy can +be formatted as either one- or two-sided. Specifying -d1 limits the +floppy to single density; with -d2 (the default), the floppy can be +formatted in either single or double density. Specifying -8 allows +the floppy to be formatted in an emulated 8" drive; by default it will +work properly only in an emulated 5" drive. Setting -s1 or -d1 +saves space after the floppy is formatted; setting -8 consumes +additional space. Specifying -i activates a peculiar feature in some +TRS-80 emulators that causes each formatted sector to appear to be +both single and double density. +.SH Making Emulated Hard Drives +With the -h flag, \fBmkdisk\fP makes an unformatted emulated hard +drive with \fIcyl\fP cylinders, \fIsec\fP sectors, and \fIgran\fP +granules (LDOS allocation units) per cylinder. The hard drive +will have cylinder \fIdir\fP marked for use as its directory. + +You will usually want to use the default values for all these +parameters. The default is 202 cylinders, 256 sectors per cylinder +(that is, 8 heads and 32 sectors per track), and 8 granules per +cylinder. This is the largest hard drive that can be used by all +LDOS/LS-DOS operating systems without partitioning the drive or +patching the FORMAT command. The details on what nondefault values +are possible vary, depending on which of xtrs's two hard drive +emulations you are using and which other emulators you want to be +compatible with, and it is probably best not to delve into these +complexities, but read on if you really want to. + +For \fIcyl\fP, the number of cylinders on the drive, the default value +is 202, the minimum is 3, and the maximum that can be represented in +the HDV file's header is 256. You can use 203 cylinders with LDOS and +LS-DOS if you format the drive with Model 4 LS-DOS; a minor bug in +Model I/III FORMAT/CMD prevents more than 202 cylinders from being +formatted, but the system can use 203 thereafter. 203 cylinders is +the absolute maximum for LDOS/LS-DOS drivers that do not support +partitioning, including the emulator-specific drivers supplied with xtrs +(XTRSHARD/DCT), with Matthew Reed's emulator (HARD/CMD), and with +David Keil's emulator (EHARD/DCT). + +In xtrs 4.1 and later, and in David Keil's emulator version 6.0 and +later, a true emulation of Radio Shack's WD1010-based hard disk +controller is also available, which works with the native drivers for +the original hardware, such as RSHARDx/DCT and the hard disk drivers +for NEWDOS and CP/M. In xtrs, the WD1010 emulation ignores the +maximum number of cylinders specified in the HDV file's header and +allows the driver to format up to 65536 cylinders. This may be useful +if your drivers support partitioning (but why would anyone want to +partition an emulated hard drive instead of just making two smaller +ones?), or if your operating system supports more than 203 cylinders +per partition. Note that although RSHARDx/DCT allows up to 406 +cylinders per partition, if you use more than 203, the maximum number +of sectors per cylinder is limited to 128, so you gain nothing; the +maximum size of a partition is still the same. + +For \fIsec\fP, the number of sectors per cylinder, the default value +is 256, the maximum is 256, and the minimum is 4. There are some +restrictions on the values that will work. For the greatest +portability, choose a value that is divisible by 32. With xtrs's +XTRSHARD/DCT and David Keil's EHARD/DCT, any value is allowed that can +be evenly divided into granules; see the next paragraph. With Matthew +Reed's HARD/CMD, if \fIsec\fP is greater than 32, it must be divisible +by 32. With the emulation of a real WD1010 in newer versions of xtrs +(and probably David Keil's emulator too), \fIsec\fP must always be +divisible by 32, because we always emulate a drive with 32 sectors per +track and from 1 to 8 heads (tracks per cylinder). The RSHARDx/DCT +driver assumes that there are always 32 sectors per track. + +For \fIgran\fP, the default value is 8, the maximum is 8, and the +minimum is 1. In addition, it is necessary that \fIsec\fP be evenly +divisible by \fIgran\fP, and that \fIsec/gran\fP be less than or equal +to 32. This value is used only with the emulator-specific drivers +listed above; it is ignored when xtrs is using native hardware drivers +such as RSHARDx/DCT. + +The maximum size of a hard drive image is controlled by \fIcyl\fP and +\fIsec\fP: it can be at most \fIcyl*sec\fP 256-byte sectors. The +image file starts out small and grows as you write to more cylinders. +The allocation efficiency is controlled by the granule size: LDOS +allocates file space in granules. Therefore (1) +\fIgran\fP should always be set as large as possible and (2) reducing +\fIsec\fP, thereby making the granules smaller, reduces wasted space +due to fragmentation but limits the maximum size of the drive. + +Seeing that the maximum unpartitioned drive size is less than 13 MB and +that the maximum granule size is only 8 KB, wasted space should not be +much of a concern for most \fBxtrs\fP users. Therefore the default +parameters have been chosen to give you the largest drive possible without +partitioning. +.SH Write Protection +With the -p flag, \fBmkdisk\fP turns on write protection +for an existing emulated floppy or hard drive. It turns off all Unix +write permission bits on the file, and (except for JV1 floppies) also sets +a write-protected flag inside the file. + +With the -u flag, \fBmkdisk\fP turns off write protection +for an existing emulated floppy or hard drive. It turns on Unix +write permissions to the file, masked by your current umask and +the file's current read permissions. +It also clears +a write-protected flag inside the file (except on JV1 floppies, which +don't have such a flag). + +\fBmkdisk\fP currently does not have code to auto-recognize file +formats, so the -p or -u flag must be accompanied by either -1 (JV1), +-3 (JV3), -k (DMK), or -h (hard disk) to identify the file format. +There is also no checking for the correct file format, so if you +give the wrong flag, the wrong byte inside your file will be changed. + +.SH Technical data +The JV1 format is just an array of 256-byte sectors, in the order +(track 0 sector 0, track 0 sector 1, ... track 0 sector 9, track 1 +sector 0, ...). It can represent only single-sided, single-density +floppies. The directory is assumed to be track 17. + +The original JV3 format is documented in the printed manual for Jeff +Vavasour's commercial Model III/4 emulator. The xtrs implementation +includes some extensions. + +Full documentation for both JV1 and JV3 can be found at +http://www.tim-mann.org/trs80/dskspec.html. +A copy of this html file is also included in the \fBxtrs\fP distribution. + +The DMK format is documented in a file on David Keil's web site, +http://discover-net.net/~dmkeil/trsdoc.htm#Technical-disks; this file +is also included with his emulator. Some updates to the 4.00 version +of the document: (1) If neither the single density nor ignore density +option is set and single density data is recorded, each single density +byte is written twice (i.e., the four bytes 12345678 would be written as +1212343456567878). This ensures that when single and double density +sectors are mixed, each type occupies the correct relative amount of +space in the track. This update will be effective in version 4.3 of +David's emulator; it is incompatible with previous versions. (2) Bit +15 of an IDAM offset is 1 if the sector is double-density, 0 if single +density. Bit 14 is reserved; it currently must be 0. The actual +offset is in bits 13-0. These offsets are relative to the start of +the track header, they must be in ascending order (I hope!!), and an +offset of 0 or 0xffff terminates the list. + +An HDV (hard disk) image has the following format. This information +is based on email from Matthew Reed. There is an initial 256-byte +header block, followed by an array of sectors. The geometry of the +drive is defined in the header block, which looks like this (from +mkdisk.c): + +.nf +typedef unsigned char Uchar; +typedef struct { + Uchar id1; /* 0: Identifier #1: 56H */ + Uchar id2; /* 1: Identifier #2: CBH */ + Uchar ver; /* 2: Version of format: 10H = version 1.0 */ + Uchar cksum; /* 3: Simple checksum: + To calculate, add together bytes 0 to 31 of header + (excepting byte 3), then XOR result with 4CH */ + Uchar blks; /* 4: Number of 256 byte blocks in header: should be 1 */ + Uchar mb4; /* 5: Not used, currently set to 4 */ + Uchar media; /* 6: Media type: 0 for hard disk */ + Uchar flag1; /* 7: Flags #1: + bit 7: Write protected: 0 for no, 1 for yes + [warning: xtrs currently ignores this flag] + bit 6: Must be 0 + bit 5 - 0: reserved */ + Uchar flag2; /* 8: Flags #2: reserved */ + Uchar flag3; /* 9: Flags #3: reserved */ + Uchar crtr; /* 10: Created by: + 14H = HDFORMAT + 42H = xtrs mkdisk + 80H = Cervasio xtrshard port to Vavasour M4 emulator */ + Uchar dfmt; /* 11: Disk format: 0 = LDOS/LS-DOS */ + Uchar mm; /* 12: Creation month: mm */ + Uchar dd; /* 13: Creation day: dd */ + Uchar yy; /* 14: Creation year: yy (offset from 1900) */ + Uchar res1[12]; /* 15 - 26: reserved */ + Uchar dparm; /* 27: Disk parameters: (unused with hard drives) + bit 7: Density: 0 = double, 1 = single + bit 6: Sides: 0 = one side, 1 = 2 sides + bit 5: First sector: 0 if sector 0, 1 if sector 1 + bit 4: DAM convention: 0 if normal (LDOS), + 1 if reversed (TRSDOS 1.3) + bit 3 - 0: reserved */ + Uchar cyl; /* 28: Number of cylinders per disk */ + Uchar sec; /* 29: Number of sectors per track (floppy); cyl (hard) */ + Uchar gran; /* 30: Number of granules per track (floppy); cyl (hard)*/ + Uchar dcyl; /* 31: Directory cylinder [mkdisk sets to 1; xtrs + ignores, but value must be correct if image is + to be used with Reed emulators.] */ + char label[32]; /* 32: Volume label: 31 bytes terminated by 0 */ + char filename[8];/* 64 - 71: 8 characters of filename (without extension) + [Cervasio addition. xtrs actually doesn't limit this + to 8 chars or strip the extension] */ + Uchar res2[184]; /* 72 - 255: reserved */ +} ReedHardHeader; +.fi + +.SH See also +.BR xtrs (1) + +http://www.tim-mann.org/trs80/dskspec.html +.SH Authors +\fBmkdisk\fP was written by Timothy Mann (see http://tim-mann.org/). + +The floppy file formats here called JV1 and JV3 were developed by Jeff +Vavasour for his MSDOS-based Model I and Model III/4 emulators +(respectively). They have become a de facto standard in the TRS-80 +emulation community, and much TRS-80 software is available on the +Internet in .dsk format. Thanks to Jeff for designing and documenting +the formats. + +The format here called DMK was developed by David Keil for his +MSDOS-based Model 4 emulator. This format has the advantage that it +can represent essentially everything the original TRS-80 floppy disk +controllers can write, including all forms of copy protected disk. +Thanks to David for designing and documenting this format. + +The hard drive format was developed by Matthew Reed for his +MSDOS-based Model I/III and Model 4 emulators. I have duplicated his +format to allow users to exchange .hdv hard drive images between +\fBxtrs\fP and Matthew's emulators. Thanks to Matthew for designing +the format and providing documentation. diff --git a/mkdisk.txt b/mkdisk.txt new file mode 100644 index 0000000..614f0a4 --- /dev/null +++ b/mkdisk.txt @@ -0,0 +1,255 @@ +mkdisk(1) mkdisk(1) + + + +[1mName[0m + mkdisk - Make a blank emulated floppy or hard disk for xtrs, or + add/remove an emulated write protect tab + +[1mSyntax[0m + [1mmkdisk -1 filename[0m + [1mmkdisk [-3] filename[0m + [1mmkdisk -k [-s sides] [-d density] [-8] [-i] filename[0m + [1mmkdisk -h [-c cyl] [-s sec] [-g gran] filename[0m + [1mmkdisk {-p|-u} {-1|-3|-k|-h} filename[0m + +[1mDescription[0m + The mkdisk program is part of the [1mxtrs[22m(1) package. It has two distinct + functions: (1) It can make a blank (unformatted) emulated floppy or + hard drive in a file. (2) With the -p or -u flag, it can turn the + write protect flag on or off for an existing emulated floppy or hard + drive file. See the xtrs man page for background information. + + The conventional file extensions are .dsk for emulated floppies and + .hdv for emulated hard drives, but [1mmkdisk [22mdoes not enforce this conven- + tion; you can use any filename. Other extensions sometimes used for + emulated floppies are .jv1, .jv3, .8in, and .dmk. + +[1mMaking Emulated Floppies[0m + With the -1 flag, [1mmkdisk [22mmakes an unformatted emulated floppy of type + JV1. No additional flags are accepted. + + With the -3 flag (which is the default and should normally be used), + [1mmkdisk [22mmakes an unformatted emulated floppy of type JV3. No additional + flags are accepted. + + With the -k flag, [1mmkdisk [22mmakes an unformatted emulated floppy of type + DMK. With -k, the optional flags -s, -d, -8, and -i can be used to + give the emulated floppy special properties. Specifying -s1 limits the + floppy to one side; with -s2 (the default), the floppy can be formatted + as either one- or two-sided. Specifying -d1 limits the floppy to sin- + gle density; with -d2 (the default), the floppy can be formatted in + either single or double density. Specifying -8 allows the floppy to be + formatted in an emulated 8" drive; by default it will work properly + only in an emulated 5" drive. Setting -s1 or -d1 saves space after the + floppy is formatted; setting -8 consumes additional space. Specifying + -i activates a peculiar feature in some TRS-80 emulators that causes + each formatted sector to appear to be both single and double density. + +[1mMaking Emulated Hard Drives[0m + With the -h flag, [1mmkdisk [22mmakes an unformatted emulated hard drive with + [4mcyl[24m cylinders, [4msec[24m sectors, and [4mgran[24m granules (LDOS allocation units) + per cylinder. The hard drive will have cylinder [4mdir[24m marked for use as + its directory. + + You will usually want to use the default values for all these parame- + ters. The default is 202 cylinders, 256 sectors per cylinder (that is, + 8 heads and 32 sectors per track), and 8 granules per cylinder. This + is the largest hard drive that can be used by all LDOS/LS-DOS operating + systems without partitioning the drive or patching the FORMAT command. + The details on what nondefault values are possible vary, depending on + which of xtrs's two hard drive emulations you are using and which other + emulators you want to be compatible with, and it is probably best not + to delve into these complexities, but read on if you really want to. + + For [4mcyl[24m, the number of cylinders on the drive, the default value is + 202, the minimum is 3, and the maximum that can be represented in the + HDV file's header is 256. You can use 203 cylinders with LDOS and LS- + DOS if you format the drive with Model 4 LS-DOS; a minor bug in Model + I/III FORMAT/CMD prevents more than 202 cylinders from being formatted, + but the system can use 203 thereafter. 203 cylinders is the absolute + maximum for LDOS/LS-DOS drivers that do not support partitioning, + including the emulator-specific drivers supplied with xtrs (XTR- + SHARD/DCT), with Matthew Reed's emulator (HARD/CMD), and with David + Keil's emulator (EHARD/DCT). + + In xtrs 4.1 and later, and in David Keil's emulator version 6.0 and + later, a true emulation of Radio Shack's WD1010-based hard disk con- + troller is also available, which works with the native drivers for the + original hardware, such as RSHARDx/DCT and the hard disk drivers for + NEWDOS and CP/M. In xtrs, the WD1010 emulation ignores the maximum + number of cylinders specified in the HDV file's header and allows the + driver to format up to 65536 cylinders. This may be useful if your + drivers support partitioning (but why would anyone want to partition an + emulated hard drive instead of just making two smaller ones?), or if + your operating system supports more than 203 cylinders per partition. + Note that although RSHARDx/DCT allows up to 406 cylinders per parti- + tion, if you use more than 203, the maximum number of sectors per + cylinder is limited to 128, so you gain nothing; the maximum size of a + partition is still the same. + + For [4msec[24m, the number of sectors per cylinder, the default value is 256, + the maximum is 256, and the minimum is 4. There are some restrictions + on the values that will work. For the greatest portability, choose a + value that is divisible by 32. With xtrs's XTRSHARD/DCT and David + Keil's EHARD/DCT, any value is allowed that can be evenly divided into + granules; see the next paragraph. With Matthew Reed's HARD/CMD, if [4msec[0m + is greater than 32, it must be divisible by 32. With the emulation of + a real WD1010 in newer versions of xtrs (and probably David Keil's emu- + lator too), [4msec[24m must always be divisible by 32, because we always emu- + late a drive with 32 sectors per track and from 1 to 8 heads (tracks + per cylinder). The RSHARDx/DCT driver assumes that there are always 32 + sectors per track. + + For [4mgran[24m, the default value is 8, the maximum is 8, and the minimum is + 1. In addition, it is necessary that [4msec[24m be evenly divisible by [4mgran[24m, + and that [4msec/gran[24m be less than or equal to 32. This value is used only + with the emulator-specific drivers listed above; it is ignored when + xtrs is using native hardware drivers such as RSHARDx/DCT. + + The maximum size of a hard drive image is controlled by [4mcyl[24m and [4msec[24m: it + can be at most [4mcyl*sec[24m 256-byte sectors. The image file starts out + small and grows as you write to more cylinders. The allocation effi- + ciency is controlled by the granule size: LDOS allocates file space in + granules. Therefore (1) [4mgran[24m should always be set as large as possible + and (2) reducing [4msec[24m, thereby making the granules smaller, reduces + wasted space due to fragmentation but limits the maximum size of the + drive. + + Seeing that the maximum unpartitioned drive size is less than 13 MB and + that the maximum granule size is only 8 KB, wasted space should not be + much of a concern for most [1mxtrs [22musers. Therefore the default parame- + ters have been chosen to give you the largest drive possible without + partitioning. + +[1mWrite Protection[0m + With the -p flag, [1mmkdisk [22mturns on write protection for an existing emu- + lated floppy or hard drive. It turns off all Unix write permission + bits on the file, and (except for JV1 floppies) also sets a write-pro- + tected flag inside the file. + + With the -u flag, [1mmkdisk [22mturns off write protection for an existing + emulated floppy or hard drive. It turns on Unix write permissions to + the file, masked by your current umask and the file's current read per- + missions. It also clears a write-protected flag inside the file + (except on JV1 floppies, which don't have such a flag). + + [1mmkdisk [22mcurrently does not have code to auto-recognize file formats, so + the -p or -u flag must be accompanied by either -1 (JV1), -3 (JV3), -k + (DMK), or -h (hard disk) to identify the file format. There is also no + checking for the correct file format, so if you give the wrong flag, + the wrong byte inside your file will be changed. + + +[1mTechnical data[0m + The JV1 format is just an array of 256-byte sectors, in the order + (track 0 sector 0, track 0 sector 1, ... track 0 sector 9, track 1 sec- + tor 0, ...). It can represent only single-sided, single-density flop- + pies. The directory is assumed to be track 17. + + The original JV3 format is documented in the printed manual for Jeff + Vavasour's commercial Model III/4 emulator. The xtrs implementation + includes some extensions. + + Full documentation for both JV1 and JV3 can be found at http://www.tim- + mann.org/trs80/dskspec.html. A copy of this html file is also included + in the [1mxtrs [22mdistribution. + + The DMK format is documented in a file on David Keil's web site, + http://discover-net.net/~dmkeil/trsdoc.htm#Technical-disks; this file + is also included with his emulator. Some updates to the 4.00 version + of the document: (1) If neither the single density nor ignore density + option is set and single density data is recorded, each single density + byte is written twice (i.e., the four bytes 12345678 would be written + as 1212343456567878). This ensures that when single and double density + sectors are mixed, each type occupies the correct relative amount of + space in the track. This update will be effective in version 4.3 of + David's emulator; it is incompatible with previous versions. (2) Bit 15 + of an IDAM offset is 1 if the sector is double-density, 0 if single + density. Bit 14 is reserved; it currently must be 0. The actual off- + set is in bits 13-0. These offsets are relative to the start of the + track header, they must be in ascending order (I hope!!), and an offset + of 0 or 0xffff terminates the list. + + An HDV (hard disk) image has the following format. This information is + based on email from Matthew Reed. There is an initial 256-byte header + block, followed by an array of sectors. The geometry of the drive is + defined in the header block, which looks like this (from mkdisk.c): + + typedef unsigned char Uchar; + typedef struct { + Uchar id1; /* 0: Identifier #1: 56H */ + Uchar id2; /* 1: Identifier #2: CBH */ + Uchar ver; /* 2: Version of format: 10H = version 1.0 */ + Uchar cksum; /* 3: Simple checksum: + To calculate, add together bytes 0 to 31 of header + (excepting byte 3), then XOR result with 4CH */ + Uchar blks; /* 4: Number of 256 byte blocks in header: should be 1 */ + Uchar mb4; /* 5: Not used, currently set to 4 */ + Uchar media; /* 6: Media type: 0 for hard disk */ + Uchar flag1; /* 7: Flags #1: + bit 7: Write protected: 0 for no, 1 for yes + [warning: xtrs currently ignores this flag] + bit 6: Must be 0 + bit 5 - 0: reserved */ + Uchar flag2; /* 8: Flags #2: reserved */ + Uchar flag3; /* 9: Flags #3: reserved */ + Uchar crtr; /* 10: Created by: + 14H = HDFORMAT + 42H = xtrs mkdisk + 80H = Cervasio xtrshard port to Vavasour M4 emulator */ + Uchar dfmt; /* 11: Disk format: 0 = LDOS/LS-DOS */ + Uchar mm; /* 12: Creation month: mm */ + Uchar dd; /* 13: Creation day: dd */ + Uchar yy; /* 14: Creation year: yy (offset from 1900) */ + Uchar res1[12]; /* 15 - 26: reserved */ + Uchar dparm; /* 27: Disk parameters: (unused with hard drives) + bit 7: Density: 0 = double, 1 = single + bit 6: Sides: 0 = one side, 1 = 2 sides + bit 5: First sector: 0 if sector 0, 1 if sector 1 + bit 4: DAM convention: 0 if normal (LDOS), + 1 if reversed (TRSDOS 1.3) + bit 3 - 0: reserved */ + Uchar cyl; /* 28: Number of cylinders per disk */ + Uchar sec; /* 29: Number of sectors per track (floppy); cyl (hard) */ + Uchar gran; /* 30: Number of granules per track (floppy); cyl (hard)*/ + Uchar dcyl; /* 31: Directory cylinder [mkdisk sets to 1; xtrs + ignores, but value must be correct if image is + to be used with Reed emulators.] */ + char label[32]; /* 32: Volume label: 31 bytes terminated by 0 */ + char filename[8];/* 64 - 71: 8 characters of filename (without extension) + [Cervasio addition. xtrs actually doesn't limit this + to 8 chars or strip the extension] */ + Uchar res2[184]; /* 72 - 255: reserved */ + } ReedHardHeader; + + +[1mSee also[0m + [1mxtrs[22m(1) + + http://www.tim-mann.org/trs80/dskspec.html + +[1mAuthors[0m + [1mmkdisk [22mwas written by Timothy Mann (see http://tim-mann.org/). + + The floppy file formats here called JV1 and JV3 were developed by Jeff + Vavasour for his MSDOS-based Model I and Model III/4 emulators (respec- + tively). They have become a de facto standard in the TRS-80 emulation + community, and much TRS-80 software is available on the Internet in + .dsk format. Thanks to Jeff for designing and documenting the formats. + + The format here called DMK was developed by David Keil for his MSDOS- + based Model 4 emulator. This format has the advantage that it can rep- + resent essentially everything the original TRS-80 floppy disk con- + trollers can write, including all forms of copy protected disk. Thanks + to David for designing and documenting this format. + + The hard drive format was developed by Matthew Reed for his MSDOS-based + Model I/III and Model 4 emulators. I have duplicated his format to + allow users to exchange .hdv hard drive images between [1mxtrs [22mand + Matthew's emulators. Thanks to Matthew for designing the format and + providing documentation. + + + + mkdisk(1) diff --git a/mount.ccc b/mount.ccc new file mode 100644 index 0000000..df4cada --- /dev/null +++ b/mount.ccc @@ -0,0 +1,104 @@ +/* mount.ccc -- Misosys C program to switch emulated floppies on xtrs */ +/* Copyright (c) 1998, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 14:54:58 PST 1998 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +usage() +{ + fprintf(stderr, "usage: mount [-l] file.dsk unit#\n"); + exit(1); +} + +#define DDIRSIZE 256 +#define DNAMESIZE 512 +#define FNAMESIZE 256 +#define CMDSIZE 512 +#define ERRBUFSIZE 512 + +int main(argc, argv) + int argc; + char **argv; +{ + char ddir[DDIRSIZE]; + char dname[DNAMESIZE], fname[FNAMESIZE], cmd[CMDSIZE]; + char errbuf[ERRBUFSIZE]; + char model[4]; + int hl, bc, de, ret, lower, i; + char *retp, *p, *q; + + if (argc < 2) { + usage(); + } + i = 1; + if (argv[i][0] == '-') { + if (tolower(argv[i][1]) != 'l') { + usage(); + } + i++; + lower = 1; + } else { + lower = 0; + } + if (argc - i != 2 || !isdigit(argv[i+1][0])) { + usage(); + } + retp = emt_gtddir(ddir, DDIRSIZE); + if (retp == NULL) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "emt_getddir: %s\n", errbuf); + exit(1); + } + emt_4misc(EMT_MISC_QUERY_MODEL, &hl, &bc, &de); + if (hl == 5) { + strcpy(model, "4p"); + } else { + sprintf(model, "%d", hl); + } + sprintf(dname, "%s/disk%s-%s", ddir, model, argv[i+1]); + sprintf(cmd, "rm -f %s", dname); + /*printf("$ %s\n", cmd);*/ + ret = emt_system(cmd); + if (ret != 0) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "emt_system: %s\n", errbuf); + exit(1); + } + + p = fname; + q = argv[i]; + if (lower) { + while (*q) { + if (*q == '[' && *(q+1)) { + q++; + *p++ = *q++; + } else if (isalpha(*q)) { + *p++ = tolower(*q++); + } else { + *p++ = *q++; + } + } + } else { + while (*q) *p++ = *q++; + } + *p = '\000'; + + sprintf(cmd, "ln -s %s %s", fname, dname); + /*printf("$ %s\n", cmd);*/ + ret = emt_system(cmd); + if (ret != 0) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "emt_system: %s\n", errbuf); + exit(1); + } + emt_misc(EMT_MISC_DISK_CHANGE); + exit(0); +} diff --git a/mount.cmd b/mount.cmd Binary files differnew file mode 100644 index 0000000..0454ea1 --- /dev/null +++ b/mount.cmd diff --git a/mount6.cmd b/mount6.cmd Binary files differnew file mode 100644 index 0000000..f7be04c --- /dev/null +++ b/mount6.cmd @@ -0,0 +1,43 @@ +/* pwd.ccc -- Misosys C program to print the Unix working directory of xtrs */ +/* Copyright (c) 1998, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 16:59:26 PST 1998 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +usage() +{ + fprintf(stderr, "usage: pwd\n"); + exit(1); +} + +#define ERRBUFSIZE 256 +#define DIRBUFSIZE 512 + +int main(argc, argv) + int argc; + char **argv; +{ + int i; + char *ret; + char errbuf[ERRBUFSIZE]; + char dirbuf[DIRBUFSIZE]; + if (argc != 1) { + usage(); + } + ret = emt_getcwd(dirbuf, DIRBUFSIZE); + if (ret == NULL) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "pwd: %s\n", errbuf); + exit(1); + } + printf("%s\n", dirbuf); + exit(0); +} Binary files differdiff --git a/pwd6.cmd b/pwd6.cmd Binary files differnew file mode 100644 index 0000000..49bb7a9 --- /dev/null +++ b/pwd6.cmd @@ -0,0 +1,47 @@ +/* Matthew Reed's hard drive format. Thanks to Matthew for providing + documentation. The comments below are copied from his mail + messages, with some additions. */ + +typedef struct { + Uchar id1; /* 0: Identifier #1: 56H */ + Uchar id2; /* 1: Identifier #2: CBH */ + Uchar ver; /* 2: Version of format: 10H = version 1.0 */ + Uchar cksum; /* 3: Simple checksum: + To calculate, add together bytes 0 to 31 of header + (excepting byte 3), then XOR result with 4CH */ + Uchar blks; /* 4: Number of 256 byte blocks in header: should be 1 */ + Uchar mb4; /* 5: Not used, but HDFORMAT sets to 4 */ + Uchar media; /* 6: Media type: 0 for hard disk */ + Uchar flag1; /* 7: Flags #1: + bit 7: Write protected: 0 for no, 1 for yes + [xtrshard/dct ignores for now] + bit 6: Must be 0 + bit 5 - 0: reserved */ + Uchar flag2; /* 8: Flags #2: reserved */ + Uchar flag3; /* 9: Flags #3: reserved */ + Uchar crtr; /* 10: Created by: + 14H = HDFORMAT + 42H = xtrs mkdisk + 80H = Cervasio xtrshard port to Vavasour M4 emulator */ + Uchar dfmt; /* 11: Disk format: 0 = LDOS/LS-DOS */ + Uchar mm; /* 12: Creation month: mm */ + Uchar dd; /* 13: Creation day: dd */ + Uchar yy; /* 14: Creation year: yy (offset from 1900) */ + Uchar res1[12]; /* 15 - 26: reserved */ + Uchar dparm; /* 27: Disk parameters: (unused with hard drives) + bit 7: Density: 0 = double, 1 = single + bit 6: Sides: 0 = one side, 1 = 2 sides + bit 5: First sector: 0 if sector 0, 1 if sector 1 + bit 4: DAM convention: 0 if normal (LDOS), + 1 if reversed (TRSDOS 1.3) + bit 3 - 0: reserved */ + Uchar cyl; /* 28: Number of cylinders per disk */ + Uchar sec; /* 29: Number of sectors per track (floppy); cyl (hard) */ + Uchar gran; /* 30: Number of granules per track (floppy); cyl (hard)*/ + Uchar dcyl; /* 31: Directory cylinder [mkdisk sets to 1; xtrs ignores]*/ + char label[32]; /* 32: Volume label: 31 bytes terminated by 0 */ + char filename[8];/* 64 - 71: 8 characters of filename (without extension) + [Cervasio addition. xtrs actually doesn't limit this + to 8 chars or strip the extension] */ + Uchar res2[184]; /* 72 - 255: reserved */ +} ReedHardHeader; diff --git a/settime.ccc b/settime.ccc new file mode 100644 index 0000000..9fd0d69 --- /dev/null +++ b/settime.ccc @@ -0,0 +1,30 @@ +/* settime.ccc -- Misosys C program to set time using xtrs emulator traps */ +/* Copyright (c) 1997, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Sat Sep 20 13:41:00 PDT 1997 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +main() +{ + time_t now; + struct tm *nowtm; + char cmd[81]; + + now = emt_time(EMT_TIME_LOCAL); + nowtm = localtime(&now); + sprintf(cmd, "date %02d/%02d/%02d", + nowtm->tm_mon + 1, nowtm->tm_mday, nowtm->tm_year % 100); + system(cmd); + sprintf(cmd, "time %02d:%02d:%02d", + nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec); + system(cmd); + system("time"); +} diff --git a/settime.cmd b/settime.cmd Binary files differnew file mode 100644 index 0000000..6792638 --- /dev/null +++ b/settime.cmd diff --git a/settime.lst b/settime.lst new file mode 100644 index 0000000..f810bb8 --- /dev/null +++ b/settime.lst @@ -0,0 +1,224 @@ + 1: ;; settime.z + 2: ;; + 3: ;; Read date and time from xtrs 1.9 emulator trap and set + 4: ;; TRS-80 system date and time. + 5: ;; + 6: ;; Copyright (c) 1998 Ulrich Mueller + 7: ;; + 8: ;; This software may be copied, modified, and used for any + 9: ;; purpose without fee, provided that (1) the above copyright + 10: ;; notice is retained, and (2) modified versions are clearly + 11: ;; marked as having been modified, with the modifier's name and + 12: ;; the date included. + 13: ;; + 14: ;; Last modified on Fri May 19 00:38:41 PDT 2000 by mann + 15: ;; modified on Sun Feb 22 21:27:13 CET 1998 by ulm + 16: + 17: ;; Model I/III addresses + 18: 402D @exit equ 402dh + 19: 4030 @abort equ 4030h + 20: + 21: ;; Model 1 and 3 store the last 2 digits of the year in the year byte + 22: 4041 time1$ equ 4041h ; seconds/minutes/hours + 23: 4044 date1$ equ 4044h ; year/day/month + 24: 4217 time3$ equ 4217h + 25: 421A date3$ equ 421ah + 26: + 27: ;; Model 4 SVCs + 28: 0028 @svc equ 40 ; rst address for SVCs + 29: ;@svc equ 5 ; older zmac requires 8080-style "rst 5" + 30: 0016 @exit6 equ 22 + 31: 0015 @abort6 equ 21 + 32: + 33: ;; Model 4 stores the offset from 1900 in the year byte + 34: 002D time4$ equ 002dh + 35: 0033 date4$ equ 0033h + 36: + 37: ;; Emulator trap instructions + 38: 36ED emt_time equ 36edh + 39: + 40: 5200 org 5200h + 41: 5200 settime: + 42: 5200 CDB452 call initj ; init OS-dependent tables + 43: 5203 01FFFF ld bc, 0ffffh + 44: 5206 3E01 ld a, 1 ; get local time from Unix + 45: 5208 ED36 defw emt_time ; BCDE: seconds since 1970 + 46: 520A 78 ld a, b + 47: 520B A1 and c + 48: 520C 3C inc a + 49: 520D CA9052 jp z, abort + 50: 5210 F3 di + 51: 5211 2A9352 ld hl, (time) + 52: 5214 3E3C ld a, 60 + 53: 5216 CD7452 call divide + 54: 5219 77 ld (hl), a ; seconds + 55: 521A 23 inc hl + 56: 521B 3E3C ld a, 60 + 57: 521D CD7452 call divide + 58: 5220 77 ld (hl), a ; minutes + 59: 5221 23 inc hl + 60: 5222 3E18 ld a, 24 + 61: 5224 CD7452 call divide + 62: 5227 77 ld (hl), a ; hours + 63: 5228 EB ex de, hl ; HL: days since 1970 + 64: 5229 014513 ld bc, 19*256+70-1 + 65: 522C 116D01 ld de, 365 + 66: 522F 0C year1: inc c ; count years in C + 67: 5230 79 ld a, c + 68: 5231 D664 sub 100 + 69: 5233 3803 jr c, year2 + 70: 5235 4F ld c, a + 71: 5236 04 inc b ; centuries + 72: 5237 78 ld a, b + 73: 5238 E603 year2: and 3 + 74: 523A D601 sub 1 + 75: 523C ED52 sbc hl, de + 76: 523E 30EF jr nc, year1 + 77: 5240 07 rlca + 78: 5241 ED5A adc hl, de ; HL: days since 1 january + 79: 5243 E5 push hl + 80: 5244 2A9552 ld hl, (date) + 81: 5247 CD9752 call putyr ; year to (hl) + 82: 524A 23 inc hl + 83: 524B E3 ex (sp), hl + 84: 524C E601 and 1 ; A=1 for leap year, 0 else + 85: 524E C61C add a, 28 + 86: 5250 4F ld c, a + 87: 5251 0600 ld b, 0 + 88: 5253 50 ld d, b + 89: 5254 04 month1: inc b ; count months in B + 90: 5255 78 ld a, b + 91: 5256 59 ld e, c + 92: 5257 FE02 cp 2 + 93: 5259 2808 jr z, month2 ; february + 94: 525B 1E1F ld e, 31 + 95: 525D E609 and 9 + 96: 525F E26352 jp po, month2 ; 31 days + 97: 5262 1D dec e ; 30 days + 98: 5263 ED52 month2: sbc hl, de ; subtract length of month + 99: 5265 30ED jr nc, month1 + 100: 5267 ED5A adc hl, de + 101: 5269 4D ld c, l + 102: 526A E1 pop hl + 103: 526B 71 ld (hl), c ; day + 104: 526C 23 inc hl + 105: 526D 70 ld (hl), b ; month + 106: 526E FB ei + 107: 526F 210000 ld hl,0 ; needed on Model 4 --mann + 108: 5272 1819 jr exit + 109: + 110: ;; divide BCDE / A + 111: ;; returns quotient in BCDE, remainder in A + 112: 5274 E5 divide: push hl + 113: 5275 ED44 neg + 114: 5277 67 ld h, a + 115: 5278 97 sub a + 116: 5279 2E21 ld l, 33 + 117: 527B 17 div1: rla + 118: 527C 84 add a, h + 119: 527D 3801 jr c, div2 + 120: 527F 94 sub h + 121: 5280 CB13 div2: rl e + 122: 5282 CB12 rl d + 123: 5284 CB11 rl c + 124: 5286 CB10 rl b + 125: 5288 2D dec l + 126: 5289 20F0 jr nz, div1 + 127: 528B E1 pop hl + 128: 528C C9 ret + 129: + 130: ;; Jump tables for OS independence + 131: ;; Model 1 + 132: 528D startj: + 133: 528D C32D40 exit: jp @exit + 134: 5290 C33040 abort: jp @abort + 135: 5293 4140 time: defw time1$ + 136: 5295 4440 date: defw date1$ + 137: 5297 C3D052 putyr: jp putyr1 + 138: 529A endj: + 139: + 140: ;; Model 3 + 141: 529A startj3: + 142: 529A C32D40 jp @exit + 143: 529D C33040 jp @abort + 144: 52A0 1742 defw time3$ + 145: 52A2 1A42 defw date3$ + 146: 52A4 C3D052 jp putyr3 + 147: + 148: ;; Model 4 + 149: 52A7 startj4: + 150: 52A7 3E16 ld a, @exit6 + 151: 52A9 EF rst @svc + 152: 52AA 3E15 ld a, @abort6 + 153: 52AC EF rst @svc + 154: 52AD 2D00 defw time4$ + 155: 52AF 3300 defw date4$ + 156: 52B1 C3D252 jp putyr4 + 157: + 158: ;; Initialize tables + 159: ;; Changed to work even on a Model III (or Model 4 in III mode) + 160: ;; using MODELA/III as its ROM. Previous version didn't. --mann + 161: ;; + 162: 52B4 21A752 initj: ld hl, startj4 ; model 4? + 163: 52B7 3A0A00 ld a, (000ah) + 164: 52BA FE40 cp 40h + 165: 52BC 2009 jr nz, movej ; go if so + 166: 52BE 219A52 ld hl, startj3 ; model 3? + 167: 52C1 3A2501 ld a, (0125h) + 168: 52C4 FE49 cp 'I' + 169: 52C6 C0 ret nz ; return if not + 170: 52C7 118D52 movej: ld de, startj + 171: 52CA 010D00 ld bc, endj - startj + 172: 52CD EDB0 ldir + 173: 52CF C9 ret + 174: + 175: ;; Create year byte. + 176: ;; On entry, c has 2-digit year, b has century. + 177: ;; On exit, (hl) has year byte. + 178: + 179: ;; Model I/III, put 2-digit year + 180: 52D0 putyr1: + 181: 52D0 71 putyr3: ld (hl), c + 182: 52D1 C9 ret + 183: + 184: ;; Model 4, put offset from 1900 (laboriously recomputed, sigh) + 185: 52D2 F5 putyr4: push af + 186: 52D3 C5 push bc + 187: 52D4 78 ld a, b + 188: 52D5 D613 sub 19 + 189: 52D7 47 ld b, a + 190: 52D8 79 ld a, c + 191: 52D9 2804 jr z, py41 + 192: 52DB C664 py40: add a, 100 + 193: 52DD 10FC djnz py40 + 194: 52DF 77 py41: ld (hl), a + 195: 52E0 C1 pop bc + 196: 52E1 F1 pop af + 197: 52E2 C9 ret + 198: + 199: 5200 end settime + + + +Statistics: + + 36 symbols + 227 bytes + + + +Symbol Table: + +@abort =4030 emt_time =36ed settime 5200 +@abort6 = 15 endj 529a startj 528d +@exit =402d exit 528d startj3 529a +@exit6 = 16 initj 52b4 startj4 52a7 +@svc = 28 month1 5254 time 5293 +abort 5290 month2 5263 time1 =4041 +date 5295 movej 52c7 time3 =4217 +date1 =4044 putyr 5297 time4 = 2d +date3 =421a putyr1 52d0 year1 522f +date4 = 33 putyr3 52d0 year2 5238 +div1 527b putyr4 52d2 +div2 5280 py40 52db +divide 5274 py41 52df diff --git a/settime.z80 b/settime.z80 new file mode 100644 index 0000000..ea0165f --- /dev/null +++ b/settime.z80 @@ -0,0 +1,201 @@ +;; settime.z +;; +;; Read date and time from xtrs 1.9 emulator trap and set +;; TRS-80 system date and time. +;; +;; Copyright (c) 1998 Ulrich Mueller +;; +;; This software may be copied, modified, and used for any +;; purpose without fee, provided that (1) the above copyright +;; notice is retained, and (2) modified versions are clearly +;; marked as having been modified, with the modifier's name and +;; the date included. +;; +;; Last modified on Fri May 19 00:38:41 PDT 2000 by mann +;; modified on Sun Feb 22 21:27:13 CET 1998 by ulm + +;; Model I/III addresses +@exit equ 402dh +@abort equ 4030h + +;; Model 1 and 3 store the last 2 digits of the year in the year byte +time1$ equ 4041h ; seconds/minutes/hours +date1$ equ 4044h ; year/day/month +time3$ equ 4217h +date3$ equ 421ah + +;; Model 4 SVCs +@svc equ 40 ; rst address for SVCs +;@svc equ 5 ; older zmac requires 8080-style "rst 5" +@exit6 equ 22 +@abort6 equ 21 + +;; Model 4 stores the offset from 1900 in the year byte +time4$ equ 002dh +date4$ equ 0033h + +;; Emulator trap instructions +emt_time equ 36edh + + org 5200h +settime: + call initj ; init OS-dependent tables + ld bc, 0ffffh + ld a, 1 ; get local time from Unix + defw emt_time ; BCDE: seconds since 1970 + ld a, b + and c + inc a + jp z, abort + di + ld hl, (time) + ld a, 60 + call divide + ld (hl), a ; seconds + inc hl + ld a, 60 + call divide + ld (hl), a ; minutes + inc hl + ld a, 24 + call divide + ld (hl), a ; hours + ex de, hl ; HL: days since 1970 + ld bc, 19*256+70-1 + ld de, 365 +year1: inc c ; count years in C + ld a, c + sub 100 + jr c, year2 + ld c, a + inc b ; centuries + ld a, b +year2: and 3 + sub 1 + sbc hl, de + jr nc, year1 + rlca + adc hl, de ; HL: days since 1 january + push hl + ld hl, (date) + call putyr ; year to (hl) + inc hl + ex (sp), hl + and 1 ; A=1 for leap year, 0 else + add a, 28 + ld c, a + ld b, 0 + ld d, b +month1: inc b ; count months in B + ld a, b + ld e, c + cp 2 + jr z, month2 ; february + ld e, 31 + and 9 + jp po, month2 ; 31 days + dec e ; 30 days +month2: sbc hl, de ; subtract length of month + jr nc, month1 + adc hl, de + ld c, l + pop hl + ld (hl), c ; day + inc hl + ld (hl), b ; month + ei + ld hl,0 ; needed on Model 4 --mann + jr exit + +;; divide BCDE / A +;; returns quotient in BCDE, remainder in A +divide: push hl + neg + ld h, a + sub a + ld l, 33 +div1: rla + add a, h + jr c, div2 + sub h +div2: rl e + rl d + rl c + rl b + dec l + jr nz, div1 + pop hl + ret + +;; Jump tables for OS independence +;; Model 1 +startj: +exit: jp @exit +abort: jp @abort +time: defw time1$ +date: defw date1$ +putyr: jp putyr1 +endj: + +;; Model 3 +startj3: + jp @exit + jp @abort + defw time3$ + defw date3$ + jp putyr3 + +;; Model 4 +startj4: + ld a, @exit6 + rst @svc + ld a, @abort6 + rst @svc + defw time4$ + defw date4$ + jp putyr4 + +;; Initialize tables +;; Changed to work even on a Model III (or Model 4 in III mode) +;; using MODELA/III as its ROM. Previous version didn't. --mann +;; +initj: ld hl, startj4 ; model 4? + ld a, (000ah) + cp 40h + jr nz, movej ; go if so + ld hl, startj3 ; model 3? + ld a, (0125h) + cp 'I' + ret nz ; return if not +movej: ld de, startj + ld bc, endj - startj + ldir + ret + +;; Create year byte. +;; On entry, c has 2-digit year, b has century. +;; On exit, (hl) has year byte. + +;; Model I/III, put 2-digit year +putyr1: +putyr3: ld (hl), c + ret + +;; Model 4, put offset from 1900 (laboriously recomputed, sigh) +putyr4: push af + push bc + ld a, b + sub 19 + ld b, a + ld a, c + jr z, py41 +py40: add a, 100 + djnz py40 +py41: ld (hl), a + pop bc + pop af + ret + + end settime + + @@ -0,0 +1,173 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Tue May 1 20:29:21 PDT 2001 by mann +*/ + +/* + * trs.h + */ +#ifndef _TRS_H +#define _TRS_H + +#include "z80.h" + +#define NORMAL 0 +#define EXPANDED 1 +#define INVERSE 2 +#define ALTERNATE 4 + +extern int trs_model; /* 1, 3, 4, 5(=4p) */ +extern int trs_paused; +extern int trs_autodelay; +void trs_suspend_delay(void); +void trs_restore_delay(void); +extern int trs_continuous; /* 1= run continuously, + 0= enter debugger after instruction, + -1= suppress interrupt and enter debugger */ +extern int trs_disk_debug_flags; +extern int trs_emtsafe; + +extern int trs_parse_command_line(int argc, char **argv, int *debug); + +extern void trs_screen_init(void); +extern void trs_screen_write_char(int position, int char_index); +extern void trs_screen_expanded(int flag); +extern void trs_screen_alternate(int flag); +extern void trs_screen_80x24(int flag); +extern void trs_screen_inverse(int flag); +extern void trs_screen_scroll(void); +extern void trs_screen_refresh(void); +extern void trs_screen_batch(); +extern void trs_screen_unbatch(); + +extern void trs_reset(int hard); +extern void trs_exit(void); + +extern void trs_kb_reset(void); +extern void trs_kb_bracket(int shifted); +extern int trs_kb_mem_read(int address); +extern int trs_next_key(int wait); +extern void trs_kb_heartbeat(void); +extern void trs_xlate_keysym(int keysym); +extern void queue_key(int key); +extern int dequeue_key(void); +extern void clear_key_queue(void); +extern void trs_end_kbwait(void); +extern int stretch_amount; + +extern void trs_get_event(int wait); +extern volatile int x_poll_count; +extern void trs_x_flush(void); +extern volatile int x_flush_needed; + +extern void trs_printer_write(int value); +extern int trs_printer_read(void); + +extern void trs_cassette_motor(int value); +extern void trs_cassette_out(int value); +extern int trs_cassette_in(void); +extern void trs_sound_out(int value); +extern void trs_sound_init(int ioport, int vol); + +extern int trs_joystick_in(void); + +extern int trs_rom_size; +extern int trs_rom1_size; +extern int trs_rom3_size; +extern int trs_rom4p_size; +extern unsigned char trs_rom1[]; +extern unsigned char trs_rom3[]; +extern unsigned char trs_rom4p[]; + +extern void trs_load_compiled_rom(int size, unsigned char rom[]); +extern void trs_load_rom(char *filename); + +extern unsigned char trs_interrupt_latch_read(void); +extern unsigned char trs_nmi_latch_read(void); +extern void trs_interrupt_mask_write(unsigned char); +extern void trs_nmi_mask_write(unsigned char); +extern void trs_reset_button_interrupt(int state); +extern void trs_disk_intrq_interrupt(int state); +extern void trs_disk_drq_interrupt(int state); +extern void trs_disk_motoroff_interrupt(int state); +extern void trs_uart_err_interrupt(int state); +extern void trs_uart_rcv_interrupt(int state); +extern void trs_uart_snd_interrupt(int state); +extern void trs_timer_interrupt(int state); +extern void trs_timer_init(void); +extern void trs_timer_off(void); +extern void trs_timer_on(void); +extern void trs_timer_speed(int flag); +extern void trs_cassette_rise_interrupt(int dummy); +extern void trs_cassette_fall_interrupt(int dummy); +extern void trs_cassette_clear_interrupts(void); +extern int trs_cassette_interrupts_enabled(void); +extern void trs_cassette_update(int dummy); +extern int cassette_default_sample_rate; +extern void trs_orch90_out(int chan, int value); +extern void trs_cassette_reset(void); + +extern void trs_disk_change(int drive); +extern void trs_disk_change_all(void); +extern void trs_disk_debug(void); +extern int trs_disk_motoroff(void); + +extern void mem_video_page(int which); +extern void mem_bank(int which); +extern void mem_map(int which); +extern void mem_romin(int state); + +extern void trs_debug(void); + +typedef void (*trs_event_func)(int arg); +void trs_schedule_event(trs_event_func f, int arg, int tstates); +void trs_schedule_event_us(trs_event_func f, int arg, int us); +void trs_do_event(void); +void trs_cancel_event(void); +trs_event_func trs_event_scheduled(void); + +void grafyx_write_x(int value); +void grafyx_write_y(int value); +void grafyx_write_data(int value); +int grafyx_read_data(void); +void grafyx_write_mode(int value); +void grafyx_write_xoffset(int value); +void grafyx_write_yoffset(int value); +void grafyx_write_overlay(int value); +void grafyx_set_microlabs(int on_off); +int grafyx_get_microlabs(void); +void grafyx_m3_reset(); +int grafyx_m3_active(); /* true if currently intercepting video i/o */ +void grafyx_m3_write_mode(int value); +unsigned char grafyx_m3_read_byte(int position); +int grafyx_m3_write_byte(int position, int value); +void hrg_onoff(int enable); +void hrg_write_addr(int addr, int mask); +void hrg_write_data(int data); +int hrg_read_data(void); + +void trs_get_mouse_pos(int *x, int *y, unsigned int *buttons); +void trs_set_mouse_pos(int x, int y); +void trs_get_mouse_max(int *x, int *y, unsigned int *sens); +void trs_set_mouse_max(int x, int y, unsigned int sens); +int trs_get_mouse_type(void); + +void sb_set_volume(int vol); +int sb_get_volume(void); + +#endif /*_TRS_H*/ diff --git a/trs_cassette.c b/trs_cassette.c new file mode 100644 index 0000000..b2efb07 --- /dev/null +++ b/trs_cassette.c @@ -0,0 +1,1479 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Sat May 6 14:53:21 PDT 2000 by mann +*/ + +/* + * This module implements cassette I/O, game sound, and Orchestra + * 85/90 sound. "Game sound" is defined as output to the cassette + * port when the cassette motor is off, or output to the Model III/4 + * sound option card (a 1-bit DAC). + * + * Compile time options: + * + * HAVE_OSS You have the Open Sound System. If this is set, cassettes + * can be read/written directly from/to /dev/dsp. This should work on + * Linux and other systems with OSS. + * + * OSS_SOUND Game sound is emulated using the Open Sound System. + * HAVE_OSS must also be set. + * + * SB_SOUND Game sound is emulated by read/writing SoundBlaster + * hardware registers directly. This probably works only on Linux, + * and of course only with true SoundBlaster-compatible hardware. + * Requires root privileges and the -sb command line option. If you + * define both SB_SOUND and OSS_SOUND, then SB_SOUND will be used if + * the -sb command line option is given, OSS_SOUND otherwise. + * OSS_SOUND seems to work much better than SB_SOUND now, so + * SB_SOUND is off by default and -sb has been removed from the + * man page. In case you turn it on, here is how it works: + * + * -sb portbase,vol + * Enable sound support using direct I/O to a SoundBlaster with I/O + * port base at portbase, playing sounds at vol percent of + * maximum volume. A typical setting would be -sb 0x220,30. + * Fabio Ferrari contributed the SB_SOUND implementation. + */ + +#if __linux +#define HAVE_OSS 1 +#define OSS_SOUND 1 +#endif + +/*#define CASSDEBUG 1*/ +/*#define CASSDEBUG2 1*/ +/*#define CASSDEBUG3 1*/ +/*#define CASSDEBUG4 1*/ + +#include "trs.h" +#include "z80.h" +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +#if SB_SOUND +/*#include <sys/io.h> delete this line if it gives you a compile error */ +#include <asm/io.h> +#endif + +#if HAVE_OSS +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sys/soundcard.h> +#endif + +#define CLOSE 0 +#define READ 1 +#define WRITE 2 +#define SOUND 3 /* used for OSS_SOUND only */ +#define ORCH90 4 /* used for OSS_SOUND only */ +#define FAILED 5 + +#define CAS_FORMAT 1 /* recovered bit/byte stream */ +#define CPT_FORMAT 2 /* cassette pulse train w/ exact timing */ +#define WAV_FORMAT 3 /* wave file */ +#define DIRECT_FORMAT 4 /* direct to sound card */ +#define DEBUG_FORMAT 5 /* like cpt but in ASCII */ +static char *format_name[] = { + NULL, "cas", "cpt", "wav", "direct", "debug" }; +#define DEFAULT_SAMPLE_RATE 44100 /* samples/sec to use for .wav files */ +#define NOISE_FLOOR 64 + +#define CONTROL_FILENAME ".cassette.ctl" +#define DEFAULT_FILENAME "cassette.cas" +#define DSP_FILENAME "/dev/dsp" /* for sound output */ +#define DEFAULT_FORMAT CAS_FORMAT + +#define FLUSH -500 /* special fake signal value used when turning off motor */ + +static char cassette_filename[256]; +static int cassette_position; +static int cassette_format; +static int cassette_state = CLOSE; +static int cassette_motor = 0; +static FILE *cassette_file; +static float cassette_avg; +static float cassette_env; +static int cassette_noisefloor; +static int cassette_sample_rate; +int cassette_default_sample_rate = DEFAULT_SAMPLE_RATE; +static int cassette_stereo = 0; +#if HAVE_OSS +static int cassette_afmt = AFMT_U8; +#endif + +/* For bit-level emulation */ +static tstate_t cassette_transition; +static tstate_t last_sound; +static tstate_t cassette_firstoutread; +static int cassette_value, cassette_next, cassette_flipflop; +static int cassette_lastnonzero; +static int cassette_transitionsout; +static unsigned long cassette_delta; +static float cassette_roundoff_error; + +/* For bit/byte conversion (.cas file i/o) */ +static int cassette_byte; +static int cassette_bitnumber; +static int cassette_pulsestate; +#define SPEED_500 0 +#define SPEED_1500 1 +#define SPEED_250 2 +int cassette_speed = SPEED_500; + +/* Pulse shapes for conversion from .cas on input */ +#define CAS_MAXSTATES 8 +struct { + int delta_us; + int next; +} pulse_shape[3][2][CAS_MAXSTATES] = { + {{ + /* Low-speed zero: clock 1 data 0 */ + { 0, 1 }, + { 128, 2 }, + { 128, 0 }, + { 1871, 0 }, /* normally 1757; 1871 after 8th bit */ + { -1, -1 } + }, { + /* Low-speed one: clock 1 data 1 */ + { 0, 1 }, + { 128, 2 }, + { 128, 0 }, + { 748, 1 }, + { 128, 2 }, + { 128, 0 }, + { 860, 0 }, /* normally 748; 860 after 8th bit; 1894 after a5 sync */ + { -1, -1 } + }}, {{ + /* High-speed zero: wide pulse */ + { 0, 1 }, + { 376, 2 }, + { 376, 1 }, + { -1, -1 } + }, { + /* High-speed one: narrow pulse */ + { 0, 1 }, + { 188, 2 }, + { 188, 1 }, + { -1, -1 } + }}, {{ + /* Level I zero: clock 1 data 0 */ + { 0, 1 }, + { 125, 2 }, + { 125, 0 }, + { 3568, 0 }, + { -1, -1 } + }, { + /* Level I one: clock 1 data 1 */ + { 0, 1 }, + { 128, 2 }, + { 128, 0 }, + { 1673, 1 }, + { 128, 2 }, + { 128, 0 }, + { 1673, 0 }, + { -1, -1 } + }} +}; + +/* States and thresholds for conversion to .cas on output */ +#define ST_INITIAL 0 +#define ST_500GOTCLK 1 +#define ST_500GOTDAT 2 +#define ST_1500 3 +#define ST_250 4 +#define ST_250GOTCLK 5 +#define ST_250GOTDAT 6 +#define ST_500THRESH 1250.0 /* us threshold between 0 and 1 */ +#define ST_1500THRESH 282.0 /* us threshold between 1 and 0 */ +#define ST_250THRESH 2500.0 /* us threshold between 0 and 1 */ + +#define DETECT_250 1200.0 /* detect level 1 input routine */ + +/* Values for conversion to .wav on output */ +/* Values in comments are from Model I technical manual. Model III/4 are + close though not quite the same, as one resistor in the network was + changed; we ignore the difference. Actually, we ignore more than + that; we convert the values as if 0 were really halfway between + high and low. */ +Uchar value_to_sample[] = { 127, /* 0.46 V */ + 254, /* 0.85 V */ + 0, /* 0.00 V */ + 127, /* unused, but close to 0.46 V */ +}; + +/* .wav file definitions */ +#define WAVE_FORMAT_PCM (0x0001) +#define WAVE_FORMAT_MONO 1 +#define WAVE_FORMAT_STEREO 2 +#define WAVE_FORMAT_8BIT 8 +#define WAVE_FORMAT_16BIT 16 +#define WAVE_RIFFSIZE_OFFSET 0x04 +#define WAVE_RIFF_OFFSET 0x08 +#define WAVE_DATAID_OFFSET 0x24 +#define WAVE_DATASIZE_OFFSET 0x28 +#define WAVE_DATA_OFFSET 0x2c +static long wave_dataid_offset = WAVE_DATAID_OFFSET; +static long wave_datasize_offset = WAVE_DATASIZE_OFFSET; +static long wave_data_offset = WAVE_DATA_OFFSET; + +/* Orchestra 80/85/90 stuff */ +static int orch90_left = 128, orch90_right = 128; + +#if SB_SOUND +/* ioport of the SoundBlaster command register. 0 means none */ +static unsigned char sb_cassette_volume[4]; +static unsigned char sb_sound_volume[2]; +#endif /*SB_SOUND*/ +static unsigned int sb_address=0; +static int sb_volume = 0; + +/* Put a 2-byte quantity to a file in little-endian order */ +/* Return -1 on error, 0 otherwise */ +static int +put_twobyte(Ushort n, FILE* f) +{ + int c; + struct twobyte *p = (struct twobyte *) &n; + c = putc(p->low, f); + if (c == -1) return c; + c = putc(p->high, f); + if (c == -1) return c; + return 0; +} + +/* Put a 4-byte quantity to a file in little-endian order */ +/* Return -1 on error, 0 otherwise */ +static int +put_fourbyte(Uint n, FILE* f) +{ + int c; + struct fourbyte *p = (struct fourbyte *) &n; + c = putc(p->byte0, f); + if (c == -1) return c; + c = putc(p->byte1, f); + if (c == -1) return c; + c = putc(p->byte2, f); + if (c == -1) return c; + c = putc(p->byte3, f); + if (c == -1) return c; + return 0; +} + +/* Get a 2-byte quantity from a file in little-endian order */ +/* Return -1 on error, 0 otherwise */ +static int +get_twobyte(Ushort *pp, FILE* f) +{ + int c; + struct twobyte *p = (struct twobyte *) pp; + c = getc(f); + if (c == -1) return c; + p->low = c; + c = getc(f); + if (c == -1) return c; + p->high = c; + return 0; +} + +/* Get a 4-byte quantity from a file in little-endian order */ +/* Return -1 on error, 0 otherwise */ +static int +get_fourbyte(Uint *pp, FILE* f) +{ + int c; + struct fourbyte *p = (struct fourbyte *) pp; + c = getc(f); + if (c == -1) return c; + p->byte0 = c; + c = getc(f); + if (c == -1) return c; + p->byte1 = c; + c = getc(f); + if (c == -1) return c; + p->byte2 = c; + c = getc(f); + if (c == -1) return c; + p->byte3 = c; + return 0; +} + +/* Output an 8-byte unsigned sample, if necessary converting to a + * different sample format. */ +static void +put_sample(Uchar sample, int convert, FILE* f) +{ + if (convert) { +#if HAVE_OSS + switch (cassette_afmt) { + case AFMT_U8: + putc(sample, f); + break; + case AFMT_S16_LE: + put_twobyte((sample << 8) - 0x8000, f); + break; + default: + error("sample format 0x%x not supported", cassette_afmt); + break; + } + return; +#endif + } + putc(sample, f); +} + +/* Get an 8-byte unsigned sample, if necessary converting from a + * different sample format and/or reducing stereo to mono. */ +static int +get_sample(int convert, FILE* f) +{ +#if HAVE_OSS + if (convert) { + int ret = 0; + short s = 0; + switch (cassette_afmt) { + case AFMT_U8: + ret = getc(f); + break; + case AFMT_S16_LE: + ret = get_twobyte((Ushort *)&s, cassette_file); + if (ret == EOF) break; + ret = ((s + 0x8000) >> 8) & 0xff; + break; + default: + error("sample format 0x%x not supported", cassette_afmt); + break; + } + return ret; + } +#endif + return getc(f); +} + +/* Write a new .wav file header to a file. Return -1 on error. */ +static int +create_wav_header(FILE *f) +{ + Uint field; + /* Chunk sizes don't count the 4-byte chunk type name nor the 4-byte + size field itself. The RIFF chunk is the whole file, so its size + is the actual length of the file minus WAVE_RIFF_OFFSET (=8). + The data chunk is the actual sample data, so its size is the size + of the file minus wave_data_offset. */ + + wave_dataid_offset = WAVE_DATAID_OFFSET; + wave_datasize_offset = WAVE_DATASIZE_OFFSET; + wave_data_offset = WAVE_DATA_OFFSET; + if (cassette_position < wave_data_offset) { + cassette_position = wave_data_offset; + } + + if (fputs("RIFF", f) < 0) return -1; + if (put_fourbyte(0, f) < 0) return -1; /* RIFF chunk size */ + if (fputs("WAVEfmt ", f) < 0) return -1; + if (put_fourbyte(16, f) < 0) return -1; /* fmt chunk size */ + if (put_twobyte(WAVE_FORMAT_PCM, f) < 0) return -1; + if (put_twobyte(WAVE_FORMAT_MONO, f) < 0) return -1; + if (put_fourbyte(cassette_sample_rate, f) < 0) return -1; + field = (WAVE_FORMAT_MONO * cassette_sample_rate * WAVE_FORMAT_8BIT/8); + if (put_fourbyte(field, f) < 0) return -1; + field = (WAVE_FORMAT_MONO * WAVE_FORMAT_8BIT/8); + if (put_twobyte(field, f) < 0) return -1; + if (put_twobyte(WAVE_FORMAT_8BIT, f) < 0) return -1; /* end of fmt chunk */ + if (fputs("data", f) < 0) return -1; + if (put_fourbyte(0, f) < 0) return -1; /* size of data chunk payload */ + /* payload starts here */ + return 0; +} + +/* Error message generator */ +static int +check_chunk_id(char *expected, FILE* f) +{ + char c4[5]; + c4[4] = '\0'; + if (fread(c4, 4, 1, f) != 1) return -1; + if (strcmp(c4, expected) != 0) { + error("unusable wav file: expected chunk id '%s', got '%s'", expected, c4); + return -1; + } + return 0; +} + +/* Parse a .wav file's RIFF header. We don't understand much about + the RIFF format, so we might fail on valid .WAV files. For now, + that's just tough. Try running the file through sox to convert it + to something more vanilla. */ +static int +parse_wav_header(FILE *f) +{ + Uint n4; + Uint fmt_size; + Ushort n2, expect2; + + if (check_chunk_id("RIFF", f) < 0) return -1; + if (get_fourbyte(&n4, f) < 0) return -1; /* ignore this field */ + if (check_chunk_id("WAVE", f) < 0) return -1; + if (check_chunk_id("fmt ", f) < 0) return -1; + if (get_fourbyte(&fmt_size, f) < 0) return -1; + if (get_twobyte(&n2, f) < 0) return -1; + if (n2 != WAVE_FORMAT_PCM) { + error("unusable wav file: must be pcm"); + return -1; + } + if (get_twobyte(&n2, f) < 0) return -1; + if (n2 != WAVE_FORMAT_MONO) { + error("unusable wav file: must be mono"); + return -1; + } + if (get_fourbyte(&n4, f) < 0) return -1; + cassette_sample_rate = n4; + if (get_fourbyte(&n4, f) < 0) return -1; /* ignore this field */ + expect2 = WAVE_FORMAT_MONO * WAVE_FORMAT_8BIT/8; + if (get_twobyte(&n2, f) < 0) return -1; + if (n2 != expect2) { + error("unusable wav file: must be %d bytes/sample", expect2); + return -1; + } + expect2 = WAVE_FORMAT_8BIT; + if (get_twobyte(&n2, f) < 0) return -1; + if (n2 != expect2) { + error("unusable wav file: must be %d bits/sample", expect2); + return -1; + } + fmt_size -= 16; /* size read so far */ + while (fmt_size-- > 0) getc(f); /* ignore additional */ + wave_dataid_offset = ftell(f); + if (check_chunk_id("data", f) < 0) return -1; + wave_datasize_offset = ftell(f); + if (get_fourbyte(&n4, f) < 0) return -1; /* ignore this field */ + wave_data_offset = ftell(f); + if (cassette_position < wave_data_offset) { + cassette_position = wave_data_offset; + } + return 0; +} + +#if !USESOX +static int +set_audio_format(FILE *f, int state) +{ +#if HAVE_OSS + int audio_fd = fileno(f); + int format, stereo, speed, req; + req = format = AFMT_U8; /* unsigned 8-bit */ + if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1) return -1; + if (format != req) { + req = format = AFMT_S16_LE; /* signed 16-bit little-endian */ + if (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format)==-1) return -1; + if (format != req) { + error("requested audio format 0x%x, got 0x%x", req, format); + errno = EINVAL; + return -1; + } + } + cassette_afmt = format; + req = stereo = (state == ORCH90); + if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo)==-1) return -1; + if (req && !stereo) { + error("requested stereo, got mono"); + errno = EINVAL; + return -1; + } + cassette_stereo = stereo; + req = speed = cassette_sample_rate; + if (ioctl(audio_fd, SNDCTL_DSP_SPEED, &speed)==-1) return -1; + if (abs(speed - req) > req/20) { + error("requested sample rate %d Hz, got %d Hz", req, speed); + errno = EINVAL; + return -1; + } +#else + /* Hope for the best. Might work on Suns with /dev/audio (mu-law). */ +#endif + return 0; +} +#endif + +static void get_control() +{ + FILE *f; + + f = fopen(CONTROL_FILENAME, "r"); + cassette_format = DEFAULT_FORMAT; + if ((!f) || + (fscanf(f, "%s %d %d", cassette_filename, + &cassette_position, &cassette_format) < 2)) { + error("can't read %s (%s);\n cassette file will be: %s, format %s", + CONTROL_FILENAME, strerror(errno), + DEFAULT_FILENAME, format_name[DEFAULT_FORMAT]); + strcpy(cassette_filename, DEFAULT_FILENAME); + cassette_position = 0; + } + if (f) { + fclose(f); + } +} + +static void put_control() +{ + FILE *f; + + f = fopen(CONTROL_FILENAME, "w"); + + if (f) { + trs_paused = 1; /* disable speed measurement for this round */ + fprintf(f, "%s %d %d\n", cassette_filename, cassette_position, + cassette_format); + fclose(f); + } +} + +/* Return value: 1 = already that state; 0 = state changed; -1 = failed */ +static int assert_state(int state) +{ + if (cassette_state == state) { + return 1; + } + if (cassette_state == FAILED && state != CLOSE) { + return -1; + } + +#if CASSDEBUG + debug("state %d -> %d\n", cassette_state, state); +#endif + + if (cassette_state == ORCH90) { + trs_orch90_out(0, FLUSH); + } + + if (cassette_state != CLOSE && cassette_state != FAILED) { + if (cassette_format == DIRECT_FORMAT) { +#if USESOX + pclose(cassette_file); +#else + sigset_t set, oldset; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; /* disable speed measurement for this round */ + fclose(cassette_file); + sigprocmask(SIG_SETMASK, &oldset, NULL); +#endif + cassette_position = 0; + } else { + cassette_position = ftell(cassette_file); + if (cassette_format == WAV_FORMAT && cassette_state == WRITE) { + fseek(cassette_file, WAVE_RIFFSIZE_OFFSET, 0); + put_fourbyte(cassette_position - WAVE_RIFF_OFFSET, cassette_file); + fseek(cassette_file, wave_datasize_offset, 0); + put_fourbyte(cassette_position - wave_data_offset, cassette_file); + } + fclose(cassette_file); + } + if (cassette_state != SOUND && cassette_state != ORCH90) { + put_control(); + } +#if HAVE_OSS + cassette_stereo = 0; + cassette_afmt = AFMT_U8; +#endif + } + + switch (state) { + case READ: + get_control(); + if (cassette_format == DIRECT_FORMAT) { +#if USESOX + char command[256]; + cassette_sample_rate = cassette_default_sample_rate; + sprintf(command, + "sox -t ossdsp -r %d -u -b /dev/dsp -t raw -r %d -u -b -", + cassette_sample_rate, cassette_sample_rate); + cassette_file = popen(command, "r"); + if (cassette_file == NULL) { + error("couldn't read from sound card: %s", strerror(errno)); + cassette_state = FAILED; + return -1; + } +#else + cassette_file = fopen(cassette_filename, "r"); + if (cassette_file == NULL) { + error("couldn't read %s: %s", cassette_filename, strerror(errno)); + cassette_state = FAILED; + return -1; + } + /*setbuf(cassette_file, NULL);*/ /* seems no need for this */ + cassette_sample_rate = cassette_default_sample_rate; + if (set_audio_format(cassette_file, state) < 0) { + error("couldn't set audio format on %s: %s", + cassette_filename, strerror(errno)); + cassette_file = NULL; + cassette_state = FAILED; + return -1; + } +#endif + } else { + cassette_file = fopen(cassette_filename, "r"); + if (cassette_format == WAV_FORMAT && + cassette_file != NULL && parse_wav_header(cassette_file) < 0) { + cassette_file = NULL; + } + if (cassette_file == NULL) { + error("couldn't read %s: %s", cassette_filename, strerror(errno)); + cassette_state = FAILED; + return -1; + } + fseek(cassette_file, cassette_position, 0); + } + break; + + case SOUND: + case ORCH90: + case WRITE: + if (state == SOUND || state == ORCH90) { + cassette_format = DIRECT_FORMAT; + strcpy(cassette_filename, DSP_FILENAME); + } else { + get_control(state); + } + if (cassette_format == DIRECT_FORMAT) { +#if USESOX + char command[256]; + cassette_sample_rate = cassette_default_sample_rate; + sprintf(command, "sox -t raw -r %d -u -b -c %d - -t ossdsp /dev/dsp", + cassette_sample_rate, (state == ORCH90) ? 2 : 1); + cassette_file = popen(command, "w"); +#else + cassette_sample_rate = cassette_default_sample_rate; + cassette_file = fopen(cassette_filename, "w"); + if (cassette_file == NULL) { + error("couldn't write %s: %s", cassette_filename, strerror(errno)); + cassette_state = FAILED; + return -1; + } + setbuf(cassette_file, NULL); /* ??hangs on some OSS drivers */ +#if OSS_SOUND && HAVE_OSS + if (state == SOUND || state == ORCH90) { + /*int arg = 0x7fff0008;*/ /* unlimited fragments of size (1 << 8) */ + int arg = 0x00200008; /* 32 fragments of size (1 << 8) */ + if (ioctl(fileno(cassette_file), SNDCTL_DSP_SETFRAGMENT, &arg) < 0) { + error("warning: couldn't set sound fragment size: %s", + strerror(errno)); + } + } +#endif + if (set_audio_format(cassette_file, state) < 0) { + error("couldn't set audio format on %s: %s", + cassette_filename, strerror(errno)); + cassette_file = NULL; + cassette_state = FAILED; + return -1; + } +#endif + } else if (cassette_format == WAV_FORMAT) { + cassette_file = fopen(cassette_filename, "r+"); + if (cassette_file == NULL) { + cassette_sample_rate = cassette_default_sample_rate; + cassette_file = fopen(cassette_filename, "w"); + if (cassette_file && create_wav_header(cassette_file) < 0) { + cassette_file = NULL; + } + } else { + if (parse_wav_header(cassette_file) < 0) { + fclose(cassette_file); + cassette_file = NULL; + } + } + if (cassette_file != NULL) { + fseek(cassette_file, cassette_position, 0); + } + } else { + cassette_file = fopen(cassette_filename, "r+"); + if (cassette_file == NULL) { + cassette_file = fopen(cassette_filename, "w"); + } + if (cassette_file != NULL) { + fseek(cassette_file, cassette_position, 0); + } + } + if (cassette_file == NULL) { + error("couldn't write %s: %s", cassette_filename, strerror(errno)); + cassette_state = FAILED; + return -1; + } + break; + } + + cassette_state = state; + return 0; +} + + +/* Record an output transition. + value is either the new port value or FLUSH. +*/ +static void +transition_out(int value) +{ + Uchar sample; + long nsamples, delta_us; + Ushort code; + float ddelta_us; + sigset_t set, oldset; + + cassette_transitionsout++; + if (value != FLUSH && value == cassette_value) return; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + + ddelta_us = (z80_state.t_count - cassette_transition) / z80_state.clockMHz + - cassette_roundoff_error; + + switch (cassette_format) { + case DEBUG_FORMAT: + /* Print value and delta_us in ASCII for easier examination */ + if (value == FLUSH) value = cassette_value; + delta_us = (unsigned long) (ddelta_us + 0.5); + cassette_roundoff_error = delta_us - ddelta_us; + fprintf(cassette_file, "%d %lu\n", value, delta_us); + break; + + case CPT_FORMAT: + /* Encode value and delta_us in two bytes if delta_us is small enough. + Pack bits as ddddddddddddddvv and store this value in little- + endian order. */ + if (value == FLUSH) value = cassette_value; + delta_us = (unsigned long) (ddelta_us + 0.5); + cassette_roundoff_error = delta_us - ddelta_us; + if (delta_us < 0x3fff) { + code = value | (delta_us << 2); + put_twobyte(code, cassette_file); + } else { + /* Else write 0xffff escape code and encode in five bytes: + 1-byte value, then 4-byte delta_us in little-endian order */ + put_twobyte(0xffff, cassette_file); + putc(value, cassette_file); + put_fourbyte(delta_us, cassette_file); + } + break; + + case WAV_FORMAT: + case DIRECT_FORMAT: +#if OSS_SOUND && HAVE_OSS + if (cassette_state == SOUND) { + if (ddelta_us > 20000.0) { + /* Truncate silent periods */ + ddelta_us = 20000.0; + cassette_roundoff_error = 0.0; + } + if (trs_event_scheduled() == transition_out || + trs_event_scheduled() == (trs_event_func) assert_state) { + trs_cancel_event(); + } + if (value == FLUSH) { + trs_schedule_event((trs_event_func)assert_state, CLOSE, 5000000); + } else { + trs_schedule_event(transition_out, FLUSH, + (int)(25000 * z80_state.clockMHz)); + } + } +#endif + sample = value_to_sample[cassette_value]; + nsamples = (unsigned long) + (ddelta_us / (1000000.0/cassette_sample_rate) + 0.5); + if (nsamples == 0) nsamples = 1; /* always at least one sample */ + cassette_roundoff_error = + nsamples * (1000000.0/cassette_sample_rate) - ddelta_us; +#if CASSDEBUG + debug("%d %4lu %d -> %3lu\n", cassette_value, + z80_state.t_count - cassette_transition, value, nsamples); +#endif + if (cassette_format == DIRECT_FORMAT && cassette_stereo) nsamples *= 2; + while (nsamples-- > 0) { + put_sample(sample, cassette_format == DIRECT_FORMAT, cassette_file); + } + if (value == FLUSH) { + value = cassette_value; +#if OSS_SOUND && HAVE_OSS + if (cassette_format == DIRECT_FORMAT) { + ioctl(fileno(cassette_file), SNDCTL_DSP_POST, 0); + } + trs_restore_delay(); +#endif + } + break; + + case CAS_FORMAT: + if (value == FLUSH && cassette_bitnumber != 0) { + putc(cassette_byte, cassette_file); + cassette_byte = 0; + break; + } + sample = 2; /* i.e., no bit */ + switch (cassette_pulsestate) { + case ST_INITIAL: + if (cassette_value == 2 && value == 0) { + /* Low speed, end of first pulse. Assume clock */ + cassette_pulsestate = ST_500GOTCLK; + } else if (cassette_value == 2 && value == 1) { + /* High speed, nothing interesting yet. */ + cassette_pulsestate = ST_1500; + } + break; + + case ST_500GOTCLK: + if (cassette_value == 0 && value == 1) { + /* Low speed, start of next pulse. */ + if (ddelta_us > ST_250THRESH) { + /* Oops, really ultra-low speed */ + /* It's the next clock; bit was 0 */ + sample = 0; + /* Watch for end of this clock */ + cassette_pulsestate = ST_250; + } else if (ddelta_us > ST_500THRESH) { + /* It's the next clock; bit was 0 */ + sample = 0; + /* Watch for end of this clock */ + cassette_pulsestate = ST_INITIAL; + } else { + /* It's a data pulse; bit was 1 */ + sample = 1; + /* Ignore the data pulse falling edge */ + cassette_pulsestate = ST_500GOTDAT; + } + } + break; + + case ST_500GOTDAT: + if (cassette_value == 2 && value == 0) { + /* End of data pulse; watch for end of next clock */ + cassette_pulsestate = ST_INITIAL; + } + break; + + case ST_1500: + if (cassette_value == 1 && value == 2) { + sample = (ddelta_us < ST_1500THRESH); + } + break; + + case ST_250: + if (cassette_value == 2 && value == 0) { + /* Ultra-low speed, end of first pulse. Assume clock */ + cassette_pulsestate = ST_250GOTCLK; + } + break; + + case ST_250GOTCLK: + if (cassette_value == 0 && value == 1) { + /* Low speed, start of next pulse. */ + if (ddelta_us > ST_250THRESH) { + /* It's the next clock; bit was 0 */ + sample = 0; + /* Watch for end of this clock */ + cassette_pulsestate = ST_250; + } else { + /* It's a data pulse; bit was 1 */ + sample = 1; + /* Ignore the data pulse falling edge */ + cassette_pulsestate = ST_250GOTDAT; + } + } + break; + + case ST_250GOTDAT: + if (cassette_value == 2 && value == 0) { + /* End of data pulse; watch for end of next clock */ + cassette_pulsestate = ST_250; + } + break; + } + if (sample == 2) break; + + cassette_bitnumber--; + if (cassette_bitnumber < 0) cassette_bitnumber = 7; + cassette_byte |= (sample << cassette_bitnumber); + if (cassette_bitnumber == 0) { + putc(cassette_byte, cassette_file); + cassette_byte = 0; + } + break; + + + default: + error("output format %s not implemented", + cassette_format < (sizeof(format_name)/sizeof(char *)) ? + format_name[cassette_format] : "out of range;"); + break; + } + + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (cassette_value != value) last_sound = z80_state.t_count; + cassette_transition = z80_state.t_count; + cassette_value = value; +} + +/* Read a new transition, updating cassette_next and cassette_delta. + If file read fails (perhaps due to eof), return 0, else 1. + Set cassette_delta to (unsigned long) -1 on failure. */ +static int +transition_in() +{ + unsigned long delta_us, nsamples, maxsamples; + Ushort code; + Uint d; + int next, ret = 0; + int c, cabs; + float delta_ts; + sigset_t set, oldset; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + + switch (cassette_format) { + case DEBUG_FORMAT: + if (fscanf(cassette_file, "%d %lu\n", &next, &delta_us) == 2) { + delta_ts = delta_us * z80_state.clockMHz - cassette_roundoff_error; + cassette_delta = (unsigned long)(delta_ts + 0.5); + cassette_roundoff_error = cassette_delta - delta_ts; + cassette_next = next; +#if CASSDEBUG + debug("%d %4lu %d\n", cassette_value, cassette_delta, cassette_next); +#endif + ret = 1; + } + break; + + case CPT_FORMAT: + c = get_twobyte(&code, cassette_file); + if (c == -1) break; + if (code == 0xffff) { + c = getc(cassette_file); + if (c == EOF) break; + cassette_next = c; + c = get_fourbyte(&d, cassette_file); + if (c == -1) break; + delta_us = d; + } else { + cassette_next = code & 3; + delta_us = code >> 2; + } + delta_ts = delta_us * z80_state.clockMHz - cassette_roundoff_error; + cassette_delta = (unsigned long)(delta_ts + 0.5); + cassette_roundoff_error = cassette_delta - delta_ts; +#if CASSDEBUG + debug("%d %4lu %d\n", cassette_value, cassette_delta, cassette_next); +#endif + ret = 1; + break; + + case DIRECT_FORMAT: + case WAV_FORMAT: + nsamples = 0; + maxsamples = cassette_sample_rate / 100; + do { + int direct = (cassette_format == DIRECT_FORMAT); + c = get_sample(direct, cassette_file); + if (direct && cassette_stereo) { + /* Discard right channel */ + (void) get_sample(direct, cassette_file); + } + if (c == EOF) goto fail; + if (c > 127 + cassette_noisefloor) { + next = 1; + } else if (c <= 127 - cassette_noisefloor) { + next = 2; + } else { + next = 0; + } + if (cassette_speed == SPEED_1500) { + cassette_noisefloor = 2; + } else { + /* Attempt to learn the correct noise cutoff adaptively. + * This code is just a hack; it would be nice to know a + * real signal-processing algorithm for this application + */ + cabs = abs(c - 127); +#if CASSDEBUG2 + debug("%f %f %d %d -> %d\n", cassette_avg, cassette_env, + cassette_noisefloor, cabs, next); +#endif + if (cabs > 1) { + cassette_avg = (99*cassette_avg + cabs)/100; + } + if (cabs > cassette_env) { + cassette_env = (cassette_env + 9*cabs)/10; + } else if (cabs > 10) { + cassette_env = (99*cassette_env + cabs)/100; + } + cassette_noisefloor = (cassette_avg + cassette_env)/2; + } + nsamples++; + /* Allow reset button */ + trs_get_event(0); + if (z80_state.nmi) break; + } while (next == cassette_value && maxsamples-- > 0); + cassette_next = next; + delta_ts = nsamples * (1000000.0/cassette_sample_rate) + * z80_state.clockMHz - cassette_roundoff_error; + cassette_delta = (unsigned long) delta_ts + 0.5; + cassette_roundoff_error = cassette_delta - delta_ts; +#if CASSDEBUG + debug("%3lu -> %d %4lu %d\n", + nsamples, cassette_value, cassette_delta, cassette_next); +#endif + ret = 1; + break; + + case CAS_FORMAT: + if (cassette_pulsestate == 0) { + cassette_bitnumber--; + } + if (cassette_bitnumber < 0) { + c = getc(cassette_file); + if (c == EOF) { + /* Add one extra zero byte to work around an apparent bug + in the Vavasour Model I emulator's .CAS files */ + if (cassette_byte == 0x100) goto fail; + c = 0x100; + } + cassette_byte = c; + cassette_bitnumber = 7; + } + c = (cassette_byte >> cassette_bitnumber) & 1; + delta_us = + pulse_shape[cassette_speed][c][cassette_pulsestate].delta_us; + cassette_next = + pulse_shape[cassette_speed][c][cassette_pulsestate].next; + cassette_pulsestate++; + if (pulse_shape[cassette_speed][c][cassette_pulsestate].next == -1) { + cassette_pulsestate = 0; + /* Kludge to emulate extra delay that's needed after the initial + 0xA5 sync byte to let Basic execute the CLEAR routine. + */ + if (cassette_byte == 0xa5 && cassette_speed == SPEED_500) { + delta_us += 1034; + } + } + delta_ts = delta_us * z80_state.clockMHz - cassette_roundoff_error; + cassette_delta = (unsigned long)(delta_ts + 0.5); + cassette_roundoff_error = cassette_delta - delta_ts; +#if CASSDEBUG + debug("%d %4lu %d\n", + cassette_value, cassette_delta, cassette_next); +#endif + ret = 1; + break; + + default: + error("input format %s not implemented", + cassette_format < (sizeof(format_name)/sizeof(char *)) ? + format_name[cassette_format] : "out of range;"); + break; + } + fail: + if (ret == 0) { + cassette_delta = (unsigned long) -1; + } + sigprocmask(SIG_SETMASK, &oldset, NULL); + return ret; +} + +/* If the motor has been on for 1 second (emulated time), the i/o port + has been neither read nor written, and the Z-80 program has 1500 + bps rise or fall interrupts enabled, then give it one of each just + to get things going. */ +void +trs_cassette_kickoff(int dummy) +{ + if (cassette_motor && cassette_state == CLOSE && + trs_cassette_interrupts_enabled()) { + cassette_speed = SPEED_1500; + cassette_transition = z80_state.t_count; + trs_cassette_fall_interrupt(1); + trs_cassette_rise_interrupt(1); + } +} + +/* Z-80 program is turning motor on or off */ +void trs_cassette_motor(int value) +{ + if (value) { + /* motor on */ + if (!cassette_motor) { +#if CASSDEBUG3 + debug("motor on %ld\n", z80_state.t_count); +#endif + cassette_motor = 1; + cassette_transition = z80_state.t_count; + cassette_value = 0; + cassette_next = 0; + cassette_delta = 0; + cassette_flipflop = 0; + cassette_byte = 0; + cassette_bitnumber = 0; + cassette_pulsestate = 0; + cassette_speed = SPEED_500; + cassette_roundoff_error = 0.0; + cassette_avg = NOISE_FLOOR; + cassette_env = 127; + cassette_noisefloor = NOISE_FLOOR; + cassette_firstoutread = 0; + cassette_transitionsout = 0; + if (trs_model > 1) { + /* Get 1500bps reading started after 1 second */ + trs_schedule_event(trs_cassette_kickoff, 0, + (tstate_t) (1000000 * z80_state.clockMHz)); + } + } + } else { + /* motor off */ + if (cassette_motor) { + if (cassette_state == WRITE) { + transition_out(FLUSH); + } + assert_state(CLOSE); + cassette_motor = 0; + } + } +} + +void trs_cassette_out(int value) +{ +#if CASSDEBUG3 + debug("out %ld\n", z80_state.t_count); +#endif + if (cassette_motor) { + if (cassette_state == READ) { + trs_cassette_update(0); + cassette_flipflop = 0; + if (cassette_firstoutread == 0) { + cassette_firstoutread = z80_state.t_count; + } + } + if (cassette_state != READ && value != cassette_value) { + if (assert_state(WRITE) < 0) return; + transition_out(value); + } + } + +#if SB_SOUND + /* Do sound emulation by wiggling SoundBlaster output in real time */ + if ((cassette_motor == 0) && sb_address) { + while (inb(sb_address + 0xC) & 0x80) /*poll*/ ; + outb(0x10, sb_address + 0xC); + while (inb(sb_address + 0xC) & 0x80) /*poll*/ ; + outb(sb_cassette_volume[value], sb_address + 0xC); + } +#endif +#if OSS_SOUND && HAVE_OSS + /* Do sound emulation by sending samples to /dev/dsp */ + if (cassette_motor == 0 && !sb_address) { + if (cassette_state != SOUND && value == 0) return; + if (assert_state(SOUND) < 0) return; + trs_suspend_delay(); + transition_out(value); + } +#endif +} + + +/* Model 4 sound port */ +void +trs_sound_out(int value) +{ +#if SB_SOUND + /* Do sound emulation */ + if (sb_address) { + while (inb(sb_address + 0xC) & 0x80) /*poll*/ ; + outb(0x10, sb_address + 0xC); + while (inb(sb_address + 0xC) & 0x80) /*poll*/ ; + outb(sb_sound_volume[value], sb_address + 0xC); + } +#endif +#if HAVE_OSS + if (cassette_motor == 0 && !sb_address) { + if (assert_state(SOUND) < 0) return; + trs_suspend_delay(); + transition_out(value ? 1 : 2); + } +#endif +} + +static void +orch90_flush(int dummy) +{ + trs_orch90_out(0, FLUSH); +} + +/* Orchestra 85/90 */ +/* Not supported in obsolescent SB_SOUND mode */ +/* Implementation shares some global state with cassette and game + sound implementations. */ +void +trs_orch90_out(int channels, int value) +{ +#if HAVE_OSS + long nsamples; + float ddelta_us; + sigset_t set, oldset; + int new_left, new_right; + int v; + + /* Convert 8-bit signed to 8-bit unsigned */ + v = (value & 0xff) ^ 0x80; + + if (cassette_motor != 0) return; + if (assert_state(ORCH90) < 0) return; + trs_suspend_delay(); + if (channels & 1) { + new_left = v; + } else { + new_left = orch90_left; + } + if (channels & 2) { + new_right = v; + } else { + new_right = orch90_right; + } + if (value != FLUSH && + new_left == orch90_left && new_right == orch90_right) return; + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + + ddelta_us = (z80_state.t_count - cassette_transition) / z80_state.clockMHz + - cassette_roundoff_error; + if (ddelta_us > 300000.0) { + /* Truncate silent periods */ + ddelta_us = 300000.0; + } + nsamples = (unsigned long) + (ddelta_us / (1000000.0/cassette_sample_rate) + 0.5); + + cassette_roundoff_error = + nsamples * (1000000.0/cassette_sample_rate) - ddelta_us; + + while (nsamples-- > 0) { + put_sample(orch90_left, TRUE, cassette_file); + put_sample(orch90_right, TRUE, cassette_file); + } + + if (trs_event_scheduled() == orch90_flush || + trs_event_scheduled() == (trs_event_func) assert_state) { + trs_cancel_event(); + } + if (value == FLUSH) { + trs_schedule_event((trs_event_func)assert_state, CLOSE, 5000000); + } else { + trs_schedule_event(orch90_flush, FLUSH, + (int)(250000 * z80_state.clockMHz)); + } + + sigprocmask(SIG_SETMASK, &oldset, NULL); + last_sound = z80_state.t_count; + cassette_transition = z80_state.t_count; + orch90_left = new_left; + orch90_right = new_right; +#endif +} + +void +trs_cassette_update(int dummy) +{ + if (cassette_motor && cassette_state != WRITE && assert_state(READ) >= 0) { + int newtrans = 0; + while ((z80_state.t_count - cassette_transition) >= cassette_delta) { + + /* Simulate analog signal processing on the 500-bps cassette input */ + if (cassette_next != 0 && cassette_value == 0) { + cassette_flipflop = 0x80; + } + + /* Deliver the previously read transition from the file */ + cassette_value = cassette_next; + cassette_transition += cassette_delta; + + /* Remember last nonzero value to get hysteresis in 1500 bps + zero-crossing detector */ + if (cassette_value != 0) cassette_lastnonzero = cassette_value; + + /* Read the next transition */ + newtrans = transition_in(); + + /* Allow reset button */ + trs_get_event(0); + if (z80_state.nmi) return; + } + /* Schedule an interrupt on the 1500-bps cassette input if needed */ + if (newtrans && cassette_speed == SPEED_1500) { + if (cassette_next == 2 && cassette_lastnonzero != 2) { + trs_schedule_event(trs_cassette_fall_interrupt, 1, + cassette_delta - + (z80_state.t_count - cassette_transition)); + } else if (cassette_next == 1 && cassette_lastnonzero != 1) { + trs_schedule_event(trs_cassette_rise_interrupt, 1, + cassette_delta - + (z80_state.t_count - cassette_transition)); + } else { + trs_schedule_event(trs_cassette_update, 0, + cassette_delta - + (z80_state.t_count - cassette_transition)); + } + } + } +} + + +int +trs_cassette_in() +{ +#if CASSDEBUG3 + debug("in %ld\n", z80_state.t_count); +#endif + if (cassette_motor && cassette_transitionsout <= 1) { + assert_state(READ); + } + /* Heuristic to detect reading with Level 1 routines. If the + routine paused too long after resetting the flipflop before + reading it again, assume it must be Level 1 code. */ + if (cassette_firstoutread > 1) { + if ((z80_state.t_count - cassette_firstoutread) + / z80_state.clockMHz > DETECT_250) { + cassette_speed = SPEED_250; + } else { + cassette_speed = SPEED_500; + } +#if CASSDEBUG4 + debug("250 detector = %s (%f)\n", + (cassette_speed == SPEED_250) ? "yes" : "no", + (z80_state.t_count - cassette_firstoutread) / z80_state.clockMHz); +#endif + cassette_firstoutread = 1; /* disable detector */ + } + trs_cassette_clear_interrupts(); + trs_cassette_update(0); + if (trs_model == 1) { + return cassette_flipflop; + } else { + return cassette_flipflop | (cassette_lastnonzero == 1); + } +} + +void +trs_cassette_reset() +{ + assert_state(CLOSE); +} + +void trs_sound_init(int ioport, int vol) +{ +#if SB_SOUND +/* try to initialize SoundBlaster. Usual ioport is 0x220 */ +#define MAX_TRIES 65536 + int tries; +#ifdef SOUNDDEBUG + int major, minor; +#endif + + if (sb_address != 0) return; + if ((ioport & 0xFFFFFF0F) != 0x200) { + error("Invalid SoundBlaster I/O port"); + return; + } + sb_address = ioport; + if (ioperm(ioport, 0x10, 1)) { + error("unable to access SoundBlaster I/O ports: %s", strerror(errno)); + sb_address = 0; + return; + } + + /* Reset SoundBlaster DSP */ + outb(0x01, sb_address + 0x6); + usleep(3); + outb(0x00, sb_address + 0x6); + for (tries = 0; tries < MAX_TRIES; tries++) { + if ((inb(sb_address + 0xE) & 0x80) && + (inb(sb_address + 0xA) == 0xAA)) break; + } + if (tries == MAX_TRIES) { + error("unable to detect SoundBlaster"); + sb_address = 0; + return; + } + +#ifdef SOUNDDEBUG + /* Get DSP version number */ + while (inb(sb_address + 0xC) & 0x80) /*poll*/ ; + outb(0xE1, sb_address + 0xC); + while ((inb(sb_address + 0xE) & 0x80) == 0) /*poll*/ ; + major = inb(sb_address + 0xA); + while ((inb(sb_address + 0xE) & 0x80) == 0) /*poll*/ ; + minor = inb(sb_address + 0xA); + debug("SoundBlaster DSP version %d.%d detected\n", major, minor); +#endif + + /* Turn on DAC speaker */ + while (inb(sb_address + 0xC) & 0x80) /*poll*/ ; + outb(0xD1, sb_address + 0xC); + + sb_set_volume(vol); +#else /*!SB_SOUND*/ + error("xtrs: -sb is obsolete; see the man page"); +#endif +} + +void +sb_set_volume(int vol) +{ +#if SB_SOUND + if (sb_address == 0) return; + /* Set up volume values */ + if (vol < 0) vol = 0; + if (vol > 100) vol = 100; + sb_volume = vol; + /* Values in comments from Model I technical manual. Model III/4 used + a different value for one resistor in the network, so these + voltages are not exactly right. In particular 3 and 0 are no + longer almost identical, but as far as I know, 3 is still unused. + */ + sb_cassette_volume[0] = (vol*255)/200; /* 0.46 V */ + sb_cassette_volume[1] = (vol*255)/100; /* 0.85 V */ + sb_cassette_volume[2] = 0; /* 0.00 V */ + sb_cassette_volume[3] = (vol*255)/200; /* unused, but about 0.46 V */ + sb_sound_volume[0] = 0; + sb_sound_volume[1] = (vol*255)/100; +#endif +} + +int +sb_get_volume() +{ + return sb_volume; +} diff --git a/trs_chars.c b/trs_chars.c new file mode 100644 index 0000000..7a0c5a8 --- /dev/null +++ b/trs_chars.c @@ -0,0 +1,2301 @@ +/* + * trs_chars.c + * + * This file contains the bitmap data for all 256 characters in the + * TRS-80 character set, for both Model I and Model III/4. Thanks to + * Al Petrofsky for supplying some of the data in .bdf format. Thanks + * to Todd P. Cromwell III for supplying exact dumps of two different + * Model III character generator ROMs. + */ + +#include "trs_iodefs.h" + +char trs_char_data[][MAXCHARS][TRS_CHAR_HEIGHT] = { + +{ + /* CG 0 - for Model I */ + /* Source: MCM6674 Data Sheet */ + /* This is a very old version of the Model I font, found in only a + few machines, that has standard ASCII [ \ ] ^ instead of + directional arrows. It also has odd symbols in positions 0-31, + and the lowercase letters with descenders (plus 'a') are + raised. Level II Basic put the odd symbols on the screen instead + of uppercase if you did a homebrew lowercase conversion and did + not replace the CG. + */ + { 0x00,0x1f,0x11,0x11,0x11,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x1e,0x04,0x08,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x1b,0x15,0x1b,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x08,0x05,0x03,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1f,0x0a,0x0a,0x1b,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x02,0x0f,0x12,0x14,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x00,0x00,0x1f,0x00,0x00,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x15,0x0e,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x02,0x1f,0x02,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x1b,0x15,0x1b,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x15,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x11,0x1f,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x15,0x15,0x1d,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1d,0x15,0x15,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x17,0x15,0x15,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x15,0x15,0x17,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x14,0x08,0x15,0x03,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x0a,0x0a,0x0a,0x0a,0x0a,0x1b,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x1f,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x0a,0x04,0x0a,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x0e,0x0e,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x01,0x02,0x04,0x00,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1f,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x15,0x15,0x17,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x11,0x17,0x15,0x15,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x11,0x1d,0x15,0x15,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x15,0x15,0x1d,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x0a,0x1f,0x0a,0x1f,0x0a,0x0a,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x1e,0x05,0x0e,0x14,0x0f,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x03,0x13,0x08,0x04,0x02,0x19,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x05,0x05,0x02,0x15,0x09,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0x02,0x02,0x02,0x04,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x08,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x15,0x0e,0x1f,0x0e,0x15,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x1f,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x08,0x04,0x02,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x19,0x15,0x13,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x06,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x0e,0x01,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x0c,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x0c,0x0a,0x09,0x1f,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x0f,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x02,0x01,0x0f,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x10,0x08,0x04,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x0e,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1e,0x10,0x08,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x06,0x06,0x00,0x06,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x06,0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1f,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x08,0x04,0x00,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x16,0x15,0x15,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x0a,0x11,0x11,0x1f,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x12,0x12,0x0e,0x12,0x12,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x01,0x01,0x01,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x12,0x12,0x12,0x12,0x12,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x01,0x07,0x01,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x01,0x07,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x01,0x01,0x19,0x11,0x11,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x1f,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x09,0x05,0x03,0x05,0x09,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x1b,0x15,0x15,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x13,0x15,0x19,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x11,0x11,0x0f,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x11,0x15,0x09,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x11,0x11,0x0f,0x05,0x09,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x01,0x0e,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x0a,0x0a,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x11,0x15,0x1b,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x0a,0x04,0x0a,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x0a,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x10,0x08,0x04,0x02,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x02,0x02,0x02,0x02,0x02,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x08,0x08,0x08,0x08,0x08,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x0a,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x0c,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x10,0x1e,0x11,0x1e,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x0d,0x13,0x11,0x13,0x0d,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0e,0x11,0x01,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x16,0x19,0x11,0x19,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0e,0x11,0x1f,0x01,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x14,0x04,0x0e,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x16,0x19,0x19,0x16,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x0d,0x13,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x00,0x06,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x09,0x05,0x03,0x05,0x09,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0b,0x15,0x15,0x15,0x15,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0d,0x13,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0e,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0d,0x13,0x11,0x13,0x0d,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x16,0x19,0x11,0x19,0x16,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0d,0x13,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1e,0x01,0x0e,0x10,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x1f,0x04,0x04,0x14,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x11,0x11,0x19,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x11,0x11,0x0a,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x11,0x15,0x15,0x0a,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x0a,0x04,0x0a,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x1e,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1f,0x08,0x04,0x02,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0x04,0x02,0x04,0x04,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x00,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x04,0x08,0x04,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x15,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x15,0x0a,0x15,0x0a,0x15,0x0a,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 1 - for Model I */ + /* Source: MCM6674 Data Sheet, modified by mann (from memory) to add + arrows. */ + /* This is the standard Model I character generator found in + machines without the Radio Shack lower case modification, + including the arrows. It has odd symbols in positions 0-31, and + lowercase letters with descenders (plus 'a') are raised. Level II + Basic put the odd symbols on the screen if you did a homebrew + lowercase conversion and did not replace the CG. + */ + { 0x00,0x1f,0x11,0x11,0x11,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x1e,0x04,0x08,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x1b,0x15,0x1b,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x08,0x05,0x03,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1f,0x0a,0x0a,0x1b,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x02,0x0f,0x12,0x14,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x00,0x00,0x1f,0x00,0x00,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x15,0x0e,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x02,0x1f,0x02,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x1b,0x15,0x1b,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x15,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x11,0x1f,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x15,0x15,0x1d,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1d,0x15,0x15,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x17,0x15,0x15,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x15,0x15,0x17,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x14,0x08,0x15,0x03,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x0a,0x0a,0x0a,0x0a,0x0a,0x1b,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x1f,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x0a,0x04,0x0a,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x0e,0x0e,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x01,0x02,0x04,0x00,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1f,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x15,0x15,0x17,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x11,0x17,0x15,0x15,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x11,0x11,0x1d,0x15,0x15,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x15,0x15,0x1d,0x11,0x11,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x0a,0x1f,0x0a,0x1f,0x0a,0x0a,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x1e,0x05,0x0e,0x14,0x0f,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x03,0x13,0x08,0x04,0x02,0x19,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x05,0x05,0x02,0x15,0x09,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0x02,0x02,0x02,0x04,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x08,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x15,0x0e,0x1f,0x0e,0x15,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x1f,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x08,0x04,0x02,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x19,0x15,0x13,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x06,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x0e,0x01,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x0c,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x0c,0x0a,0x09,0x1f,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x0f,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x02,0x01,0x0f,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x10,0x08,0x04,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x0e,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x1e,0x10,0x08,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x06,0x06,0x00,0x06,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x06,0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1f,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x08,0x04,0x00,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x10,0x16,0x15,0x15,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x0a,0x11,0x11,0x1f,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x12,0x12,0x0e,0x12,0x12,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x01,0x01,0x01,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x12,0x12,0x12,0x12,0x12,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x01,0x07,0x01,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x01,0x01,0x07,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x01,0x01,0x19,0x11,0x11,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x1f,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x09,0x05,0x03,0x05,0x09,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x1b,0x15,0x15,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x13,0x15,0x19,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x11,0x11,0x0f,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x11,0x11,0x15,0x09,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x11,0x11,0x0f,0x05,0x09,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x11,0x01,0x0e,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x0a,0x0a,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x11,0x15,0x1b,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x0a,0x04,0x0a,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x0a,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x10,0x08,0x04,0x02,0x01,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x0e,0x15,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x02,0x1f,0x02,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x0c,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x10,0x1e,0x11,0x1e,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x0d,0x13,0x11,0x13,0x0d,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0e,0x11,0x01,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x16,0x19,0x11,0x19,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0e,0x11,0x1f,0x01,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x14,0x04,0x0e,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x16,0x19,0x19,0x16,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x0d,0x13,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x00,0x06,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x01,0x01,0x09,0x05,0x03,0x05,0x09,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0b,0x15,0x15,0x15,0x15,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0d,0x13,0x11,0x11,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0e,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0d,0x13,0x11,0x13,0x0d,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x16,0x19,0x11,0x19,0x16,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x0d,0x13,0x01,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1e,0x01,0x0e,0x10,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x1f,0x04,0x04,0x14,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x11,0x11,0x19,0x16,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x11,0x11,0x0a,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x11,0x15,0x15,0x0a,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x11,0x0a,0x04,0x0a,0x11,0x00,0x00,0x00,0x00 }, + { 0x00,0x11,0x11,0x11,0x1e,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1f,0x08,0x04,0x02,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0x04,0x02,0x04,0x04,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x00,0x04,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x04,0x08,0x04,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x15,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x15,0x0a,0x15,0x0a,0x15,0x0a,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 2 - for Model I */ + /* Source: MCM6674 Data Sheet, modified by mann (from memory). */ + /* This is the replacement Model I character generator you got with + the Radio Shack lowercase modification. Positions 0-31 are a copy + of the uppercase letters, to work around a bug (?) in the Level + II ROM. All characters without descenders are moved up one row. + I'm not sure I got all the changes exactly right -- help? + */ + { 0x0e,0x11,0x10,0x16,0x15,0x15,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x0a,0x11,0x11,0x1f,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x12,0x12,0x0e,0x12,0x12,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x01,0x01,0x01,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x12,0x12,0x12,0x12,0x12,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x01,0x01,0x07,0x01,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x01,0x01,0x07,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x01,0x01,0x19,0x11,0x11,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x1f,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x09,0x05,0x03,0x05,0x09,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x01,0x01,0x01,0x01,0x01,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x1b,0x15,0x15,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x13,0x15,0x19,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x11,0x11,0x0f,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x11,0x11,0x15,0x09,0x16,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x11,0x11,0x0f,0x05,0x09,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x01,0x0e,0x10,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x0a,0x0a,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x11,0x15,0x1b,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x0a,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x0e,0x15,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x02,0x1f,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x0a,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x0a,0x0a,0x1f,0x0a,0x1f,0x0a,0x0a,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x1e,0x05,0x0e,0x14,0x0f,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x03,0x13,0x08,0x04,0x02,0x19,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x05,0x05,0x02,0x15,0x09,0x16,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x04,0x02,0x02,0x02,0x04,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x04,0x08,0x08,0x08,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x15,0x0e,0x1f,0x0e,0x15,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x1f,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x04,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x19,0x15,0x13,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x06,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x10,0x0e,0x01,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x10,0x0c,0x10,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x0c,0x0a,0x09,0x1f,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x01,0x0f,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x02,0x01,0x0f,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x10,0x08,0x04,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x11,0x0e,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x11,0x1e,0x10,0x08,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x06,0x00,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x06,0x00,0x06,0x06,0x02,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1f,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x04,0x08,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x10,0x08,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x10,0x16,0x15,0x15,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x0a,0x11,0x11,0x1f,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x12,0x12,0x0e,0x12,0x12,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x01,0x01,0x01,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x12,0x12,0x12,0x12,0x12,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x01,0x01,0x07,0x01,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x01,0x01,0x07,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x01,0x01,0x19,0x11,0x11,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x1f,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x09,0x05,0x03,0x05,0x09,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x01,0x01,0x01,0x01,0x01,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x1b,0x15,0x15,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x13,0x15,0x19,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x11,0x11,0x0f,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x11,0x11,0x15,0x09,0x16,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x11,0x11,0x0f,0x05,0x09,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x11,0x01,0x0e,0x10,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x0a,0x0a,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x11,0x11,0x15,0x1b,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x0a,0x04,0x0a,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x11,0x11,0x0a,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x10,0x08,0x04,0x02,0x01,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x0e,0x15,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x15,0x0e,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x02,0x1f,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x0c,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0e,0x10,0x1e,0x11,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x01,0x01,0x0d,0x13,0x11,0x13,0x0d,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0e,0x11,0x01,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x16,0x19,0x11,0x19,0x16,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0e,0x11,0x1f,0x01,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x14,0x04,0x0e,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x16,0x19,0x19,0x16,0x10,0x0e,0x00,0x00,0x00,0x00 }, + { 0x01,0x01,0x0d,0x13,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x00,0x06,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x00,0x10,0x10,0x10,0x10,0x11,0x0e,0x00,0x00,0x00,0x00 }, + { 0x01,0x01,0x09,0x05,0x03,0x05,0x09,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x04,0x04,0x04,0x04,0x04,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0b,0x15,0x15,0x15,0x15,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0d,0x13,0x11,0x11,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0e,0x11,0x11,0x11,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0d,0x13,0x13,0x0d,0x01,0x01,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x16,0x19,0x19,0x16,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0d,0x13,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1e,0x01,0x0e,0x10,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x1f,0x04,0x04,0x14,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x11,0x11,0x11,0x19,0x16,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x11,0x11,0x11,0x0a,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x11,0x11,0x15,0x15,0x0a,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x11,0x0a,0x04,0x0a,0x11,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x11,0x11,0x11,0x1e,0x10,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1f,0x08,0x04,0x02,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x04,0x04,0x02,0x04,0x04,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x00,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x04,0x04,0x08,0x04,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x15,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x0a,0x15,0x0a,0x15,0x0a,0x15,0x0a,0x00,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 3 - for Model I */ + /* Source: BDF font from Al Petrofsky, probably adapted from another + emulator. This is not the real Model I font, which used a 6x12 + matrix; it seems to be a modified version of the 8x12 Model III + font. + */ + { 0x7c,0x82,0x80,0x8c,0x92,0x92,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x84,0x84,0xfc,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x78,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x04,0x04,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x88,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0xf8,0x04,0x04,0xe4,0x84,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0xfc,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x80,0x80,0x80,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x44,0x24,0x1c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0xc6,0xaa,0x92,0x82,0x82,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x8c,0x94,0xa4,0xc4,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0xa4,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x78,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x82,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x92,0xaa,0xc6,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x48,0x30,0x48,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x40,0x20,0x10,0x08,0x04,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x92,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x92,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0xfe,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x40,0xfe,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x48,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x48,0x48,0xfc,0x48,0xfc,0x48,0x48,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0xfc,0x12,0x7c,0x90,0x7e,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x8c,0x40,0x20,0x10,0x08,0xc4,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x28,0x18,0xa4,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x30,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x20,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x54,0x38,0xfe,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x30,0x30,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0xc4,0xa4,0x94,0x8c,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x18,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x80,0x78,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x80,0x70,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x40,0x60,0x50,0x48,0xfc,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x7c,0x80,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x70,0x08,0x04,0x7c,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x80,0x40,0x20,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x78,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0xf8,0x80,0x40,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x00,0x00,0x30,0x30,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x30,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x82,0x80,0x8c,0x92,0x92,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x84,0x84,0xfc,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x78,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x04,0x04,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x88,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0xf8,0x04,0x04,0xe4,0x84,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0xfc,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x80,0x80,0x80,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x44,0x24,0x1c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0xc6,0xaa,0x92,0x82,0x82,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x8c,0x94,0xa4,0xc4,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0xa4,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x78,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x82,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x92,0xaa,0xc6,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x48,0x30,0x48,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x40,0x20,0x10,0x08,0x04,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0xfe,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x40,0xfe,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x1c,0x08,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x80,0xf8,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x74,0x8c,0x84,0x8c,0x74,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0x04,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x80,0xb8,0xc4,0x84,0xc4,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0xfc,0x04,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x70,0x88,0x08,0x1c,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0x84,0xf8,0x80,0x78,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x74,0x8c,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x00,0xc0,0x80,0x80,0x84,0x84,0x78,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x84,0x44,0x3c,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6e,0x92,0x92,0x92,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x74,0x8c,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x74,0x8c,0x8c,0x74,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xb8,0xc4,0xc4,0xb8,0x80,0x80,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x74,0x8c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xf8,0x04,0x78,0x80,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x7c,0x10,0x10,0x90,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x44,0x44,0x44,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x82,0x82,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x82,0x92,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x84,0x48,0x30,0x48,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x84,0x84,0x84,0xf8,0x80,0x78,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x20,0x10,0x08,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x60,0x10,0x10,0x0c,0x10,0x10,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x10,0x10,0x60,0x10,0x10,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x44,0x38,0xfe,0x10,0xfe,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0xaa,0x54,0xaa,0x54,0xaa,0x54,0xaa,0x54,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 4 - for Model III */ + /* Source: A model III CG ROM, read by Todd P. Cromwell III (todd2.bin) */ + /* This is the older CG with Katakana alternate characters */ + /* Letters start at top of cell, Model III style */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x3e,0x08,0x48,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0xb8,0x44,0x64,0x54,0x4c,0x44,0x3a,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x34,0x4c,0x44,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x1c,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x5e,0x22,0x22,0x1e,0x12,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x28,0x00,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x44,0x4c,0x54,0x64,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x28,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x90,0x68,0x64,0x54,0x4c,0x2c,0x12,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x14,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x02,0x3e,0x42,0x7c,0x40,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x7c,0x04,0x7c,0x04,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x78,0x24,0x64,0x3c,0x24,0x64,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x04,0x04,0x44,0x38,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x7e,0x24,0x7e,0x24,0x24,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x78,0x14,0x38,0x50,0x3c,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x46,0x26,0x10,0x08,0x64,0x62,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x12,0x12,0x0c,0x52,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x54,0x38,0x7c,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x62,0x5a,0x46,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x18,0x14,0x10,0x10,0x10,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x30,0x0c,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x38,0x40,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x30,0x28,0x24,0x7e,0x20,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x02,0x1e,0x20,0x40,0x22,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x04,0x02,0x3e,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x42,0x20,0x10,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x7c,0x40,0x20,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x30,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x52,0x6a,0x32,0x04,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x24,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x02,0x02,0x02,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x24,0x44,0x44,0x44,0x24,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x02,0x02,0x1e,0x02,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x02,0x02,0x1e,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x02,0x72,0x42,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x70,0x20,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x22,0x12,0x0e,0x12,0x22,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x02,0x02,0x02,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x66,0x5a,0x5a,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x46,0x4a,0x52,0x62,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x42,0x42,0x3e,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x42,0x52,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x42,0x42,0x3e,0x12,0x22,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x02,0x3c,0x40,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x24,0x24,0x18,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x5a,0x5a,0x66,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x44,0x44,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x20,0x18,0x04,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x3a,0x46,0x42,0x46,0x3a,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x40,0x40,0x5c,0x62,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x3e,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x00,0x30,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x22,0x12,0x0a,0x16,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6e,0x92,0x92,0x92,0x92,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3a,0x46,0x46,0x3a,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3a,0x46,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x02,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x08,0x3e,0x08,0x08,0x48,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x82,0x92,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x42,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7e,0x20,0x18,0x04,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x08,0x08,0x04,0x08,0x08,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x10,0x10,0x20,0x10,0x10,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x92,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x7c,0xfe,0xfe,0x7c,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x6c,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x38,0x10,0xd6,0xfe,0xd6,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xa5,0x81,0x99,0xa5,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x3c,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x9c,0x62,0x62,0x9c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x44,0x3c,0x44,0x44,0x3c,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x86,0x48,0x28,0x18,0x08,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x30,0x50,0x48,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x60,0x10,0x08,0x7c,0x08,0x10,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x68,0x60,0x10,0x08,0x38,0x40,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x34,0x4a,0x48,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x7c,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x12,0x0a,0x06,0x0a,0x52,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x08,0x08,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x24,0x24,0x5c,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x04,0x18,0x04,0x38,0x40,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x2a,0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x24,0x1c,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x12,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x26,0x24,0x24,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x54,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x46,0x28,0x10,0x28,0xc4,0x00,0x00,0x00,0x00,0x00 }, + { 0x92,0x54,0x54,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x44,0x82,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x82,0x82,0xc6,0x44,0xc6,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x08,0x08,0x08,0x0a,0x0c,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x04,0x08,0x30,0x08,0x04,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x28,0x44,0xfe,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x80,0x40,0xfe,0x10,0xfe,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x7c,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x4a,0x24,0x10,0x48,0xa4,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x82,0x82,0xfe,0x44,0x44,0xc6,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x12,0x0a,0x06,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x04,0x38,0x44,0x38,0x40,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x44,0xaa,0x54,0x28,0x54,0xaa,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xb9,0x85,0x85,0xb9,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x42,0x24,0x18,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x50,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x5e,0xa5,0xa5,0x9d,0x95,0x66,0x3c,0x00,0x00,0x00,0x00 }, + { 0xfa,0x06,0xc6,0x46,0x26,0xde,0x06,0xfa,0x00,0x00,0x00,0x00 }, + { 0xff,0x20,0xc0,0x3f,0x40,0x3f,0x20,0x1f,0x00,0x00,0x00,0x00 }, + { 0x3f,0x40,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x22,0x22,0x1e,0x52,0x22,0xd2,0x00,0x00,0x00,0x00,0x00 }, + { 0x86,0x41,0x21,0x16,0x68,0x94,0x92,0x61,0x00,0x00,0x00,0x00 }, + { 0x70,0x60,0x50,0x0e,0x09,0x09,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x44,0x44,0x38,0x10,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x70,0x10,0x10,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0xff,0xc7,0xbb,0xcf,0xef,0xff,0xef,0xff,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x38,0x54,0x10,0x28,0x44,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x38,0x54,0x28,0x7c,0x28,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x44,0x44,0x54,0x6c,0x44,0x00,0x00,0x00,0x00 }, + { 0x44,0x28,0x10,0x7c,0x10,0x7c,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x04,0x0a,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x20,0x20,0x20,0x20,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x02,0x04,0x08,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x20,0x3c,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x40,0x30,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x20,0x10,0x18,0x14,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x7c,0x44,0x40,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x38,0x10,0x10,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x3c,0x18,0x14,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x08,0x7c,0x48,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x38,0x20,0x20,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x40,0x78,0x40,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x54,0x54,0x44,0x20,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x28,0x18,0x08,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x40,0x20,0x10,0x18,0x14,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x7c,0x44,0x44,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x10,0x10,0x10,0x10,0x10,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x7e,0x10,0x18,0x14,0x12,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x7e,0x48,0x48,0x48,0x44,0x72,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x10,0x7c,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x44,0x44,0x42,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x7c,0x14,0x12,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x40,0x40,0x40,0x40,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x7e,0x24,0x24,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x1c,0x40,0x4e,0x40,0x40,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x20,0x10,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x7e,0x48,0x28,0x08,0x48,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x44,0x48,0x20,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x42,0x42,0x50,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x50,0x3e,0x10,0x7c,0x10,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x00,0x7e,0x40,0x20,0x10,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x00,0x7c,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x1c,0x24,0x44,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x7c,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x00,0x00,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x40,0x28,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x7e,0x40,0x20,0x30,0x58,0x14,0x00,0x00,0x00,0x00,0x00 }, + { 0x60,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x20,0x50,0x50,0x50,0x48,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x7e,0x02,0x02,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x40,0x20,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x08,0x14,0x22,0x40,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x7c,0x10,0x54,0x54,0x54,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x40,0x28,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x3c,0x42,0x3c,0x42,0x3c,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x04,0x12,0x22,0x5e,0x00,0x00,0x00,0x00,0x00 }, + { 0x40,0x44,0x28,0x10,0x28,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x08,0x3c,0x08,0x08,0x48,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x7e,0x48,0x28,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x20,0x20,0x20,0x10,0x08,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x40,0x7c,0x40,0x40,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x54,0x54,0x44,0x40,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x42,0x22,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x0a,0x0a,0x0a,0x4a,0x4a,0x2a,0x1a,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x44,0x44,0x24,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x42,0x42,0x42,0x42,0x42,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x42,0x42,0x40,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x4e,0x40,0x40,0x40,0x20,0x12,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x12,0x24,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 5 - for Model III */ + /* Source: see corresponding Model 4/4P CG below */ + /* This is the newer CG with international alternate characters. + Characters 0-31 are also different from the older CG. I'm not + certain this CG was available in the Model III; it may have been + introduced in the Model 4 */ + /* Letters shifted up 1 to start at top of cell, Model III style */ + { 0x24,0x18,0x24,0x42,0x7e,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x3c,0x44,0x44,0x3c,0x02,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x24,0x04,0x0e,0x04,0x44,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x02,0x3c,0x42,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x14,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0xfe,0x82,0xfe,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x20,0x20,0x14,0x0c,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x3c,0x24,0x24,0x3c,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00,0x00 }, + { 0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x38,0x38,0x28,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x3e,0x08,0x08,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x7e,0x24,0x7e,0x24,0x24,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x78,0x14,0x38,0x50,0x3c,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x46,0x26,0x10,0x08,0x64,0x62,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x12,0x12,0x0c,0x52,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x54,0x38,0x7c,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x62,0x5a,0x46,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x18,0x14,0x10,0x10,0x10,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x30,0x0c,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x38,0x40,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x30,0x28,0x24,0x7e,0x20,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x02,0x1e,0x20,0x40,0x22,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x04,0x02,0x3e,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x42,0x20,0x10,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x7c,0x40,0x20,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x30,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x52,0x6a,0x32,0x04,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x24,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x02,0x02,0x02,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x24,0x44,0x44,0x44,0x24,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x02,0x02,0x1e,0x02,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x02,0x02,0x1e,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x02,0x72,0x42,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x70,0x20,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x22,0x12,0x0e,0x12,0x22,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x02,0x02,0x02,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x66,0x5a,0x5a,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x46,0x4a,0x52,0x62,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x42,0x42,0x3e,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x42,0x42,0x52,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x42,0x42,0x3e,0x12,0x22,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x02,0x3c,0x40,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x24,0x24,0x18,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x42,0x5a,0x5a,0x66,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x44,0x44,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x40,0x20,0x18,0x04,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x3a,0x46,0x42,0x46,0x3a,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x40,0x40,0x5c,0x62,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x3e,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x00,0x30,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x02,0x02,0x22,0x12,0x0a,0x16,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6e,0x92,0x92,0x92,0x92,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3a,0x46,0x46,0x3a,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3a,0x46,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x02,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x08,0x3e,0x08,0x08,0x48,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x82,0x92,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x42,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7e,0x20,0x18,0x04,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x08,0x08,0x04,0x08,0x08,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x10,0x10,0x20,0x10,0x10,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x92,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x7c,0xfe,0xfe,0x7c,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x6c,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x38,0x10,0xd6,0xfe,0xd6,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xa5,0x81,0x99,0xa5,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x3c,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x9c,0x62,0x62,0x9c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x44,0x3c,0x44,0x44,0x3c,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x86,0x48,0x28,0x18,0x08,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x30,0x50,0x48,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x60,0x10,0x08,0x7c,0x08,0x10,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x68,0x60,0x10,0x08,0x38,0x40,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x34,0x4a,0x48,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x7c,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x12,0x0a,0x06,0x0a,0x52,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x08,0x08,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x24,0x24,0x5c,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x04,0x18,0x04,0x38,0x40,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x2a,0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x24,0x1c,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x12,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x26,0x24,0x24,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x54,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x46,0x28,0x10,0x28,0xc4,0x00,0x00,0x00,0x00,0x00 }, + { 0x92,0x54,0x54,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x44,0x82,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x82,0x82,0xc6,0x44,0xc6,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x08,0x08,0x08,0x0a,0x0c,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x04,0x08,0x30,0x08,0x04,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x28,0x44,0xfe,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x80,0x40,0xfe,0x10,0xfe,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x7c,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x4a,0x24,0x10,0x48,0xa4,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x82,0x82,0xfe,0x44,0x44,0xc6,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x12,0x0a,0x06,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x04,0x38,0x44,0x38,0x40,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x44,0xaa,0x54,0x28,0x54,0xaa,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xb9,0x85,0x85,0xb9,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x42,0x24,0x18,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x50,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x5e,0xa5,0xa5,0x9d,0x95,0x66,0x3c,0x00,0x00,0x00,0x00 }, + { 0xfa,0x06,0xc6,0x46,0x26,0xde,0x06,0xfa,0x00,0x00,0x00,0x00 }, + { 0xff,0x20,0xc0,0x3f,0x40,0x3f,0x20,0x1f,0x00,0x00,0x00,0x00 }, + { 0x3f,0x40,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x22,0x22,0x1e,0x52,0x22,0xd2,0x00,0x00,0x00,0x00,0x00 }, + { 0x86,0x41,0x21,0x16,0x68,0x94,0x92,0x61,0x00,0x00,0x00,0x00 }, + { 0x70,0x60,0x50,0x0e,0x09,0x09,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x44,0x44,0x38,0x10,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x70,0x10,0x10,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0xff,0xc7,0xbb,0xcf,0xef,0xff,0xef,0xff,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x38,0x54,0x10,0x28,0x44,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x38,0x54,0x28,0x7c,0x28,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x44,0x44,0x54,0x6c,0x44,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x08,0x00,0x00,0x00,0x00 }, + { 0x18,0x24,0x04,0x0e,0x04,0x44,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x44,0x44,0x44,0x64,0x5c,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x14,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x1c,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x02,0x3c,0x42,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x9d,0xa5,0x9d,0xa5,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xb9,0x85,0x85,0xb9,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x04,0x44,0x24,0x14,0x08,0x54,0x72,0x40,0x00,0x00,0x00,0x00 }, + { 0x06,0x44,0x26,0x14,0x0e,0x54,0x72,0x40,0x00,0x00,0x00,0x00 }, + { 0x04,0x44,0x24,0x14,0x68,0x44,0x22,0x60,0x00,0x00,0x00,0x00 }, + { 0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00 }, + { 0x44,0x28,0x10,0x7c,0x10,0x7c,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x18,0x24,0x42,0x7e,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x3c,0x44,0x44,0x3c,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x07,0x52,0xaa,0xaa,0x8a,0x8a,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x3e,0x08,0x08,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x14,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x3a,0x46,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x14,0x12,0x72,0x1e,0x12,0x72,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x2e,0x50,0x7c,0x12,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x18,0x24,0x42,0x7e,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x62,0x5a,0x46,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3c,0x62,0x5a,0x46,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x46,0x4a,0x52,0x62,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x7e,0x02,0x1e,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x18,0x24,0x7e,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x38,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x08,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x00,0x08,0x04,0x02,0x22,0x1c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x7e,0x02,0x1e,0x02,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x18,0x24,0x7e,0x42,0x42,0x00,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 6 - for Model III */ + /* Source: a Model III CG ROM, read by Todd P. Cromwell III (todd2_4.bin) */ + /* I'm guessing this was a 3rd party replacement CG, not from Radio + Shack. The ROM was marked as "Model III/4 character + generator". The normal ASCII characters are bold, and alternates + are the upper case and numbers in inverse video. This ROM doesn't + seem especially suited for the Model 4, which had inverse video + in hardware. */ + /* Letters start at top of cell, Model III style */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x3e,0x08,0x48,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x40,0x40,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0xb8,0x44,0x64,0x54,0x4c,0x44,0x3a,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x34,0x4c,0x44,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x1c,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x5e,0x22,0x22,0x1e,0x12,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x28,0x00,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x44,0x4c,0x54,0x64,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x28,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x90,0x68,0x64,0x54,0x4c,0x2c,0x12,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x4c,0x32,0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x14,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x02,0x3e,0x42,0x7c,0x40,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x7c,0x04,0x7c,0x04,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x78,0x24,0x64,0x3c,0x24,0x64,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x04,0x04,0x44,0x38,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x1e,0x1e,0x0c,0x0c,0x00,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x36,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x36,0x36,0x7f,0x36,0x7f,0x36,0x36,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x3e,0x03,0x1e,0x30,0x1f,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x36,0x33,0x18,0x0c,0x66,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x1c,0x36,0x1c,0x6e,0x3b,0x33,0x6e,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x0c,0x06,0x06,0x06,0x0c,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x0c,0x18,0x18,0x18,0x0c,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x63,0x63,0x6b,0x63,0x63,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x0e,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x30,0x1c,0x06,0x33,0x3f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x30,0x1c,0x30,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x3c,0x36,0x33,0x7f,0x30,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x3f,0x03,0x1f,0x30,0x30,0x30,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x1c,0x06,0x03,0x1f,0x33,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3f,0x33,0x30,0x18,0x0c,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x33,0x1e,0x33,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x33,0x3e,0x30,0x18,0x0e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00,0x00 }, + { 0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3f,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x30,0x18,0x0c,0x00,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x63,0x7b,0x7b,0x7b,0x03,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x1e,0x33,0x33,0x3f,0x33,0x33,0x00,0x00,0x00,0x00,0x00 }, + { 0x3f,0x66,0x66,0x3e,0x66,0x66,0x3f,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x66,0x03,0x03,0x03,0x66,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x1f,0x36,0x66,0x66,0x66,0x36,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x7f,0x46,0x16,0x1e,0x16,0x46,0x7f,0x00,0x00,0x00,0x00,0x00 }, + { 0x7f,0x46,0x16,0x1e,0x16,0x06,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x66,0x03,0x03,0x73,0x66,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x33,0x33,0x33,0x3f,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x67,0x66,0x36,0x1e,0x36,0x66,0x67,0x00,0x00,0x00,0x00,0x00 }, + { 0x0f,0x06,0x06,0x06,0x46,0x66,0x7f,0x00,0x00,0x00,0x00,0x00 }, + { 0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x63,0x67,0x6f,0x7b,0x73,0x63,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3f,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x33,0x33,0x3b,0x1e,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x3f,0x66,0x66,0x3e,0x36,0x66,0x67,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x33,0x07,0x0e,0x38,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x33,0x33,0x33,0x33,0x33,0x33,0x3f,0x00,0x00,0x00,0x00,0x00 }, + { 0x33,0x33,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x33,0x33,0x33,0x1e,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x7f,0x63,0x31,0x18,0x4c,0x66,0x7f,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x0c,0x18,0x30,0x60,0xc0,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00 }, + { 0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1e,0x30,0x3e,0x33,0x6e,0x00,0x00,0x00,0x00,0x00 }, + { 0x07,0x06,0x3e,0x66,0x66,0x66,0x3b,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1e,0x33,0x03,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x30,0x3e,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x1c,0x36,0x06,0x0f,0x06,0x06,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x00,0x00 }, + { 0x07,0x06,0x36,0x6e,0x66,0x66,0x67,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x07,0x06,0x66,0x36,0x1e,0x36,0x67,0x00,0x00,0x00,0x00,0x00 }, + { 0x0e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x33,0x7f,0x7f,0x6b,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1f,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1e,0x33,0x33,0x33,0x1e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3b,0x6e,0x66,0x06,0x0f,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3e,0x03,0x1e,0x30,0x1f,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x0c,0x3e,0x0c,0x0c,0x2c,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x33,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x33,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x3f,0x19,0x0c,0x26,0x3f,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x08,0x08,0x04,0x08,0x08,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x30,0x30,0x00,0x30,0x30,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x10,0x10,0x20,0x10,0x10,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x0c,0x92,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x7c,0xfe,0xfe,0x7c,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x6c,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x38,0x10,0xd6,0xfe,0xd6,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0xa5,0x81,0x99,0xa5,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x3c,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x9c,0x62,0x62,0x9c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x44,0x3c,0x44,0x44,0x3c,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x86,0x48,0x28,0x18,0x08,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x08,0x30,0x50,0x48,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x60,0x10,0x08,0x7c,0x08,0x10,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x68,0x60,0x10,0x08,0x38,0x40,0x30,0x00,0x00,0x00,0x00,0x00 }, + { 0x34,0x4a,0x48,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x44,0x7c,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x02,0x12,0x0a,0x06,0x0a,0x52,0x22,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x08,0x08,0x08,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x24,0x24,0x24,0x24,0x5c,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x04,0x18,0x04,0x38,0x40,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x2a,0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x24,0x1c,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x12,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x26,0x24,0x24,0x24,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x54,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x46,0x28,0x10,0x28,0xc4,0x00,0x00,0x00,0x00,0x00 }, + { 0x92,0x54,0x54,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x44,0x82,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x82,0x82,0xc6,0x44,0xc6,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x08,0x08,0x08,0x0a,0x0c,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x7e,0x04,0x08,0x30,0x08,0x04,0x7e,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x28,0x44,0xfe,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x80,0x40,0xfe,0x10,0xfe,0x04,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x7c,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x4a,0x24,0x10,0x48,0xa4,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x82,0x82,0xfe,0x44,0x44,0xc6,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x12,0x0a,0x06,0x02,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x04,0x38,0x44,0x38,0x40,0x3c,0x00,0x00,0x00,0x00,0x00 }, + { 0x44,0xaa,0x54,0x28,0x54,0xaa,0x44,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x99,0x85,0x85,0x99,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x42,0x24,0x18,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x50,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x5e,0xa5,0xa5,0x9d,0x95,0x66,0x3c,0x00,0x00,0x00,0x00 }, + { 0xfa,0x06,0xc6,0x46,0x26,0xde,0x06,0xfa,0x00,0x00,0x00,0x00 }, + { 0xff,0x20,0xc0,0x3f,0x40,0x3f,0x20,0x1f,0x00,0x00,0x00,0x00 }, + { 0x3f,0x40,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x1e,0x22,0x22,0x1e,0x52,0x22,0xd2,0x00,0x00,0x00,0x00,0x00 }, + { 0x06,0x41,0x21,0x16,0x68,0x94,0x92,0x60,0x00,0x00,0x00,0x00 }, + { 0x70,0x60,0x50,0x0e,0x09,0x09,0x06,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x44,0x44,0x38,0x10,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x70,0x10,0x10,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0xff,0xc7,0xbb,0xcf,0xef,0xff,0xef,0xff,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x38,0x54,0x10,0x28,0x44,0x00,0x00,0x00,0x00 }, + { 0x10,0x28,0x10,0x38,0x54,0x28,0x7c,0x28,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x38,0x28,0x28,0x6c,0xfe,0xc6,0x00,0x00,0x00,0x00 }, + { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xf3,0xe1,0xe1,0xf3,0xf3,0xff,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0xc9,0xc9,0xc9,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xc9,0xc9,0x80,0xc9,0x80,0xc9,0xc9,0xff,0x00,0x00,0x00,0x00 }, + { 0xf3,0xc1,0xfc,0xe1,0xcf,0xe0,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xc9,0xcc,0xe7,0xf3,0x99,0x9c,0xff,0x00,0x00,0x00,0x00 }, + { 0xe3,0xc9,0xe3,0x91,0xc4,0xcc,0x91,0xff,0x00,0x00,0x00,0x00 }, + { 0xf9,0xf9,0xfc,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xe7,0xf3,0xf9,0xf9,0xf9,0xf3,0xe7,0xff,0x00,0x00,0x00,0x00 }, + { 0xf9,0xf3,0xe7,0xe7,0xe7,0xf3,0xf9,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0x99,0xc3,0x00,0xc3,0x99,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xf3,0xf3,0xc0,0xf3,0xf3,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xff,0xff,0xff,0xff,0xf3,0xf3,0xf9,0x00,0x00,0x00,0x00 }, + { 0xff,0xff,0xff,0xc0,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xff,0xff,0xff,0xff,0xf3,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0xff,0x00,0x00,0x00,0x00 }, + { 0xc1,0x9c,0x9c,0x94,0x9c,0x9c,0xc1,0xff,0x00,0x00,0x00,0x00 }, + { 0xf3,0xf1,0xf3,0xf3,0xf3,0xf3,0xc0,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xcf,0xe3,0xf9,0xcc,0xc0,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xcf,0xe3,0xcf,0xcc,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xc7,0xc3,0xc9,0xcc,0x80,0xcf,0x87,0xff,0x00,0x00,0x00,0x00 }, + { 0xc0,0xfc,0xe0,0xcf,0xcf,0xcf,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xe3,0xf9,0xfc,0xe0,0xcc,0xcc,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xc0,0xcc,0xcf,0xe7,0xf3,0xf3,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xcc,0xe1,0xcc,0xcc,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xcc,0xc1,0xcf,0xe7,0xf1,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xf3,0xf3,0xff,0xff,0xf3,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xf3,0xf3,0xff,0xff,0xf3,0xf3,0xf9,0x00,0x00,0x00,0x00 }, + { 0xe7,0xf3,0xf9,0xfc,0xf9,0xf3,0xe7,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xff,0xc0,0xff,0xc0,0xff,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0xf9,0xf3,0xe7,0xcf,0xe7,0xf3,0xf9,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xcf,0xe7,0xf3,0xff,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0xc1,0x9c,0x84,0x84,0x84,0xfc,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xf3,0xe1,0xcc,0xcc,0xc0,0xcc,0xcc,0xff,0x00,0x00,0x00,0x00 }, + { 0xc0,0x99,0x99,0xc1,0x99,0x99,0xc0,0xff,0x00,0x00,0x00,0x00 }, + { 0xc3,0x99,0xfc,0xfc,0xfc,0x99,0xc3,0xff,0x00,0x00,0x00,0x00 }, + { 0xe0,0xc9,0x99,0x99,0x99,0xc9,0xe0,0xff,0x00,0x00,0x00,0x00 }, + { 0x80,0xb9,0xe9,0xe1,0xe9,0xb9,0x80,0xff,0x00,0x00,0x00,0x00 }, + { 0x80,0xb9,0xe9,0xe1,0xe9,0xf9,0xf0,0xff,0x00,0x00,0x00,0x00 }, + { 0xc3,0x99,0xfc,0xfc,0x8c,0x99,0xc3,0xff,0x00,0x00,0x00,0x00 }, + { 0xcc,0xcc,0xcc,0xc0,0xcc,0xcc,0xcc,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xf3,0xf3,0xf3,0xf3,0xf3,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0x87,0xcf,0xcf,0xcf,0xcc,0xcc,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0x98,0x99,0xc9,0xe1,0xc9,0x99,0x98,0xff,0x00,0x00,0x00,0x00 }, + { 0xf0,0xf9,0xf9,0xf9,0xb9,0x99,0x80,0xff,0x00,0x00,0x00,0x00 }, + { 0x9c,0x88,0x80,0x80,0x94,0x9c,0x9c,0xff,0x00,0x00,0x00,0x00 }, + { 0x9c,0x98,0x90,0x84,0x8c,0x9c,0x9c,0xff,0x00,0x00,0x00,0x00 }, + { 0xc1,0x9c,0x9c,0x9c,0x9c,0x9c,0xc1,0xff,0x00,0x00,0x00,0x00 }, + { 0xc0,0x99,0x99,0xc1,0xf9,0xf9,0xf0,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xcc,0xcc,0xc4,0xe1,0xc7,0xff,0x00,0x00,0x00,0x00 }, + { 0xc0,0x99,0x99,0xc1,0xc9,0x99,0x98,0xff,0x00,0x00,0x00,0x00 }, + { 0xe1,0xcc,0xf8,0xf1,0xc7,0xcc,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xc0,0xd2,0xf3,0xf3,0xf3,0xf3,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xc0,0xff,0x00,0x00,0x00,0x00 }, + { 0xcc,0xcc,0xcc,0xcc,0xcc,0xe1,0xf3,0xff,0x00,0x00,0x00,0x00 }, + { 0x9c,0x9c,0x9c,0x94,0x80,0x88,0x9c,0xff,0x00,0x00,0x00,0x00 }, + { 0x9c,0x9c,0xc9,0xe3,0xe3,0xc9,0x9c,0xff,0x00,0x00,0x00,0x00 }, + { 0xcc,0xcc,0xcc,0xe1,0xf3,0xf3,0xe1,0xff,0x00,0x00,0x00,0x00 }, + { 0x80,0x9c,0xce,0xe7,0xb3,0x99,0x80,0xff,0x00,0x00,0x00,0x00 }, + { 0xc3,0xfb,0xfb,0xfb,0xfb,0xfb,0xc3,0xff,0x00,0x00,0x00,0x00 }, + { 0xff,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f,0xff,0x00,0x00,0x00,0x00 }, + { 0xc3,0xdf,0xdf,0xdf,0xdf,0xdf,0xc3,0xff,0x00,0x00,0x00,0x00 }, + { 0xef,0xc7,0x93,0x39,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00 }, + { 0x1c,0x2a,0x3e,0x14,0x1c,0x63,0x1c,0x63,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 7 - for Model 4/4P */ + /* Source: A model III CG ROM, read by Todd P. Cromwell III (todd2.bin) */ + /* This is the older CG with Katakana alternate characters */ + /* Letters shifted 1 line down by mann to match M4 style */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x3e,0x08,0x48,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x7e,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0xb8,0x44,0x64,0x54,0x4c,0x44,0x3a,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x34,0x4c,0x44,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x5e,0x22,0x22,0x1e,0x12,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x28,0x00,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x44,0x4c,0x54,0x64,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x28,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x90,0x68,0x64,0x54,0x4c,0x2c,0x12,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x14,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x02,0x3e,0x42,0x7c,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x7c,0x04,0x7c,0x04,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x78,0x24,0x64,0x3c,0x24,0x64,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x04,0x04,0x44,0x38,0x10,0x08,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x7e,0x24,0x7e,0x24,0x24,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x78,0x14,0x38,0x50,0x3c,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x46,0x26,0x10,0x08,0x64,0x62,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x12,0x12,0x0c,0x52,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x54,0x38,0x7c,0x38,0x54,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x62,0x5a,0x46,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x18,0x14,0x10,0x10,0x10,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x40,0x30,0x0c,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x40,0x38,0x40,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x30,0x28,0x24,0x7e,0x20,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x02,0x1e,0x20,0x40,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x04,0x02,0x3e,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x42,0x20,0x10,0x08,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x7c,0x40,0x20,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00 }, + { 0x00,0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x40,0x30,0x08,0x00,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x52,0x6a,0x32,0x04,0x78,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x02,0x02,0x02,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x24,0x44,0x44,0x44,0x24,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x02,0x02,0x1e,0x02,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x02,0x02,0x1e,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x02,0x72,0x42,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x70,0x20,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x22,0x12,0x0e,0x12,0x22,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x66,0x5a,0x5a,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x46,0x4a,0x52,0x62,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x42,0x42,0x3e,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x42,0x52,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x42,0x42,0x3e,0x12,0x22,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x02,0x3c,0x40,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x24,0x24,0x18,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x5a,0x5a,0x66,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x44,0x44,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x20,0x18,0x04,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x3a,0x46,0x42,0x46,0x3a,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x40,0x5c,0x62,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x3e,0x08,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x00,0x30,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x22,0x12,0x0a,0x16,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6e,0x92,0x92,0x92,0x92,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3a,0x46,0x46,0x3a,0x02,0x02,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x40,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3a,0x46,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x02,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x08,0x3e,0x08,0x08,0x48,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x82,0x92,0x92,0x92,0x6c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x42,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x20,0x18,0x04,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x08,0x08,0x04,0x08,0x08,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x10,0x10,0x20,0x10,0x10,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x92,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x7c,0xfe,0xfe,0x7c,0x10,0x10,0x00,0x00,0x00 }, + { 0x00,0x00,0x6c,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x38,0x10,0xd6,0xfe,0xd6,0x10,0x38,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xa5,0x81,0x99,0xa5,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x3c,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x3c,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x9c,0x62,0x62,0x9c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x44,0x3c,0x44,0x44,0x3c,0x04,0x02,0x00,0x00,0x00 }, + { 0x00,0x86,0x48,0x28,0x18,0x08,0x0c,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x30,0x50,0x48,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x60,0x10,0x08,0x7c,0x08,0x10,0x60,0x00,0x00,0x00,0x00 }, + { 0x00,0x68,0x60,0x10,0x08,0x38,0x40,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x34,0x4a,0x48,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x7c,0x44,0x28,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x12,0x0a,0x06,0x0a,0x52,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x08,0x08,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x24,0x24,0x5c,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x4c,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x04,0x18,0x04,0x38,0x40,0x30,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x2a,0x28,0x28,0x28,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x24,0x1c,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x12,0x10,0x10,0x10,0x10,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x26,0x24,0x24,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x54,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x46,0x28,0x10,0x28,0xc4,0x00,0x00,0x00,0x00 }, + { 0x00,0x92,0x54,0x54,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x44,0x82,0x92,0x92,0x6c,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x82,0x82,0xc6,0x44,0xc6,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x08,0x08,0x08,0x0a,0x0c,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x04,0x08,0x30,0x08,0x04,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x32,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x28,0x44,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x08,0x10,0x10,0x08,0x04,0x00,0x00,0x00 }, + { 0x00,0x80,0x40,0xfe,0x10,0xfe,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x20,0x7c,0x08,0x10,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0xfc,0x4a,0x24,0x10,0x48,0xa4,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x82,0x82,0xfe,0x44,0x44,0xc6,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x20,0x12,0x0a,0x06,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x04,0x38,0x44,0x38,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x44,0xaa,0x54,0x28,0x54,0xaa,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xb9,0x85,0x85,0xb9,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x42,0x24,0x18,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x50,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x5e,0xa5,0xa5,0x9d,0x95,0x66,0x3c,0x00,0x00,0x00 }, + { 0x00,0xfa,0x06,0xc6,0x46,0x26,0xde,0x06,0xfa,0x00,0x00,0x00 }, + { 0x00,0xff,0x20,0xc0,0x3f,0x40,0x3f,0x20,0x1f,0x00,0x00,0x00 }, + { 0x00,0x3f,0x40,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x22,0x22,0x1e,0x52,0x22,0xd2,0x00,0x00,0x00,0x00 }, + { 0x00,0x86,0x41,0x21,0x16,0x68,0x94,0x92,0x61,0x00,0x00,0x00 }, + { 0x00,0x70,0x60,0x50,0x0e,0x09,0x09,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x44,0x44,0x38,0x10,0x38,0x10,0x00,0x00,0x00 }, + { 0x00,0x70,0x10,0x10,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0xff,0xc7,0xbb,0xcf,0xef,0xff,0xef,0xff,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x38,0x54,0x10,0x28,0x44,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x38,0x54,0x28,0x7c,0x28,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x44,0x44,0x54,0x6c,0x44,0x00,0x00,0x00 }, + { 0x00,0x44,0x28,0x10,0x7c,0x10,0x7c,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x04,0x0a,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x04,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x02,0x04,0x08,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x20,0x3c,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x40,0x30,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x20,0x10,0x18,0x14,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x7c,0x44,0x40,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x38,0x10,0x10,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x3c,0x18,0x14,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x08,0x7c,0x48,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x38,0x20,0x20,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x40,0x78,0x40,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x54,0x54,0x44,0x20,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x28,0x18,0x08,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x10,0x18,0x14,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x7c,0x44,0x44,0x20,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x7e,0x10,0x18,0x14,0x12,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x7e,0x48,0x48,0x48,0x44,0x72,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x10,0x7c,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x44,0x44,0x42,0x20,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x7c,0x14,0x12,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x40,0x40,0x40,0x40,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x7e,0x24,0x24,0x20,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x40,0x4e,0x40,0x40,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x20,0x10,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x7e,0x48,0x28,0x08,0x48,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x44,0x48,0x20,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x42,0x42,0x50,0x20,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x50,0x3e,0x10,0x7c,0x10,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x00,0x7e,0x40,0x20,0x10,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x00,0x7c,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x1c,0x24,0x44,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x7c,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x40,0x28,0x10,0x28,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x7e,0x40,0x20,0x30,0x58,0x14,0x00,0x00,0x00,0x00 }, + { 0x00,0x60,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x20,0x50,0x50,0x50,0x48,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x7e,0x02,0x02,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x40,0x20,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x08,0x14,0x22,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x7c,0x10,0x54,0x54,0x54,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x40,0x28,0x10,0x20,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x3c,0x42,0x3c,0x42,0x3c,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x04,0x12,0x22,0x5e,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x44,0x28,0x10,0x28,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x08,0x3c,0x08,0x08,0x48,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x7e,0x48,0x28,0x08,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x20,0x20,0x20,0x10,0x08,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x40,0x7c,0x40,0x40,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x54,0x54,0x44,0x40,0x20,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x42,0x22,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x0a,0x0a,0x0a,0x4a,0x4a,0x2a,0x1a,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x04,0x04,0x44,0x44,0x24,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x42,0x42,0x42,0x42,0x42,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x42,0x42,0x40,0x20,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x4e,0x40,0x40,0x40,0x20,0x12,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x12,0x24,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x0a,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 8 - for Model 4/4P */ + /* Source: Reed M4 emulator (reedm4l.xbm, reedm4la.xbm). Checked by + eye against my Model 4P. I should really pull out the ROM and read + it directly to be sure this is right. Apologies to Matthew Reed. */ + /* This is the newer CG with international alternate characters. + Characters 0-31 are also different from the older CG. */ + /* Letters start one line from top of cell, Model 4 style */ + { 0x00,0x24,0x18,0x24,0x42,0x7e,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x38,0x44,0x3c,0x44,0x44,0x3c,0x02,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x04,0x0e,0x04,0x44,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x02,0x3c,0x42,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x14,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x08,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x82,0xfe,0x00,0x00,0x00 }, + { 0x00,0x00,0x1c,0x20,0x20,0x14,0x0c,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x42,0x3c,0x24,0x24,0x3c,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x00,0x00,0x00 }, + { 0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x38,0x38,0x28,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x3e,0x08,0x08,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x7e,0x24,0x7e,0x24,0x24,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x78,0x14,0x38,0x50,0x3c,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x46,0x26,0x10,0x08,0x64,0x62,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x12,0x12,0x0c,0x52,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x10,0x10,0x10,0x08,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x54,0x38,0x7c,0x38,0x54,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x62,0x5a,0x46,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x18,0x14,0x10,0x10,0x10,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x40,0x30,0x0c,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x40,0x38,0x40,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x30,0x28,0x24,0x7e,0x20,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x02,0x1e,0x20,0x40,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x04,0x02,0x3e,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x42,0x20,0x10,0x08,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x7c,0x40,0x20,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x10,0x08,0x00,0x00,0x00 }, + { 0x00,0x60,0x30,0x18,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x40,0x30,0x08,0x00,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x52,0x6a,0x32,0x04,0x78,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x02,0x02,0x02,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x24,0x44,0x44,0x44,0x24,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x02,0x02,0x1e,0x02,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x02,0x02,0x1e,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x02,0x72,0x42,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x7e,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x70,0x20,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x22,0x12,0x0e,0x12,0x22,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x66,0x5a,0x5a,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x46,0x4a,0x52,0x62,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x42,0x42,0x3e,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x42,0x42,0x52,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x42,0x42,0x3e,0x12,0x22,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x02,0x3c,0x40,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x24,0x24,0x18,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x42,0x5a,0x5a,0x66,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x42,0x42,0x24,0x18,0x24,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x44,0x44,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x40,0x20,0x18,0x04,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x3a,0x46,0x42,0x46,0x3a,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x40,0x5c,0x62,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x3e,0x08,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x00,0x30,0x20,0x20,0x20,0x22,0x1c,0x00,0x00,0x00 }, + { 0x00,0x02,0x02,0x22,0x12,0x0a,0x16,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6e,0x92,0x92,0x92,0x92,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3a,0x46,0x42,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3a,0x46,0x46,0x3a,0x02,0x02,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x5c,0x62,0x62,0x5c,0x40,0x40,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3a,0x46,0x02,0x02,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x02,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x08,0x3e,0x08,0x08,0x48,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x82,0x92,0x92,0x92,0x6c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x42,0x42,0x62,0x5c,0x40,0x3c,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7e,0x20,0x18,0x04,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x08,0x08,0x04,0x08,0x08,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x10,0x10,0x20,0x10,0x10,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x92,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x7c,0xfe,0xfe,0x7c,0x10,0x10,0x00,0x00,0x00 }, + { 0x00,0x00,0x6c,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x38,0x10,0xd6,0xfe,0xd6,0x10,0x38,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xa5,0x81,0x99,0xa5,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x3c,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x3c,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x9c,0x62,0x62,0x9c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x44,0x3c,0x44,0x44,0x3c,0x04,0x02,0x00,0x00,0x00 }, + { 0x00,0x86,0x48,0x28,0x18,0x08,0x0c,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x30,0x50,0x48,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x60,0x10,0x08,0x7c,0x08,0x10,0x60,0x00,0x00,0x00,0x00 }, + { 0x00,0x68,0x60,0x10,0x08,0x38,0x40,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x34,0x4a,0x48,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x7c,0x44,0x28,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x12,0x0a,0x06,0x0a,0x52,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x08,0x08,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x24,0x24,0x5c,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x4c,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x04,0x18,0x04,0x38,0x40,0x30,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x2a,0x28,0x28,0x28,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x24,0x1c,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x12,0x10,0x10,0x10,0x10,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x26,0x24,0x24,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x54,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x46,0x28,0x10,0x28,0xc4,0x00,0x00,0x00,0x00 }, + { 0x00,0x92,0x54,0x54,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x44,0x82,0x92,0x92,0x6c,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x82,0x82,0xc6,0x44,0xc6,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x08,0x08,0x08,0x0a,0x0c,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x04,0x08,0x30,0x08,0x04,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x32,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x28,0x44,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x08,0x10,0x10,0x08,0x04,0x00,0x00,0x00 }, + { 0x00,0x80,0x40,0xfe,0x10,0xfe,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x20,0x7c,0x08,0x10,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0xfc,0x4a,0x24,0x10,0x48,0xa4,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x82,0x82,0xfe,0x44,0x44,0xc6,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x20,0x12,0x0a,0x06,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x04,0x38,0x44,0x38,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x44,0xaa,0x54,0x28,0x54,0xaa,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xb9,0x85,0x85,0xb9,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x42,0x24,0x18,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x50,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x5e,0xa5,0xa5,0x9d,0x95,0x66,0x3c,0x00,0x00,0x00 }, + { 0x00,0xfa,0x06,0xc6,0x46,0x26,0xde,0x06,0xfa,0x00,0x00,0x00 }, + { 0x00,0xff,0x20,0xc0,0x3f,0x40,0x3f,0x20,0x1f,0x00,0x00,0x00 }, + { 0x00,0x3f,0x40,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x22,0x22,0x1e,0x52,0x22,0xd2,0x00,0x00,0x00,0x00 }, + { 0x00,0x86,0x41,0x21,0x16,0x68,0x94,0x92,0x61,0x00,0x00,0x00 }, + { 0x00,0x70,0x60,0x50,0x0e,0x09,0x09,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x44,0x44,0x38,0x10,0x38,0x10,0x00,0x00,0x00 }, + { 0x00,0x70,0x10,0x10,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0xff,0xc7,0xbb,0xcf,0xef,0xff,0xef,0xff,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x38,0x54,0x10,0x28,0x44,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x38,0x54,0x28,0x7c,0x28,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x44,0x44,0x54,0x6c,0x44,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x42,0x02,0x42,0x3c,0x08,0x00,0x00,0x00 }, + { 0x00,0x18,0x24,0x04,0x0e,0x04,0x44,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x44,0x44,0x44,0x64,0x5c,0x02,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x1c,0x14,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x08,0x1c,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x02,0x3c,0x42,0x3c,0x40,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x9d,0xa5,0x9d,0xa5,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xb9,0x85,0x85,0xb9,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x04,0x44,0x24,0x14,0x08,0x54,0x72,0x40,0x00,0x00,0x00 }, + { 0x00,0x06,0x44,0x26,0x14,0x0e,0x54,0x72,0x40,0x00,0x00,0x00 }, + { 0x00,0x04,0x44,0x24,0x14,0x68,0x44,0x22,0x60,0x00,0x00,0x00 }, + { 0x00,0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x00,0x00,0x00,0x00 }, + { 0x00,0x44,0x28,0x10,0x7c,0x10,0x7c,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x18,0x24,0x42,0x7e,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x38,0x44,0x3c,0x44,0x44,0x3c,0x02,0x00,0x00,0x00 }, + { 0x00,0x00,0x07,0x52,0xaa,0xaa,0x8a,0x8a,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x3e,0x08,0x08,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x14,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x3a,0x46,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x14,0x12,0x72,0x1e,0x12,0x72,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x2e,0x50,0x7c,0x12,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x18,0x24,0x42,0x7e,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x62,0x5a,0x46,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3c,0x62,0x5a,0x46,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x46,0x4a,0x52,0x62,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x7e,0x02,0x1e,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x18,0x24,0x7e,0x42,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x38,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x3c,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x08,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x00,0x08,0x04,0x02,0x22,0x1c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x7e,0x02,0x1e,0x02,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x18,0x24,0x7e,0x42,0x42,0x00,0x00,0x00,0x00 }, +}, + +{ + /* CG 9 - for Model 4/4P */ + /* Source: a Model III CG ROM, read by Todd P. Cromwell III (todd2_4.bin) */ + /* I'm guessing this was a 3rd party replacement CG, not from Radio + Shack. The ROM was marked as "Model III/4 character + generator". The normal ASCII characters are bold, and alternates + are the upper case and numbers in inverse video. This ROM doesn't + seem especially suited for the Model 4, which had inverse video + in hardware. */ + /* Letters shifted 1 line down by mann to match M4 style */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x3e,0x08,0x48,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x3c,0x42,0x7e,0x02,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x7e,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x28,0x00,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0xb8,0x44,0x64,0x54,0x4c,0x44,0x3a,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x34,0x4c,0x44,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x5e,0x22,0x22,0x1e,0x12,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x28,0x00,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x10,0x28,0x44,0x7c,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x44,0x4c,0x54,0x64,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x28,0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x90,0x68,0x64,0x54,0x4c,0x2c,0x12,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x3c,0x42,0x42,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x44,0x44,0x3c,0x44,0x44,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x00,0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x4c,0x32,0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x54,0x50,0x38,0x14,0x54,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x14,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x00,0x1c,0x20,0x3c,0x22,0x5c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x02,0x3e,0x42,0x7c,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x7c,0x04,0x7c,0x04,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x78,0x24,0x64,0x3c,0x24,0x64,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x04,0x04,0x44,0x38,0x10,0x08,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x1e,0x1e,0x0c,0x0c,0x00,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x36,0x36,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x36,0x36,0x7f,0x36,0x7f,0x36,0x36,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x3e,0x03,0x1e,0x30,0x1f,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x36,0x33,0x18,0x0c,0x66,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x36,0x1c,0x6e,0x3b,0x33,0x6e,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x06,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x18,0x0c,0x06,0x06,0x06,0x0c,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x0c,0x18,0x18,0x18,0x0c,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0c,0x0c,0x3f,0x0c,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x63,0x63,0x6b,0x63,0x63,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x0e,0x0c,0x0c,0x0c,0x0c,0x3f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x30,0x1c,0x06,0x33,0x3f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x30,0x1c,0x30,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x3c,0x36,0x33,0x7f,0x30,0x78,0x00,0x00,0x00,0x00 }, + { 0x00,0x3f,0x03,0x1f,0x30,0x30,0x30,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x06,0x03,0x1f,0x33,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3f,0x33,0x30,0x18,0x0c,0x0c,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x33,0x1e,0x33,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x33,0x3e,0x30,0x18,0x0e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x0c,0x0c,0x00,0x00,0x0c,0x0c,0x06,0x00,0x00,0x00 }, + { 0x00,0x18,0x0c,0x06,0x03,0x06,0x0c,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3f,0x00,0x3f,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x0c,0x18,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x30,0x18,0x0c,0x00,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x63,0x7b,0x7b,0x7b,0x03,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x1e,0x33,0x33,0x3f,0x33,0x33,0x00,0x00,0x00,0x00 }, + { 0x00,0x3f,0x66,0x66,0x3e,0x66,0x66,0x3f,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x66,0x03,0x03,0x03,0x66,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x1f,0x36,0x66,0x66,0x66,0x36,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x7f,0x46,0x16,0x1e,0x16,0x46,0x7f,0x00,0x00,0x00,0x00 }, + { 0x00,0x7f,0x46,0x16,0x1e,0x16,0x06,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x66,0x03,0x03,0x73,0x66,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x33,0x33,0x33,0x3f,0x33,0x33,0x33,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x67,0x66,0x36,0x1e,0x36,0x66,0x67,0x00,0x00,0x00,0x00 }, + { 0x00,0x0f,0x06,0x06,0x06,0x46,0x66,0x7f,0x00,0x00,0x00,0x00 }, + { 0x00,0x63,0x77,0x7f,0x7f,0x6b,0x63,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x63,0x67,0x6f,0x7b,0x73,0x63,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x3e,0x63,0x63,0x63,0x63,0x63,0x3e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3f,0x66,0x66,0x3e,0x06,0x06,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x33,0x33,0x3b,0x1e,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x3f,0x66,0x66,0x3e,0x36,0x66,0x67,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x33,0x07,0x0e,0x38,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x3f,0x2d,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x33,0x33,0x33,0x33,0x33,0x33,0x3f,0x00,0x00,0x00,0x00 }, + { 0x00,0x33,0x33,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x63,0x63,0x36,0x1c,0x1c,0x36,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x33,0x33,0x33,0x1e,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x7f,0x63,0x31,0x18,0x4c,0x66,0x7f,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x04,0x04,0x04,0x04,0x04,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0xc0,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x20,0x20,0x20,0x20,0x20,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00 }, + { 0x00,0x18,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1e,0x30,0x3e,0x33,0x6e,0x00,0x00,0x00,0x00 }, + { 0x00,0x07,0x06,0x3e,0x66,0x66,0x66,0x3b,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1e,0x33,0x03,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x30,0x3e,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1e,0x33,0x3f,0x03,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x1c,0x36,0x06,0x0f,0x06,0x06,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x00 }, + { 0x00,0x07,0x06,0x36,0x6e,0x66,0x66,0x67,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x00,0x0e,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x00,0x30,0x30,0x30,0x33,0x33,0x1e,0x00,0x00,0x00 }, + { 0x00,0x07,0x06,0x66,0x36,0x1e,0x36,0x67,0x00,0x00,0x00,0x00 }, + { 0x00,0x0e,0x0c,0x0c,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x33,0x7f,0x7f,0x6b,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1f,0x33,0x33,0x33,0x33,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x1e,0x33,0x33,0x33,0x1e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3b,0x66,0x66,0x3e,0x06,0x0f,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6e,0x33,0x33,0x3e,0x30,0x78,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3b,0x6e,0x66,0x06,0x0f,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3e,0x03,0x1e,0x30,0x1f,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x0c,0x3e,0x0c,0x0c,0x2c,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x33,0x33,0x33,0x33,0x6e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x33,0x33,0x33,0x1e,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x63,0x6b,0x7f,0x7f,0x36,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x63,0x36,0x1c,0x36,0x63,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x33,0x33,0x33,0x3e,0x30,0x1f,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x3f,0x19,0x0c,0x26,0x3f,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x08,0x08,0x04,0x08,0x08,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x30,0x30,0x00,0x30,0x30,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x10,0x10,0x20,0x10,0x10,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x0c,0x92,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x7c,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x7c,0xfe,0xfe,0x7c,0x10,0x10,0x00,0x00,0x00 }, + { 0x00,0x00,0x6c,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x38,0x10,0xd6,0xfe,0xd6,0x10,0x38,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xa5,0x81,0xa5,0x99,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0xa5,0x81,0x99,0xa5,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x3c,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x10,0x20,0x10,0x08,0x04,0x3c,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x9c,0x62,0x62,0x9c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x44,0x3c,0x44,0x44,0x3c,0x04,0x02,0x00,0x00,0x00 }, + { 0x00,0x86,0x48,0x28,0x18,0x08,0x0c,0x0c,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x48,0x08,0x30,0x50,0x48,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x60,0x10,0x08,0x7c,0x08,0x10,0x60,0x00,0x00,0x00,0x00 }, + { 0x00,0x68,0x60,0x10,0x08,0x38,0x40,0x30,0x00,0x00,0x00,0x00 }, + { 0x00,0x34,0x4a,0x48,0x48,0x40,0x40,0x40,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x44,0x7c,0x44,0x28,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x04,0x04,0x04,0x44,0x44,0x38,0x00,0x00,0x00,0x00 }, + { 0x00,0x02,0x12,0x0a,0x06,0x0a,0x52,0x22,0x00,0x00,0x00,0x00 }, + { 0x00,0x04,0x08,0x08,0x08,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x24,0x24,0x24,0x24,0x5c,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x4c,0x48,0x28,0x18,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x04,0x18,0x04,0x38,0x40,0x30,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x2a,0x28,0x28,0x28,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x18,0x24,0x24,0x1c,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x12,0x12,0x0c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x7c,0x12,0x10,0x10,0x10,0x10,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x26,0x24,0x24,0x24,0x18,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x54,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x46,0x28,0x10,0x28,0xc4,0x00,0x00,0x00,0x00 }, + { 0x00,0x92,0x54,0x54,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x44,0x82,0x92,0x92,0x6c,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x82,0x82,0xc6,0x44,0xc6,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x08,0x08,0x08,0x0a,0x0c,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x10,0x00,0x7c,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x7e,0x04,0x08,0x30,0x08,0x04,0x7e,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x4c,0x32,0x00,0x4c,0x32,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x10,0x28,0x44,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x10,0x08,0x08,0x10,0x10,0x08,0x04,0x00,0x00,0x00 }, + { 0x00,0x80,0x40,0xfe,0x10,0xfe,0x04,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x10,0x20,0x7c,0x08,0x10,0x20,0x00,0x00,0x00,0x00 }, + { 0x00,0xfc,0x4a,0x24,0x10,0x48,0xa4,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x82,0x82,0xfe,0x44,0x44,0xc6,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x6c,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x40,0x20,0x12,0x0a,0x06,0x02,0x00,0x00,0x00,0x00 }, + { 0x00,0x78,0x04,0x38,0x44,0x38,0x40,0x3c,0x00,0x00,0x00,0x00 }, + { 0x00,0x44,0xaa,0x54,0x28,0x54,0xaa,0x44,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x42,0x99,0x85,0x85,0x99,0x42,0x3c,0x00,0x00,0x00 }, + { 0x00,0x42,0x24,0x18,0x24,0x18,0x24,0x42,0x00,0x00,0x00,0x00 }, + { 0x00,0x7c,0x52,0x52,0x5c,0x50,0x50,0x50,0x50,0x00,0x00,0x00 }, + { 0x00,0x10,0x38,0x54,0x14,0x54,0x38,0x10,0x00,0x00,0x00,0x00 }, + { 0x00,0x3c,0x5e,0xa5,0xa5,0x9d,0x95,0x66,0x3c,0x00,0x00,0x00 }, + { 0x00,0xfa,0x06,0xc6,0x46,0x26,0xde,0x06,0xfa,0x00,0x00,0x00 }, + { 0x00,0xff,0x20,0xc0,0x3f,0x40,0x3f,0x20,0x1f,0x00,0x00,0x00 }, + { 0x00,0x3f,0x40,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x1e,0x22,0x22,0x1e,0x52,0x22,0xd2,0x00,0x00,0x00,0x00 }, + { 0x00,0x06,0x41,0x21,0x16,0x68,0x94,0x92,0x60,0x00,0x00,0x00 }, + { 0x00,0x70,0x60,0x50,0x0e,0x09,0x09,0x06,0x00,0x00,0x00,0x00 }, + { 0x00,0x38,0x44,0x44,0x44,0x38,0x10,0x38,0x10,0x00,0x00,0x00 }, + { 0x00,0x70,0x10,0x10,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0xff,0xc7,0xbb,0xcf,0xef,0xff,0xef,0xff,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x38,0x54,0x10,0x28,0x44,0x00,0x00,0x00 }, + { 0x00,0x10,0x28,0x10,0x38,0x54,0x28,0x7c,0x28,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x38,0x28,0x28,0x6c,0xfe,0xc6,0x00,0x00,0x00 }, + { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xf3,0xe1,0xe1,0xf3,0xf3,0xff,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0xc9,0xc9,0xc9,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xc9,0xc9,0x80,0xc9,0x80,0xc9,0xc9,0xff,0x00,0x00,0x00 }, + { 0x00,0xf3,0xc1,0xfc,0xe1,0xcf,0xe0,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xc9,0xcc,0xe7,0xf3,0x99,0x9c,0xff,0x00,0x00,0x00 }, + { 0x00,0xe3,0xc9,0xe3,0x91,0xc4,0xcc,0x91,0xff,0x00,0x00,0x00 }, + { 0x00,0xf9,0xf9,0xfc,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xe7,0xf3,0xf9,0xf9,0xf9,0xf3,0xe7,0xff,0x00,0x00,0x00 }, + { 0x00,0xf9,0xf3,0xe7,0xe7,0xe7,0xf3,0xf9,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0x99,0xc3,0x00,0xc3,0x99,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xf3,0xf3,0xc0,0xf3,0xf3,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xff,0xff,0xff,0xff,0xf3,0xf3,0xf9,0x00,0x00,0x00 }, + { 0x00,0xff,0xff,0xff,0xc0,0xff,0xff,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xff,0xff,0xff,0xff,0xf3,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0xff,0x00,0x00,0x00 }, + { 0x00,0xc1,0x9c,0x9c,0x94,0x9c,0x9c,0xc1,0xff,0x00,0x00,0x00 }, + { 0x00,0xf3,0xf1,0xf3,0xf3,0xf3,0xf3,0xc0,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xcf,0xe3,0xf9,0xcc,0xc0,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xcf,0xe3,0xcf,0xcc,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xc7,0xc3,0xc9,0xcc,0x80,0xcf,0x87,0xff,0x00,0x00,0x00 }, + { 0x00,0xc0,0xfc,0xe0,0xcf,0xcf,0xcf,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xe3,0xf9,0xfc,0xe0,0xcc,0xcc,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xc0,0xcc,0xcf,0xe7,0xf3,0xf3,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xcc,0xe1,0xcc,0xcc,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xcc,0xc1,0xcf,0xe7,0xf1,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xf3,0xf3,0xff,0xff,0xf3,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xf3,0xf3,0xff,0xff,0xf3,0xf3,0xf9,0x00,0x00,0x00 }, + { 0x00,0xe7,0xf3,0xf9,0xfc,0xf9,0xf3,0xe7,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xff,0xc0,0xff,0xc0,0xff,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0xf9,0xf3,0xe7,0xcf,0xe7,0xf3,0xf9,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xcf,0xe7,0xf3,0xff,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0xc1,0x9c,0x84,0x84,0x84,0xfc,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xf3,0xe1,0xcc,0xcc,0xc0,0xcc,0xcc,0xff,0x00,0x00,0x00 }, + { 0x00,0xc0,0x99,0x99,0xc1,0x99,0x99,0xc0,0xff,0x00,0x00,0x00 }, + { 0x00,0xc3,0x99,0xfc,0xfc,0xfc,0x99,0xc3,0xff,0x00,0x00,0x00 }, + { 0x00,0xe0,0xc9,0x99,0x99,0x99,0xc9,0xe0,0xff,0x00,0x00,0x00 }, + { 0x00,0x80,0xb9,0xe9,0xe1,0xe9,0xb9,0x80,0xff,0x00,0x00,0x00 }, + { 0x00,0x80,0xb9,0xe9,0xe1,0xe9,0xf9,0xf0,0xff,0x00,0x00,0x00 }, + { 0x00,0xc3,0x99,0xfc,0xfc,0x8c,0x99,0xc3,0xff,0x00,0x00,0x00 }, + { 0x00,0xcc,0xcc,0xcc,0xc0,0xcc,0xcc,0xcc,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xf3,0xf3,0xf3,0xf3,0xf3,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0x87,0xcf,0xcf,0xcf,0xcc,0xcc,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0x98,0x99,0xc9,0xe1,0xc9,0x99,0x98,0xff,0x00,0x00,0x00 }, + { 0x00,0xf0,0xf9,0xf9,0xf9,0xb9,0x99,0x80,0xff,0x00,0x00,0x00 }, + { 0x00,0x9c,0x88,0x80,0x80,0x94,0x9c,0x9c,0xff,0x00,0x00,0x00 }, + { 0x00,0x9c,0x98,0x90,0x84,0x8c,0x9c,0x9c,0xff,0x00,0x00,0x00 }, + { 0x00,0xc1,0x9c,0x9c,0x9c,0x9c,0x9c,0xc1,0xff,0x00,0x00,0x00 }, + { 0x00,0xc0,0x99,0x99,0xc1,0xf9,0xf9,0xf0,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xcc,0xcc,0xc4,0xe1,0xc7,0xff,0x00,0x00,0x00 }, + { 0x00,0xc0,0x99,0x99,0xc1,0xc9,0x99,0x98,0xff,0x00,0x00,0x00 }, + { 0x00,0xe1,0xcc,0xf8,0xf1,0xc7,0xcc,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xc0,0xd2,0xf3,0xf3,0xf3,0xf3,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0xc0,0xff,0x00,0x00,0x00 }, + { 0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xe1,0xf3,0xff,0x00,0x00,0x00 }, + { 0x00,0x9c,0x9c,0x9c,0x94,0x80,0x88,0x9c,0xff,0x00,0x00,0x00 }, + { 0x00,0x9c,0x9c,0xc9,0xe3,0xe3,0xc9,0x9c,0xff,0x00,0x00,0x00 }, + { 0x00,0xcc,0xcc,0xcc,0xe1,0xf3,0xf3,0xe1,0xff,0x00,0x00,0x00 }, + { 0x00,0x80,0x9c,0xce,0xe7,0xb3,0x99,0x80,0xff,0x00,0x00,0x00 }, + { 0x00,0xc3,0xfb,0xfb,0xfb,0xfb,0xfb,0xc3,0xff,0x00,0x00,0x00 }, + { 0x00,0xff,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f,0xff,0x00,0x00,0x00 }, + { 0x00,0xc3,0xdf,0xdf,0xdf,0xdf,0xdf,0xc3,0xff,0x00,0x00,0x00 }, + { 0x00,0xef,0xc7,0x93,0x39,0xff,0xff,0xff,0xff,0x00,0x00,0x00 }, + { 0x00,0x1c,0x2a,0x3e,0x14,0x1c,0x63,0x1c,0x63,0x00,0x00,0x00 }, +}, + +{ + /* CG 10 - charset including the German special chars ("Umlauts"). + * Version of CG 3 modified by Jenz Guenther to add the national + * characters used in the GENIE, a German TRS-80 clone. */ + { 0x7c,0x82,0x80,0x8c,0x92,0x92,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x84,0x84,0xfc,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x78,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x04,0x04,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x88,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0xf8,0x04,0x04,0xe4,0x84,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0xfc,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x80,0x80,0x80,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x44,0x24,0x1c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0xc6,0xaa,0x92,0x82,0x82,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x8c,0x94,0xa4,0xc4,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0xa4,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x78,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x82,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x92,0xaa,0xc6,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x48,0x30,0x48,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x40,0x20,0x10,0x08,0x04,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x38,0x54,0x92,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x92,0x54,0x38,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x08,0x04,0xfe,0x04,0x08,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x20,0x40,0xfe,0x40,0x20,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x48,0x48,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x48,0x48,0xfc,0x48,0xfc,0x48,0x48,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0xfc,0x12,0x7c,0x90,0x7e,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x8c,0x40,0x20,0x10,0x08,0xc4,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x44,0x28,0x18,0xa4,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x30,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x20,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x54,0x38,0xfe,0x38,0x54,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x10,0x10,0x7c,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x30,0x30,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x40,0x20,0x10,0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0xc4,0xa4,0x94,0x8c,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x18,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x80,0x78,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x80,0x70,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x40,0x60,0x50,0x48,0xfc,0x40,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x7c,0x80,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x70,0x08,0x04,0x7c,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x80,0x40,0x20,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x78,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0xf8,0x80,0x40,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x30,0x00,0x00,0x30,0x30,0x10,0x08,0x00,0x00,0x00,0x00 }, + { 0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xfc,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x3c,0x42,0x40,0x30,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x82,0x80,0x8c,0x92,0x92,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x30,0x48,0x84,0x84,0xfc,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x78,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x04,0x04,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x88,0x88,0x88,0x88,0x88,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0xfc,0x04,0x04,0x3c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0xf8,0x04,0x04,0xe4,0x84,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0xfc,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x38,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x80,0x80,0x80,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x44,0x24,0x1c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x04,0x04,0x04,0x04,0xfc,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0xc6,0xaa,0x92,0x82,0x82,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x8c,0x94,0xa4,0xc4,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x84,0x84,0xa4,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x7c,0x84,0x84,0x7c,0x24,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x78,0x84,0x04,0x78,0x80,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x82,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x82,0x92,0xaa,0xc6,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x84,0x48,0x30,0x48,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x82,0x82,0x44,0x38,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0xfe,0x40,0x20,0x10,0x08,0x04,0xfe,0x00,0x00,0x00,0x00,0x00 }, + { 0x84,0x30,0x48,0x84,0xfc,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, /* =C4 */ + { 0x84,0x00,0x78,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, /* =D6 */ + { 0x84,0x00,0x84,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, /* =DC */ + { 0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, /* - */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00 }, + { 0x08,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x80,0xf8,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x74,0x8c,0x84,0x8c,0x74,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0x04,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x80,0xb8,0xc4,0x84,0xc4,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0xfc,0x04,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x70,0x88,0x08,0x1c,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0x84,0xf8,0x80,0x78,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x74,0x8c,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x00,0x18,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x80,0x00,0xc0,0x80,0x80,0x84,0x84,0x78,0x00,0x00,0x00,0x00 }, + { 0x04,0x04,0x84,0x44,0x3c,0x44,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x18,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x6e,0x92,0x92,0x92,0x82,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x74,0x8c,0x84,0x84,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x78,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x74,0x8c,0x8c,0x74,0x04,0x04,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xb8,0xc4,0xc4,0xb8,0x80,0x80,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x74,0x8c,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0xf8,0x04,0x78,0x80,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x10,0x10,0x7c,0x10,0x10,0x90,0x60,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x44,0x44,0x44,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x82,0x82,0x44,0x28,0x10,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x82,0x92,0x92,0x92,0x6c,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x84,0x48,0x30,0x48,0x84,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x84,0x84,0x84,0xf8,0x80,0x78,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x7c,0x20,0x10,0x08,0x7c,0x00,0x00,0x00,0x00,0x00 }, + { 0x48,0x00,0x78,0x80,0xf8,0x84,0xf8,0x00,0x00,0x00,0x00,0x00 }, /* =E4 */ + { 0x48,0x00,0x78,0x84,0x84,0x84,0x78,0x00,0x00,0x00,0x00,0x00 }, /* =F6 */ + { 0x44,0x00,0x44,0x44,0x44,0x44,0xb8,0x00,0x00,0x00,0x00,0x00 }, /* =FC */ + { 0x38,0x44,0x44,0x74,0x84,0x84,0x74,0x00,0x00,0x00,0x00,0x00 }, /* =DF */ + { 0xaa,0x54,0xaa,0x54,0xaa,0x54,0xaa,0x54,0x00,0x00,0x00,0x00 }, +} + +}; diff --git a/trs_disk.c b/trs_disk.c new file mode 100644 index 0000000..a7f7b84 --- /dev/null +++ b/trs_disk.c @@ -0,0 +1,3565 @@ +/* Copyright (c) 1996-97, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue May 1 20:34:56 PDT 2001 by mann */ + +/* + * Emulate Model I or III/4 disk controller + */ + +/* + * Debug flags. Update help_message in debug.c if these change. + */ +#define DISKDEBUG_FDCREG (1<<0) /* FDC register reads and writes */ +#define DISKDEBUG_FDCCMD (1<<1) /* FDC commands */ +#define DISKDEBUG_VTOS3 (1<<2) /* VTOS 3.0 JV3 kludges */ +#define DISKDEBUG_GAPS (1<<3) /* Gaps and real_writetrk */ +#define DISKDEBUG_REALSIZE (1<<4) /* REAL sector size detection */ +#define DISKDEBUG_READADR (1<<5) /* Read Address timing */ +#define DISKDEBUG_DMK (1<<6) /* DMK support */ +#define DISKDEBUG_REALERR (1<<7) /* ioctl errors accessing real disks */ + +#define TSTATEREV 1 /* Index holes timed by T-states, not real time */ +#define SIZERETRY 1 /* Retry in different sizes on real_read */ +#define DMK_MARK_IAM 0 /* Mark IAMs in track header; poor idea */ + +#include "z80.h" +#include "trs.h" +#include "trs_disk.h" +#include "trs_hard.h" +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> + +#include "crc.c" + +#if __linux +#include <sys/types.h> +#include <fcntl.h> +#include <linux/fd.h> +#include <linux/fdreg.h> +#include <sys/ioctl.h> +#endif + +#define NDRIVES 8 + +int trs_disk_nocontroller = 0; +int trs_disk_doubler = TRSDISK_BOTH; +char *trs_disk_dir = DISKDIR; +unsigned short trs_disk_changecount = 0; +static int trs_disk_needchange = 0; +float trs_disk_holewidth = 0.01; +int trs_disk_truedam = 0; +int trs_disk_debug_flags = 0; + +typedef struct { + /* Registers */ + unsigned char status; + unsigned char track; + unsigned char sector; + unsigned char data; + /* Other state */ + unsigned char currcommand; + int lastdirection; + int bytecount; /* bytes left to transfer this command */ + int format; /* write track state machine */ + int format_bytecount; /* bytes left in this part of write track */ + int format_sec; /* current sector number or id_index */ + int format_gapcnt; /* measure requested gaps */ + int format_gap[5]; + unsigned short crc; + unsigned curdrive; + unsigned curside; + unsigned density; /* sden=0, dden=1 */ + unsigned char controller; /* TRSDISK_P1771 or TRSDISK_P1791 */ + int last_readadr; /* id index found by last readadr */ + tstate_t motor_timeout; /* 0 if stopped, else time when it stops */ +} FDCState; + +FDCState state, other_state; + +/* Format states - what is expected next? */ +#define FMT_GAP0 0 +#define FMT_IAM 1 +#define FMT_GAP1 2 +#define FMT_IDAM 3 +#define FMT_TRACKID 4 +#define FMT_HEADID 5 +#define FMT_SECID 6 +#define FMT_SIZEID 7 +#define FMT_IDCRC 8 +#define FMT_GAP2 9 +#define FMT_DAM 10 +#define FMT_DATA 11 +#define FMT_DCRC 12 +#define FMT_GAP3 13 +#define FMT_GAP4 14 +#define FMT_DONE 15 +#define FMT_PREAM 16 /* DDEN DMK only -- just saw preamble to an AM */ +#define FMT_IPREAM 17 /* DDEN DMK only -- just saw preamble to an IAM */ + + +/* Gap 0+1 and gap 4 angular size, used in Read Address timing emulation. + Units: fraction of a complete circle. */ +#define GAP1ANGLE 0.020 +#define GAP4ANGLE 0.050 + +/* How long does emulated motor stay on after drive selected? (us of + emulated time) */ +#define MOTOR_USEC 2000000 + +/* Heuristic: how often are we willing to check whether real drive + has a disk in it? (seconds of real time) */ +#define EMPTY_TIMEOUT 3 + +/* + * The following rather quirky data structure is designed to be + * compatible with what Jeff Vavasour's Model III/4 emulator uses to + * represent disk formatting information. My interpretation is based + * on reading his documentation, looking at some disk images, and + * experimenting with his emulator to generate odd cases, so the + * compatibility should be excellent. + * + * I have compatibly extended the format to allow for more sectors, so + * that 8" DSDD drives can be supported, by adding a second block of + * ids after the block of data sectors that is described by the first + * block. JV himself says that sounds like a good idea. Matthew + * Reed's emulators now support this extension. + * + * I've further extended the format to add a flag bit for non-IBM + * sectors. Only a subset of the non-IBM functionality is supported, + * rigged to make the VTOS 3.0 copy-protection system work. Non-IBM + * sectors were a feature of the 1771 only. Using this feature, you + * could have a sector of any length from 16 to 4096 bytes in + * multiples of 16. xtrs supports only 16-byte non-IBM sectors (with + * their data stored in a 256-byte field), and has some special kludges + * to detect and support VTOS's trick of formatting these sectors with + * inadequate gaps between so that writing to one would smash the + * index block of the next one. + * + * Finally, I've extended the format to support (IBM) sector lengths + * other than 256 bytes. The standard lengths 128, 256, 512, and 1024 + * are supported, using the last two available bits in the header + * flags byte. The data area of a floppy image thus becomes an array + * of *variable length* sectors, making it more complicated to find + * the sector data corresponding to a given header and to manage freed + * sectors. + * + * NB: JV's model I emulator uses no auxiliary data structure for disk + * format. It simply assumes that all disks are single density, 256 + * bytes/sector, 10 sectors/track, single sided, with nonstandard FA + * data address mark on all sectors on track 17. */ + +/* Values for flags below */ +#define JV3_DENSITY 0x80 /* 1=dden, 0=sden */ +#define JV3_DAM 0x60 /* data address mark; values follow */ +#define JV3_DAMSDFB 0x00 +#define JV3_DAMSDFA 0x20 +#define JV3_DAMSDF9 0x40 +#define JV3_DAMSDF8 0x60 +#define JV3_DAMDDFB 0x00 +#define JV3_DAMDDF8 0x20 +#define JV3_SIDE 0x10 /* 0=side 0, 1=side 1 */ +#define JV3_ERROR 0x08 /* 0=ok, 1=CRC error */ +#define JV3_NONIBM 0x04 /* 0=normal, 1=short (for VTOS 3.0, xtrs only) */ +#define JV3_SIZE 0x03 /* in used sectors: 0=256,1=128,2=1024,3=512 + in free sectors: 0=512,1=1024,2=128,3=256 */ + +#define JV3_FREE 0xff /* in track/sector fields */ +#define JV3_FREEF 0xfc /* in flags field, or'd with size code */ + +typedef struct { + unsigned char track; + unsigned char sector; + unsigned char flags; +} SectorId; + +#define MAXTRACKS 255 +#define JV1_SECSIZE 256 +#define MAXSECSIZE 1024 + +/* Max bytes per unformatted track. */ +/* Select codes 1, 2, 4, 8 are emulated 5" drives, disk?-0 to disk?-3 */ +#define TRKSIZE_SD 3125 /* 250kHz / 5 Hz [300rpm] / (2 * 8) */ + /* or 300kHz / 6 Hz [360rpm] / (2 * 8) */ +#define TRKSIZE_DD 6250 /* 250kHz / 5 Hz [300rpm] / 8 */ + /* or 300kHz / 6 Hz [360rpm] / 8 */ +/* Select codes 3, 5, 6, 7 are emulated 8" drives, disk?-4 to disk?-7 */ +#define TRKSIZE_8SD 5208 /* 500kHz / 6 Hz [360rpm] / (2 * 8) */ +#define TRKSIZE_8DD 10416 /* 500kHz / 6 Hz [360rpm] / 8 */ +/* TRS-80 software has no concept of HD, so these constants are unused, + * but expanded JV3 would be big enough even for 3.5" HD. */ +#define TRKSIZE_5HD 10416 /* 500kHz / 6 Hz [360rpm] / 8 */ +#define TRKSIZE_3HD 12500 /* 500kHz / 5 Hz [300rpm] / 8 */ + +#define JV3_SIDES 2 +#define JV3_IDSTART 0 +#define JV3_SECSTART (34*256) /* start of sectors within file */ +#define JV3_SECSPERBLK ((int)(JV3_SECSTART/3)) +#define JV3_SECSMAX (2*JV3_SECSPERBLK) + +#define JV1_SECPERTRK 10 + +/* Values for emulated disk image type (emutype) below */ +#define JV1 1 /* compatible with Vavasour Model I emulator */ +#define JV3 3 /* compatible with Vavasour Model III/4 emulator */ +#define DMK 4 /* compatible with Keil Model III/4 emulator */ +#define REAL 100 /* real floppy drive, PC controller */ +#define CATW 101 /* real floppy drive, Catweasel controller (future) */ +#define NONE 0 + +typedef struct { + int free_id[4]; /* first free id, if any, of each size */ + int last_used_id; /* last used index */ + int nblocks; /* number of blocks of ids, 1 or 2 */ + int sorted_valid; /* sorted_id array valid */ + SectorId id[JV3_SECSMAX + 1]; /* extra one is a loop sentinel */ + int offset[JV3_SECSMAX + 1]; /* offset into file for each id */ + short sorted_id[JV3_SECSMAX + 1]; + short track_start[MAXTRACKS][JV3_SIDES]; +} JV3State; + +typedef struct { + int rps; /* phys rotations/sec; emutype REAL only */ + int size_code; /* most recent sector size; REAL only */ + int empty; /* 1=emulate empty drive */ + time_t empty_timeout; /* real_empty valid until this time */ + int fmt_nbytes; /* number of PC format command bytes */ + int fmt_fill; /* fill byte for data sectors */ + unsigned char buf[MAXSECSIZE]; +} RealState; + +/* Some constants for DMK format */ +#define DMK_WRITEPROT 0 +#define DMK_NTRACKS 1 +#define DMK_TRACKLEN 2 +#define DMK_TRACKLEN_SIZE 2 +#define DMK_OPTIONS 4 +#define DMK_FORMAT 0x0c +#define DMK_FORMAT_SIZE 4 +#define DMK_HDR_SIZE 0x10 +#define DMK_TKHDR_SIZE 0x80 /* Space reserved for IDAM pointers */ +#define DMK_TRACKLEN_MAX 0x4000 + +/* Bit assignments in options */ +#define DMK_SSIDE_OPT 0x10 +#define DMK_SDEN_OPT 0x40 +#define DMK_IGNDEN_OPT 0x80 + +/* Bit assignments in IDAM pointers */ +#define DMK_DDEN_FLAG 0x8000 +#define DMK_EXTRA_FLAG 0x4000 /* unused */ +#define DMK_IDAMP_BITS 0x3fff + +#define dmk_incr(d) \ + (((d)->u.dmk.ignden || (d)->u.dmk.sden || state.density) ? 1 : 2) + +typedef struct { + int ntracks; /* max number of tracks formatted */ + int tracklen; /* bytes reserved per track in file */ + int nsides; /* 1 or 2 (single-sided flag in header) */ + int sden; /* single-density-only flag in header */ + int ignden; /* ignore-density flag in header */ + int curtrack, curside; /* track/side in track buffer, or -1/-1 */ + int curbyte; /* index in buf for current op */ + int nextidam; /* index in buf to put next idam */ + unsigned char buf[DMK_TRACKLEN_MAX]; +} DMKState; + +typedef struct { + int writeprot; /* emulated write protect tab */ + int phytrack; /* where are we really? */ + int emutype; + int inches; /* 5 or 8, as seen by TRS-80 */ + int real_step; /* 1=normal, 2=double-step if REAL */ + FILE* file; + union { + JV3State jv3; /* valid if emutype = JV3 */ + RealState real; /* valid if emutype = REAL */ + DMKState dmk; /* valid if emutype = DMK */ + } u; +} DiskState; + +DiskState disk[NDRIVES]; + +/* Emulate interleave in JV1 mode */ +unsigned char jv1_interleave[10] = {0, 5, 1, 6, 2, 7, 3, 8, 4, 9}; + +/* Forward */ +void real_verify(); +void real_restore(int curdrive); +void real_seek(); +void real_read(); +void real_write(); +void real_readadr(); +void real_readtrk(); +void real_writetrk(); +int real_check_empty(DiskState *d); + +/* Entry point for the zbx debugger */ +void +trs_disk_debug() +{ + int i; + printf("Floppy disk controller state:\n"); + printf(" status 0x%02x, track %d (0x%02x), sector %d (0x%02x), " + "data 0x%02x\n", state.status, state.track, state.track, + state.sector, state.sector, state.data); + printf(" currcommand 0x%02x, bytecount left %d, last step direction %d\n", + state.currcommand, state.bytecount, state.lastdirection); + printf(" curdrive %d, curside %d, density %d, controller %s\n", + state.curdrive, state.curside, state.density, + state.controller == TRSDISK_P1771 ? "WD1771" : "WD1791/93"); + printf(" crc state 0x%04x, last_readadr %d, motor timeout %ld\n", + state.crc, state.last_readadr, + (long) (state.motor_timeout - z80_state.t_count)); + printf(" last (non-DMK) format gaps %d %d %d %d %d\n", + state.format_gap[0], state.format_gap[1], state.format_gap[2], + state.format_gap[3], state.format_gap[4]); + printf(" debug flags: %#x\n", trs_disk_debug_flags); + for (i=0; i<NDRIVES; i++) { + DiskState *d = &disk[i]; + printf("Drive %d state: " + "writeprot %d, phytrack %d (0x%02x), inches %d, step %d, type ", + i, d->writeprot, d->phytrack, d->phytrack, d->inches, d->real_step); + if (d->file == NULL) { + printf("EMPTY\n"); + } else { + switch (d->emutype) { + case JV1: + printf("JV1\n"); + break; + case JV3: + printf("JV3\n"); + printf(" last used id %d, id blocks %d\n", + d->u.jv3.last_used_id, d->u.jv3.nblocks); + break; + case DMK: + printf("DMK\n"); + printf(" ntracks %d (0x%02x), tracklen 0x%04x, nsides %d, sden %d, " + "ignden %d\n", d->u.dmk.ntracks, d->u.dmk.ntracks, + d->u.dmk.tracklen, d->u.dmk.nsides, d->u.dmk.sden, + d->u.dmk.ignden); + printf(" buffered track %d, side %d, curbyte %d, nextidam %d\n", + d->u.dmk.curtrack, d->u.dmk.curside, d->u.dmk.curbyte, + d->u.dmk.nextidam); + break; + case REAL: + printf("REAL\n"); + printf(" rpm %d, empty %d, last size code %d, last fmt fill 0x%02x\n", + d->u.real.rps * 60, d->u.real.empty, d->u.real.size_code, + d->u.real.fmt_fill); + break; + default: + printf("UNKNOWN\n"); + break; + } + } + } +} + +void +trs_disk_setsize(int unit, int value) +{ + if (unit < 0 || unit > 7) return; + disk[unit].inches = (value == 8) ? 8 : 5; +} + +void +trs_disk_setstep(int unit, int value) +{ + if (unit < 0 || unit > 7) return; + disk[unit].real_step = (value == 2) ? 2 : 1; +} + +int +trs_disk_getsize(int unit) +{ + if (unit < 0 || unit > 7) return 0; + return disk[unit].inches; +} + +int +trs_disk_getstep(int unit) +{ + if (unit < 0 || unit > 7) return 0; + return disk[unit].real_step; +} + +void +trs_sigusr1(int signo) +{ + trs_disk_needchange = 1; +} + +void +trs_disk_init(int reset_button) +{ + int i; + struct sigaction sa; + + state.status = TRSDISK_NOTRDY|TRSDISK_TRKZERO; + state.track = 0; + state.sector = 0; + state.data = 0; + state.currcommand = TRSDISK_RESTORE; + state.lastdirection = 1; + state.bytecount = 0; + state.format = FMT_DONE; + state.format_bytecount = 0; + state.format_sec = 0; + state.curdrive = state.curside = 0; + state.density = 0; + state.controller = (trs_model == 1) ? TRSDISK_P1771 : TRSDISK_P1791; + state.last_readadr = -1; + state.motor_timeout = 0; + if (!reset_button) { + for (i=0; i<NDRIVES; i++) { + disk[i].phytrack = 0; + disk[i].emutype = NONE; + } + } + trs_disk_change_all(); + trs_cancel_event(); + + trs_disk_nocontroller = (trs_model < 5 && disk[0].file == NULL); + + sa.sa_handler = trs_sigusr1; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGUSR1); + sa.sa_flags = SA_RESTART; + sigaction(SIGUSR1, &sa, NULL); +} + +void +trs_disk_change_all() +{ + int i; + for (i=0; i<NDRIVES; i++) { + trs_disk_change(i); + } + trs_disk_changecount++; + trs_hard_init(1); +} + + +/* trs_event_func used for delayed command completion. Clears BUSY, + sets any additional bits specified, and generates a command + completion interrupt */ +static void +trs_disk_done(int bits) +{ + state.status &= ~TRSDISK_BUSY; + state.status |= bits; + trs_disk_intrq_interrupt(1); +} + + +/* trs_event_func to abort the last command with LOSTDATA if it is + still in progress */ +static void +trs_disk_lostdata(int cmd) +{ + if (state.currcommand == cmd) { + state.status &= ~TRSDISK_BUSY; + state.status |= TRSDISK_LOSTDATA; + state.bytecount = 0; + trs_disk_intrq_interrupt(1); + } +} + +/* trs_event_func used as a delayed command start. Sets DRQ, + generates a DRQ interrupt, sets any additional bits specified, and + schedules a trs_disk_lostdata event. */ +static void +trs_disk_firstdrq(int bits) +{ + state.status |= TRSDISK_DRQ | bits; + trs_disk_drq_interrupt(1); + trs_schedule_event(trs_disk_lostdata, state.currcommand, + 500000 * z80_state.clockMHz); +} + +static void +trs_disk_unimpl(unsigned char cmd, char* more) +{ + state.status = TRSDISK_NOTRDY|TRSDISK_WRITEFLT|TRSDISK_NOTFOUND; + state.bytecount = state.format_bytecount = 0; + state.format = FMT_DONE; + trs_disk_drq_interrupt(0); + trs_schedule_event(trs_disk_done, 0, 0); + error("trs_disk_command(0x%02x) not implemented - %s", cmd, more); +} + +/* Sort first by track, second by side, third by position in emulated-disk + sector array (i.e., physical sector order on track). */ +static int +jv3_id_compare(const void* p1, const void* p2) +{ + DiskState *d = &disk[state.curdrive]; + int i1 = *(short*)p1; + int i2 = *(short*)p2; + int r = d->u.jv3.id[i1].track - d->u.jv3.id[i2].track; + if (r != 0) return r; + r = (d->u.jv3.id[i1].flags & JV3_SIDE) - (d->u.jv3.id[i2].flags & JV3_SIDE); + if (r != 0) return r; + return i1 - i2; +} + +/* (Re-)create the sorted_id data structure for the given drive */ +void +jv3_sort_ids(int drive) +{ + DiskState *d = &disk[drive]; + int olddrive = state.curdrive; + int i, track, side; + + for (i=0; i<=JV3_SECSMAX; i++) { + d->u.jv3.sorted_id[i] = i; + } + state.curdrive = drive; + qsort((void*) d->u.jv3.sorted_id, JV3_SECSMAX, sizeof(short), + jv3_id_compare); + state.curdrive = olddrive; + + for (track=0; track<MAXTRACKS; track++) { + d->u.jv3.track_start[track][0] = -1; + d->u.jv3.track_start[track][1] = -1; + } + track = side = -1; + for (i=0; i<JV3_SECSMAX; i++) { + SectorId *sid = &d->u.jv3.id[d->u.jv3.sorted_id[i]]; + if (sid->track != track || + (sid->flags & JV3_SIDE ? 1 : 0) != side) { + track = sid->track; + if (track == JV3_FREE) break; + side = sid->flags & JV3_SIDE ? 1 : 0; + d->u.jv3.track_start[track][side] = i; + } + } + + d->u.jv3.sorted_valid = 1; +} + +/* JV3 only */ +int +id_index_to_size_code(DiskState *d, int id_index) +{ + return (d->u.jv3.id[id_index].flags & JV3_SIZE) ^ + ((d->u.jv3.id[id_index].track == JV3_FREE) ? 2 : 1); +} + +/* IBM formats only */ +int +size_code_to_size(int code) +{ + return 128 << code; +} + +/* JV3 only */ +int +id_index_to_size(DiskState *d, int id_index) +{ + return 128 << id_index_to_size_code(d, id_index); +} + +/* Return the offset of the data block for the id_index'th sector + in an emulated-disk file. Not used for DMK. */ +static off_t +offset(DiskState *d, int id_index) +{ + if (d->emutype == JV1) { + return id_index * JV1_SECSIZE; + } else if (d->emutype == JV3) { + return d->u.jv3.offset[id_index]; + } else { + trs_disk_unimpl(state.currcommand, "DMK offset (internal error)"); + return 0; + } +} + +/* Return the offset of the id block for the id_index'th sector + in an emulated-disk file. Initialize a new block if needed. JV3 only. */ +static off_t +idoffset(DiskState *d, int id_index) +{ + if (d->emutype == JV1 || d->emutype == DMK) { + trs_disk_unimpl(state.currcommand, "JV1 or DMK idoffset (internal error)"); + return -1; + } else { + if (id_index < JV3_SECSPERBLK) { + return JV3_IDSTART + id_index * sizeof(SectorId); + } else { + int idstart2 = d->u.jv3.offset[JV3_SECSPERBLK-1] + + id_index_to_size(d, JV3_SECSPERBLK-1); + + if (d->u.jv3.nblocks == 1) { + /* Initialize new block of ids */ + int c; + fseek(d->file, idstart2, 0); + c = fwrite((void*)&d->u.jv3.id[JV3_SECSPERBLK], JV3_SECSTART, 1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + c = fflush(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + d->u.jv3.nblocks = 2; + } + return idstart2 + (id_index - JV3_SECSPERBLK) * sizeof(SectorId); + } + } +} + +int +jv3_alloc_sector(DiskState *d, int size_code) +{ + int maybe = d->u.jv3.free_id[size_code]; + d->u.jv3.sorted_valid = 0; + while (maybe <= d->u.jv3.last_used_id) { + if (d->u.jv3.id[maybe].track == JV3_FREE && + id_index_to_size_code(d, maybe) == size_code) { + d->u.jv3.free_id[size_code] = maybe + 1; + return maybe; + } + maybe++; + } + d->u.jv3.free_id[size_code] = JV3_SECSMAX; /* none are free */ + if (d->u.jv3.last_used_id >= JV3_SECSMAX-1) { + return -1; + } + d->u.jv3.last_used_id++; + d->u.jv3.offset[d->u.jv3.last_used_id + 1] = + d->u.jv3.offset[d->u.jv3.last_used_id] + size_code_to_size(size_code); + if (d->u.jv3.last_used_id + 1 == JV3_SECSPERBLK) { + d->u.jv3.offset[d->u.jv3.last_used_id + 1] += JV3_SECSTART; + } + return d->u.jv3.last_used_id; +} + +void +jv3_free_sector(DiskState *d, int id_index) +{ + int c; + int size_code = (d->u.jv3.id[id_index].flags & JV3_SIZE) ^ 1; + if (d->u.jv3.free_id[size_code] > id_index) { + d->u.jv3.free_id[size_code] = id_index; + } + d->u.jv3.sorted_valid = 0; + d->u.jv3.id[id_index].track = JV3_FREE; + d->u.jv3.id[id_index].sector = JV3_FREE; + d->u.jv3.id[id_index].flags = + (d->u.jv3.id[id_index].flags | JV3_FREEF) ^ JV3_SIZE; + fseek(d->file, idoffset(d, id_index), 0); + c = fwrite(&d->u.jv3.id[id_index], sizeof(SectorId), 1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + + if (id_index == d->u.jv3.last_used_id) { + int newlen; + while (d->u.jv3.id[d->u.jv3.last_used_id].track == JV3_FREE) { + d->u.jv3.last_used_id--; + } + fflush(d->file); + rewind(d->file); + if (d->u.jv3.last_used_id >= 0) { + newlen = offset(d, d->u.jv3.last_used_id) + + id_index_to_size(d, d->u.jv3.last_used_id); + } else { + newlen = offset(d, 0); + } + ftruncate(fileno(d->file), newlen); + } +} + +/* Heuristic to decide what file format we have */ +/* Also decodes write-protect state */ +void +trs_disk_emutype(DiskState *d) +{ + int c; + char fmt[4]; + int count; + + fseek(d->file, 0, 0); + c = getc(d->file); + if (c == -1) { + d->emutype = JV1; + return; + } + if (c == 0 || c == 0xff) { + fseek(d->file, DMK_FORMAT, 0); + count = fread(fmt, 1, DMK_FORMAT_SIZE, d->file); + if (count != DMK_FORMAT_SIZE) { + d->emutype = JV1; + return; + } + if (fmt[0] == 0 && fmt[1] == 0 && fmt[2] == 0 && fmt[3] == 0) { + fseek(d->file, DMK_TRACKLEN, 0); + count = (unsigned char) getc(d->file); + count += (unsigned char) getc(d->file) << 8; + if (count >= 16 && count <= DMK_TRACKLEN_MAX) { + d->emutype = DMK; + d->writeprot = d->writeprot || (c == 0xff); + return; + } + } + if (fmt[0] == 0x78 && fmt[1] == 0x56 && fmt[2] == 0x34 && fmt[3] == 0x12) { + error("Real disk specifier file from DMK emulator not supported"); + d->emutype = NONE; + fclose(d->file); + d->file = NULL; + return; + } + } + if (c == 0) { + fseek(d->file, 1, 0); + if (getc(d->file) == 0xfe) { + d->emutype = JV1; + return; + } + } + fseek(d->file, JV3_SECSPERBLK*sizeof(SectorId), 0); + c = getc(d->file); + if (c == 0 || c == 0xff) { + d->emutype = JV3; + d->writeprot = d->writeprot || (c == 0); + return; + } + d->emutype = JV1; +} + +void +trs_disk_change(int drive) +{ + char diskname[1024]; + DiskState *d = &disk[drive]; + struct stat st; + int c, res; + + if (d->file != NULL) { + c = fclose(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + if (trs_model == 5) { + sprintf(diskname, "%s/disk4p-%d", trs_disk_dir, drive); + } else { + sprintf(diskname, "%s/disk%d-%d", trs_disk_dir, trs_model, drive); + } + res = stat(diskname, &st); + if (res == -1) { + d->file = NULL; + return; + } +#if __linux + if (S_ISBLK(st.st_mode)) { + /* Real floppy drive */ + int fd; + int reset_now = 0; + struct floppy_drive_params fdp; + fd = open(diskname, O_ACCMODE|O_NDELAY); + if (fd == -1) { + error("%s: %s", diskname, strerror(errno)); + d->file = NULL; + d->emutype = JV3; + return; + } + d->file = fdopen(fd, "r+"); + if (d->file == NULL) { + error("%s: %s", diskname, strerror(errno)); + d->emutype = JV3; + return; + } + d->writeprot = 0; + ioctl(fileno(d->file), FDRESET, &reset_now); + ioctl(fileno(d->file), FDGETDRVPRM, &fdp); + d->u.real.rps = fdp.rps; + d->u.real.size_code = 1; /* initial guess: 256 bytes */ + d->u.real.empty_timeout = 0; + if (d->emutype != REAL) { + d->emutype = REAL; + d->phytrack = 0; + real_restore(drive); + } + } else +#endif + { + d->file = fopen(diskname, "r+"); + if (d->file == NULL) { + d->file = fopen(diskname, "r"); + if (d->file == NULL) return; + d->writeprot = 1; + } else { + d->writeprot = 0; + } + trs_disk_emutype(d); + } + if (d->emutype == JV3) { + int id_index, n; + int ofst; + + memset((void*)d->u.jv3.id, JV3_FREE, sizeof(d->u.jv3.id)); + + /* Read first block of ids */ + fseek(d->file, JV3_IDSTART, 0); + fread((void*)&d->u.jv3.id[0], 3, JV3_SECSPERBLK, d->file); + + /* Scan to find their offsets */ + ofst = JV3_SECSTART; + for (id_index=0; id_index<JV3_SECSPERBLK; id_index++) { + d->u.jv3.offset[id_index] = ofst; + ofst += id_index_to_size(d, id_index); + } + + /* Read second block of ids, if any */ + fseek(d->file, ofst, 0); + n = fread((void*)&d->u.jv3.id[JV3_SECSPERBLK], 3, JV3_SECSPERBLK, d->file); + d->u.jv3.nblocks = n > 0 ? 2 : 1; + + /* Scan to find their offsets */ + ofst += JV3_SECSTART; + for (id_index=JV3_SECSPERBLK; id_index<JV3_SECSPERBLK*2; id_index++) { + d->u.jv3.offset[id_index] = ofst; + ofst += id_index_to_size(d, id_index); + } + + /* Find u.jv3.last_used_id value and u.jv3.free_id hints */ + for (n=0; n<4; n++) { + d->u.jv3.free_id[n] = JV3_SECSMAX; + } + d->u.jv3.last_used_id = -1; + for (id_index=0; id_index<JV3_SECSMAX; id_index++) { + if (d->u.jv3.id[id_index].track == JV3_FREE) { + int size_code = id_index_to_size_code(d, id_index); + if (d->u.jv3.free_id[size_code] == JV3_SECSMAX) { + d->u.jv3.free_id[size_code] = id_index; + } + } else { + d->u.jv3.last_used_id = id_index; + } + } + jv3_sort_ids(drive); + } else if (d->emutype == DMK) { + fseek(d->file, DMK_NTRACKS, 0); + d->u.dmk.ntracks = (unsigned char) getc(d->file); + d->u.dmk.tracklen = (unsigned char) getc(d->file) + + (((unsigned char) getc(d->file)) << 8); + c = getc(d->file); + d->u.dmk.nsides = (c & DMK_SSIDE_OPT) ? 1 : 2; + d->u.dmk.sden = (c & DMK_SDEN_OPT) != 0; + d->u.dmk.ignden = (c & DMK_IGNDEN_OPT) != 0; + d->u.dmk.curtrack = d->u.dmk.curside = -1; + + if (trs_disk_debug_flags & DISKDEBUG_DMK) { + debug("DMK drv=%d wp=%d #tk=%d tklen=0x%x nsides=%d sden=%d ignden=%d\n", + drive, d->writeprot, d->u.dmk.ntracks, d->u.dmk.tracklen, + d->u.dmk.nsides, d->u.dmk.sden, d->u.dmk.ignden); + } + } +} + +static int +cmd_type(unsigned char cmd) +{ + switch (cmd & TRSDISK_CMDMASK) { + case TRSDISK_RESTORE: + case TRSDISK_SEEK: + case TRSDISK_STEP: + case TRSDISK_STEPU: + case TRSDISK_STEPIN: + case TRSDISK_STEPINU: + case TRSDISK_STEPOUT: + case TRSDISK_STEPOUTU: + return 1; + case TRSDISK_READ: + case TRSDISK_READM: + case TRSDISK_WRITE: + case TRSDISK_WRITEM: + return 2; + case TRSDISK_READADR: + case TRSDISK_READTRK: + case TRSDISK_WRITETRK: + return 3; + case TRSDISK_FORCEINT: + return 4; + } + return -1; /* not reached */ +} + + +/* Called by the interrupt code to determine whether a motoroff NMI is + required. Called even if this NMI is masked, so we also use it here + to set NOTRDY and LOSTDATA. */ +int +trs_disk_motoroff() +{ + int stopped; + int cmdtype; + + stopped = (state.motor_timeout - z80_state.t_count > TSTATE_T_MID); + if (stopped) { + state.status |= TRSDISK_NOTRDY; + cmdtype = cmd_type(state.currcommand); + if ((cmdtype == 2 || cmdtype == 3) && (state.status & TRSDISK_DRQ)) { + /* Also end the command and set Lost Data for good measure */ + state.status = (state.status | TRSDISK_LOSTDATA) & + ~(TRSDISK_BUSY | TRSDISK_DRQ); + state.bytecount = 0; + } + } + return stopped; +} + +/* Get the on-disk track data from the current track/side into the buffer */ +void +dmk_get_track(DiskState* d) +{ + int res; + if (d->phytrack == d->u.dmk.curtrack && + state.curside == d->u.dmk.curside) return; + d->u.dmk.curtrack = d->phytrack; + d->u.dmk.curside = state.curside; + if (d->u.dmk.curtrack >= d->u.dmk.ntracks || + (d->u.dmk.curside && d->u.dmk.nsides == 1)) { + memset(d->u.dmk.buf, 0, sizeof(d->u.dmk.buf)); + return; + } + fseek(d->file, (DMK_HDR_SIZE + + (d->u.dmk.curtrack * d->u.dmk.nsides + d->u.dmk.curside) + * d->u.dmk.tracklen), 0); + res = fread(d->u.dmk.buf, d->u.dmk.tracklen, 1, d->file); + if (res != 1) { + memset(d->u.dmk.buf, 0, sizeof(d->u.dmk.buf)); + return; + } +} + + +/* Search for a sector on the current physical track. For JV1 or JV3, + return its index within the emulated disk's array of sectors. For + DMK, get the track into the buffer, return the index of the next + byte after the header CRC, and set state.bytecount to its size + code. Set status and return -1 if there is no such sector. If + sector == -1, return the first sector found if any. If side == 0 + or 1, perform side compare against sector ID; if -1, don't. */ +static int +search(int sector, int side) +{ + DiskState *d = &disk[state.curdrive]; + if (d->file == NULL) { + state.status |= TRSDISK_NOTFOUND; + return -1; + } + if (d->emutype == JV1) { + if (d->phytrack < 0 || d->phytrack >= MAXTRACKS || + state.curside > 0 || sector >= JV1_SECPERTRK || d->file == NULL || + d->phytrack != state.track || state.density == 1 || side == 1) { + state.status |= TRSDISK_NOTFOUND; + return -1; + } + return JV1_SECPERTRK * d->phytrack + (sector < 0 ? 0 : sector); + } else if (d->emutype == JV3) { + int i; + SectorId *sid; + if (d->phytrack < 0 || d->phytrack >= MAXTRACKS || + state.curside >= JV3_SIDES || + (side != -1 && side != state.curside) || + d->phytrack != state.track || d->file == NULL) { + state.status |= TRSDISK_NOTFOUND; + return -1; + } + if (!d->u.jv3.sorted_valid) jv3_sort_ids(state.curdrive); + i = d->u.jv3.track_start[d->phytrack][state.curside]; + if (i != -1) { + for (;;) { + sid = &d->u.jv3.id[d->u.jv3.sorted_id[i]]; + if (sid->track != d->phytrack || + (sid->flags & JV3_SIDE ? 1 : 0) != state.curside) break; + if ((sector == -1 || sid->sector == sector) && + ((sid->flags & JV3_DENSITY) ? 1 : 0) == state.density) { + return d->u.jv3.sorted_id[i]; + } + i++; + } + } + state.status |= TRSDISK_NOTFOUND; + return -1; + } else /* d->emutype == DMK */ { + /* !!maybe someday start at a point determined by angle() and wrap + back. would deal more realistically with disks that have more + than one of the same sector. */ + int i; + int incr = dmk_incr(d); + + /* get current phytrack into buffer */ + dmk_get_track(d); + + /* loop through IDAMs in track */ + for (i = 0; i < DMK_TKHDR_SIZE; i+=2) { + unsigned char *p; + + /* fetch index of next IDAM */ + int idamp = d->u.dmk.buf[i] + (d->u.dmk.buf[i+1] << 8); + + /* fail if no more IDAMs */ + if (idamp == 0) break; + + /* skip IDAM if wrong density */ + if (!d->u.dmk.ignden && + state.density != ((idamp & DMK_DDEN_FLAG) != 0)) continue; + + /* point p to IDAM */ + idamp &= DMK_IDAMP_BITS; + p = &d->u.dmk.buf[idamp]; + + /* fail if IDAM out of range */ + if (idamp >= DMK_TRACKLEN_MAX) break; + + /* initialize ID CRC */ + state.crc = state.density ? 0xcdb4 /* CRC of a1 a1 a1 */ : 0xffff; + + /* sanity check; is this an IDAM at all? */ + if (*p != 0xfe) continue; + state.crc = calc_crc1(state.crc, *p); + p += incr; + + /* compare track field of ID */ + if (*p != state.track) continue; + state.crc = calc_crc1(state.crc, *p); + p += incr; + + /* compare side field of ID if desired */ + if ((*p & 1) != side && side != -1) continue; + state.crc = calc_crc1(state.crc, *p); + p += incr; + + /* compare sector field of ID if desired */ + if (*p != sector && sector != -1) continue; + state.crc = calc_crc1(state.crc, *p); + p += incr; + + /* save size code field of ID; caller converts to actual byte count */ + state.bytecount = *p; + state.crc = calc_crc1(state.crc, *p); + p += incr; + + /* fold CRC field into computation; result should be 0 */ + state.crc = calc_crc1(state.crc, *p); + p += incr; + state.crc = calc_crc1(state.crc, *p); + p += incr; + + if (state.crc != 0) { + /* set CRC error flag and look for another ID that matches */ + state.status |= TRSDISK_CRCERR; + continue; + } else { + /* clear CRC error flag in case set for an earlier ID match */ + state.status &= ~TRSDISK_CRCERR; + } + + /* Found an ID that matches */ + d->u.dmk.nextidam = i + 2; /* remember where the next one is */ + return p - d->u.dmk.buf; + } + state.status |= TRSDISK_NOTFOUND; + return -1; + } +} + + +/* Search for the first sector on the current physical track (in + either density) and return its index within the sorted index array + (JV3), or index within the sector array (JV1). Not used for DMK. + Return -1 if there is no such sector, or if reading JV1 in double + density. Don't set TRSDISK_NOTFOUND; leave the caller to do + that. */ +static int +search_adr() +{ + DiskState *d = &disk[state.curdrive]; + if (d->file == NULL) { + return -1; + } + if (d->emutype == JV1) { + if (d->phytrack < 0 || d->phytrack >= MAXTRACKS || + state.curside > 0 || d->file == NULL || state.density == 1) { + return -1; + } + return JV1_SECPERTRK * d->phytrack; + } else { + if (d->phytrack < 0 || d->phytrack >= MAXTRACKS || + state.curside >= JV3_SIDES || d->file == NULL) { + return -1; + } + if (!d->u.jv3.sorted_valid) jv3_sort_ids(state.curdrive); + return d->u.jv3.track_start[d->phytrack][state.curside]; + } +} + + +void +verify() +{ + /* Verify that head is on the expected track */ + DiskState *d = &disk[state.curdrive]; + if (d->emutype == REAL) { + real_verify(); + } else if (d->emutype == JV1) { + if (d->file == NULL) { + state.status |= TRSDISK_NOTFOUND; + } if (state.density == 1) { + state.status |= TRSDISK_NOTFOUND; + } else if (state.track != d->phytrack) { + state.status |= TRSDISK_SEEKERR; + } + } else { + search(-1, -1); /* TRSDISK_SEEKERR == TRSDISK_NOTFOUND */ + } +} + +/* Return a value in [0,1) indicating how far we've rotated + * from the leading edge of the index hole */ +float +angle() +{ + DiskState *d = &disk[state.curdrive]; + float a; + /* Set revus to number of microseconds per revolution */ + int revus = d->inches == 5 ? 200000 /* 300 RPM */ : 166666 /* 360 RPM */; +#if TSTATEREV + /* Lock revolution rate to emulated time measured in T-states */ + /* Minor bug: there will be a glitch when t_count wraps around on + a 32-bit machine */ + int revt = (int)(revus * z80_state.clockMHz); + a = ((float)(z80_state.t_count % revt)) / ((float)revt); +#else + /* Old way: lock revolution rate to real time */ + struct timeval tv; + gettimeofday(&tv, NULL); + /* Ignore the seconds field; this is OK if there are a round number + of revolutions per second */ + a = ((float)(tv.tv_usec % revus)) / ((float)revus); +#endif + return a; +} + +static void +type1_status() +{ + DiskState *d = &disk[state.curdrive]; + + switch (cmd_type(state.currcommand)) { + case 1: + case 4: + break; + default: + return; + } + + if (d->file == NULL || (d->emutype == REAL && d->u.real.empty)) { + state.status |= TRSDISK_INDEX; + } else { + if (angle() < trs_disk_holewidth) { + state.status |= TRSDISK_INDEX; + } else { + state.status &= ~TRSDISK_INDEX; + } + if (d->writeprot) { + state.status |= TRSDISK_WRITEPRT; + } else { + state.status &= ~TRSDISK_WRITEPRT; + } + } + if (d->phytrack == 0) { + state.status |= TRSDISK_TRKZERO; + } else { + state.status &= ~TRSDISK_TRKZERO; + } + /* RDY and HLT inputs are wired together on TRS-80 I/III/4/4P */ + if (state.status & TRSDISK_NOTRDY) { + state.status &= ~TRSDISK_HEADENGD; + } else { + state.status |= TRSDISK_HEADENGD; + } +} + +void +trs_disk_select_write(unsigned char data) +{ + static int old_data = -1; + + if ((trs_disk_debug_flags & DISKDEBUG_FDCREG) && data != old_data) { + debug("select_write(0x%02x) pc 0x%04x\n", data, REG_PC); + old_data = data; + } + + state.status &= ~TRSDISK_NOTRDY; + if (trs_model == 1) { + /* Disk 3 and side select share a bit. You can't have a drive :3 + on a real Model I if any drive is two-sided. Here we are more + generous and just forbid drive :3 from being 2-sided. */ + state.curside = ( (data & (TRSDISK_0|TRSDISK_1|TRSDISK_2)) != 0 && + (data & TRSDISK_SIDE) != 0 ); + if (state.curside) data &= ~TRSDISK_SIDE; + + } else { + state.curside = (data & TRSDISK3_SIDE) != 0; + state.density = (data & TRSDISK3_MFM) != 0; + if (data & TRSDISK3_WAIT) { + /* If there was an event pending, simulate waiting until + it was due. */ + if (trs_event_scheduled() != NULL && + trs_event_scheduled() != trs_disk_lostdata) { + z80_state.t_count = z80_state.sched; + trs_do_event(); + } + } + } + switch (data & (TRSDISK_0|TRSDISK_1|TRSDISK_2|TRSDISK_3)) { + case 0: + state.status |= TRSDISK_NOTRDY; + break; + case TRSDISK_0: + state.curdrive = 0; + break; + case TRSDISK_1: + state.curdrive = 1; + break; + case TRSDISK_2: + state.curdrive = 2; + break; + case TRSDISK_3: + state.curdrive = 3; + break; + case TRSDISK_4: + /* fake value for emulator only */ + state.curdrive = 4; + break; + case TRSDISK_5: + /* fake value for emulator only */ + state.curdrive = 5; + break; + case TRSDISK_6: + /* fake value for emulator only */ + state.curdrive = 6; + break; + case TRSDISK_7: + /* fake value for emulator only */ + state.curdrive = 7; + break; + default: + trs_disk_unimpl(data, "bogus drive select"); + state.status |= TRSDISK_NOTRDY; + break; + } + + /* If a drive was selected... */ + if (!(state.status & TRSDISK_NOTRDY)) { + DiskState *d = &disk[state.curdrive]; + + /* Retrigger emulated motor timeout */ + state.motor_timeout = z80_state.t_count + + MOTOR_USEC * z80_state.clockMHz; + trs_disk_motoroff_interrupt(0); + + /* If a SIGUSR1 disk change is pending, accept it here */ + if (trs_disk_needchange) { + trs_disk_change_all(); + trs_disk_needchange = 0; + } + + /* Update our knowledge of whether there is a real disk present */ + if (d->emutype == REAL) real_check_empty(d); + } +} + +unsigned char +trs_disk_track_read(void) +{ + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("track_read() => 0x%02x pc 0x%04x\n", state.track, REG_PC); + } + return state.track; +} + +void +trs_disk_track_write(unsigned char data) +{ + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("track_write(0x%02x) pc 0x%04x\n", data, REG_PC); + } + state.track = data; +} + +unsigned char +trs_disk_sector_read(void) +{ + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("sector_read() => 0x%02x pc 0x%04x\n", state.sector, REG_PC); + } + return state.sector; +} + +void +trs_disk_set_controller(int controller) +{ + /* Support for more accurate Doubler emulation */ + FDCState tmp_state; + if (state.controller == controller) return; + tmp_state.status = state.status; + tmp_state.track = state.track; + tmp_state.sector = state.sector; + tmp_state.data = state.data; + tmp_state.lastdirection = state.lastdirection; + state.controller = controller; + state.status = other_state.status; + state.track = other_state.track; + state.sector = other_state.sector; + state.data = other_state.data; + state.lastdirection = other_state.lastdirection; + other_state.status = tmp_state.status; + other_state.track = tmp_state.track; + other_state.sector = tmp_state.sector; + other_state.data = tmp_state.data; + other_state.lastdirection = tmp_state.lastdirection; +} + +void +trs_disk_sector_write(unsigned char data) +{ + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("sector_write(0x%02x) pc 0x%04x\n", data, REG_PC); + } + if (trs_model == 1 && (trs_disk_doubler & TRSDISK_TANDY)) { + switch (data) { + /* Emulate Radio Shack doubler */ + case TRSDISK_R1791: + trs_disk_set_controller(TRSDISK_P1791); + state.density = 1; + break; + case TRSDISK_R1771: + trs_disk_set_controller(TRSDISK_P1771); + state.density = 0; + break; + case TRSDISK_NOPRECMP: + case TRSDISK_PRECMP: + /* Nothing for emulator to do */ + break; + default: + break; + } + } + state.sector = data; +} + +unsigned char +trs_disk_data_read(void) +{ + DiskState *d = &disk[state.curdrive]; + SectorId *sid; + switch (state.currcommand & TRSDISK_CMDMASK) { + + case TRSDISK_READ: + if (state.bytecount > 0 && (state.status & TRSDISK_DRQ)) { + int c; + if (d->emutype == REAL) { + c = d->u.real.buf[size_code_to_size(d->u.real.size_code) + - state.bytecount]; + } else if (d->emutype == DMK) { + c = d->u.dmk.buf[d->u.dmk.curbyte]; + state.crc = calc_crc1(state.crc, c); + d->u.dmk.curbyte += dmk_incr(d); + } else { + c = getc(d->file); + if (c == EOF) { + c = 0xe5; + if (d->emutype == JV1) { + state.status &= ~TRSDISK_RECTYPE; + state.status |= (state.controller == TRSDISK_P1771) ? + TRSDISK_1771_FB : TRSDISK_1791_FB; + } + } + } + state.data = c; + state.bytecount--; + if (state.bytecount <= 0) { + if (d->emutype == DMK) { + state.crc = calc_crc1(state.crc, d->u.dmk.buf[d->u.dmk.curbyte]); + d->u.dmk.curbyte += dmk_incr(d); + state.crc = calc_crc1(state.crc, d->u.dmk.buf[d->u.dmk.curbyte]); + if (state.crc != 0) { + state.status |= TRSDISK_CRCERR; + } + } + state.bytecount = 0; + state.status &= ~TRSDISK_DRQ; + trs_disk_drq_interrupt(0); + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 64); + } + } + break; + + case TRSDISK_READADR: + if (state.bytecount <= 0 || !(state.status & TRSDISK_DRQ)) break; + if (d->emutype == REAL) { +#if 0 + state.sector = d->u.real.buf[0]; /*179x data sheet says this*/ +#else + state.track = d->u.real.buf[0]; /*let's guess it meant this*/ + state.sector = d->u.real.buf[2]; /*1771 data sheet says this*/ +#endif + state.data = d->u.real.buf[6 - state.bytecount]; + + } else if (d->emutype == DMK) { + state.data = d->u.dmk.buf[d->u.dmk.curbyte]; +#if 0 + if (state.bytecount == 6) { + state.sector = state.data; /*179x data sheet says this*/ + } +#else + if (state.bytecount == 6) { + state.track = state.data; /*let's guess it meant this!!*/ + } else if (state.bytecount == 4) { + state.sector = state.data; /*1771 data sheet says this*/ + } +#endif + d->u.dmk.curbyte += dmk_incr(d); + + } else if (state.last_readadr >= 0) { + if (d->emutype == JV1) { + switch (state.bytecount) { + case 6: + state.data = d->phytrack; +#if 0 + state.sector = d->phytrack; /*179x data sheet says this*/ +#else + state.track = d->phytrack; /*let's guess it meant this*/ +#endif + break; + case 5: + state.data = 0; + break; + case 4: + state.data = jv1_interleave[state.last_readadr % JV1_SECPERTRK]; + state.sector = state.data; /*1771 data sheet says this*/ + break; + case 3: + state.data = 0x01; /* 256 bytes always */ + break; + case 2: + case 1: + state.data = state.crc >> 8; + break; + } + } else if (d->emutype == JV3) { + sid = &d->u.jv3.id[d->u.jv3.sorted_id[state.last_readadr]]; + switch (state.bytecount) { + case 6: + state.data = sid->track; +#if 0 + state.sector = sid->track; /*179x data sheet says this*/ +#else + state.track = sid->track; /*let's guess it meant this*/ +#endif + break; + case 5: + state.data = (sid->flags & JV3_SIDE) != 0; + break; + case 4: + state.data = sid->sector; + state.sector = sid->sector; /*1771 data sheet says this*/ + break; + case 3: + state.data = + id_index_to_size_code(d, d->u.jv3.sorted_id[state.last_readadr]); + break; + case 2: + case 1: + state.data = state.crc >> 8; + break; + } + } + } + state.crc = calc_crc1(state.crc, state.data); + state.bytecount--; + if (state.bytecount <= 0) { + if (d->emutype == DMK && state.crc != 0) { + state.status |= TRSDISK_CRCERR; + } + state.bytecount = 0; + state.status &= ~TRSDISK_DRQ; + trs_disk_drq_interrupt(0); + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 64); + } + break; + + case TRSDISK_READTRK: + /* assert(emutype == DMK) */ + if (!(state.status & TRSDISK_DRQ)) break; + if (state.bytecount > 0) { + state.data = d->u.dmk.buf[d->u.dmk.curbyte]; + d->u.dmk.curbyte += dmk_incr(d); + state.bytecount = state.bytecount - 2 + state.density; + } + if (state.bytecount <= 0) { + state.bytecount = 0; + state.status &= ~TRSDISK_DRQ; + trs_disk_drq_interrupt(0); + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 64); + } + break; + + default: + break; + } + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("data_read() => 0x%02x pc 0x%04x\n", state.data, REG_PC); + } + return state.data; +} + +void +trs_disk_data_write(unsigned char data) +{ + DiskState *d = &disk[state.curdrive]; + int c; + + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("data_write(0x%02x) pc 0x%04x\n", data, REG_PC); + } + switch (state.currcommand & TRSDISK_CMDMASK) { + case TRSDISK_WRITE: + if (state.bytecount > 0) { + if (d->emutype == REAL) { + d->u.real.buf[size_code_to_size(d->u.real.size_code) + - state.bytecount] = data; + state.bytecount--; + if (state.bytecount <= 0) { + real_write(); + } + break; + } + c = putc(data, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + if (d->emutype == DMK) { + d->u.dmk.buf[d->u.dmk.curbyte++] = data; + if (dmk_incr(d) == 2) { + d->u.dmk.buf[d->u.dmk.curbyte++] = data; + c = putc(data, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + state.crc = calc_crc1(state.crc, data); + } + state.bytecount--; + if (state.bytecount <= 0) { + if (d->emutype == DMK) { + int idamp, i, j; + c = state.crc >> 8; + d->u.dmk.buf[d->u.dmk.curbyte++] = c; + c = putc(c, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + if (dmk_incr(d) == 2) { + d->u.dmk.buf[d->u.dmk.curbyte++] = c; + c = putc(c, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + c = state.crc & 0xff; + d->u.dmk.buf[d->u.dmk.curbyte++] = c; + c = putc(c, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + if (dmk_incr(d) == 2) { + d->u.dmk.buf[d->u.dmk.curbyte++] = c; + c = putc(c, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + /* Check if we smashed one or more following IDAMs; can + happen with weird "protected" formats */ + i = j = d->u.dmk.nextidam; + while (i < DMK_TKHDR_SIZE) { + idamp = (d->u.dmk.buf[i] + (d->u.dmk.buf[i+1] << 8)) + & DMK_IDAMP_BITS; + if (idamp != 0 && idamp != DMK_IDAMP_BITS && + d->u.dmk.curbyte /*!!+ erase shutoff slop?*/ > idamp) { + /* Yes, smashed this one */ + i += 2; + if (trs_disk_debug_flags & DISKDEBUG_DMK) { + debug("DMK smashed phytk %d physec %d\n", d->phytrack, i/2); + } + } else { + /* No, keep this one */ + if (j == i) break; /* none were smashed; early exit */ + d->u.dmk.buf[j++] = d->u.dmk.buf[i++]; + d->u.dmk.buf[j++] = d->u.dmk.buf[i++]; + } + } + if (j != i) { + /* Smashed at least one; rewrite the track header */ + while (j < DMK_TKHDR_SIZE) { + d->u.dmk.buf[j++] = 0; + } + fseek(d->file, DMK_HDR_SIZE + + (d->phytrack * d->u.dmk.nsides + state.curside) * + d->u.dmk.tracklen, 0); + c = fwrite(d->u.dmk.buf, DMK_TKHDR_SIZE, 1, d->file); + if (c != 1) state.status |= TRSDISK_WRITEFLT; + } + } + state.bytecount = 0; + state.status &= ~TRSDISK_DRQ; + trs_disk_drq_interrupt(0); + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 64); + c = fflush(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + } + break; + case TRSDISK_WRITETRK: + state.bytecount = state.bytecount - 2 + state.density; + if (d->emutype == DMK) { + if (state.bytecount <= 0) { + if (state.format != FMT_DONE) { + if (trs_disk_debug_flags & DISKDEBUG_DMK) { + debug("complete track format dens %d tk %d side %d\n", + state.density, d->phytrack, state.curside); + } + state.format = FMT_DONE; + state.status &= ~TRSDISK_DRQ; + /* Done: write modified track */ + fseek(d->file, DMK_HDR_SIZE + + (d->phytrack * d->u.dmk.nsides + state.curside) * + d->u.dmk.tracklen, 0); + c = fwrite(d->u.dmk.buf, d->u.dmk.tracklen, 1, d->file); + if (c != 1) state.status |= TRSDISK_WRITEFLT; + if (d->phytrack >= d->u.dmk.ntracks) { + d->u.dmk.ntracks = d->phytrack + 1; + fseek(d->file, DMK_NTRACKS, 0); + putc(d->u.dmk.ntracks, d->file); + } + c = fflush(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + trs_disk_drq_interrupt(0); + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 64); + } + } else { + switch (data) { + case 0xf5: + if (state.density) { + data = 0xa1; + state.format = FMT_PREAM; + state.crc = 0x968b; /* CRC of a1 a1 */ + } else { + state.format = FMT_DATA; + } + break; + case 0xf6: + if (state.density) { + data = 0xc2; + state.format = FMT_IPREAM; + } else { + state.format = FMT_DATA; + } + break; + case 0xf7: + data = state.crc >> 8; + d->u.dmk.buf[d->u.dmk.curbyte++] = data; + if (dmk_incr(d) == 2) { + d->u.dmk.buf[d->u.dmk.curbyte++] = data; + } + state.bytecount = state.bytecount - 2 + state.density; + data = state.crc & 0xff; + state.format = FMT_DATA; + break; + case 0xfe: + if (!state.density || state.format == FMT_PREAM) { + unsigned short idamp = d->u.dmk.curbyte + + (state.density ? DMK_DDEN_FLAG : 0); + if (d->u.dmk.nextidam >= DMK_TKHDR_SIZE) { + error("DMK formatting too many address marks on track"); + } else if (d->u.dmk.curbyte > d->u.dmk.tracklen) { + error("DMK address mark past end of track"); + } else { + d->u.dmk.buf[d->u.dmk.nextidam++] = idamp & 0xff; + d->u.dmk.buf[d->u.dmk.nextidam++] = idamp >> 8; + } + } + state.format = FMT_DATA; + if (!state.density) { + state.crc = 0xffff; + } + break; +#if DMK_MARK_IAM + /* Mark IAMs in the track header like IDAMs. This turns + out to cause bogus errors when doing Read Address commands + both in current versions of David Keil's emulator and in + xtrs 4.5a and earlier, so we disable it, at least for now. */ + case 0xfc: + if (!state.density || state.format == FMT_IPREAM) { + unsigned short idamp = d->u.dmk.curbyte + + (state.density ? DMK_DDEN_FLAG : 0); + if (d->u.dmk.nextidam >= DMK_TKHDR_SIZE) { + error("DMK formatting too many address marks on track"); + } else if (d->u.dmk.curbyte > d->u.dmk.tracklen) { + error("DMK address mark past end of track"); + } else { + d->u.dmk.buf[d->u.dmk.nextidam++] = idamp & 0xff; + d->u.dmk.buf[d->u.dmk.nextidam++] = idamp >> 8; + } + } + state.format = FMT_DATA; + break; +#endif + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + if (!state.density) { + state.crc = 0xffff; + } + state.format = FMT_DATA; + break; + default: + state.format = FMT_DATA; + break; + } + d->u.dmk.buf[d->u.dmk.curbyte++] = data; + if (dmk_incr(d) == 2) { + d->u.dmk.buf[d->u.dmk.curbyte++] = data; + } + state.crc = calc_crc1(state.crc, data); + } + break; + } + if (state.bytecount <= 0) { + if (state.format == FMT_DONE) break; + if (state.format == FMT_GAP2) { + /* False ID: there was no DAM for following data */ + if (d->emutype != JV3) { + trs_disk_unimpl(state.currcommand, "false sector ID (no data)"); + } else { + /* We do not have a flag for this; try using CRC error */ + d->u.jv3.id[state.format_sec].flags |= JV3_ERROR; + error("warning: recording false sector ID as CRC error"); + + /* Write the sector id */ + fseek(d->file, idoffset(d, state.format_sec), 0); + c = fwrite(&d->u.jv3.id[state.format_sec], + sizeof(SectorId), 1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + } else if (state.format != FMT_GAP3) { + /* If not in FMT_GAP3 state, format data was either too long, + had extra garbage following, or was intentionally non- + standard. SuperUtility does a few tricks like "software + bulk erase" and duplication of protected disks, so we + do not complain about this any more. */ +#if BOGUS + error("format data end is not in gap4"); +#endif + state.format_gap[4] = 0; + } else { + /* This was really GAP4 */ + state.format_gap[4] = state.format_gapcnt; + state.format_gapcnt = 0; + } + if (trs_disk_debug_flags & DISKDEBUG_GAPS) { + debug("trk %d side %d gap0 %d gap1 %d gap2 %d gap3 %d gap4 %d\n", + d->phytrack, state.curside, + state.format_gap[0], state.format_gap[1], state.format_gap[2], + state.format_gap[3], state.format_gap[4]); + } + state.format = FMT_DONE; + state.status &= ~TRSDISK_DRQ; + if (d->emutype == REAL) { + real_writetrk(); + } else { + c = fflush(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + trs_disk_drq_interrupt(0); + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 64); + break; + } + switch (state.format) { + case FMT_GAP0: + if (data == 0xfc) { + state.format = FMT_GAP1; + state.format_gap[0] = state.format_gapcnt; + state.format_gapcnt = 0; + } else if (data == 0xfe) { + /* There wasn't a gap 0; we were really in gap 1 */ + state.format_gap[0] = 0; + goto got_idam; + } else { + state.format_gapcnt++; + } + break; + case FMT_GAP1: + if (data == 0xfe) { + got_idam: + /* We've received the first ID address mark */ + state.format_gap[1] = state.format_gapcnt; + state.format_gapcnt = 0; + state.format = FMT_TRACKID; + } else { + state.format_gapcnt++; + } + break; + case FMT_GAP3: + if (data == 0xfe) { + got_idam2: + /* We've received an ID address mark */ + state.format_gap[3] = state.format_gapcnt; + state.format_gapcnt = 0; + state.format = FMT_TRACKID; + } else { + state.format_gapcnt++; + } + break; + case FMT_TRACKID: + if (d->emutype == REAL) { + if (d->u.real.fmt_nbytes >= sizeof(d->u.real.buf)) { + /* Data structure full */ + state.status |= TRSDISK_WRITEFLT; + state.bytecount = 0; + state.format_bytecount = 0; + state.format = FMT_DONE; + } else { + d->u.real.buf[d->u.real.fmt_nbytes++] = data; + state.format = FMT_HEADID; + } + } else { + if (data != d->phytrack) { + trs_disk_unimpl(state.currcommand, "false track number"); + } + state.format = FMT_HEADID; + } + break; + case FMT_HEADID: + if (d->emutype == REAL) { + d->u.real.buf[d->u.real.fmt_nbytes++] = data; + } else if (d->emutype == JV1) { + if (data != 0) { + trs_disk_unimpl(state.currcommand, "JV1 double sided"); + } + if (state.density) { + trs_disk_unimpl(state.currcommand, "JV1 double density"); + } + } else { + if (data != state.curside) { + trs_disk_unimpl(state.currcommand, "false head number"); + } + } + state.format = FMT_SECID; + break; + case FMT_SECID: + if (d->emutype == REAL) { + d->u.real.buf[d->u.real.fmt_nbytes++] = data; + } else if (d->emutype == JV1) { + if (data >= JV1_SECPERTRK) { + trs_disk_unimpl(state.currcommand, "JV1 sector number >= 10"); + } + } else { + state.format_sec = data; + } + state.format = FMT_SIZEID; + break; + case FMT_SIZEID: + if (data > 0x03) { + trs_disk_unimpl(state.currcommand, "invalid sector size"); + } + if (d->emutype == JV3) { + int id_index; + id_index = jv3_alloc_sector(d, data); + if (id_index == -1) { + /* Data structure full */ + state.status |= TRSDISK_WRITEFLT; + state.bytecount = 0; + state.format_bytecount = 0; + state.format = FMT_DONE; + break; + } + d->u.jv3.sorted_valid = 0; + d->u.jv3.id[id_index].track = d->phytrack; + d->u.jv3.id[id_index].sector = state.format_sec; + d->u.jv3.id[id_index].flags = + (state.curside ? JV3_SIDE : 0) | (state.density ? JV3_DENSITY : 0) | + ((data & 3) ^ 1); + state.format_sec = id_index; + + } else if (d->emutype == REAL) { + d->u.real.buf[d->u.real.fmt_nbytes++] = data; + if (d->u.real.size_code != -1 && d->u.real.size_code != data) { + trs_disk_unimpl(state.currcommand, + "varying sector size on same track on real floppy"); + } + d->u.real.size_code = data; + } else { + if (data != 0x01) { + trs_disk_unimpl(state.currcommand, "sector size != 256"); + } + } + state.format = FMT_GAP2; + break; + case FMT_GAP2: + if ((data & 0xfc) == 0xf8) { + /* Found a DAM */ + if (d->emutype == REAL) { + switch (data) { + case 0xfb: /* Standard DAM */ + break; + case 0xfa: + if (state.density) { + /* This DAM is illegal, but SuperUtility uses it, so + ignore the error. This seems to be a bug in SU for + Model I, where it meant to use F8 instead. I think + the WD controller would read back the FA as FB, so we + treat it as FB here. */ + } else { + if (trs_disk_truedam) { + error("format DAM FA on real floppy"); + } + } + break; + case 0xf9: + if (trs_disk_truedam) { + error("format DAM F9 on real floppy"); + } + break; + case 0xf8: + /* This is probably needed by Model III TRSDOS, but it is + a pain to implement. We would have to remember to do a + Write Deleted after the format is complete to change + the DAM. + */ + error("format DAM F8 on real floppy"); + break; + } + } else if (d->emutype == JV1) { + switch (data) { + case 0xf9: + trs_disk_unimpl(state.currcommand, "JV1 DAM cannot be F9"); + break; + case 0xf8: + case 0xfa: + if (d->phytrack != 17) + trs_disk_unimpl(state.currcommand, + "JV1 directory track must be 17"); + break; + default: /* impossible */ + case 0xfb: + break; + } + } else /* JV3 */ { + if (state.density) { + /* Double density */ + switch (data) { + case 0xf8: /* Standard deleted DAM */ + case 0xf9: /* Illegal, probably never used; ignore error. */ + d->u.jv3.id[state.format_sec].flags |= JV3_DAMDDF8; + break; + case 0xfb: /* Standard DAM */ + case 0xfa: /* Illegal, but SuperUtility uses it! */ + default: /* Impossible */ + d->u.jv3.id[state.format_sec].flags |= JV3_DAMDDFB; + break; + } + } else { + /* Single density */ + switch (data) { + case 0xf8: + if (trs_disk_truedam) { + d->u.jv3.id[state.format_sec].flags |= JV3_DAMSDF8; + } else { + d->u.jv3.id[state.format_sec].flags |= JV3_DAMSDFA; + } + break; + case 0xf9: + d->u.jv3.id[state.format_sec].flags |= JV3_DAMSDF9; + break; + case 0xfa: + d->u.jv3.id[state.format_sec].flags |= JV3_DAMSDFA; + break; + default: /* impossible */ + case 0xfb: + d->u.jv3.id[state.format_sec].flags |= JV3_DAMDDFB; + break; + } + } + } + if (d->emutype == JV3) { + /* Prepare to write the data */ + fseek(d->file, offset(d, state.format_sec), 0); + state.format_bytecount = id_index_to_size(d, state.format_sec); + } else if (d->emutype == JV1) { + state.format_bytecount = JV1_SECSIZE; + } else if (d->emutype == REAL) { + state.format_bytecount = size_code_to_size(d->u.real.size_code); + } + state.format_gap[2] = state.format_gapcnt; + state.format_gapcnt = 0; + state.format = FMT_DATA; + } else if (data == 0xfe) { + /* False ID: there was no DAM for following data */ + if (d->emutype != JV3) { + trs_disk_unimpl(state.currcommand, "false sector ID (no data)"); + } else { + /* We do not have a flag for this; try using CRC error */ + error("warning: recording false sector ID as CRC error"); + d->u.jv3.id[state.format_sec].flags |= JV3_ERROR; + + /* Write the sector id */ + fseek(d->file, idoffset(d, state.format_sec), 0); + c = fwrite(&d->u.jv3.id[state.format_sec], sizeof(SectorId), 1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + goto got_idam2; + } else { + state.format_gapcnt++; + } + break; + case FMT_DATA: + if (data == 0xfe) { + /* Short sector with intentional CRC error */ + if (d->emutype == JV3) { + d->u.jv3.id[state.format_sec].flags |= JV3_NONIBM | JV3_ERROR; + if (trs_disk_debug_flags & DISKDEBUG_VTOS3) { + debug("non-IBM sector: drv 0x%02x, sid %d," + " trk 0x%02x, sec 0x%02x\n", + state.curdrive, state.curside, + d->u.jv3.id[state.format_sec].track, + d->u.jv3.id[state.format_sec].sector); + } + /* Write the sector id */ + fseek(d->file, idoffset(d, state.format_sec), 0); + c = fwrite(&d->u.jv3.id[state.format_sec], + sizeof(SectorId), 1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + goto got_idam; + } else { + trs_disk_unimpl(state.currcommand, "JV1 non-IBM sector"); + } + } + if (d->emutype == JV3) { + c = putc(data, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } else if (d->emutype == REAL) { + d->u.real.fmt_fill = data; + } + if (--state.format_bytecount <= 0) { + state.format = FMT_DCRC; + } + break; + case FMT_DCRC: + if (data == 0xf7) { + state.bytecount--; /* two bytes are written */ + } else { + /* Intentional CRC error */ + if (d->emutype != JV3) { + trs_disk_unimpl(state.currcommand, "intentional CRC error"); + } else { + d->u.jv3.id[state.format_sec].flags |= JV3_ERROR; + } + } + if (d->emutype == JV3) { + /* Write the sector id */ + fseek(d->file, idoffset(d, state.format_sec), 0); + c = fwrite(&d->u.jv3.id[state.format_sec], sizeof(SectorId), 1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + state.format = FMT_GAP3; + break; + case FMT_DONE: + break; + case FMT_GAP4: + case FMT_IAM: + case FMT_IDAM: + case FMT_IDCRC: + case FMT_DAM: + default: + error("error in format state machine"); + break; + } + default: + break; + } + state.data = data; + return; +} + +unsigned char +trs_disk_status_read(void) +{ + static int last_status = -1; + + if (trs_disk_nocontroller) return 0xff; + type1_status(); + if (!(state.status & TRSDISK_NOTRDY)) { + if (state.motor_timeout - z80_state.t_count > TSTATE_T_MID) { + /* Subtraction wrapped; motor stopped */ + state.status |= TRSDISK_NOTRDY; + } + } + if ((trs_disk_debug_flags & DISKDEBUG_FDCREG) && + state.status != last_status) { + debug("status_read() => 0x%02x pc 0x%04x\n", state.status, REG_PC); + last_status = state.status; + } + +#if BOGUS + /* Clear intrq unless user did a Force Interrupt with immediate interrupt. */ + /* The 17xx data sheets say this is how it is supposed to work, but it + * makes Model I SuperUtility hang due to the interrupt routine failing + * to clear the interrupt. I suspect the data sheets are wrong. + */ + if (!(((state.currcommand & TRSDISK_CMDMASK) == TRSDISK_FORCEINT) && + ((state.currcommand & 0x08) != 0))) +#else + /* Clear intrq always */ +#endif + { + /* Don't call trs_schedule_event, which could cancel a pending + * interrupt that should occur later and prevent it from ever + * happening; just clear the interrupt right now. + */ + trs_disk_intrq_interrupt(0); + } + + return state.status; +} + +void +trs_disk_command_write(unsigned char cmd) +{ + int id_index, non_ibm, goal_side, new_status; + DiskState *d = &disk[state.curdrive]; + trs_event_func event; + + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug("command_write(0x%02x) pc 0x%04x\n", cmd, REG_PC); + } + + /* Handle DMK partial track reformat */ + if (d->emutype == DMK && + (state.currcommand & ~TRSDISK_EBIT) == TRSDISK_WRITETRK && + state.format != FMT_DONE) { + /* Interrupted format: must write out partial track */ + unsigned char oldtkhdr[DMK_TKHDR_SIZE]; + int c, i, j, idamp; + + if (trs_disk_debug_flags & DISKDEBUG_DMK) { + debug("partial track format dens %d tk %d side %d\n", + state.density, d->phytrack, state.curside); + } + + /* Fetch old IDAM pointers if any */ + fseek(d->file, DMK_HDR_SIZE + + (d->phytrack * d->u.dmk.nsides + state.curside) * + d->u.dmk.tracklen, 0); + c = fread(oldtkhdr, DMK_TKHDR_SIZE, 1, d->file); + if (c == 1) { + /* Copy any pointers to IDAMs that are not being overwritten */ + i = 0; + j = d->u.dmk.nextidam; + while (i < DMK_TKHDR_SIZE) { + idamp = (oldtkhdr[i] + (oldtkhdr[i+1] << 8)) & DMK_IDAMP_BITS; + if (idamp == 0 || idamp == DMK_IDAMP_BITS) break; + if (idamp < d->u.dmk.curbyte) { + /* IDAM overwritten; don't copy */ + i += 2; + if (trs_disk_debug_flags & DISKDEBUG_DMK) { + debug(" discarding physec %d\n", i); + } + } else { + /* IDAM not overwritten; need to copy in */ + if (j >= DMK_TKHDR_SIZE) { + /* No room */ + error("DMK reformatting adds too many sectors to track"); + break; + } + d->u.dmk.buf[j++] = oldtkhdr[i++]; + d->u.dmk.buf[j++] = oldtkhdr[i++]; + if (trs_disk_debug_flags & DISKDEBUG_DMK) { + debug(" preserving physec %d as %d\n", i, j); + } + } + } + } else { + if (trs_disk_debug_flags & DISKDEBUG_FDCREG) { + debug(" no existing sectors\n"); + } + } + /* Write modified portion of track only */ + fseek(d->file, DMK_HDR_SIZE + + (d->phytrack * d->u.dmk.nsides + state.curside) * + d->u.dmk.tracklen, 0); + fwrite(d->u.dmk.buf, d->u.dmk.curbyte, 1, d->file); + if (d->phytrack >= d->u.dmk.ntracks) { + d->u.dmk.ntracks = d->phytrack + 1; + fseek(d->file, DMK_NTRACKS, 0); + putc(d->u.dmk.ntracks, d->file); + } + fflush(d->file); + + /* Invalidate buffer since not all data is here */ + d->u.dmk.curtrack = d->u.dmk.curside = -1; + state.format = FMT_DONE; + } + + /* Cancel any ongoing command */ + event = trs_event_scheduled(); + if (event == trs_disk_lostdata || event == trs_disk_intrq_interrupt) { + trs_cancel_event(); + } + trs_disk_intrq_interrupt(0); + state.bytecount = 0; + state.currcommand = cmd; + switch (cmd & TRSDISK_CMDMASK) { + + case TRSDISK_RESTORE: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("restore 0x%02x drv %d\n", cmd, state.curdrive); + } + state.last_readadr = -1; + d->phytrack = 0; + state.track = 0; + state.status = TRSDISK_TRKZERO|TRSDISK_BUSY; + if (d->emutype == REAL) real_restore(state.curdrive); + /* Should this set lastdirection? */ + if (cmd & TRSDISK_VBIT) verify(); + trs_schedule_event(trs_disk_done, 0, 2000); + break; + + case TRSDISK_SEEK: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("seek 0x%02x drv %d ptk %d otk %d ntk %d\n", + cmd, state.curdrive, d->phytrack, state.track, state.data); + } + state.last_readadr = -1; + d->phytrack += (state.data - state.track); + state.track = state.data; + if (d->phytrack <= 0) { + d->phytrack = 0; /* state.track too? */ + state.status = TRSDISK_TRKZERO|TRSDISK_BUSY; + } else { + state.status = TRSDISK_BUSY; + } + if (d->emutype == REAL) real_seek(); + /* Should this set lastdirection? */ + if (cmd & TRSDISK_VBIT) verify(); + trs_schedule_event(trs_disk_done, 0, 2000); + break; + + case TRSDISK_STEP: + case TRSDISK_STEPU: + step: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("step%s %s 0x%02x drv %d ptk %d otk %d\n", + (cmd & TRSDISK_UBIT) ? "u" : "", + (state.lastdirection < 0) ? "out" : "in", cmd, + state.curdrive, d->phytrack, state.track); + } + state.last_readadr = -1; + d->phytrack += state.lastdirection; + if (cmd & TRSDISK_UBIT) { + state.track += state.lastdirection; + } + if (d->phytrack <= 0) { + d->phytrack = 0; /* state.track too? */ + state.status = TRSDISK_TRKZERO|TRSDISK_BUSY; + } else { + state.status = TRSDISK_BUSY; + } + if (d->emutype == REAL) real_seek(); + if (cmd & TRSDISK_VBIT) verify(); + trs_schedule_event(trs_disk_done, 0, 2000); + break; + + case TRSDISK_STEPIN: + case TRSDISK_STEPINU: + state.lastdirection = 1; + goto step; + + case TRSDISK_STEPOUT: + case TRSDISK_STEPOUTU: + state.lastdirection = -1; + goto step; + + case TRSDISK_READ: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("read 0x%02x drv %d ptk %d tk %d sec %d %sden\n", cmd, + state.curdrive, d->phytrack, state.track, state.sector, + state.density ? "d" : "s"); + } + state.last_readadr = -1; + state.status = 0; + non_ibm = 0; + goal_side = -1; + new_status = 0; + if (state.controller == TRSDISK_P1771) { + if (!(cmd & TRSDISK_BBIT)) { + if (trs_disk_debug_flags & DISKDEBUG_VTOS3) { + debug("non-IBM read: drv 0x%02x, sid %d, trk 0x%02x, sec 0x%02x\n", + state.curdrive, state.curside, state.track, state.sector); + } + if (d->emutype == REAL) { + trs_disk_unimpl(cmd, "non-IBM read on real floppy"); + } + non_ibm = 1; + } else { + if (trs_disk_debug_flags & DISKDEBUG_VTOS3) { + if (state.sector >= 0x7c) { + debug("IBM read: drv 0x%02x, sid %d, trk 0x%02x, sec 0x%02x\n", + state.curdrive, state.curside, state.track, state.sector); + } + } + } + } else { + if (cmd & TRSDISK_CBIT) { + goal_side = (cmd & TRSDISK_BBIT) != 0; + } + } + if (d->emutype == REAL) { + real_read(); + break; + } + id_index = search(state.sector, goal_side); + if (id_index == -1) { + state.status |= TRSDISK_BUSY; + trs_schedule_event(trs_disk_done, 0, 512); + } else { + if (d->emutype == JV1) { + + if (d->phytrack == 17) { + if (state.controller == TRSDISK_P1771) { + new_status = TRSDISK_1771_FA; + } else { + new_status = TRSDISK_1791_F8; + } + } + state.bytecount = JV1_SECSIZE; + fseek(d->file, offset(d, id_index), 0); + + } else if (d->emutype == JV3) { + + if (state.controller == TRSDISK_P1771) { + switch (d->u.jv3.id[id_index].flags & JV3_DAM) { + case JV3_DAMSDFB: + new_status = TRSDISK_1771_FB; + break; + case JV3_DAMSDFA: + new_status = TRSDISK_1771_FA; + break; + case JV3_DAMSDF9: + new_status = TRSDISK_1771_F9; + break; + case JV3_DAMSDF8: + new_status = TRSDISK_1771_F8; + break; + } + } else if (state.density == 0) { + /* single density 179x */ + switch (d->u.jv3.id[id_index].flags & JV3_DAM) { + case JV3_DAMSDFB: + new_status = TRSDISK_1791_FB; + break; + case JV3_DAMSDFA: + if (trs_disk_truedam) { + new_status = TRSDISK_1791_FB; + } else { + new_status = TRSDISK_1791_F8; + } + break; + case JV3_DAMSDF9: + new_status = TRSDISK_1791_F8; + break; + case JV3_DAMSDF8: + new_status = TRSDISK_1791_F8; + break; + } + } else { + /* double density 179x */ + switch (d->u.jv3.id[id_index].flags & JV3_DAM) { + default: /*impossible*/ + case JV3_DAMDDFB: + new_status = TRSDISK_1791_FB; + break; + case JV3_DAMDDF8: + new_status = TRSDISK_1791_F8; + break; + } + } + if (d->u.jv3.id[id_index].flags & JV3_ERROR) { + new_status |= TRSDISK_CRCERR; + } + if (non_ibm) { + state.bytecount = 16; + } else { + state.bytecount = id_index_to_size(d, id_index); + } + fseek(d->file, offset(d, id_index), 0); + + } else /* d->emutype == DMK */ { + + /* max distance past ID CRC to search for DAM */ + int damlimit = state.density ? 43 : 30; /* ref 1791 datasheet */ + unsigned char dam = 0; + + /* DMK search dumps the size code into state.bytecount; adjust + to real bytecount here */ + if (non_ibm) { + state.bytecount = 16 * (((state.bytecount - 1)&0xff)+1); + } else { + state.bytecount = 128 << (state.bytecount & 3); + } + + /* search for valid DAM */ + while (--damlimit >= 0) { + dam = d->u.dmk.buf[id_index]; + id_index += dmk_incr(d); + if (0xf8 <= dam && dam <= 0xfb) { + /* got one! */ + break; + } + } + if (damlimit < 0) { + /* found ID with good CRC but no following DAM; fail */ + state.status |= TRSDISK_BUSY; + trs_schedule_event(trs_disk_done, TRSDISK_NOTFOUND, 512); + break; + } + + /* Set flags for DAM */ + if (state.controller == TRSDISK_P1771) { + /* 1771 */ + switch (dam) { + case 0xfb: + new_status = TRSDISK_1771_FB; + break; + case 0xfa: + new_status = TRSDISK_1771_FA; + break; + case 0xf9: + new_status = TRSDISK_1771_F9; + break; + case 0xf8: + new_status = TRSDISK_1771_F8; + break; + } + } else /* state.controller == TRSDISK_P1791 */ { + switch (dam) { + case 0xfb: + new_status = TRSDISK_1791_FB; + break; + case 0xfa: + /* Note: Illegal in DDEN but Write Track can still + generate it, and of course 1771 can generate in SDEN */ + if (trs_disk_truedam) { + new_status = TRSDISK_1791_FB; + } else { + new_status = TRSDISK_1791_F8; + } + break; + case 0xf9: + /* Note: Illegal in DDEN but Write Track can still + generate it, and of course 1771 can generate in SDEN */ + new_status = TRSDISK_1791_F8; + break; + case 0xf8: + new_status = TRSDISK_1791_F8; + break; + } + } + state.crc = calc_crc1((state.density + ? 0xcdb4 /* CRC of a1 a1 a1 */ + : 0xffff), dam); + + d->u.dmk.curbyte = id_index; + + } /* end if (d->emutype == ...) */ + + state.status |= TRSDISK_BUSY; + trs_schedule_event(trs_disk_firstdrq, new_status, 64); + } + break; + + case TRSDISK_READM: + state.last_readadr = -1; + trs_disk_unimpl(cmd, "read multiple"); + break; + + case TRSDISK_WRITE: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("write 0x%02x drv %d ptk %d tk %d sec %d %sden\n", + cmd, state.curdrive, d->phytrack, state.track, state.sector, + state.density ? "d" : "s"); + } + state.last_readadr = -1; + state.status = 0; + non_ibm = 0; + goal_side = -1; + if (state.controller == TRSDISK_P1771) { + if (!(cmd & TRSDISK_BBIT)) { + if (trs_disk_debug_flags & DISKDEBUG_VTOS3) { + debug("non-IBM write drv 0x%02x, sid %d, trk 0x%02x, sec 0x%02x\n", + state.curdrive, state.curside, state.track, state.sector); + } + if (d->emutype == REAL) { + trs_disk_unimpl(cmd, "non-IBM write on real floppy"); + } + non_ibm = 1; + } else { + if (trs_disk_debug_flags & DISKDEBUG_VTOS3) { + if (state.sector >= 0x7c) { + debug("IBM write: drv 0x%02x, sid %d, trk 0x%02x, sec 0x%02x\n", + state.curdrive, state.curside, state.track, state.sector); + } + } + } + } else { + if (cmd & TRSDISK_CBIT) { + goal_side = (cmd & TRSDISK_BBIT) != 0; + } + } + if (d->emutype == REAL) { + state.status = TRSDISK_BUSY|TRSDISK_DRQ; + trs_disk_drq_interrupt(1); + trs_schedule_event(trs_disk_lostdata, state.currcommand, + 500000 * z80_state.clockMHz); + state.bytecount = size_code_to_size(d->u.real.size_code); + break; + } + if (d->writeprot) { + state.status = TRSDISK_WRITEPRT; + break; + } + id_index = search(state.sector, goal_side); + if (id_index == -1) { + state.status |= TRSDISK_BUSY; + trs_schedule_event(trs_disk_done, 0, 512); + } else { + int jv3dam = 0, dam = 0; + if (state.controller == TRSDISK_P1771) { + switch (cmd & (TRSDISK_CBIT|TRSDISK_DBIT)) { + case 0: + dam = 0xfb; + jv3dam = JV3_DAMSDFB; + break; + case 1: + dam = 0xfa; + jv3dam = JV3_DAMSDFA; + break; + case 2: + dam = 0xf9; + jv3dam = JV3_DAMSDF9; + break; + case 3: + if (trs_disk_truedam) { + dam = 0xf8; + jv3dam = JV3_DAMSDF8; + } else { + dam = 0xfa; + jv3dam = JV3_DAMSDFA; + } + break; + } + } else if (state.density == 0) { + /* 179x single */ + switch (cmd & TRSDISK_DBIT) { + case 0: + dam = 0xfb; + jv3dam = JV3_DAMSDFB; + break; + case 1: + if (trs_disk_truedam) { + dam = 0xf8; + jv3dam = JV3_DAMSDF8; + } else { + dam = 0xfa; + jv3dam = JV3_DAMSDFA; + } + break; + } + } else { + /* 179x double */ + switch (cmd & TRSDISK_DBIT) { + case 0: + dam = 0xfb; + jv3dam = JV3_DAMDDFB; + break; + case 1: + dam = 0xf8; + jv3dam = JV3_DAMDDF8; + break; + } + } + + if (d->emutype == JV1) { + if (dam == 0xf9) { + trs_disk_unimpl(state.currcommand, "JV1 DAM cannot be F9"); + } else if ((dam == 0xfb) == (d->phytrack == 17)) { + trs_disk_unimpl(state.currcommand, "JV1 directory must be track 17"); + break; + } + state.bytecount = JV1_SECSIZE; + fseek(d->file, offset(d, id_index), 0); + + } else if (d->emutype == JV3) { + SectorId *sid = &d->u.jv3.id[id_index]; + unsigned char newflags = sid->flags; + newflags &= ~(JV3_ERROR|JV3_DAM); /* clear CRC error and DAM */ + newflags |= jv3dam; + if (newflags != sid->flags) { + int c; + fseek(d->file, idoffset(d, id_index) + + ((char *) &sid->flags) - ((char *) sid), 0); + c = putc(newflags, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + c = fflush(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + sid->flags = newflags; + } + + /* Kludge for VTOS 3.0 */ + if (sid->flags & JV3_NONIBM) { + int i, j, c; + /* Smash following sectors. This is especially a kludge because + it uses the sector numbers, not the known physical sector + order. */ + for (i = state.sector+1; i <= 0x7f; i++) { + j = search(i, -1); + if (j != -1) { + if (trs_disk_debug_flags & DISKDEBUG_VTOS3) { + debug("smashing tk %d sector 0x%02x id_index %d\n", + state.track, i, j); + } + jv3_free_sector(d, j); + c = fflush(d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + } + /* Smash only one for non-IBM write */ + if (non_ibm) break; + } + } + /* end kludge */ + + if (non_ibm) { + state.bytecount = 16; + } else { + state.bytecount = id_index_to_size(d, id_index); + } + fseek(d->file, offset(d, id_index), 0); + + } else /* d->emutype == DMK */ { + int c, nzeros, i; + + /* DMK search dumps the size code into state.bytecount; adjust + to real bytecount here */ + if (non_ibm) { + state.bytecount = 16 * (((state.bytecount - 1)&0xff)+1); + } else { + state.bytecount = 128 << (state.bytecount & 3); + } + + /* Skip initial part of gap, per 1771 and 179x data sheets */ + id_index += 11 * (state.density ? 2 : 1) * dmk_incr(d); + fseek(d->file, (DMK_HDR_SIZE + + (d->u.dmk.curtrack*d->u.dmk.nsides + d->u.dmk.curside) + * d->u.dmk.tracklen + id_index), 0); + + /* Write remaining gap (per data sheets) and DAM */ + nzeros = 6 * (state.density ? 2 : 1) * dmk_incr(d); + for (i=0; i<nzeros; i++) { + c = putc(0, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + d->u.dmk.buf[id_index++] = 0; + } + if (state.density) { + for (i=0; i<3; i++) { + c = putc(0xa1, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + d->u.dmk.buf[id_index++] = 0xa1; + } + } + c = putc(dam, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + d->u.dmk.buf[id_index++] = dam; + if (dmk_incr(d) == 2) { + c = putc(dam, d->file); + if (c == EOF) state.status |= TRSDISK_WRITEFLT; + d->u.dmk.buf[id_index++] = dam; + } + + /* Initialize CRC */ + state.crc = calc_crc1((state.density + ? 0xcdb4 /* CRC of a1 a1 a1 */ + : 0xffff), dam); + + d->u.dmk.curbyte = id_index; + + } /* end if (d->emutype == ...) */ + + state.status |= TRSDISK_BUSY|TRSDISK_DRQ; + trs_disk_drq_interrupt(1); + trs_schedule_event(trs_disk_lostdata, state.currcommand, + 500000 * z80_state.clockMHz); + } + break; + + case TRSDISK_WRITEM: + state.last_readadr = -1; + if (d->writeprot) { + state.status = TRSDISK_WRITEPRT; + break; + } + trs_disk_unimpl(cmd, "write multiple"); + break; + + case TRSDISK_READADR: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("readadr 0x%02x drv %d ptk %d tk %d last %d %sden\n", + cmd, state.curdrive, d->phytrack, state.track, + state.last_readadr, state.density ? "d" : "s"); + } + state.data = 0; /* workaround for apparent SU1 bug */ + if (state.density) { + state.crc = 0xb230; /* CRC of a1 a1 a1 fe */ + } else { + state.crc = 0xef21; /* CRC of fe */ + } + if (d->emutype == REAL) { + real_readadr(); + break; + } else if (d->emutype == JV1 || d->emutype == JV3) { + int totbyt, i, ts, denok; + float a, b, bytlen; + id_index = search_adr(); + if (id_index == -1) { + state.status = TRSDISK_BUSY; + state.bytecount = 0; + trs_schedule_event(trs_disk_done, TRSDISK_NOTFOUND, + 1000000*z80_state.clockMHz); + break; + } + /* Compute how long it should have taken for this sector to come + by and delay by an appropriate number of t-states. This + makes the "A" command in HyperZap work (on emulated floppies + only). It is not terribly useful, since other important HyperZap + functions (like mixed-density formatting) do not work, while + SuperUtility and Trakcess both work fine without the delay feature. + Note: it would probably be better to assume the sectors are + positioned using nominal gap sizes (say, the ones that HyperZap + uses when generating tracks using the D/G subcommand) instead + of the even spacing nonsense below. + */ + if (d->emutype == JV1) { + /* Which sector header is next? Use a rough assumption + that the sectors are all the same angular length (bytlen). + */ + a = angle(); + bytlen = (1.0 - GAP1ANGLE - GAP4ANGLE)/((float)JV1_SECPERTRK); + i = (int)( (a - GAP1ANGLE) / bytlen + 1.0 ); + if (i >= JV1_SECPERTRK) { + /* Wrap around to start of track */ + i = 0; + } + b = ((float)i) * bytlen + GAP1ANGLE; + if (b < a) b += 1.0; + i += id_index; + } else { + /* Count data bytes on track. Also check if there + are any sectors of the correct density. */ + i = id_index; + totbyt = 0; + denok = 0; + for (;;) { + SectorId *sid = &d->u.jv3.id[d->u.jv3.sorted_id[i]]; + int dden = (sid->flags & JV3_DENSITY) != 0; + if (sid->track != d->phytrack || + (sid->flags & JV3_SIDE ? 1 : 0) != state.curside) break; + totbyt += (dden ? 1 : 2) * + id_index_to_size(d, d->u.jv3.sorted_id[i]); + if (dden == state.density) denok = 1; + i++; + } + if (!denok) { + /* No sectors of the correct density */ + state.status = TRSDISK_BUSY; + state.bytecount = 0; + trs_schedule_event(trs_disk_done, TRSDISK_NOTFOUND, + 1000000*z80_state.clockMHz); + break; + } + /* Which sector header is next? Use a rough assumption that + sectors are evenly spaced, taking up room proportional to + their data length (and twice as much for single density). + Here bytlen = angular size per byte. + */ + a = angle(); + b = GAP1ANGLE; + bytlen = (1.0 - GAP1ANGLE - GAP4ANGLE)/((float)totbyt); + i = id_index; + for (;;) { + SectorId *sid = &d->u.jv3.id[d->u.jv3.sorted_id[i]]; + if (sid->track != d->phytrack || + (sid->flags & JV3_SIDE ? 1 : 0) != state.curside) { + /* Wrap around to start of track */ + i = id_index; + b = 1 + GAP1ANGLE; + break; + } + if (b > a && (((sid->flags & JV3_DENSITY) != 0) == state.density)) { + break; + } + b += ((sid->flags & JV3_DENSITY) ? 1 : 2) * + id_index_to_size(d, d->u.jv3.sorted_id[i]) * bytlen; + i++; + } + } + /* Convert angular delay to t-states */ + ts = (d->inches == 5 ? 200000 : 166667) * (b - a) * z80_state.clockMHz; + + state.status = TRSDISK_BUSY; + state.last_readadr = i; + state.bytecount = 6; + trs_schedule_event(trs_disk_firstdrq, 0, ts); + if (trs_disk_debug_flags & DISKDEBUG_READADR) { + debug("readadr phytrack %d angle %f i %d ts %d\n", + d->phytrack, a, i, ts); + } + } else /* d->emutype == DMK */ { + /* Compute how far it will be to the next ID in the correct density */ + float a = angle(); + int ia = a * (d->inches ? TRKSIZE_DD : TRKSIZE_8DD); + int ib = 0; + int i, j, idamp, dden, prev_idamp, prev_dden, ts; + + dmk_get_track(d); + + for (j = 0; j < 2; j++) { + idamp = d->u.dmk.buf[0] + (d->u.dmk.buf[1] << 8); + dden = (idamp & DMK_DDEN_FLAG) != 0; + idamp = DMK_TKHDR_SIZE; + + for (i = 0; i < DMK_TKHDR_SIZE; i+=2) { + prev_idamp = idamp; + prev_dden = dden; + idamp = d->u.dmk.buf[i] + (d->u.dmk.buf[i+1] << 8); + if (idamp == 0) break; + dden = (idamp & DMK_DDEN_FLAG) != 0; + idamp &= DMK_IDAMP_BITS; + if (idamp >= DMK_TRACKLEN_MAX) break; + ib += (idamp - prev_idamp) * + ((!prev_dden && (d->u.dmk.sden || d->u.dmk.ignden)) ? 2 : 1); + if (ib > ia && dden == state.density && + d->u.dmk.buf[idamp] == 0xfe) goto found; + } + /* Next ID (if any) is past the index hole */ + ib = (d->inches ? TRKSIZE_DD : TRKSIZE_8DD); + } + /* no suitable ID found */ + state.status = TRSDISK_BUSY; + state.bytecount = 0; + trs_schedule_event(trs_disk_done, TRSDISK_NOTFOUND, + 1000000*z80_state.clockMHz); + break; + found: + /* Convert dden byte count to t-states */ + ts = ((float) ((ib - ia) * (d->inches == 5 ? 32 : 16))) + * z80_state.clockMHz; + + state.status = TRSDISK_BUSY; + state.last_readadr = i; + state.bytecount = 6; + state.crc = calc_crc1((state.density + ? 0xcdb4 /* CRC of a1 a1 a1 */ + : 0xffff), + d->u.dmk.buf[idamp]); + d->u.dmk.curbyte = idamp + dmk_incr(d); + trs_schedule_event(trs_disk_firstdrq, 0, ts); + if (trs_disk_debug_flags & DISKDEBUG_READADR) { + debug("readadr phytrack %d angle %f i %d ts %d\n", + d->phytrack, a, i, ts); + } + } + break; + + case TRSDISK_READTRK: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("readtrk 0x%02x drv %d ptk %d tk %d %sden\n", + cmd, state.curdrive, d->phytrack, state.track, + state.density ? "d" : "s"); + } + state.last_readadr = -1; + if (d->file == NULL) { + /* Data sheet says we wait forever for an index pulse, ugh */ + state.status = TRSDISK_BUSY; + state.bytecount = 0; + break; + } + if (d->emutype == REAL) { + real_readtrk(); + break; + } + if (d->emutype != DMK) { + trs_disk_unimpl(cmd, "read track"); + break; + } + dmk_get_track(d); + d->u.dmk.curbyte = DMK_TKHDR_SIZE; + if (disk[state.curdrive].inches == 5) { + state.bytecount = TRKSIZE_DD; /* decrement by 2's if SD */ + } else { + state.bytecount = TRKSIZE_8DD; /* decrement by 2's if SD */ + } + state.status = TRSDISK_BUSY|TRSDISK_DRQ; + trs_disk_drq_interrupt(1); + trs_schedule_event(trs_disk_lostdata, state.currcommand, + 500000 * z80_state.clockMHz); + break; + + case TRSDISK_WRITETRK: + state.last_readadr = -1; + /* Really a write track? */ + if (trs_model == 1 && (cmd == TRSDISK_P1771 || cmd == TRSDISK_P1791)) { + /* No; emulate Percom Doubler */ + state.currcommand = TRSDISK_FORCEINT; + if (trs_disk_doubler & TRSDISK_PERCOM) { + trs_disk_set_controller(cmd); + /* The Doubler's 1791 is hardwired to double density */ + state.density = (state.controller == TRSDISK_P1791); + } + } else { + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("writetrk 0x%02x drv %d ptk %d tk %d %sden\n", + cmd, state.curdrive, d->phytrack, state.track, + state.density ? "d" : "s"); + } + /* Yes; a real write track */ + if (d->emutype != REAL && d->writeprot) { + state.status = TRSDISK_WRITEPRT; + break; + } + state.status = 0; + if (d->file == NULL) { + /* Data sheet says we wait forever for an index pulse, ugh */ + state.status = TRSDISK_BUSY; + state.bytecount = 0; + break; + } + if (d->emutype == JV3) { + /* Erase track if already formatted */ + int i; + for (i=0; i<=d->u.jv3.last_used_id; i++) { + if (d->u.jv3.id[i].track == d->phytrack && + ((d->u.jv3.id[i].flags & JV3_SIDE) != 0) == state.curside) { + jv3_free_sector(d, i); + } + } + } else if (d->emutype == REAL) { + d->u.real.size_code = -1; /* watch for first, then check others match*/ + d->u.real.fmt_nbytes = 0; /* size of PC formatting command buffer */ + } else if (d->emutype == DMK) { + if (state.density && d->u.dmk.sden) { + error("DMK disk created as single density only"); + state.status |= TRSDISK_WRITEFLT; + } + if (state.curside && d->u.dmk.nsides == 1) { + error("DMK disk created as single sided only"); + state.status |= TRSDISK_WRITEFLT; + } + d->u.dmk.curtrack = d->phytrack; + d->u.dmk.curside = state.curside; + memset(d->u.dmk.buf, 0, sizeof(d->u.dmk.buf)); + d->u.dmk.curbyte = DMK_TKHDR_SIZE; + d->u.dmk.nextidam = 0; + } + state.status |= TRSDISK_BUSY|TRSDISK_DRQ; + trs_disk_drq_interrupt(1); + trs_schedule_event(trs_disk_lostdata, state.currcommand, + 500000 * z80_state.clockMHz); + state.format = FMT_GAP0; + state.format_gapcnt = 0; + if (disk[state.curdrive].inches == 5) { + state.bytecount = TRKSIZE_DD; /* decrement by 2's if SD */ + } else { + state.bytecount = TRKSIZE_8DD; /* decrement by 2's if SD */ + } + } + break; + + case TRSDISK_FORCEINT: + if (trs_disk_debug_flags & DISKDEBUG_FDCCMD) { + debug("forceint 0x%02x\n", cmd); + } + /* Stop whatever is going on and forget it */ + trs_cancel_event(); + state.status = 0; + type1_status(); + if ((cmd & 0x07) != 0) { + /* Conditional interrupt features not implemented. */ + trs_disk_unimpl(cmd, "force interrupt with condition"); + } else if ((cmd & 0x08) != 0) { + /* Immediate interrupt */ + trs_disk_intrq_interrupt(1); + } else { + trs_disk_intrq_interrupt(0); + } + break; + } +} + +/* Interface to real floppy drive */ +int +real_rate(DiskState *d) +{ + if (d->inches == 5) { + if (d->u.real.rps == 5) { + return 2; + } else if (d->u.real.rps == 6) { + return 1; + } + } else if (d->inches == 8) { + return 0; + } + trs_disk_unimpl(state.currcommand, "real_rate internal error"); + return 1; +} + +void +real_error(DiskState *d, unsigned int flags, char *msg) +{ + time_t now = time(NULL); + if (now > d->u.real.empty_timeout) { + d->u.real.empty_timeout = time(NULL) + EMPTY_TIMEOUT; + d->u.real.empty = 1; + } + if (trs_disk_debug_flags & DISKDEBUG_REALERR) { + debug("error on real_%s\n", msg); + } +} + +void +real_ok(DiskState *d) +{ + d->u.real.empty_timeout = time(NULL) + EMPTY_TIMEOUT; + d->u.real.empty = 0; +} + +int +real_check_empty(DiskState *d) +{ +#if __linux + int reset_now = 0; + struct floppy_raw_cmd raw_cmd; + int res, i = 0; + sigset_t set, oldset; + + if (time(NULL) <= d->u.real.empty_timeout) return d->u.real.empty; + + if (d->file == NULL) { + d->u.real.empty = 1; + return 1; + } + + ioctl(fileno(d->file), FDRESET, &reset_now); + + /* Do a read id command. Assume a disk is in the drive iff + we get a nonnegative status back from the ioctl. */ + memset(&raw_cmd, 0, sizeof(raw_cmd)); + raw_cmd.rate = real_rate(d); + raw_cmd.flags = FD_RAW_INTR; + raw_cmd.cmd[i++] = state.density ? 0x4a : 0x0a; /* read ID */ + raw_cmd.cmd[i++] = state.curside ? 4 : 0; + raw_cmd.cmd_count = i; + raw_cmd.data = NULL; + raw_cmd.length = 0; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (res < 0) { + real_error(d, raw_cmd.flags, "check_empty"); + } else { + real_ok(d); + } +#else + trs_disk_unimpl(state.currcommand, "check for empty on real floppy"); +#endif + return d->u.real.empty; +} + +void +real_verify() +{ + /* Verify that head is on the expected track */ + /*!! ignore for now*/ +} + +void +real_restore(curdrive) +{ +#if __linux + DiskState *d = &disk[curdrive]; + struct floppy_raw_cmd raw_cmd; + int res, i = 0; + sigset_t set, oldset; + + raw_cmd.flags = FD_RAW_INTR; + raw_cmd.cmd[i++] = FD_RECALIBRATE; + raw_cmd.cmd[i++] = 0; + raw_cmd.cmd_count = i; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (res < 0) { + real_error(d, raw_cmd.flags, "restore"); + state.status |= TRSDISK_SEEKERR; + return; + } +#else + trs_disk_unimpl(state.currcommand, "restore real floppy"); +#endif +} + +void +real_seek() +{ +#if __linux + DiskState *d = &disk[state.curdrive]; + struct floppy_raw_cmd raw_cmd; + int res, i = 0; + sigset_t set, oldset; + + /* Always use a recal if going to track 0. This should help us + recover from confusion about what track the disk is really on. + I'm still not sure why the confusion sometimes arises. */ + if (d->phytrack == 0) { + real_restore(state.curdrive); + return; + } + + state.last_readadr = -1; + memset(&raw_cmd, 0, sizeof(raw_cmd)); + raw_cmd.length = 256; + raw_cmd.data = NULL; + raw_cmd.rate = real_rate(d); + raw_cmd.flags = FD_RAW_INTR; + raw_cmd.cmd[i++] = FD_SEEK; + raw_cmd.cmd[i++] = 0; + raw_cmd.cmd[i++] = d->phytrack * d->real_step; + raw_cmd.cmd_count = i; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (res < 0) { + real_error(d, raw_cmd.flags, "seek"); + state.status |= TRSDISK_SEEKERR; + return; + } +#else + trs_disk_unimpl(state.currcommand, "seek real floppy"); +#endif +} + +void +real_read() +{ +#if __linux + DiskState *d = &disk[state.curdrive]; + struct floppy_raw_cmd raw_cmd; + int res, i, retry, new_status; + sigset_t set, oldset; + + /* Try once at each supported sector size */ + retry = 0; + for (;;) { + state.status = 0; + new_status = 0; + memset(&raw_cmd, 0, sizeof(raw_cmd)); + raw_cmd.rate = real_rate(d); + raw_cmd.flags = FD_RAW_READ | FD_RAW_INTR; + i = 0; + raw_cmd.cmd[i++] = state.density ? 0x46 : 0x06; + raw_cmd.cmd[i++] = state.curside ? 4 : 0; + raw_cmd.cmd[i++] = state.track; + raw_cmd.cmd[i++] = state.curside; + raw_cmd.cmd[i++] = state.sector; + raw_cmd.cmd[i++] = d->u.real.size_code; + raw_cmd.cmd[i++] = 255; + raw_cmd.cmd[i++] = 0x0a; + raw_cmd.cmd[i++] = 0xff; /* unused */ + raw_cmd.cmd_count = i; + raw_cmd.data = (void*) d->u.real.buf; + raw_cmd.length = 128 << d->u.real.size_code; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (res < 0) { + real_error(d, raw_cmd.flags, "read"); + new_status |= TRSDISK_NOTFOUND; + } else { + real_ok(d); /* premature? */ + if (raw_cmd.reply[1] & 0x04) { + /* Could have been due to wrong sector size, so we'll retry + internally in each other size before returning an error. */ + if (trs_disk_debug_flags & DISKDEBUG_REALSIZE) { + debug("real_read not fnd: side %d tk %d sec %d size 0%d phytk %d\n", + state.curside, state.track, state.sector, d->u.real.size_code, + d->phytrack*d->real_step); + } +#if SIZERETRY + d->u.real.size_code = (d->u.real.size_code + 1) % 4; + if (++retry < 4) { + continue; /* retry */ + } +#endif + new_status |= TRSDISK_NOTFOUND; + } + if (raw_cmd.reply[1] & 0x81) new_status |= TRSDISK_NOTFOUND; + if (raw_cmd.reply[1] & 0x20) { + new_status |= TRSDISK_CRCERR; + if (!(raw_cmd.reply[2] & 0x20)) new_status |= TRSDISK_NOTFOUND; + } + if (raw_cmd.reply[1] & 0x10) new_status |= TRSDISK_LOSTDATA; + if (raw_cmd.reply[2] & 0x40) { + if (state.controller == TRSDISK_P1771) { + if (trs_disk_truedam) { + new_status |= TRSDISK_1771_F8; + } else { + new_status |= TRSDISK_1771_FA; + } + } else { + new_status |= TRSDISK_1791_F8; + } + } + if (raw_cmd.reply[2] & 0x20) new_status |= TRSDISK_CRCERR; + if (raw_cmd.reply[2] & 0x13) new_status |= TRSDISK_NOTFOUND; + if ((new_status & TRSDISK_NOTFOUND) == 0) { + /* Start read */ + state.status = TRSDISK_BUSY; + trs_schedule_event(trs_disk_firstdrq, new_status, 64); + state.bytecount = size_code_to_size(d->u.real.size_code); + return; + } + } + break; /* exit retry loop */ + } + /* Sector not found; fail */ + state.status = TRSDISK_BUSY; + trs_schedule_event(trs_disk_done, new_status, 512); +#else + trs_disk_unimpl(state.currcommand, "read real floppy"); +#endif +} + +void +real_write() +{ +#if __linux + DiskState *d = &disk[state.curdrive]; + struct floppy_raw_cmd raw_cmd; + int res, i = 0; + sigset_t set, oldset; + + state.status = 0; + memset(&raw_cmd, 0, sizeof(raw_cmd)); + raw_cmd.rate = real_rate(d); + raw_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR; + if (trs_disk_truedam && !state.density) { + switch (state.currcommand & 0x03) { + case 0: + case 3: + break; + case 1: + error("writing FA DAM on real floppy"); + break; + case 2: + error("writing F9 DAM on real floppy"); + break; + } + } + /* Use F8 DAM for F8, F9, or FA */ + raw_cmd.cmd[i++] = ((state.currcommand & + (state.controller == TRSDISK_P1771 ? 0x03 : 0x01)) + ? 0x09 : 0x05) | (state.density ? 0x40 : 0x00); + raw_cmd.cmd[i++] = state.curside ? 4 : 0; + raw_cmd.cmd[i++] = state.track; + raw_cmd.cmd[i++] = state.curside; + raw_cmd.cmd[i++] = state.sector; + raw_cmd.cmd[i++] = d->u.real.size_code; + raw_cmd.cmd[i++] = 255; + raw_cmd.cmd[i++] = 0x0a; + raw_cmd.cmd[i++] = 0xff; /* 256 */ + raw_cmd.cmd_count = i; + raw_cmd.data = (void*) d->u.real.buf; + raw_cmd.length = 128 << d->u.real.size_code; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (res < 0) { + real_error(d, raw_cmd.flags, "write"); + state.status |= TRSDISK_NOTFOUND; + } else { + real_ok(d); /* premature? */ + if (raw_cmd.reply[1] & 0x04) { + state.status |= TRSDISK_NOTFOUND; + /* Could have been due to wrong sector size. Presumably + the Z-80 software will do some retries, so we'll cause + it to try the next sector size next time. */ + if (trs_disk_debug_flags & DISKDEBUG_REALSIZE) { + debug("real_write not found: side %d tk %d sec %d size 0%d phytk %d\n", + state.curside, state.track, state.sector, d->u.real.size_code, + d->phytrack*d->real_step); + } +#if SIZERETRY + d->u.real.size_code = (d->u.real.size_code + 1) % 4; +#endif + } + if (raw_cmd.reply[1] & 0x81) state.status |= TRSDISK_NOTFOUND; + if (raw_cmd.reply[1] & 0x20) { + state.status |= TRSDISK_CRCERR; + if (!(raw_cmd.reply[2] & 0x20)) state.status |= TRSDISK_NOTFOUND; + } + if (raw_cmd.reply[1] & 0x10) state.status |= TRSDISK_LOSTDATA; + if (raw_cmd.reply[1] & 0x02) { + state.status |= TRSDISK_WRITEPRT; + d->writeprot = 1; + } else { + d->writeprot = 0; + } + if (raw_cmd.reply[2] & 0x20) state.status |= TRSDISK_CRCERR; + if (raw_cmd.reply[2] & 0x13) state.status |= TRSDISK_NOTFOUND; + } + state.bytecount = 0; + trs_disk_drq_interrupt(0); + state.status |= TRSDISK_BUSY; + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 512); +#else + trs_disk_unimpl(state.currcommand, "write real floppy"); +#endif +} + +void +real_readadr() +{ +#if __linux + DiskState *d = &disk[state.curdrive]; + struct floppy_raw_cmd raw_cmd; + int res, i, new_status; + sigset_t set, oldset; + + state.status = 0; + new_status = 0; + memset(&raw_cmd, 0, sizeof(raw_cmd)); + raw_cmd.rate = real_rate(d); + raw_cmd.flags = FD_RAW_INTR; + i = 0; + raw_cmd.cmd[i++] = state.density ? 0x4a : 0x0a; + raw_cmd.cmd[i++] = state.curside ? 4 : 0; + raw_cmd.cmd_count = i; + raw_cmd.data = NULL; + raw_cmd.length = 0; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + state.bytecount = 0; + if (res < 0) { + real_error(d, raw_cmd.flags, "readadr"); + new_status |= TRSDISK_NOTFOUND; + } else { + real_ok(d); /* premature? */ + if (raw_cmd.reply[1] & 0x85) new_status |= TRSDISK_NOTFOUND; + if (raw_cmd.reply[1] & 0x20) new_status |= TRSDISK_CRCERR; + if (raw_cmd.reply[1] & 0x10) new_status |= TRSDISK_LOSTDATA; + if (raw_cmd.reply[2] & 0x40) { + if (state.controller == TRSDISK_P1771) { + new_status |= TRSDISK_1771_FA; + } else { + new_status |= TRSDISK_1791_F8; + } + } + if (raw_cmd.reply[2] & 0x20) new_status |= TRSDISK_CRCERR; + if (raw_cmd.reply[2] & 0x13) new_status |= TRSDISK_NOTFOUND; + if ((new_status & TRSDISK_NOTFOUND) == 0) { + state.status = TRSDISK_BUSY; + trs_schedule_event(trs_disk_firstdrq, new_status, 64); + memcpy(d->u.real.buf, &raw_cmd.reply[3], 4); + d->u.real.buf[4] = d->u.real.buf[5] = 0; /* CRC not emulated */ + state.bytecount = 6; + d->u.real.size_code = d->u.real.buf[3]; /* update hint */ + return; + } + } + state.last_readadr = -1; + /* Sector not found; fail */ + state.status = TRSDISK_BUSY; + trs_schedule_event(trs_disk_done, new_status, 200000*z80_state.clockMHz); +#else + trs_disk_unimpl(state.currcommand, "read address on real floppy"); +#endif +} + +void +real_readtrk() +{ + trs_disk_unimpl(state.currcommand, "read track on real floppy"); +} + +void +real_writetrk() +{ +#if __linux + DiskState *d = &disk[state.curdrive]; + struct floppy_raw_cmd raw_cmd; + int res, i, gap3; + sigset_t set, oldset; + state.status = 0; + + /* Compute a usable gap3 */ + /* Constants based on IBM format as explained in "The floppy user guide" + by Michael Haardt, Alain Knaff, and David C. Niemi */ + /* The formulas and constants are not factored out, in case some of + those that are the same now need to change when I learn more. */ + if (state.density) { + /* MFM recording */ + if (d->inches == 5) { + /* 5" DD = 250 kHz MFM */ + gap3 = (TRKSIZE_DD - 161 - /*slop*/16)/(d->u.real.fmt_nbytes / 4) + - 62 - (128 << d->u.real.size_code) - /*slop*/2; + } else { + /* 8" DD = 5" HD = 500 kHz MFM */ + gap3 = (TRKSIZE_8DD - 161 - /*slop*/16)/(d->u.real.fmt_nbytes / 4) + - 62 - (128 << d->u.real.size_code) - /*slop*/2; + } + } else { + /* FM recording */ + if (d->inches == 5) { + /* 5" SD = 250 kHz FM (125 kbps) */ + gap3 = (TRKSIZE_SD - 99 - /*slop*/16)/(d->u.real.fmt_nbytes / 4) + - 33 - (128 << d->u.real.size_code) - /*slop*/2; + } else { + /* 8" SD = 5" HD operated in FM = 500 kHz FM (250 kbps) */ + gap3 = (TRKSIZE_8SD - 99 - /*slop*/16)/(d->u.real.fmt_nbytes / 4) + - 33 - (128 << d->u.real.size_code) - /*slop*/2; + } + } + if (gap3 < 1) { + error("gap3 too small"); + gap3 = 1; + } else if (gap3 > 0xff) { + gap3 = 0xff; + } + + /* Do the actual write track */ + memset(&raw_cmd, 0, sizeof(raw_cmd)); + raw_cmd.rate = real_rate(d); + raw_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR; + i = 0; + raw_cmd.cmd[i++] = 0x0d | (state.density ? 0x40 : 0x00); + raw_cmd.cmd[i++] = state.curside ? 4 : 0; + raw_cmd.cmd[i++] = d->u.real.size_code; + raw_cmd.cmd[i++] = d->u.real.fmt_nbytes / 4; + raw_cmd.cmd[i++] = gap3; + raw_cmd.cmd[i++] = d->u.real.fmt_fill; + raw_cmd.cmd_count = i; + raw_cmd.data = (void*) d->u.real.buf; + raw_cmd.length = d->u.real.fmt_nbytes; + + if (trs_disk_debug_flags & DISKDEBUG_GAPS) { + debug("real_writetrk size 0%d secs %d gap3 %d fill 0x%02x hex data ", + d->u.real.size_code, d->u.real.fmt_nbytes/4, gap3, + d->u.real.fmt_fill); + for (i=0; i<d->u.real.fmt_nbytes; i+=4) { + debug("%02x%02x%02x%02x ", d->u.real.buf[i], d->u.real.buf[i+1], + d->u.real.buf[i+2], d->u.real.buf[i+3]); + } + debug("\n"); + } + + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + trs_paused = 1; + res = ioctl(fileno(d->file), FDRAWCMD, &raw_cmd); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (res < 0) { + real_error(d, raw_cmd.flags, "writetrk"); + state.status |= TRSDISK_WRITEFLT; + } else { + real_ok(d); /* premature? */ + if (raw_cmd.reply[1] & 0x85) state.status |= TRSDISK_NOTFOUND; + if (raw_cmd.reply[1] & 0x20) state.status |= TRSDISK_CRCERR; + if (raw_cmd.reply[1] & 0x10) state.status |= TRSDISK_LOSTDATA; + if (raw_cmd.reply[1] & 0x02) { + state.status |= TRSDISK_WRITEPRT; + d->writeprot = 1; + } else { + d->writeprot = 0; + } + if (raw_cmd.reply[2] & 0x20) state.status |= TRSDISK_CRCERR; + if (raw_cmd.reply[2] & 0x13) state.status |= TRSDISK_NOTFOUND; + } + state.bytecount = 0; + trs_disk_drq_interrupt(0); + state.status |= TRSDISK_BUSY; + if (trs_event_scheduled() == trs_disk_lostdata) { + trs_cancel_event(); + } + trs_schedule_event(trs_disk_done, 0, 512); +#else + trs_disk_unimpl(state.currcommand, "write track on real floppy"); +#endif +} + diff --git a/trs_disk.h b/trs_disk.h new file mode 100644 index 0000000..0c0406e --- /dev/null +++ b/trs_disk.h @@ -0,0 +1,214 @@ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Sat Jan 2 20:39:56 PST 1999 by mann */ + +/* + * Emulate Model-I or Model-III disk controller + */ + +extern void trs_disk_init(int reset_button); +extern void trs_disk_select_write(unsigned char data); +extern unsigned char trs_disk_track_read(void); +extern void trs_disk_track_write(unsigned char data); +extern unsigned char trs_disk_sector_read(void); +extern void trs_disk_sector_write(unsigned char data); +extern unsigned char trs_disk_data_read(void); +extern void trs_disk_data_write(unsigned char data); +extern unsigned char trs_disk_status_read(void); +extern void trs_disk_command_write(unsigned char cmd); +extern unsigned char trs_disk_interrupt_read(void); /* M3 only */ +extern void trs_disk_interrupt_write(unsigned char mask); /* M3 only */ + +extern void trs_disk_setstep(int unit, int value); +extern int trs_disk_getstep(int unit); +extern void trs_disk_setsize(int unit, int value); +extern int trs_disk_getsize(int unit); + +extern int trs_disk_doubler; +extern char* trs_disk_dir; +extern unsigned short trs_disk_changecount; +extern int trs_disk_truedam; + +/* Values for trs_disk_doubler flag word */ +#define TRSDISK_NODOUBLER 0 +#define TRSDISK_PERCOM 1 +#define TRSDISK_TANDY 2 +#define TRSDISK_BOTH 3 + +/* Model I drive select register -- address bits 0,1 not decoded */ +#define TRSDISK_SELECT(addr) (((addr)&~3) == 0x37e0) +#define TRSDISK_0 0x1 +#define TRSDISK_1 0x2 +#define TRSDISK_2 0x4 +#define TRSDISK_3 0x8 +#define TRSDISK_SIDE 0x8 /* shared on Model I */ +#define TRSDISK_4 0x3 /* fake value for emulator only */ +#define TRSDISK_5 0x5 /* fake value for emulator only */ +#define TRSDISK_6 0x6 /* fake value for emulator only */ +#define TRSDISK_7 0x7 /* fake value for emulator only */ + +/* FDC address space in Model I */ +#define TRSDISK_FDC 0x37ec +#define TRSDISK_FDCLEN 4 /* 4 bytes are mapped, offsets as follows: */ +#define TRSDISK_COMMAND 0x37ec /* writing */ +#define TRSDISK_STATUS 0x37ec /* reading */ +#define TRSDISK_TRACK 0x37ed +#define TRSDISK_SECTOR 0x37ee +#define TRSDISK_DATA 0x37ef + +/* FDC port space in Model III */ +#define TRSDISK3_INTERRUPT 0xe4 +#define TRSDISK3_COMMAND 0xf0 /* writing */ +#define TRSDISK3_STATUS 0xf0 /* reading */ +#define TRSDISK3_TRACK 0xf1 +#define TRSDISK3_SECTOR 0xf2 +#define TRSDISK3_DATA 0xf3 +#define TRSDISK3_SELECT 0xf4 /* write-only */ + +/* Interrupt register bits (M3 only) */ +#define TRSDISK3_INTRQ 0x80 +#define TRSDISK3_DRQ 0x40 +#define TRSDISK3_RESET 0x20 /* reset button, not really disk related */ + +/* Select register bits (M3 only). Drives 0-3 same as model 1. */ +#define TRSDISK3_MFM 0x80 +#define TRSDISK3_WAIT 0x40 +#define TRSDISK3_PRECOMP 0x20 +#define TRSDISK3_SIDE 0x10 /* not shared! */ + +/* Commands */ +#define TRSDISK_CMDMASK 0xf0 /* high nybble selects which command */ + +/* Type I commands: cccchvrr, where + cccc = command number + h = head load + v = verify (i.e., read next address to check we're on the right track) + rr = step rate: 00=6ms, 01=12ms, 10=20ms, 11=40ms +*/ +#define TRSDISK_RESTORE 0x00 +#define TRSDISK_SEEK 0x10 +#define TRSDISK_STEP 0x20 /* don't update track reg */ +#define TRSDISK_STEPU 0x30 /* do update track reg */ +#define TRSDISK_STEPIN 0x40 +#define TRSDISK_STEPINU 0x50 +#define TRSDISK_STEPOUT 0x60 +#define TRSDISK_STEPOUTU 0x70 +#define TRSDISK_UBIT 0x10 +#define TRSDISK_HBIT 0x08 +#define TRSDISK_VBIT 0x04 + +/* Type II commands: ccccbecd, where + cccc = command number + e = delay for head engage (10ms) + 1771: + b = 1=IBM format, 0=nonIBM format + cd = select data address mark (writes only, 00 for reads): + 00=FB (normal), 01=FA, 10=F9, 11=F8 (deleted) + 1791: + b = side expected + c = side compare (0=disable, 1=enable) + d = select data address mark (writes only, 0 for reads): + 0=FB (normal), 1=F8 (deleted) +*/ +#define TRSDISK_READ 0x80 /* single sector */ +#define TRSDISK_READM 0x90 /* multiple sectors */ +#define TRSDISK_WRITE 0xa0 +#define TRSDISK_WRITEM 0xb0 +#define TRSDISK_MBIT 0x10 +#define TRSDISK_BBIT 0x08 +#define TRSDISK_EBIT 0x04 +#define TRSDISK_CBIT 0x02 +#define TRSDISK_DBIT 0x01 + +/* Type III commands: ccccxxxs (?), where + cccc = command number + xxx = ?? (usually 010) + s = 1=READTRK no synchronize; otherwise 0 +*/ +#define TRSDISK_READADR 0xc0 +#define TRSDISK_READTRK 0xe0 +#define TRSDISK_WRITETRK 0xf0 +/* These commands are peculiar to the Percom Doubler */ +#define TRSDISK_P1771 0xfe /* Select 1771 single density controller */ +#define TRSDISK_P1791 0xff /* Select 1791 double density controller */ + +/* Type IV command: cccciiii, where + cccc = command number + iiii = bitmask of events to terminate and interrupt on (unused on trs80); + 0000 for immediate terminate with no interrupt. + */ +#define TRSDISK_FORCEINT 0xd0 + +/* These "commands" are peculiar to the Radio Shack Doubler. They + are written to the sector register, not the command register! + */ +#define TRSDISK_R1791 0x80 +#define TRSDISK_R1771 0xa0 +#define TRSDISK_NOPRECMP 0xc0 +#define TRSDISK_PRECMP 0xe0 + +/* Type I status bits */ +#define TRSDISK_BUSY 0x01 +#define TRSDISK_INDEX 0x02 +#define TRSDISK_TRKZERO 0x04 +#define TRSDISK_CRCERR 0x08 +#define TRSDISK_SEEKERR 0x10 +#define TRSDISK_HEADENGD 0x20 +#define TRSDISK_WRITEPRT 0x40 +#define TRSDISK_NOTRDY 0x80 + +/* Read status bits */ +/* TRSDISK_BUSY 0x01*/ +#define TRSDISK_DRQ 0x02 +#define TRSDISK_LOSTDATA 0x04 +/* TRSDISK_CRCERR 0x08*/ +#define TRSDISK_NOTFOUND 0x10 +#define TRSDISK_RECTYPE 0x60 +#define TRSDISK_1771_FB 0x00 +#define TRSDISK_1771_FA 0x20 +#define TRSDISK_1771_F9 0x40 +#define TRSDISK_1771_F8 0x60 +#define TRSDISK_1791_FB 0x00 +#define TRSDISK_1791_F8 0x20 +/* TRSDISK_NOTRDY 0x80*/ + +/* Write status bits */ +/* TRSDISK_BUSY 0x01*/ +/* TRSDISK_DRQ 0x02*/ +/* TRSDISK_LOSTDATA 0x04*/ +/* TRSDISK_CRCERR 0x08*/ +/* TRSDISK_NOTFOUND 0x10*/ +#define TRSDISK_WRITEFLT 0x20 +/* TRSDISK_WRITEPRT 0x40*/ +/* TRSDISK_NOTRDY 0x80*/ + +/* Read address status bits */ +/* TRSDISK_BUSY 0x01*/ +/* TRSDISK_DRQ 0x02*/ +/* TRSDISK_LOSTDATA 0x04*/ +/* TRSDISK_CRCERR 0x08*/ +/* TRSDISK_NOTFOUND 0x10*/ +/* unused, mbz 0x60*/ +/* TRSDISK_NOTRDY 0x80*/ + +/* Read track status bits */ +/* TRSDISK_BUSY 0x01*/ +/* TRSDISK_DRQ 0x02*/ +/* TRSDISK_LOSTDATA 0x04*/ +/* unused, mbz 0x78*/ +/* TRSDISK_NOTRDY 0x80*/ + +/* Write track status bits */ +/* TRSDISK_BUSY 0x01*/ +/* TRSDISK_DRQ 0x02*/ +/* TRSDISK_LOSTDATA 0x04*/ +/* unused, mbz 0x18*/ +/* TRSDISK_WRITEFLT 0x20*/ +/* TRSDISK_WRITEPRT 0x40*/ +/* TRSDISK_NOTRDY 0x80*/ + diff --git a/trs_hard.c b/trs_hard.c new file mode 100644 index 0000000..eee0abc --- /dev/null +++ b/trs_hard.c @@ -0,0 +1,495 @@ +/* Copyright (c) 2000, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Thu May 18 00:42:46 PDT 2000 by mann */ + +/* + * Emulation of the Radio Shack TRS-80 Model I/III/4/4P + * hard disk controller. This is a Western Digital WD1000/WD1010 + * mapped at ports 0xc8-0xcf, plus control registers at 0xc0-0xc1. + */ + +#include <errno.h> +#include <string.h> +#include "trs.h" +#include "trs_hard.h" +#include "reed.h" + +/*#define HARDDEBUG1 1*/ /* show detail on all port i/o */ +/*#define HARDDEBUG2 1*/ /* show all commands */ +/*#define HARDDEBUG3 1*/ /* show failure to open a drive */ + +/* Private types and data */ + +/* Structure describing one drive */ +typedef struct { + FILE* file; + /* Values decoded from rhh */ + int writeprot; + int cyls; /* cyls per drive */ + int heads; /* tracks per cyl */ + int secs; /* secs per track */ +} Drive; + +/* Structure describing controller state */ +typedef struct { + /* Controller present? Yes if we have any drives, no if none */ + int present; + + /* Controller register images */ + Uchar control; + Uchar data; + Uchar error; + Uchar seccnt; + Uchar secnum; + Ushort cyl; + Uchar drive; + Uchar head; + Uchar status; + Uchar command; + + /* Number of bytes already done in current read/write */ + int bytesdone; + + /* Drive geometries and files */ + Drive d[TRS_HARD_MAXDRIVES]; +} State; + +static State state; + +/* Forward */ +static int hard_data_in(); +static void hard_data_out(int value); +static void hard_restore(int cmd); +static void hard_read(int cmd); +static void hard_write(int cmd); +static void hard_verify(int cmd); +static void hard_format(int cmd); +static void hard_init(int cmd); +static void hard_seek(int cmd); +static int open_drive(int drive); +static int find_sector(int newstatus); +static int open_drive(int n); +static void set_dir_cyl(int cyl); + +/* Powerup or reset button */ +void trs_hard_init(int reset_button) +{ + int i; + state.control = 0; + state.data = 0; + state.error = 0; + state.seccnt = 0; + state.secnum = 0; + state.cyl = 0; + state.drive = 0; + state.head = 0; + state.status = 0; + state.command = 0; + for (i=0; i<TRS_HARD_MAXDRIVES; i++) { + if (state.d[i].file) { + fclose(state.d[i].file); + state.d[i].file = NULL; + } + state.d[i].writeprot = 0; + state.d[i].cyls = 0; + state.d[i].heads = 0; + state.d[i].secs = 0; + } +} + +/* Read from an I/O port mapped to the controller */ +int trs_hard_in(int port) +{ + int v; + if (state.present) { + switch (port) { + case TRS_HARD_WP: { + int i; + v = 0; + for (i=0; i<TRS_HARD_MAXDRIVES; i++) { + open_drive(i); + if (state.d[i].writeprot) { + v |= TRS_HARD_WPBIT(i) | TRS_HARD_WPSOME; + } + } + break; } + case TRS_HARD_CONTROL: + v = state.control; + break; + case TRS_HARD_DATA: + v = hard_data_in(); + break; + case TRS_HARD_ERROR: + v = state.error; + break; + case TRS_HARD_SECCNT: + v = state.seccnt; + break; + case TRS_HARD_SECNUM: + v = state.secnum; + break; + case TRS_HARD_CYLLO: + v = state.cyl & 0xff; + break; + case TRS_HARD_CYLHI: + v = (state.cyl >> 8) & 0xff; + break; + case TRS_HARD_SDH: + v = (state.drive << 3) | state.head; + break; + case TRS_HARD_STATUS: + v = state.status; + break; + default: + v = 0xff; + break; + } + } else { + v = 0xff; + } +#if HARDDEBUG1 + debug("%02x -> %02x\n", port, v); +#endif + return v; +} + +/* Write to an I/O port mapped to the controller */ +void trs_hard_out(int port, int value) +{ +#if HARDDEBUG1 + debug("%02x <- %02x\n", port, value); +#endif + switch (port) { + case TRS_HARD_WP: + break; + case TRS_HARD_CONTROL: + if (value & TRS_HARD_SOFTWARE_RESET) { + trs_hard_init(1); + } + if ((value & TRS_HARD_DEVICE_ENABLE) && state.present == 0) { + int i; + for (i=0; i<TRS_HARD_MAXDRIVES; i++) { + if (open_drive(i)) state.present = 1; + } + } + state.control = value; + break; + case TRS_HARD_DATA: + hard_data_out(value); + break; + case TRS_HARD_PRECOMP: + break; + case TRS_HARD_SECCNT: + state.seccnt = value; + break; + case TRS_HARD_SECNUM: + state.secnum = value; + break; + case TRS_HARD_CYLLO: + state.cyl = (state.cyl & 0xff00) | (value & 0x00ff); + break; + case TRS_HARD_CYLHI: + state.cyl = (state.cyl & 0x00ff) | ((value << 8) & 0xff00); + break; + case TRS_HARD_SDH: + if (value & TRS_HARD_SIZEMASK) { + error("trs_hard: size bits set to nonzero value (0x%02x)", + value & TRS_HARD_SIZEMASK); + } + state.drive = (value & TRS_HARD_DRIVEMASK) >> TRS_HARD_DRIVESHIFT; + state.head = (value & TRS_HARD_HEADMASK) >> TRS_HARD_HEADSHIFT; +#if 0 + if (!open_drive(state.drive)) state.status &= ~TRS_HARD_READY; +#else + /* Ready, but perhaps not able! This way seems to work better; it + * avoids a long delay in the Model 4P boot ROM when there is no + * unit 0. */ + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE; +#endif + break; + + case TRS_HARD_COMMAND: + state.bytesdone = 0; + state.command = value; + switch (value & TRS_HARD_CMDMASK) { + default: + error("trs_hard: unknown command 0x%02x", value); + break; + case TRS_HARD_RESTORE: + hard_restore(value); + break; + case TRS_HARD_READ: + hard_read(value); + break; + case TRS_HARD_WRITE: + hard_write(value); + break; + case TRS_HARD_VERIFY: + hard_verify(value); + break; + case TRS_HARD_FORMAT: + hard_format(value); + break; + case TRS_HARD_INIT: + hard_init(value); + break; + case TRS_HARD_SEEK: + hard_seek(value); + break; + } + break; + + default: + break; + } +} + +static void hard_restore(int cmd) +{ +#if HARDDEBUG2 + debug("hard_restore drive %d\n", state.drive); +#endif + state.cyl = 0; + /*!! should anything else be zeroed? */ + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE; +} + +static void hard_read(int cmd) +{ +#if HARDDEBUG2 + debug("hard_read drive %d cyl %d hd %d sec %d\n", + state.drive, state.cyl, state.head, state.secnum); +#endif + if (cmd & TRS_HARD_MULTI) { + error("trs_hard: multi-sector read not supported (0x%02x)", cmd); + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_ERR; + state.error = TRS_HARD_ABRTERR; + return; + } + find_sector(TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_DRQ); +} + +static void hard_write(int cmd) +{ +#if HARDDEBUG2 + debug("hard_write drive %d cyl %d hd %d sec %d\n", + state.drive, state.cyl, state.head, state.secnum); +#endif + if (cmd & TRS_HARD_MULTI) { + error("trs_hard: multi-sector write not supported (0x%02x)", cmd); + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_ERR; + state.error = TRS_HARD_ABRTERR; + return; + } + find_sector(TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_DRQ); +} + +static void hard_verify(int cmd) +{ +#if HARDDEBUG2 + debug("hard_verify drive %d cyl %d hd %d sec %d\n", + state.drive, state.cyl, state.head, state.secnum); +#endif + find_sector(TRS_HARD_READY | TRS_HARD_SEEKDONE); +} + +static void hard_format(int cmd) +{ +#if HARDDEBUG2 + debug("hard_format drive %d cyl %d hd %d\n", + state.drive, state.cyl, state.head); +#endif + if (state.seccnt != TRS_HARD_SEC_PER_TRK) { + error("trs_hard: can only do %d sectors/track, not %d", + TRS_HARD_SEC_PER_TRK, state.seccnt); + } + if (state.secnum != TRS_HARD_SECSIZE_CODE) { + error("trs_hard: can only do %d bytes/sectors (code %d), not code %d", + TRS_HARD_SECSIZE, TRS_HARD_SECSIZE_CODE, state.secnum); + } + /* !!should probably set up to read skew table here */ + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE; +} + +static void hard_init(int cmd) +{ +#if HARDDEBUG2 + debug("hard_init drive %d cyl %d hd %d sec %d\n", + state.drive, state.cyl, state.head, state.secnum); +#endif + /* I don't know what this command does */ + error("trs_hard: init command (0x%02x) not implemented", cmd); + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE; +} + +static void hard_seek(int cmd) +{ +#if HARDDEBUG2 + debug("hard_seek drive %d cyl %d hd %d sec %d\n", + state.drive, state.cyl, state.head, state.secnum); +#endif + find_sector(TRS_HARD_READY | TRS_HARD_SEEKDONE); +} + +/* + * 1) Make sure the file for the current drive is open. If the file + * cannot be opened, return 0 and set the controller error status. + * + * 2) If newly opening the file, establish the hardware write protect + * status and geometry in the Drive structure. + * + * 3) Return 1 if all OK. + */ +static int open_drive(int drive) +{ + Drive *d = &state.d[drive]; + char diskname[1024]; + ReedHardHeader rhh; + size_t res; + + if (d->file != NULL) return 1; + + /* Compute the filename */ + if (trs_model == 5) { + sprintf(diskname, "%s/hard4p-%d", trs_disk_dir, drive); + } else { + sprintf(diskname, "%s/hard%d-%d", trs_disk_dir, trs_model, drive); + } + + /* First try opening for reading and writing */ + d->file = fopen(diskname, "r+"); + if (d->file == NULL) { + /* No luck, try for reading only */ + d->file = fopen(diskname, "r"); + if (d->file == NULL) { +#if HARDDEBUG3 + error("trs_hard: could not open hard drive image %s: %s", + diskname, strerror(errno)); +#endif + goto fail; + } + d->writeprot = 1; + } else { + d->writeprot = 0; + } + + /* Read in the Reed header and check some basic magic numbers (not all) */ + res = fread(&rhh, sizeof(rhh), 1, d->file); + if (res != 1 || rhh.id1 != 0x56 || rhh.id2 != 0xcb || rhh.ver != 0x10) { + error("trs_hard: unrecognized hard drive image %s", diskname); + goto fail; + } + if (rhh.flag1 & 0x80) d->writeprot = 1; + + /* Use the number of cylinders specified in the header */ + d->cyls = rhh.cyl ? rhh.cyl : 256; + + /* Use the number of secs/track that RSHARD requires */ + d->secs = TRS_HARD_SEC_PER_TRK; + + /* Header gives only secs/cyl. Compute number of heads from + this and the assumed number of secs/track. */ + d->heads = (rhh.sec ? rhh.sec : 256) / d->secs; + + if ((rhh.sec % d->secs) != 0 || + d->heads <= 0 || d->heads > TRS_HARD_MAXHEADS) { + error("trs_hard: unusable geometry in image %s", diskname); + goto fail; + } + + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE; + return 1; + + fail: + if (d->file) fclose(d->file); + d->file = NULL; + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_ERR; + state.error = TRS_HARD_NFERR; + return 0; +} + +/* + * Check whether the current position is in bounds for the geometry. + * If not, return 0 and set the controller error status. If so, fseek + * the file to the start of the current sector, return 1, and set + * the controller status to newstatus. + */ +static int find_sector(int newstatus) +{ + Drive *d = &state.d[state.drive]; + if (open_drive(state.drive) == 0) return 0; + if (/**state.cyl >= d->cyls ||**/ /* ignore this limit */ + state.head >= d->heads || + state.secnum > d->secs /* allow 0-origin or 1-origin */ ) { + error("trs_hard: requested cyl %d hd %d sec %d; max cyl %d hd %d sec %d\n", + state.cyl, state.head, state.secnum, d->cyls, d->heads, d->secs); + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_ERR; + state.error = TRS_HARD_NFERR; + return 0; + } + fseek(d->file, + sizeof(ReedHardHeader) + + TRS_HARD_SECSIZE * (state.cyl * d->heads * d->secs + + state.head * d->secs + + (state.secnum % d->secs)), + 0); + state.status = newstatus; + return 1; +} + +static int hard_data_in() +{ + Drive *d = &state.d[state.drive]; + if ((state.command & TRS_HARD_CMDMASK) == TRS_HARD_READ && + (state.status & TRS_HARD_ERR) == 0) { + if (state.bytesdone < TRS_HARD_SECSIZE) { + state.data = getc(d->file); + state.bytesdone++; + } + } + return state.data; +} + +static void hard_data_out(int value) +{ + Drive *d = &state.d[state.drive]; + int res = 0; + state.data = value; + if ((state.command & TRS_HARD_CMDMASK) == TRS_HARD_WRITE && + (state.status & TRS_HARD_ERR) == 0) { + if (state.bytesdone < TRS_HARD_SECSIZE) { + if (state.cyl == 0 && state.head == 0 && + state.secnum == 0 && state.bytesdone == 2) { + set_dir_cyl(value); + } + res = putc(state.data, d->file); + state.bytesdone++; + if (res != EOF && state.bytesdone == TRS_HARD_SECSIZE) { + res = fflush(d->file); + } + } + } + if (res == EOF) { + error("trs_hard: errno %d while writing drive %d", errno, state.drive); + state.status = TRS_HARD_READY | TRS_HARD_SEEKDONE | TRS_HARD_ERR; + state.error = TRS_HARD_DATAERR; /* arbitrary choice */ + } +} + +/* Sleazy trick to update the "directory cylinder" byte in the Reed + header. This value is only needed by the Reed emulator itself, and + we would like xtrs to set it automatically so that the user doesn't + have to know about it. */ +static void set_dir_cyl(int cyl) +{ + Drive *d = &state.d[state.drive]; + long where = ftell(d->file); + fseek(d->file, 31, 0); + putc(cyl, d->file); + fseek(d->file, where, 0); +} diff --git a/trs_hard.h b/trs_hard.h new file mode 100644 index 0000000..5fddcbd --- /dev/null +++ b/trs_hard.h @@ -0,0 +1,210 @@ +/* Copyright (c) 2000, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Wed May 17 23:33:12 PDT 2000 by mann */ + +/* + * Definitions for the Radio Shack TRS-80 Model I/III/4/4P + * hard disk controller. This is a Western Digital WD1000/WD1010 + * mapped at ports 0xc8-0xcf, plus control registers at 0xc0-0xc1. + * + * Definitions inferred from various drivers and sketchy documents + * found in odd corners. Anyone have a real WD10xx data sheet? + */ + +extern void trs_hard_init(int reset_button); +extern int trs_hard_in(int port); +extern void trs_hard_out(int port, int value); +extern char* trs_disk_dir; + +/* Sector size is always 256 for TRSDOS/LDOS/etc. */ +/* Other sizes currently not emulated */ +#define TRS_HARD_SECSIZE 256 +#define TRS_HARD_SECSIZE_CODE 0x0f /* size code for 256 byte sectors?!! */ + +/* RSHARD assumes 32 sectors/track */ +/* Other sizes currently not emulated */ +#define TRS_HARD_SEC_PER_TRK 32 + +/* + * Tandy-specific registers + */ + +/* Write protect switch register (read only): + * abcd--pi + * a = drive 0 write protected + * b = drive 1 write protected + * c = drive 2 write protected + * d = drive 3 write protected + * p = at least one drive write protected + * i = interrupt request + */ +#define TRS_HARD_WP 0xc0 +#define TRS_HARD_WPBIT(d) (0x80 >> (d)) +#define TRS_HARD_WPSOME 0x02 +#define TRS_HARD_INTRQ 0x01 /* not emulated */ + +/* Control register (read(?)/write): + * ---sdw-- + * s = software reset + * d = device enable + * w = wait enable + */ +#define TRS_HARD_CONTROL 0xc1 +#define TRS_HARD_SOFTWARE_RESET 0x10 +#define TRS_HARD_DEVICE_ENABLE 0x08 +#define TRS_HARD_WAIT_ENABLE 0x04 + +/* ID register (Model II only!) + */ +#define TRS_HARD_ID0 0xc2 +#define TRS_HARD_ID1 0xc3 + +/* CTC channels (Model II only!) + */ +#define TRS_HARD_CTC0 0xc4 +#define TRS_HARD_CTC1 0xc5 +#define TRS_HARD_CTC2 0xc6 +#define TRS_HARD_CTC3 0xc7 + +/* + * WD1010 registers + */ + +/* Data register (read/write) */ +#define TRS_HARD_DATA 0xc8 + +/* Error register (read only): + * bdin-atm + * b = block marked bad + * e = uncorrectable error in data + * i = uncorrectable error in id (unused?) + * n = id not found + * - = unused + * a = command aborted + * t = track 0 not found + * m = bad address mark + */ +#define TRS_HARD_ERROR (TRS_HARD_DATA+1) +#define TRS_HARD_BBDERR 0x80 +#define TRS_HARD_DATAERR 0x40 +#define TRS_HARD_IDERR 0x20 /* unused? */ +#define TRS_HARD_NFERR 0x10 +#define TRS_HARD_MCRERR 0x08 /* unused */ +#define TRS_HARD_ABRTERR 0x04 +#define TRS_HARD_TRK0ERR 0x02 +#define TRS_HARD_MARKERR 0x01 + +/* Write precompensation register (write only) */ +/* Value *4 is the starting cylinder for write precomp */ +#define TRS_HARD_PRECOMP (TRS_HARD_DATA+1) + +/* Sector count register (read/write) */ +/* Used only for multiple sector accesses; otherwise ignored. */ +/* Autodecrements when used. */ +#define TRS_HARD_SECCNT (TRS_HARD_DATA+2) + +/* Sector number register (read/write) */ +#define TRS_HARD_SECNUM (TRS_HARD_DATA+3) + +/* Cylinder low byte register (read/write) */ +#define TRS_HARD_CYLLO (TRS_HARD_DATA+4) + +/* Cylinder high byte register (read/write) */ +#define TRS_HARD_CYLHI (TRS_HARD_DATA+5) + +/* Size/drive/head register (read/write): + * essddhhh + * e = 0 if CRCs used; 1 if ECC used + * ss = sector size; 00=256, 01=512, 10=1024, 11=128 + * dd = drive + * hhh = head + */ +#define TRS_HARD_SDH (TRS_HARD_DATA+6) +#define TRS_HARD_ECCMASK 0x80 +#define TRS_HARD_ECCSHIFT 7 +#define TRS_HARD_SIZEMASK 0x60 +#define TRS_HARD_SIZESHIFT 5 +#define TRS_HARD_DRIVEMASK 0x18 +#define TRS_HARD_DRIVESHIFT 3 +#define TRS_HARD_MAXDRIVES 4 +#define TRS_HARD_HEADMASK 0x07 +#define TRS_HARD_HEADSHIFT 0 +#define TRS_HARD_MAXHEADS 8 + +/* Status register (read only): + * brwsdcie + * b = busy + * r = drive ready + * w = write error + * s = seek complete + * d = data request + * c = corrected ecc (reserved) + * i = command in progress (=software reset required) + * e = error + */ +#define TRS_HARD_STATUS (TRS_HARD_DATA+7) +#define TRS_HARD_BUSY 0x80 +#define TRS_HARD_READY 0x40 +#define TRS_HARD_WRERR 0x20 +#define TRS_HARD_SEEKDONE 0x10 +#define TRS_HARD_DRQ 0x08 +#define TRS_HARD_ECC 0x04 +#define TRS_HARD_CIP 0x02 +#define TRS_HARD_ERR 0x01 + +/* Command register (write only) */ +#define TRS_HARD_COMMAND (TRS_HARD_DATA+7) + +/* + * WD1010 commands + */ + +#define TRS_HARD_CMDMASK 0xf0 + +/* Restore: + * 0001rrrr + * rrrr = step rate; 0000=35us, else rrrr*0.5ms + */ +#define TRS_HARD_RESTORE 0x10 + +/* Read sector: + * 0010dm00 + * d = 0 for interrupt on DRQ, 1 for interrupt at end (DMA style) + * TRS-80 always uses programmed I/O, INTRQ not connected, I believe. + * m = multiple sector flag (not emulated!) + */ +#define TRS_HARD_READ 0x20 +#define TRS_HARD_DMA 0x08 +#define TRS_HARD_MULTI 0x04 + +/* Write sector: + * 00110m00 + * m = multiple sector flag (not emulated!) + */ +#define TRS_HARD_WRITE 0x30 + +/* Verify sector (or "Scan ID"): + * 01000000 + */ +#define TRS_HARD_VERIFY 0x40 + +/* Format track: + * 01010000 + */ +#define TRS_HARD_FORMAT 0x50 + +/* Init (??): + * 01100000 + */ +#define TRS_HARD_INIT 0x60 + +/* Seek to specified sector/head/cylinder: + * 0111rrrr + * rrrr = step rate; 0000=35us, else rrrr*0.5ms + */ +#define TRS_HARD_SEEK 0x70 diff --git a/trs_imp_exp.c b/trs_imp_exp.c new file mode 100644 index 0000000..739b760 --- /dev/null +++ b/trs_imp_exp.c @@ -0,0 +1,682 @@ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* + * trs_imp_exp.c + * + * Features to make transferring files into and out of the emulator + * easier. + */ + +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <time.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include "trs_imp_exp.h" +#include "z80.h" +#include "trs.h" +#include "trs_disk.h" + +/* + If the following option is set, potentially dangerous emulator traps + will be blocked, including file writes to the host filesystem and shell + command execution. + */ +int trs_emtsafe = 0; + +/* New emulator traps */ + +#define MAX_OPENDIR 32 +DIR *dir[MAX_OPENDIR] = { NULL, }; + +typedef struct { + int fd; + int inuse; +} OpenDisk; +#define MAX_OPENDISK 32 +OpenDisk od[MAX_OPENDISK]; + +void do_emt_system() +{ + int res; + if (trs_emtsafe) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + res = system((char *)mem_pointer(REG_HL, 0)); + if (res == -1) { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } else { + REG_A = 0; + REG_F |= ZERO_MASK; + } + REG_BC = res; +} + +void do_emt_mouse() +{ + int x, y; + unsigned int buttons, sens; + switch (REG_B) { + case 1: + trs_get_mouse_pos(&x, &y, &buttons); + REG_HL = x; + REG_DE = y; + REG_A = buttons; + if (REG_A) { + REG_F &= ~ZERO_MASK; + } else { + REG_F |= ZERO_MASK; + } + break; + case 2: + trs_set_mouse_pos(REG_HL, REG_DE); + REG_A = 0; + REG_F |= ZERO_MASK; + break; + case 3: + trs_get_mouse_max(&x, &y, &sens); + REG_HL = x; + REG_DE = y; + REG_A = sens; + if (REG_A) { + REG_F &= ~ZERO_MASK; + } else { + REG_F |= ZERO_MASK; + } + break; + case 4: + trs_set_mouse_max(REG_HL, REG_DE, REG_C); + REG_A = 0; + REG_F |= ZERO_MASK; + break; + case 5: + REG_A = trs_get_mouse_type(); + if (REG_A) { + REG_F &= ~ZERO_MASK; + } else { + REG_F |= ZERO_MASK; + } + break; + default: + error("undefined emt_mouse function code"); + break; + } +} + +void do_emt_getddir() +{ + if (REG_HL + REG_BC > 0x10000 || + REG_HL + strlen(trs_disk_dir) + 1 > REG_HL + REG_BC) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + strcpy((char *)mem_pointer(REG_HL, 1), trs_disk_dir); + REG_A = 0; + REG_F |= ZERO_MASK; + REG_BC = strlen(trs_disk_dir); +} + +void do_emt_setddir() +{ + if (trs_emtsafe) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + trs_disk_dir = strdup((char *)mem_pointer(REG_HL, 0)); + if (trs_disk_dir[0] == '~' && + (trs_disk_dir[1] == '/' || trs_disk_dir[1] == '\0')) { + char* home = getenv("HOME"); + if (home) { + char *p = (char*)malloc(strlen(home) + strlen(trs_disk_dir) + 2); + sprintf(p, "%s/%s", home, trs_disk_dir+1); + free(trs_disk_dir); + trs_disk_dir = p; + } + } + REG_A = 0; + REG_F |= ZERO_MASK; +} + +void do_emt_open() +{ + int fd, oflag, eoflag; + eoflag = REG_BC; + switch (eoflag & EO_ACCMODE) { + case EO_RDONLY: + default: + oflag = O_RDONLY; + break; + case EO_WRONLY: + oflag = O_WRONLY; + break; + case EO_RDWR: + oflag = O_RDWR; + break; + } + if (eoflag & EO_CREAT) oflag |= O_CREAT; + if (eoflag & EO_EXCL) oflag |= O_EXCL; + if (eoflag & EO_TRUNC) oflag |= O_TRUNC; + if (eoflag & EO_APPEND) oflag |= O_APPEND; + + if (trs_emtsafe && oflag != O_RDONLY) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + fd = open((char *)mem_pointer(REG_HL, 0), oflag, REG_DE); + if (fd >= 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } + REG_DE = fd; +} + +void do_emt_close() +{ + int res; + res = close(REG_DE); + if (res >= 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } +} + +void do_emt_read() +{ + int size; + if (REG_HL + REG_BC > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + size = read(REG_DE, mem_pointer(REG_HL, 1), REG_BC); + if (size >= 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } + REG_BC = size; +} + + +void do_emt_write() +{ + int size; + if (trs_emtsafe) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + if (REG_HL + REG_BC > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + size = write(REG_DE, mem_pointer(REG_HL, 0), REG_BC); + if (size >= 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } + REG_BC = size; +} + +void do_emt_lseek() +{ + int i; + off_t offset; + if (REG_HL + 8 > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + return; + } + offset = 0; + for (i=0; i<8; i++) { + offset = offset + (mem_read(REG_HL + i) << i*8); + } + offset = lseek(REG_DE, offset, REG_BC); + if (offset != (off_t) -1) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } + for (i=REG_HL; i<8; i++) { + mem_write(REG_HL + i, offset & 0xff); + offset >>= 8; + } +} + +void do_emt_strerror() +{ + char *msg; + int size; + if (REG_HL + REG_BC > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + errno = 0; + msg = strerror(REG_A); + size = strlen(msg); + if (errno != 0) { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } else if (REG_BC < size + 2) { + REG_A = ERANGE; + REG_F &= ~ZERO_MASK; + size = REG_BC - 1; + } else { + REG_A = 0; + REG_F |= ZERO_MASK; + } + memcpy(mem_pointer(REG_HL, 1), msg, size); + mem_write(REG_HL + size++, '\r'); + mem_write(REG_HL + size, '\0'); + if (errno == 0) { + REG_BC = size; + } else { + REG_BC = 0xFFFF; + } +} + +void do_emt_time() +{ + time_t now = time(0); + if (REG_A == 1) { +#if __alpha + struct tm *loctm = localtime(&now); + now += loctm->tm_gmtoff; +#else + struct tm loctm = *(localtime(&now)); + struct tm gmtm = *(gmtime(&now)); + int daydiff = loctm.tm_mday - gmtm.tm_mday; + now += (loctm.tm_sec - gmtm.tm_sec) + + (loctm.tm_min - gmtm.tm_min) * 60 + + (loctm.tm_hour - gmtm.tm_hour) * 3600; + switch (daydiff) { + case 0: + case 1: + case -1: + now += 24*3600 * daydiff; + break; + case 30: + case 29: + case 28: + case 27: + now -= 24*3600; + break; + case -30: + case -29: + case -28: + case -27: + now += 24*3600; + break; + default: + error("trouble computing local time in emt_time"); + } +#endif + } else if (REG_A != 0) { + error("unsupported function code to emt_time"); + } + REG_BC = (now >> 16) & 0xffff; + REG_DE = now & 0xffff; +} + +void do_emt_opendir() +{ + int i; + for (i = 0; i < MAX_OPENDIR; i++) { + if (dir[i] == NULL) break; + } + if (i == MAX_OPENDIR) { + REG_DE = 0xffff; + REG_A = EMFILE; + return; + } + dir[i] = opendir((char *)mem_pointer(REG_HL, 0)); + if (dir[i] == NULL) { + REG_DE = 0xffff; + REG_A = errno; + REG_F &= ~ZERO_MASK; + } else { + REG_DE = i; + REG_A = 0; + REG_F |= ZERO_MASK; + } +} + +void do_emt_closedir() +{ + int i = REG_DE; + int ok; + if (i < 0 || i >= MAX_OPENDIR || dir[i] == NULL) { + REG_A = EBADF; + REG_F &= ~ZERO_MASK; + return; + } + ok = closedir(dir[i]); + dir[i] = NULL; + if (ok >= 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } +} + +void do_emt_readdir() +{ + int size, i = REG_DE; + struct dirent *result; + + if (i < 0 || i >= MAX_OPENDIR || dir[i] == NULL) { + REG_A = EBADF; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + if (REG_HL + REG_BC > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + result = readdir(dir[i]); + if (result == NULL) { + REG_A = errno; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + size = strlen(result->d_name); + if (size + 1 > REG_BC) { + REG_A = ERANGE; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + strcpy((char *)mem_pointer(REG_HL, 1), result->d_name); + REG_A = 0; + REG_F |= ZERO_MASK; + REG_BC = size; +} + +void do_emt_chdir() +{ + int ok; + if (trs_emtsafe) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + ok = chdir((char *)mem_pointer(REG_HL, 0)); + if (ok < 0) { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } else { + REG_A = 0; + REG_F |= ZERO_MASK; + } +} + +void do_emt_getcwd() +{ + char *result; + if (REG_HL + REG_BC > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + result = getcwd((char *)mem_pointer(REG_HL, 1), REG_BC); + if (result == NULL) { + REG_A = errno; + REG_F &= ~ZERO_MASK; + REG_BC = 0xFFFF; + return; + } + REG_A = 0; + REG_F |= ZERO_MASK; + REG_BC = strlen(result); +} + +void do_emt_misc() +{ + switch (REG_A) { + case 0: + trs_disk_change_all(); + REG_HL = trs_disk_changecount; + break; + case 1: + trs_exit(); + break; + case 2: + trs_debug(); + break; + case 3: + trs_reset(0); + break; + case 4: + REG_HL = trs_disk_changecount; + break; + case 5: + REG_HL = trs_model; + break; + case 6: + REG_HL = trs_disk_getsize(REG_BC); + break; + case 7: + trs_disk_setsize(REG_BC, REG_HL); + break; + case 8: + REG_HL = trs_disk_getstep(REG_BC); + break; + case 9: + trs_disk_setstep(REG_BC, REG_HL); + break; + case 10: + REG_HL = grafyx_get_microlabs(); + break; + case 11: + grafyx_set_microlabs(REG_HL); + break; + case 12: + REG_HL = z80_state.delay; + REG_BC = trs_autodelay; + break; + case 13: + z80_state.delay = REG_HL; + trs_autodelay = REG_BC; + break; + case 14: + REG_HL = stretch_amount; + break; + case 15: + stretch_amount = REG_HL; + break; + case 16: + REG_HL = trs_disk_doubler; + break; + case 17: + trs_disk_doubler = REG_HL; + break; + case 18: + REG_HL = sb_get_volume(); + break; + case 19: + sb_set_volume(REG_HL); + break; + case 20: + REG_HL = trs_disk_truedam; + break; + case 21: + trs_disk_truedam = REG_HL; + break; + default: + error("unsupported function code to emt_misc"); + break; + } +} + +void do_emt_ftruncate() +{ + int i, result; + off_t offset; + if (trs_emtsafe) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + if (REG_HL + 8 > 0x10000) { + REG_A = EFAULT; + REG_F &= ~ZERO_MASK; + return; + } + offset = 0; + for (i=0; i<8; i++) { + offset = offset + (mem_read(REG_HL + i) << i*8); + } + result = ftruncate(REG_DE, offset); + if (result == 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } +} + +void do_emt_opendisk() +{ + char *name = (char *)mem_pointer(REG_HL, 0); + char *qname; + int i; + int oflag, eoflag; + + eoflag = REG_BC; + switch (eoflag & EO_ACCMODE) { + case EO_RDONLY: + default: + oflag = O_RDONLY; + break; + case EO_WRONLY: + oflag = O_WRONLY; + break; + case EO_RDWR: + oflag = O_RDWR; + break; + } + if (eoflag & EO_CREAT) oflag |= O_CREAT; + if (eoflag & EO_EXCL) oflag |= O_EXCL; + if (eoflag & EO_TRUNC) oflag |= O_TRUNC; + if (eoflag & EO_APPEND) oflag |= O_APPEND; + + if (trs_emtsafe && oflag != O_RDONLY) { + error("potentially dangerous emulator trap blocked"); + REG_A = EACCES; + REG_F &= ~ZERO_MASK; + return; + } + + if (*name == '/' || *trs_disk_dir == '\0') { + qname = strdup(name); + } else { + qname = (char *)malloc(strlen(trs_disk_dir) + 1 + strlen(name) + 1); + strcpy(qname, trs_disk_dir); + strcat(qname, "/"); + strcat(qname, name); + } + for (i = 0; i < MAX_OPENDISK; i++) { + if (!od[i].inuse) break; + } + if (i == MAX_OPENDISK) { + REG_DE = 0xffff; + REG_A = EMFILE; + REG_F &= ~ZERO_MASK; + free(qname); + return; + } + od[i].fd = open(qname, oflag, REG_DE); + free(qname); + if (od[i].fd >= 0) { + od[i].inuse = 1; + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } + REG_DE = od[i].fd; +} + +void do_emt_closedisk() +{ + int i; + int res; + if (REG_DE == 0xffff) { + for (i = 0; i < MAX_OPENDISK; i++) { + if (od[i].inuse) { + close(od[i].fd); + od[i].inuse = 0; + } + } + REG_A = 0; + REG_F |= ZERO_MASK; + return; + } + + for (i = 0; i < MAX_OPENDISK; i++) { + if (od[i].inuse && od[i].fd == REG_DE) break; + } + if (i == MAX_OPENDISK) { + REG_A = EBADF; + REG_F &= ~ZERO_MASK; + return; + } + od[i].inuse = 0; + res = close(od[i].fd); + if (res >= 0) { + REG_A = 0; + REG_F |= ZERO_MASK; + } else { + REG_A = errno; + REG_F &= ~ZERO_MASK; + } +} diff --git a/trs_imp_exp.h b/trs_imp_exp.h new file mode 100644 index 0000000..2125f21 --- /dev/null +++ b/trs_imp_exp.h @@ -0,0 +1,267 @@ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* + * trs_imp_exp.h + * + * Features to make transferring files into and out of the emulator + * (etc.) easier, using a set of emulator traps (instructions that + * don't exist in a real Z-80). + * + * ED20-ED21 reserved; used by Jeff Vavasour Model III/4 emulator + * + * ED28 emt_system + * Before, HL => shell command, null terminated + * After, AF = 0 if OK, error number if not (Z flag affected) + * BC = command exit status, normally 0 if OK + * + * ED29 emt_mouse + * Implements Goben/Reed-style mouse driver in one instruction. + * Documentation adapted from original Misosys Quarterly V.iii article. + * (Notes: 1. The emt does not affect registers that do not have + * documented return values. 2. Different versions of the native + * drivers varied in whether coordinate values ranged from 0 to + * XSIZE or 0 to XSIZE-1. This driver will return values up to + * XSIZE if XSIZE is odd, up to XSIZE-1 if XSIZE is even, thus + * finessing the issue. 3. The sensitivity value is ignored. 4. The + * mouse is assumed to have 3 buttons. 5. The set operations always + * return success.) + * + * GET MOUSE CURSOR POSITION + * Before, B = 1 + * After, HL = mouse cursor X value (0 to XSIZE-1) + * DE = mouse cursor Y value (0 to YSIZE-1) + * A = button status + * Bit 0: Reset if right button pressed + * Bit 1: Reset if middle button pressed (both buttons) + * Bit 2: Reset if left button pressed + * + * SET MOUSE CURSOR POSITION + * Before, B = 2 + * HL = X value (0 - XSIZE-1) + * DE = Y value (0 - YSIZE-1) + * After, AF = Z flag set if operation was successful + * + * GET SENSITIVITY AND MAXIMUM VALUES + * Before, B = 3 + * After, HL = current X maximum (XSIZE) + * DE = current Y maximum (YSIZE) + * A = current sensitivity (0 - 3) + * + * SET SENSITIVITY AND MAXIMUM VALUES + * Before, B = 4 + * HL = new X maximum (XSIZE) + * DE = new Y maximum (YSIZE) + * C = sensitivity (0 - 3); 3 is most sensitive + * After, AF = Z flag set if operation was successful + * + * GET MOUSE TYPE + * Before, B = 5 + * After, A = 0 if 2-button, 1 if 3-button + * + * ED2A emt_getddir + * Get the value of the -diskdir command-line parameter. + * Before, BC = nbytes + * HL => buffer + * After, AF = 0 if OK, error number if not (Z flag affected) + * HL => same buffer, containing NUL-terminated pathname + * BC = strlen(buffer), 0xFFFF if error + * + * ED2B emt_setddir + * Change the value of the -diskdir command-line parameter. Does not + * force a disk change; use emt_misc afterwards if you want one. + * Before, HL => path, null terminated + * After, AF = 0 if OK, error number if not (Z flag affected) + * + * ED2C-ED2E reserved + * + * ED2F emt_debug + * Enter zbx, the xtrs debugger. + * + * ED30 emt_open + * Before, HL => path, null terminated + * BC = oflag (use EO_ values defined below) + * DE = mode + * After, AF = 0 if OK, error number if not (Z flag affected) + * DE = fd, 0xFFFF if error + * + * ED31 emt_close + * Before, DE = fd + * After, AF = 0 if OK, error number if not (Z flag affected) + * + * ED32 emt_read + * Before, BC = nbytes + * DE = fd + * HL => buffer + * After, AF = 0 if OK, error number if not (Z flag affected) + * BC = nbytes read, 0xFFFF if error + * + * ED33 emt_write + * Before, BC = nbytes + * DE = fd + * HL => buffer + * After, AF = 0 if OK, error number if not (Z flag affected) + * BC = nbytes written, 0xFFFF if error + * + * ED34 emt_lseek + * Before, BC = whence + * DE = fd + * HL => offset (8-byte little-endian integer) + * After, AF = 0 if OK, error number if not (Z flag affected) + * HL => location in file, 0xFFFFFFFF if error + * + * ED35 emt_strerror + * Before, A = error number + * HL => buffer for error string + * BC = buffer size + * After, AF = 0 if OK, new error number if not (Z flag affected) + * HL => same buffer, containing \r\0-terminated error string + * BC = strlen(buffer), 0xFFFF if error + * + * ED36 emt_time + * Before, A = 0 for UTC (GMT), 1 for local time + * After, BCDE= time in seconds since Jan 1, 1970 00:00:00 + * + * ED37 emt_opendir + * Before, HL => path, null terminated + * After, AF = 0 if OK, error number if not (Z flag affected) + * DE = dirfd, 0xFFFF if error + * + * ED38 emt_closedir + * Before, DE = dirfd + * After, AF = 0 if OK, error number if not (Z flag affected) + * + * ED39 emt_readdir + * Before, BC = nbytes + * DE = dirfd + * HL => buffer + * After, AF = 0 if OK, error number if not (Z flag affected) + * HL => same buffer, containing NUL-terminated filename + * BC = strlen(buffer), 0xFFFF if error + * + * ED3A emt_chdir + * Warning: Changing the working directory will change where the disk*-* + * files are found on the next disk-change command, unless -diskdir has + * been specified as an absolute pathname. + * Before, HL => path, null terminated + * After, AF = 0 if OK, error number if not (Z flag affected) + * + * ED3B emt_getcwd + * Before, BC = nbytes + * HL => buffer + * After, AF = 0 if OK, error number if not (Z flag affected) + * HL => same buffer, containing NUL-terminated pathname + * BC = strlen(buffer), 0xFFFF if error + * + * ED3C emt_misc + * Before, A = function code, other registers as listed + * After, other registers as listed + * + * Function codes: + * 0 = disk change + * After, HL = disk change count (F7 presses + emt_misc 0 calls) + * 1 = exit emulator + * 2 = enter debugger (if active) + * 3 = press reset button + * 4 = query disk change count + * After, HL = disk change count (F7 presses + emt_misc 0 calls) + * 5 = query model + * After, HL = model: 1, 3, 4, or 5 (=4P) + * 6 = query disk size + * Before, BC = unit number, 0-7 + * After, HL = size, 5 or 8 + * 7 = set disk size + * Before, BC = unit number, 0-7 + * HL = new size, 5 or 8 + * 8 = query disk single/double step + * Before, BC = unit number, 0-7 + * After, HL = step, 1 or 2 + * 9 = set disk single/double step + * Before, BC = unit number, 0-7 + * HL = new step, 1 or 2 + * 10 = query graphics type + * Before, HL = 0 Radio Shack, 1 Micro Labs + * 11 = set graphics type + * Before, HL = 0 Radio Shack, 1 Micro Labs + * 12 = query delay + * After, HL = delay + * BC = autodelay flag (0 or 1) + * 13 = set delay + * Before, HL = new delay + * BC = autodelay flag (0 or 1) + * 14 = query keystretch + * After, HL = amount (in T-states) + * 15 = set keystretch + * Before, HL = amount (in T-states) + * 16 = query doubler + * After, HL = 0 none, 1 Percom, 2 Tandy, 3 both + * 17 = set doubler + * Before, HL = 0 none, 1 Percom, 2 Tandy, 3 both + * 18 = query SoundBlaster volume (obsolete) + * After, HL = 0-100 + * 19 = set SoundBlaster volume (obsolete) + * Before, HL = 0-100 + * 20 = query truedam flag + * After, HL = 0 or 1 + * 21 = set truedam flag + * Before, HL = 0 or 1 + * + * ED3D emt_ftruncate + * Before, DE = fd + * HL => offset (8-byte little-endian integer) + * After, AF = 0 if OK, error number if not (Z flag affected) + * + * ED3E emt_opendisk + * Similar to emt_open, except that (1) the path, if not absolute, is + * interpreted relative to -diskdir, (2) emt_closedisk must be + * used in place of emt_close. + * Before, HL => path, null terminated + * BC = oflag (use EO_ values defined below) + * DE = mode + * After, AF = 0 if OK, error number if not (Z flag affected) + * DE = fd, 0xFFFF if error + * + * ED3F emt_closedisk + * Similar to emt_close, but pairs with emt_opendisk. + * Before, DE = fd, or -1 to close all fds opened with emt_opendisk + * After, AF = 0 if OK, error number if not (Z flag affected) + * + */ + +/* Minimal subset of standard O_ flags. We have to define our own for + portability; the numeric values differ amongst Unix + implementations. */ + +#define EO_ACCMODE 03 +#define EO_RDONLY 00 +#define EO_WRONLY 01 +#define EO_RDWR 02 +#define EO_CREAT 0100 +#define EO_EXCL 0200 +#define EO_TRUNC 01000 +#define EO_APPEND 02000 + +extern void do_emt_system(); +extern void do_emt_getddir(); +extern void do_emt_setddir(); +extern void do_emt_mouse(); +extern void do_emt_open(); +extern void do_emt_close(); +extern void do_emt_read(); +extern void do_emt_write(); +extern void do_emt_lseek(); +extern void do_emt_strerror(); +extern void do_emt_time(); +extern void do_emt_opendir(); +extern void do_emt_closedir(); +extern void do_emt_readdir(); +extern void do_emt_chdir(); +extern void do_emt_getcwd(); +extern void do_emt_misc(); +extern void do_emt_ftruncate(); +extern void do_emt_opendisk(); +extern void do_emt_closedisk(); diff --git a/trs_interrupt.c b/trs_interrupt.c new file mode 100644 index 0000000..6f008bb --- /dev/null +++ b/trs_interrupt.c @@ -0,0 +1,501 @@ +/* Copyright (c) 1996, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Wed May 17 22:02:19 PDT 2000 by mann */ + +/* + * Emulate interrupts + */ + +#include "z80.h" +#include "trs.h" +#include <stdio.h> +#include <sys/time.h> +#include <time.h> +#include <signal.h> + +/*#define IDEBUG 1*/ +/*#define IDEBUG2 1*/ + +/* IRQs */ +#define M1_TIMER_BIT 0x80 +#define M1_DISK_BIT 0x40 +#define M3_UART_ERR_BIT 0x40 +#define M3_UART_RCV_BIT 0x20 +#define M3_UART_SND_BIT 0x10 +#define M3_IOBUS_BIT 0x80 /* not emulated */ +#define M3_TIMER_BIT 0x04 +#define M3_CASSFALL_BIT 0x02 +#define M3_CASSRISE_BIT 0x01 +static unsigned char interrupt_latch = 0; +static unsigned char interrupt_mask = 0; + +/* NMIs (M3/4/4P only) */ +#define M3_INTRQ_BIT 0x80 /* FDC chip INTRQ line */ +#define M3_MOTOROFF_BIT 0x40 /* FDC motor timed out (stopped) */ +#define M3_RESET_BIT 0x20 /* User pressed Reset button */ +static unsigned char nmi_latch = 1; /* ?? One diagnostic program needs this */ +static unsigned char nmi_mask = M3_RESET_BIT; + +#define TIMER_HZ_1 40 +#define TIMER_HZ_3 30 +#define TIMER_HZ_4 60 +static int timer_hz; + +#define CLOCK_MHZ_1 1.77408 +#define CLOCK_MHZ_3 2.02752 +#define CLOCK_MHZ_4 4.05504 + +/* Kludge: LDOS hides the date (not time) in a memory area across reboots. */ +/* We put it there on powerup, so LDOS magically knows the date! */ +#define LDOS_MONTH 0x4306 +#define LDOS_DAY 0x4307 +#define LDOS_YEAR 0x4466 +#define LDOS3_MONTH 0x442f +#define LDOS3_DAY 0x4457 +#define LDOS3_YEAR 0x4413 +#define LDOS4_MONTH 0x0035 +#define LDOS4_DAY 0x0034 +#define LDOS4_YEAR 0x0033 + +static int timer_on = 1; +#ifdef IDEBUG +long lost_timer_interrupts = 0; +#endif + +/* Note: the independent interrupt latch and mask model is not correct + for all interrupts. The cassette rise/fall interrupt enable is + clocked into the interrupt latch when the event occurs (we get this + right), and is *not* masked against the latch output (we get this + wrong, but it doesn't really matter). */ + +void +trs_cassette_rise_interrupt(int dummy) +{ + interrupt_latch = (interrupt_latch & ~M3_CASSRISE_BIT) | + (interrupt_mask & M3_CASSRISE_BIT); + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; + trs_cassette_update(0); +} + +void +trs_cassette_fall_interrupt(int dummy) +{ + interrupt_latch = (interrupt_latch & ~M3_CASSFALL_BIT) | + (interrupt_mask & M3_CASSFALL_BIT); + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; + trs_cassette_update(0); +} + +void +trs_cassette_clear_interrupts() +{ + interrupt_latch &= ~(M3_CASSRISE_BIT|M3_CASSFALL_BIT); + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; +} + +int +trs_cassette_interrupts_enabled() +{ + return interrupt_mask & (M3_CASSRISE_BIT|M3_CASSFALL_BIT); +} + +void +trs_timer_interrupt(int state) +{ + if (trs_model == 1) { + if (state) { +#ifdef IDEBUG + if (interrupt_latch & M1_TIMER_BIT) lost_timer_interrupts++; +#endif + interrupt_latch |= M1_TIMER_BIT; + z80_state.irq = 1; + } else { + interrupt_latch &= ~M1_TIMER_BIT; + } + } else { + if (state) { +#ifdef IDEBUG + if (interrupt_latch & M3_TIMER_BIT) lost_timer_interrupts++; +#endif + interrupt_latch |= M3_TIMER_BIT; + } else { + interrupt_latch &= ~M3_TIMER_BIT; + } + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; + } +} + +void +trs_disk_intrq_interrupt(int state) +{ + if (trs_model == 1) { + if (state) { + interrupt_latch |= M1_DISK_BIT; + z80_state.irq = 1; + } else { + interrupt_latch &= ~M1_DISK_BIT; + } + } else { + if (state) { + nmi_latch |= M3_INTRQ_BIT; + } else { + nmi_latch &= ~M3_INTRQ_BIT; + } + z80_state.nmi = (nmi_latch & nmi_mask) != 0; + if (!z80_state.nmi) z80_state.nmi_seen = 0; + } +} + +void +trs_disk_motoroff_interrupt(int state) +{ + /* Drive motor timed out (stopped). */ + if (trs_model == 1) { + /* no such interrupt */ + } else { + if (state) { + nmi_latch |= M3_MOTOROFF_BIT; + } else { + nmi_latch &= ~M3_MOTOROFF_BIT; + } + z80_state.nmi = (nmi_latch & nmi_mask) != 0; + if (!z80_state.nmi) z80_state.nmi_seen = 0; + } +} + +void +trs_disk_drq_interrupt(int state) +{ + /* no effect */ +} + +void +trs_uart_err_interrupt(int state) +{ + if (trs_model > 1) { + if (state) { + interrupt_latch |= M3_UART_ERR_BIT; + } else { + interrupt_latch &= ~M3_UART_ERR_BIT; + } + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; + } +} + +void +trs_uart_rcv_interrupt(int state) +{ + if (trs_model > 1) { + if (state) { + interrupt_latch |= M3_UART_RCV_BIT; + } else { + interrupt_latch &= ~M3_UART_RCV_BIT; + } + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; + } +} + +void +trs_uart_snd_interrupt(int state) +{ + if (trs_model > 1) { + if (state) { + interrupt_latch |= M3_UART_SND_BIT; + } else { + interrupt_latch &= ~M3_UART_SND_BIT; + } + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; + } +} + +void +trs_reset_button_interrupt(int state) +{ + if (trs_model == 1) { + z80_state.nmi = state; + } else { + if (state) { + nmi_latch |= M3_RESET_BIT; + } else { + nmi_latch &= ~M3_RESET_BIT; + } + z80_state.nmi = (nmi_latch & nmi_mask) != 0; + } + if (!z80_state.nmi) z80_state.nmi_seen = 0; +} + +unsigned char +trs_interrupt_latch_read() +{ + unsigned char tmp = interrupt_latch; + if (trs_model == 1) { + trs_timer_interrupt(0); /* acknowledge this one (only) */ + z80_state.irq = (interrupt_latch != 0); + return tmp; + } else { + return ~tmp; + } +} + +void +trs_interrupt_mask_write(unsigned char value) +{ + interrupt_mask = value; + z80_state.irq = (interrupt_latch & interrupt_mask) != 0; +} + +/* M3 only */ +unsigned char +trs_nmi_latch_read() +{ + return ~nmi_latch; +} + +void +trs_nmi_mask_write(unsigned char value) +{ + nmi_mask = value | M3_RESET_BIT; + z80_state.nmi = (nmi_latch & nmi_mask) != 0; +#if IDEBUG2 + if (z80_state.nmi && !z80_state.nmi_seen) { + debug("mask write caused nmi, mask %02x latch %02x\n", + nmi_mask, nmi_latch); + } +#endif + if (!z80_state.nmi) z80_state.nmi_seen = 0; +} + +static int saved_delay; + +/* Temporarily reduce the delay, until trs_restore_delay is called. + Useful if we know we're about to do something that's emulated more + slowly than most instructions, such as video or real-time sound. + In case the boost is too big or too small, we allow the normal + autodelay algorithm to continue to run and adjust the new delay. */ +void +trs_suspend_delay() +{ + if (!saved_delay) { + saved_delay = z80_state.delay; + z80_state.delay /= 2; /* dividing by 2 is arbitrary */ + } +} + +/* Put back the saved delay */ +void +trs_restore_delay() +{ + if (saved_delay) { + z80_state.delay = saved_delay; + saved_delay = 0; + trs_paused = 1; + } +} + +#define UP_F 1.50 +#define DOWN_F 0.50 + +void +trs_timer_event(int signo) +{ + struct timeval tv; + struct itimerval it; + + gettimeofday(&tv, NULL); + if (trs_autodelay) { + static struct timeval oldtv; + static int increment = 1; + static int oldtoofast = 0; +#if __GNUC__ + static unsigned long long oldtcount; +#else + static unsigned long oldtcount; +#endif + if (!trs_paused /*&& !saved_delay*/) { + int toofast = (z80_state.t_count - oldtcount) > + ((tv.tv_sec*1000000 + tv.tv_usec) - + (oldtv.tv_sec*1000000 + oldtv.tv_usec))*z80_state.clockMHz; + if (toofast == oldtoofast) { + increment = (int)(increment * UP_F + 0.5); + } else { + increment = (int)(increment * DOWN_F + 0.5); + } + oldtoofast = toofast; + if (increment < 1) increment = 1; + if (toofast) { + z80_state.delay += increment; + } else { + z80_state.delay -= increment; + if (z80_state.delay < 0) { + z80_state.delay = 0; + increment = 1; + } + } + } + trs_paused = 0; + oldtv = tv; + oldtcount = z80_state.t_count; + } + + if (timer_on) { + trs_timer_interrupt(1); /* generate */ + trs_disk_motoroff_interrupt(trs_disk_motoroff()); + trs_kb_heartbeat(); /* part of keyboard stretch kludge */ + } +#if HAVE_SIGIO + x_flush_needed = 1; /* be sure to flush X output */ +#else + x_poll_count = 0; /* be sure to flush and check for X events */ +#endif + + /* Schedule next tick. We do it this way because the host system + probably didn't wake us up at exactly the right time. For + instance, on Linux i386 the real clock ticks at 10ms, but we want + to tick at 25ms. If we ask setitimer to wake us up in 25ms, it + will really wake us up in 30ms. The algorithm below compensates + for such an error by making the next tick shorter. */ + it.it_value.tv_sec = 0; + it.it_value.tv_usec = + (1000000/timer_hz) - (tv.tv_usec % (1000000/timer_hz)); + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 1000000/timer_hz; /* fail-safe */ + setitimer(ITIMER_REAL, &it, NULL); +} + +void +trs_timer_init() +{ + struct sigaction sa; + struct tm *lt; + time_t tt; + + if (trs_model == 1) { + timer_hz = TIMER_HZ_1; + z80_state.clockMHz = CLOCK_MHZ_1; + } else { + /* initially... */ + timer_hz = TIMER_HZ_3; + z80_state.clockMHz = CLOCK_MHZ_3; + } + + sa.sa_handler = trs_timer_event; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGALRM); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + + trs_timer_event(SIGALRM); + + /* Also initialize the clock in memory - hack */ + tt = time(NULL); + lt = localtime(&tt); + if (trs_model == 1) { + mem_write(LDOS_MONTH, (lt->tm_mon + 1) ^ 0x50); + mem_write(LDOS_DAY, lt->tm_mday); + mem_write(LDOS_YEAR, lt->tm_year - 80); + } else { + mem_write(LDOS3_MONTH, (lt->tm_mon + 1) ^ 0x50); + mem_write(LDOS3_DAY, lt->tm_mday); + mem_write(LDOS3_YEAR, lt->tm_year - 80); + if (trs_model >= 4) { + extern Uchar memory[]; + memory[LDOS4_MONTH] = lt->tm_mon + 1; + memory[LDOS4_DAY] = lt->tm_mday; + memory[LDOS4_YEAR] = lt->tm_year; + } + } +} + +void +trs_timer_off() +{ + timer_on = 0; +} + +void +trs_timer_on() +{ + if (!timer_on) { + timer_on = 1; + trs_timer_event(SIGALRM); + } +} + +void +trs_timer_speed(int fast) +{ + if (trs_model >= 4) { + timer_hz = fast ? TIMER_HZ_4 : TIMER_HZ_3; + z80_state.clockMHz = fast ? CLOCK_MHZ_4 : CLOCK_MHZ_3; + } else if (trs_model == 1) { + /* Typical 2x clock speedup kit */ + z80_state.clockMHz = CLOCK_MHZ_1 * ((fast&1) + 1); + } +} + +static trs_event_func event_func = NULL; +static int event_arg; + +/* Schedule an event to occur after "countdown" more t-states have + * executed. 0 makes the event happen immediately -- that is, at + * the end of the current instruction, but before the emulator checks + * for interrupts. It is legal for an event function to call + * trs_schedule_event. + * + * Only one event can be buffered. If you try to schedule a second + * event while one is still pending, the pending event (along with + * any further events that it schedules) is executed immediately. + */ +void +trs_schedule_event(trs_event_func f, int arg, int countdown) +{ + while (event_func) { +#if EDEBUG + error("warning: trying to schedule two events"); +#endif + trs_do_event(); + } + event_func = f; + event_arg = arg; + z80_state.sched = z80_state.t_count + (tstate_t) countdown; + if (z80_state.sched == 0) z80_state.sched--; +} + +/* + * If an event is scheduled, do it now. (If the event function + * schedules a new event, however, leave that one pending.) + */ +void +trs_do_event() +{ + trs_event_func f = event_func; + if (f) { + event_func = NULL; + z80_state.sched = 0; + f(event_arg); + } +} + +/* + * Cancel scheduled event, if any. + */ +void +trs_cancel_event() +{ + event_func = NULL; + z80_state.sched = 0; +} + +/* + * Check event scheduled + */ +trs_event_func +trs_event_scheduled() +{ + return event_func; +} diff --git a/trs_io.c b/trs_io.c new file mode 100644 index 0000000..73204fb --- /dev/null +++ b/trs_io.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 and following. +*/ + +/*#define PORTDEBUG1 1*/ +/*#define PORTDEBUG2 1*/ + +#include <time.h> + +#include "z80.h" +#include "trs.h" +#include "trs_disk.h" +#include "trs_hard.h" +#include "trs_uart.h" + +static int modesel = 0; /* Model I */ +static int modeimage = 0x8; /* Model III/4/4p */ +static int ctrlimage = 0; /* Model 4/4p */ +static int rominimage = 0; /* Model 4p */ + +/*ARGSUSED*/ +void z80_out(int port, int value) +{ +#if PORTDEBUG1 + debug("out (0x%02x), 0x%02x; pc 0x%04x\n", port, value, z80_state.pc.word); +#endif + /* First, ports common to all models */ + switch (port) { + case TRS_HARD_WP: /* 0xC0 */ + case TRS_HARD_CONTROL: /* 0xC1 */ + case TRS_HARD_DATA: /* 0xC8 */ + case TRS_HARD_ERROR: /* 0xC9 */ /*=TRS_HARD_PRECOMP*/ + case TRS_HARD_SECCNT: /* 0xCA */ + case TRS_HARD_SECNUM: /* 0xCB */ + case TRS_HARD_CYLLO: /* 0xCC */ + case TRS_HARD_CYLHI: /* 0xCD */ + case TRS_HARD_SDH: /* 0xCE */ + case TRS_HARD_STATUS: /* 0xCF */ /*=TRS_HARD_COMMAND*/ + trs_hard_out(port, value); + break; + case TRS_UART_RESET: /* 0xE8 */ + trs_uart_reset_out(value); + break; + case TRS_UART_BAUD: /* 0xE9 */ + trs_uart_baud_out(value); + break; + case TRS_UART_CONTROL: /* 0xEA */ + trs_uart_control_out(value); + break; + case TRS_UART_DATA: /* 0xEB */ + trs_uart_data_out(value); + break; + } + + if (trs_model == 1) { + /* Next, Model I only */ + switch (port) { + case 0x00: /* HRG off */ + case 0x01: /* HRG on */ + hrg_onoff(port); + break; + case 0x02: /* HRG write address low byte */ + hrg_write_addr(value, 0xff); + break; + case 0x03: /* HRG write address high byte */ + hrg_write_addr(value << 8, 0x3f00); + break; + case 0x05: /* HRG write data byte */ + hrg_write_data(value); + break; + case 0xB9: /* Orchestra-85 left channel */ + trs_orch90_out(1, value); + break; + case 0xB5: /* Orchestra-85 right channel */ + trs_orch90_out(2, value); + break; + case 0xFD: + /* GENIE location of printer port */ + trs_printer_write(value); + break; + case 0xFE: + /* Typical location for clock speedup kits */ + trs_timer_speed(value); + break; + case 0xFF: + /* screen mode select is on D3 line */ + modesel = (value >> 3) & 1; + trs_screen_expanded(modesel); + /* do cassette emulation */ + trs_cassette_motor((value >> 2) & 1); + trs_cassette_out(value & 0x3); + break; + default: + break; + } + + } else { + /* Next, Models III/4/4P only */ + switch (port) { + case 0x79: /* Orchestra-90 left channel */ + trs_orch90_out(1, value); + break; + case 0x75: /* Orchestra-90 right channel */ + trs_orch90_out(2, value); + break; + case 0x80: + if (trs_model >= 3) grafyx_write_x(value); + break; + case 0x81: + if (trs_model >= 3) grafyx_write_y(value); + break; + case 0x82: + if (trs_model >= 3) grafyx_write_data(value); + break; + case 0x83: + if (trs_model >= 3) grafyx_write_mode(value); + break; + case 0x84: + case 0x85: + case 0x86: + case 0x87: + if (trs_model >= 4) { + int changes = value ^ ctrlimage; + if (changes & 0x80) { + mem_video_page((value & 0x80) >> 7); + } + if (changes & 0x70) { + mem_bank((value & 0x70) >> 4); + } + if (changes & 0x08) { + trs_screen_inverse((value & 0x08) >> 3); + } + if (changes & 0x04) { + trs_screen_80x24((value & 0x04) >> 2); + } + if (changes & 0x03) { + mem_map(value & 0x03); + } + ctrlimage = value; + } + break; + case 0x8c: + if (trs_model >= 4) grafyx_write_xoffset(value); + break; + case 0x8d: + if (trs_model >= 4) grafyx_write_yoffset(value); + break; + case 0x8e: + if (trs_model >= 4) grafyx_write_overlay(value); + break; + case 0x90: + case 0x91: + case 0x92: + case 0x93: + trs_sound_out(value & 1); + break; + case 0x9C: + case 0x9D: /* !!? */ + case 0x9E: /* !!? */ + case 0x9F: /* !!? */ + if (trs_model == 5 /*4p*/) { + rominimage = value & 1; + mem_romin(rominimage); + } + break; + case 0xE0: + trs_interrupt_mask_write(value); + break; + case TRSDISK3_INTERRUPT: /* 0xE4 */ + case 0xE5: + case 0xE6: + case 0xE7: + trs_nmi_mask_write(value); + break; + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + modeimage = value; + /* cassette motor is on D1 */ + trs_cassette_motor((modeimage & 0x02) >> 1); + /* screen mode select is on D2 */ + trs_screen_expanded((modeimage & 0x04) >> 2); + /* alternate char set is on D3 */ + trs_screen_alternate(!((modeimage & 0x08) >> 3)); + /* clock speed is on D6; it affects timer HZ too */ + trs_timer_speed((modeimage & 0x40) >> 6); + break; + case TRSDISK3_COMMAND: /* 0xF0 */ + trs_disk_command_write(value); + break; + case TRSDISK3_TRACK: /* 0xF1 */ + trs_disk_track_write(value); + break; + case TRSDISK3_SECTOR: /* 0xF2 */ + trs_disk_sector_write(value); + break; + case TRSDISK3_DATA: /* 0xF3 */ + trs_disk_data_write(value); + break; + case TRSDISK3_SELECT: /* 0xF4 */ + case 0xF5: + case 0xF6: + case 0xF7: + trs_disk_select_write(value); + break; + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + trs_printer_write(value); + break; + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + if (trs_model == 3 && (value & 0x20) && grafyx_get_microlabs()) { + /* do Model III Micro Labs graphics card */ + grafyx_m3_write_mode(value); + } else { + /* do cassette emulation */ + trs_cassette_out(value & 3); + } + break; + default: + break; + } + } + return; +} + +/*ARGSUSED*/ +int z80_in(int port) +{ + /* First, ports common to all models */ +#if PORTDEBUG2 + debug("in (0x%02x); pc %04x\n", port, z80_state.pc.word); +#endif + + /* Support for a special HW real-time clock (TimeDate80?) + * I used to have. It was a small card-edge unit with a + * battery that held the time/date with power off. + * - Joe Peterson (joe@skyrush.com) + * + * According to the LDOS Quarterly 1-6, TChron1, TRSWatch, and + * TimeDate80 are accessible at high ports 0xB0-0xBC, while + * T-Timer is accessible at high ports 0xC0-0xCC. It does + * not say where the low ports were; Joe's code had 0x70-0x7C, + * so I presume that's correct at least for the TimeDate80. + * Note: 0xC0-0xCC conflicts with Radio Shack hard disk, so + * clock access at these ports is disabled starting in xtrs 4.1. + * + * These devices were based on the MSM5832 chip, which returns only + * a 2-digit year. It's not clear what software will do with the + * date in years beyond 1999. + */ + + if ((port >= 0x70 && port <= 0x7C) + || (port >= 0xB0 && port <= 0xBC) + /*|| (port >= 0xC0 && port <= 0xCC)*/) { + struct tm *time_info; + time_t time_secs; + + time_secs = time(NULL); + time_info = localtime(&time_secs); + + switch (port & 0x0F) { + case 0xC: /* year (high) */ + return (time_info->tm_year / 10) % 10; + case 0xB: /* year (low) */ + return (time_info->tm_year % 10); + case 0xA: /* month (high) */ + return ((time_info->tm_mon + 1) / 10); + case 0x9: /* month (low) */ + return ((time_info->tm_mon + 1) % 10); + case 0x8: /* date (high) and leap year (bit 2) */ + return ((time_info->tm_mday / 10) | ((time_info->tm_year % 4) ? 0 : 4)); + case 0x7: /* date (low) */ + return (time_info->tm_mday % 10); + case 0x6: /* day-of-week */ + return time_info->tm_wday; + case 0x5: /* hours (high) and PM (bit 2) and 24hr (bit 3) */ + return ((time_info->tm_hour / 10) | 8); + case 0x4: /* hours (low) */ + return (time_info->tm_hour % 10); + case 0x3: /* minutes (high) */ + return (time_info->tm_min / 10); + case 0x2: /* minutes (low) */ + return (time_info->tm_min % 10); + case 0x1: /* seconds (high) */ + return (time_info->tm_sec / 10); + case 0x0: /* seconds (low) */ + return (time_info->tm_sec % 10); + } + } + + switch (port) { + case 0x00: + return trs_joystick_in(); + case TRS_HARD_WP: /* 0xC0 */ + case TRS_HARD_CONTROL: /* 0xC1 */ + case TRS_HARD_DATA: /* 0xC8 */ + case TRS_HARD_ERROR: /* 0xC9 */ /*=TRS_HARD_PRECOMP*/ + case TRS_HARD_SECCNT: /* 0xCA */ + case TRS_HARD_SECNUM: /* 0xCB */ + case TRS_HARD_CYLLO: /* 0xCC */ + case TRS_HARD_CYLHI: /* 0xCD */ + case TRS_HARD_SDH: /* 0xCE */ + case TRS_HARD_STATUS: /* 0xCF */ /*=TRS_HARD_COMMAND*/ + return trs_hard_in(port); + case TRS_UART_MODEM: /* 0xE8 */ + return trs_uart_modem_in(); + case TRS_UART_SWITCHES: /* 0xE9 */ + return trs_uart_switches_in(); + case TRS_UART_STATUS: /* 0xEA */ + return trs_uart_status_in(); + case TRS_UART_DATA: /* 0xEB */ + return trs_uart_data_in(); + default: + break; + } + + if (trs_model == 1) { + /* Model I only */ + switch (port) { + case 0x00: /* HRG off (undocumented) */ + case 0x01: /* HRG on (undocumented) */ + hrg_onoff(port); + break; + case 0x04: /* HRG read data byte */ + return hrg_read_data(); + case 0xFD: + /* GENIE location of printer port */ + return trs_printer_read(); + case 0xFF: + return (modesel ? 0x7f : 0x3f) | trs_cassette_in(); + default: + break; + } + + } else { + /* Models III/4/4P only */ + switch (port) { + case 0x82: + if (trs_model >= 3) { + return grafyx_read_data(); + } + break; + case 0x9C: /* !!? */ + case 0x9D: /* !!? */ + case 0x9E: /* !!? */ + case 0x9F: /* !!? */ + if (trs_model == 5 /*4p*/) { + return rominimage; + } + break; + case TRS_HARD_WP: /* 0xC0 */ + case TRS_HARD_CONTROL: /* 0xC1 */ + case TRS_HARD_DATA: /* 0xC8 */ + case TRS_HARD_ERROR: /* 0xC9 */ /*=TRS_HARD_PRECOMP*/ + case TRS_HARD_SECCNT: /* 0xCA */ + case TRS_HARD_SECNUM: /* 0xCB */ + case TRS_HARD_CYLLO: /* 0xCC */ + case TRS_HARD_CYLHI: /* 0xCD */ + case TRS_HARD_SDH: /* 0xCE */ + case TRS_HARD_STATUS: /* 0xCF */ /*=TRS_HARD_COMMAND*/ + return trs_hard_in(port); + break; + case 0xE0: + return trs_interrupt_latch_read(); + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + trs_timer_interrupt(0); /* acknowledge */ + return 0xFF; + case TRSDISK3_INTERRUPT: /* 0xE4 */ + return trs_nmi_latch_read(); + case TRSDISK3_STATUS: /* 0xF0 */ + return trs_disk_status_read(); + case TRSDISK3_TRACK: /* 0xF1 */ + return trs_disk_track_read(); + case TRSDISK3_SECTOR: /* 0xF2 */ + return trs_disk_sector_read(); + case TRSDISK3_DATA: /* 0xF3 */ + return trs_disk_data_read(); + case 0xF8: + return trs_printer_read(); + case 0xFF: + return (modeimage & 0x7e) | trs_cassette_in(); + default: + break; + } + } + + /* other ports -- unmapped */ + return 0xFF; +} diff --git a/trs_iodefs.h b/trs_iodefs.h new file mode 100644 index 0000000..b95540d --- /dev/null +++ b/trs_iodefs.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +#define MAXCHARS 256 +#define TRS_CHAR_WIDTH 8 +#define TRS_CHAR_HEIGHT 12 +#define TRS_CHAR_HEIGHT4 10 diff --git a/trs_keyboard.c b/trs_keyboard.c new file mode 100644 index 0000000..0f51c01 --- /dev/null +++ b/trs_keyboard.c @@ -0,0 +1,1014 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Tue May 1 20:32:35 PDT 2001 by mann +*/ + +/*#define KBDEBUG 1*/ +/*#define JOYDEBUG 1*/ +#define KP_JOYSTICK 1 /* emulate joystick with keypad */ +/*#define KPNUM_JOYSTICK 1*/ /* emulate joystick with keypad + NumLock */ +#define SHIFT_F1_IS_F13 1 /* use if X reports Shift+F1..F8 as F13..F20 */ +/*#define SHIFT_F1_IS_F11 1*/ /* use if X reports Shift+F1..F10 as F11..F20 */ + +#include "z80.h" +#include "trs.h" +#include <X11/keysym.h> +#include <X11/X.h> +#include <unistd.h> + +/* + * Key event queue + */ +#define KEY_QUEUE_SIZE (32) +static int key_queue[KEY_QUEUE_SIZE]; +static int key_queue_head; +static int key_queue_entries; +static int key_immediate; + +/* + * TRS-80 key matrix + */ +#define TK(a, b) (((a)<<4)+(b)) +#define TK_ADDR(tk) (((tk) >> 4)&0xf) +#define TK_DATA(tk) ((tk)&0xf) +#define TK_DOWN(tk) (((tk)&0x10000) == 0) + +#define TK_AtSign TK(0, 0) /* @ */ +#define TK_A TK(0, 1) +#define TK_B TK(0, 2) +#define TK_C TK(0, 3) +#define TK_D TK(0, 4) +#define TK_E TK(0, 5) +#define TK_F TK(0, 6) +#define TK_G TK(0, 7) +#define TK_H TK(1, 0) +#define TK_I TK(1, 1) +#define TK_J TK(1, 2) +#define TK_K TK(1, 3) +#define TK_L TK(1, 4) +#define TK_M TK(1, 5) +#define TK_N TK(1, 6) +#define TK_O TK(1, 7) +#define TK_P TK(2, 0) +#define TK_Q TK(2, 1) +#define TK_R TK(2, 2) +#define TK_S TK(2, 3) +#define TK_T TK(2, 4) +#define TK_U TK(2, 5) +#define TK_V TK(2, 6) +#define TK_W TK(2, 7) +#define TK_X TK(3, 0) +#define TK_Y TK(3, 1) +#define TK_Z TK(3, 2) +#define TK_LeftBracket TK(3, 3) /* [ { */ /* not really on keyboard */ +#define TK_Backslash TK(3, 4) /* \ | */ /* not really on keyboard */ +#define TK_RightBracket TK(3, 5) /* ] } */ /* not really on keyboard */ +#define TK_Caret TK(3, 6) /* ^ ~ */ /* not really on keyboard */ +#define TK_Underscore TK(3, 7) /* _ */ /* not really on keyboard */ +#define TK_0 TK(4, 0) /* 0 */ +#define TK_1 TK(4, 1) /* 1 ! */ +#define TK_2 TK(4, 2) /* 2 " */ +#define TK_3 TK(4, 3) /* 3 # */ +#define TK_4 TK(4, 4) /* 4 $ */ +#define TK_5 TK(4, 5) /* 5 % */ +#define TK_6 TK(4, 6) /* 6 & */ +#define TK_7 TK(4, 7) /* 7 ' */ +#define TK_8 TK(5, 0) /* 8 ( */ +#define TK_9 TK(5, 1) /* 9 ) */ +#define TK_Colon TK(5, 2) /* : * */ +#define TK_Semicolon TK(5, 3) /* ; + */ +#define TK_Comma TK(5, 4) /* , < */ +#define TK_Minus TK(5, 5) /* - = */ +#define TK_Period TK(5, 6) /* . > */ +#define TK_Slash TK(5, 7) /* / ? */ +#define TK_Enter TK(6, 0) +#define TK_Clear TK(6, 1) +#define TK_Break TK(6, 2) +#define TK_Up TK(6, 3) +#define TK_Down TK(6, 4) +#define TK_Left TK(6, 5) +#define TK_Right TK(6, 6) +#define TK_Space TK(6, 7) +#define TK_LeftShift TK(7, 0) +#define TK_RightShift TK(7, 1) /* M3/4 only; both shifts are 7, 0 on M1 */ +#define TK_Ctrl TK(7, 2) /* M4 only */ +#define TK_CapsLock TK(7, 3) /* M4 only */ +#define TK_F1 TK(7, 4) /* M4 only */ +#define TK_F2 TK(7, 5) /* M4 only */ +#define TK_F3 TK(7, 6) /* M4 only */ +#define TK_Unused TK(7, 7) + +/* Fake keycodes with special meanings */ +#define TK_NULL TK(8, 0) +#define TK_Neutral TK(8, 1) +#define TK_ForceShift TK(8, 2) +#define TK_ForceNoShift TK(8, 3) +#define TK_ForceShiftPersistent TK(8, 4) +#define TK_AllKeysUp TK(8, 5) +#define TK_Joystick TK(10, 0) +#define TK_North TK(10, 1) +#define TK_Northeast TK(10, 9) +#define TK_East TK(10, 8) +#define TK_Southeast TK(10, 10) +#define TK_South TK(10, 2) +#define TK_Southwest TK(10, 5) +#define TK_West TK(10, 4) +#define TK_Northwest TK(10, 6) +#define TK_Fire TK(10, 16) + +typedef struct +{ + int bit_action; + int shift_action; +} KeyTable; + +/* Keysyms in the extended ASCII range 0x0000 - 0x00ff */ + +KeyTable ascii_key_table[] = { +/* 0x0 */ { TK_NULL, TK_Neutral }, /* undefined keysyms... */ +/* 0x1 */ { TK_NULL, TK_Neutral }, +/* 0x2 */ { TK_NULL, TK_Neutral }, +/* 0x3 */ { TK_NULL, TK_Neutral }, +/* 0x4 */ { TK_NULL, TK_Neutral }, +/* 0x5 */ { TK_NULL, TK_Neutral }, +/* 0x6 */ { TK_NULL, TK_Neutral }, +/* 0x7 */ { TK_NULL, TK_Neutral }, +/* 0x8 */ { TK_NULL, TK_Neutral }, +/* 0x9 */ { TK_NULL, TK_Neutral }, +/* 0xa */ { TK_NULL, TK_Neutral }, +/* 0xb */ { TK_NULL, TK_Neutral }, +/* 0xc */ { TK_NULL, TK_Neutral }, +/* 0xd */ { TK_NULL, TK_Neutral }, +/* 0xe */ { TK_NULL, TK_Neutral }, +/* 0xf */ { TK_NULL, TK_Neutral }, +/* 0x10 */ { TK_NULL, TK_Neutral }, +/* 0x11 */ { TK_NULL, TK_Neutral }, +/* 0x12 */ { TK_NULL, TK_Neutral }, +/* 0x13 */ { TK_NULL, TK_Neutral }, +/* 0x14 */ { TK_NULL, TK_Neutral }, +/* 0x15 */ { TK_NULL, TK_Neutral }, +/* 0x16 */ { TK_NULL, TK_Neutral }, +/* 0x17 */ { TK_NULL, TK_Neutral }, +/* 0x18 */ { TK_NULL, TK_Neutral }, +/* 0x19 */ { TK_NULL, TK_Neutral }, +/* 0x1a */ { TK_NULL, TK_Neutral }, +/* 0x1b */ { TK_NULL, TK_Neutral }, +/* 0x1c */ { TK_NULL, TK_Neutral }, +/* 0x1d */ { TK_NULL, TK_Neutral }, +/* 0x1e */ { TK_NULL, TK_Neutral }, +/* 0x1f */ { TK_NULL, TK_Neutral }, /* ...end undefined keysyms */ +/* 0x20 */ { TK_Space, TK_Neutral }, +/* 0x21 */ { TK_1, TK_ForceShift }, +/* 0x22 */ { TK_2, TK_ForceShift }, +/* 0x23 */ { TK_3, TK_ForceShift }, +/* 0x24 */ { TK_4, TK_ForceShift }, +/* 0x25 */ { TK_5, TK_ForceShift }, +/* 0x26 */ { TK_6, TK_ForceShift }, +/* 0x27 */ { TK_7, TK_ForceShift }, +/* 0x28 */ { TK_8, TK_ForceShift }, +/* 0x29 */ { TK_9, TK_ForceShift }, +/* 0x2a */ { TK_Colon, TK_ForceShift }, +/* 0x2b */ { TK_Semicolon, TK_ForceShift }, +/* 0x2c */ { TK_Comma, TK_ForceNoShift }, +/* 0x2d */ { TK_Minus, TK_ForceNoShift }, +/* 0x2e */ { TK_Period, TK_ForceNoShift }, +/* 0x2f */ { TK_Slash, TK_ForceNoShift }, +/* 0x30 */ { TK_0, TK_ForceNoShift }, +/* 0x31 */ { TK_1, TK_ForceNoShift }, +/* 0x32 */ { TK_2, TK_ForceNoShift }, +/* 0x33 */ { TK_3, TK_ForceNoShift }, +/* 0x34 */ { TK_4, TK_ForceNoShift }, +/* 0x35 */ { TK_5, TK_ForceNoShift }, +/* 0x36 */ { TK_6, TK_ForceNoShift }, +/* 0x37 */ { TK_7, TK_ForceNoShift }, +/* 0x38 */ { TK_8, TK_ForceNoShift }, +/* 0x39 */ { TK_9, TK_ForceNoShift }, +/* 0x3a */ { TK_Colon, TK_ForceNoShift }, +/* 0x3b */ { TK_Semicolon, TK_ForceNoShift }, +/* 0x3c */ { TK_Comma, TK_ForceShift }, +/* 0x3d */ { TK_Minus, TK_ForceShift }, +/* 0x3e */ { TK_Period, TK_ForceShift }, +/* 0x3f */ { TK_Slash, TK_ForceShift }, +/* 0x40 */ { TK_AtSign, TK_ForceNoShift }, +/* 0x41 */ { TK_A, TK_ForceShift }, +/* 0x42 */ { TK_B, TK_ForceShift }, +/* 0x43 */ { TK_C, TK_ForceShift }, +/* 0x44 */ { TK_D, TK_ForceShift }, +/* 0x45 */ { TK_E, TK_ForceShift }, +/* 0x46 */ { TK_F, TK_ForceShift }, +/* 0x47 */ { TK_G, TK_ForceShift }, +/* 0x48 */ { TK_H, TK_ForceShift }, +/* 0x49 */ { TK_I, TK_ForceShift }, +/* 0x4a */ { TK_J, TK_ForceShift }, +/* 0x4b */ { TK_K, TK_ForceShift }, +/* 0x4c */ { TK_L, TK_ForceShift }, +/* 0x4d */ { TK_M, TK_ForceShift }, +/* 0x4e */ { TK_N, TK_ForceShift }, +/* 0x4f */ { TK_O, TK_ForceShift }, +/* 0x50 */ { TK_P, TK_ForceShift }, +/* 0x51 */ { TK_Q, TK_ForceShift }, +/* 0x52 */ { TK_R, TK_ForceShift }, +/* 0x53 */ { TK_S, TK_ForceShift }, +/* 0x54 */ { TK_T, TK_ForceShift }, +/* 0x55 */ { TK_U, TK_ForceShift }, +/* 0x56 */ { TK_V, TK_ForceShift }, +/* 0x57 */ { TK_W, TK_ForceShift }, +/* 0x58 */ { TK_X, TK_ForceShift }, +/* 0x59 */ { TK_Y, TK_ForceShift }, +/* 0x5a */ { TK_Z, TK_ForceShift }, +/* 0x5b */ { TK_LeftBracket, TK_ForceNoShift }, +/* 0x5c */ { TK_Backslash, TK_ForceNoShift }, +/* 0x5d */ { TK_RightBracket, TK_ForceNoShift }, +/* 0x5e */ { TK_Caret, TK_ForceNoShift }, +/* 0x5f */ { TK_Underscore, TK_ForceNoShift }, +/* 0x60 */ { TK_AtSign, TK_ForceShift }, +/* 0x61 */ { TK_A, TK_ForceNoShift }, +/* 0x62 */ { TK_B, TK_ForceNoShift }, +/* 0x63 */ { TK_C, TK_ForceNoShift }, +/* 0x64 */ { TK_D, TK_ForceNoShift }, +/* 0x65 */ { TK_E, TK_ForceNoShift }, +/* 0x66 */ { TK_F, TK_ForceNoShift }, +/* 0x67 */ { TK_G, TK_ForceNoShift }, +/* 0x68 */ { TK_H, TK_ForceNoShift }, +/* 0x69 */ { TK_I, TK_ForceNoShift }, +/* 0x6a */ { TK_J, TK_ForceNoShift }, +/* 0x6b */ { TK_K, TK_ForceNoShift }, +/* 0x6c */ { TK_L, TK_ForceNoShift }, +/* 0x6d */ { TK_M, TK_ForceNoShift }, +/* 0x6e */ { TK_N, TK_ForceNoShift }, +/* 0x6f */ { TK_O, TK_ForceNoShift }, +/* 0x70 */ { TK_P, TK_ForceNoShift }, +/* 0x71 */ { TK_Q, TK_ForceNoShift }, +/* 0x72 */ { TK_R, TK_ForceNoShift }, +/* 0x73 */ { TK_S, TK_ForceNoShift }, +/* 0x74 */ { TK_T, TK_ForceNoShift }, +/* 0x75 */ { TK_U, TK_ForceNoShift }, +/* 0x76 */ { TK_V, TK_ForceNoShift }, +/* 0x77 */ { TK_W, TK_ForceNoShift }, +/* 0x78 */ { TK_X, TK_ForceNoShift }, +/* 0x79 */ { TK_Y, TK_ForceNoShift }, +/* 0x7a */ { TK_Z, TK_ForceNoShift }, +/* 0x7b */ { TK_LeftBracket, TK_ForceShift }, +/* 0x7c */ { TK_Backslash, TK_ForceShift }, +/* 0x7d */ { TK_RightBracket, TK_ForceShift }, +/* 0x7e */ { TK_Caret, TK_ForceShift }, +/* 0x7f */ { TK_NULL, TK_Neutral }, +/* 0x80 */ { TK_NULL, TK_Neutral }, +/* 0x81 */ { TK_NULL, TK_Neutral }, +/* 0x82 */ { TK_NULL, TK_Neutral }, +/* 0x83 */ { TK_NULL, TK_Neutral }, +/* 0x84 */ { TK_NULL, TK_Neutral }, +/* 0x85 */ { TK_NULL, TK_Neutral }, +/* 0x86 */ { TK_NULL, TK_Neutral }, +/* 0x87 */ { TK_NULL, TK_Neutral }, +/* 0x88 */ { TK_NULL, TK_Neutral }, +/* 0x89 */ { TK_NULL, TK_Neutral }, +/* 0x8a */ { TK_NULL, TK_Neutral }, +/* 0x8b */ { TK_NULL, TK_Neutral }, +/* 0x8c */ { TK_NULL, TK_Neutral }, +/* 0x8d */ { TK_NULL, TK_Neutral }, +/* 0x8e */ { TK_NULL, TK_Neutral }, +/* 0x8f */ { TK_NULL, TK_Neutral }, +/* 0x90 */ { TK_NULL, TK_Neutral }, +/* 0x91 */ { TK_NULL, TK_Neutral }, +/* 0x92 */ { TK_NULL, TK_Neutral }, +/* 0x93 */ { TK_NULL, TK_Neutral }, +/* 0x94 */ { TK_NULL, TK_Neutral }, +/* 0x95 */ { TK_NULL, TK_Neutral }, +/* 0x96 */ { TK_NULL, TK_Neutral }, +/* 0x97 */ { TK_NULL, TK_Neutral }, +/* 0x98 */ { TK_NULL, TK_Neutral }, +/* 0x99 */ { TK_NULL, TK_Neutral }, +/* 0x9a */ { TK_NULL, TK_Neutral }, +/* 0x9b */ { TK_NULL, TK_Neutral }, +/* 0x9c */ { TK_NULL, TK_Neutral }, +/* 0x9d */ { TK_NULL, TK_Neutral }, +/* 0x9e */ { TK_NULL, TK_Neutral }, +/* 0x9f */ { TK_NULL, TK_Neutral }, +/* 0xa0 */ { TK_NULL, TK_Neutral }, +/* 0xa1 */ { TK_NULL, TK_Neutral }, +/* 0xa2 */ { TK_NULL, TK_Neutral }, +/* 0xa3 */ { TK_NULL, TK_Neutral }, +/* 0xa4 */ { TK_NULL, TK_Neutral }, +/* 0xa5 */ { TK_NULL, TK_Neutral }, +/* 0xa6 */ { TK_NULL, TK_Neutral }, +/* 0xa7 */ { TK_NULL, TK_Neutral }, +/* 0xa8 */ { TK_NULL, TK_Neutral }, +/* 0xa9 */ { TK_NULL, TK_Neutral }, +/* 0xaa */ { TK_NULL, TK_Neutral }, +/* 0xab */ { TK_NULL, TK_Neutral }, +/* 0xac */ { TK_NULL, TK_Neutral }, +/* 0xad */ { TK_NULL, TK_Neutral }, +/* 0xae */ { TK_NULL, TK_Neutral }, +/* 0xaf */ { TK_NULL, TK_Neutral }, +/* 0xb0 */ { TK_NULL, TK_Neutral }, +/* 0xb1 */ { TK_NULL, TK_Neutral }, +/* 0xb2 */ { TK_NULL, TK_Neutral }, +/* 0xb3 */ { TK_NULL, TK_Neutral }, +/* 0xb4 */ { TK_NULL, TK_Neutral }, +/* 0xb5 */ { TK_NULL, TK_Neutral }, +/* 0xb6 */ { TK_NULL, TK_Neutral }, +/* 0xb7 */ { TK_NULL, TK_Neutral }, +/* 0xb8 */ { TK_NULL, TK_Neutral }, +/* 0xb9 */ { TK_NULL, TK_Neutral }, +/* 0xba */ { TK_NULL, TK_Neutral }, +/* 0xbb */ { TK_NULL, TK_Neutral }, +/* 0xbc */ { TK_NULL, TK_Neutral }, +/* 0xbd */ { TK_NULL, TK_Neutral }, +/* 0xbe */ { TK_NULL, TK_Neutral }, +/* 0xbf */ { TK_NULL, TK_Neutral }, +/* 0xc0 */ { TK_NULL, TK_Neutral }, +/* 0xc1 */ { TK_NULL, TK_Neutral }, +/* 0xc2 */ { TK_NULL, TK_Neutral }, +/* 0xc3 */ { TK_NULL, TK_Neutral }, +/* 0xc4 */ { TK_LeftBracket, TK_ForceShift }, /* Ä */ +/* 0xc5 */ { TK_NULL, TK_Neutral }, +/* 0xc6 */ { TK_NULL, TK_Neutral }, +/* 0xc7 */ { TK_NULL, TK_Neutral }, +/* 0xc8 */ { TK_NULL, TK_Neutral }, +/* 0xc9 */ { TK_NULL, TK_Neutral }, +/* 0xca */ { TK_NULL, TK_Neutral }, +/* 0xcb */ { TK_NULL, TK_Neutral }, +/* 0xcc */ { TK_NULL, TK_Neutral }, +/* 0xcd */ { TK_NULL, TK_Neutral }, +/* 0xce */ { TK_NULL, TK_Neutral }, +/* 0xcf */ { TK_NULL, TK_Neutral }, +/* 0xd0 */ { TK_NULL, TK_Neutral }, +/* 0xd1 */ { TK_NULL, TK_Neutral }, +/* 0xd2 */ { TK_NULL, TK_Neutral }, +/* 0xd3 */ { TK_NULL, TK_Neutral }, +/* 0xd4 */ { TK_NULL, TK_Neutral }, +/* 0xd5 */ { TK_NULL, TK_Neutral }, +/* 0xd6 */ { TK_Backslash, TK_ForceShift }, /* Ö */ +/* 0xd7 */ { TK_NULL, TK_Neutral }, +/* 0xd8 */ { TK_NULL, TK_Neutral }, +/* 0xd9 */ { TK_NULL, TK_Neutral }, +/* 0xda */ { TK_NULL, TK_Neutral }, +/* 0xdb */ { TK_NULL, TK_Neutral }, +/* 0xdc */ { TK_RightBracket, TK_ForceShift }, /* Ü */ +/* 0xdd */ { TK_NULL, TK_Neutral }, +/* 0xde */ { TK_NULL, TK_Neutral }, +/* 0xdf */ { TK_Caret, TK_ForceNoShift }, /* ß */ +/* 0xe0 */ { TK_NULL, TK_Neutral }, +/* 0xe1 */ { TK_NULL, TK_Neutral }, +/* 0xe2 */ { TK_NULL, TK_Neutral }, +/* 0xe3 */ { TK_NULL, TK_Neutral }, +/* 0xe4 */ { TK_LeftBracket, TK_ForceNoShift }, /* ä */ +/* 0xe5 */ { TK_NULL, TK_Neutral }, +/* 0xe6 */ { TK_NULL, TK_Neutral }, +/* 0xe7 */ { TK_NULL, TK_Neutral }, +/* 0xe8 */ { TK_NULL, TK_Neutral }, +/* 0xe9 */ { TK_NULL, TK_Neutral }, +/* 0xea */ { TK_NULL, TK_Neutral }, +/* 0xeb */ { TK_NULL, TK_Neutral }, +/* 0xec */ { TK_NULL, TK_Neutral }, +/* 0xed */ { TK_NULL, TK_Neutral }, +/* 0xee */ { TK_NULL, TK_Neutral }, +/* 0xef */ { TK_NULL, TK_Neutral }, +/* 0xf0 */ { TK_NULL, TK_Neutral }, +/* 0xf1 */ { TK_NULL, TK_Neutral }, +/* 0xf2 */ { TK_NULL, TK_Neutral }, +/* 0xf3 */ { TK_NULL, TK_Neutral }, +/* 0xf4 */ { TK_NULL, TK_Neutral }, +/* 0xf5 */ { TK_NULL, TK_Neutral }, +/* 0xf6 */ { TK_Backslash, TK_ForceNoShift }, /* ö */ +/* 0xf7 */ { TK_NULL, TK_Neutral }, +/* 0xf8 */ { TK_NULL, TK_Neutral }, +/* 0xf9 */ { TK_NULL, TK_Neutral }, +/* 0xfa */ { TK_NULL, TK_Neutral }, +/* 0xfb */ { TK_NULL, TK_Neutral }, +/* 0xfc */ { TK_RightBracket, TK_ForceNoShift }, /* ü */ +/* 0xfd */ { TK_NULL, TK_Neutral }, +/* 0xfe */ { TK_NULL, TK_Neutral }, +/* 0xff */ { TK_NULL, TK_Neutral } +}; + +/* Keysyms in the function key range 0xff00 - 0xffff */ + +KeyTable function_key_table[] = { +/* 0xff00 */ { TK_NULL, TK_Neutral }, +/* 0xff01 */ { TK_NULL, TK_Neutral }, +/* 0xff02 */ { TK_NULL, TK_Neutral }, +/* 0xff03 */ { TK_NULL, TK_Neutral }, +/* 0xff04 */ { TK_NULL, TK_Neutral }, +/* 0xff05 */ { TK_NULL, TK_Neutral }, +/* 0xff06 */ { TK_NULL, TK_Neutral }, +/* 0xff07 */ { TK_NULL, TK_Neutral }, +/* 0xff08 XK_BackSpace */ { TK_Left, TK_Neutral }, +/* 0xff09 XK_Tab */ { TK_Right, TK_Neutral }, +/* 0xff0a XK_Linefeed */ { TK_Down, TK_Neutral }, +/* 0xff0b XK_Clear */ { TK_Clear, TK_Neutral }, +/* 0xff0c */ { TK_NULL, TK_Neutral }, +/* 0xff0d XK_Return */ { TK_Enter, TK_Neutral }, +/* 0xff0e */ { TK_NULL, TK_Neutral }, +/* 0xff0f */ { TK_NULL, TK_Neutral }, +/* 0xff10 */ { TK_NULL, TK_Neutral }, +/* 0xff11 */ { TK_NULL, TK_Neutral }, +/* 0xff12 */ { TK_NULL, TK_Neutral }, +/* 0xff13 XK_Pause */ { TK_NULL, TK_Neutral }, +/* 0xff14 XK_ScrollLock */ { TK_AtSign, TK_Neutral }, +/* 0xff15 XK_Sys_Req */ { TK_NULL, TK_Neutral }, +/* 0xff16 */ { TK_NULL, TK_Neutral }, +/* 0xff17 */ { TK_NULL, TK_Neutral }, +/* 0xff18 */ { TK_NULL, TK_Neutral }, +/* 0xff19 */ { TK_NULL, TK_Neutral }, +/* 0xff1a */ { TK_NULL, TK_Neutral }, +/* 0xff1b XK_Escape */ { TK_Break, TK_Neutral }, +/* 0xff1c */ { TK_NULL, TK_Neutral }, +/* 0xff1d */ { TK_NULL, TK_Neutral }, +/* 0xff1e */ { TK_NULL, TK_Neutral }, +/* 0xff1f */ { TK_NULL, TK_Neutral }, +/* 0xff20 XK_Multi_key */ { TK_AtSign, TK_Neutral }, +/* 0xff21 */ { TK_NULL, TK_Neutral }, +/* 0xff22 */ { TK_NULL, TK_Neutral }, +/* 0xff23 */ { TK_NULL, TK_Neutral }, +/* 0xff24 */ { TK_NULL, TK_Neutral }, +/* 0xff25 */ { TK_NULL, TK_Neutral }, +/* 0xff26 */ { TK_NULL, TK_Neutral }, +/* 0xff27 */ { TK_NULL, TK_Neutral }, +/* 0xff28 */ { TK_NULL, TK_Neutral }, +/* 0xff29 */ { TK_NULL, TK_Neutral }, +/* 0xff2a */ { TK_NULL, TK_Neutral }, +/* 0xff2b */ { TK_NULL, TK_Neutral }, +/* 0xff2c */ { TK_NULL, TK_Neutral }, +/* 0xff2d */ { TK_NULL, TK_Neutral }, +/* 0xff2e */ { TK_NULL, TK_Neutral }, +/* 0xff2f */ { TK_NULL, TK_Neutral }, +/* 0xff30 */ { TK_NULL, TK_Neutral }, +/* 0xff31 */ { TK_NULL, TK_Neutral }, +/* 0xff32 */ { TK_NULL, TK_Neutral }, +/* 0xff33 */ { TK_NULL, TK_Neutral }, +/* 0xff34 */ { TK_NULL, TK_Neutral }, +/* 0xff35 */ { TK_NULL, TK_Neutral }, +/* 0xff36 */ { TK_NULL, TK_Neutral }, +/* 0xff37 */ { TK_NULL, TK_Neutral }, +/* 0xff38 */ { TK_NULL, TK_Neutral }, +/* 0xff39 */ { TK_NULL, TK_Neutral }, +/* 0xff3a */ { TK_NULL, TK_Neutral }, +/* 0xff3b */ { TK_NULL, TK_Neutral }, +/* 0xff3c */ { TK_NULL, TK_Neutral }, +/* 0xff3d */ { TK_NULL, TK_Neutral }, +/* 0xff3e */ { TK_NULL, TK_Neutral }, +/* 0xff3f */ { TK_NULL, TK_Neutral }, +/* 0xff40 */ { TK_NULL, TK_Neutral }, +/* 0xff41 */ { TK_NULL, TK_Neutral }, +/* 0xff42 */ { TK_NULL, TK_Neutral }, +/* 0xff43 */ { TK_NULL, TK_Neutral }, +/* 0xff44 */ { TK_NULL, TK_Neutral }, +/* 0xff45 */ { TK_NULL, TK_Neutral }, +/* 0xff46 */ { TK_NULL, TK_Neutral }, +/* 0xff47 */ { TK_NULL, TK_Neutral }, +/* 0xff48 */ { TK_NULL, TK_Neutral }, +/* 0xff49 */ { TK_NULL, TK_Neutral }, +/* 0xff4a */ { TK_NULL, TK_Neutral }, +/* 0xff4b */ { TK_NULL, TK_Neutral }, +/* 0xff4c */ { TK_NULL, TK_Neutral }, +/* 0xff4d */ { TK_NULL, TK_Neutral }, +/* 0xff4e */ { TK_NULL, TK_Neutral }, +/* 0xff4f */ { TK_NULL, TK_Neutral }, +/* 0xff50 XK_Home */ { TK_Clear, TK_Neutral }, +/* 0xff51 XK_Left */ { TK_Left, TK_Neutral }, +/* 0xff52 XK_Up */ { TK_Up, TK_Neutral }, +/* 0xff53 XK_Right */ { TK_Right, TK_Neutral }, +/* 0xff54 XK_Down */ { TK_Down, TK_Neutral }, +/* 0xff55 XK_Prior, XK_Page_Up */ { TK_LeftShift, TK_Neutral }, +/* 0xff56 XK_Next, XK_Page_Down */ { TK_RightShift, TK_Neutral }, +/* 0xff57 XK_End */ { TK_Unused, TK_Neutral }, +/* 0xff58 XK_Begin */ { TK_NULL, TK_Neutral }, +/* 0xff59 */ { TK_NULL, TK_Neutral }, +/* 0xff5a */ { TK_NULL, TK_Neutral }, +/* 0xff5b */ { TK_NULL, TK_Neutral }, +/* 0xff5c */ { TK_NULL, TK_Neutral }, +/* 0xff5d */ { TK_NULL, TK_Neutral }, +/* 0xff5e */ { TK_NULL, TK_Neutral }, +/* 0xff5f */ { TK_NULL, TK_Neutral }, +/* 0xff60 XK_Select */ { TK_NULL, TK_Neutral }, +/* 0xff61 XK_Print */ { TK_NULL, TK_Neutral }, +/* 0xff62 XK_Execute */ { TK_NULL, TK_Neutral }, +/* 0xff63 XK_Insert */ { TK_Underscore, TK_Neutral }, +/* 0xff64 */ { TK_NULL, TK_Neutral }, +/* 0xff65 XK_Undo */ { TK_NULL, TK_Neutral }, +/* 0xff66 XK_Redo */ { TK_NULL, TK_Neutral }, +/* 0xff67 XK_Menu */ { TK_NULL, TK_Neutral }, +/* 0xff68 XK_Find */ { TK_NULL, TK_Neutral }, +/* 0xff69 XK_Cancel */ { TK_NULL, TK_Neutral }, +/* 0xff6a XK_Help */ { TK_NULL, TK_Neutral }, +/* 0xff6b XK_Break */ { TK_Break, TK_Neutral }, +/* 0xff6c */ { TK_NULL, TK_Neutral }, +/* 0xff6d */ { TK_NULL, TK_Neutral }, +/* 0xff6e */ { TK_NULL, TK_Neutral }, +/* 0xff6f */ { TK_NULL, TK_Neutral }, +/* 0xff70 */ { TK_NULL, TK_Neutral }, +/* 0xff71 */ { TK_NULL, TK_Neutral }, +/* 0xff72 */ { TK_NULL, TK_Neutral }, +/* 0xff73 */ { TK_NULL, TK_Neutral }, +/* 0xff74 */ { TK_NULL, TK_Neutral }, +/* 0xff75 */ { TK_NULL, TK_Neutral }, +/* 0xff76 */ { TK_NULL, TK_Neutral }, +/* 0xff77 */ { TK_NULL, TK_Neutral }, +/* 0xff78 */ { TK_NULL, TK_Neutral }, +/* 0xff79 */ { TK_NULL, TK_Neutral }, +/* 0xff7a */ { TK_NULL, TK_Neutral }, +/* 0xff7b */ { TK_NULL, TK_Neutral }, +/* 0xff7c */ { TK_NULL, TK_Neutral }, +/* 0xff7d */ { TK_NULL, TK_Neutral }, +/* 0xff7e XK_Mode_switch */ { TK_NULL, TK_Neutral }, +/* 0xff7f XK_Num_Lock */ { TK_NULL, TK_Neutral }, +/* 0xff80 XK_KP_Space */ { TK_Space, TK_Neutral }, +/* 0xff81 */ { TK_NULL, TK_Neutral }, +/* 0xff82 */ { TK_NULL, TK_Neutral }, +/* 0xff83 */ { TK_NULL, TK_Neutral }, +/* 0xff84 */ { TK_NULL, TK_Neutral }, +/* 0xff85 */ { TK_NULL, TK_Neutral }, +/* 0xff86 */ { TK_NULL, TK_Neutral }, +/* 0xff87 */ { TK_NULL, TK_Neutral }, +/* 0xff88 */ { TK_NULL, TK_Neutral }, +/* 0xff89 XK_KP_Tab */ { TK_Right, TK_Neutral }, +/* 0xff8a */ { TK_NULL, TK_Neutral }, +/* 0xff8b */ { TK_NULL, TK_Neutral }, +/* 0xff8c */ { TK_NULL, TK_Neutral }, +/* 0xff8d XK_KP_Enter */ { TK_Enter, TK_Neutral }, +/* 0xff8e */ { TK_NULL, TK_Neutral }, +/* 0xff8f */ { TK_NULL, TK_Neutral }, +/* 0xff90 */ { TK_NULL, TK_Neutral }, +/* 0xff91 XK_KP_F1 */ { TK_F1, TK_Neutral }, +/* 0xff92 XK_KP_F2 */ { TK_F2, TK_Neutral }, +/* 0xff93 XK_KP_F3 */ { TK_F3, TK_Neutral }, +/* 0xff94 XK_KP_F4 */ { TK_CapsLock, TK_Neutral }, +#if KP_JOYSTICK +/* 0xff95 XK_KP_Home */ { TK_Northwest, TK_Neutral }, +/* 0xff96 XK_KP_Left */ { TK_West, TK_Neutral }, +/* 0xff97 XK_KP_Up */ { TK_North, TK_Neutral }, +/* 0xff98 XK_KP_Right */ { TK_East, TK_Neutral }, +/* 0xff99 XK_KP_Down */ { TK_South, TK_Neutral }, +/* 0xff9a XK_KP_Prior, XK_KP_Page_Up */ { TK_Northeast, TK_Neutral }, +/* 0xff9b XK_KP_Next, XK_KP_Page_Down */ { TK_Southeast, TK_Neutral }, +/* 0xff9c XK_KP_End */ { TK_Southwest, TK_Neutral }, +/* 0xff9d XK_KP_Begin */ { TK_Fire, TK_Neutral }, +/* 0xff9e XK_KP_Insert */ { TK_Fire, TK_Neutral }, +#else +/* 0xff95 XK_KP_Home */ { TK_Clear, TK_Neutral }, +/* 0xff96 XK_KP_Left */ { TK_Left, TK_Neutral }, +/* 0xff97 XK_KP_Up */ { TK_Up, TK_Neutral }, +/* 0xff98 XK_KP_Right */ { TK_Right, TK_Neutral }, +/* 0xff99 XK_KP_Down */ { TK_Down, TK_Neutral }, +/* 0xff9a XK_KP_Prior, XK_KP_Page_Up */ { TK_LeftShift, TK_Neutral }, +/* 0xff9b XK_KP_Next, XK_KP_Page_Down */ { TK_RightShift, TK_Neutral }, +/* 0xff9c XK_KP_End */ { TK_Unused, TK_Neutral }, +/* 0xff9d XK_KP_Begin */ { TK_NULL, TK_Neutral }, +/* 0xff9e XK_KP_Insert */ { TK_Underscore, TK_Neutral }, +#endif +/* 0xff9f XK_KP_Delete */ { TK_Left, TK_Neutral }, +/* 0xffa0 */ { TK_NULL, TK_Neutral }, +/* 0xffa1 */ { TK_NULL, TK_Neutral }, +/* 0xffa2 */ { TK_NULL, TK_Neutral }, +/* 0xffa3 */ { TK_NULL, TK_Neutral }, +/* 0xffa4 */ { TK_NULL, TK_Neutral }, +/* 0xffa5 */ { TK_NULL, TK_Neutral }, +/* 0xffa6 */ { TK_NULL, TK_Neutral }, +/* 0xffa7 */ { TK_NULL, TK_Neutral }, +/* 0xffa8 */ { TK_NULL, TK_Neutral }, +/* 0xffa9 */ { TK_NULL, TK_Neutral }, +/* 0xffaa XK_KP_Multiply */ { TK_Colon, TK_ForceShift }, +/* 0xffab XK_KP_Add */ { TK_Semicolon, TK_ForceShift }, +/* 0xffac XK_KP_Separator*/ { TK_Comma, TK_Neutral }, +/* 0xffad XK_KP_Subtract */ { TK_Minus, TK_Neutral }, +/* 0xffae XK_KP_Decimal */ { TK_Period, TK_Neutral }, +/* 0xffaf XK_KP_Divide */ { TK_Slash, TK_Neutral }, +#if KPNUM_JOYSTICK +/* 0xffb0 XK_KP_0 */ { TK_Fire, TK_Neutral }, +/* 0xffb1 XK_KP_1 */ { TK_Southwest, TK_Neutral }, +/* 0xffb2 XK_KP_2 */ { TK_South, TK_Neutral }, +/* 0xffb3 XK_KP_3 */ { TK_Southeast, TK_Neutral }, +/* 0xffb4 XK_KP_4 */ { TK_West, TK_Neutral }, +/* 0xffb5 XK_KP_5 */ { TK_Fire, TK_Neutral }, +/* 0xffb6 XK_KP_6 */ { TK_East, TK_Neutral }, +/* 0xffb7 XK_KP_7 */ { TK_Northwest, TK_Neutral }, +/* 0xffb8 XK_KP_8 */ { TK_North, TK_Neutral }, +/* 0xffb9 XK_KP_9 */ { TK_Northeast, TK_Neutral }, +#else +/* 0xffb0 XK_KP_0 */ { TK_0, TK_Neutral }, +/* 0xffb1 XK_KP_1 */ { TK_1, TK_Neutral }, +/* 0xffb2 XK_KP_2 */ { TK_2, TK_Neutral }, +/* 0xffb3 XK_KP_3 */ { TK_3, TK_Neutral }, +/* 0xffb4 XK_KP_4 */ { TK_4, TK_Neutral }, +/* 0xffb5 XK_KP_5 */ { TK_5, TK_Neutral }, +/* 0xffb6 XK_KP_6 */ { TK_6, TK_Neutral }, +/* 0xffb7 XK_KP_7 */ { TK_7, TK_Neutral }, +/* 0xffb8 XK_KP_8 */ { TK_8, TK_Neutral }, +/* 0xffb9 XK_KP_9 */ { TK_9, TK_Neutral }, +#endif +/* 0xffba */ { TK_NULL, TK_Neutral }, +/* 0xffbb */ { TK_NULL, TK_Neutral }, +/* 0xffbc */ { TK_NULL, TK_Neutral }, +/* 0xffbd XK_KP_Equal */ { TK_Minus, TK_ForceShift }, +/* 0xffbe XK_F1 */ { TK_F1, TK_Neutral }, +/* 0xffbf XK_F2 */ { TK_F2, TK_Neutral }, +/* 0xffc0 XK_F3 */ { TK_F3, TK_Neutral }, +/* 0xffc1 XK_F4 */ { TK_CapsLock, TK_Neutral }, +/* 0xffc2 XK_F5 */ { TK_AtSign, TK_Neutral }, +/* 0xffc3 XK_F6 */ { TK_0, TK_Neutral }, +/* 0xffc4 XK_F7 */ { TK_NULL, TK_Neutral }, +/* 0xffc5 XK_F8 */ { TK_NULL, TK_Neutral }, +/* 0xffc6 XK_F9 */ { TK_NULL, TK_Neutral }, +/* 0xffc7 XK_F10 */ { TK_NULL, TK_Neutral }, +/* In some versions of XFree86, XK_F11 to XK_F20 are produced for the + shifted F1 to F10 keys, in some XK_F13 to XK_F20 are produced for + the shifted F1 to F8 keys, and in some the keysyms are not altered. + Arrgh. +*/ +#if SHIFT_F1_IS_F11 +/* 0xffc8 XK_F11 */ { TK_F1, TK_Neutral }, +/* 0xffc9 XK_F12 */ { TK_F2, TK_Neutral }, +/* 0xffca XK_F13 */ { TK_F3, TK_Neutral }, +/* 0xffcb XK_F14 */ { TK_CapsLock, TK_Neutral }, +/* 0xffcc XK_F15 */ { TK_AtSign, TK_Neutral }, +/* 0xffcd XK_F16 */ { TK_0, TK_Neutral }, +/* 0xffce XK_F17 */ { TK_NULL, TK_Neutral }, +/* 0xffcf XK_F18 */ { TK_NULL, TK_Neutral }, +#elif SHIFT_F1_IS_F13 +/* 0xffc8 XK_F11 */ { TK_NULL, TK_Neutral }, +/* 0xffc9 XK_F12 */ { TK_NULL, TK_Neutral }, +/* 0xffca XK_F13 */ { TK_F1, TK_Neutral }, +/* 0xffcb XK_F14 */ { TK_F2, TK_Neutral }, +/* 0xffcc XK_F15 */ { TK_F3, TK_Neutral }, +/* 0xffcd XK_F16 */ { TK_CapsLock, TK_Neutral }, +/* 0xffce XK_F17 */ { TK_AtSign, TK_Neutral }, +/* 0xffcf XK_F18 */ { TK_0, TK_Neutral }, +#else +/* 0xffc8 XK_F11 */ { TK_NULL, TK_Neutral }, +/* 0xffc9 XK_F12 */ { TK_NULL, TK_Neutral }, +/* 0xffca XK_F13 */ { TK_NULL, TK_Neutral }, +/* 0xffcb XK_F14 */ { TK_NULL, TK_Neutral }, +/* 0xffcc XK_F15 */ { TK_NULL, TK_Neutral }, +/* 0xffcd XK_F16 */ { TK_NULL, TK_Neutral }, +/* 0xffce XK_F17 */ { TK_NULL, TK_Neutral }, +/* 0xffcf XK_F18 */ { TK_NULL, TK_Neutral }, +#endif +/* 0xffd0 XK_F19 */ { TK_NULL, TK_Neutral }, +/* 0xffd1 XK_F20 */ { TK_NULL, TK_Neutral }, +/* 0xffd2 XK_F21 */ { TK_NULL, TK_Neutral }, +/* 0xffd3 XK_F22 */ { TK_NULL, TK_Neutral }, +/* 0xffd4 XK_F23 */ { TK_NULL, TK_Neutral }, +/* 0xffd5 XK_F24 */ { TK_NULL, TK_Neutral }, +/* 0xffd6 XK_F25 */ { TK_NULL, TK_Neutral }, +/* 0xffd7 XK_F26 */ { TK_NULL, TK_Neutral }, +/* 0xffd8 XK_F27 */ { TK_NULL, TK_Neutral }, +/* 0xffd9 XK_F28 */ { TK_NULL, TK_Neutral }, +/* 0xffda XK_F29 */ { TK_NULL, TK_Neutral }, +/* 0xffdb XK_F30 */ { TK_NULL, TK_Neutral }, +/* 0xffdc XK_F31 */ { TK_NULL, TK_Neutral }, +/* 0xffdd XK_F32 */ { TK_NULL, TK_Neutral }, +/* 0xffde XK_F33 */ { TK_NULL, TK_Neutral }, +/* 0xffdf XK_F34 */ { TK_NULL, TK_Neutral }, +/* 0xffe0 XK_F35 */ { TK_NULL, TK_Neutral }, +/* 0xffe1 XK_Shift_L */ { TK_LeftShift, TK_Neutral }, +/* 0xffe2 XK_Shift_R */ { TK_RightShift, TK_Neutral }, +/* 0xffe3 XK_Control_L */ { TK_Ctrl, TK_Neutral }, +/* 0xffe4 XK_Control_R */ { TK_Ctrl, TK_Neutral }, +/* 0xffe5 XK_Caps_Lock */ { TK_NULL, TK_Neutral }, +/* 0xffe6 XK_Shift_Lock */ { TK_NULL, TK_Neutral }, +/* 0xffe7 XK_Meta_L */ { TK_Clear, TK_Neutral }, +/* 0xffe8 XK_Meta_R */ { TK_Down, TK_ForceShiftPersistent }, +/* 0xffe9 XK_Alt_L */ { TK_Clear, TK_Neutral }, +/* 0xffea XK_Alt_R */ { TK_Down, TK_ForceShiftPersistent }, +/* 0xffeb XK_Super_L */ { TK_NULL, TK_Neutral }, +/* 0xffec XK_Super_R */ { TK_NULL, TK_Neutral }, +/* 0xffed XK_Hyper_L */ { TK_NULL, TK_Neutral }, +/* 0xffee XK_Hyper_R */ { TK_NULL, TK_Neutral }, +/* 0xffef */ { TK_NULL, TK_Neutral }, +/* 0xfff0 */ { TK_NULL, TK_Neutral }, +/* 0xfff1 */ { TK_NULL, TK_Neutral }, +/* 0xfff2 */ { TK_NULL, TK_Neutral }, +/* 0xfff3 */ { TK_NULL, TK_Neutral }, +/* 0xfff4 */ { TK_NULL, TK_Neutral }, +/* 0xfff5 */ { TK_NULL, TK_Neutral }, +/* 0xfff6 */ { TK_NULL, TK_Neutral }, +/* 0xfff7 */ { TK_NULL, TK_Neutral }, +/* 0xfff8 */ { TK_NULL, TK_Neutral }, +/* 0xfff9 */ { TK_NULL, TK_Neutral }, +/* 0xfffa */ { TK_NULL, TK_Neutral }, +/* 0xfffb */ { TK_NULL, TK_Neutral }, +/* 0xfffc */ { TK_NULL, TK_Neutral }, +/* 0xfffd */ { TK_NULL, TK_Neutral }, +/* 0xfffe */ { TK_NULL, TK_Neutral }, +/* 0xffff XK_Delete */ { TK_Left, TK_Neutral } +}; + +static int keystate[8] = { 0, }; +static int force_shift = TK_Neutral; +static int joystate = 0; + +/* Avoid changing state too fast so keystrokes aren't lost. */ +#define STRETCH_AMOUNT 4000 +static tstate_t key_stretch_timeout; +int stretch_amount = STRETCH_AMOUNT; + +void trs_kb_reset() +{ + key_stretch_timeout = z80_state.t_count; +} + +int key_heartbeat = 0; +void trs_kb_heartbeat() +{ + /* Don't hold keys in queue too long */ + key_heartbeat++; +} + +void trs_kb_bracket(int shifted) +{ + /* Set the shift state for the emulation of the "[ {", "\ |", + "] }", "^ ~", and "_ DEL" keys. Some Model 4 keyboard drivers + decode these with [ shifted and { unshifted, etc., while most + other keyboard drivers either ignore them or decode them with + [ unshifted and { shifted. We default to the latter. Note that + these keys didn't exist on real machines anyway. + */ + int i; + for (i=0x5b; i<=0x5f; i++) { + ascii_key_table[i].shift_action = + shifted ? TK_ForceShift : TK_ForceNoShift; + } + for (i=0x7b; i<=0x7f; i++) { + ascii_key_table[i].shift_action = + shifted ? TK_ForceNoShift : TK_ForceShift; + } +} + +/* Emulate joystick with the keypad */ +int trs_emulate_joystick(int key_down, int bit_action) +{ + if (bit_action < TK_Joystick) return 0; + if (key_down) { + joystate |= (bit_action & 0x1f); + } else { + joystate &= ~(bit_action & 0x1f); + } + return 1; +} + +int trs_joystick_in() +{ +#if JOYDEBUG + debug("joy %02x ", joystate); +#endif + return ~joystate; +} + +void trs_xlate_keysym(int keysym) +{ + int key_down; + KeyTable* kt; + static int shift_action = TK_Neutral; + + if (keysym == 0x10000) { + /* force all keys up */ + queue_key(TK_AllKeysUp); + shift_action = TK_Neutral; + return; + } + + key_down = (keysym & 0x10000) == 0; + if (keysym & 0xff00) { + kt = &function_key_table[keysym & 0xff]; + } else { + kt = &ascii_key_table[keysym & 0xff]; + } + if (kt->bit_action == TK_NULL) return; + if (trs_emulate_joystick(key_down, kt->bit_action)) return; + + if (key_down) { + if (shift_action != TK_ForceShiftPersistent && + shift_action != kt->shift_action) { + shift_action = kt->shift_action; + queue_key(shift_action); + } + queue_key(kt->bit_action); + } else { + queue_key(kt->bit_action | 0x10000); + if (shift_action != TK_Neutral && + shift_action == kt->shift_action) { + shift_action = TK_Neutral; + queue_key(shift_action); + } + } +} + +static void change_keystate(int action) +{ + int key_down; + int i; +#ifdef KBDEBUG + debug("change_keystate: action 0x%x\n", action); +#endif + + switch (action) { + case TK_AllKeysUp: + /* force all keys up */ + for (i=0; i<7; i++) { + keystate[i] = 0; + } + force_shift = TK_Neutral; + break; + + case TK_Neutral: + case TK_ForceShift: + case TK_ForceNoShift: + case TK_ForceShiftPersistent: + force_shift = action; + break; + + default: + key_down = TK_DOWN(action); + if (key_down) { + keystate[TK_ADDR(action)] |= (1 << TK_DATA(action)); + } else { + keystate[TK_ADDR(action)] &= ~(1 << TK_DATA(action)); + } + } +} + +static int kb_mem_value(int address) +{ + int i, bitpos, data = 0; + + for (i=0, bitpos=1; i<7; i++, bitpos<<=1) { + if (address & bitpos) { + data |= keystate[i]; + } + } + if (address & 0x80) { + int tmp = keystate[7]; + if (trs_model == 1) { + if (force_shift == TK_ForceNoShift) { + /* deactivate shift key */ + tmp &= ~1; + } else if (force_shift != TK_Neutral) { + /* activate shift key */ + tmp |= 1; + } + } else { + if (force_shift == TK_ForceNoShift) { + /* deactivate both shift keys */ + tmp &= ~3; + } else if (force_shift != TK_Neutral) { + /* if no shift keys are down, activate left shift key */ + if ((tmp & 3) == 0) tmp |= 1; + } + } + data |= tmp; + } + return data; +} + +int trs_kb_mem_read(int address) +{ + int key = -1; + int i, wait; + static int recursion = 0; + static int timesseen; + + /* Prevent endless recursive calls to this routine (by mem_read_word + below) if REG_SP happens to point to keyboard memory. */ + if (recursion) return 0; + + /* Avoid delaying key state changes in queue for too long */ + if (key_heartbeat > 2) { + do { + key = trs_next_key(0); + if (key >= 0) { + change_keystate(key); + timesseen = 1; + } + } while (key >= 0); + } + + /* After each key state change, impose a timeout before the next one + so that the Z-80 program doesn't miss any by polling too rarely, + and so that we don't tickle the bugs in some common TRS-80 keyboard + drivers that strike if two keys change simultaneously */ + if (key_stretch_timeout - z80_state.t_count > TSTATE_T_MID) { + + /* Check if we are in the system keyboard driver, called from + the wait-for-input routine. If so, and there are no + keystrokes queued, and the current state has been seen by + at least 16 such reads, then trs_next_key will pause the + process to avoid burning host CPU needlessly. + + The test below works on both Model I and III and is + insensitive to what keyboard driver is being used, as long + as it is called through the wait-for-key routine at ROM + address 0x0049 and has not pushed too much on the stack yet + when it first reads from the key matrix. The search is + needed (at least) for NEWDOS80, which pushes 2 extra bytes + on the stack. */ + wait = 0; + if (timesseen++ >= 16) { + recursion = 1; + for (i=0; i<=4; i+=2) { + if (mem_read_word(REG_SP + 2 + i) == 0x4015) { + wait = mem_read_word(REG_SP + 10 + i) == 0x004c; + break; + } + } + recursion = 0; + } + /* Get the next key */ + key = trs_next_key(wait); + key_stretch_timeout = z80_state.t_count + stretch_amount; + } + + if (key >= 0) { + change_keystate(key); + timesseen = 1; + } + key_heartbeat = 0; + return kb_mem_value(address); +} + +void clear_key_queue() +{ + key_queue_head = 0; + key_queue_entries = 0; +#if QDEBUG + debug("clear_key_queue\n"); +#endif +} + +void queue_key(int state) +{ + key_queue[(key_queue_head + key_queue_entries) % KEY_QUEUE_SIZE] = state; +#if QDEBUG + debug("queue_key 0x%x\n", state); +#endif + if (key_queue_entries < KEY_QUEUE_SIZE) { + key_queue_entries++; + } else { +#if QDEBUG + debug("queue_key overflow\n"); +#endif + } +} + +int dequeue_key() +{ + int rval = -1; + + if(key_queue_entries > 0) + { + rval = key_queue[key_queue_head]; + key_queue_head = (key_queue_head + 1) % KEY_QUEUE_SIZE; + key_queue_entries--; +#if QDEBUG + debug("dequeue_key 0x%x\n", rval); +#endif + } + return rval; +} + +void +trs_end_kbwait() +{ + key_immediate = 1; +} + +int trs_next_key(int wait) +{ +#if KBWAIT + if (wait) { + int rval; + for (;;) { + if ((rval = dequeue_key()) >= 0) break; + if ((z80_state.nmi && !z80_state.nmi_seen) || + (z80_state.irq && z80_state.iff1) || + trs_event_scheduled() || key_immediate) { + rval = -1; + break; + } + trs_paused = 1; + pause(); /* Wait for SIGALRM or SIGIO */ + key_immediate = 0; + trs_get_event(0); + } + return rval; + } +#endif + return dequeue_key(); + +} diff --git a/trs_memory.c b/trs_memory.c new file mode 100644 index 0000000..5d98e8c --- /dev/null +++ b/trs_memory.c @@ -0,0 +1,535 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Fri Dec 15 15:21:10 PST 2000 by mann +*/ + +/* + * trs_memory.c -- memory emulation functions for the TRS-80 emulator + * + * Routines in this file perform operations such as mem_read and mem_write, + * and are the top level emulation points for memory-mapped devices such + * as the screen and keyboard. + */ + +#include "z80.h" +#include "trs.h" +#include <stdlib.h> +#include "trs_disk.h" +#include "trs_hard.h" + +#define MAX_ROM_SIZE (0x3800) +#define MAX_VIDEO_SIZE (0x0800) + +/* Locations for Model I, Model III, and Model 4 map 0 */ +#define ROM_START (0x0000) +#define IO_START (0x3000) +#define KEYBOARD_START (0x3800) +#define MORE_IO_START (0x3c00) +#define VIDEO_START (0x3c00) +#define RAM_START (0x4000) +#define PRINTER_ADDRESS (0x37E8) + +/* Interrupt latch register in EI (Model 1) */ +#define TRS_INTLATCH(addr) (((addr)&~3) == 0x37e0) + +Uchar memory[0x20001]; /* +1 so strings from mem_pointer are NUL-terminated */ +Uchar *rom; +int trs_rom_size; +Uchar *video; +int trs_video_size; + +int memory_map = 0; +int bank_offset[2]; +#define VIDEO_PAGE_0 0 +#define VIDEO_PAGE_1 1024 +int video_offset = (-VIDEO_START + VIDEO_PAGE_0); +int romin = 0; /* Model 4p */ + +/*SUPPRESS 53*/ +/*SUPPRESS 112*/ + +void mem_video_page(int which) +{ + video_offset = -VIDEO_START + (which ? VIDEO_PAGE_1 : VIDEO_PAGE_0); +} + +void mem_bank(int command) +{ + switch (command) { + case 0: + bank_offset[0] = 0 << 15; + bank_offset[1] = 0 << 15; + break; + case 2: + bank_offset[0] = 0 << 15; + bank_offset[1] = 1 << 15; + break; + case 3: + bank_offset[0] = 0 << 15; + bank_offset[1] = 2 << 15; + break; + case 6: + bank_offset[0] = 2 << 15; + bank_offset[1] = 0 << 15; + break; + case 7: + bank_offset[0] = 3 << 15; + bank_offset[1] = 0 << 15; + break; + default: + error("unknown mem_bank command %d", command); + break; + } +} + +void trs_exit() +{ + exit(0); +} + +/* Handle reset button if hard=0; handle hard reset if hard=1 */ +void trs_reset(int hard) +{ + /* Reset devices (Model I SYSRES, Model III/4 RESET) */ + trs_cassette_reset(); + trs_timer_speed(0); + trs_disk_init(1); + /* I'm told that the hard disk controller is enabled on powerup */ + trs_hard_out(TRS_HARD_CONTROL, + TRS_HARD_SOFTWARE_RESET|TRS_HARD_DEVICE_ENABLE); + if (trs_model == 5) { + /* Switch in boot ROM */ + z80_out(0x9C, 1); + } + if (trs_model >= 4) { + /* Turn off various memory map and video mode bits */ + z80_out(0x84, 0); + } + if (trs_model >= 3) { + grafyx_write_mode(0); + trs_interrupt_mask_write(0); + trs_nmi_mask_write(0); + } + if (trs_model == 3) { + grafyx_m3_reset(); + } + if (trs_model == 1) { + hrg_onoff(0); /* Switch off HRG1B hi-res graphics. */ + } + trs_kb_reset(); /* Part of keyboard stretch kludge */ + + trs_cancel_event(); + if (hard || trs_model >= 4) { + /* Reset processor */ + z80_reset(); + } else { + /* Signal a nonmaskable interrupt. */ + trs_reset_button_interrupt(1); + trs_schedule_event(trs_reset_button_interrupt, 0, 2000); + } +} + +void mem_map(int which) +{ + memory_map = which + (trs_model << 4) + (romin << 2); +} + +void mem_romin(state) +{ + romin = (state & 1); + memory_map = (memory_map & ~4) + (romin << 2); +} + +void mem_init() +{ + if (trs_model <= 3) { + rom = &memory[ROM_START]; + video = &memory[VIDEO_START]; + trs_video_size = 1024; + } else { + /* +1 so strings from mem_pointer are NUL-terminated */ + rom = (Uchar *) calloc(MAX_ROM_SIZE+1, 1); + video = (Uchar *) calloc(MAX_VIDEO_SIZE+1, 1); + trs_video_size = MAX_VIDEO_SIZE; + } + mem_map(0); + mem_bank(0); + mem_video_page(0); + if (trs_model == 5) { + z80_out(0x9C, 1); + } +} + +/* + * hack to let us initialize the ROM memory + */ +void mem_write_rom(int address, int value) +{ + address &= 0xffff; + + rom[address] = value; +} + +/* Called by load_hex */ +void hex_data(int address, int value) +{ + mem_write_rom(address, value); +} + +/* Called by load_hex */ +void hex_transfer_address(int address) +{ + /* Ignore */ +} + +int mem_read(int address) +{ + /*address &= 0xffff; caller must guarantee this now */ + + switch (memory_map) { + case 0x10: /* Model I */ + if (address >= VIDEO_START) return memory[address]; + if (address < trs_rom_size) return memory[address]; + if (address == TRSDISK_DATA) return trs_disk_data_read(); + if (TRS_INTLATCH(address)) return trs_interrupt_latch_read(); + if (address == TRSDISK_STATUS) return trs_disk_status_read(); + if (address == PRINTER_ADDRESS) return trs_printer_read(); + if (address == TRSDISK_TRACK) return trs_disk_track_read(); + if (address == TRSDISK_SECTOR) return trs_disk_sector_read(); + if (address >= KEYBOARD_START) return trs_kb_mem_read(address); + return 0xff; + + case 0x30: /* Model III */ + if (address >= RAM_START) return memory[address]; + if (address == PRINTER_ADDRESS) return trs_printer_read(); + if (address < trs_rom_size) return memory[address]; + if (address >= VIDEO_START) { + return grafyx_m3_read_byte(address - VIDEO_START); + } + if (address >= KEYBOARD_START) return trs_kb_mem_read(address); + return 0xff; + + case 0x40: /* Model 4 map 0 */ + if (address >= RAM_START) { + return memory[address + bank_offset[address>>15]]; + } + if (address == PRINTER_ADDRESS) return trs_printer_read(); + if (address < trs_rom_size) return rom[address]; + if (address >= VIDEO_START) { + return video[address + video_offset]; + } + if (address >= KEYBOARD_START) return trs_kb_mem_read(address); + return 0xff; + + case 0x54: /* Model 4P map 0, boot ROM in */ + case 0x55: /* Model 4P map 1, boot ROM in */ + if (address < trs_rom_size) return rom[address]; + /* else fall thru */ + case 0x41: /* Model 4 map 1 */ + case 0x50: /* Model 4P map 0, boot ROM out */ + case 0x51: /* Model 4P map 1, boot ROM out */ + if (address >= RAM_START || address < KEYBOARD_START) { + return memory[address + bank_offset[address>>15]]; + } + if (address >= VIDEO_START) { + return video[address + video_offset]; + } + if (address >= KEYBOARD_START) return trs_kb_mem_read(address); + return 0xff; + + case 0x42: /* Model 4 map 2 */ + case 0x52: /* Model 4P map 2, boot ROM out */ + case 0x56: /* Model 4P map 2, boot ROM in */ + if (address < 0xf400) { + return memory[address + bank_offset[address>>15]]; + } + if (address >= 0xf800) return video[address-0xf800]; + return trs_kb_mem_read(address); + + case 0x43: /* Model 4 map 3 */ + case 0x53: /* Model 4P map 3, boot ROM out */ + case 0x57: /* Model 4P map 3, boot ROM in */ + return memory[address + bank_offset[address>>15]]; + } + /* not reached */ + return 0xff; +} + +void mem_write(int address, int value) +{ + address &= 0xffff; + + switch (memory_map) { + case 0x10: /* Model I */ + if (address >= RAM_START) { + memory[address] = value; + } else if (address >= VIDEO_START) { + int vaddr = address + video_offset; +#if UPPERCASE + /* + * Video write. Hack here to make up for the missing bit 6 + * video ram, emulating the gate in Z30. + */ + if (trs_model == 1) { + if(value & 0xa0) + value &= 0xbf; + else + value |= 0x40; + } +#endif + if (video[vaddr] != value) { + video[vaddr] = value; + trs_screen_write_char(vaddr, value); + } + } else if (address == PRINTER_ADDRESS) { + trs_printer_write(value); + } else if (address == TRSDISK_DATA) { + trs_disk_data_write(value); + } else if (address == TRSDISK_STATUS) { + trs_disk_command_write(value); + } else if (address == TRSDISK_TRACK) { + trs_disk_track_write(value); + } else if (address == TRSDISK_SECTOR) { + trs_disk_sector_write(value); + } else if (TRSDISK_SELECT(address)) { + trs_disk_select_write(value); + } + break; + + case 0x30: /* Model III */ + if (address >= RAM_START) { + memory[address] = value; + } else if (address >= VIDEO_START) { + int vaddr = address + video_offset; + if (grafyx_m3_write_byte(vaddr, value)) return; + if (video[vaddr] != value) { + video[vaddr] = value; + trs_screen_write_char(vaddr, value); + } + } else if (address == PRINTER_ADDRESS) { + trs_printer_write(value); + } + break; + + case 0x40: /* Model 4 map 0 */ + case 0x50: /* Model 4P map 0, boot ROM out */ + case 0x54: /* Model 4P map 0, boot ROM in */ + if (address >= RAM_START) { + memory[address + bank_offset[address>>15]] = value; + } else if (address >= VIDEO_START) { + int vaddr = address+ video_offset; + if (video[vaddr] != value) { + video[vaddr] = value; + trs_screen_write_char(vaddr, value); + } + } else if (address == PRINTER_ADDRESS) { + trs_printer_write(value); + } + break; + + case 0x41: /* Model 4 map 1 */ + case 0x51: /* Model 4P map 1, boot ROM out */ + case 0x55: /* Model 4P map 1, boot ROM in */ + if (address >= RAM_START || address < KEYBOARD_START) { + memory[address + bank_offset[address>>15]] = value; + } else if (address >= VIDEO_START) { + int vaddr = address + video_offset; + if (video[vaddr] != value) { + video[vaddr] = value; + trs_screen_write_char(vaddr, value); + } + } + break; + + case 0x42: /* Model 4 map 2 */ + case 0x52: /* Model 4P map 2, boot ROM out */ + case 0x56: /* Model 4P map 2, boot ROM in */ + if (address < 0xf400) { + memory[address + bank_offset[address>>15]] = value; + } else if (address >= 0xf800) { + int vaddr = address - 0xf800; + if (video[vaddr] != value) { + video[vaddr] = value; + trs_screen_write_char(vaddr, value); + } + } + break; + + case 0x43: /* Model 4 map 3 */ + case 0x53: /* Model 4P map 3, boot ROM out */ + case 0x57: /* Model 4P map 3, boot ROM in */ + memory[address + bank_offset[address>>15]] = value; + break; + } +} + +/* + * Words are stored with the low-order byte in the lower address. + */ +int mem_read_word(int address) +{ + int rval; + + rval = mem_read(address++); + rval |= mem_read(address & 0xffff) << 8; + return rval; +} + +void mem_write_word(int address, int value) +{ + mem_write(address++, value & 0xff); + mem_write(address, value >> 8); +} + +/* + * Get a pointer to the given address. Note that there is no checking + * whether the next virtual address is physically contiguous. The + * caller is responsible for making sure his strings don't span + * memory map boundaries. + */ +Uchar *mem_pointer(int address, int writing) +{ + address &= 0xffff; + + switch (memory_map + (writing << 3)) { + case 0x10: /* Model I reading */ + case 0x30: /* Model III reading */ + if (address >= VIDEO_START) return &memory[address]; + if (address < trs_rom_size) return &rom[address]; + return NULL; + + case 0x18: /* Model I writing */ + case 0x38: /* Model III writing */ + if (address >= VIDEO_START) return &memory[address]; + return NULL; + + case 0x40: /* Model 4 map 0 reading */ + if (address >= RAM_START) { + return &memory[address + bank_offset[address>>15]]; + } + if (address < trs_rom_size) return &rom[address]; + if (address >= VIDEO_START) { + return &video[address + video_offset]; + } + return NULL; + + case 0x48: /* Model 4 map 0 writing */ + case 0x58: /* Model 4P map 0, boot ROM out, writing */ + case 0x5c: /* Model 4P map 0, boot ROM in, writing */ + if (address >= RAM_START) { + return &memory[address + bank_offset[address>>15]]; + } + if (address >= VIDEO_START) { + return &video[address + video_offset]; + } + return NULL; + + case 0x54: /* Model 4P map 0, boot ROM in, reading */ + case 0x55: /* Model 4P map 1, boot ROM in, reading */ + if (address < trs_rom_size) return &rom[address]; + /* else fall thru */ + case 0x41: /* Model 4 map 1 reading */ + case 0x49: /* Model 4 map 1 writing */ + case 0x50: /* Model 4P map 0, boot ROM out, reading */ + case 0x51: /* Model 4P map 1, boot ROM out, reading */ + case 0x59: /* Model 4P map 1, boot ROM out, writing */ + case 0x5d: /* Model 4P map 1, boot ROM in, writing */ + if (address >= RAM_START || address < KEYBOARD_START) { + return &memory[address + bank_offset[address>>15]]; + } + if (address >= VIDEO_START) { + return &video[address + video_offset]; + } + return NULL; + + case 0x42: /* Model 4 map 1, reading */ + case 0x4a: /* Model 4 map 1, writing */ + case 0x52: /* Model 4P map 2, boot ROM out, reading */ + case 0x5a: /* Model 4P map 2, boot ROM out, writing */ + case 0x56: /* Model 4P map 2, boot ROM in, reading */ + case 0x5e: /* Model 4P map 2, boot ROM in, writing */ + if (address < 0xf400) { + return &memory[address + bank_offset[address>>15]]; + } + if (address >= 0xf800) return &video[address-0xf800]; + return NULL; + + case 0x43: /* Model 4 map 3, reading */ + case 0x4b: /* Model 4 map 3, writing */ + case 0x53: /* Model 4P map 3, boot ROM out, reading */ + case 0x5b: /* Model 4P map 3, boot ROM out, writing */ + case 0x57: /* Model 4P map 3, boot ROM in, reading */ + case 0x5f: /* Model 4P map 3, boot ROM in, writing */ + return &memory[address + bank_offset[address>>15]]; + } + /* not reached */ + return NULL; +} + +/* + * Block move instructions, for LDIR and LDDR instructions. + * + * Direction is either +1 or -1. + * + * Note that a count of zero => move 64K bytes. + * + * These can be special cased to do fun stuff like fast + * video scrolling. + */ +int +mem_block_transfer(Ushort dest, Ushort source, int direction, Ushort count) +{ + int ret; + /* special case for screen scroll */ + if((trs_model <= 3 || (memory_map & 3) < 2) && + (dest == VIDEO_START) && (source == VIDEO_START + 0x40) && + (count == 0x3c0) && (direction > 0) && !grafyx_m3_active()) + { + /* scroll screen one line */ + unsigned char *p = video, *q = video + 0x40; + trs_screen_scroll(); + do { *p++ = ret = *q++; } while (count--); + } + else + { + trs_screen_batch(); + + if(direction > 0) + { + do + { + mem_write(dest++, ret = mem_read(source++)); + count--; + } + while(count); + } + else + { + do + { + mem_write(dest--, ret = mem_read(source--)); + count--; + } + while(count); + } + + trs_screen_unbatch(); + } + return ret; +} + diff --git a/trs_printer.c b/trs_printer.c new file mode 100644 index 0000000..8edd950 --- /dev/null +++ b/trs_printer.c @@ -0,0 +1,34 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +#include "z80.h" +#include "trs.h" + +void trs_printer_write(value) +{ + if(value == 0x0D) + { + putchar('\n'); + } + else + { + putchar(value); + } +} + +int trs_printer_read() +{ + return 0x30; /* printer selected, ready, with paper, not busy */ +} diff --git a/trs_uart.c b/trs_uart.c new file mode 100644 index 0000000..c8ac193 --- /dev/null +++ b/trs_uart.c @@ -0,0 +1,421 @@ +/* Copyright (c) 2000, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Thu May 18 00:42:46 PDT 2000 by mann */ + +/* + * Emulation of the Radio Shack TRS-80 Model I/III/4/4P serial port. + */ + +#include <errno.h> +#include <termios.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <fcntl.h> +#include <string.h> +#include <signal.h> +#include "trs.h" +#include "trs_uart.h" + +#ifndef FNONBLOCK +#define FNONBLOCK O_NONBLOCK +#endif + +#define BUFSIZE 256 +/*#define UARTDEBUG 1*/ +/*#define UARTDEBUG2 1*/ + +#if __linux +char *trs_uart_name = "/dev/ttyS0"; +#else +char *trs_uart_name = "/dev/tty00"; +#endif +int trs_uart_switches = + 0x7 | TRS_UART_NOPAR | TRS_UART_WORD8; /* Default: 9600 8N1 */ + +static int initialized = 0; + +static struct { + int modem; + int switches; + int baud; + int status; + int control; + int idata; + int odata; + + Uchar buf[BUFSIZE]; + Uchar* bufp; + int bufleft; + int tstates; + + int fd; + int fdflags; + struct termios t; +} uart; + +static int trs_uart_wordbits[] = TRS_UART_WORDBITS_TABLE; +static float trs_uart_baud[] = TRS_UART_BAUD_TABLE; + +static int +xlate_baud(int trs_baud) +{ + switch (trs_baud) { + case TRS_UART_50: + return B50; + case TRS_UART_75: + return B75; + case TRS_UART_110: + return B110; + case TRS_UART_134: + return B134; + case TRS_UART_150: + return B150; + case TRS_UART_300: + return B300; + case TRS_UART_600: + return B600; + case TRS_UART_1200: + return B1200; + case TRS_UART_1800: + return B1800; + case TRS_UART_2000: + error("unix does not support 2000 baud, using 38400"); + return B38400; + case TRS_UART_2400: + return B2400; + case TRS_UART_3600: +#ifdef B57600 + error("unix does not support 3600 baud, using 57600"); + return B57600; +#else + error("unix does not support 3600 baud"); + return B0; +#endif + case TRS_UART_4800: + return B4800; + case TRS_UART_7200: +#ifdef B115200 + error("unix does not support 7200 baud, using 115200"); + return B115200; +#else + error("unix does not support 7200 baud"); + return B0; +#endif + case TRS_UART_9600: + return B9600; + case TRS_UART_19200: + return B19200; + } + return B0; /* not reached */ +} + +void +trs_uart_init(int reset_button) +{ + int err; +#if UARTDEBUG + debug("trs_uart_init\n"); +#endif + if (initialized == 1 && uart.fd != -1) close(uart.fd); + if (trs_uart_name == NULL || trs_uart_name[0] == '\000') { + /* Emulate having no serial port */ + initialized = -1; + return; + } + initialized = 1; + uart.fd = open(trs_uart_name, O_RDWR|O_NOCTTY|O_NONBLOCK); + if (uart.fd == -1) { + error("can't open %s: %s", trs_uart_name, strerror(errno)); + } else { + uart.fdflags = FNONBLOCK; +#if HAVE_SIGIO + if (trs_model > 1) { + uart.fdflags |= FASYNC; + fcntl(uart.fd, F_SETOWN, getpid()); /* is this needed? */ + fcntl(uart.fd, F_SETFL, uart.fdflags); + } +#endif + err = tcgetattr(uart.fd, &uart.t); + if (err < 0) { + error("can't get attributes of %s: %s", trs_uart_name, strerror(errno)); + } + } + + uart.t.c_iflag = 0; + uart.t.c_oflag = 0; + uart.t.c_lflag = 0; + memset(uart.t.c_cc, 0, sizeof(uart.t.c_cc)); + + /* Not readable from a user process on unix */ + uart.modem = TRS_UART_CTS | TRS_UART_DSR | TRS_UART_CD; + + uart.switches = (trs_model == 1) ? trs_uart_switches : 0xff; + + /* arbitrary default */ + uart.baud = -1; + trs_uart_baud_out((TRS_UART_9600 << 4) + TRS_UART_9600); + + /* arbitrary default */ + uart.control = -1; + trs_uart_control_out(TRS_UART_NOPAR | TRS_UART_WORD8 | TRS_UART_NOTBREAK | + TRS_UART_DTR | TRS_UART_RTS); + + uart.status = TRS_UART_SENT; + trs_uart_snd_interrupt(1); + + uart.bufp = uart.buf; + uart.bufleft = 0; +} + +int +trs_uart_modem_in() +{ + /* should poll hardware here, if we could */ + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return 0xff; +#if UARTDEBUG2 + debug("trs_uart_modem_in returns 0x%02x\n", uart.modem); +#endif + return uart.modem; +} + +void +trs_uart_reset_out(int value) +{ +#if UARTDEBUG + debug("trs_uart_reset_out\n"); +#endif + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) { + error("serial port emulation is not enabled"); + return; + } +} + +int +trs_uart_switches_in() +{ + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return 0xff; +#if UARTDEBUG + debug("trs_uart_switches_in returns 0x%02x\n", uart.switches); +#endif + return uart.switches; +} + +void +trs_uart_baud_out(int value) +{ + int err; + int bits; +#if UARTDEBUG + debug("trs_uart_baud_out 0x%02x\n", value); +#endif + + if (initialized == 1 && uart.baud == value) return; + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return; + uart.baud = value; + + cfsetispeed(&uart.t, xlate_baud(TRS_UART_RCVBAUD(value))); + cfsetospeed(&uart.t, xlate_baud(TRS_UART_SNDBAUD(value))); + + bits = 1 + trs_uart_wordbits[TRS_UART_WORDBITS(uart.control)] + + ((uart.control & TRS_UART_NOPAR) ? 0 : 1) + + ((uart.control & TRS_UART_STOP2) ? 2 : 1); + uart.tstates = (z80_state.clockMHz * 1000000.0 * bits) + / trs_uart_baud[TRS_UART_SNDBAUD(value)]; +#if UARTDEBUG + debug("total bits %d; tstates per word %d\n", bits, uart.tstates); +#endif + + if (uart.fd != -1) { + err = tcsetattr(uart.fd, TCSADRAIN, &uart.t); + if (err == -1) { + error("can't set attributes of %s: %s", trs_uart_name, strerror(errno)); + } + } +} + +void +trs_uart_set_avail(int dummy) +{ + uart.status |= TRS_UART_RCVD; + trs_uart_rcv_interrupt(1); +} + +void +trs_uart_set_empty(int dummy) +{ + uart.status |= TRS_UART_SENT; + trs_uart_snd_interrupt(1); +} + +int +trs_uart_check_avail() +{ + if (initialized == 1 && uart.bufleft == 0 && uart.fd != -1) { + /* check for data available */ + int rc; + if (!(uart.fdflags & FNONBLOCK)) { +#if UARTDEBUG + debug("trs_uart nonblocking\n"); +#endif + uart.fdflags |= FNONBLOCK; + fcntl(uart.fd, F_SETFL, uart.fdflags); + } + do { + rc = read(uart.fd, uart.buf, BUFSIZE); + } while (rc < 0 && errno == EINTR); +#if UARTDEBUG +#if !UARTDEBUG2 + if (rc >= 0 || errno != EAGAIN) +#endif + debug("trs_uart read returns %d, errno %d\n", rc, errno); +#endif + if (rc < 0) { + if (errno != EAGAIN) { + error("can't read from %s: %s", trs_uart_name, strerror(errno)); + } + rc = 0; + } + uart.bufp = uart.buf; + uart.bufleft = rc; + if (rc > 0) { + /* be sure events don't happen too fast */ + trs_schedule_event(trs_uart_set_avail, 1, uart.tstates); + } + } +#if UARTDEBUG2 + debug("trs_uart_check_avail returns %d\n", uart.bufleft); +#endif + return uart.bufleft; +} + +int +trs_uart_status_in() +{ +#if UARTDEBUG + static int oldstatus = -1; +#endif + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return 0xff; + trs_uart_check_avail(); +#if UARTDEBUG + if (uart.status != oldstatus) { + debug("trs_uart_status_in returns 0x%02x\n", uart.status); + oldstatus = uart.status; + } +#endif + return uart.status; +} + +void +trs_uart_control_out(int value) +{ + int err; + int cflag = HUPCL|CREAD|CLOCAL; +#if UARTDEBUG + debug("trs_uart_control_out 0x%02x\n", value); +#endif + if (initialized == 1 && uart.control == value) return; + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return; + uart.control = value; + if (!(value & TRS_UART_EVENPAR)) cflag |= PARODD; + switch (value & TRS_UART_WORDMASK) { + case TRS_UART_WORD5: + cflag |= CS5; + break; + case TRS_UART_WORD6: + cflag |= CS6; + break; + case TRS_UART_WORD7: + cflag |= CS7; + break; + case TRS_UART_WORD8: + cflag |= CS8; + break; + } + if (value & TRS_UART_STOP2) cflag |= CSTOPB; + if (!(value & TRS_UART_NOPAR)) cflag |= PARENB; + uart.t.c_cflag = cflag; + if (uart.fd != -1) { + err = tcsetattr(uart.fd, TCSADRAIN, &uart.t); + if (err == -1) { + error("can't set attributes of %s: %s", trs_uart_name, strerror(errno)); + } + } + + if (!(value & TRS_UART_NOTBREAK) && uart.fd != -1) { + sigset_t set, oldset; + sigemptyset(&set); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIGIO); + sigprocmask(SIG_BLOCK, &set, &oldset); + err = tcsendbreak(uart.fd, 0); + sigprocmask(SIG_SETMASK, &oldset, NULL); + if (err == -1) { + error("can't send break on %s: %s", trs_uart_name, strerror(errno)); + } + } +} + +int +trs_uart_data_in() +{ + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return 0xff; + trs_uart_check_avail(); + if (uart.status & TRS_UART_RCVD) { + uart.status &= ~TRS_UART_RCVD; + trs_uart_rcv_interrupt(0); + uart.bufleft--; + uart.idata = *uart.bufp++; + if (uart.bufleft) { + trs_schedule_event(trs_uart_set_avail, 1, uart.tstates); + } + } +#if UARTDEBUG + debug("trs_uart_data_in returns 0x%02x\n", uart.idata); +#endif + return uart.idata; +} + +void +trs_uart_data_out(int value) +{ + int err; + +#if UARTDEBUG + debug("trs_uart_data_out 0x%02x\n", value); +#endif + if (initialized == 0) trs_uart_init(0); + if (initialized == -1) return; + uart.odata = value; + if (uart.fd != -1) { + for (;;) { + err = write(uart.fd, &uart.odata, 1); + if (err >= 0) return; + if (errno != EAGAIN) { + error("can't read from %s: %s", trs_uart_name, strerror(errno)); + return; + } + /* Oops, here we didn't really want nonblocking i/o */ +#if UARTDEBUG + debug("trs_uart blocking\n"); +#endif + uart.fdflags &= ~FNONBLOCK; + fcntl(uart.fd, F_SETFL, uart.fdflags); + } + trs_uart_snd_interrupt(0); + trs_schedule_event(trs_uart_set_empty, 1, uart.tstates); + } +} diff --git a/trs_uart.h b/trs_uart.h new file mode 100644 index 0000000..1afc820 --- /dev/null +++ b/trs_uart.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2000, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Thu May 18 00:42:46 PDT 2000 by mann */ + +/* + * Emulation of the Radio Shack TRS-80 Model I/III/4/4P serial port. + */ + +#include <errno.h> +#include "trs.h" +#include "trs_hard.h" + +extern void trs_uart_init(int reset_button); +extern int trs_uart_check_avail(); +extern int trs_uart_modem_in(); +extern void trs_uart_reset_out(int value); +extern int trs_uart_switches_in(); +extern void trs_uart_baud_out(int value); +extern int trs_uart_status_in(); +extern void trs_uart_control_out(int value); +extern int trs_uart_data_in(); +extern void trs_uart_data_out(int value); +extern char *trs_uart_name; +extern int trs_uart_switches; + +#define TRS_UART_MODEM 0xE8 /* in */ +#define TRS_UART_RESET 0xE8 /* out */ +#define TRS_UART_SWITCHES 0xE9 /* in, model I only */ +#define TRS_UART_BAUD 0xE9 /* out */ +#define TRS_UART_STATUS 0xEA /* in */ +#define TRS_UART_CONTROL 0xEA /* out */ +#define TRS_UART_DATA 0xEB /* in/out */ + +/* Bits in TRS_UART_MODEM port */ +#define TRS_UART_CTS 0x80 +#define TRS_UART_DSR 0x40 +#define TRS_UART_CD 0x20 +#define TRS_UART_RI 0x10 +#define TRS_UART_RCVIN 0x02 + +/* Bits in TRS_UART_BAUD port */ +#define TRS_UART_SNDBAUD(v) (((v)&0xf0)>>4) +#define TRS_UART_RCVBAUD(v) (((v)&0x0f)>>0) +#define TRS_UART_50 0x00 +#define TRS_UART_75 0x01 +#define TRS_UART_110 0x02 +#define TRS_UART_134 0x03 +#define TRS_UART_150 0x04 +#define TRS_UART_300 0x05 +#define TRS_UART_600 0x06 +#define TRS_UART_1200 0x07 +#define TRS_UART_1800 0x08 +#define TRS_UART_2000 0x09 +#define TRS_UART_2400 0x0a +#define TRS_UART_3600 0x0b +#define TRS_UART_4800 0x0c +#define TRS_UART_7200 0x0d +#define TRS_UART_9600 0x0e +#define TRS_UART_19200 0x0f +#define TRS_UART_BAUD_TABLE \ + { 50.0, 75.0, 110.0, 134.5, 150.0, 300.0, 600.0, 1200.0, 1800.0, \ + 2000.0, 2400.0, 3600.0, 4800.0, 7200.0, 9600.0, 19200.0 } + +/* Bits in TRS_UART_STATUS port */ +#define TRS_UART_RCVD 0x80 +#define TRS_UART_SENT 0x40 +#define TRS_UART_OVRERR 0x20 +#define TRS_UART_FRMERR 0x10 +#define TRS_UART_PARERR 0x08 + +/* Bits in TRS_UART_CONTROL port */ +#define TRS_UART_EVENPAR 0x80 +#define TRS_UART_WORDMASK 0x60 +#define TRS_UART_WORD5 0x00 +#define TRS_UART_WORD6 0x40 +#define TRS_UART_WORD7 0x20 +#define TRS_UART_WORD8 0x60 +#define TRS_UART_WORDBITS(v) (((v)&TRS_UART_WORDMASK)>>5) +#define TRS_UART_WORDBITS_TABLE { 5, 7, 6, 8 } +#define TRS_UART_STOP2 0x10 +#define TRS_UART_NOPAR 0x08 +#define TRS_UART_NOTBREAK 0x04 +#define TRS_UART_DTR 0x02 /* mislabelled as RTS in Model I manual */ +#define TRS_UART_RTS 0x01 /* mislabelled as DTR in Model I manual */ diff --git a/trs_xinterface.c b/trs_xinterface.c new file mode 100644 index 0000000..f8498e3 --- /dev/null +++ b/trs_xinterface.c @@ -0,0 +1,2035 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Tue May 1 21:39:24 PDT 2001 by mann +*/ + +/*#define MOUSEDEBUG 1*/ +/*#define XDEBUG 1*/ +/*#define QDEBUG 1*/ + +/* + * trs_xinterface.c + * + * X Windows interface for TRS-80 simulator + */ + +#include <stdio.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/file.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> +#include <X11/Xresource.h> + +#include "trs_iodefs.h" +#include "trs.h" +#include "z80.h" +#include "trs_disk.h" +#include "trs_uart.h" +#include "trs_imp_exp.h" + +#define DEF_FONT1 "-misc-fixed-medium-r-normal--20-200-75-75-*-100-iso8859-1" +#define DEF_WIDEFONT1 "-misc-fixed-medium-r-normal--20-200-75-75-*-200-iso8859-1" +#define DEF_FONT3 "-misc-fixed-medium-r-normal--20-200-75-75-*-100-iso8859-1" +#define DEF_WIDEFONT3 "-misc-fixed-medium-r-normal--20-200-75-75-*-200-iso8859-1" +#define DEF_USEFONT 0 + +extern char trs_char_data[][MAXCHARS][TRS_CHAR_HEIGHT]; + +#define EVENT_MASK \ + ExposureMask | KeyPressMask | MapRequest | KeyReleaseMask | \ + StructureNotifyMask | LeaveWindowMask + +#define MAX_SCALE 4 + +/* Private data */ +static unsigned char trs_screen[2048]; +static int screen_chars = 1024; +static int row_chars = 64; +static int col_chars = 16; +static int resize = 0; +static int top_margin = 0; +static int left_margin = 0; +static int border_width = 2; +static Pixmap trs_char[2][MAXCHARS]; +static Pixmap trs_box[2][64]; +static Display *display; +static int screen; +static Window window; +static GC gc; +static GC gc_inv; +static GC gc_xor; +static int currentmode = NORMAL; +static int OrigHeight,OrigWidth; +static int usefont = DEF_USEFONT; +static int cur_char_width = TRS_CHAR_WIDTH; +static int cur_char_height = TRS_CHAR_HEIGHT * 2; +static int text80x24 = 0, screen640x240 = 0; +static XFontStruct *myfont, *mywidefont, *curfont; +static XKeyboardState repeat_state; +static int trs_charset; +static int scale_x = 1; +static int scale_y = 2; + +static XrmOptionDescRec opts[] = { +/* Option */ /* Resource */ /* Value from arg? */ /* Value if no arg */ +{"-iconic", "*iconic", XrmoptionNoArg, (caddr_t)""}, +{"-background", "*background", XrmoptionSepArg, (caddr_t)NULL}, +{"-bg", "*background", XrmoptionSepArg, (caddr_t)NULL}, +{"-foreground", "*foreground", XrmoptionSepArg, (caddr_t)NULL}, +{"-fg", "*foreground", XrmoptionSepArg, (caddr_t)NULL}, +{"-borderwidth","*borderwidth", XrmoptionSepArg, (caddr_t)NULL}, +{"-usefont", "*usefont", XrmoptionNoArg, (caddr_t)"on"}, +{"-nofont", "*usefont", XrmoptionNoArg, (caddr_t)"off"}, +{"-font", "*font", XrmoptionSepArg, (caddr_t)NULL}, +{"-widefont", "*widefont", XrmoptionSepArg, (caddr_t)NULL}, +{"-display", "*display", XrmoptionSepArg, (caddr_t)NULL}, +{"-debug", "*debug", XrmoptionNoArg, (caddr_t)"on"}, +{"-nodebug", "*debug", XrmoptionNoArg, (caddr_t)"off"}, +{"-romfile", "*romfile", XrmoptionSepArg, (caddr_t)NULL}, +{"-romfile3", "*romfile3", XrmoptionSepArg, (caddr_t)NULL}, +{"-romfile4p", "*romfile4p", XrmoptionSepArg, (caddr_t)NULL}, +{"-resize", "*resize", XrmoptionNoArg, (caddr_t)"on"}, +{"-noresize", "*resize", XrmoptionNoArg, (caddr_t)"off"}, +{"-doublestep", "*doublestep", XrmoptionNoArg, (caddr_t)"on"}, +{"-nodoublestep","*doublestep", XrmoptionNoArg, (caddr_t)"off"}, +{"-model", "*model", XrmoptionSepArg, (caddr_t)NULL}, +{"-model1", "*model", XrmoptionNoArg, (caddr_t)"1"}, +{"-model3", "*model", XrmoptionNoArg, (caddr_t)"3"}, +{"-model4", "*model", XrmoptionNoArg, (caddr_t)"4"}, +{"-model4p", "*model", XrmoptionNoArg, (caddr_t)"4p"}, +{"-diskdir", "*diskdir", XrmoptionSepArg, (caddr_t)NULL}, +{"-delay", "*delay", XrmoptionSepArg, (caddr_t)NULL}, +{"-autodelay", "*autodelay", XrmoptionNoArg, (caddr_t)"on"}, +{"-noautodelay","*autodelay", XrmoptionNoArg, (caddr_t)"off"}, +{"-keystretch", "*keystretch", XrmoptionSepArg, (caddr_t)NULL}, +{"-microlabs", "*microlabs", XrmoptionNoArg, (caddr_t)"on"}, +{"-nomicrolabs","*microlabs", XrmoptionNoArg, (caddr_t)"off"}, +{"-doubler", "*doubler", XrmoptionSepArg, (caddr_t)NULL}, +{"-sizemap", "*sizemap", XrmoptionSepArg, (caddr_t)NULL}, +{"-stepmap", "*stepmap", XrmoptionSepArg, (caddr_t)NULL}, +{"-charset", "*charset", XrmoptionSepArg, (caddr_t)NULL}, +{"-truedam", "*truedam", XrmoptionNoArg, (caddr_t)"on"}, +{"-notruedam", "*truedam", XrmoptionNoArg, (caddr_t)"off"}, +{"-samplerate", "*samplerate", XrmoptionSepArg, (caddr_t)NULL}, +{"-title", "*title", XrmoptionSepArg, (caddr_t)NULL}, +{"-scale", "*scale", XrmoptionSepArg, (caddr_t)NULL}, +{"-scale1", "*scale", XrmoptionNoArg, (caddr_t)"1"}, +{"-scale2", "*scale", XrmoptionNoArg, (caddr_t)"2"}, +{"-scale3", "*scale", XrmoptionNoArg, (caddr_t)"3"}, +{"-scale4", "*scale", XrmoptionNoArg, (caddr_t)"4"}, +{"-serial", "*serial", XrmoptionSepArg, (caddr_t)NULL}, +{"-switches", "*switches", XrmoptionSepArg, (caddr_t)NULL}, +{"-shiftbracket","*shiftbracket",XrmoptionNoArg, (caddr_t)"on"}, +{"-noshiftbracket","*shiftbracket",XrmoptionNoArg, (caddr_t)"off"}, +#if __linux +{"-sb", "*sb", XrmoptionSepArg, (caddr_t)NULL}, +#endif /* linux */ +{"-emtsafe", "*emtsafe", XrmoptionNoArg, (caddr_t)"on"}, +{"-noemtsafe", "*emtsafe", XrmoptionNoArg, (caddr_t)"off"}, +}; + +static int num_opts = (sizeof opts / sizeof opts[0]); + +/* Support for Micro Labs Grafyx Solution and Radio Shack hi-res card */ + +/* True size of graphics memory -- some is offscreen */ +#define G_XSIZE 128 +#define G_YSIZE 256 +char grafyx[(2*G_YSIZE*MAX_SCALE) * (G_XSIZE*MAX_SCALE)]; +unsigned char grafyx_unscaled[G_YSIZE][G_XSIZE]; + +unsigned char grafyx_microlabs = 0; +unsigned char grafyx_x = 0, grafyx_y = 0, grafyx_mode = 0; +unsigned char grafyx_enable = 0; +unsigned char grafyx_overlay = 0; +unsigned char grafyx_xoffset = 0, grafyx_yoffset = 0; + +/* Port 0x83 (grafyx_mode) bits */ +#define G_ENABLE 1 +#define G_UL_NOTEXT 2 /* Micro Labs only */ +#define G_RS_WAIT 2 /* Radio Shack only */ +#define G_XDEC 4 +#define G_YDEC 8 +#define G_XNOCLKR 16 +#define G_YNOCLKR 32 +#define G_XNOCLKW 64 +#define G_YNOCLKW 128 + +/* Port 0xFF (grafyx_m3_mode) bits */ +#define G3_COORD 0x80 +#define G3_ENABLE 0x40 +#define G3_COMMAND 0x20 +#define G3_YLOW(v) (((v)&0x1e)>>1) + +XImage image = { + /*width, height*/ 8*G_XSIZE, 2*G_YSIZE, /* if scale_x=1, scale_y=2 */ + /*xoffset*/ 0, + /*format*/ XYBitmap, + /*data*/ (char*)grafyx, + /*byte_order*/ LSBFirst, + /*bitmap_unit*/ 8, + /*bitmap_bit_order*/ MSBFirst, + /*bitmap_pad*/ 8, + /*depth*/ 1, + /*bytes_per_line*/ G_XSIZE, /* if scale = 1 */ + /*bits_per_pixel*/ 1, + /*red_mask*/ 1, + /*green_mask*/ 1, + /*blue_mask*/ 1, + /*obdata*/ NULL, + /*f*/ { NULL, NULL, NULL, NULL, NULL, NULL } +}; + +#define HRG_MEMSIZE (1024 * 12) /* 12k * 8 bit graphics memory */ +static unsigned char hrg_screen[HRG_MEMSIZE]; +static int hrg_pixel_x[2][6+1]; +static int hrg_pixel_y[12+1]; +static int hrg_pixel_width[2][6]; +static int hrg_pixel_height[12]; +static int hrg_enable = 0; +static int hrg_addr = 0; +static void hrg_update_char(int position); + +/* dummy buffer for stat() call */ +struct stat statbuf; + +/* Private routines */ +void bitmap_init(); +void screen_init(); +void trs_event_init(); +void trs_event(); + +static XrmDatabase x_db = NULL; +static XrmDatabase command_db = NULL; +extern char *program_name; +char *title; + +int trs_parse_command_line(int argc, char **argv, int *debug) +{ + char option[512]; + char *type; + XrmValue value; + char *xrms, *tmp; + int stepdefault, i, s[8]; + + title = program_name; /* default */ + + XrmInitialize(); + + /* parse command line options */ + XrmParseCommand(&command_db,opts,num_opts,program_name,&argc,argv); + + (void) sprintf(option, "%s%s", program_name, ".display"); + (void) XrmGetResource(command_db, option, "Xtrs.Display", &type, &value); + /* open display */ + if ( (display = XOpenDisplay (value.addr)) == NULL) { + fprintf(stderr, "Unable to open display."); + exit(-1); + } + + /* get defaults from server */ + xrms = XResourceManagerString(display); + if (xrms != NULL) { + x_db = XrmGetStringDatabase(xrms); + XrmMergeDatabases(command_db,&x_db); + } else { + x_db = command_db; + } + + /* get defaults from $HOME/.Xdefaults and $HOME/Xtrs */ + if ((tmp = getenv("HOME"))) { + sprintf(option, "%s/%s", tmp, ".Xdefaults"); + XrmCombineFileDatabase(option, &x_db, False); + sprintf(option, "%s/%s", tmp, "Xtrs"); + XrmCombineFileDatabase(option, &x_db, False); + } + + /* get defaults from /usr/X11/lib/X11/app-defaults/Xtrs */ +#ifdef APPDEFAULTS + sprintf(option, "%s/%s", APPDEFAULTS, "Xtrs"); + XrmCombineFileDatabase(option, &x_db, False); +#endif + + (void) sprintf(option, "%s%s", program_name, ".scale"); + if (XrmGetResource(x_db, option, "Xtrs.Scale", &type, &value)) + { + if (sscanf(value.addr, "%d,%d", &scale_x, &scale_y) < 2) + scale_y = scale_x * 2; + if (scale_x <= 0) scale_x = 1; + if (scale_x > MAX_SCALE) scale_x = MAX_SCALE; + if (scale_y <= 0) scale_y = 1; + if (scale_y > MAX_SCALE * 2) scale_y = MAX_SCALE * 2; + } + image.width *= scale_x; + image.height = image.height * scale_y / 2; + image.bytes_per_line *= scale_x; + +#if __linux + (void) sprintf(option, "%s%s", program_name, ".sb"); + if (XrmGetResource(x_db, option, "Xtrs.Sb", &type, &value)) { + char *next; int ioport, vol; + ioport = strtol(value.addr, &next, 0); + if(*next == ',') { + next++; + vol=atoi(next); + trs_sound_init(ioport, vol); /* requires root privilege */ + } + } + setuid(getuid()); +#endif /* linux */ + + (void) sprintf(option, "%s%s", program_name, ".emtsafe"); + if (XrmGetResource(x_db, option, "Xtrs.Emtsafe", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + trs_emtsafe = True; + } else if (strcmp(value.addr,"off") == 0) { + trs_emtsafe = False; + } + } + + (void) sprintf(option, "%s%s", program_name, ".debug"); + if (XrmGetResource(x_db, option, "Xtrs.Debug", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + *debug = True; + } else if (strcmp(value.addr,"off") == 0) { + *debug = False; + } + } + + (void) sprintf(option, "%s%s", program_name, ".autodelay"); + if (XrmGetResource(x_db, option, "Xtrs.Autodelay", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + trs_autodelay = True; + } else if (strcmp(value.addr,"off") == 0) { + trs_autodelay = False; + } + } + + (void) sprintf(option, "%s%s", program_name, ".model"); + if (XrmGetResource(x_db, option, "Xtrs.Model", &type, &value)) { + if (strcmp(value.addr, "1") == 0 || + strcasecmp(value.addr, "I") == 0) { + trs_model = 1; + } else if (strcmp(value.addr, "3") == 0 || + strcasecmp(value.addr, "III") == 0) { + trs_model = 3; + } else if (strcmp(value.addr, "4") == 0 || + strcasecmp(value.addr, "IV") == 0) { + trs_model = 4; + } else if (strcasecmp(value.addr, "4P") == 0 || + strcasecmp(value.addr, "IVp") == 0) { + trs_model = 5; + } else { + fatal("TRS-80 Model %s not supported", value.addr); + } + } + + /* !!Note: charset numbers must match trs_chars.c */ + if (trs_model == 1) { + char *charset_name = "wider"; /* default */ + (void) sprintf(option, "%s%s", program_name, ".charset"); + if (XrmGetResource(x_db, option, "Xtrs.Charset", &type, &value)) { + charset_name = (char*) value.addr; + } + if (isdigit(*charset_name)) { + trs_charset = atoi(charset_name); + cur_char_width = 8 * scale_x; + } else { + if (charset_name[0] == 'e'/*early*/) { + trs_charset = 0; + cur_char_width = 6 * scale_x; + } else if (charset_name[0] == 's'/*stock*/) { + trs_charset = 1; + cur_char_width = 6 * scale_x; + } else if (charset_name[0] == 'l'/*lcmod*/) { + trs_charset = 2; + cur_char_width = 6 * scale_x; + } else if (charset_name[0] == 'w'/*wider*/) { + trs_charset = 3; + cur_char_width = 8 * scale_x; + } else if (charset_name[0] == 'g'/*genie or german*/) { + trs_charset = 10; + cur_char_width = 8 * scale_x; + } else { + fatal("unknown charset name %s", value.addr); + } + } + cur_char_height = TRS_CHAR_HEIGHT * scale_y; + } else /* trs_model > 1 */ { + char *charset_name; + /* Set default */ + if (trs_model == 3) { + charset_name = "katakana"; + } else { + charset_name = "international"; + } + (void) sprintf(option, "%s%s", program_name, ".charset"); + if (XrmGetResource(x_db, option, "Xtrs.Charset", &type, &value)) { + charset_name = (char*) value.addr; + } + if (isdigit(*charset_name)) { + trs_charset = atoi(charset_name); + } else { + if (charset_name[0] == 'k'/*katakana*/) { + trs_charset = 4 + 3*(trs_model > 3); + } else if (charset_name[0] == 'i'/*international*/) { + trs_charset = 5 + 3*(trs_model > 3); + } else if (charset_name[0] == 'b'/*bold*/) { + trs_charset = 6 + 3*(trs_model > 3); + } else { + fatal("unknown charset name %s", value.addr); + } + } + cur_char_width = TRS_CHAR_WIDTH * scale_x; + cur_char_height = TRS_CHAR_HEIGHT * scale_y; + } + + (void) sprintf(option, "%s%s", program_name, ".diskdir"); + if (XrmGetResource(x_db, option, "Xtrs.Diskdir", &type, &value)) { + trs_disk_dir = strdup(value.addr); + } + if (trs_disk_dir[0] == '~' && + (trs_disk_dir[1] == '/' || trs_disk_dir[1] == '\0')) { + char* home = getenv("HOME"); + if (home) { + char *p = (char*)malloc(strlen(home) + strlen(trs_disk_dir) + 1); + sprintf(p, "%s/%s", home, trs_disk_dir+1); + trs_disk_dir = p; + } + } + + (void) sprintf(option, "%s%s", program_name, ".delay"); + if (XrmGetResource(x_db, option, "Xtrs.Delay", &type, &value)) { + z80_state.delay = strtol(value.addr, NULL, 0); + } + + (void) sprintf(option, "%s%s", program_name, ".keystretch"); + if (XrmGetResource(x_db, option, "Xtrs.Keystretch", &type, &value)) { + if (strchr(value.addr, ',')) { + fatal("-keystretch now takes only one argument; see man page"); + } + stretch_amount = strtol(value.addr, NULL, 0); + } + + (void) sprintf(option, "%s%s", program_name, ".microlabs"); + if (XrmGetResource(x_db, option, "Xtrs.Microlabs", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + grafyx_set_microlabs(True); + } else if (strcmp(value.addr,"off") == 0) { + grafyx_set_microlabs(False); + } + } + + (void) sprintf(option, "%s%s", program_name, ".doubler"); + if (XrmGetResource(x_db, option, "Xtrs.Doubler", &type, &value)) { + switch (*(char*)value.addr) { + case 'p': + case 'P': + trs_disk_doubler = TRSDISK_PERCOM; + break; + case 'r': + case 'R': + case 't': + case 'T': + trs_disk_doubler = TRSDISK_TANDY; + break; + case 'b': + case 'B': + default: + trs_disk_doubler = TRSDISK_BOTH; + break; + case 'n': + case 'N': + trs_disk_doubler = TRSDISK_NODOUBLER; + break; + } + } + + /* Defaults for sizemap */ + s[0] = 5; + s[1] = 5; + s[2] = 5; + s[3] = 5; + s[4] = 8; + s[5] = 8; + s[6] = 8; + s[7] = 8; + (void) sprintf(option, "%s%s", program_name, ".sizemap"); + if (XrmGetResource(x_db, option, "Xtrs.Sizemap", &type, &value)) { + sscanf((char*)value.addr, "%d,%d,%d,%d,%d,%d,%d,%d", + &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7]); + } + for (i=0; i<=7; i++) { + if (s[i] != 5 && s[i] != 8) { + fatal("bad value %d for disk %d size", s[i], i); + } else { + trs_disk_setsize(i, s[i]); + } + } + + /* Defaults for stepmap */ + (void) sprintf(option, "%s%s", program_name, ".doublestep"); + stepdefault = 1; + if (XrmGetResource(x_db, option, "Xtrs.doublestep", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + stepdefault = 2; + } else if (strcmp(value.addr,"off") == 0) { + stepdefault = 1; + } + } + + for (i=0; i<=7; i++) { + s[i] = stepdefault; + } + (void) sprintf(option, "%s%s", program_name, ".stepmap"); + if (XrmGetResource(x_db, option, "Xtrs.Stepmap", &type, &value)) { + sscanf((char*)value.addr, "%d,%d,%d,%d,%d,%d,%d,%d", + &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7]); + } + for (i=0; i<=7; i++) { + if (s[i] != 1 && s[i] != 2) { + fatal("bad value %d for disk %d single/double step\n", s[i], i); + } else { + trs_disk_setstep(i, s[i]); + } + } + + (void) sprintf(option, "%s%s", program_name, ".truedam"); + if (XrmGetResource(x_db, option, "Xtrs.Truedam", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + trs_disk_truedam = True; + } else if (strcmp(value.addr,"off") == 0) { + trs_disk_truedam = False; + } + } + + (void) sprintf(option, "%s%s", program_name, ".samplerate"); + if (XrmGetResource(x_db, option, "Xtrs.Samplerate", &type, &value)) { + cassette_default_sample_rate = strtol(value.addr, NULL, 0); + } + + (void) sprintf(option, "%s%s", program_name, ".title"); + if (XrmGetResource(x_db, option, "Xtrs.title", &type, &value)) { + title = strdup(value.addr); + } + + (void) sprintf(option, "%s%s", program_name, ".serial"); + if (XrmGetResource(x_db, option, "Xtrs.serial", &type, &value)) { + trs_uart_name = strdup(value.addr); + } + + (void) sprintf(option, "%s%s", program_name, ".switches"); + if (XrmGetResource(x_db, option, "Xtrs.serial", &type, &value)) { + trs_uart_switches = strtol(value.addr, NULL, 0); + } + + (void) sprintf(option, "%s%s", program_name, ".shiftbracket"); + if (XrmGetResource(x_db, option, "Xtrs.Shiftbracket", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + trs_kb_bracket(True); + } else if (strcmp(value.addr,"off") == 0) { + trs_kb_bracket(False); + } + } else { + trs_kb_bracket(trs_model >= 4); + } + + return argc; +} + +static void +save_repeat() +{ + XGetKeyboardControl(display, &repeat_state); + XAutoRepeatOff(display); + XSync(display, False); +} + +static void +restore_repeat() +{ + if (repeat_state.global_auto_repeat == AutoRepeatModeOn) { + XAutoRepeatOn(display); + XSync(display, False); + } +} + +void trs_fix_size (Window window, int width, int height) +{ + XSizeHints sizehints; + + sizehints.flags = PMinSize | PMaxSize | PBaseSize; + sizehints.min_width = width; + sizehints.max_width = width; + sizehints.base_width = width; + + sizehints.min_height = height; + sizehints.max_height = height; + sizehints.base_height = height; + + XSetWMNormalHints(display, window, &sizehints); +} + +int trs_screen_batched = 0; + +void trs_screen_batch() +{ +#if BATCH + /* Defer screen updates until trs_screen_unbatch, then redraw screen + if anything changed. Unfortunately, this seems to slow things + down, so it's disabled. Probably what we should really be doing + is rendering into an offscreen buffer when trs_screen_batched is + set, then copying to the real screen in trs_screen_unbatch. Also + (and orthogonally) we should probably be keeping track of what + part of the screen changed and only redrawing that part. */ + trs_screen_batched = 1; +#endif +} + +void trs_screen_unbatch() +{ +#if BATCH + if (trs_screen_batched > 1) { + trs_screen_batched = 0; + trs_screen_refresh(); + } else { + trs_screen_batched = 0; + } +#endif +} + +/* exits if something really bad happens */ +void trs_screen_init() +{ + Window root_window; + unsigned long fore_pixel, back_pixel, foreground, background; + char option[512]; + char *type; + XrmValue value; + Colormap color_map; + XColor cdef; + XGCValues gcvals; + char *fontname = NULL; + char *widefontname = NULL; + int len; + char *romfile = NULL; + + screen = DefaultScreen(display); + color_map = DefaultColormap(display,screen); + + (void) sprintf(option, "%s%s", program_name, ".foreground"); + if (XrmGetResource(x_db, option, "Xtrs.Foreground", &type, &value)) { + /* debug("foreground is %s\n",value.addr); */ + XParseColor(display, color_map, value.addr, &cdef); + XAllocColor(display, color_map, &cdef); + fore_pixel = cdef.pixel; + } else { + fore_pixel = WhitePixel(display, screen); + } + + (void) sprintf(option, "%s%s", program_name, ".background"); + if (XrmGetResource(x_db, option, "Xtrs.Background", &type, &value)) { + XParseColor(display, color_map, value.addr, &cdef); + XAllocColor(display, color_map, &cdef); + back_pixel = cdef.pixel; + } else { + back_pixel = BlackPixel(display, screen); + } + + (void) sprintf(option, "%s%s", program_name, ".borderwidth"); + if (XrmGetResource(x_db, option, "Xtrs.Borderwidth", &type, &value)) { + if ((border_width = atoi(value.addr)) < 0) + border_width = 0; + } else { + border_width = 2; + } + + (void) sprintf(option, "%s%s", program_name, ".usefont"); + if (XrmGetResource(x_db, option, "Xtrs.Usefont", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + usefont = 1; + } else if (strcmp(value.addr,"off") == 0) { + usefont = 0; + } + } + + if (usefont) { + (void) sprintf(option, "%s%s", program_name, ".font"); + if (XrmGetResource(x_db, option, "Xtrs.Font", &type, &value)) { + len = strlen(value.addr); + fontname = malloc(len + 1); + strcpy(fontname,value.addr); + } else { + char *def_font = (trs_model == 1 ? DEF_FONT1 : DEF_FONT3); + len = strlen(def_font); + fontname = malloc(len+1); + strcpy(fontname, def_font); + } + (void) sprintf(option, "%s%s", program_name, ".widefont"); + if (XrmGetResource(x_db, option, "Xtrs.Widefont", &type, &value)) { + len = strlen(value.addr); + widefontname = malloc(len + 1); + strcpy(widefontname,value.addr); + } else { + char *def_widefont = + (trs_model == 1 ? DEF_WIDEFONT1 : DEF_WIDEFONT3); + len = strlen(def_widefont); + widefontname = malloc(len+1); + strcpy(widefontname, def_widefont); + } + } + + switch (trs_model) { + case 1: + (void) sprintf(option, "%s%s", program_name, ".romfile"); + if (XrmGetResource(x_db, option, "Xtrs.Romfile", &type, &value)) { + if ((stat(value.addr, &statbuf)) == 0) { /* romfile exists */ + romfile = value.addr; + } +#ifdef DEFAULT_ROM + } else if ((stat(DEFAULT_ROM, &statbuf)) == 0) { + romfile = DEFAULT_ROM; +#endif + } + if (romfile != NULL) { + trs_load_rom(romfile); + } else if (trs_rom1_size > 0) { + trs_load_compiled_rom(trs_rom1_size, trs_rom1); + } else { + fatal("ROM file not specified!"); + } + break; + case 3: case 4: + (void) sprintf(option, "%s%s", program_name, ".romfile3"); + if (XrmGetResource(x_db, option, "Xtrs.Romfile3", &type, &value)) { + if ((stat(value.addr, &statbuf)) == 0) { /* romfile exists */ + romfile = value.addr; + } +#ifdef DEFAULT_ROM3 + } else if ((stat(DEFAULT_ROM3, &statbuf)) == 0) { + romfile = DEFAULT_ROM3; +#endif + } + if (romfile != NULL) { + trs_load_rom(romfile); + } else if (trs_rom3_size > 0) { + trs_load_compiled_rom(trs_rom3_size, trs_rom3); + } else { + fatal("ROM file not specified!"); + } + break; + default: /* 4P */ + (void) sprintf(option, "%s%s", program_name, ".romfile4p"); + if (XrmGetResource(x_db, option, "Xtrs.Romfile4p", &type, &value)) { + if ((stat(value.addr, &statbuf)) == 0) { /* romfile exists */ + romfile = value.addr; + } +#ifdef DEFAULT_ROM4P + } else if ((stat(DEFAULT_ROM4P, &statbuf)) == 0) { + romfile = DEFAULT_ROM4P; +#endif + } + if (romfile != NULL) { + trs_load_rom(romfile); + } else if (trs_rom4p_size > 0) { + trs_load_compiled_rom(trs_rom4p_size, trs_rom4p); + } else { + fatal("ROM file not specified!"); + } + break; + } + + (void) sprintf(option, "%s%s", program_name, ".resize"); + if (XrmGetResource(x_db, option, "Xtrs.Resize", &type, &value)) { + if (strcmp(value.addr,"on") == 0) { + resize = 1; + } else if (strcmp(value.addr,"off") == 0) { + resize = 0; + } + } else { + resize = (trs_model == 3); + } + + clear_key_queue(); /* init the key queue */ + + /* setup root window, and gc */ + root_window = DefaultRootWindow(display); + + /* save keyboard repeat state */ + XGetKeyboardControl(display, &repeat_state); + atexit(restore_repeat); + + foreground = fore_pixel; + background = back_pixel; + gcvals.graphics_exposures = False; + + gc = XCreateGC(display, root_window, GCGraphicsExposures, &gcvals); + XSetForeground(display, gc, fore_pixel); + XSetBackground(display, gc, back_pixel); + + gc_inv = XCreateGC(display, root_window, GCGraphicsExposures, &gcvals); + XSetForeground(display, gc_inv, back_pixel); + XSetBackground(display, gc_inv, fore_pixel); + + gc_xor = XCreateGC(display, root_window, GCGraphicsExposures, &gcvals); + XSetForeground(display, gc_xor, back_pixel^fore_pixel); + XSetBackground(display, gc_xor, 0); + XSetFunction(display, gc_xor, GXxor); + + if (usefont) { + if ((myfont = XLoadQueryFont(display,fontname)) == NULL) { + fatal("can't open font %s!\n", fontname); + } + if ((mywidefont = XLoadQueryFont(display,widefontname)) == NULL) { + fatal("can't open font %s!", widefontname); + } + curfont = myfont; + XSetFont(display,gc,myfont->fid); + XSetFont(display,gc_inv,myfont->fid); + cur_char_width = myfont->max_bounds.width; + cur_char_height = myfont->ascent + myfont->descent; + } + + if (trs_model >= 3 && !resize) { + OrigWidth = cur_char_width * 80 + 2 * border_width; + left_margin = cur_char_width * (80 - row_chars)/2 + border_width; + if (usefont) { + OrigHeight = cur_char_height * 24 + 2 * border_width; + top_margin = cur_char_height * (24 - col_chars)/2 + border_width; + } else { + OrigHeight = TRS_CHAR_HEIGHT4 * scale_y * 24 + 2 * border_width; + top_margin = (TRS_CHAR_HEIGHT4 * scale_y * 24 - + cur_char_height * col_chars)/2 + border_width; + } + } else { + OrigWidth = cur_char_width * row_chars + 2 * border_width; + left_margin = border_width; + OrigHeight = cur_char_height * col_chars + 2 * border_width; + top_margin = border_width; + } + window = XCreateSimpleWindow(display, root_window, 400, 400, + OrigWidth, OrigHeight, 1, foreground, + background); +#if XDEBUG + debug("XCreateSimpleWindow(%d, %d)\n", OrigWidth, OrigHeight); +#endif /*XDEBUG*/ + trs_fix_size(window, OrigWidth, OrigHeight); + XStoreName(display,window,title); + XSelectInput(display, window, EVENT_MASK); + + (void) sprintf(option, "%s%s", program_name, ".iconic"); + if (XrmGetResource(x_db, option, "Xtrs.Iconic", &type, &value)) { + XWMHints * hints = XAllocWMHints(); + hints->flags = StateHint; + hints->initial_state = IconicState; + XSetWMHints(display, window, hints); + XFree(hints); + } + + XMapWindow(display, window); + bitmap_init(foreground, background); + screen_init(); + XClearWindow(display,window); + +#if HAVE_SIGIO + trs_event_init(); +#endif +} + +#if HAVE_SIGIO +void trs_event_init() +{ + int fd; + struct sigaction sa; + + /* set up event handler */ + sa.sa_handler = trs_event; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGIO); + sa.sa_flags = SA_RESTART; + sigaction(SIGIO, &sa, NULL); + + fd = ConnectionNumber(display); + if (fcntl(fd, F_SETOWN, getpid()) != 0) { /* is this needed? */ + error("fcntl F_SETOWN error: %s", strerror(errno)); + } + if (fcntl(fd, F_SETFL, FASYNC) != 0) { + error("fcntl F_SETFL async error: %s", strerror(errno)); + } +} + +/* ARGSUSED */ +void trs_event(int signo) +{ + x_poll_count = 0; +} +#endif /*HAVE_SIGIO*/ + +KeySym last_key[256]; + +/* + * Flush output to X server + */ +inline void trs_x_flush() +{ + XFlush(display); +} + +/* + * Get and process X event(s). + * If wait is true, process one event, blocking until one is available. + * If wait is false, process as many events as are available, returning + * when none are left. + * Handle interrupt-driven uart input here too. + */ +void trs_get_event(int wait) +{ + XEvent event; + KeySym key; + char buf[10]; + XComposeStatus status; + + if (trs_model > 1) { + (void)trs_uart_check_avail(); + } + + do { + if (wait) { + XNextEvent(display, &event); + } else { + if (!XCheckMaskEvent(display, ~0, &event)) return; + } + + switch(event.type) { + case Expose: +#if XDEBUG + debug("Expose\n"); +#endif + if (event.xexpose.count == 0) { + while (XCheckMaskEvent(display, ExposureMask, &event)) /*skip*/; + trs_screen_refresh(); + } + break; + + case MapNotify: +#if XDEBUG + debug("MapNotify\n"); +#endif + trs_screen_refresh(); + break; + + case EnterNotify: +#if XDEBUG + debug("EnterNotify\n"); +#endif + save_repeat(); + trs_xlate_keysym(0x10000); /* all keys up */ + break; + + case LeaveNotify: +#if XDEBUG + debug("LeaveNotify\n"); +#endif + restore_repeat(); + trs_xlate_keysym(0x10000); /* all keys up */ + break; + + case KeyPress: + (void) XLookupString((XKeyEvent *)&event,buf,10,&key,&status); +#if XDEBUG + debug("KeyPress: state 0x%x, keycode 0x%x, key 0x%x\n", + event.xkey.state, event.xkey.keycode, (unsigned int) key); +#endif + switch (key) { + /* Trap some function keys here */ + case XK_F10: + trs_reset(0); + key = 0; + trs_end_kbwait(); + break; + case XK_F9: + trs_debug(); + key = 0; + trs_end_kbwait(); + break; + case XK_F8: + trs_exit(); + key = 0; + trs_end_kbwait(); + break; + case XK_F7: + trs_disk_change_all(); + key = 0; + trs_end_kbwait(); + break; + default: + break; + } + if ( ((event.xkey.state & (ShiftMask|LockMask)) + == (ShiftMask|LockMask)) + && key >= 'A' && key <= 'Z' ) { + /* Make Shift + CapsLock give lower case */ + key = (int) key + 0x20; + } + if (key == XK_Shift_R && trs_model == 1) { + key = XK_Shift_L; + } + if (last_key[event.xkey.keycode] != 0) { + trs_xlate_keysym(0x10000 | last_key[event.xkey.keycode]); + } + last_key[event.xkey.keycode] = key; + if (key != 0) { + trs_xlate_keysym(key); + } + break; + + case KeyRelease: + key = last_key[event.xkey.keycode]; + last_key[event.xkey.keycode] = 0; + if (key != 0) { + trs_xlate_keysym(0x10000 | key); + } +#if XDEBUG + debug("KeyRelease: state 0x%x, keycode 0x%x, last_key 0x%x\n", + event.xkey.state, event.xkey.keycode, (unsigned int) key); +#endif + break; + + default: +#if XDEBUG + debug("Unhandled event: type %d\n", event.type); +#endif + break; + } + } while (!wait); +} + +void trs_screen_expanded(int flag) +{ + int bit = flag ? EXPANDED : 0; + if ((currentmode ^ bit) & EXPANDED) { + currentmode ^= EXPANDED; + if (usefont) { + curfont = (flag ? mywidefont : myfont); + XSetFont(display,gc,curfont->fid); + XSetFont(display,gc_inv,curfont->fid); + } + XClearWindow(display,window); + trs_screen_refresh(); + } +} + +void trs_screen_inverse(int flag) +{ + int bit = flag ? INVERSE : 0; + int i; + if ((currentmode ^ bit) & INVERSE) { + currentmode ^= INVERSE; + for (i = 0; i < screen_chars; i++) { + if (trs_screen[i] & 0x80) + trs_screen_write_char(i, trs_screen[i]); + } + } +} + +void trs_screen_alternate(int flag) +{ + int bit = flag ? ALTERNATE : 0; + int i; + if ((currentmode ^ bit) & ALTERNATE) { + currentmode ^= ALTERNATE; + for (i = 0; i < screen_chars; i++) { + if (trs_screen[i] >= 0xc0) + trs_screen_write_char(i, trs_screen[i]); + } + } +} + +void trs_screen_640x240(int flag) +{ + if (flag == screen640x240) return; + screen640x240 = flag; + if (flag) { + row_chars = 80; + col_chars = 24; + if (!usefont) cur_char_height = TRS_CHAR_HEIGHT4 * scale_y; + } else { + row_chars = 64; + col_chars = 16; + if (!usefont) cur_char_height = TRS_CHAR_HEIGHT * scale_y; + } + screen_chars = row_chars * col_chars; + if (resize) { + OrigWidth = cur_char_width * row_chars + 2 * border_width; + OrigHeight = cur_char_height * col_chars + 2 * border_width; + left_margin = border_width; + top_margin = border_width; + trs_fix_size(window, OrigWidth, OrigHeight); + XResizeWindow(display, window, OrigWidth, OrigHeight); + XClearWindow(display,window); + XFlush(display); +#if XDEBUG + debug("XResizeWindow(%d, %d)\n", OrigWidth, OrigHeight); +#endif /*XDEBUG*/ + } else { + left_margin = cur_char_width * (80 - row_chars)/2 + border_width; + if (usefont) { + top_margin = cur_char_height * (24 - col_chars)/2 + border_width; + } else { + top_margin = (TRS_CHAR_HEIGHT4 * scale_y * 24 - + cur_char_height * col_chars)/2 + border_width; + } + if (left_margin > border_width || top_margin > border_width) { + XClearWindow(display,window); + } + } + trs_screen_refresh(); +} + +void trs_screen_80x24(int flag) +{ + if (!grafyx_enable || grafyx_overlay) { + trs_screen_640x240(flag); + } + text80x24 = flag; +} + +void screen_init() +{ + int i; + + /* initially, screen is blank (i.e. full of spaces) */ + for (i = 0; i < sizeof(trs_screen); i++) + trs_screen[i] = ' '; +} + +void +boxes_init(int foreground, int background, int width, int height, int expanded) +{ + int graphics_char, bit, p; + XRectangle bits[6]; + XRectangle cur_bits[6]; + + /* + * Calculate what the 2x3 boxes look like. + */ + bits[0].x = bits[2].x = bits[4].x = 0; + bits[0].width = bits[2].width = bits[4].width = + bits[1].x = bits[3].x = bits[5].x = width / 2; + bits[1].width = bits[3].width = bits[5].width = width - bits[1].x; + + bits[0].y = bits[1].y = 0; + bits[0].height = bits[1].height = + bits[2].y = bits[3].y = height / 3; + bits[4].y = bits[5].y = (height * 2) / 3; + bits[2].height = bits[3].height = bits[4].y - bits[2].y; + bits[4].height = bits[5].height = height - bits[4].y; + + for (graphics_char = 0; graphics_char < 64; ++graphics_char) { + trs_box[expanded][graphics_char] = + XCreatePixmap(display, window, width, height, + DefaultDepth(display, screen)); + + /* Clear everything */ + XSetForeground(display, gc, background); + XFillRectangle(display, trs_box[expanded][graphics_char], + gc, 0, 0, width, height); + + /* Set the bits */ + XSetForeground(display, gc, foreground); + + for (bit = 0, p = 0; bit < 6; ++bit) { + if (graphics_char & (1 << bit)) { + cur_bits[p++] = bits[bit]; + } + } + XFillRectangles(display, trs_box[expanded][graphics_char], + gc, cur_bits, p); + } +} + +/* DPL 20000129 + * This routine creates a rescaled charater bitmap, and then + * calls XCreateBitmapFromData. It then can be used pretty much + * as a drop-in replacement for XCreateBitmapFromData. + */ +Pixmap XCreateBitmapFromDataScale(Display *display, Drawable window, + char *data, unsigned int width, + unsigned int height, + unsigned int scale_x, + unsigned int scale_y) +{ + static unsigned char *mydata; + static unsigned char *mypixels; + int i, j, k; + + if (mydata == NULL) + { + /* + * Allocate a bit more room than necessary - There shouldn't be + * any proportional characters, but just in case... + * These arrays never get released, but they are really not + * too big, so we should be OK. + */ + mydata = (unsigned char *)malloc(width * height * + scale_x * scale_y * 4); + mypixels= (unsigned char *)malloc(width * height * 4); + } + + /* Read the character data */ + for (j= 0; j< width * height; j += 8) + { + for (i= j + 7; i >= j; i--) + { + *(mypixels + i)= (*(data + (j >> 3)) >> (i - j)) & 1; + } + } + + /* Clear out the rescaled character array */ + for (i= 0; i< width / 8 * height * scale_x * scale_y * 4; i++) + { + *(mydata + i)= 0; + } + + /* And prepare our rescaled character. */ + k= 0; + for (j= 0; j< height * scale_y; j++) + { + for (i= 0; i< width * scale_x; i++) + { + *(mydata + (k >> 3)) = *(mydata + (k >> 3)) | + (*(mypixels + ((int)(j / scale_y) * width) + + (int)(i / scale_x)) << (k & 7)); + k++; + } + } + + return XCreateBitmapFromData(display,window, + (char*)mydata, + width * scale_x, height * scale_y); +} + +void bitmap_init(unsigned long foreground, unsigned long background) +{ + if (usefont) { + int dwidth, dheight; + + boxes_init(foreground, background, + cur_char_width, cur_char_height, 0); + dwidth = 2*cur_char_width; + dheight = 2*cur_char_height; + if (dwidth > mywidefont->max_bounds.width) { + dwidth = mywidefont->max_bounds.width; + } + if (dheight > mywidefont->ascent + mywidefont->descent) { + dheight = mywidefont->ascent + mywidefont->descent; + } + boxes_init(foreground, background, dwidth, dheight, 1); + } else { + /* Initialize from built-in font bitmaps. */ + int i; + + for (i = 0; i < MAXCHARS; i++) { + trs_char[0][i] = + XCreateBitmapFromDataScale(display,window, + trs_char_data[trs_charset][i], + TRS_CHAR_WIDTH,TRS_CHAR_HEIGHT, + scale_x,scale_y); + trs_char[1][i] = + XCreateBitmapFromDataScale(display,window, + trs_char_data[trs_charset][i], + TRS_CHAR_WIDTH,TRS_CHAR_HEIGHT, + scale_x*2,scale_y); + } + boxes_init(foreground, background, + cur_char_width, TRS_CHAR_HEIGHT * scale_y, 0); + boxes_init(foreground, background, + cur_char_width*2, TRS_CHAR_HEIGHT * scale_y, 1); + } +} + +void trs_screen_refresh() +{ + int i, srcx, srcy, dunx, duny; + + if (trs_screen_batched) { + trs_screen_batched++; + return; + } + +#if XDEBUG + debug("trs_screen_refresh\n"); +#endif + if (grafyx_enable && !grafyx_overlay) { + srcx = cur_char_width * grafyx_xoffset; + srcy = scale_y * grafyx_yoffset; + XPutImage(display, window, gc, &image, + srcx, srcy, + left_margin, top_margin, + cur_char_width*row_chars, + cur_char_height*col_chars); + /* Draw wrapped portions if any */ + dunx = image.width - srcx; + if (dunx < cur_char_width*row_chars) { + XPutImage(display, window, gc, &image, + 0, srcy, + left_margin + dunx, top_margin, + cur_char_width*row_chars - dunx, + cur_char_height*col_chars); + } + duny = image.height - srcy; + if (duny < cur_char_height*col_chars) { + XPutImage(display, window, gc, &image, + srcx, 0, + left_margin, top_margin + duny, + cur_char_width*row_chars, + cur_char_height*col_chars - duny); + if (dunx < cur_char_width*row_chars) { + XPutImage(display, window, gc, &image, + 0, 0, + left_margin + dunx, top_margin + duny, + cur_char_width*row_chars - dunx, + cur_char_height*col_chars - duny); + } + } + } else { + for (i = 0; i < screen_chars; i++) { + trs_screen_write_char(i, trs_screen[i]); + } + } +} + +void trs_screen_write_char(int position, int char_index) +{ + int row,col,destx,desty; + int plane; + char temp_char; + + trs_screen[position] = char_index; + if (position >= screen_chars) { + return; + } + if ((currentmode & EXPANDED) && (position & 1)) { + return; + } + if (grafyx_enable && !grafyx_overlay) { + return; + } + if (trs_screen_batched) { + trs_screen_batched++; + return; + } + row = position / row_chars; + col = position - (row * row_chars); + destx = col * cur_char_width + left_margin; + desty = row * cur_char_height + top_margin; + + if (trs_model == 1 && char_index >= 0xc0) { + /* On Model I, 0xc0-0xff is another copy of 0x80-0xbf */ + char_index -= 0x40; + } + if (char_index >= 0x80 && char_index <= 0xbf && !(currentmode & INVERSE)) { + /* Use graphics character bitmap instead of font */ + switch (currentmode & EXPANDED) { + case NORMAL: + XCopyArea(display, + trs_box[0][char_index-0x80],window,gc,0,0, + cur_char_width,cur_char_height,destx,desty); + break; + case EXPANDED: + /* use expanded graphics character bitmap instead of font */ + XCopyArea(display, + trs_box[1][char_index-0x80],window,gc,0,0, + cur_char_width*2,cur_char_height,destx,desty); + break; + } + } else if (usefont) { + /* Draw character using a font */ + if (trs_model == 1) { +#if !UPPERCASE + /* Emulate Radio Shack lowercase mod. The replacement character + generator ROM had another copy of the uppercase characters in + the control character positions, to compensate for a bug in the + Level II ROM that stores such values instead of uppercase. */ + if (char_index < 0x20) char_index += 0x40; +#endif + } + if (trs_model > 1 && char_index >= 0xc0 && + (currentmode & (ALTERNATE+INVERSE)) == 0) { + char_index -= 0x40; + } + desty += curfont->ascent; + if (currentmode & INVERSE) { + temp_char = (char)(char_index & 0x7f); + XDrawImageString(display, window, + (char_index & 0x80) ? gc_inv : gc, + destx, desty, &temp_char, 1); + } else { + temp_char = (char)char_index; + XDrawImageString(display, window, gc, + destx, desty, &temp_char, 1); + } + } else { + /* Draw character using a builtin bitmap */ + if (trs_model > 1 && char_index >= 0xc0 && + (currentmode & (ALTERNATE+INVERSE)) == 0) { + char_index -= 0x40; + } + plane = 1; + switch (currentmode & ~ALTERNATE) { + case NORMAL: + XCopyPlane(display,trs_char[0][char_index], + window,gc,0,0,cur_char_width, + cur_char_height,destx,desty,plane); + break; + case EXPANDED: + XCopyPlane(display,trs_char[1][char_index], + window,gc,0,0,cur_char_width*2, + cur_char_height,destx,desty,plane); + break; + case INVERSE: + XCopyPlane(display,trs_char[0][char_index & 0x7f],window, + (char_index & 0x80) ? gc_inv : gc, + 0,0,cur_char_width,cur_char_height,destx,desty,plane); + break; + case EXPANDED+INVERSE: + XCopyPlane(display,trs_char[1][char_index & 0x7f],window, + (char_index & 0x80) ? gc_inv : gc, + 0,0,cur_char_width*2,cur_char_height,destx,desty,plane); + break; + } + } + if (grafyx_enable) { + /* assert(grafyx_overlay); */ + int srcx, srcy, duny; + srcx = ((col+grafyx_xoffset) % G_XSIZE)*cur_char_width; + srcy = (row*cur_char_height + grafyx_yoffset*scale_y) + % (G_YSIZE*scale_y); + XPutImage(display, window, gc_xor, &image, srcx, srcy, + destx, desty, cur_char_width, cur_char_height); + /* Draw wrapped portion if any */ + duny = image.height - srcy; + if (duny < cur_char_height) { + XPutImage(display, window, gc_xor, &image, + srcx, 0, + destx, desty + duny, + cur_char_width, cur_char_height - duny); + } + } + if (hrg_enable) { + hrg_update_char(position); + } +} + + /* Copy lines 1 through col_chars-1 to lines 0 through col_chars-2. + Doesn't need to clear line col_chars-1. */ +void trs_screen_scroll() +{ + int i = 0; + + for (i = row_chars; i < screen_chars; i++) + trs_screen[i-row_chars] = trs_screen[i]; + + if (trs_screen_batched) { + trs_screen_batched++; + return; + } + if (grafyx_enable) { + if (grafyx_overlay) { + trs_screen_refresh(); + } + } else if (hrg_enable) { + trs_screen_refresh(); + } else { + XCopyArea(display,window,window,gc, + left_margin,cur_char_height+top_margin, + (cur_char_width*row_chars),(cur_char_height*col_chars), + left_margin,top_margin); + } +} + +void grafyx_write_byte(int x, int y, char byte) +{ + int i, j; + char exp[MAX_SCALE]; + int screen_x = ((x - grafyx_xoffset + G_XSIZE) % G_XSIZE); + int screen_y = ((y - grafyx_yoffset + G_YSIZE) % G_YSIZE); + int on_screen = screen_x < row_chars && + screen_y < col_chars*cur_char_height/scale_y; + + if (grafyx_enable && grafyx_overlay && on_screen) { + if (trs_screen_batched) { + trs_screen_batched++; + } else { + /* Erase old byte, preserving text */ + XPutImage(display, window, gc_xor, &image, + x*cur_char_width, y*scale_y, + left_margin + screen_x*cur_char_width, + top_margin + screen_y*scale_y, + cur_char_width, scale_y); + } + } + + /* Save new byte in local memory */ + grafyx_unscaled[y][x] = byte; + switch (scale_x) { + case 1: + exp[0] = byte; + break; + case 2: + exp[1] = ((byte & 0x01) + ((byte & 0x02) << 1) + + ((byte & 0x04) << 2) + ((byte & 0x08) << 3)) * 3; + exp[0] = (((byte & 0x10) >> 4) + ((byte & 0x20) >> 3) + + ((byte & 0x40) >> 2) + ((byte & 0x80) >> 1)) * 3; + break; + case 3: + exp[2] = ((byte & 0x01) + ((byte & 0x02) << 2) + + ((byte & 0x04) << 4)) * 7; + exp[1] = (((byte & 0x08) >> 2) + (byte & 0x10) + + ((byte & 0x20) << 2)) * 7 + ((byte & 0x04) >> 2); + exp[0] = (((byte & 0x40) >> 4) + ((byte & 0x80) >> 2)) * 7 + + ((byte & 0x20) >> 5) * 3; + break; + case 4: + exp[3] = ((byte & 0x01) + ((byte & 0x02) << 3)) * 15; + exp[2] = (((byte & 0x04) >> 2) + ((byte & 0x08) << 1)) * 15; + exp[1] = (((byte & 0x10) >> 4) + ((byte & 0x20) >> 1)) * 15; + exp[0] = (((byte & 0x40) >> 6) + ((byte & 0x80) >> 3)) * 15; + break; + default: + fatal("scaling graphics by %dx not implemented\n", scale_x); + } + for (j=0; j<scale_y; j++) { + for (i=0; i<scale_x; i++) { + grafyx[(y*scale_y + j)*image.bytes_per_line + x*scale_x + i] = exp[i]; + } + } + + if (grafyx_enable && on_screen) { + if (trs_screen_batched) { + trs_screen_batched++; + } else { + /* Draw new byte */ + if (grafyx_overlay) { + XPutImage(display, window, gc_xor, &image, + x*cur_char_width, y*scale_y, + left_margin + screen_x*cur_char_width, + top_margin + screen_y*scale_y, + cur_char_width, scale_y); + } else { + XPutImage(display, window, gc, &image, + x*cur_char_width, y*scale_y, + left_margin + screen_x*cur_char_width, + top_margin + screen_y*scale_y, + cur_char_width, scale_y); + } + } + } +} + +void grafyx_write_x(int value) +{ + grafyx_x = value; +} + +void grafyx_write_y(int value) +{ + grafyx_y = value; +} + +void grafyx_write_data(int value) +{ + grafyx_write_byte(grafyx_x % G_XSIZE, grafyx_y, value); + if (!(grafyx_mode & G_XNOCLKW)) { + if (grafyx_mode & G_XDEC) { + grafyx_x--; + } else { + grafyx_x++; + } + } + if (!(grafyx_mode & G_YNOCLKW)) { + if (grafyx_mode & G_YDEC) { + grafyx_y--; + } else { + grafyx_y++; + } + } +} + +int grafyx_read_data() +{ + int value = grafyx_unscaled[grafyx_y][grafyx_x % G_XSIZE]; + if (!(grafyx_mode & G_XNOCLKR)) { + if (grafyx_mode & G_XDEC) { + grafyx_x--; + } else { + grafyx_x++; + } + } + if (!(grafyx_mode & G_YNOCLKR)) { + if (grafyx_mode & G_YDEC) { + grafyx_y--; + } else { + grafyx_y++; + } + } + return value; +} + +void grafyx_write_mode(int value) +{ + unsigned char old_enable = grafyx_enable; + unsigned char old_overlay = grafyx_overlay; + + grafyx_enable = value & G_ENABLE; + if (grafyx_microlabs) { + grafyx_overlay = (value & G_UL_NOTEXT) == 0; + } + grafyx_mode = value; + trs_screen_640x240((grafyx_enable && !grafyx_overlay) || text80x24); + if (old_enable != grafyx_enable || + (grafyx_enable && old_overlay != grafyx_overlay)) { + + trs_screen_refresh(); + } +} + +void grafyx_write_xoffset(int value) +{ + unsigned char old_xoffset = grafyx_xoffset; + grafyx_xoffset = value % G_XSIZE; + if (grafyx_enable && old_xoffset != grafyx_xoffset) { + trs_screen_refresh(); + } +} + +void grafyx_write_yoffset(int value) +{ + unsigned char old_yoffset = grafyx_yoffset; + grafyx_yoffset = value; + if (grafyx_enable && old_yoffset != grafyx_yoffset) { + trs_screen_refresh(); + } +} + +void grafyx_write_overlay(int value) +{ + unsigned char old_overlay = grafyx_overlay; + grafyx_overlay = value & 1; + if (grafyx_enable && old_overlay != grafyx_overlay) { + trs_screen_640x240((grafyx_enable && !grafyx_overlay) || text80x24); + trs_screen_refresh(); + } +} + +int grafyx_get_microlabs() +{ + return grafyx_microlabs; +} + +void grafyx_set_microlabs(int on_off) +{ + grafyx_microlabs = on_off; +} + +/* Model III MicroLabs support */ +void grafyx_m3_reset() +{ + if (grafyx_microlabs) grafyx_m3_write_mode(0); +} + +void grafyx_m3_write_mode(int value) +{ + int enable = (value & G3_ENABLE) != 0; + int changed = (enable != grafyx_enable); + grafyx_enable = enable; + grafyx_overlay = enable; + grafyx_mode = value; + grafyx_y = G3_YLOW(value); + if (changed) trs_screen_refresh(); +} + +int grafyx_m3_write_byte(int position, int byte) +{ + if (grafyx_microlabs && (grafyx_mode & G3_COORD)) { + int x = (position % 64); + int y = (position / 64) * 12 + grafyx_y; + grafyx_write_byte(x, y, byte); + return 1; + } else { + return 0; + } +} + +unsigned char grafyx_m3_read_byte(int position) +{ + if (grafyx_microlabs && (grafyx_mode & G3_COORD)) { + int x = (position % 64); + int y = (position / 64) * 12 + grafyx_y; + return grafyx_unscaled[y][x]; + } else { + return trs_screen[position]; + } +} + +int grafyx_m3_active() +{ + return (trs_model == 3 && grafyx_microlabs && (grafyx_mode & G3_COORD)); +} + +/* + * Support for Model I HRG1B 384*192 graphics card + * (sold in Germany for Model I and Video Genie by RB-Elektronik). + * + * Assignment of ports is as follows: + * Port 0x00 (out): switch graphics screen off (value ignored). + * Port 0x01 (out): switch graphics screen on (value ignored). + * Port 0x02 (out): select screen memory address (LSB). + * Port 0x03 (out): select screen memory address (MSB). + * Port 0x04 (in): read byte from screen memory. + * Port 0x05 (out): write byte to screen memory. + * (The real hardware decodes only address lines A0-A2 and A7, so + * that there are several "shadow" ports in the region 0x08-0x7d. + * However, these undocumented ports are not implemented here.) + * + * The 16-bit memory address (port 2 and 3) is used for subsequent + * read or write operations. It corresponds to a position on the + * graphics screen, in the following way: + * Bits 0-5: character column address (0-63) + * Bits 6-9: character row address (0-15) + * (i.e. bits 0-9 are the "PRINT @" position.) + * Bits 10-13: address of line within character cell (0-11) + * Bits 14-15: not used + * + * <----port 2 (LSB)----> <-------port 3 (MSB)-------> + * Bit: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * <-column addr.-> <row addr> <-line addr.-> <n.u.> + * + * Reading from port 4 or writing to port 5 will access six + * neighbouring pixels corresponding (from left to right) to bits + * 0-5 of the data byte. Bits 6 and 7 are present in memory, but + * are ignored. + * + * In expanded mode (32 chars per line), the graphics screen has + * only 192*192 pixels. Pixels with an odd column address (i.e. + * every second group of 6 pixels) are suppressed. + */ + +/* Initialize HRG. */ +static void +hrg_init() +{ + int i; + + /* Precompute arrays of pixel sizes and offsets. */ + for (i = 0; i <= 6; i++) { + hrg_pixel_x[0][i] = cur_char_width * i / 6; + hrg_pixel_x[1][i] = cur_char_width*2 * i / 6; + if (i != 0) { + hrg_pixel_width[0][i-1] = hrg_pixel_x[0][i] - hrg_pixel_x[0][i-1]; + hrg_pixel_width[1][i-1] = hrg_pixel_x[1][i] - hrg_pixel_x[1][i-1]; + } + } + for (i = 0; i <= 12; i++) { + hrg_pixel_y[i] = cur_char_height * i / 12; + if (i != 0) + hrg_pixel_height[i-1] = hrg_pixel_y[i] - hrg_pixel_y[i-1]; + } + if (cur_char_width % 6 != 0 || cur_char_height % 12 != 0) + error("character size %d*%d not a multiple of 6*12 HRG raster", + cur_char_width, cur_char_height); +} + +/* Switch HRG on (1) or off (0). */ +void +hrg_onoff(int enable) +{ + static int init = 0; + + if ((hrg_enable!=0) == (enable!=0)) return; /* State does not change. */ + + if (!init) { + hrg_init(); + init = 1; + } + hrg_enable = enable; + trs_screen_refresh(); +} + +/* Write address to latch. */ +void +hrg_write_addr(int addr, int mask) +{ + hrg_addr = (hrg_addr & ~mask) | (addr & mask); +} + +/* Write byte to HRG memory. */ +void +hrg_write_data(int data) +{ + int old_data; + int position, line; + int bits0, bits1; + + if (hrg_addr >= HRG_MEMSIZE) return; /* nonexistent address */ + old_data = hrg_screen[hrg_addr]; + hrg_screen[hrg_addr] = data; + + if (!hrg_enable) return; + if ((currentmode & EXPANDED) && (hrg_addr & 1)) return; + if ((data &= 0x3f) == (old_data &= 0x3f)) return; + + if (trs_screen_batched) { + trs_screen_batched++; + return; + } + + position = hrg_addr & 0x3ff; /* bits 0-9: "PRINT @" screen position */ + line = hrg_addr >> 10; /* vertical offset inside character cell */ + bits0 = ~data & old_data; /* pattern to clear */ + bits1 = data & ~old_data; /* pattern to set */ + + if (bits0 == 0 + || trs_screen[position] == 0x20 + || trs_screen[position] == 0x80 + /*|| (trs_screen[position] < 0x80 && line >= 8 && !usefont)*/ + ) { + /* Only additional bits set, or blank text character. + No need for update of text. */ + int destx = (position % row_chars) * cur_char_width + left_margin; + int desty = (position / row_chars) * cur_char_height + top_margin + + hrg_pixel_y[line]; + int *x = hrg_pixel_x[(currentmode&EXPANDED)!=0]; + int *w = hrg_pixel_width[(currentmode&EXPANDED)!=0]; + int h = hrg_pixel_height[line]; + XRectangle rect0[3]; /* 6 bits => max. 3 groups of adjacent "0" bits */ + XRectangle rect1[3]; + int n0 = 0; + int n1 = 0; + int flag = 0; + int j, b; + + /* Compute arrays of rectangles to clear and to set. */ + for (j = 0, b = 1; j < 6; j++, b <<= 1) { + if (bits0 & b) { + if (flag >= 0) { /* Start new rectangle. */ + rect0[n0].x = destx + x[j]; + rect0[n0].y = desty; + rect0[n0].width = w[j]; + rect0[n0].height = h; + n0++; + flag = -1; + } + else { /* Increase width of rectangle. */ + rect0[n0-1].width += w[j]; + } + } + else if (bits1 & b) { + if (flag <= 0) { + rect1[n1].x = destx + x[j]; + rect1[n1].y = desty; + rect1[n1].width = w[j]; + rect1[n1].height = h; + n1++; + flag = 1; + } + else { + rect1[n1-1].width += w[j]; + } + } + else { + flag = 0; + } + } + if (n0 != 0) XFillRectangles(display, window, gc_inv, rect0, n0); + if (n1 != 0) XFillRectangles(display, window, gc, rect1, n1); + } + else { + /* Unfortunately, HRG1B combines text and graphics with an + (inclusive) OR. Thus, in the general case, we cannot erase + the old graphics byte without losing the text information. + Call trs_screen_write_char to restore the text character + (erasing the graphics). This function will in turn call + hrg_update_char and restore 6*12 graphics pixels. Sigh. */ + trs_screen_write_char(position, trs_screen[position]); + } +} + +/* Read byte from HRG memory. */ +int +hrg_read_data() +{ + if (hrg_addr >= HRG_MEMSIZE) return 0xff; /* nonexistent address */ + return hrg_screen[hrg_addr]; +} + +/* Update graphics at given screen position. + Called by trs_screen_write_char. */ +static void +hrg_update_char(int position) +{ + int destx = (position % row_chars) * cur_char_width + left_margin; + int desty = (position / row_chars) * cur_char_height + top_margin; + int *x = hrg_pixel_x[(currentmode&EXPANDED)!=0]; + int *w = hrg_pixel_width[(currentmode&EXPANDED)!=0]; + XRectangle rect[3*12]; + int byte; + int prev_byte = 0; + int n = 0; + int np = 0; + int i, j, flag; + + /* Compute array of rectangles. */ + for (i = 0; i < 12; i++) { + if ((byte = hrg_screen[position+(i<<10)] & 0x3f) == 0) { + } + else if (byte != prev_byte) { + np = n; + flag = 0; + for (j = 0; j < 6; j++) { + if (!(byte & 1<<j)) { + flag = 0; + } + else if (!flag) { /* New rectangle. */ + rect[n].x = destx + x[j]; + rect[n].y = desty + hrg_pixel_y[i]; + rect[n].width = w[j]; + rect[n].height = hrg_pixel_height[i]; + n++; + flag = 1; + } + else { /* Increase width. */ + rect[n-1].width += w[j]; + } + } + } + else { /* Increase heights. */ + for (j = np; j < n; j++) + rect[j].height += hrg_pixel_height[i]; + } + prev_byte = byte; + } + if (n != 0) + XFillRectangles(display, window, gc, rect, n); +} + + +/*---------- X mouse support --------------*/ + +int mouse_x_size = 640, mouse_y_size = 240; +int mouse_sens = 3; +int mouse_last_x = -1, mouse_last_y = -1; +unsigned int mouse_last_buttons; +int mouse_old_style = 0; + +void trs_get_mouse_pos(int *x, int *y, unsigned int *buttons) +{ + Window root, child; + int root_x, root_y, win_x, win_y; + unsigned int mask; + XQueryPointer(display, window, &root, &child, + &root_x, &root_y, &win_x, &win_y, &mask); +#if MOUSEDEBUG + debug("get_mouse %d %d 0x%x ->", win_x, win_y, mask); +#endif + if (win_x >= 0 && win_x < OrigWidth && + win_y >= 0 && win_y < OrigHeight) { + /* Mouse is within emulator window */ + if (win_x < left_margin) win_x = left_margin; + if (win_x >= OrigWidth - left_margin) win_x = OrigWidth - left_margin - 1; + if (win_y < top_margin) win_y = top_margin; + if (win_y >= OrigHeight - top_margin) win_y = OrigHeight - top_margin - 1; + *x = mouse_last_x = (win_x - left_margin) + * mouse_x_size + / (OrigWidth - 2*left_margin); + *y = mouse_last_y = (win_y - top_margin) + * mouse_y_size + / (OrigHeight - 2*top_margin); + mouse_last_buttons = 7; + /* !!Note: assuming 3-button mouse */ + if (mask & Button1Mask) mouse_last_buttons &= ~4; + if (mask & Button2Mask) mouse_last_buttons &= ~2; + if (mask & Button3Mask) mouse_last_buttons &= ~1; + } + *x = mouse_last_x; + *y = mouse_last_y; + *buttons = mouse_last_buttons; +#if MOUSEDEBUG + debug("%d %d 0x%x\n", + mouse_last_x, mouse_last_y, mouse_last_buttons); +#endif +} + +void trs_set_mouse_pos(int x, int y) +{ + int dest_x, dest_y; + if (x == mouse_last_x && y == mouse_last_y) { + /* Kludge: Ignore warp if it says to move the mouse to where we + last said it was. In general someone could really want to do that, + but with MDRAW, gratuitous warps to the last location occur frequently. + */ + return; + } + dest_x = left_margin + x * (OrigWidth - 2*left_margin) / mouse_x_size; + dest_y = top_margin + y * (OrigHeight - 2*top_margin) / mouse_y_size; + +#if MOUSEDEBUG + debug("set_mouse %d %d -> %d %d\n", x, y, dest_x, dest_y); +#endif + XWarpPointer(display, window, window, 0, 0, OrigWidth, OrigHeight, + dest_x, dest_y); +} + +void trs_get_mouse_max(int *x, int *y, unsigned int *sens) +{ + *x = mouse_x_size - (mouse_old_style ? 0 : 1); + *y = mouse_y_size - (mouse_old_style ? 0 : 1); + *sens = mouse_sens; +} + +void trs_set_mouse_max(int x, int y, unsigned int sens) +{ + if ((x & 1) == 0 && (y & 1) == 0) { + /* "Old style" mouse drivers took the size here; new style take + the maximum. As a heuristic kludge, we assume old style if + the values are even, new style if not. */ + mouse_old_style = 1; + } + mouse_x_size = x + (mouse_old_style ? 0 : 1); + mouse_y_size = y + (mouse_old_style ? 0 : 1); + mouse_sens = sens; +} + +int trs_get_mouse_type() +{ + /* !!Note: assuming 3-button mouse */ + return 1; +} + diff --git a/truedam.ccc b/truedam.ccc new file mode 100644 index 0000000..178737a --- /dev/null +++ b/truedam.ccc @@ -0,0 +1,45 @@ +/* truedam.ccc -- Misosys C program to query/set truedam flag on xtrs */ +/* Copyright (c) 1998, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 14:54:58 PST 1998 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +usage(n) + int n; +{ + fprintf(stderr, "usage: truedam [0|1]\n"); + exit(1); +} + +int main(argc, argv) + int argc; + char **argv; +{ + int hl, bc, de, ret; + char *retp; + int func; + + if (argc == 1) { + func = EMT_MISC_QUERY_TRUEDAM; + } else if ((argc == 2) && isdigit(argv[1][0])) { + func = EMT_MISC_SET_TRUEDAM; + if (strlen(argv[1]) != 1) usage(); + hl = atoi(argv[1]); + if (hl > 1) usage(); + } else { + usage(); + } + + emt_4misc(func, &hl, &bc, &de); + printf("truedam = %d\n", hl); + + exit(0); +} diff --git a/truedam.cmd b/truedam.cmd Binary files differnew file mode 100644 index 0000000..4e3738a --- /dev/null +++ b/truedam.cmd diff --git a/truedam6.cmd b/truedam6.cmd Binary files differnew file mode 100644 index 0000000..05a6b79 --- /dev/null +++ b/truedam6.cmd diff --git a/umount.ccc b/umount.ccc new file mode 100644 index 0000000..d63635e --- /dev/null +++ b/umount.ccc @@ -0,0 +1,63 @@ +/* umount.ccc -- Misosys C program to unmount an emulated floppy on xtrs */ +/* Copyright (c) 1998, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 15:21:16 PST 1998 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +usage() +{ + fprintf(stderr, "usage: umount unit#\n"); + exit(1); +} + +#define DDIRSIZE 256 +#define DNAMESIZE 512 +#define CMDSIZE 512 +#define ERRBUFSIZE 512 + +int main(argc, argv) + int argc; + char **argv; +{ + char ddir[DDIRSIZE]; + char dname[DNAMESIZE], cmd[CMDSIZE]; + char errbuf[ERRBUFSIZE]; + char model[4]; + int hl, bc, de, ret; + char *retp; + + if (argc != 2 || !isdigit(argv[1][0])) { + usage(); + } + retp = emt_gtddir(ddir, DDIRSIZE); + if (retp == NULL) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "emt_getddir: %s\n", errbuf); + exit(1); + } + emt_4misc(EMT_MISC_QUERY_MODEL, &hl, &bc, &de); + if (hl == 5) { + strcpy(model, "4p"); + } else { + sprintf(model, "%d", hl); + } + sprintf(dname, "%s/disk%s-%s", ddir, model, argv[1]); + sprintf(cmd, "rm -f %s", dname); + /*printf("$ %s\n", cmd);*/ + ret = emt_system(cmd); + if (ret != 0) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "emt_system: %s\n", errbuf); + exit(1); + } + emt_misc(EMT_MISC_DISK_CHANGE); + exit(0); +} diff --git a/umount.cmd b/umount.cmd Binary files differnew file mode 100644 index 0000000..bc6b7c6 --- /dev/null +++ b/umount.cmd diff --git a/umount6.cmd b/umount6.cmd Binary files differnew file mode 100644 index 0000000..3e13f32 --- /dev/null +++ b/umount6.cmd diff --git a/unix.ccc b/unix.ccc new file mode 100644 index 0000000..977852d --- /dev/null +++ b/unix.ccc @@ -0,0 +1,80 @@ +/* unix.ccc -- Misosys C program to execute a Unix shell command from xtrs */ +/* Copyright (c) 1998, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 16:59:54 PST 1998 by mann */ + +#option redirect 0 +#include "xtrsemt.h" +#include "xtrsemt.ccc" /* could use separate compilation instead */ + +usage() +{ + fprintf(stderr, "usage: unix [-l] command\n"); + exit(1); +} + +#define ERRBUFSIZE 256 +#define CMDBUFSIZE 512 + +int main(argc, argv) + int argc; + char **argv; +{ + int ret, i, lower; + char errbuf[ERRBUFSIZE]; + char cmdbuf[CMDBUFSIZE]; + char *p, *q; + + if (argc < 2) { + usage(); + } + i = 1; + if (argv[i][0] == '-') { + if (tolower(argv[i][1]) != 'l') { + usage(); + } + i++; + lower = 1; + } else { + lower = 0; + } + + p = cmdbuf; + for (; i<argc; i++) { + q = argv[i]; + if (lower) { + while (*q) { + if (*q == '[' && *(q+1)) { + q++; + *p++ = *q++; + } else if (isalpha(*q)) { + *p++ = tolower(*q++); + } else { + *p++ = *q++; + } + } + } else { + while (*q) *p++ = *q++; + } + *p++ = ' '; + } + *p = '\000'; + /*printf("$ %s\n", cmdbuf);*/ + ret = emt_system(cmdbuf); + if (ret == -1) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "unix: %s\n", errbuf); + exit(1); + } else if (ret != 0) { + emt_strerror(errno, errbuf, ERRBUFSIZE); + fprintf(stderr, "unix: error (exit status %d, signal %d)\n", + ret >> 8, ret & 0xff); + exit(1); + } + exit(0); +} diff --git a/unix.cmd b/unix.cmd Binary files differnew file mode 100644 index 0000000..b033f30 --- /dev/null +++ b/unix.cmd diff --git a/unix6.cmd b/unix6.cmd Binary files differnew file mode 100644 index 0000000..6ca649f --- /dev/null +++ b/unix6.cmd diff --git a/utility.dsk b/utility.dsk Binary files differnew file mode 100644 index 0000000..0884943 --- /dev/null +++ b/utility.dsk diff --git a/utility.jcl b/utility.jcl new file mode 100644 index 0000000..0dc8d52 --- /dev/null +++ b/utility.jcl @@ -0,0 +1,38 @@ +. Note: Use TRSDOS 6.2.1 to get old-style dates and +. thus avoid password problems with other DOSes. +format :#d#(sden,sides=1,cyl=80,dir=17,name="xtrsutil",q=n,abs) +import export.cmd export/cmd:#D# +import -n export.z80 export/z80:#D# +import import.cmd import/cmd:#D# +import -n import.z80 import/z80:#D# +import -n settime.z80 settime/z80:#D# +import settime.cmd settime/cmd:#D# +import -n xtrsemt.ccc xtrsemt/ccc:#D# +import -n xtrsemt.h xtrsemt/h:#D# +import -n settime.ccc settime/ccc:#D# +import -n m1format.fix m1format/fix:#D# +import xtrshard.dct xtrshard/dct:#D# +import -n xtrshard.z80 xtrshard/z80:#D# +import xtrs8.dct xtrs8/dct:#D# +import -n xtrs8.z80 xtrs8/z80:#D# +import xtrsmous.cmd xtrsmous/cmd:#D# +import -n xtrsmous.z80 xtrsmous/z80:#D# +import -n cd.ccc cd/ccc:#D# +import -n pwd.ccc pwd/ccc:#D# +import -n unix.ccc unix/ccc:#D# +import -n mount.ccc mount/ccc:#D# +import -n umount.ccc umount/ccc:#D# +import cd.cmd cd/cmd:#D# +import pwd.cmd pwd/cmd:#D# +import unix.cmd unix/cmd:#D# +import mount.cmd mount/cmd:#D# +import umount.cmd umount/cmd:#D# +import truedam.cmd truedam/cmd:#D# +import cd6.cmd cd6/cmd:#D# +import pwd6.cmd pwd6/cmd:#D# +import unix6.cmd unix6/cmd:#D# +import mount6.cmd mount6/cmd:#D# +import umount6.cmd umount6/cmd:#D# +import truedam6.cmd truedam6/cmd:#D# +import -n expall.bas expall/bas:#D# +import -n do6.jcl do6/jcl:#D# diff --git a/xtrs.man b/xtrs.man new file mode 100644 index 0000000..5353eff --- /dev/null +++ b/xtrs.man @@ -0,0 +1,1067 @@ +.TH xtrs 1 +.SH Name +xtrs \- TRS-80 Model I/III/4/4P emulator for the X Window System +.SH Syntax +.B xtrs [-model m] [-diskdir d] [-debug] +.I [other options] +.SH Description +\fBxtrs\fP is built on top of a Z-80 emulator, with added routines to support +keyboard and video I/O through an X interface. +The hardware emulation can operate as a TRS-80 Model I, Model III, +Model 4, or Model 4P. + +\fBxtrs\fP supports 48K of RAM in Model I or Model III mode, 128K in +Model 4 or Model 4P mode. Floppy disks and hard disks are emulated +using files to store the data; or under Linux only, real floppy drives +can be used. A printer is emulated by sending its output to standard +output. A serial port is emulated using a Unix tty device. +Cassette I/O is emulated using files to store the cassette +data; real cassettes can also be read or written (with luck), either +directly through your sound card (on Linux and other systems with +OSS-compatible sound drivers), or via .wav files. Game sound and +music output are also supported if you have an OSS-compatible sound +driver; sound output though the cassette port, through the Model 4 +sound option, and through the optional Orchestra-85/90 music synthesizer +card are all emulated. In Model I mode, the HRG1B graphics card +is emulated. In Model III and 4/4P mode, you can select whether +the Radio Shack graphics card or Micro Labs Grafyx Solution is emulated. +There is also a mouse driver for Model 4/4P mode. Several common +time-of-day clock cards are emulated on all models. The Alpha Products +joystick is emulated using the PC's numeric keypad. + +Because \fBxtrs\fP emulates the hardware, all known TRS-80 Model +I/III/4/4P operating systems should run on it, including all flavors +of TRSDOS, LDOS/LS-DOS, NEWDOS, DOSPLUS, MultiDOS, and TRS-80 CP/M. +However, the emulator also includes some extensions to the standard +hardware, and the special drivers, utilities, and instructions needed +for these are not provided for all operating systems. + +The Z-80 emulator has a debugger called zbx. You can enter the +debugger either by starting \fBxtrs\fP with the -debug flag or by +pressing F9 while \fBxtrs\fP is running. The debugger runs in the X +terminal window that you started \fBxtrs\fP from. Once you are in the +debugger, type "help" for more information. + +Special support in the emulator allows the program to block when +waiting for information from the keyboard. This will work only for +programs that wait for keyboard input using the standard Model I/III +ROM call; the emulator decides whether to block the Z-80 program when +it tries to read from the keyboard memory by pattern-matching its +stack. +.SH Keys +The following keys have special meanings to \fBxtrs\fP: + +LeftArrow, Backspace, or Delete is the TRS-80 left arrow key. +RightArrow or Tab is the right arrow key. UpArrow is the up arrow +key. DownArrow or Linefeed is the down arrow key. Esc or Break is +the Break key. Home, Clear, or LeftAlt is the Clear key. Control is +the Model 4 Ctrl key (address bit 7, data bit 2). RightAlt is +equivalent to the shifted down arrow key (used as a control key with +some TRS-80 software). + +F1, F2, and F3 are the Model 4/4P function keys (address bit 7, data bits +4, 5, and 6). F1 is also the Model I Electric Pencil control key that +some users added to their machines. F4 is the Model 4 Caps Lock key +(address bit 7, data bit 3). F5, Compose, or ScrollLock is equivalent +to the @ key (so that @ can be used as a modifier key). F6 is +equivalent to the 0 key (so that a shifted 0 can be obtained). F7 +signals a disk change in the emulated floppy drives (see below). F8 +exits the program. F9 enters the debugger (zbx). F10 is the reset button. + +In Model III, 4, and 4P modes, the left and right shift keys are +distinct; in Model I mode, they are the same. The PageUp and PageDown +keys always activate the positions that correspond to the Model +III/4/4P left and right shift keys (address bit 7, data bits 0 and 1 +respectively), even in Model I mode. The End key activates an unused +position in the keyboard matrix (address bit 7, data bit 7). + +The keys [, \\, ], ^, _, {, |, }, and ~ also activate unused positions +in the keyboard matrix (address bit 3, data bits 3-7). With many +TRS-80 keyboard drivers, these keys map to the corresponding ASCII +characters; with others, they do nothing. In some cases you may find +the shift state is reversed from what it should be; if you press [ but +{ is displayed instead (etc.), see the -shiftbracket and +-noshiftbracket options below to correct the problem. The Insert key +maps to the same position as underscore (address bit 3, data bit 7), +so that this key can be used both with and without shift pressed; +with many TRS-80 keyboard drivers one of these maps to ASCII code 0x7f. + +On a German keyboard, the umlaut and "ess-tsett" keys should activate +the corresponding characters used in the GENIE, a German Model I clone. +This feature is most useful together with the "-charset genie" command +line argument. + +Pressing a key on a PC numeric keypad with NumLock disengaged emulates +the Alpha Products joystick. Keys 2, 4, 6, 8 (KP_Down, KP_Left, +KP_Right, KP_Up) are the main directions; keys 1, 3, 7, and 9 (KP_End, +KP_Page_Down, KP_Home, KP_Page_Up) work as diagonal directions by +activating two main directions at once; and key 0 (KP_Insert) or 5 +(KP_Begin) is the fire button. Note that your X server may default to +sending digits for the keys on the numeric pad even if NumLock is not +pressed. If you have this problem, you can use the xmodmap program to remap +your numeric pad, and use the xev program to debug it. +.SH Emulated cassette +To control the emulated cassette, a file called ".cassette.ctl" in the +current directory keeps track of what file is currently loaded as +the cassette tape and the current position within that file. The +\fBcassette\fP(1) shell script provides a way to manipulate this file. +You may use this script to load and +position cassette tape files. The operation works very much like an +actual tape recorder. See the \fBcassette\fP man page for more information +about the cassette shell script and the cassette file formats that are supported. +.SH Printer +For printer support, any text sent to the TRS-80's printer (using LPRINT +or LLIST, for example) is sent to the standard output. +.SH Emulated floppy disks +In Model I mode, \fBxtrs\fP emulates a Radio Shack Expansion Interface +with the Percom Doubler or Radio Shack Doubler installed. The Doubler +provides double-density disk access by allowing either the stock +WD1771 FDC chip or a WD1791 chip to be selected under program control. +At powerup the 1771 is selected, so operating systems with no Doubler +driver see a stock system. By default, the emulator pretends to be +both a Percom and Radio Shack Doubler at the same time -- it responds +to the special commands of both -- so a driver for either should work. +Under LDOS, use the command "FDUBL" (newer versions of LDOS), or +"PDUBL" or "RDUBL" (older versions) to install the driver. Software that +tries to detect which doubler you have (such as Super Utility) may be +confused by the emulation of both at once, so you can choose to emulate +only one with a command line option; see below. + +In Model III, 4, or 4P mode, \fBxtrs\fP emulates the stock floppy +controller, which uses a WD1793 chip (software-compatible with the +WD1791) to provide both single and double density. + +Four 5.25-inch floppy drives are emulated, with storage in files named +diskM-U, where M is the TRS-80 model (1, 3, 4, or 4p) and U is the +drive unit number (0, 1, 2, or 3). If a file of the required name is +not found, a drive with no disk in it is emulated (but see below). If +the user does not have write permission for a floppy file, and/or the +file has an internal write protect flag set, a write-protect tab is +emulated. Use the \fBmkdisk\fP(1) program to turn the write protect +flag on or off. To change floppies in an emulated drive, rename the +existing file for the drive (if any), rename the new floppy file to +the proper name, and hit function key F7 in the emulator. + +If you try to boot an emulated Model I, III, or 4 with no file named +diskM-0 (that is, no disk in drive 0), \fBxtrs\fP emulates having no +floppy disk controller. The behavior of a real machine with a disk +controller in this case didn't seem useful to emulate faithfully: A +real Model I hangs with a screen full of garbage; a real Model III or +4 goes into a retry loop printing "Diskette?" on the screen and +rechecking whether you've inserted one. A real Model 4P always has a +floppy controller, however, so \fBxtrs\fP always emulates one. + +Due to a limitation of the original Model I hardware, drive :3 cannot +be double-sided in Model I mode. In the original Model I, you could +not have a drive :3 at all if any drive in the system was +double-sided, but the emulator is able to be more forgiving. + +Emulated floppy image files can be of any of three types: JV1, +compatible with Jeff Vavasour's popular freeware Model I emulator for +MS-DOS; JV3, a compatible extension of a format first used in +Vavasour's commercial Model III/4 emulator; or DMK, compatible with +David Keil's Model 4 emulator. All three types work in \fBxtrs\fP +regardless of what model it is emulating. A heuristic is used to +decide which type of image is in a drive, as none of the types has a +magic number or signature. + +JV1 supports only single density, single sided, with directory on +track 17. Sectors must be 256 bytes long. Use FORMAT (DIR=17) if you +want to format JV1 disks with more (or less) than 35 tracks under +LDOS. + +JV3 is much more flexible, though it still does not support everything +the real controllers could do. It is probably best to use JV3 for all +the disk images you create, since it is the most widely implemented by +other emulators, unless you have a special reason to use one of the +others. A JV3 disk can be formatted with 128, 256, 512, or 1024-byte +sectors, 1 or 2 sides, single or double density, with either FB +(normal) or F8 (deleted) data address mark on any sector. In single +density the nonstandard data address marks FA and F9 are also +available. You cannot format a sector with an incorrect track number +or head number. You can format a sector with an intentional CRC error +in the data field. \fBxtrs\fP supports at most 5802 total sectors on +a JV3 image. + +The original Vavasour JV3 format supported only 256-byte sectors, and +had a limit of 2901 total sectors. If you use sector sizes other +than 256 bytes or format more than 2901 sectors on a disk image, +emulators other than \fBxtrs\fP may be unable to read it. Note that +an 80 track, double-sided, double-density (18 sector) 5.25-inch floppy +will fit within the original 2901 sector limit; the extension to 5802 +is primarily for emulation of 8-inch drives (discussed below). + +The DMK format is the most flexible. It supports essentially +everything that the original hardware could do, including all +"protected" disk formats. However, a few protected disks still may +not work with xtrs due to limitations in xtrs's floppy disk controller +emulation rather than limitations of the DMK format; see the +LIMITATIONS section below. + +The program \fBmkdisk\fP(1) makes a blank emulated floppy or "bulk +erases" an existing one. By default, mkdisk makes a JV3 floppy, but +with the -1 flag it makes a JV1 floppy, or with the -k flag a DMK +floppy. See the \fBmkdisk\fP man page for more information. + +Early Model I operating systems used an FA data address mark for the +directory on single density disks, while later ones wrote F8 but would +accept either upon reading. The change was needed because FA is a +nonstandard DAM that is fully supported only by the WD1771 floppy disk +controller used in the Model I; the controllers in the Model III and 4 +cannot distinguish between FA and FB (which is used for non-directory +sectors) upon reading, and cannot write FA. To deal nicely with this +problem, \fBxtrs\fP implements the following kludge. On writing in +single density, an F8 data address mark is recorded as FA. On reading +with an emulated WD1771 (available in Model I mode only), FA is +returned as FA; on reading with a WD179x, FA is returned as F8. This +trick makes the different operating systems perfectly compatible with +each other, which is better than on a real Model I! You can use the +-truedam flag to turn off this kludge if you need to; in that case the +original hardware is emulated exactly. + +TRS-80 programs that attempt to measure the rotational +speed of their floppy disk drives using timing loops will get the +answers they expect, even when \fBxtrs\fP does not +emulate instructions at the same speed as the original +machines. This works because \fBxtrs\fP keeps a virtual clock +(technically, a T-state counter), +which measures how much time it should have taken to execute the +instruction stream on a real machine, and it ties the emulation of +floppy disk index holes to this clock, not to real time. +.SH Emulated 8-inch floppy disks +In addition to the four standard 5.25-inch drives, \fBxtrs\fP also +emulates four 8-inch floppy drives. There is no widely-accepted +standard hardware interface for 8-inch floppies on the TRS-80, so \fBxtrs\fP +emulates a pseudo-hardware interface of its own and provides an LDOS/LS-DOS +driver for it. + +Storage for the emulated 8-inch disks is in files named diskM-U, where +M is the TRS-80 model number (1, 3, 4, or 4p) and U is a unit number +(4, 5, 6, or 7). The only difference between 5.25-inch and 8-inch +emulated drives is that the emulator allows you to format more bytes +per track in the latter. A new JV3 floppy can be formatted as either +5.25-inch or 8-inch depending on whether you initially put it into a +5.25-inch or 8-inch emulated drive. A new DMK floppy, however, must +be created with the -8 flag to mkdisk in order to be large enough for +use in an 8-inch emulated drive. JV1 floppies cannot be used in +8-inch drives. Be careful not to put an emulated floppy into a +5.25-inch emulated drive after it has been formatted in an 8-inch +emulated drive or vice versa; the results are likely to be confusing. +Consider using different file extensions for the two types; say, \.dsk +for 5.25-inch and \.8in for 8-inch. + +To use the emulated 8-inch drives, you'll need a driver. Under LDOS +or LS-DOS, use the program XTRS8/DCT supplied on the emulated floppy +\fIutility.dsk\fP. This driver is a very simple wrapper around the +native LDOS/LS-DOS floppy driver. Here are detailed instructions. + +First, make sure an appropriate version of LDOS is in emulated floppy +drive 0, and the supplied file \fIutility.dsk\fP is in another +emulated floppy drive. Boot LDOS. If you are using Model I LDOS, be +sure FDUBL is running. + +Second, type the following commands. Here \fId\fP is the LDOS drive +number you want to use for the 8-inch drive and \fIu\fP is the +unit number you chose when naming the file. Most likely you will +choose \fId\fP and \fIu\fP to be equal to reduce confusion. + +.nf + SYSTEM (DRIVE=\fId\fP,DRIVER="XTRS8",ENABLE) + Enter unit number ([4]-7): \fIu\fP +.fi + +You can repeat these steps with different values of \fId\fP and +\fIu\fP to have more than one 8-inch drive. You might want to repeat +four times using 4, 5, 6, and 7, or you might want to save some drive +numbers for hard drives (see below). + +Finally, it's a good idea to give the SYSTEM (SYSGEN) command (Model +I/III) or SYSGEN command (Model 4/4P). This command saves the SYSTEM +settings, so the 8-inch drives will be available again the next time +you reboot or restart the emulator. If you need to access an 8-inch +drive after booting from a disk that hasn't been SYSGENed, simply use +the same SYSTEM command again. + +In case you want to write your own driver for another TRS-80 operating +system, here are details on the emulated pseudo-hardware. The 8-inch +drives are accessed through the normal floppy disk controller, exactly +like 5.25-inch drives. The four 5.25-inch drives have hardware select codes +1, 2, 4, and 8, corresponding respectively to files diskM-0, -1, -2, and +-3. The four 8-inch drives have hardware select codes 3, 5, 6, and 7, +corresponding respectively to files diskM-4, -5, -6, and -7. +(See also the \-sizemap option below, however.) +.SH Real floppy disks +Under Linux only, any diskM-U file can be a symbolic link to a real +floppy disk drive, typically /dev/fd0 or /dev/fd1. Most PCs should be +able to read and write TRS-80 compatible floppies in this way. Many +PC floppy controllers cannot handle single density, however, and some +may have problems even with double density disks written on a real +TRS-80, especially disks formatted by older TRS-80 operating systems. +Use the -doublestep flag if you need to read 35-track or 40-track +media in an 80-track drive. If you need to write 35-track or 40-track +media in an 80-track drive, bulk-erase the media first and format it +in the 80-track drive. Don't write to a disk in an 80-track drive if it +has ever been written to in a 40-track drive. The narrower head used +in an 80-track drive cannot erase the full track width written by the +head in a 40-track drive. + +If you link one of the 5.25-inch floppy files (diskM-0 through +diskM-3) to a real floppy drive, TRS-80 programs will see it as a +5.25-inch drive, but the actual drive can be either 3.5-inch or +5.25-inch. The drive will be operated in double density (or single +density), not high density, so be sure to use the appropriate media. + +If you link one of the 8-inch floppy files (diskM-4 through diskM-7) +to a real floppy drive, TRS-80 programs will see it as an 8-inch +drive. Again, you need to use the XTRS8/DCT driver described above to +enable LDOS/LS-DOS to access an 8-inch drive. The real drive can be +either 3.5-inch, 5.25-inch, or 8-inch. A 3.5-inch or 5.25-inch drive +will be operated in high-density mode, using MFM recording if the +TRS-80 is trying to do double density, FM recording if the TRS-80 is +trying to do single density. In this mode, these drives can hold as +much data as a standard 8-inch drive. In fact, a 5.25-inch HD drive +holds exactly the same number of bits per track as an 8-inch drive; a +3.5-inch HD drive can hold 20% more, but we waste that space when +using one to emulate an 8-inch drive. In both cases we also waste the +top three tracks, since an 8-inch drive has only 77 tracks, not 80. + +The nonstandard FA and F9 data address marks available in single +density on a real Model I with the WD1771 controller also need special +handling. A PC-style floppy disk controller can neither read nor +write sectors with such DAMs at all. This raises three issues: (1) It +will be impossible for you to read some Model I disks on your PC even +if your PC otherwise supports single density. In particular, Model I +TRSDOS 2.3 directory tracks will be unreadable. (2) On writing in +single density, \fBxtrs\fP silently records a F9 or FA DAM as F8. (3) +On reading in single density with an emulated WD1771 (Model I mode +only), F8 is returned as FA. If you need more accurate behavior, the +-truedam flag will turn on error messages on attempts to write F9 or +FA DAMs and will turn off translation of F8 to FA on reading. + +Hint: Be sure to set the drive type correctly in your PC's BIOS. +Linux and xtrs rely on this information to know how fast your drives +are spinning and hence what data rate to use when reading and writing. +All 3.5-inch drives spin at 300 RPM. Newer 5.25-inch high-density +capable drives ("1.2MB" drives) normally always spin at 360 RPM. (Some +can be jumpered to slow down to 300 RPM when in double-density mode, +but you should not do that when plugging one into a PC.) Older +5.25-inch drives that cannot do high density ("180KB", "360KB" or +"720KB" 5.25-inch drives) always spin at 300 RPM. All 8-inch drives +spin at 360 RPM. If you plug an 8-inch drive into a PC (this requires +a 50-pin to 34-pin adaptor cable), tell your BIOS that it is a +5.25-inch 1.2MB drive. +.SH Emulated hard disks +\fBxtrs\fP can emulate a hard disk in a file in one of two ways: it +can use a special, xtrs-specific LDOS driver called XTRSHARD/DCT, +or it can emulate the Radio Shack hard drive controller (based on +the Western Digital WD1010) and use the native drivers for the +original hardware. + +.B Using XTRSHARD/DCT + +The XTRSHARD/DCT driver has been tested and +works under both LDOS 5.3.1 for Model I or III and TRSDOS/LS-DOS 6.3.1 +for Model 4/4P. It may or may not work under earlier LDOS versions. It +definitely will not work under other TRS-80 operating systems or with +emulators other than \fBxtrs\fP. The hard disk format was designed by +Matthew Reed for his Model I/III and Model 4 emulators; \fBxtrs\fP +duplicates the format so that users can exchange hard drive images +across the emulators. + +To use XTRSHARD/DCT, first run the \fBmkdisk\fP program +under Unix to create a blank hard drive (.hdv) file. Typical usage +would be: \fImkdisk -h mydisk.hdv\fP. See the \fBmkdisk\fP(1) man +page for other options. + +Second, link the file to an appropriate name. XTRSHARD/DCT supports up +to eight hard drives, with names of the form hardM-U, where M is the +TRS-80 model (1, 3, or 4; in this case Model 4P also uses M=4) and U +is a unit number from 0 to 7. It looks for these files in the same +directory as the floppy disk files diskM-U. + +Third, make sure an appropriate version of LDOS is in emulated floppy +drive 0, and the supplied file \fIutility.dsk\fP is in another +emulated floppy drive. Boot LDOS. If you are using Model I LDOS +5.3.1, patch a bug in the FORMAT command by typing +\fIPATCH FORMAT/CMD.UTILITY M1FORMAT/FIX\fP. +You need to apply this patch only +once. It must not be applied to Model III or Model 4/4P LDOS. + +Fourth, type the following commands. Here \fId\fP is the LDOS drive number +you want to use for the hard drive (a typical choice would be 4) and \fIu\fP +is the unit number you chose when naming the file (most likely 0). + +.nf + SYSTEM (DRIVE=\fId\fP,DRIVER="XTRSHARD",ENABLE) + Enter unit number ([0]-7): \fIu\fP + FORMAT \fId\fP (DIR=1) +.fi + +Answer the questions asked by FORMAT as you prefer. The +\fIDIR=1\fP parameter to FORMAT is optional; it causes the hard +drive's directory to be on track 1, making the initial size of +the image smaller. You can repeat these steps with different values of +\fId\fP and \fIu\fP to have more than one hard drive. + +Finally, it's a good idea to give the SYSTEM (SYSGEN) command (Model +I/III) or SYSGEN command (Model 4/4P). This command saves the SYSTEM +settings, so the drive will be available again the next time you +reboot or restart the emulator. If you need to access the hard disk +file after booting from a floppy that hasn't been SYSGENed, simply use +the same SYSTEM command(s) again, but don't FORMAT. You can freely +use a different drive number or (if you renamed the hard disk file) a +different unit number. + +The F7 key currently doesn't allow XTRSHARD/DCT disk changes to be +recognized, but you can change to a different hard disk file for the +same unit by renaming files as needed and rebooting LDOS. + +Technical note: XTRSHARD/DCT is a small Z-80 program that implements +all the required functions of an LDOS disk driver. Instead of talking +to a real (or emulated) hard disk controller, however, it uses special +support in \fBxtrs\fP that allows Z-80 programs to open, close, read, +and write Unix files directly. This support is described further in +the "Data import and export" section below. + +.B Using native hard disk drivers + +Beginning in version 4.1, \fBxtrs\fP also emulates the Radio Shack +hard disk controller (based on the Western Digital WD1010) and will +work with the native drivers for this hardware. This emulation uses +the same hard drive (.hdv) file format that XTRSHARD/DCT does. With +LDOS/LS-DOS, the RSHARDx/DCT and TRSHD/DCT drivers are known to work. +With Montezuma CP/M 2.2, the optional Montezuma hard disk drivers are +known to work. The hard disk drivers for NEWDOS/80 and for Radio +Shack CP/M 3.0 should work, but they have not yet been tested at this +writing. Any bugs should be reported. + +To get started, run the \fBmkdisk\fP program under Unix to create a +blank hard drive (.hdv) file. Typical usage would be: +\fImkdisk -h mydisk.hdv\fP. +See the \fBmkdisk\fP(1) man page for other options. + +Second, link the file to an appropriate name. The WD1010 emulation +supports up to four hard drives, with names of the form hardM-U, where +M is the TRS-80 model (1, 3, 4, or 4p) and U is a unit number from 0 +to 3. It looks for these files in the same directory as the floppy +disk files diskM-U. If no such files are present, \fBxtrs\fP disables +the WD1010 emulation. + +Note that if hard drive unit 0 is present on a Model 4P (file +hard4p-0), the Radio Shack boot ROM will always try to boot from it, +even if the operating system does not support booting from a hard +drive. If you have this problem, either hold down F2 while booting to +force the ROM to boot from floppy, or simply avoid using unit number +0. Stock TRSDOS/LS-DOS 6 systems do not support booting from a hard +drive; M.A.D. Software's HBUILD6 add-on to LS-DOS for hard drive +booting should work, but is untested. Montezuma CP/M 2.2 does boot +from the emulated hard drive. + +Finally, obtain the correct driver for the operating system you will be +using, read its documentation, configure the driver, and format the drive. +Detailed instructions are beyond the scope of this manual page. +.SH Data import and export +Several Z-80 programs for data import and export from various TRS-80 +operating systems are included with \fBxtrs\fP on two emulated floppy +images. These programs use special support in the emulator to read +and write external Unix files, discussed further at the end of this section. + +The emulated floppy \fIutility.dsk\fP contains some programs for +transferring data between the emulator and ordinary Unix files. +IMPORT/CMD, EXPORT/CMD, and SETTIME/CMD run on +the emulator under Model I/III TRSDOS, Model I/III LDOS, Model I/III +Newdos/80, and Model 4/4P TRSDOS/LS-DOS 6; they may also work under +other TRS-80 operating systems. Model III TRSDOS users will have to +use TRSDOS's CONVERT command to read utility.dsk. + +IMPORT/CMD imports a Unix file and writes it to an emulated disk. +Usage: \fIIMPORT [-lne] unixfile [trsfile]\fP. The -n flag converts +Unix newlines (\\n) to TRS-80 newlines (\\r). The -l flag converts +the Unix filename to lower case, to compensate for TRS-80 operating +systems such as Newdos/80 that convert all command line arguments to +upper case. When using the -l flag, you can put a [ or up-arrow in +front of a character to keep it in upper case. Give the -e flag if +your TRS-80 operating system uses the Newdos/80 convention for +representing the ending record number in an open file control block. +This should be detected automatically for Newdos/80 itself and for +TRSDOS 1.3, but you'll need to give the flag for DOSPLUS and possibly +other non-LDOS operating systems. If you need the flag but don't give +it (or vice versa), imported files will come out the wrong length. If +the destination file is omitted, IMPORT uses the last component of the +Unix pathname, but with any "." changed to "/" to match TRS-80 DOS +file extension syntax. + +EXPORT/CMD reads a file from an emulated disk and exports it to a Unix +file. Usage: \fIEXPORT [-lne] trsfile [unixfile]\fP. The -n flag +converts TRS-80 newlines (\\r) to Unix newlines (\\n). The -l flag +converts the Unix filename to lower case. When using the -l flag, you +can put a [ or up-arrow in front of a character to keep it in upper +case. Give the -e flag if your TRS-80 operating system uses the +Newdos/80 convention for representing the ending record number in an +open file control block. This should be detected automatically for +Newdos/80 itself and for TRSDOS 1.3, but you'll need to give the flag +for DOSPLUS and possibly other non-LDOS operating systems. If you +need the flag but don't give it (or vice versa), exported files will +come out the wrong length. If the destination file is omitted, EXPORT +uses the TRS-80 filename, but with any "/" changed to "." to match +Unix file extension syntax. + +SETTIME/CMD reads the date and time from Unix and sets the TRS-80 +DOS's date and time accordingly. + +The next several programs were written in Misosys C and exist in two +versions on utility.dsk. The one whose name ends in "6" runs +on Model 4 TRSDOS/LS-DOS 6.x; the other runs on LDOS 5.x and most +other Model I/III operating systems. + +CD/CMD (or CD6/CMD) changes xtrs's Unix working directory. +Usage: \fICD [-l] unixdir\fP. The -l flag converts the Unix directory +name to lower case. When using the -l flag, you can put a [ or +up-arrow in front of a character to keep it in upper case. Running +CD/CMD will change the interpretation of any relative pathnames given +to IMPORT or EXPORT. It will also change the interpretation of disk +names at the next disk change, unless you specified an absolute +pathname for xtrs's -diskdir parameter. + +PWD/CMD (or PWD6/CMD) prints xtrs's Unix working directory. + +UNIX/CMD (or UNIX6/CMD) runs a Unix shell command. +Usage: \fIUNIX [-l] unix command line\fP. +The -l flag converts the Unix command line +to lower case. When using the -l flag, you can put a [ or +up-arrow in front of a character to keep it in upper case. +Standard I/O for +the command uses the xtrs program's standard I/O descriptors; it does +not go to the TRS-80 screen or come from the TRS-80 keyboard. + +MOUNT/CMD (or MOUNT6/CMD) is a convenience program that switches +emulated floppy disks in the drives. Usage: \fIMOUNT [-l] filename U\fP. +The -l flag converts the Unix filename +to lower case. When using the -l flag, you can put a [ or +up-arrow in front of a character to keep it in upper case. +The filename is any Unix filename; U is a single digit, 0 through 7. +The command deletes the file diskM-U (where M is the TRS-80 model) +from the disk directory (see -diskdir option), replaces it with a +symbolic link to the given filename, and signals a disk change (as if +F7 had been pressed). + +UMOUNT/CMD (or UMOUNT6/CMD) is a convenience program that removes an +emulated floppy disk from a drive. Usage: \fIUMOUNT U\fP. U is a +single digit, 0 through 7. The command deletes the file diskM-U +(where M is the TRS-80 model) from the disk directory (see -diskdir +option) and signals a disk change (as if F7 had been pressed). + +The emulated floppy \fIcpmutil.dsk\fP contains import and export +programs for Montezuma CP/M, written by Roland Gerlach. It was +formatted as a "Montezuma Micro Standard DATA disk (40T, SS, DD, +200K)," with 512-byte sectors. Be careful to configure your CP/M to +the proper disk format and drive parameters (40 track, not 80), or you +will have confusing problems reading this disk. Documentation is +included in the file cpmutil.html and source code in the file +cpmutil.tgz (a gzipped tar archive). +See http://members.optusnet.com.au/~rgerlach/trs-80/cpmutil.html +where you will sometimes find a newer version of the utilities +than is included with xtrs. + +The emulator implements a set of pseudo-instructions (emulator traps) +that give TRS-80 programs access to Unix files. The programs listed +above use them. If you would like to write your own such programs, +the traps are documented in the file trs_imp_exp.h. Assembler +source code for the existing programs is supplied in xtrshard.z, +import.z, export.z, and settime.z. You can also write programs that +use the traps in Misosys C, using the files xtrsemt.h and xtrsemt.ccc +as an interface; a simple example is in settime.ccc. +.SH Interrupts +The emulator supports only interrupt mode 1. It will complain if your +program enables interrupts after powerup without executing an IM 1 +instruction first. All Model I/III/4/4P software does this, as the +built-in peripherals in these machines support only IM 1. + +The Model I has a 40 Hz heartbeat clock interrupt, while the Model +III uses 30 Hz, and the Model 4/4P can run at either 30 Hz or 60 Hz. +The emulator approximates this rather well even on a system where +clock ticks come at some frequency that isn't divisible by the +emulated frequency (e.g., 100 Hz on Intel Linux), as long as the true +frequency is not slower than the emulated frequency. The emulator has +a notion of the absolute time at which each tick is supposed to occur, +and it asks the host system to wake it up at each of those times. The +net result is that some ticks may be late, but there are always the +proper number of ticks per second. For example, running in Model I +mode on Intel Linux you'd see this pattern: (tick, 30ms, tick, +20ms,...) instead of seeing ticks every 25ms. +.SH Processor speed selection +A standard Model 4 has a software-controlled switch to select +operation at either 4.05504 MHz (with heartbeat clock at 60 Hz) +or 2.02752 MHz (with heartbeat clock at 30 Hz). xtrs emulates this +feature. + +Model I's were often modified to operate at higher speeds than the +standard 1.77408 MHz. With one common modification, writing a 1 +to port 0xFE would double the speed to 3.54816 MHz, while writing a 0 +would set the speed back to normal. The heartbeat clock runs at 40 Hz +in either case. xtrs emulates this feature as well. +.SH Sound +Sound support uses +the Open Sound System /dev/dsp device, standard on Linux and available +on many other Unix versions as well. This support is compiled in +automatically on Linux; if you have OSS on another version of Unix, +you'll need to define the symbol HAVE_OSS in your Makefile or in +trs_cassette.c. Any time TRS-80 software tries to write non-zero +values to the cassette port (or the Model 4/4P optional sound port) +with the cassette motor off, it is assumed to be trying to make sounds +and xtrs opens /dev/dsp. It automatically closes the device again +after a few seconds of silence. + +If you are playing a game with sound, you'll want to use the +-autodelay flag to slow down instruction emulation to approximately +the speed of a real TRS-80. If you don't do this, sound will still +play correctly, but the gameplay may be way too fast and get ahead of +the sound. + +On the other hand, if your machine is a bit too slow, +you'll hear gaps and pops in the sound when the TRS-80 program lags +behind the demand of the sound card for more samples. The -autodelay +feature includes a small speed boost whenever a sound starts to play +to try to prevent this, but if the boost is too much or too little, +you might either find that the game runs too fast when a lot of sound +is playing, or that the sound has gaps in it anyway. If your sound +has gaps, you can try reducing the sample rate with the -samplerate flag. + +The Orchestra-85 music synthesis software will run under xtrs's Model +I emulation, and the Orchestra-90 software will run with Model III +operating systems under xtrs's Model III, 4, or 4P emulation. For +best results, use Orchestra-90 and the Model 4 emulation, as this lets +the software run at the highest emulated clock rate (4 MHz) and thus +generate the best sound. If you want to run Orchestra-85 instead, you +can tell it that you have a 3.5 MHz clock speedup with enable sequence +3E01D3FE and disable sequence 3E00D3FE; this will let the software run +twice as fast as on an unmodified Model I and generate better sound. +There is no need to use xtrs's -autodelay flag when running +Orchestra-85/90, but you might want to specify a small fixed delay to +keep from getting excessive key repeat. +.SH Mouse +A few Model 4 programs could use a mouse, such as the shareware hi-res +drawing program MDRAW-II. The program XTRSMOUS/CMD on the utility disk +(utility.dsk) is a mouse driver for Model 4/4P mode that should work +with most such programs. \fBxtrs\fP does not emulate the actual mouse +hardware (a serial mouse plugged into the Model 4 RS-232 port), so the +original mouse drivers will not work under \fBxtrs\fP. Instead, +XTRSMOUS accesses the X mouse pointer using an emulator trap. +XTRSMOUS implements the same TRSDOS/LS-DOS 6 SVC interface as the +David Goben and Matthew Reed mouse drivers. (It does not implement the +interface of the older Scott McBurney mouse driver, which may be +required by some programs.) + +By default XTRSMOUS installs itself in high memory. This is done +because MDRAW-II tests for the presence of a mouse by +looking to see whether the mouse SVC is vectored to high memory. If the +driver is installed in low memory, MDRAW thinks it is not there at +all. If you use mouse-aware programs that don't have this bug, or if +you edit the first line of MDRAW to remove the test, you can install +XTRSMOUS in low memory using the syntax "XTRSMOUS (LOW)". +.SH Time of day clock +Several battery-backed time of day clocks were sold for the various +TRS-80 models, +including the TimeDate80, TChron1, TRSWatch, and T-Timer. They are +essentially all the same hardware, but reside at a few different port +ranges. \fBxtrs\fP currently emulates them at port ranges 0x70-0x7C and +0xB0-0xBC. The T-Timer port range at 0xC0-0xCC conflicts with the Radio Shack +hard drive controller and is not emulated. + +These clocks return only a 2-digit year, and it is unknown what their +driver software will do in the year 2000 and beyond. If you have +software that works with one of them, please send email to report +what happens when it is used with \fBxtrs\fP. + +Also see SETTIME/CMD in the "Data import and export" section above for +another way to get the correct time into a Z-80 operating system +running under \fBxtrs\fP. + +Finally, you might notice that LDOS/LS-DOS always magically knows the +correct date when you boot it (but not the time). When you first +power up the emulated TRS-80, \fBxtrs\fP dumps the date into the +places in memory where LDOS and LS-DOS normally save it across +reboots, so it looks to the operating system as if you rebooted after +setting the date. +.SH Joystick +Pressing a key on a PC numeric keypad with NumLock disengaged emulates +the Alpha Products joystick. See the Keys section above for details. +The emulated joystick is mapped only at port 0, to avoid conflicts +with other devices. The joystick emulation could be made to work with +real joysticks using the X input extension, but this is not +implemented yet. +.SH Running games +Some games run rather well under \fBxtrs\fP now, +provided that your +machine is fast enough to run the emulation in real time and that you +choose the right command line options. +"Galaxy Invaders Plus" by Big 5 Software is particularly good. +You will usually want to turn on autodelay, +and if your machine is slow you may need to reduce the sound sample rate. +Running your X server in 8-bit/pixel mode +also seems to help in some cases. Example command lines: + +.nf + startx -- -bpp 8 + xtrs -autodelay +.fi + +If you have a slow machine and the sound breaks up, it is possible +that your machine is not fast enough to generate samples at the default +rate of 44,100 Hz. If you think this may be happening, +try "-samplerate 11025" or even "-samplerate 8000". +.SH Options +Defaults for all options can be specified using the standard X resource +mechanism, and the class name for \fBxtrs\fP is "Xtrs". +.TP +.B \-display \fIdisplay\fP +Set your X display to \fIdisplay\fP. The default is to +use the DISPLAY environment variable. +.TP +.B \-iconic +Start with the \fBxtrs\fP window iconified. +.TP +.B \-background \fIcolor\fP +.PD 0 +.TP +.B \-bg \fIcolor\fP +.PD +Specifies the background color of the \fBxtrs\fP window. +.TP +.B \-foreground \fIcolor\fP +.PD 0 +.TP +.B \-fg \fIcolor\fP +.PD +Specifies the foreground color of the \fBxtrs\fP window. +.TP +.B \-title \fItitletext\fP +Use \fItitletext\fP in the window title bar instead of the +program name. +.TP +.B \-borderwidth \fIwidth\fP +Put a border of \fIwidth\fP pixels +around the TRS-80 display. The default is 2. +.TP +.B \-scale \fIxfac[,yfac]\fP +Multiply the horizontal and vertical window size by \fIxfac\fP and +\fIyfac\fP, respectively. Possible values are integers in the range +[1,4] for \fIxfac\fP and [1,8] for \fIyfac\fP. Defaults are +\fIxfac\fP=1 and \fIyfac\fP=2*\fIxfac\fP. +.TP +.B \-resize +In Model III or 4/4P mode, resize the X window whenever the emulated +display mode changes between 64x16 text (or 512x192 graphics) and 80x24 +text (or 640x240 graphics). +This is the default in Model III mode, since 80x24 text is not available +and the 640x240 graphics add-on card is seldom used. +.TP +.B \-noresize +In Model III or 4/4P mode, always keep the X window large enough for +80x24 text or 640x240 graphics, putting a blank margin around the +outside when the emulated display mode is 64x16 text or 512x192 +graphics. This is the default in Model 4/4P mode, since otherwise there +is an annoying size switch during every reboot. +.TP +.B \-charset \fIname\fP +Select among several sets of built-in character bitmaps. + +In Model I mode, five sets are available. The default, \fIwider\fP, is +a modified Model III set with characters 8 pixels wide; it looks +better on a modern computer screen with square pixels than the real +Model I fonts, which were 6 pixels wide. \fIlcmod\fP is the character +set in the replacement character generator that was supplied with the +Radio Shack lower case modification. (It was reconstructed partly +from memory and may have some minor bit errors.) \fIstock\fP is the +character set in the stock character generator supplied with most +upper case only machines. Since \fIxtrs\fP currently always emulates +the extra bit of display memory needed to support lower case, this +character set gives you the authentic, unpleasant effect that real +Model I users saw when they tried to do homebrew lower case +modifications without replacing the character generator: lower case +letters appear at an inconsistent height, and if you are using the +Level II BASIC ROM display driver, upper case letters are replaced by +meaningless symbols. \fIearly\fP is the same as stock, but with the +standard ASCII characters [, \\, ], and ^ in the positions where most +Model I's had directional arrows. This was the default programming in +the Motorola character generator ROM that Radio Shack used, and a few +early machines were actually shipped with this ROM. Finally, +\fIgerman\fP or \fIgenie\fP gives an approximate emulation of the +GENIE, a German Model I clone. Characters are 8 pixels wide, and +double width is supported even though later GENIE models did not +include it. + +In Model III, 4, and 4P modes, three sets are available: +\fIkatakana\fP (the default for Model III) is the original Model III set with +Japanese Katakana characters in the alternate character +positions. This set was also used in early Model 4's. +\fIinternational\fP (the default for Model 4 and 4P) is a +later Model 4 set with accented Roman letters in the alternate positions. +\fIbold\fP is a bold set from a character generator ROM found in one +Model III, origin uncertain. +.TP +.B \-usefont +Use X fonts instead of the built-in character bitmaps. +.TP +.B \-nofont +Use the built-in character bitmaps, not a X font. This is the default. +.TP +.B \-font \fIfontname\fP +If -usefont is also given, +use the specified X font for normal width characters. +The default uses a common X fixed-width font: +"-misc-fixed-medium-r-normal--20-200-75-75-*-100-iso8859-1". +.TP +.B \-widefont \fIfontname\fP +If -usefont is also given, +use the specified X font for double width characters. +The default uses a common X fixed-width font, scaled to double width: +"-misc-fixed-medium-r-normal--20-200-75-75-*-200-iso8859-1". +.TP +.B \-nomicrolabs +In Model I mode, emulate the HRG1B 384x192 hi-res graphics card. +In Model III mode or Model 4/4P mode, emulate the Radio Shack hi-res card. +This is now the default. +.TP +.B \-microlabs +In Model III or 4/4P mode, emulate the Micro Labs Grafyx Solution hi-res +graphics card. Note that the Model III and Model 4/4P cards from Micro +Labs are very different from one another. +.TP +.B \-debug +Enter zbx, the z80 debugger. +.TP +.B \-romfile \fIfilename\fP +.PD 0 +.TP +.B \-romfile3 \fIfilename3\fP +.TP +.B \-romfile4p \fIfilename4p\fP +.PD +Use the romfile specified by \fIfilename\fP in Model I mode, the +romfile specified by \fIfilename3\fP in Model III and Model 4 mode, +or the romfile specified by \fIfilename4p\fP in Model 4P mode, +A romfile can be either a raw binary dump, Intel hex format, or +TRS-80 cmd format (for example, a MODELA/III file). +If you do not set this option or the corresponding X resource, a default +established at compile time is used (if any); see Makefile.local for +instructions on compiling in default romfiles or default romfile names. +.TP +.B \-model \fIm\fP +Specifies which TRS-80 model to emulate. Values accepted are 1 or I (Model +I), 3 or III (Model III), 4 or IV (Model 4), and 4P or IVP (Model 4P). +Model I is the default. +.TP +.B \-delay \fId\fP +A crude speed control. After each Z-80 instruction, xtrs busy-waits +for \fId\fP iterations around an empty loop. A really smart C optimizer +might delete this loop entirely, so it's possible that this option +won't work if you compile xtrs with too high an optimization level. +The default delay is 0. +.TP +.B \-autodelay +Dynamically adjusts the value of -delay to run instructions at roughly +the same rate as a real machine. The tracking is only approximate, +but it can be useful for running games. +.TP +.B \-noautodelay +Turn off -autodelay. This is the default. +.TP +.B \-keystretch \fIcycles\fP +Fine-tune the keyboard behavior. To prevent keystrokes from being +lost, xtrs "stretches" the intervals between key transitions, so that +the Z-80 program has time to see each transition before the next one +occurs. Whenever the Z-80 program reads the keyboard matrix and +sees an emulated key go up or down, xtrs waits \fIcycles\fP +Z-80 clock cycles (T-states) before it allows the program to see +another key transition. Key transitions that are received during +the waiting period or when the Z-80 program is not reading the keyboard +are held in a queue. The default stretch value is 4000 cycles; it +should seldom if ever be necessary to change it. +.TP +.B \-shiftbracket +Emulate [, \\, ], ^, and _ as shifted keys, and {, |, }, and ~ as +unshifted. This is the default in Model 4 and 4P modes, and it +works well with the keyboard driver in Model 4 TRSDOS/LS-DOS 6. +.TP +.B \-noshiftbracket +Emulate [, \\, ], ^, and _ as unshifted keys, and {, |, }, and ~ as +shifted. This is the default in Model I and III modes, and it +works well with many TRS-80 keyboard drivers. With some +keyboard drivers these keys do not work at all, however. +.TP +.B \-diskdir \fIdir\fP +Specify the directory containing floppy and hard disk images. +If the value starts with "~/" (or is just "~"), it is relative to your +home directory. The default value is ".". +.TP +.B \-doubler \fItype\fP +Specify what type of double density adaptor to emulate (Model I mode only). +The \fItype\fP may be \fIpercom\fP, \fIradioshack\fP (or \fItandy\fP), +\fIboth\fP, or \fInone\fP. The type may be abbreviated to one character. +The default is \fIboth\fP, which causes the double density adaptor emulation +to respond to the special commands of both the Percom and Radio Shack cards. +.TP +.B \-doublestep +Make all real floppy drives double-step, allowing access to 35-track or +40-track media in an 80-track drive. Linux only. See the Floppy Disks +section for limitations. +.TP +.B \-nodoublestep +Turn off double-step mode for all real floppy drives. Linux only. +This is the default. +.TP +.B \-stepmap s0,s1,s2,s3,s4,s5,s6,s7 +Selectively set double-step mode for individual real floppy drives. +If \fIsU\fP is 2 and \fIdiskM-U\fP is a real drive, the drive will +be double-stepped; if \fIsU\fP is 1, it will be single-stepped. +You can omit values from the end of the list; those drives will get the +default value set by -doublestep or -nodoublestep. +.TP +.B \-sizemap z0,z1,z2,z3,z4,z5,z6,z7 +Selectively set whether drives are emulated as 5-inch or 8-inch; see +the section "Emulated 8-inch floppy disks" above. If \fIzU\fP is 5, +the drive will appear to Z-80 software as 5-inch; if 8, as 8-inch. +The default setting (as reflected in the documentation above) is +5,5,5,5,8,8,8,8. You can omit values from the end of the list; those +drives will get the default values. Setting one or more of the first +four drives to 8-inch may be useful for CP/M software that supports +8-inch drives. You can also use XTRS8/DCT with 8-inch drives in the +first four positions; even though the prompt suggests the unit number +must be 4-7, numbers 0-3 are accepted. XTRS8 does not check whether +the unit you've selected is really being emulated as an 8-inch drive, +however; you'll simply get errors during FORMAT if you get this wrong. +.TP +.B \-truedam +Turn off the single density data address mark remapping kludges +described in the "Emulated floppy disks" and "Real floppy disks" +sections above. With this +option given, the distinction between F8 and FA data address marks +is strictly observed on both writing and reading. This option is +probably not useful unless you need to deal with Model I disks that use +the distinction as part of a copy-protection scheme. See also +"Common File Formats for Emulated TRS-80 Floppy Disks", available +at http://www.tim-mann.org/trs80/dskspec.html. +.TP +.B \-notruedam +The opposite of -truedam. This setting is the default. +.TP +.B \-samplerate \fIrate\fP +Set the sample rate for new cassette wav files, direct +cassette I/O to the sound card, and game sound output to the sound +card. Existing wav files will be read or modified using their +original sample rate regardless of this flag. The default is 44,100 +Hz. See also the cassette(1) man page. +.TP +.B \-serial \fIttyname\fP +Set the tty device to be used for I/O to the TRS-80's serial port. +The default is /dev/ttyS0 on Linux, /dev/tty00 on other versions +of Unix. Setting the name to be empty (\-serial "") emulates having +no serial port. +.TP +.B \-switches \fIvalue\fP +Set the sense switches on the Model I serial port card. This option +is meaningful only in Model I mode, and only when the -serial option +is not set to "". The default value is 0x6f, which Radio Shack software +conventionally interprets as 9600 bps, 8 bits/word, no parity, 1 stop +bit. +.TP +.B \-emtsafe +Disable emulator traps (see "Data import and export") that could write +to host files other than disk images in the original diskdir. +.TP +.B \-noemtsafe +The opposite of -emtsafe. This setting is the default. +.SH Additional resources +There are many other TRS-80 resources available on the Web, including +shareware and freeware emulators that run under MSDOS and other +operating systems, software for converting TRS-80 physical media to +the emulator's disk file format, ROM images, and TRS-80 software that +has already been converted. For pointers, see +http://www.tim-mann.org/trs80.html. +.SH Bugs and limitations +The emulated serial port's modem status and control signals are not +tied to the signals on the real serial port, because the real signals +are not available to software through the Unix tty device interface. +The ability to check for parity, framing, and overrun errors and +receive an interrupt when one occurs is not emulated. Unix does not +support 2000, 3600, or 7200 baud, so these TRS-80 data rates are +remapped to 38400, 57600, and 115200 baud respectively. + +A better signal processing algorithm might help read real cassettes +more reliably, especially at 1500bps. + +Some features of the floppy disk controller are not currently +emulated: Force Interrupt with condition bits 0x01, 0x02, or 0x04 is not +implemented. Read Track is implemented only for DMK emulated +floppies. The multiple-sector flags in Read and Write are not +implemented. The timing of returned sectors is emulated only for the +Read Address command, and not very accurately for JV1 or JV3. +If a disk has more than one sector with the same number on a track, +\fBxtrs\fP will always see the first (counting from the index hole) +when reading or writing; a real machine would see the next one to come +under the head depending on the current rotational position of the disk. +Partially reformatting a track (which TRS-80 programs like HyperZap +and Model I Super Utility do to achieve mixed density) is supported +for DMK but not JV3; however, switching densities while formatting +(which Model III and 4 Super Utility do) works on both DMK and JV3. + +Real physical floppy disks are supported only under Linux, because +Unix does not define a portable interface to the low-level floppy +controller functionality that \fBxtrs\fP needs. There are some +limitations even under Linux: Index holes are faked, not detected on +the real disk, and the timing of returned sectors is not emulated at +all. Due to the limitations of PC-style floppy disk controllers, when +formatting a physical floppy under \fBxtrs\fP, you cannot mix sectors +of different sizes on the same track, switch densities in the middle +of a track, or reformat only part of a track. However, \fBxtrs\fP can +read and write to physical floppies that have already been formatted +in these ways (perhaps by a real TRS-80). + +The extended JV3 limit of 5802 sectors is somewhat arbitrary. It +could be raised by generalizing the code to permit more than two +blocks of 2901, but this does not seem too useful. 5802 sectors is +already enough for a 3.5-inch HD (1.44MB) floppy, which the TRS-80 +didn't support anyway. If you need more space, use emulated hard +drives instead of emulated floppies with huge numbers of tracks. + +XTRSHARD/DCT ignores the internal write-protected flag in hard drive +images, but a hard drive image can still be effectively write +protected by turning off its Unix write permission bits. + +The emulator uses a heuristic to decide what format a ROM file is in. +If a raw binary ROM image starts with 0x01, 0x05, or 0x22, it can be +misidentified as being in a different format. This is rather unlikely +to occur, as ROMs typically begin with 0xF3, the DI instruction. + +The joystick emulation could be made to work with real joysticks using +the X input extension, but this is not implemented yet. + +If you discover other bugs, write fixes for any of these, or make any +other enhancements, please let us know so that we can incorporate +the changes into future releases. +.SH Authors and acknowledgements +\fBxtrs\fP 1.0 was written by David Gingold and Alec Wolman. +The current version was revised and much extended by Timothy Mann +(see http://tim-mann.org/). See README and +README.tpm for additional notes from the authors. + +We also thank the following people for their help. The JV1 and JV3 +floppy disk file formats were designed by Jeff Vavasour, originally +for his MSDOS-based TRS-80 emulators. The DMK format was designed by +David Keil for his MSDOS-based TRS-80 emulator. The hard disk file +format was designed by Matthew Reed for his MSDOS-based TRS-80 +emulators. Al Petrofsky and Todd P. Cromwell III supplied font data. +Roland Gerlach contributed the CP/M import and export programs as well +as several bug reports and fixes for the emulator itself. Ulrich +Mueller added the -borderwidth option, improved the -scale option and +the bitmap font scaling, ported the import, export, and settime +utilities to Newdos/80, and contributed the HRG1B emulation. Branden +Robinson supplied the first version of the cassette man page, fixed +Makefile bugs, translated cassette to the Bourne shell, and +implemented watchpoints in zbx. Mark McDougall provided documentation +for the Micro Labs Grafyx Solution card. Jenz Guenther added the +-title option and contributed code to emulate the GENIE (German Model +I clone). Joe Peterson contributed code to emulate the TimeDate80 and +the -emtsafe feature. Denis Leconte contributed part of the -scale +implementation. diff --git a/xtrs.txt b/xtrs.txt new file mode 100644 index 0000000..d74dba6 --- /dev/null +++ b/xtrs.txt @@ -0,0 +1,1054 @@ +xtrs(1) xtrs(1) + +Name + xtrs - TRS-80 Model I/III/4/4P emulator for the X Window System + +Syntax + xtrs [-model m] [-diskdir d] [-debug] [other options] + +Description + xtrs is built on top of a Z-80 emulator, with added routines to support + keyboard and video I/O through an X interface. The hardware emulation + can operate as a TRS-80 Model I, Model III, Model 4, or Model 4P. + + xtrs supports 48K of RAM in Model I or Model III mode, 128K in Model 4 + or Model 4P mode. Floppy disks and hard disks are emulated using files + to store the data; or under Linux only, real floppy drives can be used. + A printer is emulated by sending its output to standard output. A + serial port is emulated using a Unix tty device. Cassette I/O is emu- + lated using files to store the cassette data; real cassettes can also + be read or written (with luck), either directly through your sound card + (on Linux and other systems with OSS-compatible sound drivers), or via + .wav files. Game sound and music output are also supported if you have + an OSS-compatible sound driver; sound output though the cassette port, + through the Model 4 sound option, and through the optional Orches- + tra-85/90 music synthesizer card are all emulated. In Model I mode, + the HRG1B graphics card is emulated. In Model III and 4/4P mode, you + can select whether the Radio Shack graphics card or Micro Labs Grafyx + Solution is emulated. There is also a mouse driver for Model 4/4P + mode. Several common time-of-day clock cards are emulated on all mod- + els. The Alpha Products joystick is emulated using the PC's numeric + keypad. + + Because xtrs emulates the hardware, all known TRS-80 Model I/III/4/4P + operating systems should run on it, including all flavors of TRSDOS, + LDOS/LS-DOS, NEWDOS, DOSPLUS, MultiDOS, and TRS-80 CP/M. However, the + emulator also includes some extensions to the standard hardware, and + the special drivers, utilities, and instructions needed for these are + not provided for all operating systems. + + The Z-80 emulator has a debugger called zbx. You can enter the debug- + ger either by starting xtrs with the -debug flag or by pressing F9 + while xtrs is running. The debugger runs in the X terminal window that + you started xtrs from. Once you are in the debugger, type "help" for + more information. + + Special support in the emulator allows the program to block when wait- + ing for information from the keyboard. This will work only for pro- + grams that wait for keyboard input using the standard Model I/III ROM + call; the emulator decides whether to block the Z-80 program when it + tries to read from the keyboard memory by pattern-matching its stack. + +Keys + The following keys have special meanings to xtrs: + + LeftArrow, Backspace, or Delete is the TRS-80 left arrow key. RightAr- + row or Tab is the right arrow key. UpArrow is the up arrow key. Dow- + nArrow or Linefeed is the down arrow key. Esc or Break is the Break + key. Home, Clear, or LeftAlt is the Clear key. Control is the Model 4 + Ctrl key (address bit 7, data bit 2). RightAlt is equivalent to the + shifted down arrow key (used as a control key with some TRS-80 soft- + ware). + + F1, F2, and F3 are the Model 4/4P function keys (address bit 7, data + bits 4, 5, and 6). F1 is also the Model I Electric Pencil control key + that some users added to their machines. F4 is the Model 4 Caps Lock + key (address bit 7, data bit 3). F5, Compose, or ScrollLock is equiva- + lent to the @ key (so that @ can be used as a modifier key). F6 is + equivalent to the 0 key (so that a shifted 0 can be obtained). F7 sig- + nals a disk change in the emulated floppy drives (see below). F8 exits + the program. F9 enters the debugger (zbx). F10 is the reset button. + + In Model III, 4, and 4P modes, the left and right shift keys are dis- + tinct; in Model I mode, they are the same. The PageUp and PageDown + keys always activate the positions that correspond to the Model + III/4/4P left and right shift keys (address bit 7, data bits 0 and 1 + respectively), even in Model I mode. The End key activates an unused + position in the keyboard matrix (address bit 7, data bit 7). + + The keys [, \, ], ^, , {, |, }, and ~ also activate unused positions + in the keyboard matrix (address bit 3, data bits 3-7). With many + TRS-80 keyboard drivers, these keys map to the corresponding ASCII + characters; with others, they do nothing. In some cases you may find + the shift state is reversed from what it should be; if you press [ but + { is displayed instead (etc.), see the -shiftbracket and -noshift- + bracket options below to correct the problem. The Insert key maps to + the same position as underscore (address bit 3, data bit 7), so that + this key can be used both with and without shift pressed; with many + TRS-80 keyboard drivers one of these maps to ASCII code 0x7f. + + On a German keyboard, the umlaut and "ess-tsett" keys should activate + the corresponding characters used in the GENIE, a German Model I clone. + This feature is most useful together with the "-charset genie" command + line argument. + + Pressing a key on a PC numeric keypad with NumLock disengaged emulates + the Alpha Products joystick. Keys 2, 4, 6, 8 (KP Down, KP Left, + KP Right, KP Up) are the main directions; keys 1, 3, 7, and 9 (KP End, + KP Page Down, KP Home, KP Page Up) work as diagonal directions by acti- + vating two main directions at once; and key 0 (KP Insert) or 5 + (KP Begin) is the fire button. Note that your X server may default to + sending digits for the keys on the numeric pad even if NumLock is not + pressed. If you have this problem, you can use the xmodmap program to + remap your numeric pad, and use the xev program to debug it. + +Emulated cassette + To control the emulated cassette, a file called ".cassette.ctl" in the + current directory keeps track of what file is currently loaded as the + cassette tape and the current position within that file. The cas- + sette(1) shell script provides a way to manipulate this file. You may + use this script to load and position cassette tape files. The opera- + tion works very much like an actual tape recorder. See the cassette + man page for more information about the cassette shell script and the + cassette file formats that are supported. + +Printer + For printer support, any text sent to the TRS-80's printer (using + LPRINT or LLIST, for example) is sent to the standard output. + +Emulated floppy disks + In Model I mode, xtrs emulates a Radio Shack Expansion Interface with + the Percom Doubler or Radio Shack Doubler installed. The Doubler pro- + vides double-density disk access by allowing either the stock WD1771 + FDC chip or a WD1791 chip to be selected under program control. At + powerup the 1771 is selected, so operating systems with no Doubler + driver see a stock system. By default, the emulator pretends to be + both a Percom and Radio Shack Doubler at the same time -- it responds + to the special commands of both -- so a driver for either should work. + Under LDOS, use the command "FDUBL" (newer versions of LDOS), or + "PDUBL" or "RDUBL" (older versions) to install the driver. Software + that tries to detect which doubler you have (such as Super Utility) may + be confused by the emulation of both at once, so you can choose to emu- + late only one with a command line option; see below. + + In Model III, 4, or 4P mode, xtrs emulates the stock floppy controller, + which uses a WD1793 chip (software-compatible with the WD1791) to pro- + vide both single and double density. + + Four 5.25-inch floppy drives are emulated, with storage in files named + diskM-U, where M is the TRS-80 model (1, 3, 4, or 4p) and U is the + drive unit number (0, 1, 2, or 3). If a file of the required name is + not found, a drive with no disk in it is emulated (but see below). If + the user does not have write permission for a floppy file, and/or the + file has an internal write protect flag set, a write-protect tab is + emulated. Use the mkdisk(1) program to turn the write protect flag on + or off. To change floppies in an emulated drive, rename the existing + file for the drive (if any), rename the new floppy file to the proper + name, and hit function key F7 in the emulator. + + If you try to boot an emulated Model I, III, or 4 with no file named + diskM-0 (that is, no disk in drive 0), xtrs emulates having no floppy + disk controller. The behavior of a real machine with a disk controller + in this case didn't seem useful to emulate faithfully: A real Model I + hangs with a screen full of garbage; a real Model III or 4 goes into a + retry loop printing "Diskette?" on the screen and rechecking whether + you've inserted one. A real Model 4P always has a floppy controller, + however, so xtrs always emulates one. + + Due to a limitation of the original Model I hardware, drive :3 cannot + be double-sided in Model I mode. In the original Model I, you could + not have a drive :3 at all if any drive in the system was double-sided, + but the emulator is able to be more forgiving. + + Emulated floppy image files can be of any of three types: JV1, compati- + ble with Jeff Vavasour's popular freeware Model I emulator for MS-DOS; + JV3, a compatible extension of a format first used in Vavasour's com- + mercial Model III/4 emulator; or DMK, compatible with David Keil's + Model 4 emulator. All three types work in xtrs regardless of what + model it is emulating. A heuristic is used to decide which type of + image is in a drive, as none of the types has a magic number or signa- + ture. + + JV1 supports only single density, single sided, with directory on track + 17. Sectors must be 256 bytes long. Use FORMAT (DIR=17) if you want + to format JV1 disks with more (or less) than 35 tracks under LDOS. + + JV3 is much more flexible, though it still does not support everything + the real controllers could do. It is probably best to use JV3 for all + the disk images you create, since it is the most widely implemented by + other emulators, unless you have a special reason to use one of the + others. A JV3 disk can be formatted with 128, 256, 512, or 1024-byte + sectors, 1 or 2 sides, single or double density, with either FB (nor- + mal) or F8 (deleted) data address mark on any sector. In single den- + sity the nonstandard data address marks FA and F9 are also available. + You cannot format a sector with an incorrect track number or head num- + ber. You can format a sector with an intentional CRC error in the data + field. xtrs supports at most 5802 total sectors on a JV3 image. + + The original Vavasour JV3 format supported only 256-byte sectors, and + had a limit of 2901 total sectors. If you use sector sizes other than + 256 bytes or format more than 2901 sectors on a disk image, emulators + other than xtrs may be unable to read it. Note that an 80 track, dou- + ble-sided, double-density (18 sector) 5.25-inch floppy will fit within + the original 2901 sector limit; the extension to 5802 is primarily for + emulation of 8-inch drives (discussed below). + + The DMK format is the most flexible. It supports essentially every- + thing that the original hardware could do, including all "protected" + disk formats. However, a few protected disks still may not work with + xtrs due to limitations in xtrs's floppy disk controller emulation + rather than limitations of the DMK format; see the LIMITATIONS section + below. + + The program mkdisk(1) makes a blank emulated floppy or "bulk erases" an + existing one. By default, mkdisk makes a JV3 floppy, but with the -1 + flag it makes a JV1 floppy, or with the -k flag a DMK floppy. See the + mkdisk man page for more information. + + Early Model I operating systems used an FA data address mark for the + directory on single density disks, while later ones wrote F8 but would + accept either upon reading. The change was needed because FA is a non- + standard DAM that is fully supported only by the WD1771 floppy disk + controller used in the Model I; the controllers in the Model III and 4 + cannot distinguish between FA and FB (which is used for non-directory + sectors) upon reading, and cannot write FA. To deal nicely with this + problem, xtrs implements the following kludge. On writing in single + density, an F8 data address mark is recorded as FA. On reading with an + emulated WD1771 (available in Model I mode only), FA is returned as FA; + on reading with a WD179x, FA is returned as F8. This trick makes the + different operating systems perfectly compatible with each other, which + is better than on a real Model I! You can use the -truedam flag to + turn off this kludge if you need to; in that case the original hardware + is emulated exactly. + + TRS-80 programs that attempt to measure the rotational speed of their + floppy disk drives using timing loops will get the answers they expect, + even when xtrs does not emulate instructions at the same speed as the + original machines. This works because xtrs keeps a virtual clock (tech- + nically, a T-state counter), which measures how much time it should + have taken to execute the instruction stream on a real machine, and it + ties the emulation of floppy disk index holes to this clock, not to + real time. + +Emulated 8-inch floppy disks + In addition to the four standard 5.25-inch drives, xtrs also emulates + four 8-inch floppy drives. There is no widely-accepted standard hard- + ware interface for 8-inch floppies on the TRS-80, so xtrs emulates a + pseudo-hardware interface of its own and provides an LDOS/LS-DOS driver + for it. + + Storage for the emulated 8-inch disks is in files named diskM-U, where + M is the TRS-80 model number (1, 3, 4, or 4p) and U is a unit number + (4, 5, 6, or 7). The only difference between 5.25-inch and 8-inch emu- + lated drives is that the emulator allows you to format more bytes per + track in the latter. A new JV3 floppy can be formatted as either + 5.25-inch or 8-inch depending on whether you initially put it into a + 5.25-inch or 8-inch emulated drive. A new DMK floppy, however, must be + created with the -8 flag to mkdisk in order to be large enough for use + in an 8-inch emulated drive. JV1 floppies cannot be used in 8-inch + drives. Be careful not to put an emulated floppy into a 5.25-inch emu- + lated drive after it has been formatted in an 8-inch emulated drive or + vice versa; the results are likely to be confusing. Consider using + different file extensions for the two types; say, .dsk for 5.25-inch + and .8in for 8-inch. + + To use the emulated 8-inch drives, you'll need a driver. Under LDOS or + LS-DOS, use the program XTRS8/DCT supplied on the emulated floppy util- + ity.dsk. This driver is a very simple wrapper around the native + LDOS/LS-DOS floppy driver. Here are detailed instructions. + + First, make sure an appropriate version of LDOS is in emulated floppy + drive 0, and the supplied file utility.dsk is in another emulated + floppy drive. Boot LDOS. If you are using Model I LDOS, be sure FDUBL + is running. + + Second, type the following commands. Here d is the LDOS drive number + you want to use for the 8-inch drive and u is the unit number you chose + when naming the file. Most likely you will choose d and u to be equal + to reduce confusion. + + SYSTEM (DRIVE=d,DRIVER="XTRS8",ENABLE) + Enter unit number ([4]-7): u + + You can repeat these steps with different values of d and u to have + more than one 8-inch drive. You might want to repeat four times using + 4, 5, 6, and 7, or you might want to save some drive numbers for hard + drives (see below). + + Finally, it's a good idea to give the SYSTEM (SYSGEN) command (Model + I/III) or SYSGEN command (Model 4/4P). This command saves the SYSTEM + settings, so the 8-inch drives will be available again the next time + you reboot or restart the emulator. If you need to access an 8-inch + drive after booting from a disk that hasn't been SYSGENed, simply use + the same SYSTEM command again. + + In case you want to write your own driver for another TRS-80 operating + system, here are details on the emulated pseudo-hardware. The 8-inch + drives are accessed through the normal floppy disk controller, exactly + like 5.25-inch drives. The four 5.25-inch drives have hardware select + codes 1, 2, 4, and 8, corresponding respectively to files diskM-0, -1, + -2, and -3. The four 8-inch drives have hardware select codes 3, 5, 6, + and 7, corresponding respectively to files diskM-4, -5, -6, and -7. + (See also the -sizemap option below, however.) + +Real floppy disks + Under Linux only, any diskM-U file can be a symbolic link to a real + floppy disk drive, typically /dev/fd0 or /dev/fd1. Most PCs should be + able to read and write TRS-80 compatible floppies in this way. Many PC + floppy controllers cannot handle single density, however, and some may + have problems even with double density disks written on a real TRS-80, + especially disks formatted by older TRS-80 operating systems. Use the + -doublestep flag if you need to read 35-track or 40-track media in an + 80-track drive. If you need to write 35-track or 40-track media in an + 80-track drive, bulk-erase the media first and format it in the + 80-track drive. Don't write to a disk in an 80-track drive if it has + ever been written to in a 40-track drive. The narrower head used in an + 80-track drive cannot erase the full track width written by the head in + a 40-track drive. + + If you link one of the 5.25-inch floppy files (diskM-0 through diskM-3) + to a real floppy drive, TRS-80 programs will see it as a 5.25-inch + drive, but the actual drive can be either 3.5-inch or 5.25-inch. The + drive will be operated in double density (or single density), not high + density, so be sure to use the appropriate media. + + If you link one of the 8-inch floppy files (diskM-4 through diskM-7) to + a real floppy drive, TRS-80 programs will see it as an 8-inch drive. + Again, you need to use the XTRS8/DCT driver described above to enable + LDOS/LS-DOS to access an 8-inch drive. The real drive can be either + 3.5-inch, 5.25-inch, or 8-inch. A 3.5-inch or 5.25-inch drive will be + operated in high-density mode, using MFM recording if the TRS-80 is + trying to do double density, FM recording if the TRS-80 is trying to do + single density. In this mode, these drives can hold as much data as a + standard 8-inch drive. In fact, a 5.25-inch HD drive holds exactly the + same number of bits per track as an 8-inch drive; a 3.5-inch HD drive + can hold 20% more, but we waste that space when using one to emulate an + 8-inch drive. In both cases we also waste the top three tracks, since + an 8-inch drive has only 77 tracks, not 80. + + The nonstandard FA and F9 data address marks available in single den- + sity on a real Model I with the WD1771 controller also need special + handling. A PC-style floppy disk controller can neither read nor write + sectors with such DAMs at all. This raises three issues: (1) It will + be impossible for you to read some Model I disks on your PC even if + your PC otherwise supports single density. In particular, Model I TRS- + DOS 2.3 directory tracks will be unreadable. (2) On writing in single + density, xtrs silently records a F9 or FA DAM as F8. (3) On reading in + single density with an emulated WD1771 (Model I mode only), F8 is + returned as FA. If you need more accurate behavior, the -truedam flag + will turn on error messages on attempts to write F9 or FA DAMs and will + turn off translation of F8 to FA on reading. + + Hint: Be sure to set the drive type correctly in your PC's BIOS. Linux + and xtrs rely on this information to know how fast your drives are + spinning and hence what data rate to use when reading and writing. All + 3.5-inch drives spin at 300 RPM. Newer 5.25-inch high-density capable + drives ("1.2MB" drives) normally always spin at 360 RPM. (Some can be + jumpered to slow down to 300 RPM when in double-density mode, but you + should not do that when plugging one into a PC.) Older 5.25-inch + drives that cannot do high density ("180KB", "360KB" or "720KB" + 5.25-inch drives) always spin at 300 RPM. All 8-inch drives spin at + 360 RPM. If you plug an 8-inch drive into a PC (this requires a 50-pin + to 34-pin adaptor cable), tell your BIOS that it is a 5.25-inch 1.2MB + drive. + +Emulated hard disks + xtrs can emulate a hard disk in a file in one of two ways: it can use a + special, xtrs-specific LDOS driver called XTRSHARD/DCT, or it can emu- + late the Radio Shack hard drive controller (based on the Western Digi- + tal WD1010) and use the native drivers for the original hardware. + + Using XTRSHARD/DCT + + The XTRSHARD/DCT driver has been tested and works under both LDOS 5.3.1 + for Model I or III and TRSDOS/LS-DOS 6.3.1 for Model 4/4P. It may or + may not work under earlier LDOS versions. It definitely will not work + under other TRS-80 operating systems or with emulators other than xtrs. + The hard disk format was designed by Matthew Reed for his Model I/III + and Model 4 emulators; xtrs duplicates the format so that users can + exchange hard drive images across the emulators. + + To use XTRSHARD/DCT, first run the mkdisk program under Unix to create + a blank hard drive (.hdv) file. Typical usage would be: mkdisk -h + mydisk.hdv. See the mkdisk(1) man page for other options. + + Second, link the file to an appropriate name. XTRSHARD/DCT supports up + to eight hard drives, with names of the form hardM-U, where M is the + TRS-80 model (1, 3, or 4; in this case Model 4P also uses M=4) and U is + a unit number from 0 to 7. It looks for these files in the same direc- + tory as the floppy disk files diskM-U. + + Third, make sure an appropriate version of LDOS is in emulated floppy + drive 0, and the supplied file utility.dsk is in another emulated + floppy drive. Boot LDOS. If you are using Model I LDOS 5.3.1, patch a + bug in the FORMAT command by typing PATCH FORMAT/CMD.UTILITY M1FOR- + MAT/FIX. You need to apply this patch only once. It must not be + applied to Model III or Model 4/4P LDOS. + + Fourth, type the following commands. Here d is the LDOS drive number + you want to use for the hard drive (a typical choice would be 4) and u + is the unit number you chose when naming the file (most likely 0). + + SYSTEM (DRIVE=d,DRIVER="XTRSHARD",ENABLE) + Enter unit number ([0]-7): u + FORMAT d (DIR=1) + + Answer the questions asked by FORMAT as you prefer. The DIR=1 parame- + ter to FORMAT is optional; it causes the hard drive's directory to be + on track 1, making the initial size of the image smaller. You can + repeat these steps with different values of d and u to have more than + one hard drive. + + Finally, it's a good idea to give the SYSTEM (SYSGEN) command (Model + I/III) or SYSGEN command (Model 4/4P). This command saves the SYSTEM + settings, so the drive will be available again the next time you reboot + or restart the emulator. If you need to access the hard disk file + after booting from a floppy that hasn't been SYSGENed, simply use the + same SYSTEM command(s) again, but don't FORMAT. You can freely use a + different drive number or (if you renamed the hard disk file) a differ- + ent unit number. + + The F7 key currently doesn't allow XTRSHARD/DCT disk changes to be rec- + ognized, but you can change to a different hard disk file for the same + unit by renaming files as needed and rebooting LDOS. + + Technical note: XTRSHARD/DCT is a small Z-80 program that implements + all the required functions of an LDOS disk driver. Instead of talking + to a real (or emulated) hard disk controller, however, it uses special + support in xtrs that allows Z-80 programs to open, close, read, and + write Unix files directly. This support is described further in the + "Data import and export" section below. + + Using native hard disk drivers + + Beginning in version 4.1, xtrs also emulates the Radio Shack hard disk + controller (based on the Western Digital WD1010) and will work with the + native drivers for this hardware. This emulation uses the same hard + drive (.hdv) file format that XTRSHARD/DCT does. With LDOS/LS-DOS, the + RSHARDx/DCT and TRSHD/DCT drivers are known to work. With Montezuma + CP/M 2.2, the optional Montezuma hard disk drivers are known to work. + The hard disk drivers for NEWDOS/80 and for Radio Shack CP/M 3.0 should + work, but they have not yet been tested at this writing. Any bugs + should be reported. + + To get started, run the mkdisk program under Unix to create a blank + hard drive (.hdv) file. Typical usage would be: mkdisk -h mydisk.hdv. + See the mkdisk(1) man page for other options. + + Second, link the file to an appropriate name. The WD1010 emulation + supports up to four hard drives, with names of the form hardM-U, where + M is the TRS-80 model (1, 3, 4, or 4p) and U is a unit number from 0 to + 3. It looks for these files in the same directory as the floppy disk + files diskM-U. If no such files are present, xtrs disables the WD1010 + emulation. + + Note that if hard drive unit 0 is present on a Model 4P (file + hard4p-0), the Radio Shack boot ROM will always try to boot from it, + even if the operating system does not support booting from a hard + drive. If you have this problem, either hold down F2 while booting to + force the ROM to boot from floppy, or simply avoid using unit number 0. + Stock TRSDOS/LS-DOS 6 systems do not support booting from a hard drive; + M.A.D. Software's HBUILD6 add-on to LS-DOS for hard drive booting + should work, but is untested. Montezuma CP/M 2.2 does boot from the + emulated hard drive. + + Finally, obtain the correct driver for the operating system you will be + using, read its documentation, configure the driver, and format the + drive. Detailed instructions are beyond the scope of this manual page. + +Data import and export + Several Z-80 programs for data import and export from various TRS-80 + operating systems are included with xtrs on two emulated floppy images. + These programs use special support in the emulator to read and write + external Unix files, discussed further at the end of this section. + + The emulated floppy utility.dsk contains some programs for transferring + data between the emulator and ordinary Unix files. IMPORT/CMD, + EXPORT/CMD, and SETTIME/CMD run on the emulator under Model I/III TRS- + DOS, Model I/III LDOS, Model I/III Newdos/80, and Model 4/4P TRSDOS/LS- + DOS 6; they may also work under other TRS-80 operating systems. Model + III TRSDOS users will have to use TRSDOS's CONVERT command to read + utility.dsk. + + IMPORT/CMD imports a Unix file and writes it to an emulated disk. + Usage: IMPORT [-lne] unixfile [trsfile]. The -n flag converts Unix + newlines (\n) to TRS-80 newlines (\r). The -l flag converts the Unix + filename to lower case, to compensate for TRS-80 operating systems such + as Newdos/80 that convert all command line arguments to upper case. + When using the -l flag, you can put a [ or up-arrow in front of a char- + acter to keep it in upper case. Give the -e flag if your TRS-80 oper- + ating system uses the Newdos/80 convention for representing the ending + record number in an open file control block. This should be detected + automatically for Newdos/80 itself and for TRSDOS 1.3, but you'll need + to give the flag for DOSPLUS and possibly other non-LDOS operating sys- + tems. If you need the flag but don't give it (or vice versa), imported + files will come out the wrong length. If the destination file is omit- + ted, IMPORT uses the last component of the Unix pathname, but with any + "." changed to "/" to match TRS-80 DOS file extension syntax. + + EXPORT/CMD reads a file from an emulated disk and exports it to a Unix + file. Usage: EXPORT [-lne] trsfile [unixfile]. The -n flag converts + TRS-80 newlines (\r) to Unix newlines (\n). The -l flag converts the + Unix filename to lower case. When using the -l flag, you can put a [ + or up-arrow in front of a character to keep it in upper case. Give the + -e flag if your TRS-80 operating system uses the Newdos/80 convention + for representing the ending record number in an open file control + block. This should be detected automatically for Newdos/80 itself and + for TRSDOS 1.3, but you'll need to give the flag for DOSPLUS and possi- + bly other non-LDOS operating systems. If you need the flag but don't + give it (or vice versa), exported files will come out the wrong length. + If the destination file is omitted, EXPORT uses the TRS-80 filename, + but with any "/" changed to "." to match Unix file extension syntax. + + SETTIME/CMD reads the date and time from Unix and sets the TRS-80 DOS's + date and time accordingly. + + The next several programs were written in Misosys C and exist in two + versions on utility.dsk. The one whose name ends in "6" runs on Model + 4 TRSDOS/LS-DOS 6.x; the other runs on LDOS 5.x and most other Model + I/III operating systems. + + CD/CMD (or CD6/CMD) changes xtrs's Unix working directory. Usage: CD + [-l] unixdir. The -l flag converts the Unix directory name to lower + case. When using the -l flag, you can put a [ or up-arrow in front of + a character to keep it in upper case. Running CD/CMD will change the + interpretation of any relative pathnames given to IMPORT or EXPORT. It + will also change the interpretation of disk names at the next disk + change, unless you specified an absolute pathname for xtrs's -diskdir + parameter. + + PWD/CMD (or PWD6/CMD) prints xtrs's Unix working directory. + + UNIX/CMD (or UNIX6/CMD) runs a Unix shell command. Usage: UNIX [-l] + unix command line. The -l flag converts the Unix command line to lower + case. When using the -l flag, you can put a [ or up-arrow in front of + a character to keep it in upper case. Standard I/O for the command + uses the xtrs program's standard I/O descriptors; it does not go to the + TRS-80 screen or come from the TRS-80 keyboard. + + MOUNT/CMD (or MOUNT6/CMD) is a convenience program that switches emu- + lated floppy disks in the drives. Usage: MOUNT [-l] filename U. The + -l flag converts the Unix filename to lower case. When using the -l + flag, you can put a [ or up-arrow in front of a character to keep it in + upper case. The filename is any Unix filename; U is a single digit, 0 + through 7. The command deletes the file diskM-U (where M is the TRS-80 + model) from the disk directory (see -diskdir option), replaces it with + a symbolic link to the given filename, and signals a disk change (as if + F7 had been pressed). + + UMOUNT/CMD (or UMOUNT6/CMD) is a convenience program that removes an + emulated floppy disk from a drive. Usage: UMOUNT U. U is a single + digit, 0 through 7. The command deletes the file diskM-U (where M is + the TRS-80 model) from the disk directory (see -diskdir option) and + signals a disk change (as if F7 had been pressed). + + The emulated floppy cpmutil.dsk contains import and export programs for + Montezuma CP/M, written by Roland Gerlach. It was formatted as a "Mon- + tezuma Micro Standard DATA disk (40T, SS, DD, 200K)," with 512-byte + sectors. Be careful to configure your CP/M to the proper disk format + and drive parameters (40 track, not 80), or you will have confusing + problems reading this disk. Documentation is included in the file + cpmutil.html and source code in the file cpmutil.tgz (a gzipped tar ar- + chive). See http://members.optusnet.com.au/~rgerlach/trs-80/cpmu- + til.html where you will sometimes find a newer version of the utilities + than is included with xtrs. + + The emulator implements a set of pseudo-instructions (emulator traps) + that give TRS-80 programs access to Unix files. The programs listed + above use them. If you would like to write your own such programs, the + traps are documented in the file trs imp exp.h. Assembler source code + for the existing programs is supplied in xtrshard.z, import.z, + export.z, and settime.z. You can also write programs that use the + traps in Misosys C, using the files xtrsemt.h and xtrsemt.ccc as an + interface; a simple example is in settime.ccc. + +Interrupts + The emulator supports only interrupt mode 1. It will complain if your + program enables interrupts after powerup without executing an IM 1 + instruction first. All Model I/III/4/4P software does this, as the + built-in peripherals in these machines support only IM 1. + + The Model I has a 40 Hz heartbeat clock interrupt, while the Model III + uses 30 Hz, and the Model 4/4P can run at either 30 Hz or 60 Hz. The + emulator approximates this rather well even on a system where clock + ticks come at some frequency that isn't divisible by the emulated fre- + quency (e.g., 100 Hz on Intel Linux), as long as the true frequency is + not slower than the emulated frequency. The emulator has a notion of + the absolute time at which each tick is supposed to occur, and it asks + the host system to wake it up at each of those times. The net result + is that some ticks may be late, but there are always the proper number + of ticks per second. For example, running in Model I mode on Intel + Linux you'd see this pattern: (tick, 30ms, tick, 20ms,...) instead of + seeing ticks every 25ms. + +Processor speed selection + A standard Model 4 has a software-controlled switch to select operation + at either 4.05504 MHz (with heartbeat clock at 60 Hz) or 2.02752 MHz + (with heartbeat clock at 30 Hz). xtrs emulates this feature. + + Model I's were often modified to operate at higher speeds than the + standard 1.77408 MHz. With one common modification, writing a 1 to + port 0xFE would double the speed to 3.54816 MHz, while writing a 0 + would set the speed back to normal. The heartbeat clock runs at 40 Hz + in either case. xtrs emulates this feature as well. + +Sound + Sound support uses the Open Sound System /dev/dsp device, standard on + Linux and available on many other Unix versions as well. This support + is compiled in automatically on Linux; if you have OSS on another ver- + sion of Unix, you'll need to define the symbol HAVE OSS in your Make- + file or in trs cassette.c. Any time TRS-80 software tries to write + non-zero values to the cassette port (or the Model 4/4P optional sound + port) with the cassette motor off, it is assumed to be trying to make + sounds and xtrs opens /dev/dsp. It automatically closes the device + again after a few seconds of silence. + + If you are playing a game with sound, you'll want to use the -autodelay + flag to slow down instruction emulation to approximately the speed of a + real TRS-80. If you don't do this, sound will still play correctly, + but the gameplay may be way too fast and get ahead of the sound. + + On the other hand, if your machine is a bit too slow, you'll hear gaps + and pops in the sound when the TRS-80 program lags behind the demand of + the sound card for more samples. The -autodelay feature includes a + small speed boost whenever a sound starts to play to try to prevent + this, but if the boost is too much or too little, you might either find + that the game runs too fast when a lot of sound is playing, or that the + sound has gaps in it anyway. If your sound has gaps, you can try + reducing the sample rate with the -samplerate flag. + + The Orchestra-85 music synthesis software will run under xtrs's Model I + emulation, and the Orchestra-90 software will run with Model III oper- + ating systems under xtrs's Model III, 4, or 4P emulation. For best + results, use Orchestra-90 and the Model 4 emulation, as this lets the + software run at the highest emulated clock rate (4 MHz) and thus gener- + ate the best sound. If you want to run Orchestra-85 instead, you can + tell it that you have a 3.5 MHz clock speedup with enable sequence + 3E01D3FE and disable sequence 3E00D3FE; this will let the software run + twice as fast as on an unmodified Model I and generate better sound. + There is no need to use xtrs's -autodelay flag when running Orches- + tra-85/90, but you might want to specify a small fixed delay to keep + from getting excessive key repeat. + +Mouse + A few Model 4 programs could use a mouse, such as the shareware hi-res + drawing program MDRAW-II. The program XTRSMOUS/CMD on the utility disk + (utility.dsk) is a mouse driver for Model 4/4P mode that should work + with most such programs. xtrs does not emulate the actual mouse hard- + ware (a serial mouse plugged into the Model 4 RS-232 port), so the + original mouse drivers will not work under xtrs. Instead, XTRSMOUS + accesses the X mouse pointer using an emulator trap. XTRSMOUS imple- + ments the same TRSDOS/LS-DOS 6 SVC interface as the David Goben and + Matthew Reed mouse drivers. (It does not implement the interface of the + older Scott McBurney mouse driver, which may be required by some pro- + grams.) + + By default XTRSMOUS installs itself in high memory. This is done + because MDRAW-II tests for the presence of a mouse by looking to see + whether the mouse SVC is vectored to high memory. If the driver is + installed in low memory, MDRAW thinks it is not there at all. If you + use mouse-aware programs that don't have this bug, or if you edit the + first line of MDRAW to remove the test, you can install XTRSMOUS in low + memory using the syntax "XTRSMOUS (LOW)". + +Time of day clock + Several battery-backed time of day clocks were sold for the various + TRS-80 models, including the TimeDate80, TChron1, TRSWatch, and T- + Timer. They are essentially all the same hardware, but reside at a few + different port ranges. xtrs currently emulates them at port ranges + 0x70-0x7C and 0xB0-0xBC. The T-Timer port range at 0xC0-0xCC conflicts + with the Radio Shack hard drive controller and is not emulated. + + These clocks return only a 2-digit year, and it is unknown what their + driver software will do in the year 2000 and beyond. If you have soft- + ware that works with one of them, please send email to report what hap- + pens when it is used with xtrs. + + Also see SETTIME/CMD in the "Data import and export" section above for + another way to get the correct time into a Z-80 operating system run- + ning under xtrs. + + Finally, you might notice that LDOS/LS-DOS always magically knows the + correct date when you boot it (but not the time). When you first power + up the emulated TRS-80, xtrs dumps the date into the places in memory + where LDOS and LS-DOS normally save it across reboots, so it looks to + the operating system as if you rebooted after setting the date. + +Joystick + Pressing a key on a PC numeric keypad with NumLock disengaged emulates + the Alpha Products joystick. See the Keys section above for details. + The emulated joystick is mapped only at port 0, to avoid conflicts with + other devices. The joystick emulation could be made to work with real + joysticks using the X input extension, but this is not implemented yet. + +Running games + Some games run rather well under xtrs now, provided that your machine + is fast enough to run the emulation in real time and that you choose + the right command line options. "Galaxy Invaders Plus" by Big 5 Soft- + ware is particularly good. You will usually want to turn on autodelay, + and if your machine is slow you may need to reduce the sound sample + rate. Running your X server in 8-bit/pixel mode also seems to help in + some cases. Example command lines: + + startx -- -bpp 8 + xtrs -autodelay + + If you have a slow machine and the sound breaks up, it is possible that + your machine is not fast enough to generate samples at the default rate + of 44,100 Hz. If you think this may be happening, try "-samplerate + 11025" or even "-samplerate 8000". + +Options + Defaults for all options can be specified using the standard X resource + mechanism, and the class name for xtrs is "Xtrs". + + -display display + Set your X display to display. The default is to use the DISPLAY + environment variable. + + -iconic + Start with the xtrs window iconified. + + -background color + -bg color + Specifies the background color of the xtrs window. + + -foreground color + -fg color + Specifies the foreground color of the xtrs window. + + -title titletext + Use titletext in the window title bar instead of the program + name. + + -borderwidth width + Put a border of width pixels around the TRS-80 display. The + default is 2. + + -scale xfac[,yfac] + Multiply the horizontal and vertical window size by xfac and + yfac, respectively. Possible values are integers in the range + [1,4] for xfac and [1,8] for yfac. Defaults are xfac=1 and + yfac=2*xfac. + + -resize + In Model III or 4/4P mode, resize the X window whenever the emu- + lated display mode changes between 64x16 text (or 512x192 graph- + ics) and 80x24 text (or 640x240 graphics). This is the default + in Model III mode, since 80x24 text is not available and the + 640x240 graphics add-on card is seldom used. + + -noresize + In Model III or 4/4P mode, always keep the X window large enough + for 80x24 text or 640x240 graphics, putting a blank margin + around the outside when the emulated display mode is 64x16 text + or 512x192 graphics. This is the default in Model 4/4P mode, + since otherwise there is an annoying size switch during every + reboot. + + -charset name + Select among several sets of built-in character bitmaps. + + In Model I mode, five sets are available. The default, wider, is + a modified Model III set with characters 8 pixels wide; it looks + better on a modern computer screen with square pixels than the + real Model I fonts, which were 6 pixels wide. lcmod is the char- + acter set in the replacement character generator that was sup- + plied with the Radio Shack lower case modification. (It was + reconstructed partly from memory and may have some minor bit + errors.) stock is the character set in the stock character gen- + erator supplied with most upper case only machines. Since xtrs + currently always emulates the extra bit of display memory needed + to support lower case, this character set gives you the authen- + tic, unpleasant effect that real Model I users saw when they + tried to do homebrew lower case modifications without replacing + the character generator: lower case letters appear at an incon- + sistent height, and if you are using the Level II BASIC ROM dis- + play driver, upper case letters are replaced by meaningless sym- + bols. early is the same as stock, but with the standard ASCII + characters [, \, ], and ^ in the positions where most Model I's + had directional arrows. This was the default programming in the + Motorola character generator ROM that Radio Shack used, and a + few early machines were actually shipped with this ROM. + Finally, german or genie gives an approximate emulation of the + GENIE, a German Model I clone. Characters are 8 pixels wide, + and double width is supported even though later GENIE models did + not include it. + + In Model III, 4, and 4P modes, three sets are available: + katakana (the default for Model III) is the original Model III + set with Japanese Katakana characters in the alternate character + positions. This set was also used in early Model 4's. interna- + tional (the default for Model 4 and 4P) is a later Model 4 set + with accented Roman letters in the alternate positions. bold is + a bold set from a character generator ROM found in one Model + III, origin uncertain. + + -usefont + Use X fonts instead of the built-in character bitmaps. + + -nofont + Use the built-in character bitmaps, not a X font. This is the + default. + + -font fontname + If -usefont is also given, use the specified X font for normal + width characters. The default uses a common X fixed-width font: + "-misc-fixed-medium-r-normal--20-200-75-75-*-100-iso8859-1". + + -widefont fontname + If -usefont is also given, use the specified X font for double + width characters. The default uses a common X fixed-width font, + scaled to double width: "-misc-fixed-medium-r-nor- + mal--20-200-75-75-*-200-iso8859-1". + + -nomicrolabs + In Model I mode, emulate the HRG1B 384x192 hi-res graphics card. + In Model III mode or Model 4/4P mode, emulate the Radio Shack + hi-res card. This is now the default. + + -microlabs + In Model III or 4/4P mode, emulate the Micro Labs Grafyx Solu- + tion hi-res graphics card. Note that the Model III and Model + 4/4P cards from Micro Labs are very different from one another. + + -debug Enter zbx, the z80 debugger. + + -romfile filename + -romfile3 filename3 + -romfile4p filename4p + Use the romfile specified by filename in Model I mode, the rom- + file specified by filename3 in Model III and Model 4 mode, or + the romfile specified by filename4p in Model 4P mode, A romfile + can be either a raw binary dump, Intel hex format, or TRS-80 cmd + format (for example, a MODELA/III file). If you do not set this + option or the corresponding X resource, a default established at + compile time is used (if any); see Makefile.local for instruc- + tions on compiling in default romfiles or default romfile names. + + -model m + Specifies which TRS-80 model to emulate. Values accepted are 1 + or I (Model I), 3 or III (Model III), 4 or IV (Model 4), and 4P + or IVP (Model 4P). Model I is the default. + + -delay d + A crude speed control. After each Z-80 instruction, xtrs busy- + waits for d iterations around an empty loop. A really smart C + optimizer might delete this loop entirely, so it's possible that + this option won't work if you compile xtrs with too high an + optimization level. The default delay is 0. + + -autodelay + Dynamically adjusts the value of -delay to run instructions at + roughly the same rate as a real machine. The tracking is only + approximate, but it can be useful for running games. + + -noautodelay + Turn off -autodelay. This is the default. + + -keystretch cycles + Fine-tune the keyboard behavior. To prevent keystrokes from + being lost, xtrs "stretches" the intervals between key transi- + tions, so that the Z-80 program has time to see each transition + before the next one occurs. Whenever the Z-80 program reads the + keyboard matrix and sees an emulated key go up or down, xtrs + waits cycles Z-80 clock cycles (T-states) before it allows the + program to see another key transition. Key transitions that are + received during the waiting period or when the Z-80 program is + not reading the keyboard are held in a queue. The default + stretch value is 4000 cycles; it should seldom if ever be neces- + sary to change it. + + -shiftbracket + Emulate [, \, ], ^, and as shifted keys, and {, |, }, and ~ as + unshifted. This is the default in Model 4 and 4P modes, and it + works well with the keyboard driver in Model 4 TRSDOS/LS-DOS 6. + + -noshiftbracket + Emulate [, \, ], ^, and as unshifted keys, and {, |, }, and ~ + as shifted. This is the default in Model I and III modes, and + it works well with many TRS-80 keyboard drivers. With some key- + board drivers these keys do not work at all, however. + + -diskdir dir + Specify the directory containing floppy and hard disk images. + If the value starts with "~/" (or is just "~"), it is relative + to your home directory. The default value is ".". + + -doubler type + Specify what type of double density adaptor to emulate (Model I + mode only). The type may be percom, radioshack (or tandy), + both, or none. The type may be abbreviated to one character. + The default is both, which causes the double density adaptor + emulation to respond to the special commands of both the Percom + and Radio Shack cards. + + -doublestep + Make all real floppy drives double-step, allowing access to + 35-track or 40-track media in an 80-track drive. Linux only. + See the Floppy Disks section for limitations. + + -nodoublestep + Turn off double-step mode for all real floppy drives. Linux + only. This is the default. + + -stepmap s0,s1,s2,s3,s4,s5,s6,s7 + Selectively set double-step mode for individual real floppy + drives. If sU is 2 and diskM-U is a real drive, the drive will + be double-stepped; if sU is 1, it will be single-stepped. You + can omit values from the end of the list; those drives will get + the default value set by -doublestep or -nodoublestep. + + -sizemap z0,z1,z2,z3,z4,z5,z6,z7 + Selectively set whether drives are emulated as 5-inch or 8-inch; + see the section "Emulated 8-inch floppy disks" above. If zU is + 5, the drive will appear to Z-80 software as 5-inch; if 8, as + 8-inch. The default setting (as reflected in the documentation + above) is 5,5,5,5,8,8,8,8. You can omit values from the end of + the list; those drives will get the default values. Setting one + or more of the first four drives to 8-inch may be useful for + CP/M software that supports 8-inch drives. You can also use + XTRS8/DCT with 8-inch drives in the first four positions; even + though the prompt suggests the unit number must be 4-7, numbers + 0-3 are accepted. XTRS8 does not check whether the unit you've + selected is really being emulated as an 8-inch drive, however; + you'll simply get errors during FORMAT if you get this wrong. + + -truedam + Turn off the single density data address mark remapping kludges + described in the "Emulated floppy disks" and "Real floppy disks" + sections above. With this option given, the distinction between + F8 and FA data address marks is strictly observed on both writ- + ing and reading. This option is probably not useful unless you + need to deal with Model I disks that use the distinction as part + of a copy-protection scheme. See also "Common File Formats for + Emulated TRS-80 Floppy Disks", available at http://www.tim- + mann.org/trs80/dskspec.html. + + -notruedam + The opposite of -truedam. This setting is the default. + + -samplerate rate + Set the sample rate for new cassette wav files, direct cassette + I/O to the sound card, and game sound output to the sound card. + Existing wav files will be read or modified using their original + sample rate regardless of this flag. The default is 44,100 Hz. + See also the cassette(1) man page. + + -serial ttyname + Set the tty device to be used for I/O to the TRS-80's serial + port. The default is /dev/ttyS0 on Linux, /dev/tty00 on other + versions of Unix. Setting the name to be empty (-serial "") + emulates having no serial port. + + -switches value + Set the sense switches on the Model I serial port card. This + option is meaningful only in Model I mode, and only when the + -serial option is not set to "". The default value is 0x6f, + which Radio Shack software conventionally interprets as 9600 + bps, 8 bits/word, no parity, 1 stop bit. + + -emtsafe + Disable emulator traps (see "Data import and export") that could + write to host files other than disk images in the original + diskdir. + + -noemtsafe + The opposite of -emtsafe. This setting is the default. + +Additional resources + There are many other TRS-80 resources available on the Web, including + shareware and freeware emulators that run under MSDOS and other operat- + ing systems, software for converting TRS-80 physical media to the emu- + lator's disk file format, ROM images, and TRS-80 software that has + already been converted. For pointers, see http://www.tim- + mann.org/trs80.html. + +Bugs and limitations + The emulated serial port's modem status and control signals are not + tied to the signals on the real serial port, because the real signals + are not available to software through the Unix tty device interface. + The ability to check for parity, framing, and overrun errors and + receive an interrupt when one occurs is not emulated. Unix does not + support 2000, 3600, or 7200 baud, so these TRS-80 data rates are + remapped to 38400, 57600, and 115200 baud respectively. + + A better signal processing algorithm might help read real cassettes + more reliably, especially at 1500bps. + + Some features of the floppy disk controller are not currently emulated: + Force Interrupt with condition bits 0x01, 0x02, or 0x04 is not imple- + mented. Read Track is implemented only for DMK emulated floppies. The + multiple-sector flags in Read and Write are not implemented. The tim- + ing of returned sectors is emulated only for the Read Address command, + and not very accurately for JV1 or JV3. If a disk has more than one + sector with the same number on a track, xtrs will always see the first + (counting from the index hole) when reading or writing; a real machine + would see the next one to come under the head depending on the current + rotational position of the disk. Partially reformatting a track (which + TRS-80 programs like HyperZap and Model I Super Utility do to achieve + mixed density) is supported for DMK but not JV3; however, switching + densities while formatting (which Model III and 4 Super Utility do) + works on both DMK and JV3. + + Real physical floppy disks are supported only under Linux, because Unix + does not define a portable interface to the low-level floppy controller + functionality that xtrs needs. There are some limitations even under + Linux: Index holes are faked, not detected on the real disk, and the + timing of returned sectors is not emulated at all. Due to the limita- + tions of PC-style floppy disk controllers, when formatting a physical + floppy under xtrs, you cannot mix sectors of different sizes on the + same track, switch densities in the middle of a track, or reformat only + part of a track. However, xtrs can read and write to physical floppies + that have already been formatted in these ways (perhaps by a real + TRS-80). + + The extended JV3 limit of 5802 sectors is somewhat arbitrary. It could + be raised by generalizing the code to permit more than two blocks of + 2901, but this does not seem too useful. 5802 sectors is already + enough for a 3.5-inch HD (1.44MB) floppy, which the TRS-80 didn't sup- + port anyway. If you need more space, use emulated hard drives instead + of emulated floppies with huge numbers of tracks. + + XTRSHARD/DCT ignores the internal write-protected flag in hard drive + images, but a hard drive image can still be effectively write protected + by turning off its Unix write permission bits. + + The emulator uses a heuristic to decide what format a ROM file is in. + If a raw binary ROM image starts with 0x01, 0x05, or 0x22, it can be + misidentified as being in a different format. This is rather unlikely + to occur, as ROMs typically begin with 0xF3, the DI instruction. + + The joystick emulation could be made to work with real joysticks using + the X input extension, but this is not implemented yet. + + If you discover other bugs, write fixes for any of these, or make any + other enhancements, please let us know so that we can incorporate the + changes into future releases. + +Authors and acknowledgements + xtrs 1.0 was written by David Gingold and Alec Wolman. The current + version was revised and much extended by Timothy Mann (see http://tim- + mann.org/). See README and README.tpm for additional notes from the + authors. + + We also thank the following people for their help. The JV1 and JV3 + floppy disk file formats were designed by Jeff Vavasour, originally for + his MSDOS-based TRS-80 emulators. The DMK format was designed by David + Keil for his MSDOS-based TRS-80 emulator. The hard disk file format + was designed by Matthew Reed for his MSDOS-based TRS-80 emulators. Al + Petrofsky and Todd P. Cromwell III supplied font data. Roland Gerlach + contributed the CP/M import and export programs as well as several bug + reports and fixes for the emulator itself. Ulrich Mueller added the + -borderwidth option, improved the -scale option and the bitmap font + scaling, ported the import, export, and settime utilities to Newdos/80, + and contributed the HRG1B emulation. Branden Robinson supplied the + first version of the cassette man page, fixed Makefile bugs, translated + cassette to the Bourne shell, and implemented watchpoints in zbx. Mark + McDougall provided documentation for the Micro Labs Grafyx Solution + card. Jenz Guenther added the -title option and contributed code to + emulate the GENIE (German Model I clone). Joe Peterson contributed + code to emulate the TimeDate80 and the -emtsafe feature. Denis Leconte + contributed part of the -scale implementation. + + xtrs(1) diff --git a/xtrs8.dct b/xtrs8.dct Binary files differnew file mode 100644 index 0000000..669605e --- /dev/null +++ b/xtrs8.dct diff --git a/xtrs8.lst b/xtrs8.lst new file mode 100644 index 0000000..e5675b3 --- /dev/null +++ b/xtrs8.lst @@ -0,0 +1,547 @@ + 1: ;*=*=* + 2: ; xtrs8/dct + 3: ; LDOS driver for xtrs emulation of 8" floppy + 4: ; + 5: ; Copyright (c) 1998, Timothy Mann + 6: ; + 7: ; This software may be copied, modified, and used for any + 8: ; purpose without fee, provided that (1) the above copyright + 9: ; notice is retained, and (2) modified versions are clearly + 10: ; marked as having been modified, with the modifier's name and + 11: ; the date included. + 12: ; + 13: ; Created 4-9-98 + 14: ; Last modified on Thu Apr 9 17:26:29 PDT 1998 by mann + 15: ;*=*=* + 16: + 17: + 18: ; Number of floppy drives xtrs allows + 19: 0008 ndrive equ 8 + 20: + 21: ; ASCII chars + 22: 000A LF equ 10 + 23: 000D CR equ 13 + 24: 0003 ETX equ 3 + 25: + 26: ; Model 4 SVC numbers + 27: 0064 @high equ 100 + 28: 000A @dsply equ 10 + 29: 0065 @flags equ 101 + 30: 000C @logot equ 12 + 31: 0052 @gtdcb equ 82 + 32: 0053 @gtmod equ 83 + 33: 0009 @keyin equ 9 + 34: + 35: ; Model I/III hard addresses + 36: 0125 m3flag$ equ 0125h ; 'I' in ROM on Model III + 37: 447B @logot1 equ 447bh + 38: 428A @logot3 equ 428ah + 39: 4467 @dsply1 equ 4467h + 40: 4467 @dsply3 equ 4467h + 41: 4049 high$1 equ 4049h + 42: 4411 high$3 equ 4411h + 43: 4758 cflag$1 equ 4758h + 44: 4758 cflag$3 equ 4758h + 45: 0040 @keyin1 equ 0040h + 46: 0040 @keyin3 equ 0040h + 47: 441F osver$3 equ 441fh + 48: + 49: ; Very undocumented! ugh! + 50: 4585 flop31 equ 4585h ;Model III LDOS 5.1.x floppy driver + 51: 4583 flop33 equ 4583h ;Model III LDOS 5.3.x floppy driver + 52: + 53: ;*=*=* + 54: ; Set origin to be safe on both LDOS 5 and 6 + 55: ;*=*=* + 56: 6000 org 6000h + 57: + 58: ;*=*=* + 59: ; Relocator for disk driver + 60: ;*=*=* + 61: 6000 ED533363 instal: ld (dct),de ;Save DCT address + 62: 6004 3A0A00 ld a,(000ah) ;Determine TRS-80 model + 63: 6007 FE40 cp 40h + 64: 6009 C2F960 jp nz,lsdos6 ;Model 4 (or other LS-DOS, I hope) + 65: 600C 3A2501 ld a,(m3flag$) + 66: 600F FE49 cp 'I' + 67: 6011 CA8660 jp z,model3 ;Go if Model III + 68: ;*=*=* + 69: ; LDOS 5 Model I - See LS-DOS 6 version for comments + 70: ;*=*=* + 71: 6014 3ECD ld a,0cdh ;Insert Model I @LOGOT + 72: 6016 32E761 ld (logot),a + 73: 6019 217B44 ld hl,@logot1 + 74: 601C 22E861 ld (logot+1),hl + 75: 601F 214762 ld hl,hello_ + 76: 6022 CD6744 call @dsply1 + 77: 6025 3A5847 ld a,(cflag$1) + 78: 6028 CB5F bit 3,a ;System request? + 79: 602A CADC61 jp z,viaset + 80: 602D ED5B3363 ld de,(dct) + 81: 6031 7A ld a,d ;DRIVE= must be specified + 82: 6032 B3 or e + 83: 6033 CAD861 jp z,needdr + 84: 6036 21F062 asku1: ld hl,unit_ ;Ask which unit number + 85: 6039 CD6744 call @dsply1 + 86: 603C 213563 ld hl,unit + 87: 603F 010001 ld bc,100h + 88: 6042 CD4000 call @keyin1 + 89: 6045 DAE461 jp c,hitbrk + 90: 6048 C2E461 jp nz,hitbrk + 91: 604B 3A3563 ld a,(unit) + 92: 604E FE30 cp '0' + 93: 6050 38E4 jr c,asku1 + 94: 6052 FE38 cp '0'+ndrive + 95: 6054 30E0 jr nc,asku1 + 96: 6056 114D64 ld de,modnam ;Module already loaded? + 97: 6059 2A4940 ld hl,(high$1) + 98: 605C CD1562 call xgtmod + 99: 605F CA9761 jp z,setdct + 100: 6062 113F64 ld de,fd1 ;Find fdubl driver + 101: 6065 2A4940 ld hl,(high$1) + 102: 6068 CD1562 call xgtmod + 103: 606B C2D061 jp nz,needfd ;go if missing + 104: 606E 225764 ld (flop),hl + 105: 6071 2A4940 ld hl,(high$1) + 106: 6074 223163 ld (newend),hl + 107: 6077 112C00 ld de,length + 108: 607A 97 sub a + 109: 607B ED52 sbc hl,de + 110: 607D 224940 ld (high$1),hl + 111: 6080 CDEE61 call relo + 112: 6083 C38961 jp move + 113: ;*=*=* + 114: ; LDOS 5 Model III + 115: ;*=*=* + 116: 6086 model3: + 117: 6086 3ECD ld a,0cdh ;Insert Model III @LOGOT + 118: 6088 32E761 ld (logot),a + 119: 608B 218A42 ld hl,@logot3 + 120: 608E 22E861 ld (logot+1),hl + 121: 6091 214762 ld hl,hello_ + 122: 6094 CD6744 call @dsply3 + 123: 6097 3A5847 ld a,(cflag$3) + 124: 609A CB5F bit 3,a ;System request? + 125: 609C CADC61 jp z,viaset + 126: 609F ED5B3363 ld de,(dct) + 127: 60A3 7A ld a,d ;DRIVE= must be specified + 128: 60A4 B3 or e + 129: 60A5 CAD861 jp z,needdr + 130: 60A8 21F062 asku3: ld hl,unit_ ;Ask which unit number + 131: 60AB CD6744 call @dsply3 + 132: 60AE 213563 ld hl,unit + 133: 60B1 010001 ld bc,100h + 134: 60B4 CD4000 call @keyin3 + 135: 60B7 DAE461 jp c,hitbrk + 136: 60BA C2E461 jp nz,hitbrk + 137: 60BD 3A3563 ld a,(unit) + 138: 60C0 FE30 cp '0' + 139: 60C2 38E4 jr c,asku3 + 140: 60C4 FE38 cp '0'+ndrive + 141: 60C6 30E0 jr nc,asku3 + 142: 60C8 114D64 ld de,modnam ;Module already loaded? + 143: 60CB 2A1144 ld hl,(high$3) + 144: 60CE CD1562 call xgtmod + 145: 60D1 CA9761 jp z,setdct + 146: ; + 147: ; Doesn't work on Model III: + 148: ; ld de,fd3 ;Find floppy driver + 149: ; ld hl,(high$3) + 150: ; call xgtmod + 151: ; jp nz,needfd ;go if missing + 152: ; + 153: ; Cheat instead: + 154: 60D4 3A1F44 ld a,(osver$3) + 155: 60D7 FE51 cp 51h + 156: 60D9 218545 ld hl,flop31 + 157: 60DC 2803 jr z,gotit + 158: 60DE 218345 ld hl,flop33 + 159: 60E1 gotit: + 160: ; + 161: 60E1 225764 ld (flop),hl + 162: 60E4 2A1144 ld hl,(high$3) + 163: 60E7 223163 ld (newend),hl + 164: 60EA 112C00 ld de,length + 165: 60ED 97 sub a + 166: 60EE ED52 sbc hl,de + 167: 60F0 221144 ld (high$3),hl + 168: 60F3 CDEE61 call relo + 169: 60F6 C38961 jp move + 170: + 171: ;*=*=* + 172: ; LS-DOS 6 + 173: ;*=*=* + 174: 60F9 214762 lsdos6: ld hl,hello_ + 175: 60FC 3E0A ld a,@dsply ;Display hello + 176: 60FE EF rst 40 + 177: ;*=*=* + 178: ; Check if entry from SYSTEM command. + 179: ;*=*=* + 180: 60FF 3E65 ld a,@flags ;Get flags pointer into IY + 181: 6101 EF rst 40 + 182: 6102 FD7E02 ld a,(iy+'C'-'A') ;Get CFLAG$ + 183: 6105 CB5F bit 3,a ;System request? + 184: 6107 CADC61 jp z,viaset + 185: 610A ED5B3363 ld de,(dct) + 186: 610E 7A ld a,d ;DRIVE= must be specified + 187: 610F B3 or e + 188: 6110 CAD861 jp z,needdr + 189: ;*=*=* + 190: ; Ask which unit number + 191: ;*=*=* + 192: 6113 21F062 asku4: ld hl,unit_ ;Ask which unit number + 193: 6116 3E0A ld a,@dsply + 194: 6118 EF rst 40 + 195: 6119 213563 ld hl,unit + 196: 611C 010001 ld bc,100h + 197: 611F 3E09 ld a,@keyin + 198: 6121 EF rst 40 + 199: 6122 DAE461 jp c,hitbrk + 200: 6125 C2E461 jp nz,hitbrk + 201: 6128 3A3563 ld a,(unit) + 202: 612B FE30 cp '0' + 203: 612D 38E4 jr c,asku4 + 204: 612F FE38 cp '0'+ndrive + 205: 6131 30E0 jr nc,asku4 + 206: ;*=*=* + 207: ; Check if driver already loaded + 208: ;*=*=* + 209: 6133 114D64 ld de,modnam + 210: 6136 3E53 ld a,@gtmod + 211: 6138 EF rst 40 + 212: 6139 CA9761 jp z,setdct ;Already loaded, skip loading + 213: ;*=*=* + 214: ; Find system floppy driver + 215: ;*=*=* + 216: 613C 114464 ld de,fd4 + 217: 613F 3E53 ld a,@gtmod + 218: 6141 EF rst 40 + 219: 6142 C2D461 jp nz,curdl ;Fatal error if not found + 220: 6145 225764 ld (flop),hl + 221: ;*=*=* + 222: ; Obtain low memory driver pointer. Bizarre API here! + 223: ;*=*=* + 224: 6148 1E4B ld e,'K' ;Locate pointer to *KI DCB + 225: 614A 1649 ld d,'I' ; via @GTDCB SVC + 226: 614C 3E52 ld a,@gtdcb + 227: 614E EF rst 40 + 228: 614F C2D461 jp nz,curdl ;No error unless KI clobbered! + 229: 6152 2B dec hl ;Decrement to driver pointer + 230: 6153 56 ld d,(hl) ;P/u hi-order of pointer, + 231: 6154 2B dec hl ; decrement to and p/u + 232: 6155 5E ld e,(hl) ; lo-order of pointer + 233: ;*=*=* + 234: ; Check if driver will fit into [(LCPTR), X'12FF'] + 235: ;*=*=* + 236: 6156 E5 push hl ;Save address of pointer + 237: 6157 212C00 ld hl,length ;New pointer will be + 238: 615A 19 add hl,de ; pointer + LENGTH + 239: 615B 54 ld d,h ;Save a copy in DE + 240: 615C 5D ld e,l + 241: 615D 010113 ld bc,1301h ;If > 1300H, driver won't fit + 242: 6160 97 sub a ;Reset carry flag + 243: 6161 ED42 sbc hl,bc + 244: 6163 E1 pop hl ;Get back address of pointer + 245: 6164 300A jr nc,usehi ;Go if driver won't fit + 246: 6166 73 ld (hl),e ;Store new value of pointer + 247: 6167 23 inc hl + 248: 6168 72 ld (hl),d + 249: 6169 1B dec de ;Last byte of driver goes here + 250: 616A ED533163 ld (newend),de + 251: 616E 1816 jr dorelo + 252: ;*=*=* + 253: ; Put in high memory instead. + 254: ;*=*=* + 255: 6170 210000 usehi: ld hl,0 ;Get current HIGH$ + 256: 6173 45 ld b,l + 257: 6174 3E64 ld a,@high + 258: 6176 EF rst 40 + 259: 6177 C2E061 jp nz,nomem + 260: 617A 223163 ld (newend),hl ;Last byte of driver goes here + 261: 617D 112C00 ld de,length + 262: 6180 97 sub a ;Reset carry flag + 263: 6181 ED52 sbc hl,de ;Compute new HIGH$ + 264: 6183 3E64 ld a,@high ;Set new HIGH$ into the system + 265: 6185 EF rst 40 + 266: ;*=*=* + 267: ; Relocate internal references in driver. + 268: ; HL = address for last byte of driver. + 269: ;*=*=* + 270: 6186 CDEE61 dorelo: call relo + 271: ;*=*=* + 272: ; Move driver into low or high memory. + 273: ;*=*=* + 274: 6189 move: + 275: 6189 ED5B3163 ld de,(newend) ;Destination address + 276: 618D 217364 ld hl,dvrend ;Last byte of module + 277: 6190 012C00 ld bc,length ;Length of filter + 278: 6193 EDB8 lddr + 279: 6195 EB ex de,hl + 280: 6196 23 inc hl ;Bump to driver entry + 281: ;*=*=* + 282: ; Setup DCT + 283: ;*=*=* + 284: 6197 setdct: + 285: 6197 FD2A3363 ld iy,(dct) + 286: 619B FD7501 ld (iy+1),l ;Driver address + 287: 619E FD7402 ld (iy+2),h + 288: 61A1 FD360320 ld (iy+3),00100000b ;Flags: 8" floppy + 289: 61A5 3A3563 ld a,(unit) ;Xlate unit number to select code + 290: 61A8 E607 and 07h + 291: 61AA 4F ld c,a + 292: 61AB 0600 ld b,0 + 293: 61AD 213764 ld hl,utab + 294: 61B0 09 add hl,bc + 295: 61B1 7E ld a,(hl) + 296: 61B2 F640 or 01000000b ;Flags: dden capable, select code + 297: 61B4 FD7704 ld (iy+4),a + 298: 61B7 FD360500 ld (iy+5),0 ;current cylinder number + 299: 61BB FD36064C ld (iy+6),76 ;high cylinder number + 300: 61BF FD36070F ld (iy+7),0fh ;init to sden head/sec/gran config + 301: 61C3 FD360827 ld (iy+8),27h + 302: 61C7 FD360926 ld (iy+9),38 ;Directory cylinder (guess) + 303: + 304: 61CB 210000 ld hl,0 ;Successful completion + 305: 61CE 97 sub a + 306: 61CF C9 ret + 307: ;*=*=* + 308: 61D0 211363 needfd: ld hl,needfd_ + 309: 61D3 DD defb 0ddh + 310: 61D4 217B62 curdl: ld hl,curdl_ ;Other error + 311: 61D7 DD defb 0ddh + 312: 61D8 21D662 needdr: ld hl,needdr_ + 313: 61DB DD defb 0ddh + 314: 61DC 21AC62 viaset: ld hl,viaset_ + 315: 61DF DD defb 0ddh + 316: 61E0 218E62 nomem: ld hl,nomem_ + 317: 61E3 DD defb 0ddh + 318: 61E4 210A63 hitbrk: ld hl,hitbrk_ + 319: 61E7 3E0C logot: ld a,@logot + 320: 61E9 EF rst 40 + 321: 61EA 21FFFF ld hl,-1 ;Unuccessful completion + 322: 61ED C9 ret + 323: + 324: ;*=*=* + 325: ; Relocate internal references in driver. + 326: ; HL = address for last byte of driver. + 327: ;*=*=* + 328: 61EE 2A3163 relo: ld hl,(newend) + 329: 61F1 FD217464 ld iy,reltab ;Point to relocation tbl + 330: 61F5 117364 ld de,dvrend + 331: 61F8 97 sub a ;Clear carry flag + 332: 61F9 ED52 sbc hl,de + 333: 61FB 44 ld b,h ;Move to BC + 334: 61FC 4D ld c,l + 335: 61FD FD6E00 rloop: ld l,(iy) ;Get address to change + 336: 6200 FD6601 ld h,(iy+1) + 337: 6203 7C ld a,h + 338: 6204 B5 or l + 339: 6205 C8 ret z + 340: 6206 5E ld e,(hl) ;P/U address + 341: 6207 23 inc hl + 342: 6208 56 ld d,(hl) + 343: 6209 EB ex de,hl ;Offset it + 344: 620A 09 add hl,bc + 345: 620B EB ex de,hl + 346: 620C 72 ld (hl),d ;And put back + 347: 620D 2B dec hl + 348: 620E 73 ld (hl),e + 349: 620F FD23 inc iy + 350: 6211 FD23 inc iy + 351: 6213 18E8 jr rloop ;Loop till done + 352: + 353: ;*=*=* + 354: ; Search for existing copy of driver. + 355: ; Rough Model I/III emulation of Model 4 @GTMOD, + 356: ; hardcoded with driver address. + 357: ; Entry: HL = (HIGH$) + 358: ; DE => module name, terminated with a character <= 0x1f + 359: ; Exit Z: HL = driver address + 360: ; NZ: driver not found + 361: ;*=*=* + 362: 6215 23 xgtmod: inc hl + 363: 6216 7C ld a,h + 364: 6217 B5 or l + 365: 6218 2002 jr nz,xgtm1 + 366: 621A 3D dec a ;not found + 367: 621B C9 ret + 368: 621C 7E xgtm1: ld a,(hl) + 369: 621D FE18 cp 18h ;unconditional jr? + 370: 621F C0 ret nz ;not a module header + 371: 6220 D5 push de ;save desired name ptr + 372: 6221 E5 push hl ;save start address + 373: 6222 23 inc hl ;skip jr + 374: 6223 23 inc hl ;skip offset + 375: 6224 23 inc hl ;skip start address + 376: 6225 23 inc hl + 377: 6226 46 ld b,(hl) ;get name length + 378: 6227 23 inc hl + 379: 6228 1A xgtm2: ld a,(de) + 380: 6229 FE20 cp 20h + 381: 622B 3810 jr c,nextmd ;desired name shorter - skip + 382: 622D BE cp (hl) + 383: 622E 200D jr nz,nextmd ;character different - skip + 384: 6230 13 inc de + 385: 6231 23 inc hl + 386: 6232 10F4 djnz xgtm2 + 387: 6234 1A ld a,(de) + 388: 6235 FE20 cp 20h + 389: 6237 3004 jr nc,nextmd ;desired name longer - skip + 390: 6239 E1 pop hl ;same - found + 391: 623A D1 pop de + 392: 623B 97 sub a + 393: 623C C9 ret + 394: 623D E1 nextmd: pop hl ;get back start of module + 395: 623E 23 inc hl + 396: 623F 23 inc hl + 397: 6240 5E ld e,(hl) ;pointer to last byte + 398: 6241 23 inc hl + 399: 6242 56 ld d,(hl) + 400: 6243 EB ex de,hl + 401: 6244 D1 pop de + 402: 6245 18CE jr xgtmod + 403: + 404: ;*=*=* + 405: ; Messages and globals + 406: ;*=*=* + 407: 6247 58545253 hello_: defb 'XTRS8 - Emulated 8" floppy driver for xtrs - 4/9/98',CR + 38202D20 + 456D756C + 61746564 + 20382220 + 666C6F70 + 70792064 + 72697665 + 7220666F + 72207874 + 7273202D + 20342F39 + 2F39380D + 408: 627B 4C532D44 curdl_: defb 'LS-DOS is curdled!',CR + 4F532069 + 73206375 + 72646C65 + 64210D + 409: 628E 48696768 nomem_: defb 'High memory is not available!',CR + 206D656D + 6F727920 + 6973206E + 6F742061 + 7661696C + 61626C65 + 210D + 410: 62AC 4D757374 viaset_:defb 'Must install via SYSTEM (DRIVE=,DRIVER=)!',CR + 20696E73 + 74616C6C + 20766961 + 20535953 + 54454D20 + 28445249 + 56453D2C + 44524956 + 45523D29 + 210D + 411: 62D6 44524956 needdr_:defb 'DRIVE= must be specified!',CR + 453D206D + 75737420 + 62652073 + 70656369 + 66696564 + 210D + 412: 62F0 456E7465 unit_: defb 'Enter unit number (4-7): ',ETX + 7220756E + 6974206E + 756D6265 + 72202834 + 2D37293A + 2003 + 413: 630A 41626F72 hitbrk_:defb 'Aborted!',CR + 74656421 + 0D + 414: 6313 46445542 needfd_:defb 'FDUBL must be loaded first!',CR + 4C206D75 + 73742062 + 65206C6F + 61646564 + 20666972 + 7374210D + 415: 632F 0000 lcptr: defw 0 + 416: 6331 0000 newend: defw 0 + 417: 6333 0000 dct: defw 0 + 418: 6335 unit: defs 2 + 419: 6337 errbuf: defs 256 + 420: 6437 01020408 utab: defb 1,2,4,8,3,5,6,7 + 03050607 + 421: 643F 24464444 fd1: defb '$FDD',ETX + 03 + 422: 6444 24464403 fd4: defb '$FD',ETX + 423: + 424: ; + 425: ; Driver - just a tiny wrapper around LDOS dden floppy driver + 426: ; + 427: + 428: 6448 180C entry: jr begin ;The driver starts with the + 429: 644A 7364 defw dvrend ; DOS standard header + 430: 644A rx00 equ $-2 + 431: 644C 05 defb modptr-modnam ;Length of name + 432: 644D 78747273 modnam: defb 'xtrs8' ;Name for @GTMOD requests + 38 + 433: 6452 0000 modptr: defw 0 ;These pointers are unused, but 1st byte MBZ + 434: 6454 0000 defw 0 + 435: + 436: 6456 CD0000 begin: call $-$ ;call the real driver + 437: 6457 flop equ $-2 + 438: 6459 F5 push af + 439: 645A FDCB036E bit 5,(iy+3) ;8" drive? + 440: 645E 2812 jr z,done ;go if not + 441: 6460 01491D ld bc,1d49h ;init for dden + 442: 6463 FDCB0376 bit 6,(iy+3) ;dden? + 443: 6467 2003 jr nz,ldden ;go if so + 444: 6469 01270F ld bc,0f27h + 445: 646C FD7007 ldden: ld (iy+7), b + 446: 646F FD7108 ld (iy+8), c + 447: 6472 F1 done: pop af + 448: 6473 C9 ret + 449: + 450: 6473 dvrend equ $-1 + 451: 002C length equ $-entry + 452: 6474 4A640000 reltab: defw rx00,0 + 453: 6000 end instal + + + +Statistics: + + 76 symbols + 886 bytes + + + +Symbol Table: + +@dsply = a entry 6448 ndrive = 8 +@dsply1 =4467 errbuf 6337+ needdr 61d8 +@dsply3 =4467 etx = 3 needdr_ 62d6 +@flags = 65 fd1 643f needfd 61d0 +@gtdcb = 52 fd4 6444 needfd_ 6313 +@gtmod = 53 flop =6457 newend 6331 +@high = 64 flop31 =4585 nextmd 623d +@keyin = 9 flop33 =4583 nomem 61e0 +@keyin1 = 40 gotit 60e1 nomem_ 628e +@keyin3 = 40 hello_ 6247 osver3 =441f +@logot = c high1 =4049 relo 61ee +@logot1 =447b high3 =4411 reltab 6474 +@logot3 =428a hitbrk 61e4 rloop 61fd +asku1 6036 hitbrk_ 630a rx00 =644a +asku3 60a8 instal 6000 setdct 6197 +asku4 6113 lcptr 632f+ unit 6335 +begin 6456 ldden 646c unit_ 62f0 +cflag1 =4758 length = 2c usehi 6170 +cflag3 =4758 lf = a+ utab 6437 +cr = d logot 61e7 viaset 61dc +curdl 61d4 lsdos6 60f9 viaset_ 62ac +curdl_ 627b m3flag = 125 xgtm1 621c +dct 6333 model3 6086 xgtm2 6228 +done 6472 modnam 644d xgtmod 6215 +dorelo 6186 modptr 6452 +dvrend =6473 move 6189 diff --git a/xtrs8.z80 b/xtrs8.z80 new file mode 100644 index 0000000..c9ae8c9 --- /dev/null +++ b/xtrs8.z80 @@ -0,0 +1,453 @@ +;*=*=* +; xtrs8/dct +; LDOS driver for xtrs emulation of 8" floppy +; +; Copyright (c) 1998, Timothy Mann +; +; This software may be copied, modified, and used for any +; purpose without fee, provided that (1) the above copyright +; notice is retained, and (2) modified versions are clearly +; marked as having been modified, with the modifier's name and +; the date included. +; +; Created 4-9-98 +; Last modified on Thu Apr 9 17:26:29 PDT 1998 by mann +;*=*=* + + +; Number of floppy drives xtrs allows +ndrive equ 8 + +; ASCII chars +LF equ 10 +CR equ 13 +ETX equ 3 + +; Model 4 SVC numbers +@high equ 100 +@dsply equ 10 +@flags equ 101 +@logot equ 12 +@gtdcb equ 82 +@gtmod equ 83 +@keyin equ 9 + +; Model I/III hard addresses +m3flag$ equ 0125h ; 'I' in ROM on Model III +@logot1 equ 447bh +@logot3 equ 428ah +@dsply1 equ 4467h +@dsply3 equ 4467h +high$1 equ 4049h +high$3 equ 4411h +cflag$1 equ 4758h +cflag$3 equ 4758h +@keyin1 equ 0040h +@keyin3 equ 0040h +osver$3 equ 441fh + +; Very undocumented! ugh! +flop31 equ 4585h ;Model III LDOS 5.1.x floppy driver +flop33 equ 4583h ;Model III LDOS 5.3.x floppy driver + +;*=*=* +; Set origin to be safe on both LDOS 5 and 6 +;*=*=* + org 6000h + +;*=*=* +; Relocator for disk driver +;*=*=* +instal: ld (dct),de ;Save DCT address + ld a,(000ah) ;Determine TRS-80 model + cp 40h + jp nz,lsdos6 ;Model 4 (or other LS-DOS, I hope) + ld a,(m3flag$) + cp 'I' + jp z,model3 ;Go if Model III +;*=*=* +; LDOS 5 Model I - See LS-DOS 6 version for comments +;*=*=* + ld a,0cdh ;Insert Model I @LOGOT + ld (logot),a + ld hl,@logot1 + ld (logot+1),hl + ld hl,hello_ + call @dsply1 + ld a,(cflag$1) + bit 3,a ;System request? + jp z,viaset + ld de,(dct) + ld a,d ;DRIVE= must be specified + or e + jp z,needdr +asku1: ld hl,unit_ ;Ask which unit number + call @dsply1 + ld hl,unit + ld bc,100h + call @keyin1 + jp c,hitbrk + jp nz,hitbrk + ld a,(unit) + cp '0' + jr c,asku1 + cp '0'+ndrive + jr nc,asku1 + ld de,modnam ;Module already loaded? + ld hl,(high$1) + call xgtmod + jp z,setdct + ld de,fd1 ;Find fdubl driver + ld hl,(high$1) + call xgtmod + jp nz,needfd ;go if missing + ld (flop),hl + ld hl,(high$1) + ld (newend),hl + ld de,length + sub a + sbc hl,de + ld (high$1),hl + call relo + jp move +;*=*=* +; LDOS 5 Model III +;*=*=* +model3: + ld a,0cdh ;Insert Model III @LOGOT + ld (logot),a + ld hl,@logot3 + ld (logot+1),hl + ld hl,hello_ + call @dsply3 + ld a,(cflag$3) + bit 3,a ;System request? + jp z,viaset + ld de,(dct) + ld a,d ;DRIVE= must be specified + or e + jp z,needdr +asku3: ld hl,unit_ ;Ask which unit number + call @dsply3 + ld hl,unit + ld bc,100h + call @keyin3 + jp c,hitbrk + jp nz,hitbrk + ld a,(unit) + cp '0' + jr c,asku3 + cp '0'+ndrive + jr nc,asku3 + ld de,modnam ;Module already loaded? + ld hl,(high$3) + call xgtmod + jp z,setdct +; +; Doesn't work on Model III: +; ld de,fd3 ;Find floppy driver +; ld hl,(high$3) +; call xgtmod +; jp nz,needfd ;go if missing +; +; Cheat instead: + ld a,(osver$3) + cp 51h + ld hl,flop31 + jr z,gotit + ld hl,flop33 +gotit: +; + ld (flop),hl + ld hl,(high$3) + ld (newend),hl + ld de,length + sub a + sbc hl,de + ld (high$3),hl + call relo + jp move + +;*=*=* +; LS-DOS 6 +;*=*=* +lsdos6: ld hl,hello_ + ld a,@dsply ;Display hello + rst 40 +;*=*=* +; Check if entry from SYSTEM command. +;*=*=* + ld a,@flags ;Get flags pointer into IY + rst 40 + ld a,(iy+'C'-'A') ;Get CFLAG$ + bit 3,a ;System request? + jp z,viaset + ld de,(dct) + ld a,d ;DRIVE= must be specified + or e + jp z,needdr +;*=*=* +; Ask which unit number +;*=*=* +asku4: ld hl,unit_ ;Ask which unit number + ld a,@dsply + rst 40 + ld hl,unit + ld bc,100h + ld a,@keyin + rst 40 + jp c,hitbrk + jp nz,hitbrk + ld a,(unit) + cp '0' + jr c,asku4 + cp '0'+ndrive + jr nc,asku4 +;*=*=* +; Check if driver already loaded +;*=*=* + ld de,modnam + ld a,@gtmod + rst 40 + jp z,setdct ;Already loaded, skip loading +;*=*=* +; Find system floppy driver +;*=*=* + ld de,fd4 + ld a,@gtmod + rst 40 + jp nz,curdl ;Fatal error if not found + ld (flop),hl +;*=*=* +; Obtain low memory driver pointer. Bizarre API here! +;*=*=* + ld e,'K' ;Locate pointer to *KI DCB + ld d,'I' ; via @GTDCB SVC + ld a,@gtdcb + rst 40 + jp nz,curdl ;No error unless KI clobbered! + dec hl ;Decrement to driver pointer + ld d,(hl) ;P/u hi-order of pointer, + dec hl ; decrement to and p/u + ld e,(hl) ; lo-order of pointer +;*=*=* +; Check if driver will fit into [(LCPTR), X'12FF'] +;*=*=* + push hl ;Save address of pointer + ld hl,length ;New pointer will be + add hl,de ; pointer + LENGTH + ld d,h ;Save a copy in DE + ld e,l + ld bc,1301h ;If > 1300H, driver won't fit + sub a ;Reset carry flag + sbc hl,bc + pop hl ;Get back address of pointer + jr nc,usehi ;Go if driver won't fit + ld (hl),e ;Store new value of pointer + inc hl + ld (hl),d + dec de ;Last byte of driver goes here + ld (newend),de + jr dorelo +;*=*=* +; Put in high memory instead. +;*=*=* +usehi: ld hl,0 ;Get current HIGH$ + ld b,l + ld a,@high + rst 40 + jp nz,nomem + ld (newend),hl ;Last byte of driver goes here + ld de,length + sub a ;Reset carry flag + sbc hl,de ;Compute new HIGH$ + ld a,@high ;Set new HIGH$ into the system + rst 40 +;*=*=* +; Relocate internal references in driver. +; HL = address for last byte of driver. +;*=*=* +dorelo: call relo +;*=*=* +; Move driver into low or high memory. +;*=*=* +move: + ld de,(newend) ;Destination address + ld hl,dvrend ;Last byte of module + ld bc,length ;Length of filter + lddr + ex de,hl + inc hl ;Bump to driver entry +;*=*=* +; Setup DCT +;*=*=* +setdct: + ld iy,(dct) + ld (iy+1),l ;Driver address + ld (iy+2),h + ld (iy+3),00100000b ;Flags: 8" floppy + ld a,(unit) ;Xlate unit number to select code + and 07h + ld c,a + ld b,0 + ld hl,utab + add hl,bc + ld a,(hl) + or 01000000b ;Flags: dden capable, select code + ld (iy+4),a + ld (iy+5),0 ;current cylinder number + ld (iy+6),76 ;high cylinder number + ld (iy+7),0fh ;init to sden head/sec/gran config + ld (iy+8),27h + ld (iy+9),38 ;Directory cylinder (guess) + + ld hl,0 ;Successful completion + sub a + ret +;*=*=* +needfd: ld hl,needfd_ + defb 0ddh +curdl: ld hl,curdl_ ;Other error + defb 0ddh +needdr: ld hl,needdr_ + defb 0ddh +viaset: ld hl,viaset_ + defb 0ddh +nomem: ld hl,nomem_ + defb 0ddh +hitbrk: ld hl,hitbrk_ +logot: ld a,@logot + rst 40 + ld hl,-1 ;Unuccessful completion + ret + +;*=*=* +; Relocate internal references in driver. +; HL = address for last byte of driver. +;*=*=* +relo: ld hl,(newend) + ld iy,reltab ;Point to relocation tbl + ld de,dvrend + sub a ;Clear carry flag + sbc hl,de + ld b,h ;Move to BC + ld c,l +rloop: ld l,(iy) ;Get address to change + ld h,(iy+1) + ld a,h + or l + ret z + ld e,(hl) ;P/U address + inc hl + ld d,(hl) + ex de,hl ;Offset it + add hl,bc + ex de,hl + ld (hl),d ;And put back + dec hl + ld (hl),e + inc iy + inc iy + jr rloop ;Loop till done + +;*=*=* +; Search for existing copy of driver. +; Rough Model I/III emulation of Model 4 @GTMOD, +; hardcoded with driver address. +; Entry: HL = (HIGH$) +; DE => module name, terminated with a character <= 0x1f +; Exit Z: HL = driver address +; NZ: driver not found +;*=*=* +xgtmod: inc hl + ld a,h + or l + jr nz,xgtm1 + dec a ;not found + ret +xgtm1: ld a,(hl) + cp 18h ;unconditional jr? + ret nz ;not a module header + push de ;save desired name ptr + push hl ;save start address + inc hl ;skip jr + inc hl ;skip offset + inc hl ;skip start address + inc hl + ld b,(hl) ;get name length + inc hl +xgtm2: ld a,(de) + cp 20h + jr c,nextmd ;desired name shorter - skip + cp (hl) + jr nz,nextmd ;character different - skip + inc de + inc hl + djnz xgtm2 + ld a,(de) + cp 20h + jr nc,nextmd ;desired name longer - skip + pop hl ;same - found + pop de + sub a + ret +nextmd: pop hl ;get back start of module + inc hl + inc hl + ld e,(hl) ;pointer to last byte + inc hl + ld d,(hl) + ex de,hl + pop de + jr xgtmod + +;*=*=* +; Messages and globals +;*=*=* +hello_: defb 'XTRS8 - Emulated 8" floppy driver for xtrs - 4/9/98',CR +curdl_: defb 'LS-DOS is curdled!',CR +nomem_: defb 'High memory is not available!',CR +viaset_:defb 'Must install via SYSTEM (DRIVE=,DRIVER=)!',CR +needdr_:defb 'DRIVE= must be specified!',CR +unit_: defb 'Enter unit number (4-7): ',ETX +hitbrk_:defb 'Aborted!',CR +needfd_:defb 'FDUBL must be loaded first!',CR +lcptr: defw 0 +newend: defw 0 +dct: defw 0 +unit: defs 2 +errbuf: defs 256 +utab: defb 1,2,4,8,3,5,6,7 +fd1: defb '$FDD',ETX +fd4: defb '$FD',ETX + +; +; Driver - just a tiny wrapper around LDOS dden floppy driver +; + +entry: jr begin ;The driver starts with the + defw dvrend ; DOS standard header +rx00 equ $-2 + defb modptr-modnam ;Length of name +modnam: defb 'xtrs8' ;Name for @GTMOD requests +modptr: defw 0 ;These pointers are unused, but 1st byte MBZ + defw 0 + +begin: call $-$ ;call the real driver +flop equ $-2 + push af + bit 5,(iy+3) ;8" drive? + jr z,done ;go if not + ld bc,1d49h ;init for dden + bit 6,(iy+3) ;dden? + jr nz,ldden ;go if so + ld bc,0f27h +ldden: ld (iy+7), b + ld (iy+8), c +done: pop af + ret + +dvrend equ $-1 +length equ $-entry +reltab: defw rx00,0 + end instal diff --git a/xtrsemt.ccc b/xtrsemt.ccc new file mode 100644 index 0000000..5a8a029 --- /dev/null +++ b/xtrsemt.ccc @@ -0,0 +1,496 @@ +/* xtrsemt.ccc -- Misosys C interface to xtrs emulator traps */ +/* Copyright (c) 1997, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 14:40:25 PST 1998 by mann */ + +#include <errno.h> +#ifndef _TIME_T +#include <time.h> +#endif + +int +emt_system(cmd) + char *cmd; +{ +#asm + POP AF ;save return address + POP HL ;cmd to HL + PUSH HL ;restore stack + PUSH AF + DEFW 28EDH ;emt_system + LD H,B ;return exit status from BC + LD L,C + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +char * +emt_gtddir(buffer, bytes) + char *buffer; + int bytes; +{ +#asm + POP AF ;save return address + POP HL ;buffer to HL + POP BC ;bytes to BC + PUSH BC + PUSH HL + PUSH AF + DEFW 2AEDH ;emt_getddir + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0 +#endasm +} + +int +emt_stddir(fname) + char *fname; +{ +#asm + POP AF ;save return address + POP HL ;fname to HL + PUSH HL + PUSH AF + DEFW 2BEDH ;emt_setddir + LD HL,0 ;return 0 if OK + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0FFFFH ;return -1 +#endasm +} + + +int +emt_open(fname, oflag, mode) + char *fname; + int oflag; + int mode; +{ +#asm + POP AF ;save return address + POP HL ;fname to HL + POP BC ;oflag to BC + POP DE ;mode to DE + PUSH DE ;restore stack + PUSH BC + PUSH HL + PUSH AF + DEFW 30EDH ;emt_open + LD H,D ;return fd from DE + LD L,E + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +int +emt_close(fd) + int fd; +{ +#asm + POP AF ;save return address + POP DE ;fd to DE + PUSH DE + PUSH AF + DEFW 31EDH ;emt_close + LD HL,0 ;return 0 if no error + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0FFFFH ;return -1 +#endasm +} + +int +emt_read(fd, buffer, bytes) + int fd; + char *buffer; + int bytes; +{ +#asm + POP AF ;save return address + POP DE ;fd to DE + POP HL ;buffer to HL + POP BC ;bytes to BC + PUSH BC + PUSH HL + PUSH DE + PUSH AF + DEFW 32EDH ;emt_read + LD H,B ;return count from BC + LD L,C + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +int +emt_write(fd, buffer, bytes) + int fd; + char *buffer; + int bytes; +{ +#asm + POP AF ;save return address + POP DE ;fd to DE + POP HL ;buffer to HL + POP BC ;bytes to BC + PUSH BC + PUSH HL + PUSH DE + PUSH AF + DEFW 33EDH ;emt_write + LD H,B ;return count from BC + LD L,C + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +long +emt_lseek(fd, offset, whence) + int fd; + long offset; /* 4 bytes */ + int whence; +{ +#asm + PUSH IX + LD IX,4 ;point IX to argument frame, skipping + ADD IX,SP ; return address and old IX value + LD E,(IX+0) ;fd to DE + LD D,(IX+1) + LD HL,0 ;extend offset to 8 bytes, push on stack + PUSH HL + PUSH HL + LD L,(IX+4) ;high 2 bytes of 4-byte offset + LD H,(IX+5) + PUSH HL + LD L,(IX+2) ;low 2 bytes + LD H,(IX+3) + PUSH HL + LD HL,0 ;point HL to 8-byte offset + ADD HL,SP + LD C,(IX+6) ;whence to BC + LD B,(IX+7) + DEFW 34EDH ;emt_lseek + POP DE ;return tell value in BCDE + POP BC + POP HL ;ignore high-order 4 bytes + POP HL + POP IX + RET Z + LD H,0 ;error code to errno + LD L,A + LD (ERRNO),HL +#endasm +} + +int +emt_strerror(err, buffer, size) + int err; + char *buffer; + int size; +{ +#asm + POP AF ;save return address + POP DE ;err to DE temporarily + POP HL ;buffer to HL + POP BC ;size to BC + PUSH BC + PUSH HL + PUSH DE + PUSH AF + LD A,E ;move err to A + DEFW 35EDH ;emt_strerror + LD H,B + LD L,C + RET Z + LD B,0 ;new error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +time_t +emt_time(local) + int local; +{ +#asm + POP AF ;save return address + POP HL ;local flag to HL temporarily + PUSH HL + PUSH AF + LD A,L ;local flag to A + DEFW 36EDH ;emt_time + ;return value is in BCDE (time_t == long) +#endasm +} + +int +emt_dropen(fname) + char *fname; +{ +#asm + POP AF ;save return address + POP HL ;fname to HL + PUSH HL + PUSH AF + DEFW 37EDH ;emt_opendir + LD H,D ;return dirfd + LD L,E + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +int +emt_drclose(dirfd) + int dirfd; +{ +#asm + POP AF ;save return address + POP DE ;dirfd to DE + PUSH DE + PUSH AF + DEFW 38EDH ;emt_closedir + LD HL,0 ;return 0 if no error + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0FFFFH ;return -1 +#endasm +} + +int +emt_drread(dirfd, buffer, bytes) + int dirfd; + char *buffer; + int bytes; +{ +#asm + POP AF ;save return address + POP DE ;fd to DE + POP HL ;buffer to HL + POP BC ;bytes to BC + PUSH BC + PUSH HL + PUSH DE + PUSH AF + DEFW 39EDH ;emt_readdir + LD H,B + LD L,C + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +int +emt_chdir(fname) + char *fname; +{ +#asm + POP AF ;save return address + POP HL ;fname to HL + PUSH HL + PUSH AF + DEFW 3AEDH ;emt_chdir + LD HL,0 ;return 0 if OK + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0FFFFH ;return -1 +#endasm +} + + +char * +emt_getcwd(buffer, bytes) + char *buffer; + int bytes; +{ +#asm + POP AF ;save return address + POP HL ;buffer to HL + POP BC ;bytes to BC + PUSH BC + PUSH HL + PUSH AF + DEFW 3BEDH ;emt_getcwd + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0 ;return NULL +#endasm +} + +int +emt_misc(func) + int func; +{ +#asm + POP AF ;save return address + POP HL ;func to HL + PUSH HL + PUSH AF + LD A,L ;func to A + DEFW 3CEDH ;emt_misc, return HL +#endasm +} + +void +emt_4misc(func, hl, bc, de) + int func; + int *hl; + int *bc; + int *de; +{ +#asm + PUSH IX + LD IX,4 ;point IX to argument frame, skipping + ADD IX,SP ; return address and old IX value + LD L,(IX+4) ;bc arg to HL + LD H,(IX+5) + LD C,(HL) ;*bc to BC + INC HL + LD B,(HL) + LD L,(IX+6) ;de arg to HL + LD H,(IX+7) + LD E,(HL) ;*de to DE + INC HL + LD D,(HL) + LD L,(IX+2) ;hl arg to HL + LD H,(IX+3) + LD A,(HL) ;*hl to HL + INC HL + LD H,(HL) + LD L,A + LD A,(IX+0) ;func to A + DEFW 3CEDH ;emt_misc + PUSH HL ;save HL return + LD L,(IX+4) ;bc arg to HL + LD H,(IX+5) + LD (HL),C ;BC return to *bc + INC HL + LD (HL),B + LD L,(IX+6) ;de arg to HL + LD H,(IX+7) + LD (HL),E ;DE return to *de + INC HL + LD (HL),D + POP DE ;HL return to DE + LD L,(IX+2) ;hl arg to HL + LD H,(IX+3) + LD (HL),E ;DE (HL return) to *hl + INC HL + LD (HL),D + EX DE,HL ;HL return back to HL (not needed) + POP IX +#endasm +} + +int +emt_ftruncate(fd, length) + int fd; + long length; /* 4 bytes */ +{ +#asm + PUSH IX + LD IX,4 ;point IX to argument frame, skipping + ADD IX,SP ; return address and old IX value + LD E,(IX+0) ;fd to DE + LD D,(IX+1) + LD HL,0 ;extend length to 8 bytes, push on stack + PUSH HL + PUSH HL + LD L,(IX+4) ;high 2 bytes of 4-byte offset + LD H,(IX+5) + PUSH HL + LD L,(IX+2) ;low 2 bytes + LD H,(IX+3) + PUSH HL + LD HL,0 ;point HL to 8-byte offset + ADD HL,SP + DEFW 3DEDH ;emt_ftruncate + POP DE ;pop length from stack + POP BC + POP HL + POP HL + POP IX ;restore + RET Z + LD H,0 ;error code to errno + LD L,A + LD (ERRNO),HL +#endasm +} + +int +emt_dkopen(fname, oflag, mode) + char *fname; + int oflag; + int mode; +{ +#asm + POP AF ;save return address + POP HL ;fname to HL + POP BC ;oflag to BC + POP DE ;mode to DE + PUSH DE ;restore stack + PUSH BC + PUSH HL + PUSH AF + DEFW 3EEDH ;emt_opendisk + LD H,D ;return fd from DE + LD L,E + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC +#endasm +} + +int +emt_dkclose(fd) + int fd; +{ +#asm + POP AF ;save return address + POP DE ;fd to DE + PUSH DE + PUSH AF + DEFW 3FEDH ;emt_closedisk + LD HL,0 ;return 0 if no error + RET Z + LD B,0 ;error code to errno + LD C,A + LD (ERRNO),BC + LD HL,0FFFFH ;return -1 +#endasm +} + diff --git a/xtrsemt.h b/xtrsemt.h new file mode 100644 index 0000000..f0d733d --- /dev/null +++ b/xtrsemt.h @@ -0,0 +1,73 @@ +/* xtrsemt.h -- Misosys C interface to xtrs emulator traps */ +/* Copyright (c) 1997, Timothy Mann */ + +/* This software may be copied, modified, and used for any purpose + * without fee, provided that (1) the above copyright notice is + * retained, and (2) modified versions are clearly marked as having + * been modified, with the modifier's name and the date included. */ + +/* Last modified on Tue Dec 15 14:41:02 PST 1998 by mann */ + +#ifndef _TIME_T +#include <time.h> +#endif + +/* Some names are changed to keep them unique in the first seven characters */ +extern int emt_system(/* char *cmd */); +extern char* /*emt_getddir*/ emt_gtddir(/* char *buffer, int bytes */); +extern int /*emt_setddir*/ emt_stddir(/* char *fname */); +extern int emt_open(/* char *fname, int oflag, int mode */); +extern int emt_close(/* int fd */); +extern int emt_read(/* int fd, char *buffer, int bytes */); +extern int emt_write(/* int fd, char *buffer, int bytes */); +extern long emt_lseek(/* int fd, long offset, int whence */); +extern int emt_strerror(/* int err, char *buffer, int size */); +extern time_t emt_time(/* int local */); +extern int /*emt_opendir*/ emt_dropen(/* char *fname */); +extern int /*emt_closedir*/ emt_drclose(/* int dirfd */); +extern int /*emt_readdir*/ emt_drread(/*int dirfd, char *buffer, int bytes*/); +extern int emt_chdir(/* char *fname */); +extern char* emt_getcwd(/* char *buffer, int bytes */); +extern int emt_misc(/* int func */); +extern void emt_4misc(/* int func, int *hl, int *bc, int *de */); +extern int emt_ftruncate(/* int fd, long length */); +extern int /*emt_opendisk*/ emt_dkopen(/* char *fname, int oflag, int mode */); +extern int /*emt_closedisk*/ emt_dkclose(/* int fd */); + +/* oflag values for emt_open and emt_opendisk */ +#define EO_ACCMODE 03 +#define EO_RDONLY 00 +#define EO_WRONLY 01 +#define EO_RDWR 02 +#define EO_CREAT 0100 +#define EO_EXCL 0200 +#define EO_TRUNC 01000 +#define EO_APPEND 02000 + +/* local values for emt_time */ +#define EMT_TIME_GMT 0 +#define EMT_TIME_LOCAL 1 + +/* func values for emt_misc */ +#define EMT_MISC_DISK_CHANGE 0 +#define EMT_MISC_EXIT 1 +#define EMT_MISC_DEBUG 2 +#define EMT_MISC_RESET_BUTTON 3 +#define EMT_MISC_QUERY_DISK_CHANGE 4 +#define EMT_MISC_QUERY_MODEL 5 +#define EMT_MISC_QUERY_DISK_SIZE 6 +#define EMT_MISC_SET_DISK_SIZE 7 +#define EMT_MISC_QUERY_DBL_STEP 8 +#define EMT_MISC_SET_DBL_STEP 9 +#define EMT_MISC_QUERY_MICROLABS 10 +#define EMT_MISC_SET_MICROLABS 11 +#define EMT_MISC_QUERY_DELAY 12 +#define EMT_MISC_SET_DELAY 13 +#define EMT_MISC_QUERY_KEYSTRETCH 14 +#define EMT_MISC_SET_KEYSTRETCH 15 +#define EMT_MISC_QUERY_DOUBLER 16 +#define EMT_MISC_SET_DOUBLER 17 +#define EMT_MISC_QUERY_VOLUME 18 +#define EMT_MISC_SET_VOLUME 19 +#define EMT_MISC_QUERY_TRUEDAM 20 +#define EMT_MISC_SET_TRUEDAM 21 diff --git a/xtrshard.dct b/xtrshard.dct Binary files differnew file mode 100644 index 0000000..ce13ca9 --- /dev/null +++ b/xtrshard.dct diff --git a/xtrshard.lst b/xtrshard.lst new file mode 100644 index 0000000..5ba2b81 --- /dev/null +++ b/xtrshard.lst @@ -0,0 +1,919 @@ + 1: ;*=*=* + 2: ; xtrshard/dct + 3: ; Emulate hard disk in a Unix file under xtrs + 4: ; + 5: ; Copyright (c) 1998, Timothy Mann + 6: ; + 7: ; This software may be copied, modified, and used for any + 8: ; purpose without fee, provided that (1) the above copyright + 9: ; notice is retained, and (2) modified versions are clearly + 10: ; marked as having been modified, with the modifier's name and + 11: ; the date included. + 12: ; + 13: ; Created 1-10-98 + 14: ; Last modified on Wed May 17 00:08:52 PDT 2000 by mann + 15: ;*=*=* + 16: + 17: + 18: ; Number of drives to allow + 19: 0008 ndrive equ 8 + 20: + 21: + 22: ; ASCII chars + 23: 000A LF equ 10 + 24: 000D CR equ 13 + 25: 0003 ETX equ 3 + 26: + 27: ; Model 4 SVC numbers + 28: 0064 @high equ 100 + 29: 000A @dsply equ 10 + 30: 0065 @flags equ 101 + 31: 000C @logot equ 12 + 32: 0052 @gtdcb equ 82 + 33: 0053 @gtmod equ 83 + 34: 005D @div8 equ 93 + 35: 005B @mul16 equ 91 + 36: 0009 @keyin equ 9 + 37: + 38: ; Model I/III hard addresses + 39: 0125 m3flag$ equ 0125h ; 'I' in ROM on Model III + 40: 447B @logot1 equ 447bh + 41: 428A @logot3 equ 428ah + 42: 4467 @dsply1 equ 4467h + 43: 4467 @dsply3 equ 4467h + 44: 4049 high$1 equ 4049h + 45: 4411 high$3 equ 4411h + 46: 4758 cflag$1 equ 4758h + 47: 4758 cflag$3 equ 4758h + 48: 4303 @icnfg1 equ 4303h + 49: 421D @icnfg3 equ 421dh + 50: 4B8F @mult1 equ 4b8fh + 51: 444E @mult3 equ 444eh + 52: 4B7B @divea1 equ 4b7bh + 53: 4B7A @divea3 equ 4b7ah + 54: 0040 @keyin1 equ 0040h + 55: 0040 @keyin3 equ 0040h + 56: + 57: ; Emulator trap instructions in byte-reversed form + 58: 32ED emt_read equ 32EDH + 59: 33ED emt_write equ 33EDH + 60: 34ED emt_lseek equ 34EDH + 61: 35ED emt_strerror equ 35EDH + 62: 3DED emt_ftruncate equ 3DEDH + 63: 3EED emt_opendisk equ 3EEDH + 64: 3FED emt_closedisk equ 3FEDH + 65: + 66: ; Constants for emt_opendisk + 67: 0000 EO_RDONLY equ 00o + 68: 0001 EO_WRONLY equ 01o + 69: 0002 EO_RDWR equ 02o + 70: 0040 EO_CREAT equ 0100o + 71: 0080 EO_EXCL equ 0200o + 72: 0200 EO_TRUNC equ 01000o + 73: 0400 EO_APPEND equ 02000o + 74: + 75: ;*=*=* + 76: ; Set origin to be safe on both LDOS 5 and 6 + 77: ;*=*=* + 78: 6000 org 6000h + 79: + 80: ;*=*=* + 81: ; Relocator for disk driver + 82: ;*=*=* + 83: 6000 ED537363 instal: ld (dct),de ;Save DCT address + 84: 6004 3A0A00 ld a,(000ah) ;Determine TRS-80 model + 85: 6007 FE40 cp 40h + 86: 6009 C23661 jp nz,lsdos6 ;Model 4 (or other LS-DOS, I hope) + 87: 600C 3A2501 ld a,(m3flag$) + 88: 600F FE49 cp 'I' + 89: 6011 CAA560 jp z,model3 ;Go if Model III + 90: ;*=*=* + 91: ; LDOS 5 Model I - See LS-DOS 6 version for comments + 92: ;*=*=* + 93: 6014 3ECD ld a,0cdh ;Insert Model I @LOGOT + 94: 6016 324262 ld (logot),a + 95: 6019 217B44 ld hl,@logot1 + 96: 601C 224362 ld (logot+1),hl + 97: 601F 327B65 ld (domul),a ;Insert Model I MULT + 98: 6022 218F4B ld hl,@mult1 + 99: 6025 227C65 ld (domul+1),hl + 100: 6028 32FA65 ld (dodiv),a ;Insert Model I DIVEA + 101: 602B 217B4B ld hl,@divea1 + 102: 602E 22FB65 ld (dodiv+1),hl + 103: 6031 3E31 ld a,'1' ;Modify filename + 104: 6033 325366 ld (hmod),a + 105: 6036 219D62 ld hl,hello_ + 106: 6039 CD6744 call @dsply1 + 107: 603C 3A5847 ld a,(cflag$1) + 108: 603F CB5F bit 3,a ;System request? + 109: 6041 CA3762 jp z,viaset + 110: 6044 ED5B7363 ld de,(dct) + 111: 6048 7A ld a,d ;DRIVE= must be specified + 112: 6049 B3 or e + 113: 604A CA3362 jp z,needdr + 114: 604D 214A63 asku1: ld hl,unit_ ;Ask which unit number + 115: 6050 CD6744 call @dsply1 + 116: 6053 217563 ld hl,unit + 117: 6056 010001 ld bc,100h + 118: 6059 CD4000 call @keyin1 + 119: 605C DA3F62 jp c,hitbrk + 120: 605F C23F62 jp nz,hitbrk + 121: 6062 3A7563 ld a,(unit) + 122: 6065 FE30 cp '0' + 123: 6067 38E4 jr c,asku1 + 124: 6069 FE38 cp '0'+ndrive + 125: 606B 30E0 jr nc,asku1 + 126: 606D 2A4940 ld hl,(high$1) + 127: 6070 CD7062 call xgtmod ;Module already loaded? + 128: 6073 CAEF61 jp z,setdct + 129: 6076 2A4940 ld hl,(high$1) + 130: 6079 227163 ld (newend),hl + 131: 607C 11E001 ld de,length + 132: 607F 97 sub a + 133: 6080 ED52 sbc hl,de + 134: 6082 224940 ld (high$1),hl + 135: 6085 CD4166 call dvrini + 136: 6088 CD4962 call relo + 137: 608B 3A0343 ld a,(@icnfg1) + 138: 608E 323E66 ld (link),a + 139: 6091 2A0443 ld hl,(@icnfg1+1) + 140: 6094 223F66 ld (link+1),hl + 141: 6097 213666 ld hl,dvrcfg + 142: 6098 rx16 equ $-2 + 143: 609A 220443 ld (@icnfg1+1),hl + 144: 609D 3EC3 ld a,0c3h + 145: 609F 320343 ld (@icnfg1),a + 146: 60A2 C3E161 jp move + 147: ;*=*=* + 148: ; LDOS 5 Model III + 149: ;*=*=* + 150: 60A5 model3: + 151: 60A5 3ECD ld a,0cdh ;Insert Model III @LOGOT + 152: 60A7 324262 ld (logot),a + 153: 60AA 218A42 ld hl,@logot3 + 154: 60AD 224362 ld (logot+1),hl + 155: 60B0 327B65 ld (domul),a ;Insert Model III MULT + 156: 60B3 214E44 ld hl,@mult3 + 157: 60B6 227C65 ld (domul+1),hl + 158: 60B9 32FA65 ld (dodiv),a ;Insert Model III DIVEA + 159: 60BC 217A4B ld hl,@divea3 + 160: 60BF 22FB65 ld (dodiv+1),hl + 161: 60C2 3E33 ld a,'3' ;Modify filename + 162: 60C4 325366 ld (hmod),a + 163: 60C7 219D62 ld hl,hello_ + 164: 60CA CD6744 call @dsply3 + 165: 60CD 3A5847 ld a,(cflag$3) + 166: 60D0 CB5F bit 3,a ;System request? + 167: 60D2 CA3762 jp z,viaset + 168: 60D5 ED5B7363 ld de,(dct) + 169: 60D9 7A ld a,d ;DRIVE= must be specified + 170: 60DA B3 or e + 171: 60DB CA3362 jp z,needdr + 172: 60DE 214A63 asku3: ld hl,unit_ ;Ask which unit number + 173: 60E1 CD6744 call @dsply3 + 174: 60E4 217563 ld hl,unit + 175: 60E7 010001 ld bc,100h + 176: 60EA CD4000 call @keyin3 + 177: 60ED DA3F62 jp c,hitbrk + 178: 60F0 C23F62 jp nz,hitbrk + 179: 60F3 3A7563 ld a,(unit) + 180: 60F6 FE30 cp '0' + 181: 60F8 38E4 jr c,asku3 + 182: 60FA FE38 cp '0'+ndrive + 183: 60FC 30E0 jr nc,asku3 + 184: 60FE 2A1144 ld hl,(high$3) + 185: 6101 CD7062 call xgtmod ;Module already loaded? + 186: 6104 CAEF61 jp z,setdct + 187: 6107 2A1144 ld hl,(high$3) + 188: 610A 227163 ld (newend),hl + 189: 610D 11E001 ld de,length + 190: 6110 97 sub a + 191: 6111 ED52 sbc hl,de + 192: 6113 221144 ld (high$3),hl + 193: 6116 CD4166 call dvrini + 194: 6119 CD4962 call relo + 195: 611C 3A1D42 ld a,(@icnfg3) + 196: 611F 323E66 ld (link),a + 197: 6122 2A1E42 ld hl,(@icnfg3+1) + 198: 6125 223F66 ld (link+1),hl + 199: 6128 213666 ld hl,dvrcfg + 200: 6129 rx17 equ $-2 + 201: 612B 221E42 ld (@icnfg3+1),hl + 202: 612E 3EC3 ld a,0c3h + 203: 6130 321D42 ld (@icnfg3),a + 204: 6133 C3E161 jp move + 205: + 206: ;*=*=* + 207: ; LS-DOS 6 + 208: ;*=*=* + 209: 6136 3E34 lsdos6: ld a,'4' ;Modify filename + 210: 6138 325366 ld (hmod),a + 211: 613B 219D62 ld hl,hello_ + 212: 613E 3E0A ld a,@dsply ;Display hello + 213: 6140 EF rst 40 + 214: ;*=*=* + 215: ; Check if entry from SYSTEM command. + 216: ;*=*=* + 217: 6141 3E65 ld a,@flags ;Get flags pointer into IY + 218: 6143 EF rst 40 + 219: 6144 FD7E02 ld a,(iy+'C'-'A') ;Get CFLAG$ + 220: 6147 CB5F bit 3,a ;System request? + 221: 6149 CA3762 jp z,viaset + 222: 614C ED5B7363 ld de,(dct) + 223: 6150 7A ld a,d ;DRIVE= must be specified + 224: 6151 B3 or e + 225: 6152 CA3362 jp z,needdr + 226: ;*=*=* + 227: ; Ask which unit number + 228: ;*=*=* + 229: 6155 214A63 asku4: ld hl,unit_ ;Ask which unit number + 230: 6158 3E0A ld a,@dsply + 231: 615A EF rst 40 + 232: 615B 217563 ld hl,unit + 233: 615E 010001 ld bc,100h + 234: 6161 3E09 ld a,@keyin + 235: 6163 EF rst 40 + 236: 6164 DA3F62 jp c,hitbrk + 237: 6167 C23F62 jp nz,hitbrk + 238: 616A 3A7563 ld a,(unit) + 239: 616D FE30 cp '0' + 240: 616F 38E4 jr c,asku4 + 241: 6171 FE38 cp '0'+ndrive + 242: 6173 30E0 jr nc,asku4 + 243: ;*=*=* + 244: ; Check if driver already loaded + 245: ;*=*=* + 246: 6175 117C64 ld de,modnam + 247: 6178 3E53 ld a,@gtmod + 248: 617A EF rst 40 + 249: 617B CAEF61 jp z,setdct ;Already loaded, skip loading + 250: ;*=*=* + 251: ; Obtain low memory driver pointer. Bizarre API here! + 252: ;*=*=* + 253: 617E 1E4B ld e,'K' ;Locate pointer to *KI DCB + 254: 6180 1649 ld d,'I' ; via @GTDCB SVC + 255: 6182 3E52 ld a,@gtdcb + 256: 6184 EF rst 40 + 257: 6185 C22F62 jp nz,curdl ;No error unless KI clobbered! + 258: 6188 2B dec hl ;Decrement to driver pointer + 259: 6189 56 ld d,(hl) ;P/u hi-order of pointer, + 260: 618A 2B dec hl ; decrement to and p/u + 261: 618B 5E ld e,(hl) ; lo-order of pointer + 262: ;*=*=* + 263: ; Check if driver will fit into [(LCPTR), X'12FF'] + 264: ;*=*=* + 265: 618C E5 push hl ;Save address of pointer + 266: 618D 21E001 ld hl,length ;New pointer will be + 267: 6190 19 add hl,de ; pointer + LENGTH + 268: 6191 54 ld d,h ;Save a copy in DE + 269: 6192 5D ld e,l + 270: 6193 010113 ld bc,1301h ;If > 1300H, driver won't fit + 271: 6196 97 sub a ;Reset carry flag + 272: 6197 ED42 sbc hl,bc + 273: 6199 E1 pop hl ;Get back address of pointer + 274: 619A 300A jr nc,usehi ;Go if driver won't fit + 275: 619C 73 ld (hl),e ;Store new value of pointer + 276: 619D 23 inc hl + 277: 619E 72 ld (hl),d + 278: 619F 1B dec de ;Last byte of driver goes here + 279: 61A0 ED537163 ld (newend),de + 280: 61A4 1816 jr dorelo + 281: ;*=*=* + 282: ; Put in high memory instead. + 283: ;*=*=* + 284: 61A6 210000 usehi: ld hl,0 ;Get current HIGH$ + 285: 61A9 45 ld b,l + 286: 61AA 3E64 ld a,@high + 287: 61AC EF rst 40 + 288: 61AD C23B62 jp nz,nomem + 289: 61B0 227163 ld (newend),hl ;Last byte of driver goes here + 290: 61B3 11E001 ld de,length + 291: 61B6 97 sub a ;Reset carry flag + 292: 61B7 ED52 sbc hl,de ;Compute new HIGH$ + 293: 61B9 3E64 ld a,@high ;Set new HIGH$ into the system + 294: 61BB EF rst 40 + 295: ;*=*=* + 296: ; Relocate internal references in driver. + 297: ; HL = address for last byte of driver. + 298: ;*=*=* + 299: 61BC CD4166 dorelo: call dvrini ;Final driver init before move + 300: 61BF CD4962 call relo + 301: ;*=*=* + 302: ; Link to @ICNFG (must follow address relocation and precede movement) + 303: ;*=*=* + 304: 61C2 3E65 ld a,@flags ;Get flags pointer into IY + 305: 61C4 EF rst 40 + 306: 61C5 FD7E1C ld a,(iy+28) ;Copy current @ICNFG into LINK + 307: 61C8 FD6E1D ld l,(iy+29) + 308: 61CB FD661E ld h,(iy+30) + 309: 61CE 323E66 ld (link),a + 310: 61D1 223F66 ld (link+1),hl + 311: 61D4 213666 ld hl,dvrcfg ;Get relocated init address + 312: 61D5 rx10 equ $-2 + 313: 61D7 FD751D ld (iy+29),l ;Save in @ICNFG vector + 314: 61DA FD741E ld (iy+30),h + 315: 61DD FD361CC3 ld (iy+28),0c3h ;Insert JP opcode + 316: ;*=*=* + 317: ; Move driver into low or high memory. + 318: ;*=*=* + 319: 61E1 move: + 320: 61E1 ED5B7163 ld de,(newend) ;Destination address + 321: 61E5 215666 ld hl,dvrend ;Last byte of module + 322: 61E8 01E001 ld bc,length ;Length of filter + 323: 61EB EDB8 lddr + 324: 61ED EB ex de,hl + 325: 61EE 23 inc hl ;Bump to driver entry + 326: ;*=*=* + 327: ; Setup DCT (iy+5 to iy+9 are reset by ckopen if successful) + 328: ;*=*=* + 329: 61EF setdct: + 330: 61EF FD2A7363 ld iy,(dct) + 331: 61F3 FD3600C3 ld (iy),0c3h ;JP instruction (enable driver) + 332: 61F7 FD7501 ld (iy+1),l ;Driver address + 333: 61FA FD7402 ld (iy+2),h + 334: 61FD FD36030C ld (iy+3),00001100b ;Flags: rigid, fixed, step rate 0 + 335: 6201 3A7563 ld a,(unit) + 336: 6204 E60F and 0fh + 337: 6206 F610 or 00010000b ;Flags: alien (=no index pulses), unit# + 338: 6208 FD7704 ld (iy+4),a + 339: 620B FD360500 ld (iy+5),0 ;LDOS undefined; we use as sec/cyl (0=256). + 340: 620F FD3606C9 ld (iy+6),201 ;high cylinder number + 341: 6213 FD3607FF ld (iy+7),11111111b ;high head # (111), high sec/trak (11111) + 342: 6217 FD3608FF ld (iy+8),11111111b ;high gran # (111), high sec/gran (11111) + 343: 621B FD3609FF ld (iy+9),0ffh ;Directory cylinder + 344: ;*=*=* + 345: ; Open file now so user can get error if any, and so geometry + 346: ; is established as early as possible. + 347: ;*=*=* + 348: 621F CD9965 call ckopen + 349: 6222 210000 ld hl,0 ;Successful completion + 350: 6225 C8 ret z ;Fall thru if error + 351: ;*=*=* + 352: 6226 217763 uerror: ld hl,errbuf ;Unix error + 353: 6229 010001 ld bc,256 + 354: 622C ED35 defw emt_strerror + 355: 622E DD defb 0ddh + 356: 622F 21D562 curdl: ld hl,curdl_ ;Other error + 357: 6232 DD defb 0ddh + 358: 6233 213063 needdr: ld hl,needdr_ + 359: 6236 DD defb 0ddh + 360: 6237 210663 viaset: ld hl,viaset_ + 361: 623A DD defb 0ddh + 362: 623B 21E862 nomem: ld hl,nomem_ + 363: 623E DD defb 0ddh + 364: 623F 216663 hitbrk: ld hl,hitbrk_ + 365: 6242 3E0C logot: ld a,@logot + 366: 6244 EF rst 40 + 367: 6245 21FFFF ld hl,-1 ;Unuccessful completion + 368: 6248 C9 ret + 369: + 370: ;*=*=* + 371: ; Relocate internal references in driver. + 372: ; HL = address for last byte of driver. + 373: ;*=*=* + 374: 6249 2A7163 relo: ld hl,(newend) + 375: 624C FD215766 ld iy,reltab ;Point to relocation tbl + 376: 6250 115666 ld de,dvrend + 377: 6253 97 sub a ;Clear carry flag + 378: 6254 ED52 sbc hl,de + 379: 6256 44 ld b,h ;Move to BC + 380: 6257 4D ld c,l + 381: 6258 FD6E00 rloop: ld l,(iy) ;Get address to change + 382: 625B FD6601 ld h,(iy+1) + 383: 625E 7C ld a,h + 384: 625F B5 or l + 385: 6260 C8 ret z + 386: 6261 5E ld e,(hl) ;P/U address + 387: 6262 23 inc hl + 388: 6263 56 ld d,(hl) + 389: 6264 EB ex de,hl ;Offset it + 390: 6265 09 add hl,bc + 391: 6266 EB ex de,hl + 392: 6267 72 ld (hl),d ;And put back + 393: 6268 2B dec hl + 394: 6269 73 ld (hl),e + 395: 626A FD23 inc iy + 396: 626C FD23 inc iy + 397: 626E 18E8 jr rloop ;Loop till done + 398: + 399: ;*=*=* + 400: ; Search for existing copy of driver. + 401: ; Rough Model I/III emulation of Model 4 @GTMOD, + 402: ; hardcoded with driver address. + 403: ; Entry: HL holds HIGH$ + 404: ; Exit Z: HL holds driver address + 405: ; NZ: driver not found + 406: ;*=*=* + 407: 6270 23 xgtmod: inc hl + 408: 6271 7C ld a,h + 409: 6272 B5 or l + 410: 6273 2002 jr nz,xgtm1 + 411: 6275 3D dec a ;not found + 412: 6276 C9 ret + 413: 6277 7E xgtm1: ld a,(hl) + 414: 6278 FE18 cp 18h ;unconditional jr? + 415: 627A C0 ret nz ;not a module header + 416: 627B E5 push hl ;save start address + 417: 627C 23 inc hl ;skip jr + 418: 627D 23 inc hl ;skip offset + 419: 627E 23 inc hl ;skip start address + 420: 627F 23 inc hl + 421: 6280 7E ld a,(hl) ;compare name length + 422: 6281 FE08 cp modptr-modnam + 423: 6283 200F jr nz,nextmd ;different - skip + 424: 6285 47 ld b,a ;compare name + 425: 6286 117C64 ld de,modnam + 426: 6289 23 inc hl + 427: 628A 1A xgtm2: ld a,(de) + 428: 628B BE cp (hl) + 429: 628C 2006 jr nz,nextmd ;different - skip + 430: 628E 13 inc de + 431: 628F 23 inc hl + 432: 6290 10F8 djnz xgtm2 + 433: 6292 E1 pop hl ;same - found + 434: 6293 C9 ret + 435: 6294 E1 nextmd: pop hl ;get back start of module + 436: 6295 23 inc hl + 437: 6296 23 inc hl + 438: 6297 5E ld e,(hl) ;pointer to last byte + 439: 6298 23 inc hl + 440: 6299 56 ld d,(hl) + 441: 629A EB ex de,hl + 442: 629B 18D3 jr xgtmod + 443: + 444: ;*=*=* + 445: ; Messages and globals + 446: ;*=*=* + 447: 629D 58545253 hello_: defb 'XTRSHARD - Emulated hard disk driver for xtrs - 5/17/00',CR + 48415244 + 202D2045 + 6D756C61 + 74656420 + 68617264 + 20646973 + 6B206472 + 69766572 + 20666F72 + 20787472 + 73202D20 + 352F3137 + 2F30300D + 448: 62D5 4C532D44 curdl_: defb 'LS-DOS is curdled!',CR + 4F532069 + 73206375 + 72646C65 + 64210D + 449: 62E8 48696768 nomem_: defb 'High memory is not available!',CR + 206D656D + 6F727920 + 6973206E + 6F742061 + 7661696C + 61626C65 + 210D + 450: 6306 4D757374 viaset_:defb 'Must install via SYSTEM (DRIVE=,DRIVER=)!',CR + 20696E73 + 74616C6C + 20766961 + 20535953 + 54454D20 + 28445249 + 56453D2C + 44524956 + 45523D29 + 210D + 451: 6330 44524956 needdr_:defb 'DRIVE= must be specified!',CR + 453D206D + 75737420 + 62652073 + 70656369 + 66696564 + 210D + 452: 634A 456E7465 unit_: defb 'Enter unit number ([0]-','0'+ndrive-1,'): ',ETX + 7220756E + 6974206E + 756D6265 + 7220285B + 305D2D37 + 293A2003 + 453: 6366 41626F72 hitbrk_:defb 'Aborted!',CR + 74656421 + 0D + 454: 636F 0000 lcptr: defw 0 + 455: 6371 0000 newend: defw 0 + 456: 6373 0000 dct: defw 0 + 457: 6375 unit: defs 2 + 458: 6377 errbuf: defs 256 + 459: + 460: ; + 461: ; Driver - Based on skeletal driver from the Guide + 462: ; + 463: + 464: 6477 1827 entry: jr begin ;The driver starts with the + 465: 6479 5666 defw dvrend ; DOS standard header + 466: 6479 rx00 equ $-2 + 467: 647B 08 defb modptr-modnam ;Length of name + 468: 647C 78747273 modnam: defb 'xtrshard' ;Name for @GTMOD requests + 68617264 + 469: 6484 0000 modptr: defw 0 ;These pointers are unused + 470: 6486 0000 defw 0 + 471: 6488 fd: defs ndrive*2 ;Unix file descriptors + 472: 6498 00000000 offset: defw 0,0,0,0 ;lseek offset buffer + 00000000 + 473: 64A0 begin: + 474: ;*=*=* + 475: ; First make sure the file is open and correct the geometry + 476: ; in the DCT if needed. + 477: ;*=*=* + 478: 64A0 DDE5 push ix + 479: 64A2 CD9965 call ckopen + 480: 64A3 rx03 equ $-2 + 481: 64A5 CDAB64 call body + 482: 64A6 rx06 equ $-2 + 483: 64A8 DDE1 pop ix + 484: 64AA C9 ret + 485: 64AB 3E20 body: ld a,32 ;"Illegal drive number" + 486: 64AD C0 ret nz + 487: 64AE 78 ld a,b ;The first test will return + 488: 64AF A7 and a ; to the caller on @DCSTAT + 489: 64B0 C8 ret z ; and set the Z-flag with A=0 + 490: 64B1 FE07 notdcs: cp 7 + 491: 64B3 2804 jr z,rslct ;Transfer on @RSLCT + 492: 64B5 3004 jr nc,diskio ;Transfer on physical I/O request + 493: ;*=*=* + 494: ; @SLCT, @DCINIT, @DCRES, @RSTOR, @STEPI or @SEEK: no-op + 495: ;*=*=* + 496: 64B7 97 retzer: sub a + 497: 64B8 C9 ret + 498: ;*=*=* + 499: ; The RSLCT function should return with the hardware + 500: ; write protection status. Set bit 6 of the accumulator + 501: ; to indicate the drive is write-protected + 502: ;*=*=* + 503: 64B9 97 rslct: sub a ;No emulated hardware WP for now + 504: 64BA C9 ret + 505: ;*=*=* + 506: 64BB CB50 diskio: bit 2,b ;Test if read or write commands + 507: 64BD 2058 jr nz,wrcmd ;Transfer if functions <12-15> + 508: 64BF FE0A cp 10 + 509: 64C1 2847 jr z,vrsec + 510: 64C3 304E jr nc,rdtrk + 511: 64C5 FE09 cp 9 + 512: 64C7 2804 jr z,rdsec + 513: 64C9 3E20 rdhdr: ld a,32 ;Not supported ("Illegal drive number") + 514: 64CB A7 and a + 515: 64CC C9 ret + 516: ;*=*=* + 517: 64CD rdsec: ;Read a sector of data + 518: 64CD FD7E06 ld a,(iy+6) ;Get high cyl # + 519: 64D0 BA cp d ;At or below it? + 520: 64D1 3003 jr nc,rdok + 521: 64D3 3E02 ld a,2 ;"Seek error during read" + 522: 64D5 C9 ret ;NZ already set + 523: 64D6 D5 rdok: push de + 524: 64D7 E5 push hl + 525: 64D8 CD6F65 call doseek ;Setup and do lseek + 526: 64D9 rx01 equ $-2 + 527: 64DB E1 pop hl + 528: 64DC 3E05 ld a,5 ;"Data record not found during read" + 529: 64DE 2007 jr nz,rddun + 530: 64E0 010001 ld bc,256 + 531: 64E3 ED32 defw emt_read + 532: 64E5 3E04 ld a,4 ;"Parity error during read" + 533: 64E7 D1 rddun: pop de + 534: 64E8 C0 ret nz + 535: 64E9 78 ld a,b ;Check for end of file + 536: 64EA B1 or c + 537: 64EB 2012 jr nz,rddun2 + 538: 64ED D5 push de + 539: 64EE E5 push hl ;Return a block full of 0E5H + 540: 64EF C5 push bc + 541: 64F0 36E5 ld (hl),0e5h + 542: 64F2 54 ld d,h + 543: 64F3 5D ld e,l + 544: 64F4 13 inc de + 545: 64F5 01FF00 ld bc,0ffh + 546: 64F8 EDB0 ldir + 547: 64FA C1 pop bc + 548: 64FB E1 pop hl + 549: 64FC D1 pop de + 550: 64FD 97 sub a + 551: 64FE C9 ret + 552: 64FF 7A rddun2: ld a,d + 553: 6500 FD9609 sub (iy+9) + 554: 6503 2003 jr nz,rddun1 + 555: 6505 C606 add a,6 ;"Attempted to read system data record" + 556: 6507 C9 ret + 557: 6508 97 rddun1: sub a + 558: 6509 C9 ret + 559: ;*=*=* + 560: 650A vrsec: ;Read/verify -- we don't bother reading + 561: 650A FD7E06 ld a,(iy+6) ;Get high cyl # + 562: 650D BA cp d ;At or below it? + 563: 650E 30EF jr nc,rddun2 ;Go if so + 564: 6510 3E02 ld a,2 ;"Seek error during read" + 565: 6512 C9 ret ;NZ already set + 566: ;*=*=* + 567: ; On RDSEC and VRSEC, if the read referenced the + 568: ; directory cylinder and was successful, + 569: ; then you need to return an error code 6. A floppy + 570: ; disk controller will provide the indicated status. + 571: ; Hard disk users may have to compare the requested + 572: ; cylinder to DIRCYL in the DCT. + 573: ;*=*=* + 574: 6513 3E20 rdtrk: ld a,32 ;Not supported ("Illegal drive number") + 575: 6515 A7 and a + 576: 6516 C9 ret + 577: ;*=*=* + 578: 6517 FDCB037E wrcmd: bit 7,(iy+3) ;Check for software write protect + 579: 651B 2803 jr z,wrcmd1 ;Transfer if no soft WP + 580: 651D 3E0F ld a,15 ;Set "Write protected disk" error + 581: 651F C9 ret + 582: 6520 FE0E wrcmd1: cp 14 ;Now parse functions 12-15 + 583: 6522 2829 jr z,wrssc + 584: 6524 3045 jr nc,wrtrk + 585: 6526 FE0D cp 13 + 586: 6528 281E jr z,wrsec + 587: ;*=*=* + 588: 652A hdfmt: ;Low-level format (=erase) + 589: 652A FD3609FF ld (iy+9),0ffh ;Invalidate directory cylinder + 590: 652E D5 push de + 591: 652F E5 push hl + 592: 6530 DD5E00 ld e,(ix) ;Get fd + 593: 6533 DD5601 ld d,(ix+1) + 594: 6536 210001 exists: ld hl,256 ;Truncate file to just the header + 595: 6539 229964 ld (offset+1),hl + 596: 653A rx07 equ $-2 + 597: 653C 219864 ld hl,offset + 598: 653D rx08 equ $-2 + 599: 653F ED3D defw emt_ftruncate + 600: 6541 E1 creatd: pop hl + 601: 6542 D1 pop de + 602: 6543 3E0E ld a,14 ;"Write fault on disk drive" + 603: 6545 C0 ret nz + 604: 6546 97 sub a + 605: 6547 C9 ret + 606: ;*=*=* + 607: 6548 wrsec: ;Write with X'FB' data address mark + 608: 6548 7A ld a,d ;Check if writing track 0, sector 0 + 609: 6549 B3 or e + 610: 654A CC1566 call z,setdir ;Set directory cyl in Reed header + 611: 654B rx20 equ $-2 + 612: ;*=*=* + 613: 654D wrssc: ;Write with X'F8' data address mark + 614: 654D FD7E06 ld a,(iy+6) ;Get high cyl # + 615: 6550 BA cp d ;Beyond it? + 616: 6551 3003 jr nc,wrok1 + 617: 6553 3E0A ld a,10 ;"Seek error during write" + 618: 6555 C9 ret ;NZ already set + 619: ;*=*=* + 620: 6556 D5 wrok1: push de + 621: 6557 E5 push hl + 622: 6558 CD6F65 call doseek + 623: 6559 rx04 equ $-2 + 624: 655B E1 pop hl + 625: 655C 3E0D ld a,13 ;"Data record not found during write" + 626: 655E 2007 jr nz,wrdun + 627: 6560 010001 ld bc,256 + 628: 6563 ED33 defw emt_write + 629: 6565 3E0C ld a,12 ;"Parity error during write" + 630: 6567 D1 wrdun: pop de + 631: 6568 C0 ret nz + 632: 6569 97 sub a + 633: 656A C9 ret + 634: ;*=*=* + 635: 656B 3E20 wrtrk: ld a,32 ;Write track + 636: 656D A7 and a ;Not supported ("Illegal drive number") + 637: 656E C9 ret + 638: ;*=*=* + 639: ; Perform lseek before r/w + 640: ;*=*=* + 641: 656F 97 doseek: sub a ;sec/cyl to hl, xlate 0 to 256 + 642: 6570 67 ld h,a + 643: 6571 FD8605 add a,(iy+5) + 644: 6574 6F ld l,a + 645: 6575 2001 jr nz,noinc + 646: 6577 24 inc h + 647: 6578 4A noinc: ld c,d ;cyl# to c + 648: 6579 43 ld b,e ;sec# to b + 649: 657A 79 ld a,c ;model I/III call uses a, not c + 650: 657B 3E5B domul: ld a,@mul16 ;hla = hl * c, smash de + 651: 657D EF rst 40 + 652: 657E 54 ld d,h ;sec# to de (h is 0) + 653: 657F 58 ld e,b + 654: 6580 65 ld h,l ;product to hl + 655: 6581 6F ld l,a + 656: 6582 23 inc hl ;add 1 extra for header + 657: 6583 19 add hl,de + 658: 6584 EB ex de,hl ;offset to de + 659: 6585 219A64 ld hl,offset+2 + 660: 6586 rx15 equ $-2 + 661: 6588 72 ld (hl),d + 662: 6589 2B dec hl + 663: 658A 73 ld (hl),e + 664: 658B 2B dec hl + 665: 658C 010000 ld bc,0 + 666: 658F 71 ld (hl),c + 667: 6590 DD5E00 ld e,(ix) ;Get fd + 668: 6593 DD5601 ld d,(ix+1) + 669: 6596 ED34 defw emt_lseek + 670: 6598 C9 ret + 671: + 672: ;*=*=* + 673: ; Open file and read geometry if needed, and + 674: ; get address of correct fd to ix. + 675: ;*=*=* + 676: 6599 DD218864 ckopen: ld ix,fd ;Compute fd address + 677: 659B rx02 equ $-2 + 678: 659D D5 push de + 679: 659E 1600 ld d,0 + 680: 65A0 FD7E04 ld a,(iy+4) + 681: 65A3 E60F and 0fh + 682: 65A5 07 rlca + 683: 65A6 5F ld e,a + 684: 65A7 DD19 add ix,de + 685: 65A9 D1 pop de + 686: 65AA DD7E00 ld a,(ix) ;fd == -1? + 687: 65AD DDA601 and (ix+1) + 688: 65B0 3C inc a + 689: 65B1 2802 jr z,doopen + 690: 65B3 97 sub a + 691: 65B4 C9 ret + 692: 65B5 D5 doopen: push de + 693: 65B6 C5 push bc + 694: 65B7 E5 push hl + 695: 65B8 010200 ld bc,EO_RDWR ;Prepare to open + 696: 65BB 11B601 ld de,0666o ;mode + 697: 65BE 214F66 ld hl,hard_ ;name + 698: 65BF rx05 equ $-2 + 699: 65C1 FD7E04 ld a,(iy+4) + 700: 65C4 E60F and 0fh + 701: 65C6 C630 add a,'0' + 702: 65C8 325566 ld (hadr),a + 703: 65C9 rx09 equ $-2 + 704: 65CB ED3E defw emt_opendisk + 705: 65CD DD7300 ld (ix),e + 706: 65D0 DD7201 ld (ix+1),d + 707: 65D3 203C jr nz,opnerr + 708: 65D5 219864 ld hl,offset ;Prepare to read geometry + 709: 65D6 rx13 equ $-2 + 710: 65D8 361C ld (hl),28 ;offset to cyl/sec/gran params + 711: 65DA 010000 ld bc,0 + 712: 65DD ED439964 ld (offset+1),bc + 713: 65DF rx14 equ $-2 + 714: 65E1 ED34 defw emt_lseek + 715: 65E3 202C jr nz,opnerr + 716: 65E5 010300 ld bc,3 ;length + 717: 65E8 ED32 defw emt_read ;use offset buffer + 718: 65EA 2025 jr nz,opnerr + 719: 65EC 7E ld a,(hl) ;cyl + 720: 65ED 3D dec a + 721: 65EE FD7706 ld (iy+6),a ;max cylinder + 722: 65F1 23 inc hl + 723: 65F2 46 ld b,(hl) ;sec + 724: 65F3 FD7005 ld (iy+5),b + 725: 65F6 23 inc hl + 726: 65F7 4E ld c,(hl) ;gran + 727: 65F8 58 ld e,b ;compute sec/gran + 728: 65F9 79 ld a,c ;model I/III call uses a, not c + 729: 65FA 3E5D dodiv: ld a,@div8 ;a = e / c, e = e % c + 730: 65FC EF rst 40 ;remainder mbz, but we don't check here + 731: 65FD 3D dec a + 732: 65FE 0D dec c + 733: 65FF CB09 rrc c + 734: 6601 CB09 rrc c + 735: 6603 CB09 rrc c + 736: 6605 B1 or c + 737: 6606 FD7707 ld (iy+7),a ;heads, secs per track + 738: 6609 FD7708 ld (iy+8),a ;grans, secs per gran + 739: 660C FD3609FF ld (iy+9),0ffh ;dircyl unknown + 740: 6610 97 sub a ;no error + 741: 6611 E1 opnerr: pop hl + 742: 6612 C1 pop bc + 743: 6613 D1 pop de + 744: 6614 C9 ret + 745: ;*=*=* + 746: ; Sleazy trick to update dir cyl in Reed header: do + 747: ; it whenever track 0, sector 0 is written. + 748: ; Only important if sharing images with Reed emulator. + 749: ;*=*=* + 750: 6615 E5 setdir: push hl + 751: 6616 C5 push bc + 752: 6617 D5 push de + 753: 6618 219864 ld hl,offset + 754: 6619 rx18 equ $-2 + 755: 661B 42 ld b,d ;de is known to be 0 here + 756: 661C 4B ld c,e + 757: 661D DD5E00 ld e,(ix) ;Get fd + 758: 6620 DD5601 ld d,(ix+1) + 759: 6623 361F ld (hl),31 ;offset to dir cyl param + 760: 6625 ED439964 ld (offset+1),bc + 761: 6627 rx19 equ $-2 + 762: 6629 ED34 defw emt_lseek + 763: 662B FD7E09 ld a,(iy+9) ;dir cyl value to write + 764: 662E 77 ld (hl),a + 765: 662F 03 inc bc + 766: 6630 ED33 defw emt_write + 767: 6632 D1 pop de ;cheat, ignore errors + 768: 6633 C1 pop bc + 769: 6634 E1 pop hl + 770: 6635 C9 ret + 771: ;*=*=* + 772: ; Boot-time initialization + 773: ;*=*=* + 774: 6636 11FFFF dvrcfg: ld de,-1 ;@ICNFG chains in here + 775: 6639 ED3F defw emt_closedisk ;close any files left from before reboot + 776: 663B CD4166 call dvrini + 777: 663C rx11 equ $-2 + 778: 663E 54696D link: defb 'Tim' ;Replaced by next link in @ICNFG chain + 779: 6641 218864 dvrini: ld hl,fd + 780: 6642 rx12 equ $-2 + 781: 6644 54 ld d,h + 782: 6645 5D ld e,l + 783: 6646 13 inc de + 784: 6647 36FF ld (hl),0ffh + 785: 6649 010F00 ld bc,ndrive*2-1 + 786: 664C EDB0 ldir + 787: 664E C9 ret + 788: ;*=*=* + 789: ; Disk name: hardM-N for model M (1,3,4), number N (0-7) + 790: ;*=*=* + 791: 664F 68617264 hard_: defb 'hard' + 792: 6653 312D hmod: defb '1-' + 793: 6655 3000 hadr: defb '0',0 + 794: + 795: 6656 dvrend equ $-1 + 796: 01E0 length equ $-entry + 797: 6657 7964D964 reltab: defw rx00,rx01,rx02,rx03,rx04,rx05,rx06,rx07,rx08,rx09 + 9B65A364 + 5965BF65 + A6643A65 + 3D65C965 + 798: 666B D5613C66 defw rx10,rx11,rx12,rx13,rx14,rx15,rx16,rx17,rx18,rx19 + 4266D665 + DF658665 + 98602961 + 19662766 + 799: 667F 4B650000 defw rx20,0 + 800: 6000 end instal + + + +Statistics: + + 146 symbols + 1393 bytes + + + +Symbol Table: + +@div8 = 5d emt_write =33ed rdok 64d6 +@divea1 =4b7b entry 6477 rdsec 64cd +@divea3 =4b7a eo_append = 400+ rdtrk 6513 +@dsply = a eo_creat = 40+ relo 6249 +@dsply1 =4467 eo_excl = 80+ reltab 6657 +@dsply3 =4467 eo_rdonly = 0+ retzer 64b7+ +@flags = 65 eo_rdwr = 2 rloop 6258 +@gtdcb = 52 eo_trunc = 200+ rslct 64b9 +@gtmod = 53 eo_wronly = 1+ rx00 =6479 +@high = 64 errbuf 6377 rx01 =64d9 +@icnfg1 =4303 etx = 3 rx02 =659b +@icnfg3 =421d exists 6536+ rx03 =64a3 +@keyin = 9 fd 6488 rx04 =6559 +@keyin1 = 40 hadr 6655 rx05 =65bf +@keyin3 = 40 hard_ 664f rx06 =64a6 +@logot = c hdfmt 652a+ rx07 =653a +@logot1 =447b hello_ 629d rx08 =653d +@logot3 =428a high1 =4049 rx09 =65c9 +@mul16 = 5b high3 =4411 rx10 =61d5 +@mult1 =4b8f hitbrk 623f rx11 =663c +@mult3 =444e hitbrk_ 6366 rx12 =6642 +asku1 604d hmod 6653 rx13 =65d6 +asku3 60de instal 6000 rx14 =65df +asku4 6155 lcptr 636f+ rx15 =6586 +begin 64a0 length = 1e0 rx16 =6098 +body 64ab lf = a+ rx17 =6129 +cflag1 =4758 link 663e rx18 =6619 +cflag3 =4758 logot 6242 rx19 =6627 +ckopen 6599 lsdos6 6136 rx20 =654b +cr = d m3flag = 125 setdct 61ef +creatd 6541+ model3 60a5 setdir 6615 +curdl 622f modnam 647c uerror 6226+ +curdl_ 62d5 modptr 6484 unit 6375 +dct 6373 move 61e1 unit_ 634a +diskio 64bb ndrive = 8 usehi 61a6 +dodiv 65fa needdr 6233 viaset 6237 +domul 657b needdr_ 6330 viaset_ 6306 +doopen 65b5 newend 6371 vrsec 650a +dorelo 61bc nextmd 6294 wrcmd 6517 +doseek 656f noinc 6578 wrcmd1 6520 +dvrcfg 6636 nomem 623b wrdun 6567 +dvrend =6656 nomem_ 62e8 wrok1 6556 +dvrini 6641 notdcs 64b1+ wrsec 6548 +emt_closedisk =3fed offset 6498 wrssc 654d +emt_ftruncate =3ded opnerr 6611 wrtrk 656b +emt_lseek =34ed rddun 64e7 xgtm1 6277 +emt_opendisk =3eed rddun1 6508 xgtm2 628a +emt_read =32ed rddun2 64ff xgtmod 6270 +emt_strerror =35ed rdhdr 64c9+ diff --git a/xtrshard.z80 b/xtrshard.z80 new file mode 100644 index 0000000..0e036e1 --- /dev/null +++ b/xtrshard.z80 @@ -0,0 +1,800 @@ +;*=*=* +; xtrshard/dct +; Emulate hard disk in a Unix file under xtrs +; +; Copyright (c) 1998, Timothy Mann +; +; This software may be copied, modified, and used for any +; purpose without fee, provided that (1) the above copyright +; notice is retained, and (2) modified versions are clearly +; marked as having been modified, with the modifier's name and +; the date included. +; +; Created 1-10-98 +; Last modified on Wed May 17 00:08:52 PDT 2000 by mann +;*=*=* + + +; Number of drives to allow +ndrive equ 8 + + +; ASCII chars +LF equ 10 +CR equ 13 +ETX equ 3 + +; Model 4 SVC numbers +@high equ 100 +@dsply equ 10 +@flags equ 101 +@logot equ 12 +@gtdcb equ 82 +@gtmod equ 83 +@div8 equ 93 +@mul16 equ 91 +@keyin equ 9 + +; Model I/III hard addresses +m3flag$ equ 0125h ; 'I' in ROM on Model III +@logot1 equ 447bh +@logot3 equ 428ah +@dsply1 equ 4467h +@dsply3 equ 4467h +high$1 equ 4049h +high$3 equ 4411h +cflag$1 equ 4758h +cflag$3 equ 4758h +@icnfg1 equ 4303h +@icnfg3 equ 421dh +@mult1 equ 4b8fh +@mult3 equ 444eh +@divea1 equ 4b7bh +@divea3 equ 4b7ah +@keyin1 equ 0040h +@keyin3 equ 0040h + +; Emulator trap instructions in byte-reversed form +emt_read equ 32EDH +emt_write equ 33EDH +emt_lseek equ 34EDH +emt_strerror equ 35EDH +emt_ftruncate equ 3DEDH +emt_opendisk equ 3EEDH +emt_closedisk equ 3FEDH + +; Constants for emt_opendisk +EO_RDONLY equ 00o +EO_WRONLY equ 01o +EO_RDWR equ 02o +EO_CREAT equ 0100o +EO_EXCL equ 0200o +EO_TRUNC equ 01000o +EO_APPEND equ 02000o + +;*=*=* +; Set origin to be safe on both LDOS 5 and 6 +;*=*=* + org 6000h + +;*=*=* +; Relocator for disk driver +;*=*=* +instal: ld (dct),de ;Save DCT address + ld a,(000ah) ;Determine TRS-80 model + cp 40h + jp nz,lsdos6 ;Model 4 (or other LS-DOS, I hope) + ld a,(m3flag$) + cp 'I' + jp z,model3 ;Go if Model III +;*=*=* +; LDOS 5 Model I - See LS-DOS 6 version for comments +;*=*=* + ld a,0cdh ;Insert Model I @LOGOT + ld (logot),a + ld hl,@logot1 + ld (logot+1),hl + ld (domul),a ;Insert Model I MULT + ld hl,@mult1 + ld (domul+1),hl + ld (dodiv),a ;Insert Model I DIVEA + ld hl,@divea1 + ld (dodiv+1),hl + ld a,'1' ;Modify filename + ld (hmod),a + ld hl,hello_ + call @dsply1 + ld a,(cflag$1) + bit 3,a ;System request? + jp z,viaset + ld de,(dct) + ld a,d ;DRIVE= must be specified + or e + jp z,needdr +asku1: ld hl,unit_ ;Ask which unit number + call @dsply1 + ld hl,unit + ld bc,100h + call @keyin1 + jp c,hitbrk + jp nz,hitbrk + ld a,(unit) + cp '0' + jr c,asku1 + cp '0'+ndrive + jr nc,asku1 + ld hl,(high$1) + call xgtmod ;Module already loaded? + jp z,setdct + ld hl,(high$1) + ld (newend),hl + ld de,length + sub a + sbc hl,de + ld (high$1),hl + call dvrini + call relo + ld a,(@icnfg1) + ld (link),a + ld hl,(@icnfg1+1) + ld (link+1),hl + ld hl,dvrcfg +rx16 equ $-2 + ld (@icnfg1+1),hl + ld a,0c3h + ld (@icnfg1),a + jp move +;*=*=* +; LDOS 5 Model III +;*=*=* +model3: + ld a,0cdh ;Insert Model III @LOGOT + ld (logot),a + ld hl,@logot3 + ld (logot+1),hl + ld (domul),a ;Insert Model III MULT + ld hl,@mult3 + ld (domul+1),hl + ld (dodiv),a ;Insert Model III DIVEA + ld hl,@divea3 + ld (dodiv+1),hl + ld a,'3' ;Modify filename + ld (hmod),a + ld hl,hello_ + call @dsply3 + ld a,(cflag$3) + bit 3,a ;System request? + jp z,viaset + ld de,(dct) + ld a,d ;DRIVE= must be specified + or e + jp z,needdr +asku3: ld hl,unit_ ;Ask which unit number + call @dsply3 + ld hl,unit + ld bc,100h + call @keyin3 + jp c,hitbrk + jp nz,hitbrk + ld a,(unit) + cp '0' + jr c,asku3 + cp '0'+ndrive + jr nc,asku3 + ld hl,(high$3) + call xgtmod ;Module already loaded? + jp z,setdct + ld hl,(high$3) + ld (newend),hl + ld de,length + sub a + sbc hl,de + ld (high$3),hl + call dvrini + call relo + ld a,(@icnfg3) + ld (link),a + ld hl,(@icnfg3+1) + ld (link+1),hl + ld hl,dvrcfg +rx17 equ $-2 + ld (@icnfg3+1),hl + ld a,0c3h + ld (@icnfg3),a + jp move + +;*=*=* +; LS-DOS 6 +;*=*=* +lsdos6: ld a,'4' ;Modify filename + ld (hmod),a + ld hl,hello_ + ld a,@dsply ;Display hello + rst 40 +;*=*=* +; Check if entry from SYSTEM command. +;*=*=* + ld a,@flags ;Get flags pointer into IY + rst 40 + ld a,(iy+'C'-'A') ;Get CFLAG$ + bit 3,a ;System request? + jp z,viaset + ld de,(dct) + ld a,d ;DRIVE= must be specified + or e + jp z,needdr +;*=*=* +; Ask which unit number +;*=*=* +asku4: ld hl,unit_ ;Ask which unit number + ld a,@dsply + rst 40 + ld hl,unit + ld bc,100h + ld a,@keyin + rst 40 + jp c,hitbrk + jp nz,hitbrk + ld a,(unit) + cp '0' + jr c,asku4 + cp '0'+ndrive + jr nc,asku4 +;*=*=* +; Check if driver already loaded +;*=*=* + ld de,modnam + ld a,@gtmod + rst 40 + jp z,setdct ;Already loaded, skip loading +;*=*=* +; Obtain low memory driver pointer. Bizarre API here! +;*=*=* + ld e,'K' ;Locate pointer to *KI DCB + ld d,'I' ; via @GTDCB SVC + ld a,@gtdcb + rst 40 + jp nz,curdl ;No error unless KI clobbered! + dec hl ;Decrement to driver pointer + ld d,(hl) ;P/u hi-order of pointer, + dec hl ; decrement to and p/u + ld e,(hl) ; lo-order of pointer +;*=*=* +; Check if driver will fit into [(LCPTR), X'12FF'] +;*=*=* + push hl ;Save address of pointer + ld hl,length ;New pointer will be + add hl,de ; pointer + LENGTH + ld d,h ;Save a copy in DE + ld e,l + ld bc,1301h ;If > 1300H, driver won't fit + sub a ;Reset carry flag + sbc hl,bc + pop hl ;Get back address of pointer + jr nc,usehi ;Go if driver won't fit + ld (hl),e ;Store new value of pointer + inc hl + ld (hl),d + dec de ;Last byte of driver goes here + ld (newend),de + jr dorelo +;*=*=* +; Put in high memory instead. +;*=*=* +usehi: ld hl,0 ;Get current HIGH$ + ld b,l + ld a,@high + rst 40 + jp nz,nomem + ld (newend),hl ;Last byte of driver goes here + ld de,length + sub a ;Reset carry flag + sbc hl,de ;Compute new HIGH$ + ld a,@high ;Set new HIGH$ into the system + rst 40 +;*=*=* +; Relocate internal references in driver. +; HL = address for last byte of driver. +;*=*=* +dorelo: call dvrini ;Final driver init before move + call relo +;*=*=* +; Link to @ICNFG (must follow address relocation and precede movement) +;*=*=* + ld a,@flags ;Get flags pointer into IY + rst 40 + ld a,(iy+28) ;Copy current @ICNFG into LINK + ld l,(iy+29) + ld h,(iy+30) + ld (link),a + ld (link+1),hl + ld hl,dvrcfg ;Get relocated init address +rx10 equ $-2 + ld (iy+29),l ;Save in @ICNFG vector + ld (iy+30),h + ld (iy+28),0c3h ;Insert JP opcode +;*=*=* +; Move driver into low or high memory. +;*=*=* +move: + ld de,(newend) ;Destination address + ld hl,dvrend ;Last byte of module + ld bc,length ;Length of filter + lddr + ex de,hl + inc hl ;Bump to driver entry +;*=*=* +; Setup DCT (iy+5 to iy+9 are reset by ckopen if successful) +;*=*=* +setdct: + ld iy,(dct) + ld (iy),0c3h ;JP instruction (enable driver) + ld (iy+1),l ;Driver address + ld (iy+2),h + ld (iy+3),00001100b ;Flags: rigid, fixed, step rate 0 + ld a,(unit) + and 0fh + or 00010000b ;Flags: alien (=no index pulses), unit# + ld (iy+4),a + ld (iy+5),0 ;LDOS undefined; we use as sec/cyl (0=256). + ld (iy+6),201 ;high cylinder number + ld (iy+7),11111111b ;high head # (111), high sec/trak (11111) + ld (iy+8),11111111b ;high gran # (111), high sec/gran (11111) + ld (iy+9),0ffh ;Directory cylinder +;*=*=* +; Open file now so user can get error if any, and so geometry +; is established as early as possible. +;*=*=* + call ckopen + ld hl,0 ;Successful completion + ret z ;Fall thru if error +;*=*=* +uerror: ld hl,errbuf ;Unix error + ld bc,256 + defw emt_strerror + defb 0ddh +curdl: ld hl,curdl_ ;Other error + defb 0ddh +needdr: ld hl,needdr_ + defb 0ddh +viaset: ld hl,viaset_ + defb 0ddh +nomem: ld hl,nomem_ + defb 0ddh +hitbrk: ld hl,hitbrk_ +logot: ld a,@logot + rst 40 + ld hl,-1 ;Unuccessful completion + ret + +;*=*=* +; Relocate internal references in driver. +; HL = address for last byte of driver. +;*=*=* +relo: ld hl,(newend) + ld iy,reltab ;Point to relocation tbl + ld de,dvrend + sub a ;Clear carry flag + sbc hl,de + ld b,h ;Move to BC + ld c,l +rloop: ld l,(iy) ;Get address to change + ld h,(iy+1) + ld a,h + or l + ret z + ld e,(hl) ;P/U address + inc hl + ld d,(hl) + ex de,hl ;Offset it + add hl,bc + ex de,hl + ld (hl),d ;And put back + dec hl + ld (hl),e + inc iy + inc iy + jr rloop ;Loop till done + +;*=*=* +; Search for existing copy of driver. +; Rough Model I/III emulation of Model 4 @GTMOD, +; hardcoded with driver address. +; Entry: HL holds HIGH$ +; Exit Z: HL holds driver address +; NZ: driver not found +;*=*=* +xgtmod: inc hl + ld a,h + or l + jr nz,xgtm1 + dec a ;not found + ret +xgtm1: ld a,(hl) + cp 18h ;unconditional jr? + ret nz ;not a module header + push hl ;save start address + inc hl ;skip jr + inc hl ;skip offset + inc hl ;skip start address + inc hl + ld a,(hl) ;compare name length + cp modptr-modnam + jr nz,nextmd ;different - skip + ld b,a ;compare name + ld de,modnam + inc hl +xgtm2: ld a,(de) + cp (hl) + jr nz,nextmd ;different - skip + inc de + inc hl + djnz xgtm2 + pop hl ;same - found + ret +nextmd: pop hl ;get back start of module + inc hl + inc hl + ld e,(hl) ;pointer to last byte + inc hl + ld d,(hl) + ex de,hl + jr xgtmod + +;*=*=* +; Messages and globals +;*=*=* +hello_: defb 'XTRSHARD - Emulated hard disk driver for xtrs - 5/17/00',CR +curdl_: defb 'LS-DOS is curdled!',CR +nomem_: defb 'High memory is not available!',CR +viaset_:defb 'Must install via SYSTEM (DRIVE=,DRIVER=)!',CR +needdr_:defb 'DRIVE= must be specified!',CR +unit_: defb 'Enter unit number ([0]-','0'+ndrive-1,'): ',ETX +hitbrk_:defb 'Aborted!',CR +lcptr: defw 0 +newend: defw 0 +dct: defw 0 +unit: defs 2 +errbuf: defs 256 + +; +; Driver - Based on skeletal driver from the Guide +; + +entry: jr begin ;The driver starts with the + defw dvrend ; DOS standard header +rx00 equ $-2 + defb modptr-modnam ;Length of name +modnam: defb 'xtrshard' ;Name for @GTMOD requests +modptr: defw 0 ;These pointers are unused + defw 0 +fd: defs ndrive*2 ;Unix file descriptors +offset: defw 0,0,0,0 ;lseek offset buffer +begin: +;*=*=* +; First make sure the file is open and correct the geometry +; in the DCT if needed. +;*=*=* + push ix + call ckopen +rx03 equ $-2 + call body +rx06 equ $-2 + pop ix + ret +body: ld a,32 ;"Illegal drive number" + ret nz + ld a,b ;The first test will return + and a ; to the caller on @DCSTAT + ret z ; and set the Z-flag with A=0 +notdcs: cp 7 + jr z,rslct ;Transfer on @RSLCT + jr nc,diskio ;Transfer on physical I/O request +;*=*=* +; @SLCT, @DCINIT, @DCRES, @RSTOR, @STEPI or @SEEK: no-op +;*=*=* +retzer: sub a + ret +;*=*=* +; The RSLCT function should return with the hardware +; write protection status. Set bit 6 of the accumulator +; to indicate the drive is write-protected +;*=*=* +rslct: sub a ;No emulated hardware WP for now + ret +;*=*=* +diskio: bit 2,b ;Test if read or write commands + jr nz,wrcmd ;Transfer if functions <12-15> + cp 10 + jr z,vrsec + jr nc,rdtrk + cp 9 + jr z,rdsec +rdhdr: ld a,32 ;Not supported ("Illegal drive number") + and a + ret +;*=*=* +rdsec: ;Read a sector of data + ld a,(iy+6) ;Get high cyl # + cp d ;At or below it? + jr nc,rdok + ld a,2 ;"Seek error during read" + ret ;NZ already set +rdok: push de + push hl + call doseek ;Setup and do lseek +rx01 equ $-2 + pop hl + ld a,5 ;"Data record not found during read" + jr nz,rddun + ld bc,256 + defw emt_read + ld a,4 ;"Parity error during read" +rddun: pop de + ret nz + ld a,b ;Check for end of file + or c + jr nz,rddun2 + push de + push hl ;Return a block full of 0E5H + push bc + ld (hl),0e5h + ld d,h + ld e,l + inc de + ld bc,0ffh + ldir + pop bc + pop hl + pop de + sub a + ret +rddun2: ld a,d + sub (iy+9) + jr nz,rddun1 + add a,6 ;"Attempted to read system data record" + ret +rddun1: sub a + ret +;*=*=* +vrsec: ;Read/verify -- we don't bother reading + ld a,(iy+6) ;Get high cyl # + cp d ;At or below it? + jr nc,rddun2 ;Go if so + ld a,2 ;"Seek error during read" + ret ;NZ already set +;*=*=* +; On RDSEC and VRSEC, if the read referenced the +; directory cylinder and was successful, +; then you need to return an error code 6. A floppy +; disk controller will provide the indicated status. +; Hard disk users may have to compare the requested +; cylinder to DIRCYL in the DCT. +;*=*=* +rdtrk: ld a,32 ;Not supported ("Illegal drive number") + and a + ret +;*=*=* +wrcmd: bit 7,(iy+3) ;Check for software write protect + jr z,wrcmd1 ;Transfer if no soft WP + ld a,15 ;Set "Write protected disk" error + ret +wrcmd1: cp 14 ;Now parse functions 12-15 + jr z,wrssc + jr nc,wrtrk + cp 13 + jr z,wrsec +;*=*=* +hdfmt: ;Low-level format (=erase) + ld (iy+9),0ffh ;Invalidate directory cylinder + push de + push hl + ld e,(ix) ;Get fd + ld d,(ix+1) +exists: ld hl,256 ;Truncate file to just the header + ld (offset+1),hl +rx07 equ $-2 + ld hl,offset +rx08 equ $-2 + defw emt_ftruncate +creatd: pop hl + pop de + ld a,14 ;"Write fault on disk drive" + ret nz + sub a + ret +;*=*=* +wrsec: ;Write with X'FB' data address mark + ld a,d ;Check if writing track 0, sector 0 + or e + call z,setdir ;Set directory cyl in Reed header +rx20 equ $-2 +;*=*=* +wrssc: ;Write with X'F8' data address mark + ld a,(iy+6) ;Get high cyl # + cp d ;Beyond it? + jr nc,wrok1 + ld a,10 ;"Seek error during write" + ret ;NZ already set +;*=*=* +wrok1: push de + push hl + call doseek +rx04 equ $-2 + pop hl + ld a,13 ;"Data record not found during write" + jr nz,wrdun + ld bc,256 + defw emt_write + ld a,12 ;"Parity error during write" +wrdun: pop de + ret nz + sub a + ret +;*=*=* +wrtrk: ld a,32 ;Write track + and a ;Not supported ("Illegal drive number") + ret +;*=*=* +; Perform lseek before r/w +;*=*=* +doseek: sub a ;sec/cyl to hl, xlate 0 to 256 + ld h,a + add a,(iy+5) + ld l,a + jr nz,noinc + inc h +noinc: ld c,d ;cyl# to c + ld b,e ;sec# to b + ld a,c ;model I/III call uses a, not c +domul: ld a,@mul16 ;hla = hl * c, smash de + rst 40 + ld d,h ;sec# to de (h is 0) + ld e,b + ld h,l ;product to hl + ld l,a + inc hl ;add 1 extra for header + add hl,de + ex de,hl ;offset to de + ld hl,offset+2 +rx15 equ $-2 + ld (hl),d + dec hl + ld (hl),e + dec hl + ld bc,0 + ld (hl),c + ld e,(ix) ;Get fd + ld d,(ix+1) + defw emt_lseek + ret + +;*=*=* +; Open file and read geometry if needed, and +; get address of correct fd to ix. +;*=*=* +ckopen: ld ix,fd ;Compute fd address +rx02 equ $-2 + push de + ld d,0 + ld a,(iy+4) + and 0fh + rlca + ld e,a + add ix,de + pop de + ld a,(ix) ;fd == -1? + and (ix+1) + inc a + jr z,doopen + sub a + ret +doopen: push de + push bc + push hl + ld bc,EO_RDWR ;Prepare to open + ld de,0666o ;mode + ld hl,hard_ ;name +rx05 equ $-2 + ld a,(iy+4) + and 0fh + add a,'0' + ld (hadr),a +rx09 equ $-2 + defw emt_opendisk + ld (ix),e + ld (ix+1),d + jr nz,opnerr + ld hl,offset ;Prepare to read geometry +rx13 equ $-2 + ld (hl),28 ;offset to cyl/sec/gran params + ld bc,0 + ld (offset+1),bc +rx14 equ $-2 + defw emt_lseek + jr nz,opnerr + ld bc,3 ;length + defw emt_read ;use offset buffer + jr nz,opnerr + ld a,(hl) ;cyl + dec a + ld (iy+6),a ;max cylinder + inc hl + ld b,(hl) ;sec + ld (iy+5),b + inc hl + ld c,(hl) ;gran + ld e,b ;compute sec/gran + ld a,c ;model I/III call uses a, not c +dodiv: ld a,@div8 ;a = e / c, e = e % c + rst 40 ;remainder mbz, but we don't check here + dec a + dec c + rrc c + rrc c + rrc c + or c + ld (iy+7),a ;heads, secs per track + ld (iy+8),a ;grans, secs per gran + ld (iy+9),0ffh ;dircyl unknown + sub a ;no error +opnerr: pop hl + pop bc + pop de + ret +;*=*=* +; Sleazy trick to update dir cyl in Reed header: do +; it whenever track 0, sector 0 is written. +; Only important if sharing images with Reed emulator. +;*=*=* +setdir: push hl + push bc + push de + ld hl,offset +rx18 equ $-2 + ld b,d ;de is known to be 0 here + ld c,e + ld e,(ix) ;Get fd + ld d,(ix+1) + ld (hl),31 ;offset to dir cyl param + ld (offset+1),bc +rx19 equ $-2 + defw emt_lseek + ld a,(iy+9) ;dir cyl value to write + ld (hl),a + inc bc + defw emt_write + pop de ;cheat, ignore errors + pop bc + pop hl + ret +;*=*=* +; Boot-time initialization +;*=*=* +dvrcfg: ld de,-1 ;@ICNFG chains in here + defw emt_closedisk ;close any files left from before reboot + call dvrini +rx11 equ $-2 +link: defb 'Tim' ;Replaced by next link in @ICNFG chain +dvrini: ld hl,fd +rx12 equ $-2 + ld d,h + ld e,l + inc de + ld (hl),0ffh + ld bc,ndrive*2-1 + ldir + ret +;*=*=* +; Disk name: hardM-N for model M (1,3,4), number N (0-7) +;*=*=* +hard_: defb 'hard' +hmod: defb '1-' +hadr: defb '0',0 + +dvrend equ $-1 +length equ $-entry +reltab: defw rx00,rx01,rx02,rx03,rx04,rx05,rx06,rx07,rx08,rx09 + defw rx10,rx11,rx12,rx13,rx14,rx15,rx16,rx17,rx18,rx19 + defw rx20,0 + end instal diff --git a/xtrsmous.cmd b/xtrsmous.cmd Binary files differnew file mode 100644 index 0000000..d5b0679 --- /dev/null +++ b/xtrsmous.cmd diff --git a/xtrsmous.lst b/xtrsmous.lst new file mode 100644 index 0000000..ee5aee6 --- /dev/null +++ b/xtrsmous.lst @@ -0,0 +1,323 @@ + 1: ;*=*=* + 2: ; xtrsmous/cmd + 3: ; LS-DOS driver for xtrs emulation of mouse + 4: ; + 5: ; Copyright (c) 1998, Timothy Mann + 6: ; + 7: ; This software may be copied, modified, and used for any + 8: ; purpose without fee, provided that (1) the above copyright + 9: ; notice is retained, and (2) modified versions are clearly + 10: ; marked as having been modified, with the modifier's name and + 11: ; the date included. + 12: ; + 13: ; Created 9-28-98 + 14: ; Last modified on Sat Oct 10 20:57:36 PDT 1998 by mann + 15: ; + 16: ; Usage: + 17: ; xtrsmous To load driver in high memory. + 18: ; xtrsmous (low) To load driver in low memory if possible, + 19: ; or in high memory if low memory is full. + 20: ; + 21: ; The default is to use high memory because MDRAW/BAS contains a + 22: ; PEEK in the first line of code that looks for the driver in + 23: ; high memory; if it is in low memory, MDRAW thinks the driver + 24: ; is not installed and exits. If you edit this line of code to + 25: ; remove the test, the driver will work fine in low memory. + 26: ;*=*=* + 27: + 28: + 29: ; ASCII chars + 30: 000A LF equ 10 + 31: 000D CR equ 13 + 32: 0003 ETX equ 3 + 33: + 34: ; Model 4 SVC numbers + 35: 0064 @high equ 100 + 36: 000A @dsply equ 10 + 37: 0065 @flags equ 101 + 38: 000C @logot equ 12 + 39: 0052 @gtdcb equ 82 + 40: 0053 @gtmod equ 83 + 41: 0009 @keyin equ 9 + 42: 0011 @param equ 17 + 43: 0078 @mouse equ 120 ;unofficial value + 44: + 45: 01F0 svcvec equ 100h + 2*@mouse + 46: + 47: ; xtrs emts (byte-reversed) + 48: 29ED emt_mouse equ 029edh + 49: + 50: 3000 org 3000h + 51: ;*=*=* + 52: ; Relocator for disk driver + 53: ;*=*=* + 54: 3000 116B31 instal: ld de,prmtab + 55: 3003 3E11 ld a,@param ;Parse parameters + 56: 3005 EF rst 40 + 57: 3006 C29530 jp nz,prmerr + 58: 3009 21D230 ld hl,hello_ + 59: 300C 3E0A ld a,@dsply ;Display hello + 60: 300E EF rst 40 + 61: ;*=*=* + 62: ; Check if driver already loaded + 63: ;*=*=* + 64: 300F 118131 ld de,modnam + 65: 3012 3E53 ld a,@gtmod + 66: 3014 EF rst 40 + 67: 3015 CA9930 jp z,loaded ;Already loaded + 68: ;*=*=* + 69: ; Check if OK to use low memory. + 70: ;*=*=* + 71: 3018 010000 lparm: ld bc,0 + 72: 301B 78 ld a,b + 73: 301C B1 or c + 74: 301D 2828 jr z,usehi + 75: ;*=*=* + 76: ; Obtain low memory driver pointer. Bizarre API here! + 77: ;*=*=* + 78: 301F 1E4B ld e,'K' ;Locate pointer to *KI DCB + 79: 3021 1649 ld d,'I' ; via @GTDCB SVC + 80: 3023 3E52 ld a,@gtdcb + 81: 3025 EF rst 40 + 82: 3026 C29D30 jp nz,curdl ;No error unless KI clobbered! + 83: 3029 2B dec hl ;Decrement to driver pointer + 84: 302A 56 ld d,(hl) ;P/u hi-order of pointer, + 85: 302B 2B dec hl ; decrement to and p/u + 86: 302C 5E ld e,(hl) ; lo-order of pointer + 87: ;*=*=* + 88: ; Check if driver will fit into [(LCPTR), X'12FF'] + 89: ;*=*=* + 90: 302D E5 push hl ;Save address of pointer + 91: 302E 211F00 ld hl,length ;New pointer will be + 92: 3031 19 add hl,de ; pointer + LENGTH + 93: 3032 54 ld d,h ;Save a copy in DE + 94: 3033 5D ld e,l + 95: 3034 010113 ld bc,1301h ;If > 1300H, driver won't fit + 96: 3037 97 sub a ;Reset carry flag + 97: 3038 ED42 sbc hl,bc + 98: 303A E1 pop hl ;Get back address of pointer + 99: 303B 300A jr nc,usehi ;Go if driver won't fit + 100: 303D 73 ld (hl),e ;Store new value of pointer + 101: 303E 23 inc hl + 102: 303F 72 ld (hl),d + 103: 3040 1B dec de ;Last byte of driver goes here + 104: 3041 ED536931 ld (newend),de + 105: 3045 1816 jr dorelo + 106: ;*=*=* + 107: ; Put in high memory instead. + 108: ;*=*=* + 109: 3047 210000 usehi: ld hl,0 ;Get current HIGH$ + 110: 304A 45 ld b,l + 111: 304B 3E64 ld a,@high + 112: 304D EF rst 40 + 113: 304E C2A130 jp nz,nomem + 114: 3051 226931 ld (newend),hl ;Last byte of driver goes here + 115: 3054 111F00 ld de,length + 116: 3057 97 sub a ;Reset carry flag + 117: 3058 ED52 sbc hl,de ;Compute new HIGH$ + 118: 305A 3E64 ld a,@high ;Set new HIGH$ into the system + 119: 305C EF rst 40 + 120: ;*=*=* + 121: ; Relocate internal references in driver. + 122: ; HL = address for last byte of driver. + 123: ;*=*=* + 124: 305D CDAB30 dorelo: call relo + 125: ;*=*=* + 126: ; Link to @ICNFG (must follow address relocation and precede movement) + 127: ;*=*=* + 128: 3060 3E65 ld a,@flags ;Get flags pointer into IY + 129: 3062 EF rst 40 + 130: 3063 FD7E1C ld a,(iy+28) ;Copy current @ICNFG into LINK + 131: 3066 FD6E1D ld l,(iy+29) + 132: 3069 FD661E ld h,(iy+30) + 133: 306C 329131 ld (link),a + 134: 306F 229231 ld (link+1),hl + 135: 3072 218E31 ld hl,dvrcfg ;Get relocated init address + 136: 3073 rx01 equ $-2 + 137: 3075 FD751D ld (iy+29),l ;Save in @ICNFG vector + 138: 3078 FD741E ld (iy+30),h + 139: 307B FD361CC3 ld (iy+28),0c3h ;Insert JP opcode + 140: ;*=*=* + 141: ; Move driver into low or high memory. + 142: ;*=*=* + 143: 307F move: + 144: 307F ED5B6931 ld de,(newend) ;Destination address + 145: 3083 219A31 ld hl,dvrend ;Last byte of module + 146: 3086 011F00 ld bc,length ;Length of filter + 147: 3089 EDB8 lddr + 148: 308B EB ex de,hl + 149: 308C 23 inc hl ;Bump to driver entry + 150: ;*=*=* + 151: ; Driver is loaded; finish up. + 152: ;*=*=* + 153: 308D CD9431 call dvrini ;Hook into SVC table + 154: + 155: 3090 210000 ld hl,0 ;Successful completion + 156: 3093 97 sub a + 157: 3094 C9 ret + 158: + 159: ;*=*=* + 160: ; Error vectors + 161: ;*=*=* + 162: 3095 215731 prmerr: ld hl,prmerr_ + 163: 3098 DD defb 0ddh + 164: 3099 213731 loaded: ld hl,loaded_ + 165: 309C DD defb 0ddh + 166: 309D 210631 curdl: ld hl,curdl_ + 167: 30A0 DD defb 0ddh + 168: 30A1 211931 nomem: ld hl,nomem_ + 169: 30A4 3E0C logot: ld a,@logot + 170: 30A6 EF rst 40 + 171: 30A7 21FFFF ld hl,-1 ;Unuccessful completion + 172: 30AA C9 ret + 173: + 174: ;*=*=* + 175: ; Relocate internal references in driver. + 176: ; HL = address for last byte of driver. + 177: ;*=*=* + 178: 30AB 2A6931 relo: ld hl,(newend) + 179: 30AE FD219B31 ld iy,reltab ;Point to relocation tbl + 180: 30B2 119A31 ld de,dvrend + 181: 30B5 97 sub a ;Clear carry flag + 182: 30B6 ED52 sbc hl,de + 183: 30B8 44 ld b,h ;Move to BC + 184: 30B9 4D ld c,l + 185: 30BA FD6E00 rloop: ld l,(iy) ;Get address to change + 186: 30BD FD6601 ld h,(iy+1) + 187: 30C0 7C ld a,h + 188: 30C1 B5 or l + 189: 30C2 C8 ret z + 190: 30C3 5E ld e,(hl) ;P/U address + 191: 30C4 23 inc hl + 192: 30C5 56 ld d,(hl) + 193: 30C6 EB ex de,hl ;Offset it + 194: 30C7 09 add hl,bc + 195: 30C8 EB ex de,hl + 196: 30C9 72 ld (hl),d ;And put back + 197: 30CA 2B dec hl + 198: 30CB 73 ld (hl),e + 199: 30CC FD23 inc iy + 200: 30CE FD23 inc iy + 201: 30D0 18E8 jr rloop ;Loop till done + 202: + 203: ;*=*=* + 204: ; Messages and globals + 205: ;*=*=* + 206: 30D2 58545253 hello_: defb 'XTRSMOUS - Emulated mouse driver for xtrs - 9/28/98',CR + 4D4F5553 + 202D2045 + 6D756C61 + 74656420 + 6D6F7573 + 65206472 + 69766572 + 20666F72 + 20787472 + 73202D20 + 392F3238 + 2F39380D + 207: 3106 4C532D44 curdl_: defb 'LS-DOS is curdled!',CR + 4F532069 + 73206375 + 72646C65 + 64210D + 208: 3119 48696768 nomem_: defb 'High memory is not available!',CR + 206D656D + 6F727920 + 6973206E + 6F742061 + 7661696C + 61626C65 + 210D + 209: 3137 4D6F7573 loaded_:defb 'Mouse driver is already loaded!',CR + 65206472 + 69766572 + 20697320 + 616C7265 + 61647920 + 6C6F6164 + 6564210D + 210: 3157 42616420 prmerr_:defb 'Bad parameters!',CR + 70617261 + 6D657465 + 7273210D + 211: 3167 0000 lcptr: defw 0 + 212: 3169 0000 newend: defw 0 + 213: + 214: ;*=*=* + 215: ; Parameter table + 216: ;*=*=* + 217: 316B 4C4F5720 prmtab: defb 'LOW ' + 2020 + 218: 3171 1930 defw lparm+1 + 219: 3173 4C202020 defb 'L ' + 2020 + 220: 3179 1930 defw lparm+1 + 221: 317B 00 defb 0 + 222: + 223: ;*=*=* + 224: ; Driver + 225: ;*=*=* + 226: 317C 180D entry: jr begin ;The driver starts with the + 227: 317E 9A31 defw dvrend ; DOS standard header + 228: 317E rx00 equ $-2 + 229: 3180 06 defb modptr-modnam ;Length of name + 230: 3181 244D4F55 modnam: defb '$MOUSE' ;Name for @GTMOD requests + 5345 + 231: 3187 0000 modptr: defw 0 ;These pointers are unused, but 1st byte MBZ + 232: 3189 0000 defw 0 + 233: ;*=*=* + 234: ; Do the real work using an emulator trap + 235: ;*=*=* + 236: 318B ED29 begin: defw emt_mouse + 237: 318D C9 ret + 238: ;*=*=* + 239: ; Boot-time initialization + 240: ;*=*=* + 241: 318E dvrcfg: ;@ICNFG chains in here + 242: 318E CD9431 call dvrini + 243: 318F rx02 equ $-2 + 244: 3191 54696D link: defb 'Tim' ;Replaced by next link in @ICNFG chain + 245: ;*=*=* + 246: ; Hook into SVC table + 247: ;*=*=* + 248: 3194 217C31 dvrini: ld hl,entry + 249: 3195 rx03 equ $-2 + 250: 3197 22F001 ld (svcvec),hl + 251: 319A C9 ret + 252: + 253: 319A dvrend equ $-1 + 254: 001F length equ $-entry + 255: 319B 7E317330 reltab: defw rx00,rx01,rx02,rx03,0 + 8F319531 + 0000 + 256: 3000 end instal + + + +Statistics: + + 48 symbols + 421 bytes + + + +Symbol Table: + +@dsply = a emt_mouse =29ed nomem 30a1 +@flags = 65 entry 317c nomem_ 3119 +@gtdcb = 52 etx = 3+ prmerr 3095 +@gtmod = 53 hello_ 30d2 prmerr_ 3157 +@high = 64 instal 3000 prmtab 316b +@keyin = 9+ lcptr 3167+ relo 30ab +@logot = c length = 1f reltab 319b +@mouse = 78 lf = a+ rloop 30ba +@param = 11 link 3191 rx00 =317e +begin 318b loaded 3099 rx01 =3073 +cr = d loaded_ 3137 rx02 =318f +curdl 309d logot 30a4+ rx03 =3195 +curdl_ 3106 lparm 3018 svcvec = 1f0 +dorelo 305d modnam 3181 usehi 3047 +dvrcfg 318e modptr 3187 +dvrend =319a move 307f+ +dvrini 3194 newend 3169 diff --git a/xtrsmous.z80 b/xtrsmous.z80 new file mode 100644 index 0000000..afbfcf7 --- /dev/null +++ b/xtrsmous.z80 @@ -0,0 +1,256 @@ +;*=*=* +; xtrsmous/cmd +; LS-DOS driver for xtrs emulation of mouse +; +; Copyright (c) 1998, Timothy Mann +; +; This software may be copied, modified, and used for any +; purpose without fee, provided that (1) the above copyright +; notice is retained, and (2) modified versions are clearly +; marked as having been modified, with the modifier's name and +; the date included. +; +; Created 9-28-98 +; Last modified on Sat Oct 10 20:57:36 PDT 1998 by mann +; +; Usage: +; xtrsmous To load driver in high memory. +; xtrsmous (low) To load driver in low memory if possible, +; or in high memory if low memory is full. +; +; The default is to use high memory because MDRAW/BAS contains a +; PEEK in the first line of code that looks for the driver in +; high memory; if it is in low memory, MDRAW thinks the driver +; is not installed and exits. If you edit this line of code to +; remove the test, the driver will work fine in low memory. +;*=*=* + + +; ASCII chars +LF equ 10 +CR equ 13 +ETX equ 3 + +; Model 4 SVC numbers +@high equ 100 +@dsply equ 10 +@flags equ 101 +@logot equ 12 +@gtdcb equ 82 +@gtmod equ 83 +@keyin equ 9 +@param equ 17 +@mouse equ 120 ;unofficial value + +svcvec equ 100h + 2*@mouse + +; xtrs emts (byte-reversed) +emt_mouse equ 029edh + + org 3000h +;*=*=* +; Relocator for disk driver +;*=*=* +instal: ld de,prmtab + ld a,@param ;Parse parameters + rst 40 + jp nz,prmerr + ld hl,hello_ + ld a,@dsply ;Display hello + rst 40 +;*=*=* +; Check if driver already loaded +;*=*=* + ld de,modnam + ld a,@gtmod + rst 40 + jp z,loaded ;Already loaded +;*=*=* +; Check if OK to use low memory. +;*=*=* +lparm: ld bc,0 + ld a,b + or c + jr z,usehi +;*=*=* +; Obtain low memory driver pointer. Bizarre API here! +;*=*=* + ld e,'K' ;Locate pointer to *KI DCB + ld d,'I' ; via @GTDCB SVC + ld a,@gtdcb + rst 40 + jp nz,curdl ;No error unless KI clobbered! + dec hl ;Decrement to driver pointer + ld d,(hl) ;P/u hi-order of pointer, + dec hl ; decrement to and p/u + ld e,(hl) ; lo-order of pointer +;*=*=* +; Check if driver will fit into [(LCPTR), X'12FF'] +;*=*=* + push hl ;Save address of pointer + ld hl,length ;New pointer will be + add hl,de ; pointer + LENGTH + ld d,h ;Save a copy in DE + ld e,l + ld bc,1301h ;If > 1300H, driver won't fit + sub a ;Reset carry flag + sbc hl,bc + pop hl ;Get back address of pointer + jr nc,usehi ;Go if driver won't fit + ld (hl),e ;Store new value of pointer + inc hl + ld (hl),d + dec de ;Last byte of driver goes here + ld (newend),de + jr dorelo +;*=*=* +; Put in high memory instead. +;*=*=* +usehi: ld hl,0 ;Get current HIGH$ + ld b,l + ld a,@high + rst 40 + jp nz,nomem + ld (newend),hl ;Last byte of driver goes here + ld de,length + sub a ;Reset carry flag + sbc hl,de ;Compute new HIGH$ + ld a,@high ;Set new HIGH$ into the system + rst 40 +;*=*=* +; Relocate internal references in driver. +; HL = address for last byte of driver. +;*=*=* +dorelo: call relo +;*=*=* +; Link to @ICNFG (must follow address relocation and precede movement) +;*=*=* + ld a,@flags ;Get flags pointer into IY + rst 40 + ld a,(iy+28) ;Copy current @ICNFG into LINK + ld l,(iy+29) + ld h,(iy+30) + ld (link),a + ld (link+1),hl + ld hl,dvrcfg ;Get relocated init address +rx01 equ $-2 + ld (iy+29),l ;Save in @ICNFG vector + ld (iy+30),h + ld (iy+28),0c3h ;Insert JP opcode +;*=*=* +; Move driver into low or high memory. +;*=*=* +move: + ld de,(newend) ;Destination address + ld hl,dvrend ;Last byte of module + ld bc,length ;Length of filter + lddr + ex de,hl + inc hl ;Bump to driver entry +;*=*=* +; Driver is loaded; finish up. +;*=*=* + call dvrini ;Hook into SVC table + + ld hl,0 ;Successful completion + sub a + ret + +;*=*=* +; Error vectors +;*=*=* +prmerr: ld hl,prmerr_ + defb 0ddh +loaded: ld hl,loaded_ + defb 0ddh +curdl: ld hl,curdl_ + defb 0ddh +nomem: ld hl,nomem_ +logot: ld a,@logot + rst 40 + ld hl,-1 ;Unuccessful completion + ret + +;*=*=* +; Relocate internal references in driver. +; HL = address for last byte of driver. +;*=*=* +relo: ld hl,(newend) + ld iy,reltab ;Point to relocation tbl + ld de,dvrend + sub a ;Clear carry flag + sbc hl,de + ld b,h ;Move to BC + ld c,l +rloop: ld l,(iy) ;Get address to change + ld h,(iy+1) + ld a,h + or l + ret z + ld e,(hl) ;P/U address + inc hl + ld d,(hl) + ex de,hl ;Offset it + add hl,bc + ex de,hl + ld (hl),d ;And put back + dec hl + ld (hl),e + inc iy + inc iy + jr rloop ;Loop till done + +;*=*=* +; Messages and globals +;*=*=* +hello_: defb 'XTRSMOUS - Emulated mouse driver for xtrs - 9/28/98',CR +curdl_: defb 'LS-DOS is curdled!',CR +nomem_: defb 'High memory is not available!',CR +loaded_:defb 'Mouse driver is already loaded!',CR +prmerr_:defb 'Bad parameters!',CR +lcptr: defw 0 +newend: defw 0 + +;*=*=* +; Parameter table +;*=*=* +prmtab: defb 'LOW ' + defw lparm+1 + defb 'L ' + defw lparm+1 + defb 0 + +;*=*=* +; Driver +;*=*=* +entry: jr begin ;The driver starts with the + defw dvrend ; DOS standard header +rx00 equ $-2 + defb modptr-modnam ;Length of name +modnam: defb '$MOUSE' ;Name for @GTMOD requests +modptr: defw 0 ;These pointers are unused, but 1st byte MBZ + defw 0 +;*=*=* +; Do the real work using an emulator trap +;*=*=* +begin: defw emt_mouse + ret +;*=*=* +; Boot-time initialization +;*=*=* +dvrcfg: ;@ICNFG chains in here + call dvrini +rx02 equ $-2 +link: defb 'Tim' ;Replaced by next link in @ICNFG chain +;*=*=* +; Hook into SVC table +;*=*=* +dvrini: ld hl,entry +rx03 equ $-2 + ld (svcvec),hl + ret + +dvrend equ $-1 +length equ $-entry +reltab: defw rx00,rx01,rx02,rx03,0 + end instal diff --git a/xtrsrom4p.README b/xtrsrom4p.README new file mode 100644 index 0000000..417f803 --- /dev/null +++ b/xtrsrom4p.README @@ -0,0 +1,89 @@ +Free Model 4P mode boot ROM for xtrs - Version 0.02 +Copyright 1999, Peter W. Cervasio (cervasio@airmail.net) +Modified by Tim Mann to create version 0.05, 4-4-99 + +This software may be copied, modified, and used for any purpose +without fee, provided that (1) the above copyright notice is +retained, and (2) modified versions are clearly marked as having +been modified, with the modifier's name and the date included. + +This is a freely available Model 4P mode boot ROM which can +be used with Tim Mann's version of xtrs in order to boot up +LS-DOS 6.3.1. + +This code was written with the help of the LS-DOS 6.3.1 source +code, which Roy Soltoff has allowed everyone to download and +freely distribute (though he retains the copyright). The Model +4/4P technical reference manual proved invaluable, as well, even +if I didn't make this do everything. + +The code was tested with xtrs 3.3 on Linux 2.2.2 using an LS-DOS +6.3.1 disk image. I got it to the point where xtrs would boot up +LS-DOS 6.3.1 and would reboot it when pressing F10 or typing +"boot" at the LS-DOS command line. + +It is only useful for the Model 4P mode of operation, and it +might not work for all operating systems. The ROM does not +contain any code to access any sector other than track 0, sector +1. It will not boot a Model III operating system, and is unable +to load in a "MODELA/III" ROM image from disk, like the real +Model 4P ROM does. + +This ROM will not work in plain Model 4 mode. If you want to use +Model 4 mode, boot LS-DOS 6.3.1, export the MODELA/III file to +Unix, and set it up as a Model 3 and 4 ROM. + +Features: +========= + +Small and simple (and easy to fix). + +To do: +====== + +Write code for all the RST handlers as in the real 4P ROM, which +would allow xtrs to boot a Model III disk when in Model 4P mode. +This would require considerable work, and isn't planned for any +time in the forseeable future. + +Any questions? +============== + +Here's my list of IEQ's (Infreqently Expected Questions) about the +xtrsrom4p rom image: + +1) Does xtrsrom4p handle the special keypresses the 4P uses at boot up +time, such as F1, F2 and F3 to modify the boot sequence? + +Sorry, but it doesn't look at the keyboard. See the answer to #2. + +2) Will it load MODELA/III from disk so I can boot LDOS/TRSDOS 1.3 or +NewDos/80 for the Model III? + +Sorry. You're welcome to write the code for it, though. + +3) Can I use it to boot from from a hard disk image? + +Um.... no. See the answer to #2. + +4) How about Network 4? + +Uh... no, it won't do that either. Also see #2's answer. + +5) What about booting from RS-232? + +See #2 again. + +6) How about the diagnostics? + +What diagnostics? Oh, those diagnostics. See the answer to #2. + +<grin> + +Along with not doing any of that other stuff, it also doesn't follow +the technical reference as far as RST handlers go, so if you have a +custom 4P setup that uses the RST instructions, that's not going to +work without some additional code. I wrote this so people could get +something up and running with what's in the xtrs distribution. From +there, you can export a MODELA/III image from a disk (or disk image) +to use in booting up Model III mode. diff --git a/xtrsrom4p.hex b/xtrsrom4p.hex new file mode 100644 index 0000000..3cbdc53 --- /dev/null +++ b/xtrsrom4p.hex @@ -0,0 +1,33 @@ +:10000000F33E01D39CC36900C30040000000000020 +:10001000C303400000000000C306400000000000D1 +:10002000C309400000000000C30C400000000000B5 +:10003000C30F400000000000C31240C90000C90007 +:1000400000C90000C90000C90000C90000C90000C3 +:0B005000C32C014D4FAFD39CC30043F5 +:10006600C31540AFD3E43E01D3843E50D3EC3100F8 +:1000760042213B00110040012000EDB021003C115F +:10008600013C01FF033620EDB0ED56CDA000CDD4E6 +:1000960000CCEB00C2B900C31A403E05ED3C7DFE24 +:1000A60005C8F521490111C03D013100EDB0C6304A +:1000B6001218FE217A01371F3804232318F95E230C +:1000C60056EB11C03D7EA728FE12231318F73E817A +:1000D600D3F43ED0CD3901060010FE3E0CCD3901D9 +:1000E600E699C818CE110100210043CDFA00E61C9E +:1000F600C8C3B90006810EF4ED410D3E18ED51CD91 +:1001060039017BD3F23E81D3F41116813E80D3F0C0 +:10011600066410FE3EC0D3E4DBF0A328FBEDA27A12 +:10012600D3F4EDA218FAAFD3E43E81D3F4DBF0E3C7 +:10013600E1ED45D3F0066410FEDBF0CB47C8CB7F7C +:10014600C018F65468697320524F4D20636F64657A +:1001560020646F6573204E4F5420776F726B207743 +:1001660068656E20656D756C6174696E67204D6F8C +:1001760064656C20D4018C018C01A001B6018C0150 +:100186008C01D4018C01456D756C61746F72206FA2 +:100196007220524F4D2062756700426F6F74207354 +:1001A6006563746F7220435243206572726F7200EA +:1001B600426F6F7420736563746F72206E6F742064 +:1001C600666F756E64206F6E206469736B00596F7D +:1001D6007520646F206E6F742068617665206120DB +:1001E6006469736B20696D616765206C6F61646516 +:0201F6006400A3 +:00000001FF diff --git a/xtrsrom4p.lst b/xtrsrom4p.lst new file mode 100644 index 0000000..1a68726 --- /dev/null +++ b/xtrsrom4p.lst @@ -0,0 +1,361 @@ + 1: ;------------------------------------------------------------------ + 2: ; Free Model 4P mode boot ROM for xtrs - Version 0.05 + 3: ; Copyright 1999, Peter W. Cervasio (cervasio@airmail.net) + 4: ;------------------------------------------------------------------ + 5: ; This software may be copied, modified, and used for any purpose + 6: ; without fee, provided that (1) the above copyright notice is + 7: ; retained, and (2) modified versions are clearly marked as having + 8: ; been modified, with the modifier's name and the date included. + 9: ;------------------------------------------------------------------ + 10: ; Checks to see if a disk image is loaded in emulated drive 0. If + 11: ; so, it reads the boot sector to 0x4300 and jumps to that address. + 12: ; If no disk image loaded, complains and sits in a loop. + 13: ; + 14: ; This is only useful for the Model 4P mode of operation, and it + 15: ; might not work for all programs. Complains if xtrs thinks it's + 16: ; in model 1, 3, or 4 mode. + 17: ; + 18: ; Written with the help of the LS-DOS 6.3.1 source code, which is + 19: ; freely avaliable, though Roy Soltoff holds the copyright to it, + 20: ; and the Model 4/4P technical reference. + 21: ; + 22: ; 3-19-99, Tim Mann: Improved disk error handling. Ruled out + 23: ; attempts to work in plain Model 4 mode. (v0.03) + 24: ; 4-4-99, Tim Mann: Made read loop continue until NMI instead of + 25: ; counting 256 bytes; this should let boot sectors of other than + 26: ; 256 bytes work. (v0.04) Fixed bug in sending fdc read command; + 27: ; don't use fdcmd, since that waits for completion. (v0.05) + 28: ; + 29: ; TODO: + 30: ; possibly write code for the RST handlers as in the real 4P rom + 31: ; + 32: ; Last modified on Sat Mar 25 21:33:08 PST 2000 by mann + 33: ; modified on Mon Mar 15 17:25:49 CST 1999 by petec + 34: ; + 35: ;------------------------------------------------------------------ + 36: + 37: ; + 38: ; misc equates needed + 39: ; + 40: 3DC0 video equ 3c00h+7*64 + 41: 4300 bootram equ 4300h ; load to this address + 42: 401A goboot equ 401ah ; jump here to run boot sec! + 43: ; + 44: ; hardware addresses + 45: ; + 46: 0084 opreg equ 084h ; Operation control + 47: 009C romport equ 09ch ; ROM control + 48: 00E4 wrnmiport equ 0e4h ; NMI mask register + 49: 00EC modport equ 0ech ; speed, etc. + 50: ; + 51: 00F0 fdccmnd equ 0f0h ; FDC Command register (write) + 52: 00F0 fdcstat equ 0f0h ; FDC Status register (read) + 53: 00F1 fdctrak equ 0f1h ; FDC Track register (r/w) + 54: 00F2 fdcsect equ 0f2h ; FDC Sector register (r/w) + 55: 00F3 fdcdata equ 0f3h ; FDC Data register (r/w) + 56: 00F4 fdcslct equ 0f4h ; Drive select, SDEN/DDEN, + 57: + 58: 0000 org 0 + 59: + 60: ; The following three instructions are what you would + 61: ; have in RAM at address 0, which would set up the + 62: ; Model 4/4P to turn the ROM back on (lsdos does this) + 63: ; + 64: 0000 F3 start: di ; disable interrupts + 65: 0001 3E01 ld a,1 ; map in the ROM + 66: 0003 D39C out (romport),a ; do it! + 67: ; + 68: ; start of "real" ROM instructions + 69: ; + 70: 0005 C36900 jp contin ; continue farther up + 71: ; + 72: ; rst 08h through rst 38h jumps (per tech ref) + 73: ; + 74: 0008 C30040 rst8: jp 4000h + 75: 000B 00000000 db 0,0,0,0,0 + 00 + 76: 0010 C30340 rst10: jp 4003h + 77: 0013 00000000 db 0,0,0,0,0 + 00 + 78: 0018 C30640 rst18: jp 4006h + 79: 001B 00000000 db 0,0,0,0,0 + 00 + 80: 0020 C30940 rst20: jp 4009h + 81: 0023 00000000 db 0,0,0,0,0 + 00 + 82: 0028 C30C40 rst28: jp 400ch + 83: 002B 00000000 db 0,0,0,0,0 + 00 + 84: 0030 C30F40 rst30: jp 400fh + 85: 0033 00000000 db 0,0,0,0,0 + 00 + 86: 0038 C31240 rst38: jp 4012h + 87: ; + 88: ; Data to load from 4000h to ?? + 89: ; + 90: 003B C9 retdat: ret ; 4000h (rst 8h) + 91: 003C 0000 dw 0 + 92: 003E C9 ret ; 4003h (rst 10h) + 93: 003F 0000 dw 0 + 94: 0041 C9 ret ; 4006h (rst 18h) + 95: 0042 0000 dw 0 + 96: 0044 C9 ret ; 4009h (rst 20h) + 97: 0045 0000 dw 0 + 98: 0047 C9 ret ; 400ch (rst 28h) + 99: 0048 0000 dw 0 + 100: 004A C9 ret ; 400fh (rst 30h) + 101: 004B 0000 dw 0 + 102: 004D C9 ret ; 4012h (rst 38h) + 103: 004E 0000 dw 0 + 104: 0050 C32C01 jp nmiret ; 4015h (nmi) + 105: ; + 106: 0053 4D4F db 'MO' ; 4P detect by sysinit + 107: ; + 108: ; code that jumps to the boot sector (401ah) + 109: ; + 110: 0055 AF xor a + 111: 0056 D39C out (romport),a ; disable rom + 112: 0058 C30043 jp bootram ; run boot sector + 113: ; + 114: 0020 retlen equ $-retdat ; size of code + 115: ; + 116: ; nmi address + 117: ; + 118: 0066 org 66h + 119: ; + 120: 0066 C31540 jp 4015h ; std M4 point in RAM + 121: ; + 122: ; continue booting the machine + 123: ; + 124: 0069 AF contin: xor a + 125: 006A D3E4 out (wrnmiport),a ; disable interrupts + 126: 006C 3E01 ld a,1 + 127: 006E D384 out (opreg),a ; set ram mode + 128: 0070 3E50 ld a,01010000b + 129: 0072 D3EC out (modport),a ; set speed/vid + 130: ; + 131: 0074 310042 ld sp,bootram-100h ; set stack pointer + 132: ; + 133: 0077 213B00 ld hl,retdat ; code for RAM + 134: 007A 110040 ld de,4000h ; move it here + 135: 007D 012000 ld bc,retlen + 136: 0080 EDB0 ldir + 137: ; + 138: 0082 21003C ld hl,3c00h ; clear video screen + 139: 0085 11013C ld de,3c01h + 140: 0088 01FF03 ld bc,1023 + 141: 008B 3620 ld (hl),' ' + 142: 008D EDB0 ldir + 143: ; + 144: 008F ED56 im 1 + 145: ; + 146: 0091 CDA000 call chkmdl ; check for model 4p + 147: 0094 CDD400 call rstdrv ; restore drive + 148: 0097 CCEB00 call z,readbt ; read boot sector + 149: 009A C2B900 jp nz,dskerr ; go on error + 150: ; + 151: 009D C31A40 jmprom: jp goboot ; jump to boot sector + 152: ; + 153: ; chkmdl - make sure we're in 4P mode + 154: ; + 155: 00A0 3E05 chkmdl: ld a,5 ; model query + 156: 00A2 ED3C dw 3cedh ; emt_misc instr. + 157: 00A4 7D ld a,l ; get model in a + 158: 00A5 FE05 cp 5 ; model 4p? + 159: 00A7 C8 ret z + 160: ; + 161: ; romerr - rom installed in model 1, 3, or 4 mode!! + 162: ; + 163: 00A8 F5 romerr: push af + 164: 00A9 214901 ld hl,rommsg ; "invalid rom for model" + 165: 00AC 11C03D ld de,video + 166: 00AF 013100 ld bc,romlen + 167: 00B2 EDB0 ldir ; put msg + 168: 00B4 C630 add a,'0' ; convert model to ascii + 169: 00B6 12 ld (de),a ; stuff onto screen + 170: 00B7 18FE jr $ ; and hang + 171: ; + 172: ; dskerr - error reading disk + 173: ; + 174: 00B9 217A01 dskerr: ld hl,errtab ; table of error messages + 175: 00BC 37 scf ; ensure termination + 176: 00BD 1F nxterr: rra ; loop through bits, low first + 177: 00BE 3804 jr c,goterr ; go if error bit found + 178: 00C0 23 inc hl ; no, step to next message + 179: 00C1 23 inc hl + 180: 00C2 18F9 jr nxterr + 181: 00C4 5E goterr: ld e,(hl) ; get message address + 182: 00C5 23 inc hl + 183: 00C6 56 ld d,(hl) + 184: 00C7 EB ex de,hl + 185: 00C8 11C03D ld de,video ; where to show it + 186: 00CB 7E chrout: ld a,(hl) + 187: 00CC A7 and a + 188: 00CD 28FE jr z,$ ; hang in a loop when done + 189: 00CF 12 ld (de),a + 190: 00D0 23 inc hl + 191: 00D1 13 inc de + 192: 00D2 18F7 jr chrout + 193: ; + 194: ; rstdrv - seek track 0 and set up for boot read + 195: ; + 196: 00D4 3E81 rstdrv: ld a,81h ; drive 0, dden (no nmi) + 197: 00D6 D3F4 out (fdcslct),a ; select drive + 198: 00D8 3ED0 ld a,0d0h ; force interrupt + 199: 00DA CD3901 call fdcmd ; send to fdc + 200: 00DD 0600 ld b,0 + 201: 00DF 10FE djnz $ ; wait a bit + 202: 00E1 3E0C ld a,0ch ; restore w/verify + 203: 00E3 CD3901 call fdcmd ; + 204: 00E6 E699 and 99h ; mask error bits + 205: 00E8 C8 ret z ; return if okay + 206: 00E9 18CE jr dskerr + 207: ; + 208: ; readbt - read boot sector from drive 0 + 209: ; + 210: 00EB 110100 readbt: ld de,0001h ; trk 0, sec 1 + 211: 00EE 210043 ld hl,bootram ; set buffer + 212: 00F1 CDFA00 call readsb ; attempt read + 213: 00F4 E61C and 1ch ; keep RNF,CRC,LOST DATA + 214: 00F6 C8 ret z ; return if no error + 215: 00F7 C3B900 jp dskerr ; go on error + 216: ; + 217: ; readsb - read sector to buffer + 218: ; + 219: 00FA 0681 readsb: ld b,81h + 220: 00FC 0EF4 ld c,fdcslct ; set dden,ds0 + 221: 00FE ED41 out (c),b ; select + 222: 0100 0D dec c ; set to data register + 223: 0101 3E18 ld a,18h ; fdc seek command + 224: 0103 ED51 out (c),d ; track # to fdc + 225: 0105 CD3901 call fdcmd ; send command to fdc + 226: 0108 7B ld a,e + 227: 0109 D3F2 out (fdcsect),a ; desired sector + 228: 010B 3E81 ld a,81h ; dden & ds0 + 229: 010D D3F4 out (fdcslct),a ; reselect drive + 230: 010F 111681 ld de,08116h ; D=DS0, dden, wait + 231: ; E=mask to see DRQ or error + 232: 0112 3E80 ld a,80h ; fdc read command + 233: 0114 D3F0 out (fdccmnd),a ; send command + 234: 0116 0664 ld b,100 ; short delay + 235: 0118 10FE djnz $ + 236: 011A 3EC0 ld a,0c0h ; enable intrq + 237: 011C D3E4 out (wrnmiport),a + 238: 011E DBF0 rdlp1: in a,(fdcstat) ; get status + 239: 0120 A3 and e ; test bit 1 + 240: 0121 28FB jr z,rdlp1 + 241: 0123 EDA2 ini + 242: 0125 7A ld a,d + 243: 0126 D3F4 rdlp2: out (fdcslct),a + 244: 0128 EDA2 ini + 245: 012A 18FA jr rdlp2 ; tight loop waiting for NMI + 246: ; jr nz,rdlp2 + 247: ; in a,(fdcstat) + 248: ; ret + 249: + 250: 012C AF nmiret: xor a + 251: 012D D3E4 out (wrnmiport),a ; no interrupts + 252: 012F 3E81 ld a,81h ; DS 0, dden + 253: 0131 D3F4 out (fdcslct),a + 254: 0133 DBF0 in a,(fdcstat) + 255: 0135 E3 ex (sp),hl ; discard one level of return address + 256: 0136 E1 pop hl + 257: 0137 ED45 retn + 258: + 259: ; + 260: ; fdcmd - send command in A to fdc and wait for completion + 261: ; + 262: 0139 D3F0 fdcmd: out (fdccmnd),a ; send command + 263: 013B 0664 ld b,100 ; short delay + 264: 013D 10FE djnz $ + 265: 013F DBF0 fdst: in a,(fdcstat) + 266: 0141 CB47 bit 0,a ; busy? + 267: 0143 C8 ret z ; return if not + 268: 0144 CB7F bit 7,a ; not ready? + 269: 0146 C0 ret nz ; return if set + 270: 0147 18F6 jr fdst ; else loop + 271: + 272: + 273: ;------------------------------------------------------------------ + 274: ; messages + 275: ;------------------------------------------------------------------ + 276: ; + 277: 0149 54686973 rommsg: db 'This ROM code does NOT work when emulating Model ' + 20524F4D + 20636F64 + 6520646F + 6573204E + 4F542077 + 6F726B20 + 7768656E + 20656D75 + 6C617469 + 6E67204D + 6F64656C + 20 + 278: 0031 romlen equ $-rommsg + 279: ; + 280: 017A D4018C01 errtab: dw dskmsg,ecant,elost,ecrc,ernf,ecant,ecant,dskmsg,ecant + 8C01A001 + B6018C01 + 8C01D401 + 8C01 + 281: 018C elost: + 282: 018C 456D756C ecant: db 'Emulator or ROM bug',0 + 61746F72 + 206F7220 + 524F4D20 + 62756700 + 283: 01A0 426F6F74 ecrc: db 'Boot sector CRC error',0 + 20736563 + 746F7220 + 43524320 + 6572726F + 7200 + 284: 01B6 426F6F74 ernf: db 'Boot sector not found on disk',0 + 20736563 + 746F7220 + 6E6F7420 + 666F756E + 64206F6E + 20646973 + 6B00 + 285: 01D4 596F7520 dskmsg: db 'You do not have a disk image loaded',0 + 646F206E + 6F742068 + 61766520 + 61206469 + 736B2069 + 6D616765 + 206C6F61 + 64656400 + 286: ; + 287: 0000 end start + + + +Statistics: + + 47 symbols + 493 bytes + + + +Symbol Table: + +bootram =4300 fdcstat = f0 romerr a8+ +chkmdl a0 fdctrak = f1+ romlen = 31 +chrout cb fdst 13f rommsg 149 +contin 69 goboot =401a romport = 9c +dskerr b9 goterr c4 rst10 10+ +dskmsg 1d4 jmprom 9d+ rst18 18+ +ecant 18c modport = ec rst20 20+ +ecrc 1a0 nmiret 12c rst28 28+ +elost 18c nxterr bd rst30 30+ +ernf 1b6 opreg = 84 rst38 38+ +errtab 17a rdlp1 11e rst8 8+ +fdccmnd = f0 rdlp2 126 rstdrv d4 +fdcdata = f3+ readbt eb start 0 +fdcmd 139 readsb fa video =3dc0 +fdcsect = f2 retdat 3b wrnmiport = e4 +fdcslct = f4 retlen = 20 diff --git a/xtrsrom4p.z80 b/xtrsrom4p.z80 new file mode 100644 index 0000000..d535ed7 --- /dev/null +++ b/xtrsrom4p.z80 @@ -0,0 +1,287 @@ +;------------------------------------------------------------------ +; Free Model 4P mode boot ROM for xtrs - Version 0.05 +; Copyright 1999, Peter W. Cervasio (cervasio@airmail.net) +;------------------------------------------------------------------ +; This software may be copied, modified, and used for any purpose +; without fee, provided that (1) the above copyright notice is +; retained, and (2) modified versions are clearly marked as having +; been modified, with the modifier's name and the date included. +;------------------------------------------------------------------ +; Checks to see if a disk image is loaded in emulated drive 0. If +; so, it reads the boot sector to 0x4300 and jumps to that address. +; If no disk image loaded, complains and sits in a loop. +; +; This is only useful for the Model 4P mode of operation, and it +; might not work for all programs. Complains if xtrs thinks it's +; in model 1, 3, or 4 mode. +; +; Written with the help of the LS-DOS 6.3.1 source code, which is +; freely avaliable, though Roy Soltoff holds the copyright to it, +; and the Model 4/4P technical reference. +; +; 3-19-99, Tim Mann: Improved disk error handling. Ruled out +; attempts to work in plain Model 4 mode. (v0.03) +; 4-4-99, Tim Mann: Made read loop continue until NMI instead of +; counting 256 bytes; this should let boot sectors of other than +; 256 bytes work. (v0.04) Fixed bug in sending fdc read command; +; don't use fdcmd, since that waits for completion. (v0.05) +; +; TODO: +; possibly write code for the RST handlers as in the real 4P rom +; +; Last modified on Sat Mar 25 21:33:08 PST 2000 by mann +; modified on Mon Mar 15 17:25:49 CST 1999 by petec +; +;------------------------------------------------------------------ + +; +; misc equates needed +; +video equ 3c00h+7*64 +bootram equ 4300h ; load to this address +goboot equ 401ah ; jump here to run boot sec! +; +; hardware addresses +; +opreg equ 084h ; Operation control +romport equ 09ch ; ROM control +wrnmiport equ 0e4h ; NMI mask register +modport equ 0ech ; speed, etc. +; +fdccmnd equ 0f0h ; FDC Command register (write) +fdcstat equ 0f0h ; FDC Status register (read) +fdctrak equ 0f1h ; FDC Track register (r/w) +fdcsect equ 0f2h ; FDC Sector register (r/w) +fdcdata equ 0f3h ; FDC Data register (r/w) +fdcslct equ 0f4h ; Drive select, SDEN/DDEN, + + org 0 + +; The following three instructions are what you would +; have in RAM at address 0, which would set up the +; Model 4/4P to turn the ROM back on (lsdos does this) +; +start: di ; disable interrupts + ld a,1 ; map in the ROM + out (romport),a ; do it! +; +; start of "real" ROM instructions +; + jp contin ; continue farther up +; +; rst 08h through rst 38h jumps (per tech ref) +; +rst8: jp 4000h + db 0,0,0,0,0 +rst10: jp 4003h + db 0,0,0,0,0 +rst18: jp 4006h + db 0,0,0,0,0 +rst20: jp 4009h + db 0,0,0,0,0 +rst28: jp 400ch + db 0,0,0,0,0 +rst30: jp 400fh + db 0,0,0,0,0 +rst38: jp 4012h +; +; Data to load from 4000h to ?? +; +retdat: ret ; 4000h (rst 8h) + dw 0 + ret ; 4003h (rst 10h) + dw 0 + ret ; 4006h (rst 18h) + dw 0 + ret ; 4009h (rst 20h) + dw 0 + ret ; 400ch (rst 28h) + dw 0 + ret ; 400fh (rst 30h) + dw 0 + ret ; 4012h (rst 38h) + dw 0 + jp nmiret ; 4015h (nmi) +; + db 'MO' ; 4P detect by sysinit +; +; code that jumps to the boot sector (401ah) +; + xor a + out (romport),a ; disable rom + jp bootram ; run boot sector +; +retlen equ $-retdat ; size of code +; +; nmi address +; + org 66h +; + jp 4015h ; std M4 point in RAM +; +; continue booting the machine +; +contin: xor a + out (wrnmiport),a ; disable interrupts + ld a,1 + out (opreg),a ; set ram mode + ld a,01010000b + out (modport),a ; set speed/vid +; + ld sp,bootram-100h ; set stack pointer +; + ld hl,retdat ; code for RAM + ld de,4000h ; move it here + ld bc,retlen + ldir +; + ld hl,3c00h ; clear video screen + ld de,3c01h + ld bc,1023 + ld (hl),' ' + ldir +; + im 1 +; + call chkmdl ; check for model 4p + call rstdrv ; restore drive + call z,readbt ; read boot sector + jp nz,dskerr ; go on error +; +jmprom: jp goboot ; jump to boot sector +; +; chkmdl - make sure we're in 4P mode +; +chkmdl: ld a,5 ; model query + dw 3cedh ; emt_misc instr. + ld a,l ; get model in a + cp 5 ; model 4p? + ret z +; +; romerr - rom installed in model 1, 3, or 4 mode!! +; +romerr: push af + ld hl,rommsg ; "invalid rom for model" + ld de,video + ld bc,romlen + ldir ; put msg + add a,'0' ; convert model to ascii + ld (de),a ; stuff onto screen + jr $ ; and hang +; +; dskerr - error reading disk +; +dskerr: ld hl,errtab ; table of error messages + scf ; ensure termination +nxterr: rra ; loop through bits, low first + jr c,goterr ; go if error bit found + inc hl ; no, step to next message + inc hl + jr nxterr +goterr: ld e,(hl) ; get message address + inc hl + ld d,(hl) + ex de,hl + ld de,video ; where to show it +chrout: ld a,(hl) + and a + jr z,$ ; hang in a loop when done + ld (de),a + inc hl + inc de + jr chrout +; +; rstdrv - seek track 0 and set up for boot read +; +rstdrv: ld a,81h ; drive 0, dden (no nmi) + out (fdcslct),a ; select drive + ld a,0d0h ; force interrupt + call fdcmd ; send to fdc + ld b,0 + djnz $ ; wait a bit + ld a,0ch ; restore w/verify + call fdcmd ; + and 99h ; mask error bits + ret z ; return if okay + jr dskerr +; +; readbt - read boot sector from drive 0 +; +readbt: ld de,0001h ; trk 0, sec 1 + ld hl,bootram ; set buffer + call readsb ; attempt read + and 1ch ; keep RNF,CRC,LOST DATA + ret z ; return if no error + jp dskerr ; go on error +; +; readsb - read sector to buffer +; +readsb: ld b,81h + ld c,fdcslct ; set dden,ds0 + out (c),b ; select + dec c ; set to data register + ld a,18h ; fdc seek command + out (c),d ; track # to fdc + call fdcmd ; send command to fdc + ld a,e + out (fdcsect),a ; desired sector + ld a,81h ; dden & ds0 + out (fdcslct),a ; reselect drive + ld de,08116h ; D=DS0, dden, wait + ; E=mask to see DRQ or error + ld a,80h ; fdc read command + out (fdccmnd),a ; send command + ld b,100 ; short delay + djnz $ + ld a,0c0h ; enable intrq + out (wrnmiport),a +rdlp1: in a,(fdcstat) ; get status + and e ; test bit 1 + jr z,rdlp1 + ini + ld a,d +rdlp2: out (fdcslct),a + ini + jr rdlp2 ; tight loop waiting for NMI +; jr nz,rdlp2 +; in a,(fdcstat) +; ret + +nmiret: xor a + out (wrnmiport),a ; no interrupts + ld a,81h ; DS 0, dden + out (fdcslct),a + in a,(fdcstat) + ex (sp),hl ; discard one level of return address + pop hl + retn + +; +; fdcmd - send command in A to fdc and wait for completion +; +fdcmd: out (fdccmnd),a ; send command + ld b,100 ; short delay + djnz $ +fdst: in a,(fdcstat) + bit 0,a ; busy? + ret z ; return if not + bit 7,a ; not ready? + ret nz ; return if set + jr fdst ; else loop + + +;------------------------------------------------------------------ +; messages +;------------------------------------------------------------------ +; +rommsg: db 'This ROM code does NOT work when emulating Model ' +romlen equ $-rommsg +; +errtab: dw dskmsg,ecant,elost,ecrc,ernf,ecant,ecant,dskmsg,ecant +elost: +ecant: db 'Emulator or ROM bug',0 +ecrc: db 'Boot sector CRC error',0 +ernf: db 'Boot sector not found on disk',0 +dskmsg: db 'You do not have a disk image loaded',0 +; + end start @@ -0,0 +1,4370 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Thu May 3 02:49:09 PDT 2001 by mann +*/ + +/* + * z80.c: The guts of the Z-80 emulator. + * + * The Z-80 emulator should be general and complete enough to be + * easily adapted to emulate any Z-80 machine. All of the documented + * Z-80 flags and instructions are implemented. The only documented + * features we cheat a little on are interrupt handling (modes 0 and 2 + * are not supported) and the refresh register (reading it returns a + * random number; writing it is ignored). + * + * All of the undocumented instructions, flags, and features listed in + * http://www.msxnet.org/tech/Z80/z80undoc.txt are implemented too, + * with some minor exceptions. There seems to be a disagreement about + * undocumented flag handling for "bit" instructions between + * z80undoc.txt and the ZEXALL validator from Yaze. Since ZEXALL + * passes on both a real Z-80 and Yaze, but fails on my attempt to + * implement "bit n,r" according to z80undoc.txt, I've imitated Yaze's + * implementation. On block in/out instructions, z80undoc.txt gives + * some very complicated rules for undocumented flag handling that I + * have not implemented. + */ + +#include "z80.h" +#include "trs.h" +#include "trs_imp_exp.h" +#include <stdlib.h> /* for rand() */ +#include <unistd.h> /* for pause() */ +#include <time.h> /* for time() */ + +/* + * Keep Saber quiet. + */ +/*SUPPRESS 53*/ +/*SUPPRESS 51*/ +/*SUPPRESS 112*/ +/*SUPPRESS 115*/ + +/* + * The state of our Z-80 registers is kept in this structure: + */ +struct z80_state_struct z80_state; + +/* + * Tables and routines for computing various flag values: + */ + +static Uchar sign_carry_overflow_table[] = +{ + 0, + OVERFLOW_MASK | SIGN_MASK, + CARRY_MASK, + SIGN_MASK, + CARRY_MASK, + SIGN_MASK, + CARRY_MASK | OVERFLOW_MASK, + CARRY_MASK | SIGN_MASK, +}; + +static Uchar half_carry_table[] = +{ + 0, + 0, + HALF_CARRY_MASK, + 0, + HALF_CARRY_MASK, + 0, + HALF_CARRY_MASK, + HALF_CARRY_MASK, +}; + +static Uchar subtract_sign_carry_overflow_table[] = +{ + 0, + CARRY_MASK | SIGN_MASK, + CARRY_MASK, + OVERFLOW_MASK | CARRY_MASK | SIGN_MASK, + OVERFLOW_MASK, + SIGN_MASK, + 0, + CARRY_MASK | SIGN_MASK, +}; + +static Uchar subtract_half_carry_table[] = +{ + 0, + HALF_CARRY_MASK, + HALF_CARRY_MASK, + HALF_CARRY_MASK, + 0, + 0, + 0, + HALF_CARRY_MASK, +}; + +static int parity(unsigned value) +{ + /* for parity flag, 1 = even parity, 0 = odd parity. */ + static char parity_table[256] = + { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 + }; + + return(parity_table[value]); +} + +static void do_add_flags(int a, int b, int result) +{ + /* + * Compute the flag values for a + b = result operation + */ + int index; + int f; + + /* + * Sign, carry, and overflow depend upon values of bit 7. + * Half-carry depends upon values of bit 3. + * We mask those bits, munge them into an index, and look + * up the flag values in the above tables. + * Undocumented flags in bit 3, 5 of F come from the result. + */ + + + index = ((a & 0x88) >> 1) | ((b & 0x88) >> 2) | ((result & 0x88) >> 3); + f = half_carry_table[index & 7] | + sign_carry_overflow_table[index >> 4] | + (result & (UNDOC3_MASK|UNDOC5_MASK)); + + if((result & 0xFF) == 0) f |= ZERO_MASK; + + REG_F = f; +} + +static void do_sub_flags(int a, int b, int result) +{ + int index; + int f; + + /* + * Sign, carry, and overflow depend upon values of bit 7. + * Half-carry depends upon values of bit 3. + * We mask those bits, munge them into an index, and look + * up the flag values in the above tables. + * Undocumented flags in bit 3, 5 of F come from the result. + */ + + index = ((a & 0x88) >> 1) | ((b & 0x88) >> 2) | ((result & 0x88) >> 3); + f = SUBTRACT_MASK | subtract_half_carry_table[index & 7] | + subtract_sign_carry_overflow_table[index >> 4] | + (result & (UNDOC3_MASK|UNDOC5_MASK)); + + if((result & 0xFF) == 0) f |= ZERO_MASK; + + REG_F = f; +} + + +static void do_adc_word_flags(int a, int b, int result) +{ + int index; + int f; + + /* + * Sign, carry, and overflow depend upon values of bit 15. + * Half-carry depends upon values of bit 11. + * We mask those bits, munge them into an index, and look + * up the flag values in the above tables. + * Undocumented flags in bit 3, 5 of F come from the result high byte. + */ + + index = ((a & 0x8800) >> 9) | ((b & 0x8800) >> 10) | + ((result & 0x8800) >> 11); + + f = half_carry_table[index & 7] | + sign_carry_overflow_table[index >> 4] | + ((result >> 8) & (UNDOC3_MASK|UNDOC5_MASK)); + + if((result & 0xFFFF) == 0) f |= ZERO_MASK; + + REG_F = f; +} + +static void do_add_word_flags(int a, int b, int result) +{ + int index; + int f; + + /* + * Carry depends upon values of bit 15. + * Half-carry depends upon values of bit 11. + * We mask those bits, munge them into an index, and look + * up the flag values in the above tables. + * Undocumented flags in bit 3, 5 of F come from the result high byte. + */ + + index = ((a & 0x8800) >> 9) | ((b & 0x8800) >> 10) | + ((result & 0x8800) >> 11); + + f = half_carry_table[index & 7] | + (sign_carry_overflow_table[index >> 4] & CARRY_MASK) | + (REG_F & (ZERO_MASK | PARITY_MASK | SIGN_MASK)) | + ((result >> 8) & (UNDOC3_MASK | UNDOC5_MASK)); + + REG_F = f; +} + +static void do_sbc_word_flags(int a, int b, int result) +{ + int index; + int f; + + /* + * Sign, carry, and overflow depend upon values of bit 15. + * Half-carry depends upon values of bit 11. + * We mask those bits, munge them into an index, and look + * up the flag values in the above tables. + * Undocumented flags in bit 3, 5 of F come from the result high byte. + */ + + index = ((a & 0x8800) >> 9) | ((b & 0x8800) >> 10) | + ((result & 0x8800) >> 11); + + f = SUBTRACT_MASK | subtract_half_carry_table[index & 7] | + subtract_sign_carry_overflow_table[index >> 4] | + ((result >> 8) & (UNDOC3_MASK | UNDOC5_MASK)); + + if((result & 0xFFFF) == 0) f |= ZERO_MASK; + + REG_F = f; +} + +static void do_flags_dec_byte(int value) +{ + Uchar set; + + set = SUBTRACT_MASK; + + if(value == 0x7f) + set |= OVERFLOW_MASK; + if((value & 0xF) == 0xF) + set |= HALF_CARRY_MASK; + if(value == 0) + set |= ZERO_MASK; + if(value & 0x80) + set |= SIGN_MASK; + + REG_F = (REG_F & CARRY_MASK) | set + | (value & (UNDOC3_MASK | UNDOC5_MASK)); +} + +static void do_flags_inc_byte(int value) +{ + Uchar set; + + set = 0; + + if(value == 0x80) + set |= OVERFLOW_MASK; + if((value & 0xF) == 0) + set |= HALF_CARRY_MASK; + if(value == 0) + set |= ZERO_MASK; + if(value & 0x80) + set |= SIGN_MASK; + + REG_F = (REG_F & CARRY_MASK) | set + | (value & (UNDOC3_MASK | UNDOC5_MASK)); +} + +/* + * Routines for executing or assisting various non-trivial arithmetic + * instructions: + */ +static void do_and_byte(int value) +{ + int result; + Uchar set; + + result = (REG_A &= value); + + set = HALF_CARRY_MASK; + + if(parity(result)) + set |= PARITY_MASK; + if(result == 0) + set |= ZERO_MASK; + if(result & 0x80) + set |= SIGN_MASK; + + REG_F = set | (result & (UNDOC3_MASK | UNDOC5_MASK)); +} + +static void do_or_byte(int value) +{ + int result; /* the result of the or operation */ + Uchar set; + + result = (REG_A |= value); + + set = 0; + + if(parity(result)) + set |= PARITY_MASK; + if(result == 0) + set |= ZERO_MASK; + if(result & 0x80) + set |= SIGN_MASK; + + REG_F = set | (result & (UNDOC3_MASK | UNDOC5_MASK)); +} + +static void do_xor_byte(int value) +{ + int result; /* the result of the xor operation */ + Uchar set; + + result = (REG_A ^= value); + + set = 0; + + if(parity(result)) + set |= PARITY_MASK; + if(result == 0) + set |= ZERO_MASK; + if(result & 0x80) + set |= SIGN_MASK; + + REG_F = set | (result & (UNDOC3_MASK | UNDOC5_MASK)); +} + +static void do_add_byte(int value) +{ + int a, result; + + result = (a = REG_A) + value; + REG_A = result; + do_add_flags(a, value, result); +} + +static void do_adc_byte(int value) +{ + int a, result; + + if(CARRY_FLAG) + result = (a = REG_A) + value + 1; + else + result = (a = REG_A) + value; + REG_A = result; + do_add_flags(a, value, result); +} + +static void do_sub_byte(int value) +{ + int a, result; + + result = (a = REG_A) - value; + REG_A = result; + do_sub_flags(a, value, result); +} + +static void do_negate() +{ + int a; + + a = REG_A; + REG_A = - a; + do_sub_flags(0, a, REG_A); +} + +static void do_sbc_byte(int value) +{ + int a, result; + + if(CARRY_FLAG) + result = (a = REG_A) - (value + 1); + else + result = (a = REG_A) - value; + REG_A = result; + do_sub_flags(a, value, result); +} + +static void do_add_word(int value) +{ + int a, result; + + result = (a = REG_HL) + value; + REG_HL = result; + + do_add_word_flags(a, value, result); +} + +static void do_adc_word(int value) +{ + int a, result; + + if(CARRY_FLAG) + result = (a = REG_HL) + value + 1; + else + result = (a = REG_HL) + value; + + REG_HL = result; + + do_adc_word_flags(a, value, result); +} + +static void do_sbc_word(int value) +{ + int a, result; + + if(CARRY_FLAG) + result = (a = REG_HL) - (value + 1); + else + result = (a = REG_HL) - value; + + REG_HL = result; + + do_sbc_word_flags(a, value, result); +} + +static void do_add_word_index(Ushort *regp, int value) +{ + int a, result; + + result = (a = *regp) + value; + *regp = result; + + do_add_word_flags(a, value, result); +} + +/* compare this value with A's contents */ +static void do_cp(int value) +{ + int a, result; + int index; + int f; + + result = (a = REG_A) - value; + + /* + * Sign, carry, and overflow depend upon values of bit 7. + * Half-carry depends upon values of bit 3. + * We mask those bits, munge them into an index, and look + * up the flag values in the above tables. + * Undocumented flags in bit 3, 5 of F come from the second operand. + */ + + index = ((a & 0x88) >> 1) | ((value & 0x88) >> 2) | ((result & 0x88) >> 3); + f = SUBTRACT_MASK | subtract_half_carry_table[index & 7] | + subtract_sign_carry_overflow_table[index >> 4] | + (value & (UNDOC3_MASK|UNDOC5_MASK)); + + if((result & 0xFF) == 0) f |= ZERO_MASK; + + REG_F = f; +} + +static void do_cpd() +{ + int oldcarry = REG_F & CARRY_MASK; + int a, value, result; + value = mem_read(REG_HL); + result = (a = REG_A) - value; + REG_HL--; + REG_BC--; + + do_sub_flags(a, value, result); + REG_F = (REG_F & ~(CARRY_MASK | OVERFLOW_MASK | UNDOC5_MASK)) + | oldcarry | (REG_BC == 0 ? 0 : OVERFLOW_MASK) + | (((result - ((REG_F & HALF_CARRY_MASK) >> 4)) & 2) << 4); + if ((result & 15) == 8 && (REG_F & HALF_CARRY_MASK) != 0) { + REG_F &= ~UNDOC3_MASK; + } + + T_COUNT(16); +} + +static void do_cpi() +{ + int oldcarry = REG_F & CARRY_MASK; + int a, value, result; + value = mem_read(REG_HL); + result = (a = REG_A) - value; + REG_HL++; + REG_BC--; + + do_sub_flags(a, value, result); + REG_F = (REG_F & ~(CARRY_MASK | OVERFLOW_MASK | UNDOC5_MASK)) + | oldcarry | (REG_BC == 0 ? 0 : OVERFLOW_MASK) + | (((result - ((REG_F & HALF_CARRY_MASK) >> 4)) & 2) << 4); + if ((result & 15) == 8 && (REG_F & HALF_CARRY_MASK) != 0) { + REG_F &= ~UNDOC3_MASK; + } + + T_COUNT(16); +} + +static void do_cpdr() +{ + int oldcarry = REG_F & CARRY_MASK; + int a = REG_A, value, result; + do + { + result = a - (value = mem_read(REG_HL)); + REG_HL--; + REG_BC--; + + T_COUNT(21); + + } while((REG_BC != 0) && (result != 0)); + + do_sub_flags(a, value, result); + REG_F = (REG_F & ~(CARRY_MASK | OVERFLOW_MASK | UNDOC5_MASK)) + | oldcarry | (REG_BC == 0 ? 0 : OVERFLOW_MASK) + | (((result - ((REG_F & HALF_CARRY_MASK) >> 4)) & 2) << 4); + if ((result & 15) == 8 && (REG_F & HALF_CARRY_MASK) != 0) { + REG_F &= ~UNDOC3_MASK; + } + + T_COUNT(-5); +} + +static void do_cpir() +{ + int oldcarry = REG_F & CARRY_MASK; + int a = REG_A, value, result; + do + { + result = a - (value = mem_read(REG_HL)); + REG_HL++; + REG_BC--; + + T_COUNT(21); + + } while((REG_BC != 0) && (result != 0)); + + do_sub_flags(a, value, result); + REG_F = (REG_F & ~(CARRY_MASK | OVERFLOW_MASK | UNDOC5_MASK)) + | oldcarry | (REG_BC == 0 ? 0 : OVERFLOW_MASK) + | (((result - ((REG_F & HALF_CARRY_MASK) >> 4)) & 2) << 4); + if ((result & 15) == 8 && (REG_F & HALF_CARRY_MASK) != 0) { + REG_F &= ~UNDOC3_MASK; + } + + T_COUNT(-5); +} + +#if 1 +/* The following passes ZEXALL and matches Yaze, but if you believe + http://www.msxnet.org/tech/Z80/z80undoc.txt, it gets UNDOC3 and UNDOC5 wrong. + It remains to be seen which (if either) is really right. */ +static void do_test_bit(int op, int value, int bit) +{ + int result = value & (1 << bit); + REG_F = (REG_F & CARRY_MASK) | HALF_CARRY_MASK | (result & SIGN_MASK) + | (result ? 0 : (OVERFLOW_MASK | ZERO_MASK)) + | (((op & 7) == 6) ? 0 : (value & (UNDOC3_MASK | UNDOC5_MASK))); +} +#else +/* The following matches http://www.msxnet.org/tech/Z80/z80undoc.txt + for "bit n,r", but does not attempt to emulate the full weirdness + of "bit n,(ix/iy+d)" and "bit n,(hl)". It fails ZEXALL even if + code is added to make the latter two instructions behave as in + the version that passes ZEXALL, leading me to think that z80undoc.txt + may be mistaken about "bit n,r". This should be checked in detail + against a real Z-80, I suppose. Ugh. */ +static void do_test_bit(int op, int value, int bit) +{ + int result = value & (1 << bit); + REG_F = (REG_F & CARRY_MASK) | HALF_CARRY_MASK + | (result & (UNDOC3_MASK | UNDOC5_MASK | SIGN_MASK)) + | (result ? 0 : (OVERFLOW_MASK | ZERO_MASK)); +} +#endif + +static int rl_byte(int value) +{ + /* + * Compute rotate-left-through-carry + * operation, setting flags as appropriate. + */ + + Uchar set; + int result; + + set = 0; + + if(CARRY_FLAG) + { + result = ((value << 1) & 0xFF) | 1; + } + else + { + result = (value << 1) & 0xFF; + } + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + if(value & 0x80) + set |= CARRY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +static int rr_byte(int value) +{ + /* + * Compute rotate-right-through-carry + * operation, setting flags as appropriate. + */ + + Uchar set; + int result; + + set = 0; + + if(CARRY_FLAG) + { + result = (value >> 1) | 0x80; + } + else + { + result = (value >> 1); + } + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + if(value & 0x1) + set |= CARRY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +static int rlc_byte(int value) +{ + /* + * Compute the result of an RLC operation and set the flags appropriately. + * This does not do the right thing for the RLCA instruction. + */ + + Uchar set; + int result; + + set = 0; + + if(value & 0x80) + { + result = ((value << 1) & 0xFF) | 1; + set |= CARRY_MASK; + } + else + { + result = (value << 1) & 0xFF; + } + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +static int rrc_byte(int value) +{ + Uchar set; + int result; + + set = 0; + + if(value & 0x1) + { + result = (value >> 1) | 0x80; + set |= CARRY_MASK; + } + else + { + result = (value >> 1); + } + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +/* + * Perform the RLA, RLCA, RRA, RRCA instructions. These set the flags + * differently than the other rotate instrucitons. + */ +static void do_rla() +{ + Uchar set; + + set = 0; + + if(REG_A & 0x80) + set |= CARRY_MASK; + + if(CARRY_FLAG) + { + REG_A = ((REG_A << 1) & 0xFF) | 1; + } + else + { + REG_A = (REG_A << 1) & 0xFF; + } + + REG_F = (REG_F & (OVERFLOW_MASK | ZERO_MASK | SIGN_MASK)) + | set | (REG_A & (UNDOC3_MASK | UNDOC5_MASK )); +} + +static void do_rra() +{ + Uchar set; + + set = 0; + + if(REG_A & 0x1) + set |= CARRY_MASK; + + if(CARRY_FLAG) + { + REG_A = (REG_A >> 1) | 0x80; + } + else + { + REG_A = REG_A >> 1; + } + REG_F = (REG_F & (OVERFLOW_MASK | ZERO_MASK | SIGN_MASK)) + | set | (REG_A & (UNDOC3_MASK | UNDOC5_MASK )); +} + +static void do_rlca() +{ + Uchar set; + + set = 0; + + if(REG_A & 0x80) + { + REG_A = ((REG_A << 1) & 0xFF) | 1; + set |= CARRY_MASK; + } + else + { + REG_A = (REG_A << 1) & 0xFF; + } + REG_F = (REG_F & (OVERFLOW_MASK | ZERO_MASK | SIGN_MASK)) + | set | (REG_A & (UNDOC3_MASK | UNDOC5_MASK )); +} + +static void do_rrca() +{ + Uchar set; + + set = 0; + + if(REG_A & 0x1) + { + REG_A = (REG_A >> 1) | 0x80; + set |= CARRY_MASK; + } + else + { + REG_A = REG_A >> 1; + } + REG_F = (REG_F & (OVERFLOW_MASK | ZERO_MASK | SIGN_MASK)) + | set | (REG_A & (UNDOC3_MASK | UNDOC5_MASK )); +} + +static int sla_byte(int value) +{ + Uchar set; + int result; + + set = 0; + + result = (value << 1) & 0xFF; + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + if(value & 0x80) + set |= CARRY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +static int sra_byte(int value) +{ + Uchar set; + int result; + + set = 0; + + if(value & 0x80) + { + result = (value >> 1) | 0x80; + set |= SIGN_MASK; + } + else + { + result = value >> 1; + } + + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + if(value & 0x1) + set |= CARRY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +/* undocumented opcode slia: shift left and increment */ +static int slia_byte(int value) +{ + Uchar set; + int result; + + set = 0; + + result = ((value << 1) & 0xFF) | 1; + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + if(value & 0x80) + set |= CARRY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +static int srl_byte(int value) +{ + Uchar set; + int result; + + set = 0; + + result = value >> 1; + + if(result & 0x80) + set |= SIGN_MASK; + if(result == 0) + set |= ZERO_MASK; + if(parity(result)) + set |= PARITY_MASK; + if(value & 0x1) + set |= CARRY_MASK; + + REG_F = (result & (UNDOC3_MASK | UNDOC5_MASK)) | set; + + return result; +} + +static void do_ldd() +{ + int moved, undoc; + mem_write(REG_DE, moved = mem_read(REG_HL)); + REG_DE--; + REG_HL--; + REG_BC--; + + if(REG_BC == 0) + CLEAR_OVERFLOW(); + else + SET_OVERFLOW(); + undoc = REG_A + moved; + REG_F = (REG_F & ~(UNDOC3_MASK|UNDOC5_MASK|HALF_CARRY_MASK|SUBTRACT_MASK)) + | (undoc & UNDOC3_MASK) | ((undoc & 2) ? UNDOC5_MASK : 0); + T_COUNT(16); +} + +static void do_ldi() +{ + int moved, undoc; + mem_write(REG_DE, moved = mem_read(REG_HL)); + REG_DE++; + REG_HL++; + REG_BC--; + + if(REG_BC == 0) + CLEAR_OVERFLOW(); + else + SET_OVERFLOW(); + undoc = REG_A + moved; + REG_F = (REG_F & ~(UNDOC3_MASK|UNDOC5_MASK|HALF_CARRY_MASK|SUBTRACT_MASK)) + | (undoc & UNDOC3_MASK) | ((undoc & 2) ? UNDOC5_MASK : 0); + T_COUNT(16); +} + +static void do_ldir() +{ + /* repeating block load with increment */ + int moved, undoc; + + moved = mem_block_transfer(REG_DE, REG_HL, 1, REG_BC); + T_COUNT(((REG_BC-1) & 0xffff) * 21 + 16); + + /* set registers to final values */ + REG_DE += REG_BC; + REG_HL += REG_BC; + REG_BC = 0; + + /* set up flags */ + undoc = REG_A + moved; + REG_F = (REG_F & (CARRY_MASK | ZERO_MASK | SIGN_MASK)) + | (undoc & UNDOC3_MASK) | ((undoc & 2) ? UNDOC5_MASK : 0); +} + +static void do_lddr() +{ + /* repeating block load with decrement */ + int moved, undoc; + + moved = mem_block_transfer(REG_DE, REG_HL, -1, REG_BC); + T_COUNT(((REG_BC-1) & 0xffff) * 21 + 16); + + /* set registers to final values */ + REG_DE -= REG_BC; + REG_HL -= REG_BC; + REG_BC = 0; + + /* set up flags */ + undoc = REG_A + moved; + REG_F = (REG_F & (CARRY_MASK | ZERO_MASK | SIGN_MASK)) + | (undoc & UNDOC3_MASK) | ((undoc & 2) ? UNDOC5_MASK : 0); +} + +static void do_ld_a_i() +{ + Uchar set; + + set = 0; + + REG_A = REG_I; + + if(REG_A & 0x80) + set |= SIGN_MASK; + if(REG_A == 0) + set |= ZERO_MASK; + + if(z80_state.iff2) + set |= OVERFLOW_MASK; + + REG_F = (REG_F & CARRY_MASK) | (REG_A & (UNDOC3_MASK | UNDOC5_MASK)) | set; +} + +static void do_ld_a_r() +{ + Uchar set; + + set = 0; + + /* Fetch a random value. */ + REG_A = (rand() >> 8) & 0xFF; + + if(REG_A & 0x80) + set |= SIGN_MASK; + if(REG_A == 0) + set |= ZERO_MASK; + + if(z80_state.iff2) + set |= OVERFLOW_MASK; + + REG_F = (REG_F & CARRY_MASK) | (REG_A & (UNDOC3_MASK | UNDOC5_MASK)) | set; +} + +/* Completely new implementation adapted from yaze. + The old one was very wrong. */ +static void do_daa() +{ + int a = REG_A, f = REG_F; + int alow = a & 0xf; + int carry = f & CARRY_MASK; + int hcarry = f & HALF_CARRY_MASK; + if (f & SUBTRACT_MASK) { + int hd = carry || a > 0x99; + if (hcarry || alow > 9) { + if (alow > 5) hcarry = 0; + a = (a - 6) & 0xff; + } + if (hd) a -= 0x160; + } else { + if (hcarry || alow > 9) { + hcarry = alow > 9 ? HALF_CARRY_MASK : 0; + a += 6; + } + if (carry || ((a & 0x1f0) > 0x90)) { + a += 0x60; + } + } + if (a & 0x100) carry = CARRY_MASK; + + REG_A = a = a & 0xff; + REG_F = ((a & 0x80) ? SIGN_MASK : 0) + | (a & (UNDOC3_MASK|UNDOC5_MASK)) + | (a ? 0 : ZERO_MASK) + | (f & SUBTRACT_MASK) + | hcarry | (parity(a) ? PARITY_MASK : 0) | carry; +} + +static void do_rld() +{ + /* + * Rotate-left-decimal. + */ + int old_value, new_value; + Uchar set; + + set = 0; + + old_value = mem_read(REG_HL); + + /* left-shift old value, add lower bits of a */ + new_value = ((old_value << 4) | (REG_A & 0x0f)) & 0xff; + + /* rotate high bits of old value into low bits of a */ + REG_A = (REG_A & 0xf0) | (old_value >> 4); + + if(REG_A & 0x80) + set |= SIGN_MASK; + if(REG_A == 0) + set |= ZERO_MASK; + if(parity(REG_A)) + set |= PARITY_MASK; + + REG_F = (REG_F & CARRY_MASK) | set | (REG_A & (UNDOC3_MASK | UNDOC5_MASK)); + mem_write(REG_HL,new_value); +} + +static void do_rrd() +{ + /* + * Rotate-right-decimal. + */ + int old_value, new_value; + Uchar set; + + set = 0; + + old_value = mem_read(REG_HL); + + /* right-shift old value, add lower bits of a */ + new_value = (old_value >> 4) | ((REG_A & 0x0f) << 4); + + /* rotate low bits of old value into low bits of a */ + REG_A = (REG_A & 0xf0) | (old_value & 0x0f); + + if(REG_A & 0x80) + set |= SIGN_MASK; + if(REG_A == 0) + set |= ZERO_MASK; + if(parity(REG_A)) + set |= PARITY_MASK; + + REG_F = (REG_F & CARRY_MASK) | set | (REG_A & (UNDOC3_MASK | UNDOC5_MASK)); + mem_write(REG_HL,new_value); +} + + +/* + * Input/output instruction support: + */ + +static void do_ind() +{ + mem_write(REG_HL, z80_in(REG_C)); + REG_HL--; + REG_B--; + + if(REG_B == 0) + SET_ZERO(); + else + CLEAR_ZERO(); + + SET_SUBTRACT(); + T_COUNT(15); +} + +static void do_indr() +{ + do + { + mem_write(REG_HL, z80_in(REG_C)); + REG_HL--; + REG_B--; + T_COUNT(20); + } while(REG_B != 0); + T_COUNT(-5); + + SET_ZERO(); + SET_SUBTRACT(); +} + +static void do_ini() +{ + mem_write(REG_HL, z80_in(REG_C)); + REG_HL++; + REG_B--; + + if(REG_B == 0) + SET_ZERO(); + else + CLEAR_ZERO(); + + SET_SUBTRACT(); + T_COUNT(15); +} + +static void do_inir() +{ + do + { + mem_write(REG_HL, z80_in(REG_C)); + REG_HL++; + REG_B--; + T_COUNT(20); + } while(REG_B != 0); + T_COUNT(-5); + + SET_ZERO(); + SET_SUBTRACT(); +} + +static int in_with_flags(int port) +{ + /* + * Do the appropriate flag calculations for the in instructions + * which compute the flags. Return the input value. + */ + + int value; + Uchar clear, set; + + clear = (Uchar) ~(SIGN_MASK | ZERO_MASK | HALF_CARRY_MASK | + PARITY_MASK | SUBTRACT_MASK); + set = 0; + + value = z80_in(port); + + if(value & 0x80) + set |= SIGN_MASK; + if(value == 0) + set |= ZERO_MASK; + if(parity(value)) + set |= PARITY_MASK; + + /* What should the half-carry do? Is this a mistake? */ + + REG_F = (REG_F & clear) | set; + + return value; +} + +static void do_outd() +{ + z80_out(REG_C, mem_read(REG_HL)); + REG_HL--; + REG_B--; + + if(REG_B == 0) + SET_ZERO(); + else + CLEAR_ZERO(); + + SET_SUBTRACT(); + T_COUNT(15); +} + +static void do_outdr() +{ + do + { + z80_out(REG_C, mem_read(REG_HL)); + REG_HL--; + REG_B--; + T_COUNT(20); + } while(REG_B != 0); + T_COUNT(-5); + + SET_ZERO(); + SET_SUBTRACT(); +} + +static void do_outi() +{ + z80_out(REG_C, mem_read(REG_HL)); + REG_HL++; + REG_B--; + + if(REG_B == 0) + SET_ZERO(); + else + CLEAR_ZERO(); + + SET_SUBTRACT(); + T_COUNT(15); +} + +static void do_outir() +{ + do + { + z80_out(REG_C, mem_read(REG_HL)); + REG_HL++; + REG_B--; + T_COUNT(20); + } while(REG_B != 0); + T_COUNT(-5); + + SET_ZERO(); + SET_SUBTRACT(); +} + + +/* + * Interrupt handling routines: + */ + +static void do_di() +{ + z80_state.iff1 = z80_state.iff2 = 0; +} + +static void do_ei() +{ + z80_state.iff1 = z80_state.iff2 = 1; +} + +static void do_im0() +{ + z80_state.interrupt_mode = 0; +} + +static void do_im1() +{ + z80_state.interrupt_mode = 1; +} + +static void do_im2() +{ + z80_state.interrupt_mode = 2; +} + +static void do_int() +{ + /* handle a maskable interrupt */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + z80_state.iff1 = 0; + switch (z80_state.interrupt_mode) { + case 0: + /* REG_PC = get_irq_vector() & 0x38; */ + error("interrupt in im0 not supported"); + break; + case 1: + REG_PC = 0x38; + break; + case 2: + /* REG_PC = REG_I << 8 + get_irq_vector(); */ + error("interrupt in im2 not supported"); + break; + } +} + +static void do_nmi() +{ + /* handle a non-maskable interrupt */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + z80_state.iff1 = 0; + REG_PC = 0x66; +} + +/* + * Extended instructions which have 0xCB as the first byte: + */ +static void do_CB_instruction() +{ + Uchar instruction; + + instruction = mem_read(REG_PC++); + + switch(instruction) + { + case 0x47: /* bit 0, a */ + do_test_bit(instruction, REG_A, 0); T_COUNT(8); + break; + case 0x40: /* bit 0, b */ + do_test_bit(instruction, REG_B, 0); T_COUNT(8); + break; + case 0x41: /* bit 0, c */ + do_test_bit(instruction, REG_C, 0); T_COUNT(8); + break; + case 0x42: /* bit 0, d */ + do_test_bit(instruction, REG_D, 0); T_COUNT(8); + break; + case 0x43: /* bit 0, e */ + do_test_bit(instruction, REG_E, 0); T_COUNT(8); + break; + case 0x44: /* bit 0, h */ + do_test_bit(instruction, REG_H, 0); T_COUNT(8); + break; + case 0x45: /* bit 0, l */ + do_test_bit(instruction, REG_L, 0); T_COUNT(8); + break; + case 0x4F: /* bit 1, a */ + do_test_bit(instruction, REG_A, 1); T_COUNT(8); + break; + case 0x48: /* bit 1, b */ + do_test_bit(instruction, REG_B, 1); T_COUNT(8); + break; + case 0x49: /* bit 1, c */ + do_test_bit(instruction, REG_C, 1); T_COUNT(8); + break; + case 0x4A: /* bit 1, d */ + do_test_bit(instruction, REG_D, 1); T_COUNT(8); + break; + case 0x4B: /* bit 1, e */ + do_test_bit(instruction, REG_E, 1); T_COUNT(8); + break; + case 0x4C: /* bit 1, h */ + do_test_bit(instruction, REG_H, 1); T_COUNT(8); + break; + case 0x4D: /* bit 1, l */ + do_test_bit(instruction, REG_L, 1); T_COUNT(8); + break; + case 0x57: /* bit 2, a */ + do_test_bit(instruction, REG_A, 2); T_COUNT(8); + break; + case 0x50: /* bit 2, b */ + do_test_bit(instruction, REG_B, 2); T_COUNT(8); + break; + case 0x51: /* bit 2, c */ + do_test_bit(instruction, REG_C, 2); T_COUNT(8); + break; + case 0x52: /* bit 2, d */ + do_test_bit(instruction, REG_D, 2); T_COUNT(8); + break; + case 0x53: /* bit 2, e */ + do_test_bit(instruction, REG_E, 2); T_COUNT(8); + break; + case 0x54: /* bit 2, h */ + do_test_bit(instruction, REG_H, 2); T_COUNT(8); + break; + case 0x55: /* bit 2, l */ + do_test_bit(instruction, REG_L, 2); T_COUNT(8); + break; + case 0x5F: /* bit 3, a */ + do_test_bit(instruction, REG_A, 3); T_COUNT(8); + break; + case 0x58: /* bit 3, b */ + do_test_bit(instruction, REG_B, 3); T_COUNT(8); + break; + case 0x59: /* bit 3, c */ + do_test_bit(instruction, REG_C, 3); T_COUNT(8); + break; + case 0x5A: /* bit 3, d */ + do_test_bit(instruction, REG_D, 3); T_COUNT(8); + break; + case 0x5B: /* bit 3, e */ + do_test_bit(instruction, REG_E, 3); T_COUNT(8); + break; + case 0x5C: /* bit 3, h */ + do_test_bit(instruction, REG_H, 3); T_COUNT(8); + break; + case 0x5D: /* bit 3, l */ + do_test_bit(instruction, REG_L, 3); T_COUNT(8); + break; + case 0x67: /* bit 4, a */ + do_test_bit(instruction, REG_A, 4); T_COUNT(8); + break; + case 0x60: /* bit 4, b */ + do_test_bit(instruction, REG_B, 4); T_COUNT(8); + break; + case 0x61: /* bit 4, c */ + do_test_bit(instruction, REG_C, 4); T_COUNT(8); + break; + case 0x62: /* bit 4, d */ + do_test_bit(instruction, REG_D, 4); T_COUNT(8); + break; + case 0x63: /* bit 4, e */ + do_test_bit(instruction, REG_E, 4); T_COUNT(8); + break; + case 0x64: /* bit 4, h */ + do_test_bit(instruction, REG_H, 4); T_COUNT(8); + break; + case 0x65: /* bit 4, l */ + do_test_bit(instruction, REG_L, 4); T_COUNT(8); + break; + case 0x6F: /* bit 5, a */ + do_test_bit(instruction, REG_A, 5); T_COUNT(8); + break; + case 0x68: /* bit 5, b */ + do_test_bit(instruction, REG_B, 5); T_COUNT(8); + break; + case 0x69: /* bit 5, c */ + do_test_bit(instruction, REG_C, 5); T_COUNT(8); + break; + case 0x6A: /* bit 5, d */ + do_test_bit(instruction, REG_D, 5); T_COUNT(8); + break; + case 0x6B: /* bit 5, e */ + do_test_bit(instruction, REG_E, 5); T_COUNT(8); + break; + case 0x6C: /* bit 5, h */ + do_test_bit(instruction, REG_H, 5); T_COUNT(8); + break; + case 0x6D: /* bit 5, l */ + do_test_bit(instruction, REG_L, 5); T_COUNT(8); + break; + case 0x77: /* bit 6, a */ + do_test_bit(instruction, REG_A, 6); T_COUNT(8); + break; + case 0x70: /* bit 6, b */ + do_test_bit(instruction, REG_B, 6); T_COUNT(8); + break; + case 0x71: /* bit 6, c */ + do_test_bit(instruction, REG_C, 6); T_COUNT(8); + break; + case 0x72: /* bit 6, d */ + do_test_bit(instruction, REG_D, 6); T_COUNT(8); + break; + case 0x73: /* bit 6, e */ + do_test_bit(instruction, REG_E, 6); T_COUNT(8); + break; + case 0x74: /* bit 6, h */ + do_test_bit(instruction, REG_H, 6); T_COUNT(8); + break; + case 0x75: /* bit 6, l */ + do_test_bit(instruction, REG_L, 6); T_COUNT(8); + break; + case 0x7F: /* bit 7, a */ + do_test_bit(instruction, REG_A, 7); T_COUNT(8); + break; + case 0x78: /* bit 7, b */ + do_test_bit(instruction, REG_B, 7); T_COUNT(8); + break; + case 0x79: /* bit 7, c */ + do_test_bit(instruction, REG_C, 7); T_COUNT(8); + break; + case 0x7A: /* bit 7, d */ + do_test_bit(instruction, REG_D, 7); T_COUNT(8); + break; + case 0x7B: /* bit 7, e */ + do_test_bit(instruction, REG_E, 7); T_COUNT(8); + break; + case 0x7C: /* bit 7, h */ + do_test_bit(instruction, REG_H, 7); T_COUNT(8); + break; + case 0x7D: /* bit 7, l */ + do_test_bit(instruction, REG_L, 7); T_COUNT(8); + break; + + case 0x46: /* bit 0, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 0); T_COUNT(12); + break; + case 0x4E: /* bit 1, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 1); T_COUNT(12); + break; + case 0x56: /* bit 2, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 2); T_COUNT(12); + break; + case 0x5E: /* bit 3, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 3); T_COUNT(12); + break; + case 0x66: /* bit 4, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 4); T_COUNT(12); + break; + case 0x6E: /* bit 5, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 5); T_COUNT(12); + break; + case 0x76: /* bit 6, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 6); T_COUNT(12); + break; + case 0x7E: /* bit 7, (hl) */ + do_test_bit(instruction, mem_read(REG_HL), 7); T_COUNT(12); + break; + + case 0x87: /* res 0, a */ + REG_A &= ~(1 << 0); T_COUNT(8); + break; + case 0x80: /* res 0, b */ + REG_B &= ~(1 << 0); T_COUNT(8); + break; + case 0x81: /* res 0, c */ + REG_C &= ~(1 << 0); T_COUNT(8); + break; + case 0x82: /* res 0, d */ + REG_D &= ~(1 << 0); T_COUNT(8); + break; + case 0x83: /* res 0, e */ + REG_E &= ~(1 << 0); T_COUNT(8); + break; + case 0x84: /* res 0, h */ + REG_H &= ~(1 << 0); T_COUNT(8); + break; + case 0x85: /* res 0, l */ + REG_L &= ~(1 << 0); T_COUNT(8); + break; + case 0x8F: /* res 1, a */ + REG_A &= ~(1 << 1); T_COUNT(8); + break; + case 0x88: /* res 1, b */ + REG_B &= ~(1 << 1); T_COUNT(8); + break; + case 0x89: /* res 1, c */ + REG_C &= ~(1 << 1); T_COUNT(8); + break; + case 0x8A: /* res 1, d */ + REG_D &= ~(1 << 1); T_COUNT(8); + break; + case 0x8B: /* res 1, e */ + REG_E &= ~(1 << 1); T_COUNT(8); + break; + case 0x8C: /* res 1, h */ + REG_H &= ~(1 << 1); T_COUNT(8); + break; + case 0x8D: /* res 1, l */ + REG_L &= ~(1 << 1); T_COUNT(8); + break; + case 0x97: /* res 2, a */ + REG_A &= ~(1 << 2); T_COUNT(8); + break; + case 0x90: /* res 2, b */ + REG_B &= ~(1 << 2); T_COUNT(8); + break; + case 0x91: /* res 2, c */ + REG_C &= ~(1 << 2); T_COUNT(8); + break; + case 0x92: /* res 2, d */ + REG_D &= ~(1 << 2); T_COUNT(8); + break; + case 0x93: /* res 2, e */ + REG_E &= ~(1 << 2); T_COUNT(8); + break; + case 0x94: /* res 2, h */ + REG_H &= ~(1 << 2); T_COUNT(8); + break; + case 0x95: /* res 2, l */ + REG_L &= ~(1 << 2); T_COUNT(8); + break; + case 0x9F: /* res 3, a */ + REG_A &= ~(1 << 3); T_COUNT(8); + break; + case 0x98: /* res 3, b */ + REG_B &= ~(1 << 3); T_COUNT(8); + break; + case 0x99: /* res 3, c */ + REG_C &= ~(1 << 3); T_COUNT(8); + break; + case 0x9A: /* res 3, d */ + REG_D &= ~(1 << 3); T_COUNT(8); + break; + case 0x9B: /* res 3, e */ + REG_E &= ~(1 << 3); T_COUNT(8); + break; + case 0x9C: /* res 3, h */ + REG_H &= ~(1 << 3); T_COUNT(8); + break; + case 0x9D: /* res 3, l */ + REG_L &= ~(1 << 3); T_COUNT(8); + break; + case 0xA7: /* res 4, a */ + REG_A &= ~(1 << 4); T_COUNT(8); + break; + case 0xA0: /* res 4, b */ + REG_B &= ~(1 << 4); T_COUNT(8); + break; + case 0xA1: /* res 4, c */ + REG_C &= ~(1 << 4); T_COUNT(8); + break; + case 0xA2: /* res 4, d */ + REG_D &= ~(1 << 4); T_COUNT(8); + break; + case 0xA3: /* res 4, e */ + REG_E &= ~(1 << 4); T_COUNT(8); + break; + case 0xA4: /* res 4, h */ + REG_H &= ~(1 << 4); T_COUNT(8); + break; + case 0xA5: /* res 4, l */ + REG_L &= ~(1 << 4); T_COUNT(8); + break; + case 0xAF: /* res 5, a */ + REG_A &= ~(1 << 5); T_COUNT(8); + break; + case 0xA8: /* res 5, b */ + REG_B &= ~(1 << 5); T_COUNT(8); + break; + case 0xA9: /* res 5, c */ + REG_C &= ~(1 << 5); T_COUNT(8); + break; + case 0xAA: /* res 5, d */ + REG_D &= ~(1 << 5); T_COUNT(8); + break; + case 0xAB: /* res 5, e */ + REG_E &= ~(1 << 5); T_COUNT(8); + break; + case 0xAC: /* res 5, h */ + REG_H &= ~(1 << 5); T_COUNT(8); + break; + case 0xAD: /* res 5, l */ + REG_L &= ~(1 << 5); T_COUNT(8); + break; + case 0xB7: /* res 6, a */ + REG_A &= ~(1 << 6); T_COUNT(8); + break; + case 0xB0: /* res 6, b */ + REG_B &= ~(1 << 6); T_COUNT(8); + break; + case 0xB1: /* res 6, c */ + REG_C &= ~(1 << 6); T_COUNT(8); + break; + case 0xB2: /* res 6, d */ + REG_D &= ~(1 << 6); T_COUNT(8); + break; + case 0xB3: /* res 6, e */ + REG_E &= ~(1 << 6); T_COUNT(8); + break; + case 0xB4: /* res 6, h */ + REG_H &= ~(1 << 6); T_COUNT(8); + break; + case 0xB5: /* res 6, l */ + REG_L &= ~(1 << 6); T_COUNT(8); + break; + case 0xBF: /* res 7, a */ + REG_A &= ~(1 << 7); T_COUNT(8); + break; + case 0xB8: /* res 7, b */ + REG_B &= ~(1 << 7); T_COUNT(8); + break; + case 0xB9: /* res 7, c */ + REG_C &= ~(1 << 7); T_COUNT(8); + break; + case 0xBA: /* res 7, d */ + REG_D &= ~(1 << 7); T_COUNT(8); + break; + case 0xBB: /* res 7, e */ + REG_E &= ~(1 << 7); T_COUNT(8); + break; + case 0xBC: /* res 7, h */ + REG_H &= ~(1 << 7); T_COUNT(8); + break; + case 0xBD: /* res 7, l */ + REG_L &= ~(1 << 7); T_COUNT(8); + break; + + case 0x86: /* res 0, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 0)); T_COUNT(15); + break; + case 0x8E: /* res 1, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 1)); T_COUNT(15); + break; + case 0x96: /* res 2, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 2)); T_COUNT(15); + break; + case 0x9E: /* res 3, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 3)); T_COUNT(15); + break; + case 0xA6: /* res 4, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 4)); T_COUNT(15); + break; + case 0xAE: /* res 5, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 5)); T_COUNT(15); + break; + case 0xB6: /* res 6, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 6)); T_COUNT(15); + break; + case 0xBE: /* res 7, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) & ~(1 << 7)); T_COUNT(15); + break; + + case 0x17: /* rl a */ + REG_A = rl_byte(REG_A); T_COUNT(8); + break; + case 0x10: /* rl b */ + REG_B = rl_byte(REG_B); T_COUNT(8); + break; + case 0x11: /* rl c */ + REG_C = rl_byte(REG_C); T_COUNT(8); + break; + case 0x12: /* rl d */ + REG_D = rl_byte(REG_D); T_COUNT(8); + break; + case 0x13: /* rl e */ + REG_E = rl_byte(REG_E); T_COUNT(8); + break; + case 0x14: /* rl h */ + REG_H = rl_byte(REG_H); T_COUNT(8); + break; + case 0x15: /* rl l */ + REG_L = rl_byte(REG_L); T_COUNT(8); + break; + case 0x16: /* rl (hl) */ + mem_write(REG_HL, rl_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0x07: /* rlc a */ + REG_A = rlc_byte(REG_A); T_COUNT(8); + break; + case 0x00: /* rlc b */ + REG_B = rlc_byte(REG_B); T_COUNT(8); + break; + case 0x01: /* rlc c */ + REG_C = rlc_byte(REG_C); T_COUNT(8); + break; + case 0x02: /* rlc d */ + REG_D = rlc_byte(REG_D); T_COUNT(8); + break; + case 0x03: /* rlc e */ + REG_E = rlc_byte(REG_E); T_COUNT(8); + break; + case 0x04: /* rlc h */ + REG_H = rlc_byte(REG_H); T_COUNT(8); + break; + case 0x05: /* rlc l */ + REG_L = rlc_byte(REG_L); T_COUNT(8); + break; + case 0x06: /* rlc (hl) */ + mem_write(REG_HL, rlc_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0x1F: /* rr a */ + REG_A = rr_byte(REG_A); T_COUNT(8); + break; + case 0x18: /* rr b */ + REG_B = rr_byte(REG_B); T_COUNT(8); + break; + case 0x19: /* rr c */ + REG_C = rr_byte(REG_C); T_COUNT(8); + break; + case 0x1A: /* rr d */ + REG_D = rr_byte(REG_D); T_COUNT(8); + break; + case 0x1B: /* rr e */ + REG_E = rr_byte(REG_E); T_COUNT(8); + break; + case 0x1C: /* rr h */ + REG_H = rr_byte(REG_H); T_COUNT(8); + break; + case 0x1D: /* rr l */ + REG_L = rr_byte(REG_L); T_COUNT(8); + break; + case 0x1E: /* rr (hl) */ + mem_write(REG_HL, rr_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0x0F: /* rrc a */ + REG_A = rrc_byte(REG_A); T_COUNT(8); + break; + case 0x08: /* rrc b */ + REG_B = rrc_byte(REG_B); T_COUNT(8); + break; + case 0x09: /* rrc c */ + REG_C = rrc_byte(REG_C); T_COUNT(8); + break; + case 0x0A: /* rrc d */ + REG_D = rrc_byte(REG_D); T_COUNT(8); + break; + case 0x0B: /* rrc e */ + REG_E = rrc_byte(REG_E); T_COUNT(8); + break; + case 0x0C: /* rrc h */ + REG_H = rrc_byte(REG_H); T_COUNT(8); + break; + case 0x0D: /* rrc l */ + REG_L = rrc_byte(REG_L); T_COUNT(8); + break; + case 0x0E: /* rrc (hl) */ + mem_write(REG_HL, rrc_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0xC7: /* set 0, a */ + REG_A |= (1 << 0); T_COUNT(8); + break; + case 0xC0: /* set 0, b */ + REG_B |= (1 << 0); T_COUNT(8); + break; + case 0xC1: /* set 0, c */ + REG_C |= (1 << 0); T_COUNT(8); + break; + case 0xC2: /* set 0, d */ + REG_D |= (1 << 0); T_COUNT(8); + break; + case 0xC3: /* set 0, e */ + REG_E |= (1 << 0); T_COUNT(8); + break; + case 0xC4: /* set 0, h */ + REG_H |= (1 << 0); T_COUNT(8); + break; + case 0xC5: /* set 0, l */ + REG_L |= (1 << 0); T_COUNT(8); + break; + case 0xCF: /* set 1, a */ + REG_A |= (1 << 1); T_COUNT(8); + break; + case 0xC8: /* set 1, b */ + REG_B |= (1 << 1); T_COUNT(8); + break; + case 0xC9: /* set 1, c */ + REG_C |= (1 << 1); T_COUNT(8); + break; + case 0xCA: /* set 1, d */ + REG_D |= (1 << 1); T_COUNT(8); + break; + case 0xCB: /* set 1, e */ + REG_E |= (1 << 1); T_COUNT(8); + break; + case 0xCC: /* set 1, h */ + REG_H |= (1 << 1); T_COUNT(8); + break; + case 0xCD: /* set 1, l */ + REG_L |= (1 << 1); T_COUNT(8); + break; + case 0xD7: /* set 2, a */ + REG_A |= (1 << 2); T_COUNT(8); + break; + case 0xD0: /* set 2, b */ + REG_B |= (1 << 2); T_COUNT(8); + break; + case 0xD1: /* set 2, c */ + REG_C |= (1 << 2); T_COUNT(8); + break; + case 0xD2: /* set 2, d */ + REG_D |= (1 << 2); T_COUNT(8); + break; + case 0xD3: /* set 2, e */ + REG_E |= (1 << 2); T_COUNT(8); + break; + case 0xD4: /* set 2, h */ + REG_H |= (1 << 2); T_COUNT(8); + break; + case 0xD5: /* set 2, l */ + REG_L |= (1 << 2); T_COUNT(8); + break; + case 0xDF: /* set 3, a */ + REG_A |= (1 << 3); T_COUNT(8); + break; + case 0xD8: /* set 3, b */ + REG_B |= (1 << 3); T_COUNT(8); + break; + case 0xD9: /* set 3, c */ + REG_C |= (1 << 3); T_COUNT(8); + break; + case 0xDA: /* set 3, d */ + REG_D |= (1 << 3); T_COUNT(8); + break; + case 0xDB: /* set 3, e */ + REG_E |= (1 << 3); T_COUNT(8); + break; + case 0xDC: /* set 3, h */ + REG_H |= (1 << 3); T_COUNT(8); + break; + case 0xDD: /* set 3, l */ + REG_L |= (1 << 3); T_COUNT(8); + break; + case 0xE7: /* set 4, a */ + REG_A |= (1 << 4); T_COUNT(8); + break; + case 0xE0: /* set 4, b */ + REG_B |= (1 << 4); T_COUNT(8); + break; + case 0xE1: /* set 4, c */ + REG_C |= (1 << 4); T_COUNT(8); + break; + case 0xE2: /* set 4, d */ + REG_D |= (1 << 4); T_COUNT(8); + break; + case 0xE3: /* set 4, e */ + REG_E |= (1 << 4); T_COUNT(8); + break; + case 0xE4: /* set 4, h */ + REG_H |= (1 << 4); T_COUNT(8); + break; + case 0xE5: /* set 4, l */ + REG_L |= (1 << 4); T_COUNT(8); + break; + case 0xEF: /* set 5, a */ + REG_A |= (1 << 5); T_COUNT(8); + break; + case 0xE8: /* set 5, b */ + REG_B |= (1 << 5); T_COUNT(8); + break; + case 0xE9: /* set 5, c */ + REG_C |= (1 << 5); T_COUNT(8); + break; + case 0xEA: /* set 5, d */ + REG_D |= (1 << 5); T_COUNT(8); + break; + case 0xEB: /* set 5, e */ + REG_E |= (1 << 5); T_COUNT(8); + break; + case 0xEC: /* set 5, h */ + REG_H |= (1 << 5); T_COUNT(8); + break; + case 0xED: /* set 5, l */ + REG_L |= (1 << 5); T_COUNT(8); + break; + case 0xF7: /* set 6, a */ + REG_A |= (1 << 6); T_COUNT(8); + break; + case 0xF0: /* set 6, b */ + REG_B |= (1 << 6); T_COUNT(8); + break; + case 0xF1: /* set 6, c */ + REG_C |= (1 << 6); T_COUNT(8); + break; + case 0xF2: /* set 6, d */ + REG_D |= (1 << 6); T_COUNT(8); + break; + case 0xF3: /* set 6, e */ + REG_E |= (1 << 6); T_COUNT(8); + break; + case 0xF4: /* set 6, h */ + REG_H |= (1 << 6); T_COUNT(8); + break; + case 0xF5: /* set 6, l */ + REG_L |= (1 << 6); T_COUNT(8); + break; + case 0xFF: /* set 7, a */ + REG_A |= (1 << 7); T_COUNT(8); + break; + case 0xF8: /* set 7, b */ + REG_B |= (1 << 7); T_COUNT(8); + break; + case 0xF9: /* set 7, c */ + REG_C |= (1 << 7); T_COUNT(8); + break; + case 0xFA: /* set 7, d */ + REG_D |= (1 << 7); T_COUNT(8); + break; + case 0xFB: /* set 7, e */ + REG_E |= (1 << 7); T_COUNT(8); + break; + case 0xFC: /* set 7, h */ + REG_H |= (1 << 7); T_COUNT(8); + break; + case 0xFD: /* set 7, l */ + REG_L |= (1 << 7); T_COUNT(8); + break; + + case 0xC6: /* set 0, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 0)); T_COUNT(15); + break; + case 0xCE: /* set 1, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 1)); T_COUNT(15); + break; + case 0xD6: /* set 2, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 2)); T_COUNT(15); + break; + case 0xDE: /* set 3, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 3)); T_COUNT(15); + break; + case 0xE6: /* set 4, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 4)); T_COUNT(15); + break; + case 0xEE: /* set 5, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 5)); T_COUNT(15); + break; + case 0xF6: /* set 6, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 6)); T_COUNT(15); + break; + case 0xFE: /* set 7, (hl) */ + mem_write(REG_HL, mem_read(REG_HL) | (1 << 7)); T_COUNT(15); + break; + + case 0x27: /* sla a */ + REG_A = sla_byte(REG_A); T_COUNT(8); + break; + case 0x20: /* sla b */ + REG_B = sla_byte(REG_B); T_COUNT(8); + break; + case 0x21: /* sla c */ + REG_C = sla_byte(REG_C); T_COUNT(8); + break; + case 0x22: /* sla d */ + REG_D = sla_byte(REG_D); T_COUNT(8); + break; + case 0x23: /* sla e */ + REG_E = sla_byte(REG_E); T_COUNT(8); + break; + case 0x24: /* sla h */ + REG_H = sla_byte(REG_H); T_COUNT(8); + break; + case 0x25: /* sla l */ + REG_L = sla_byte(REG_L); T_COUNT(8); + break; + case 0x26: /* sla (hl) */ + mem_write(REG_HL, sla_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0x2F: /* sra a */ + REG_A = sra_byte(REG_A); T_COUNT(8); + break; + case 0x28: /* sra b */ + REG_B = sra_byte(REG_B); T_COUNT(8); + break; + case 0x29: /* sra c */ + REG_C = sra_byte(REG_C); T_COUNT(8); + break; + case 0x2A: /* sra d */ + REG_D = sra_byte(REG_D); T_COUNT(8); + break; + case 0x2B: /* sra e */ + REG_E = sra_byte(REG_E); T_COUNT(8); + break; + case 0x2C: /* sra h */ + REG_H = sra_byte(REG_H); T_COUNT(8); + break; + case 0x2D: /* sra l */ + REG_L = sra_byte(REG_L); T_COUNT(8); + break; + case 0x2E: /* sra (hl) */ + mem_write(REG_HL, sra_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0x37: /* slia a [undocumented] */ + REG_A = slia_byte(REG_A); T_COUNT(8); + break; + case 0x30: /* slia b [undocumented] */ + REG_B = slia_byte(REG_B); T_COUNT(8); + break; + case 0x31: /* slia c [undocumented] */ + REG_C = slia_byte(REG_C); T_COUNT(8); + break; + case 0x32: /* slia d [undocumented] */ + REG_D = slia_byte(REG_D); T_COUNT(8); + break; + case 0x33: /* slia e [undocumented] */ + REG_E = slia_byte(REG_E); T_COUNT(8); + break; + case 0x34: /* slia h [undocumented] */ + REG_H = slia_byte(REG_H); T_COUNT(8); + break; + case 0x35: /* slia l [undocumented] */ + REG_L = slia_byte(REG_L); T_COUNT(8); + break; + case 0x36: /* slia (hl) [undocumented] */ + mem_write(REG_HL, slia_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + case 0x3F: /* srl a */ + REG_A = srl_byte(REG_A); T_COUNT(8); + break; + case 0x38: /* srl b */ + REG_B = srl_byte(REG_B); T_COUNT(8); + break; + case 0x39: /* srl c */ + REG_C = srl_byte(REG_C); T_COUNT(8); + break; + case 0x3A: /* srl d */ + REG_D = srl_byte(REG_D); T_COUNT(8); + break; + case 0x3B: /* srl e */ + REG_E = srl_byte(REG_E); T_COUNT(8); + break; + case 0x3C: /* srl h */ + REG_H = srl_byte(REG_H); T_COUNT(8); + break; + case 0x3D: /* srl l */ + REG_L = srl_byte(REG_L); T_COUNT(8); + break; + case 0x3E: /* srl (hl) */ + mem_write(REG_HL, srl_byte(mem_read(REG_HL))); T_COUNT(15); + break; + + default: + disassemble(REG_PC - 2); + error("unsupported instruction"); + } +} + + +/* + * Extended instructions which have 0xDD or 0xFD as the first byte: + */ +static void do_indexed_instruction(Ushort *ixp) +{ + Uchar instruction; + + instruction = mem_read(REG_PC++); + + switch(instruction) + { + /* same for FD, except uses IY */ + + case 0x8E: /* adc a, (ix + offset) */ + do_adc_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) + & 0xffff)); + T_COUNT(19); + break; + + case 0x86: /* add a, (ix + offset) */ + do_add_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) + & 0xffff)); + T_COUNT(19); + break; + + case 0x09: /* add ix, bc */ + do_add_word_index(ixp, REG_BC); T_COUNT(15); + break; + case 0x19: /* add ix, de */ + do_add_word_index(ixp, REG_DE); T_COUNT(15); + break; + case 0x29: /* add ix, ix */ + do_add_word_index(ixp, *ixp); T_COUNT(15); + break; + case 0x39: /* add ix, sp */ + do_add_word_index(ixp, REG_SP); T_COUNT(15); + break; + + case 0xA6: /* and (ix + offset) */ + do_and_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) + & 0xffff)); + T_COUNT(19); + break; + + case 0xBE: /* cp (ix + offset) */ + do_cp(mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff)); + T_COUNT(19); + break; + + case 0x35: /* dec (ix + offset) */ + { + Ushort address; + Uchar value; + address = *ixp + (signed char) mem_read(REG_PC++); + value = mem_read(address) - 1; + mem_write(address, value); + do_flags_dec_byte(value); + } + T_COUNT(23); + break; + + case 0x2B: /* dec ix */ + (*ixp)--; + T_COUNT(10); + break; + + case 0xE3: /* ex (sp), ix */ + { + Ushort temp; + temp = mem_read_word(REG_SP); + mem_write_word(REG_SP, *ixp); + *ixp = temp; + } + T_COUNT(23); + break; + + case 0x34: /* inc (ix + offset) */ + { + Ushort address; + Uchar value; + address = *ixp + (signed char) mem_read(REG_PC++); + value = mem_read(address) + 1; + mem_write(address, value); + do_flags_inc_byte(value); + } + T_COUNT(23); + break; + + case 0x23: /* inc ix */ + (*ixp)++; + T_COUNT(10); + break; + + case 0xE9: /* jp (ix) */ + REG_PC = *ixp; + T_COUNT(8); + break; + + case 0x7E: /* ld a, (ix + offset) */ + REG_A = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + case 0x46: /* ld b, (ix + offset) */ + REG_B = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + case 0x4E: /* ld c, (ix + offset) */ + REG_C = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + case 0x56: /* ld d, (ix + offset) */ + REG_D = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + case 0x5E: /* ld e, (ix + offset) */ + REG_E = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + case 0x66: /* ld h, (ix + offset) */ + REG_H = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + case 0x6E: /* ld l, (ix + offset) */ + REG_L = mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff); + T_COUNT(19); + break; + + case 0x36: /* ld (ix + offset), value */ + mem_write(*ixp + (signed char) mem_read(REG_PC), mem_read((REG_PC+1)&0xffff)); + REG_PC += 2; + T_COUNT(19); + break; + + case 0x77: /* ld (ix + offset), a */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_A); + T_COUNT(19); + break; + case 0x70: /* ld (ix + offset), b */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_B); + T_COUNT(19); + break; + case 0x71: /* ld (ix + offset), c */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_C); + T_COUNT(19); + break; + case 0x72: /* ld (ix + offset), d */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_D); + T_COUNT(19); + break; + case 0x73: /* ld (ix + offset), e */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_E); + T_COUNT(19); + break; + case 0x74: /* ld (ix + offset), h */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_H); + T_COUNT(19); + break; + case 0x75: /* ld (ix + offset), l */ + mem_write(*ixp + (signed char) mem_read(REG_PC++), REG_L); + T_COUNT(19); + break; + + case 0x22: /* ld (address), ix */ + mem_write_word(mem_read_word(REG_PC), *ixp); + REG_PC += 2; + T_COUNT(20); + break; + + case 0xF9: /* ld sp, ix */ + REG_SP = *ixp; + T_COUNT(10); + break; + + case 0x21: /* ld ix, value */ + *ixp = mem_read_word(REG_PC); + REG_PC += 2; + T_COUNT(14); + break; + + case 0x2A: /* ld ix, (address) */ + *ixp = mem_read_word(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(20); + break; + + case 0xB6: /* or (ix + offset) */ + do_or_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff)); + T_COUNT(19); + break; + + case 0xE1: /* pop ix */ + *ixp = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(14); + break; + + case 0xE5: /* push ix */ + REG_SP -= 2; + mem_write_word(REG_SP, *ixp); + T_COUNT(15); + break; + + case 0x9E: /* sbc a, (ix + offset) */ + do_sbc_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff)); + T_COUNT(19); + break; + + case 0x96: /* sub a, (ix + offset) */ + do_sub_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff)); + T_COUNT(19); + break; + + case 0xAE: /* xor (ix + offset) */ + do_xor_byte(mem_read((*ixp + (signed char) mem_read(REG_PC++)) & 0xffff)); + T_COUNT(19); + break; + + case 0xCB: + { + signed char offset, result = 0; + Uchar sub_instruction; + + offset = (signed char) mem_read(REG_PC++); + sub_instruction = mem_read(REG_PC++); + + /* Instructions with (sub_instruction & 7) != 6 are undocumented; + their extra effect is handled after this switch */ + switch(sub_instruction&0xf8) + { + case 0x00: /* rlc (ix + offset) */ + result = rlc_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x08: /* rrc (ix + offset) */ + result = rrc_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x10: /* rl (ix + offset) */ + result = rl_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x18: /* rr (ix + offset) */ + result = rr_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x20: /* sla (ix + offset) */ + result = sla_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x28: /* sra (ix + offset) */ + result = sra_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x30: /* slia (ix + offset) [undocumented] */ + result = slia_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x38: /* srl (ix + offset) */ + result = srl_byte(mem_read((*ixp + offset) & 0xffff)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0x40: /* bit 0, (ix + offset) */ + case 0x48: /* bit 1, (ix + offset) */ + case 0x50: /* bit 2, (ix + offset) */ + case 0x58: /* bit 3, (ix + offset) */ + case 0x60: /* bit 4, (ix + offset) */ + case 0x68: /* bit 5, (ix + offset) */ + case 0x70: /* bit 6, (ix + offset) */ + case 0x78: /* bit 7, (ix + offset) */ + do_test_bit(sub_instruction, mem_read((*ixp + offset) & 0xffff), + (sub_instruction >> 3) & 7); + T_COUNT(20); + break; + + case 0x80: /* res 0, (ix + offset) */ + case 0x88: /* res 1, (ix + offset) */ + case 0x90: /* res 2, (ix + offset) */ + case 0x98: /* res 3, (ix + offset) */ + case 0xA0: /* res 4, (ix + offset) */ + case 0xA8: /* res 5, (ix + offset) */ + case 0xB0: /* res 6, (ix + offset) */ + case 0xB8: /* res 7, (ix + offset) */ + result = mem_read((*ixp + offset) & 0xffff) & + ~(1 << ((sub_instruction >> 3) & 7)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + + case 0xC0: /* set 0, (ix + offset) */ + case 0xC8: /* set 1, (ix + offset) */ + case 0xD0: /* set 2, (ix + offset) */ + case 0xD8: /* set 3, (ix + offset) */ + case 0xE0: /* set 4, (ix + offset) */ + case 0xE8: /* set 5, (ix + offset) */ + case 0xF0: /* set 6, (ix + offset) */ + case 0xF8: /* set 7, (ix + offset) */ + result = mem_read((*ixp + offset) & 0xffff) | + (1 << ((sub_instruction >> 3) & 7)); + mem_write(*ixp + offset, result); + T_COUNT(23); + break; + } + + if (sub_instruction < 0x40 || sub_instruction > 0x7f) + { + switch (sub_instruction & 7) + { + /* Undocumented cases */ + case 0: REG_B = result; break; + case 1: REG_C = result; break; + case 2: REG_D = result; break; + case 3: REG_E = result; break; + case 4: REG_H = result; break; + case 5: REG_L = result; break; + case 7: REG_A = result; break; + } + } + } + break; + + /* begin undocumented instructions -- timings are a (good) guess */ + case 0x8C: /* adc a, ixh */ + do_adc_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0x8D: /* adc a, ixl */ + do_adc_byte(LOW(ixp)); T_COUNT(8); + break; + case 0x84: /* add a, ixh */ + do_add_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0x85: /* add a, ixl */ + do_add_byte(LOW(ixp)); T_COUNT(8); + break; + case 0xA4: /* and ixh */ + do_and_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0xA5: /* and ixl */ + do_and_byte(LOW(ixp)); T_COUNT(8); + break; + case 0xBC: /* cp ixh */ + do_cp(HIGH(ixp)); T_COUNT(8); + break; + case 0xBD: /* cp ixl */ + do_cp(LOW(ixp)); T_COUNT(8); + break; + case 0x25: /* dec ixh */ + do_flags_dec_byte(--HIGH(ixp)); T_COUNT(8); + break; + case 0x2D: /* dec ixl */ + do_flags_dec_byte(--LOW(ixp)); T_COUNT(8); + break; + case 0x24: /* inc ixh */ + HIGH(ixp)++; + do_flags_inc_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0x2C: /* inc ixl */ + LOW(ixp)++; + do_flags_inc_byte(LOW(ixp)); T_COUNT(8); + break; + case 0x7C: /* ld a, ixh */ + REG_A = HIGH(ixp); T_COUNT(8); + break; + case 0x7D: /* ld a, ixl */ + REG_A = LOW(ixp); T_COUNT(8); + break; + case 0x44: /* ld b, ixh */ + REG_B = HIGH(ixp); T_COUNT(8); + break; + case 0x45: /* ld b, ixl */ + REG_B = LOW(ixp); T_COUNT(8); + break; + case 0x4C: /* ld c, ixh */ + REG_C = HIGH(ixp); T_COUNT(8); + break; + case 0x4D: /* ld c, ixl */ + REG_C = LOW(ixp); T_COUNT(8); + break; + case 0x54: /* ld d, ixh */ + REG_D = HIGH(ixp); T_COUNT(8); + break; + case 0x55: /* ld d, ixl */ + REG_D = LOW(ixp); T_COUNT(8); + break; + case 0x5C: /* ld e, ixh */ + REG_E = HIGH(ixp); T_COUNT(8); + break; + case 0x5D: /* ld e, ixl */ + REG_E = LOW(ixp); T_COUNT(8); + break; + case 0x67: /* ld ixh, a */ + HIGH(ixp) = REG_A; T_COUNT(8); + break; + case 0x60: /* ld ixh, b */ + HIGH(ixp) = REG_B; T_COUNT(8); + break; + case 0x61: /* ld ixh, c */ + HIGH(ixp) = REG_C; T_COUNT(8); + break; + case 0x62: /* ld ixh, d */ + HIGH(ixp) = REG_D; T_COUNT(8); + break; + case 0x63: /* ld ixh, e */ + HIGH(ixp) = REG_E; T_COUNT(8); + break; + case 0x64: /* ld ixh, ixh */ + HIGH(ixp) = HIGH(ixp); T_COUNT(8); + break; + case 0x65: /* ld ixh, ixl */ + HIGH(ixp) = LOW(ixp); T_COUNT(8); + break; + case 0x6F: /* ld ixl, a */ + LOW(ixp) = REG_A; T_COUNT(8); + break; + case 0x68: /* ld ixl, b */ + LOW(ixp) = REG_B; T_COUNT(8); + break; + case 0x69: /* ld ixl, c */ + LOW(ixp) = REG_C; T_COUNT(8); + break; + case 0x6A: /* ld ixl, d */ + LOW(ixp) = REG_D; T_COUNT(8); + break; + case 0x6B: /* ld ixl, e */ + LOW(ixp) = REG_E; T_COUNT(8); + break; + case 0x6C: /* ld ixl, ixh */ + LOW(ixp) = HIGH(ixp); T_COUNT(8); + break; + case 0x6D: /* ld ixl, ixl */ + LOW(ixp) = LOW(ixp); T_COUNT(8); + break; + case 0x26: /* ld ixh, value */ + HIGH(ixp) = mem_read(REG_PC++); T_COUNT(11); + break; + case 0x2E: /* ld ixl, value */ + LOW(ixp) = mem_read(REG_PC++); T_COUNT(11); + break; + case 0xB4: /* or ixh */ + do_or_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0xB5: /* or ixl */ + do_or_byte(LOW(ixp)); T_COUNT(8); + break; + case 0x9C: /* sbc a, ixh */ + do_sbc_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0x9D: /* sbc a, ixl */ + do_sbc_byte(LOW(ixp)); T_COUNT(8); + break; + case 0x94: /* sub a, ixh */ + do_sub_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0x95: /* sub a, ixl */ + do_sub_byte(LOW(ixp)); T_COUNT(8); + break; + case 0xAC: /* xor ixh */ + do_xor_byte(HIGH(ixp)); T_COUNT(8); + break; + case 0xAD: /* xor ixl */ + do_xor_byte(LOW(ixp)); T_COUNT(8); + break; + /* end undocumented instructions */ + + default: + /* Ignore DD or FD prefix and retry as normal instruction; + this is a correct emulation. [undocumented, timing guessed] */ + REG_PC--; + T_COUNT(4); + break; + } +} + + +/* + * Extended instructions which have 0xED as the first byte: + */ +static int do_ED_instruction() +{ + Uchar instruction; + int debug = 0; + + instruction = mem_read(REG_PC++); + + switch(instruction) + { + case 0x4A: /* adc hl, bc */ + do_adc_word(REG_BC); T_COUNT(15); + break; + case 0x5A: /* adc hl, de */ + do_adc_word(REG_DE); T_COUNT(15); + break; + case 0x6A: /* adc hl, hl */ + do_adc_word(REG_HL); T_COUNT(15); + break; + case 0x7A: /* adc hl, sp */ + do_adc_word(REG_SP); T_COUNT(15); + break; + + case 0xA9: /* cpd */ + do_cpd(); + break; + case 0xB9: /* cpdr */ + do_cpdr(); + break; + + case 0xA1: /* cpi */ + do_cpi(); + break; + case 0xB1: /* cpir */ + do_cpir(); + break; + + case 0x46: /* im 0 */ + case 0x66: /* im 0 [undocumented]*/ + do_im0(); T_COUNT(8); + break; + case 0x56: /* im 1 */ + case 0x76: /* im 1 [undocumented] */ + do_im1(); T_COUNT(8); + break; + case 0x5E: /* im 2 */ + case 0x7E: /* im 2 [undocumented] */ + do_im2(); T_COUNT(8); + break; + + case 0x78: /* in a, (c) */ + REG_A = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x40: /* in b, (c) */ + REG_B = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x48: /* in c, (c) */ + REG_C = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x50: /* in d, (c) */ + REG_D = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x58: /* in e, (c) */ + REG_E = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x60: /* in h, (c) */ + REG_H = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x68: /* in l, (c) */ + REG_L = in_with_flags(REG_C); T_COUNT(11); + break; + case 0x70: /* in (c) [undocumented] */ + (void) in_with_flags(REG_C); T_COUNT(11); + break; + + case 0xAA: /* ind */ + do_ind(); + break; + case 0xBA: /* indr */ + do_indr(); + break; + case 0xA2: /* ini */ + do_ini(); + break; + case 0xB2: /* inir */ + do_inir(); + break; + + case 0x57: /* ld a, i */ + do_ld_a_i(); T_COUNT(9); + break; + case 0x47: /* ld i, a */ + REG_I = REG_A; T_COUNT(9); + break; + + case 0x5F: /* ld a, r */ + do_ld_a_r(); T_COUNT(9); + break; + case 0x4F: /* ld r, a */ + /* unimplemented; ignore */ + T_COUNT(9); + break; + + case 0x4B: /* ld bc, (address) */ + REG_BC = mem_read_word(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(20); + break; + case 0x5B: /* ld de, (address) */ + REG_DE = mem_read_word(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(20); + break; + case 0x6B: /* ld hl, (address) */ + /* this instruction is redundant with the 2A instruction */ + REG_HL = mem_read_word(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(20); + break; + case 0x7B: /* ld sp, (address) */ + REG_SP = mem_read_word(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(20); + break; + + case 0x43: /* ld (address), bc */ + mem_write_word(mem_read_word(REG_PC), REG_BC); + REG_PC += 2; + T_COUNT(20); + break; + case 0x53: /* ld (address), de */ + mem_write_word(mem_read_word(REG_PC), REG_DE); + REG_PC += 2; + T_COUNT(20); + break; + case 0x63: /* ld (address), hl */ + /* this instruction is redundant with the 22 instruction */ + mem_write_word(mem_read_word(REG_PC), REG_HL); + REG_PC += 2; + T_COUNT(20); + break; + case 0x73: /* ld (address), sp */ + mem_write_word(mem_read_word(REG_PC), REG_SP); + REG_PC += 2; + T_COUNT(20); + break; + + case 0xA8: /* ldd */ + do_ldd(); + break; + case 0xB8: /* lddr */ + do_lddr(); + break; + case 0xA0: /* ldi */ + do_ldi(); + break; + case 0xB0: /* ldir */ + do_ldir(); + break; + + case 0x44: /* neg */ + case 0x4C: /* neg [undocumented] */ + case 0x54: /* neg [undocumented] */ + case 0x5C: /* neg [undocumented] */ + case 0x64: /* neg [undocumented] */ + case 0x6C: /* neg [undocumented] */ + case 0x74: /* neg [undocumented] */ + case 0x7C: /* neg [undocumented] */ + do_negate(); + T_COUNT(8); + break; + + case 0x79: /* out (c), a */ + z80_out(REG_C, REG_A); + T_COUNT(12); + break; + case 0x41: /* out (c), b */ + z80_out(REG_C, REG_B); + T_COUNT(12); + break; + case 0x49: /* out (c), c */ + z80_out(REG_C, REG_C); + T_COUNT(12); + break; + case 0x51: /* out (c), d */ + z80_out(REG_C, REG_D); + T_COUNT(12); + break; + case 0x59: /* out (c), e */ + z80_out(REG_C, REG_E); + T_COUNT(12); + break; + case 0x61: /* out (c), h */ + z80_out(REG_C, REG_H); + T_COUNT(12); + break; + case 0x69: /* out (c), l */ + z80_out(REG_C, REG_L); + T_COUNT(12); + break; + case 0x71: /* out (c), 0 [undocumented] */ + z80_out(REG_C, 0); + T_COUNT(12); + break; + + case 0xAB: /* outd */ + do_outd(); + break; + case 0xBB: /* outdr */ + do_outdr(); + break; + case 0xA3: /* outi */ + do_outi(); + break; + case 0xB3: /* outir */ + do_outir(); + break; + + case 0x4D: /* reti */ + /* no support for alerting peripherals, just like ret */ + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(14); + break; + + case 0x45: /* retn */ + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + z80_state.iff1 = z80_state.iff2; /* restore the iff state */ + T_COUNT(14); + break; + + case 0x55: /* ret [undocumented] */ + case 0x5D: /* ret [undocumented] */ + case 0x65: /* ret [undocumented] */ + case 0x6D: /* ret [undocumented] */ + case 0x75: /* ret [undocumented] */ + case 0x7D: /* ret [undocumented] */ + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(14); + break; + + case 0x6F: /* rld */ + do_rld(); + T_COUNT(18); + break; + + case 0x67: /* rrd */ + do_rrd(); + T_COUNT(18); + break; + + case 0x42: /* sbc hl, bc */ + do_sbc_word(REG_BC); + T_COUNT(15); + break; + case 0x52: /* sbc hl, de */ + do_sbc_word(REG_DE); + T_COUNT(15); + break; + case 0x62: /* sbc hl, hl */ + do_sbc_word(REG_HL); + T_COUNT(15); + break; + case 0x72: /* sbc hl, sp */ + do_sbc_word(REG_SP); + T_COUNT(15); + break; + + /* Emulator traps -- not real Z-80 instructions */ + case 0x28: /* emt_system */ + do_emt_system(); + break; + case 0x29: /* emt_mouse */ + do_emt_mouse(); + break; + case 0x2a: /* emt_getddir */ + do_emt_getddir(); + break; + case 0x2b: /* emt_setddir */ + do_emt_setddir(); + break; + case 0x2f: /* emt_debug */ + if (trs_continuous > 0) trs_continuous = 0; + debug = 1; + break; + case 0x30: /* emt_open */ + do_emt_open(); + break; + case 0x31: /* emt_close */ + do_emt_close(); + break; + case 0x32: /* emt_read */ + do_emt_read(); + break; + case 0x33: /* emt_write */ + do_emt_write(); + break; + case 0x34: /* emt_lseek */ + do_emt_lseek(); + break; + case 0x35: /* emt_strerror */ + do_emt_strerror(); + break; + case 0x36: /* emt_time */ + do_emt_time(); + break; + case 0x37: /* emt_opendir */ + do_emt_opendir(); + break; + case 0x38: /* emt_closedir */ + do_emt_closedir(); + break; + case 0x39: /* emt_readdir */ + do_emt_readdir(); + break; + case 0x3a: /* emt_chdir */ + do_emt_chdir(); + break; + case 0x3b: /* emt_getcwd */ + do_emt_getcwd(); + break; + case 0x3c: /* emt_misc */ + do_emt_misc(); + break; + case 0x3d: /* emt_ftruncate */ + do_emt_ftruncate(); + break; + case 0x3e: /* emt_opendisk */ + do_emt_opendisk(); + break; + case 0x3f: /* emt_closedisk */ + do_emt_closedisk(); + break; + + default: + disassemble(REG_PC - 2); + error("unsupported instruction"); + } + + return debug; +} + +volatile int x_poll_count = 0; +volatile int x_flush_needed = 0; +#define X_POLL_INTERVAL 10000 + +/* Hack, hack, see if we can speed this up. */ +/*extern Uchar *memory;*/ +/*#define MEM_READ(a) ((a < 0x3000) ? memory[a] : mem_read(a));*/ +/* #define MEM_READ(a) (((((a) - 0x3000) & 0xffff) >= 0xc00) ? memory[a] : mem_read(a)) */ + +int trs_continuous; + +int z80_run(int continuous) + /* + * -1 = single-step and disallow interrupts + * 0 = single-step + * 1 = continuous + */ +{ + Uchar instruction; + Ushort address; /* generic temps */ + int ret = 0; + int i; + trs_continuous = continuous; + + /* loop to do a z80 instruction */ + do { +#if HAVE_SIGIO + /* If we are using SIGIO, the SIGIO handler tells us when to + poll for X events by setting x_poll_count to 0, and when to + flush output to the X server by setting x_flush_needed to 1. */ + if (x_poll_count <= 0) { + x_poll_count = X_POLL_INTERVAL; + trs_get_event(0); + } else if (x_flush_needed) { + x_flush_needed = 0; + trs_x_flush(); + } +#else + /* If we aren't using SIGIO, we need to poll for X events + periodically, and that also flushes output to the X server. */ + if (x_poll_count <= 0) { + x_poll_count = X_POLL_INTERVAL; + trs_get_event(0); + } else { + x_poll_count--; + } +#endif + /* Speed control */ + if ((i = z80_state.delay)) { + while (--i) /*nothing*/; + } + + instruction = mem_read(REG_PC++); + + switch(instruction) + { + case 0xCB: /* CB.. extended instruction */ + do_CB_instruction(); + break; + case 0xDD: /* DD.. extended instruction */ + do_indexed_instruction(®_IX); + break; + case 0xED: /* ED.. extended instruction */ + ret = do_ED_instruction(); + break; + case 0xFD: /* FD.. extended instruction */ + do_indexed_instruction(®_IY); + break; + + case 0x8F: /* adc a, a */ + do_adc_byte(REG_A); T_COUNT(4); + break; + case 0x88: /* adc a, b */ + do_adc_byte(REG_B); T_COUNT(4); + break; + case 0x89: /* adc a, c */ + do_adc_byte(REG_C); T_COUNT(4); + break; + case 0x8A: /* adc a, d */ + do_adc_byte(REG_D); T_COUNT(4); + break; + case 0x8B: /* adc a, e */ + do_adc_byte(REG_E); T_COUNT(4); + break; + case 0x8C: /* adc a, h */ + do_adc_byte(REG_H); T_COUNT(4); + break; + case 0x8D: /* adc a, l */ + do_adc_byte(REG_L); T_COUNT(4); + break; + case 0xCE: /* adc a, value */ + do_adc_byte(mem_read(REG_PC++)); T_COUNT(7); + break; + case 0x8E: /* adc a, (hl) */ + do_adc_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0x87: /* add a, a */ + do_add_byte(REG_A); T_COUNT(4); + break; + case 0x80: /* add a, b */ + do_add_byte(REG_B); T_COUNT(4); + break; + case 0x81: /* add a, c */ + do_add_byte(REG_C); T_COUNT(4); + break; + case 0x82: /* add a, d */ + do_add_byte(REG_D); T_COUNT(4); + break; + case 0x83: /* add a, e */ + do_add_byte(REG_E); T_COUNT(4); + break; + case 0x84: /* add a, h */ + do_add_byte(REG_H); T_COUNT(4); + break; + case 0x85: /* add a, l */ + do_add_byte(REG_L); T_COUNT(4); + break; + case 0xC6: /* add a, value */ + do_add_byte(mem_read(REG_PC++)); T_COUNT(7); + break; + case 0x86: /* add a, (hl) */ + do_add_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0x09: /* add hl, bc */ + do_add_word(REG_BC); T_COUNT(11); + break; + case 0x19: /* add hl, de */ + do_add_word(REG_DE); T_COUNT(11); + break; + case 0x29: /* add hl, hl */ + do_add_word(REG_HL); T_COUNT(11); + break; + case 0x39: /* add hl, sp */ + do_add_word(REG_SP); T_COUNT(11); + break; + + case 0xA7: /* and a */ + do_and_byte(REG_A); T_COUNT(4); + break; + case 0xA0: /* and b */ + do_and_byte(REG_B); T_COUNT(4); + break; + case 0xA1: /* and c */ + do_and_byte(REG_C); T_COUNT(4); + break; + case 0xA2: /* and d */ + do_and_byte(REG_D); T_COUNT(4); + break; + case 0xA3: /* and e */ + do_and_byte(REG_E); T_COUNT(4); + break; + case 0xA4: /* and h */ + do_and_byte(REG_H); T_COUNT(4); + break; + case 0xA5: /* and l */ + do_and_byte(REG_L); T_COUNT(4); + break; + case 0xE6: /* and value */ + do_and_byte(mem_read(REG_PC++)); T_COUNT(7); + break; + case 0xA6: /* and (hl) */ + do_and_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0xCD: /* call address */ + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + break; + + case 0xC4: /* call nz, address */ + if(!ZERO_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xCC: /* call z, address */ + if(ZERO_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xD4: /* call nc, address */ + if(!CARRY_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xDC: /* call c, address */ + if(CARRY_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xE4: /* call po, address */ + if(!PARITY_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xEC: /* call pe, address */ + if(PARITY_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xF4: /* call p, address */ + if(!SIGN_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + case 0xFC: /* call m, address */ + if(SIGN_FLAG) + { + address = mem_read_word(REG_PC); + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC + 2); + REG_PC = address; + T_COUNT(17); + } + else + { + REG_PC += 2; + T_COUNT(10); + } + break; + + + case 0x3F: /* ccf */ + REG_F = (REG_F & (ZERO_MASK|PARITY_MASK|SIGN_MASK)) + | (~REG_F & CARRY_MASK) + | ((REG_F & CARRY_MASK) ? HALF_CARRY_MASK : 0) + | (REG_A & (UNDOC3_MASK|UNDOC5_MASK)); + T_COUNT(4); + break; + + case 0xBF: /* cp a */ + do_cp(REG_A); T_COUNT(4); + break; + case 0xB8: /* cp b */ + do_cp(REG_B); T_COUNT(4); + break; + case 0xB9: /* cp c */ + do_cp(REG_C); T_COUNT(4); + break; + case 0xBA: /* cp d */ + do_cp(REG_D); T_COUNT(4); + break; + case 0xBB: /* cp e */ + do_cp(REG_E); T_COUNT(4); + break; + case 0xBC: /* cp h */ + do_cp(REG_H); T_COUNT(4); + break; + case 0xBD: /* cp l */ + do_cp(REG_L); T_COUNT(4); + break; + case 0xFE: /* cp value */ + do_cp(mem_read(REG_PC++)); T_COUNT(7); + break; + case 0xBE: /* cp (hl) */ + do_cp(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0x2F: /* cpl */ + REG_A = ~REG_A; + REG_F = (REG_F & (CARRY_MASK|PARITY_MASK|ZERO_MASK|SIGN_MASK)) + | (HALF_CARRY_MASK|SUBTRACT_MASK) + | (REG_A & (UNDOC3_MASK|UNDOC5_MASK)); + T_COUNT(4); + break; + + case 0x27: /* daa */ + do_daa(); + T_COUNT(4); + break; + + case 0x3D: /* dec a */ + do_flags_dec_byte(--REG_A); T_COUNT(4); + break; + case 0x05: /* dec b */ + do_flags_dec_byte(--REG_B); T_COUNT(4); + break; + case 0x0D: /* dec c */ + do_flags_dec_byte(--REG_C); T_COUNT(4); + break; + case 0x15: /* dec d */ + do_flags_dec_byte(--REG_D); T_COUNT(4); + break; + case 0x1D: /* dec e */ + do_flags_dec_byte(--REG_E); T_COUNT(4); + break; + case 0x25: /* dec h */ + do_flags_dec_byte(--REG_H); T_COUNT(4); + break; + case 0x2D: /* dec l */ + do_flags_dec_byte(--REG_L); T_COUNT(4); + break; + + case 0x35: /* dec (hl) */ + { + Uchar value = mem_read(REG_HL) - 1; + mem_write(REG_HL, value); + do_flags_dec_byte(value); + } + T_COUNT(11); + break; + + case 0x0B: /* dec bc */ + REG_BC--; + T_COUNT(6); + break; + case 0x1B: /* dec de */ + REG_DE--; + T_COUNT(6); + break; + case 0x2B: /* dec hl */ + REG_HL--; + T_COUNT(6); + break; + case 0x3B: /* dec sp */ + REG_SP--; + T_COUNT(6); + break; + + case 0xF3: /* di */ + do_di(); + T_COUNT(4); + break; + + case 0x10: /* djnz offset */ + /* Zaks says no flag changes. */ + if(--REG_B != 0) + { + signed char byte_value; + byte_value = (signed char) mem_read(REG_PC++); + REG_PC += byte_value; + T_COUNT(13); + } + else + { + REG_PC++; + T_COUNT(8); + } + break; + + case 0xFB: /* ei */ + do_ei(); + T_COUNT(4); + break; + + case 0x08: /* ex af, af' */ + { + Ushort temp; + temp = REG_AF; + REG_AF = REG_AF_PRIME; + REG_AF_PRIME = temp; + } + T_COUNT(4); + break; + + case 0xEB: /* ex de, hl */ + { + Ushort temp; + temp = REG_DE; + REG_DE = REG_HL; + REG_HL = temp; + } + T_COUNT(4); + break; + + case 0xE3: /* ex (sp), hl */ + { + Ushort temp; + temp = mem_read_word(REG_SP); + mem_write_word(REG_SP, REG_HL); + REG_HL = temp; + } + T_COUNT(19); + break; + + case 0xD9: /* exx */ + { + Ushort tmp; + tmp = REG_BC_PRIME; + REG_BC_PRIME = REG_BC; + REG_BC = tmp; + tmp = REG_DE_PRIME; + REG_DE_PRIME = REG_DE; + REG_DE = tmp; + tmp = REG_HL_PRIME; + REG_HL_PRIME = REG_HL; + REG_HL = tmp; + } + T_COUNT(4); + break; + + case 0x76: /* halt */ + if (trs_model == 1) { + /* Z-80 HALT output is tied to reset button circuit */ + trs_reset(0); + } else { + /* Really halt (i.e., wait for interrupt) */ + /* Slight kludge: we back up the PC and keep going + around the main loop reexecuting the halt. A real + Z-80 does not back up and re-fetch the halt + instruction repeatedly; it just executes NOPs + internally. When an interrupt or NMI is delivered, + (see below) we undo this decrement to get out of + the halt state. */ + REG_PC--; + if (continuous > 0 && + !(z80_state.nmi && !z80_state.nmi_seen) && + !(z80_state.irq && z80_state.iff1) && + !trs_event_scheduled()) { + trs_paused = 1; + pause(); + } + } + T_COUNT(4); + break; + + case 0xDB: /* in a, (port) */ + REG_A = z80_in(mem_read(REG_PC++)); + T_COUNT(10); + break; + + case 0x3C: /* inc a */ + REG_A++; + do_flags_inc_byte(REG_A); T_COUNT(4); + break; + case 0x04: /* inc b */ + REG_B++; + do_flags_inc_byte(REG_B); T_COUNT(4); + break; + case 0x0C: /* inc c */ + REG_C++; + do_flags_inc_byte(REG_C); T_COUNT(4); + break; + case 0x14: /* inc d */ + REG_D++; + do_flags_inc_byte(REG_D); T_COUNT(4); + break; + case 0x1C: /* inc e */ + REG_E++; + do_flags_inc_byte(REG_E); T_COUNT(4); + break; + case 0x24: /* inc h */ + REG_H++; + do_flags_inc_byte(REG_H); T_COUNT(4); + break; + case 0x2C: /* inc l */ + REG_L++; + do_flags_inc_byte(REG_L); T_COUNT(4); + break; + + case 0x34: /* inc (hl) */ + { + Uchar value = mem_read(REG_HL) + 1; + mem_write(REG_HL, value); + do_flags_inc_byte(value); + } + T_COUNT(11); + break; + + case 0x03: /* inc bc */ + REG_BC++; + T_COUNT(6); + break; + case 0x13: /* inc de */ + REG_DE++; + T_COUNT(6); + break; + case 0x23: /* inc hl */ + REG_HL++; + T_COUNT(6); + break; + case 0x33: /* inc sp */ + REG_SP++; + T_COUNT(6); + break; + + case 0xC3: /* jp address */ + REG_PC = mem_read_word(REG_PC); + T_COUNT(10); + break; + + case 0xE9: /* jp (hl) */ + REG_PC = REG_HL; + T_COUNT(4); + break; + + case 0xC2: /* jp nz, address */ + if(!ZERO_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xCA: /* jp z, address */ + if(ZERO_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xD2: /* jp nc, address */ + if(!CARRY_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xDA: /* jp c, address */ + if(CARRY_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xE2: /* jp po, address */ + if(!PARITY_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xEA: /* jp pe, address */ + if(PARITY_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xF2: /* jp p, address */ + if(!SIGN_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + case 0xFA: /* jp m, address */ + if(SIGN_FLAG) + { + REG_PC = mem_read_word(REG_PC); + } + else + { + REG_PC += 2; + } + T_COUNT(10); + break; + + case 0x18: /* jr offset */ + { + signed char byte_value; + byte_value = (signed char) mem_read(REG_PC++); + REG_PC += byte_value; + } + T_COUNT(12); + break; + + case 0x20: /* jr nz, offset */ + if(!ZERO_FLAG) + { + signed char byte_value; + byte_value = (signed char) mem_read(REG_PC++); + REG_PC += byte_value; + T_COUNT(12); + } + else + { + REG_PC++; + T_COUNT(7); + } + break; + case 0x28: /* jr z, offset */ + if(ZERO_FLAG) + { + signed char byte_value; + byte_value = (signed char) mem_read(REG_PC++); + REG_PC += byte_value; + T_COUNT(12); + } + else + { + REG_PC++; + T_COUNT(7); + } + break; + case 0x30: /* jr nc, offset */ + if(!CARRY_FLAG) + { + signed char byte_value; + byte_value = (signed char) mem_read(REG_PC++); + REG_PC += byte_value; + T_COUNT(12); + } + else + { + REG_PC++; + T_COUNT(7); + } + break; + case 0x38: /* jr c, offset */ + if(CARRY_FLAG) + { + signed char byte_value; + byte_value = (signed char) mem_read(REG_PC++); + REG_PC += byte_value; + T_COUNT(12); + } + else + { + REG_PC++; + T_COUNT(7); + } + break; + + case 0x7F: /* ld a, a */ + REG_A = REG_A; T_COUNT(4); + break; + case 0x78: /* ld a, b */ + REG_A = REG_B; T_COUNT(4); + break; + case 0x79: /* ld a, c */ + REG_A = REG_C; T_COUNT(4); + break; + case 0x7A: /* ld a, d */ + REG_A = REG_D; T_COUNT(4); + break; + case 0x7B: /* ld a, e */ + REG_A = REG_E; T_COUNT(4); + break; + case 0x7C: /* ld a, h */ + REG_A = REG_H; T_COUNT(4); + break; + case 0x7D: /* ld a, l */ + REG_A = REG_L; T_COUNT(4); + break; + case 0x47: /* ld b, a */ + REG_B = REG_A; T_COUNT(4); + break; + case 0x40: /* ld b, b */ + REG_B = REG_B; T_COUNT(4); + break; + case 0x41: /* ld b, c */ + REG_B = REG_C; T_COUNT(4); + break; + case 0x42: /* ld b, d */ + REG_B = REG_D; T_COUNT(4); + break; + case 0x43: /* ld b, e */ + REG_B = REG_E; T_COUNT(4); + break; + case 0x44: /* ld b, h */ + REG_B = REG_H; T_COUNT(4); + break; + case 0x45: /* ld b, l */ + REG_B = REG_L; T_COUNT(4); + break; + case 0x4F: /* ld c, a */ + REG_C = REG_A; T_COUNT(4); + break; + case 0x48: /* ld c, b */ + REG_C = REG_B; T_COUNT(4); + break; + case 0x49: /* ld c, c */ + REG_C = REG_C; T_COUNT(4); + break; + case 0x4A: /* ld c, d */ + REG_C = REG_D; T_COUNT(4); + break; + case 0x4B: /* ld c, e */ + REG_C = REG_E; T_COUNT(4); + break; + case 0x4C: /* ld c, h */ + REG_C = REG_H; T_COUNT(4); + break; + case 0x4D: /* ld c, l */ + REG_C = REG_L; T_COUNT(4); + break; + case 0x57: /* ld d, a */ + REG_D = REG_A; T_COUNT(4); + break; + case 0x50: /* ld d, b */ + REG_D = REG_B; T_COUNT(4); + break; + case 0x51: /* ld d, c */ + REG_D = REG_C; T_COUNT(4); + break; + case 0x52: /* ld d, d */ + REG_D = REG_D; T_COUNT(4); + break; + case 0x53: /* ld d, e */ + REG_D = REG_E; T_COUNT(4); + break; + case 0x54: /* ld d, h */ + REG_D = REG_H; T_COUNT(4); + break; + case 0x55: /* ld d, l */ + REG_D = REG_L; T_COUNT(4); + break; + case 0x5F: /* ld e, a */ + REG_E = REG_A; T_COUNT(4); + break; + case 0x58: /* ld e, b */ + REG_E = REG_B; T_COUNT(4); + break; + case 0x59: /* ld e, c */ + REG_E = REG_C; T_COUNT(4); + break; + case 0x5A: /* ld e, d */ + REG_E = REG_D; T_COUNT(4); + break; + case 0x5B: /* ld e, e */ + REG_E = REG_E; T_COUNT(4); + break; + case 0x5C: /* ld e, h */ + REG_E = REG_H; T_COUNT(4); + break; + case 0x5D: /* ld e, l */ + REG_E = REG_L; T_COUNT(4); + break; + case 0x67: /* ld h, a */ + REG_H = REG_A; T_COUNT(4); + break; + case 0x60: /* ld h, b */ + REG_H = REG_B; T_COUNT(4); + break; + case 0x61: /* ld h, c */ + REG_H = REG_C; T_COUNT(4); + break; + case 0x62: /* ld h, d */ + REG_H = REG_D; T_COUNT(4); + break; + case 0x63: /* ld h, e */ + REG_H = REG_E; T_COUNT(4); + break; + case 0x64: /* ld h, h */ + REG_H = REG_H; T_COUNT(4); + break; + case 0x65: /* ld h, l */ + REG_H = REG_L; T_COUNT(4); + break; + case 0x6F: /* ld l, a */ + REG_L = REG_A; T_COUNT(4); + break; + case 0x68: /* ld l, b */ + REG_L = REG_B; T_COUNT(4); + break; + case 0x69: /* ld l, c */ + REG_L = REG_C; T_COUNT(4); + break; + case 0x6A: /* ld l, d */ + REG_L = REG_D; T_COUNT(4); + break; + case 0x6B: /* ld l, e */ + REG_L = REG_E; T_COUNT(4); + break; + case 0x6C: /* ld l, h */ + REG_L = REG_H; T_COUNT(4); + break; + case 0x6D: /* ld l, l */ + REG_L = REG_L; T_COUNT(4); + break; + + case 0x02: /* ld (bc), a */ + mem_write(REG_BC, REG_A); T_COUNT(7); + break; + case 0x12: /* ld (de), a */ + mem_write(REG_DE, REG_A); T_COUNT(7); + break; + case 0x77: /* ld (hl), a */ + mem_write(REG_HL, REG_A); T_COUNT(7); + break; + case 0x70: /* ld (hl), b */ + mem_write(REG_HL, REG_B); T_COUNT(7); + break; + case 0x71: /* ld (hl), c */ + mem_write(REG_HL, REG_C); T_COUNT(7); + break; + case 0x72: /* ld (hl), d */ + mem_write(REG_HL, REG_D); T_COUNT(7); + break; + case 0x73: /* ld (hl), e */ + mem_write(REG_HL, REG_E); T_COUNT(7); + break; + case 0x74: /* ld (hl), h */ + mem_write(REG_HL, REG_H); T_COUNT(7); + break; + case 0x75: /* ld (hl), l */ + mem_write(REG_HL, REG_L); T_COUNT(7); + break; + + case 0x7E: /* ld a, (hl) */ + REG_A = mem_read(REG_HL); T_COUNT(7); + break; + case 0x46: /* ld b, (hl) */ + REG_B = mem_read(REG_HL); T_COUNT(7); + break; + case 0x4E: /* ld c, (hl) */ + REG_C = mem_read(REG_HL); T_COUNT(7); + break; + case 0x56: /* ld d, (hl) */ + REG_D = mem_read(REG_HL); T_COUNT(7); + break; + case 0x5E: /* ld e, (hl) */ + REG_E = mem_read(REG_HL); T_COUNT(7); + break; + case 0x66: /* ld h, (hl) */ + REG_H = mem_read(REG_HL); T_COUNT(7); + break; + case 0x6E: /* ld l, (hl) */ + REG_L = mem_read(REG_HL); T_COUNT(7); + break; + + case 0x3E: /* ld a, value */ + REG_A = mem_read(REG_PC++); T_COUNT(7); + break; + case 0x06: /* ld b, value */ + REG_B = mem_read(REG_PC++); T_COUNT(7); + break; + case 0x0E: /* ld c, value */ + REG_C = mem_read(REG_PC++); T_COUNT(7); + break; + case 0x16: /* ld d, value */ + REG_D = mem_read(REG_PC++); T_COUNT(7); + break; + case 0x1E: /* ld e, value */ + REG_E = mem_read(REG_PC++); T_COUNT(7); + break; + case 0x26: /* ld h, value */ + REG_H = mem_read(REG_PC++); T_COUNT(7); + break; + case 0x2E: /* ld l, value */ + REG_L = mem_read(REG_PC++); T_COUNT(7); + break; + + case 0x01: /* ld bc, value */ + REG_BC = mem_read_word(REG_PC); + REG_PC += 2; + T_COUNT(10); + break; + case 0x11: /* ld de, value */ + REG_DE = mem_read_word(REG_PC); + REG_PC += 2; + T_COUNT(10); + break; + case 0x21: /* ld hl, value */ + REG_HL = mem_read_word(REG_PC); + REG_PC += 2; + T_COUNT(10); + break; + case 0x31: /* ld sp, value */ + REG_SP = mem_read_word(REG_PC); + REG_PC += 2; + T_COUNT(10); + break; + + + case 0x3A: /* ld a, (address) */ + /* this one is missing from Zaks */ + REG_A = mem_read(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(13); + break; + + case 0x0A: /* ld a, (bc) */ + REG_A = mem_read(REG_BC); + T_COUNT(7); + break; + case 0x1A: /* ld a, (de) */ + REG_A = mem_read(REG_DE); + T_COUNT(7); + break; + + case 0x32: /* ld (address), a */ + mem_write(mem_read_word(REG_PC), REG_A); + REG_PC += 2; + T_COUNT(13); + break; + + case 0x22: /* ld (address), hl */ + mem_write_word(mem_read_word(REG_PC), REG_HL); + REG_PC += 2; + T_COUNT(16); + break; + + case 0x36: /* ld (hl), value */ + mem_write(REG_HL, mem_read(REG_PC++)); + T_COUNT(10); + break; + + case 0x2A: /* ld hl, (address) */ + REG_HL = mem_read_word(mem_read_word(REG_PC)); + REG_PC += 2; + T_COUNT(16); + break; + + case 0xF9: /* ld sp, hl */ + REG_SP = REG_HL; + T_COUNT(6); + break; + + case 0x00: /* nop */ + T_COUNT(4); + break; + + case 0xF6: /* or value */ + do_or_byte(mem_read(REG_PC++)); + T_COUNT(7); + break; + + case 0xB7: /* or a */ + do_or_byte(REG_A); T_COUNT(4); + break; + case 0xB0: /* or b */ + do_or_byte(REG_B); T_COUNT(4); + break; + case 0xB1: /* or c */ + do_or_byte(REG_C); T_COUNT(4); + break; + case 0xB2: /* or d */ + do_or_byte(REG_D); T_COUNT(4); + break; + case 0xB3: /* or e */ + do_or_byte(REG_E); T_COUNT(4); + break; + case 0xB4: /* or h */ + do_or_byte(REG_H); T_COUNT(4); + break; + case 0xB5: /* or l */ + do_or_byte(REG_L); T_COUNT(4); + break; + + case 0xB6: /* or (hl) */ + do_or_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0xD3: /* out (port), a */ + z80_out(mem_read(REG_PC++), REG_A); + T_COUNT(11); + break; + + case 0xC1: /* pop bc */ + REG_BC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(10); + break; + case 0xD1: /* pop de */ + REG_DE = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(10); + break; + case 0xE1: /* pop hl */ + REG_HL = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(10); + break; + case 0xF1: /* pop af */ + REG_AF = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(10); + break; + + case 0xC5: /* push bc */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_BC); + T_COUNT(11); + break; + case 0xD5: /* push de */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_DE); + T_COUNT(11); + break; + case 0xE5: /* push hl */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_HL); + T_COUNT(11); + break; + case 0xF5: /* push af */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_AF); + T_COUNT(11); + break; + + case 0xC9: /* ret */ + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(10); + break; + + case 0xC0: /* ret nz */ + if(!ZERO_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xC8: /* ret z */ + if(ZERO_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xD0: /* ret nc */ + if(!CARRY_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xD8: /* ret c */ + if(CARRY_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xE0: /* ret po */ + if(!PARITY_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xE8: /* ret pe */ + if(PARITY_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xF0: /* ret p */ + if(!SIGN_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + case 0xF8: /* ret m */ + if(SIGN_FLAG) + { + REG_PC = mem_read_word(REG_SP); + REG_SP += 2; + T_COUNT(11); + } else { + T_COUNT(5); + } + break; + + case 0x17: /* rla */ + do_rla(); + T_COUNT(4); + break; + + case 0x07: /* rlca */ + do_rlca(); + T_COUNT(4); + break; + + case 0x1F: /* rra */ + do_rra(); + T_COUNT(4); + break; + + case 0x0F: /* rrca */ + do_rrca(); + T_COUNT(4); + break; + + case 0xC7: /* rst 00h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x00; + T_COUNT(11); + break; + case 0xCF: /* rst 08h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x08; + T_COUNT(11); + break; + case 0xD7: /* rst 10h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x10; + T_COUNT(11); + break; + case 0xDF: /* rst 18h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x18; + T_COUNT(11); + break; + case 0xE7: /* rst 20h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x20; + T_COUNT(11); + break; + case 0xEF: /* rst 28h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x28; + T_COUNT(11); + break; + case 0xF7: /* rst 30h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x30; + T_COUNT(11); + break; + case 0xFF: /* rst 38h */ + REG_SP -= 2; + mem_write_word(REG_SP, REG_PC); + REG_PC = 0x38; + T_COUNT(11); + break; + + case 0x37: /* scf */ + REG_F = (REG_F & (ZERO_FLAG|PARITY_FLAG|SIGN_FLAG)) + | CARRY_MASK + | (REG_A & (UNDOC3_MASK|UNDOC5_MASK)); + T_COUNT(4); + break; + + case 0x9F: /* sbc a, a */ + do_sbc_byte(REG_A); T_COUNT(4); + break; + case 0x98: /* sbc a, b */ + do_sbc_byte(REG_B); T_COUNT(4); + break; + case 0x99: /* sbc a, c */ + do_sbc_byte(REG_C); T_COUNT(4); + break; + case 0x9A: /* sbc a, d */ + do_sbc_byte(REG_D); T_COUNT(4); + break; + case 0x9B: /* sbc a, e */ + do_sbc_byte(REG_E); T_COUNT(4); + break; + case 0x9C: /* sbc a, h */ + do_sbc_byte(REG_H); T_COUNT(4); + break; + case 0x9D: /* sbc a, l */ + do_sbc_byte(REG_L); T_COUNT(4); + break; + case 0xDE: /* sbc a, value */ + do_sbc_byte(mem_read(REG_PC++)); T_COUNT(7); + break; + case 0x9E: /* sbc a, (hl) */ + do_sbc_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0x97: /* sub a, a */ + do_sub_byte(REG_A); T_COUNT(4); + break; + case 0x90: /* sub a, b */ + do_sub_byte(REG_B); T_COUNT(4); + break; + case 0x91: /* sub a, c */ + do_sub_byte(REG_C); T_COUNT(4); + break; + case 0x92: /* sub a, d */ + do_sub_byte(REG_D); T_COUNT(4); + break; + case 0x93: /* sub a, e */ + do_sub_byte(REG_E); T_COUNT(4); + break; + case 0x94: /* sub a, h */ + do_sub_byte(REG_H); T_COUNT(4); + break; + case 0x95: /* sub a, l */ + do_sub_byte(REG_L); T_COUNT(4); + break; + case 0xD6: /* sub a, value */ + do_sub_byte(mem_read(REG_PC++)); T_COUNT(7); + break; + case 0x96: /* sub a, (hl) */ + do_sub_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + case 0xEE: /* xor value */ + do_xor_byte(mem_read(REG_PC++)); T_COUNT(7); + break; + + case 0xAF: /* xor a */ + do_xor_byte(REG_A); T_COUNT(4); + break; + case 0xA8: /* xor b */ + do_xor_byte(REG_B); T_COUNT(4); + break; + case 0xA9: /* xor c */ + do_xor_byte(REG_C); T_COUNT(4); + break; + case 0xAA: /* xor d */ + do_xor_byte(REG_D); T_COUNT(4); + break; + case 0xAB: /* xor e */ + do_xor_byte(REG_E); T_COUNT(4); + break; + case 0xAC: /* xor h */ + do_xor_byte(REG_H); T_COUNT(4); + break; + case 0xAD: /* xor l */ + do_xor_byte(REG_L); T_COUNT(4); + break; + case 0xAE: /* xor (hl) */ + do_xor_byte(mem_read(REG_HL)); T_COUNT(7); + break; + + default: + disassemble(REG_PC - 1); + error("unsupported instruction"); + } + + /* Event scheduler */ + if (z80_state.sched && + (z80_state.sched - z80_state.t_count > TSTATE_T_MID)) { + /* Subtraction wrapped; time for event to happen */ + trs_do_event(); + } + + /* Check for an interrupt */ + if (trs_continuous >= 0) + { + /* Handle NMI first */ + if (z80_state.nmi && !z80_state.nmi_seen) + { + if (instruction == 0x76) { + /* Taking a NMI gets us out of a halt */ + REG_PC++; + } + do_nmi(); + z80_state.nmi_seen = TRUE; + if (trs_model == 1) { + /* Simulate releasing the pushbutton here; ugh. */ + trs_reset_button_interrupt(0); + } + } + /* Allow IRQ if enabled and instruction was not EI */ + else if (z80_state.irq && z80_state.iff1 == 1 + && instruction != 0xFB) + { + if (instruction == 0x76) { + /* Taking an interrupt gets us out of a halt */ + REG_PC++; + } + do_int(); + } + } + } while (trs_continuous > 0); + return ret; +} + + +void z80_reset() +{ + REG_PC = 0; + z80_state.i = 0; + z80_state.iff1 = 0; + z80_state.iff2 = 0; + z80_state.interrupt_mode = 0; + z80_state.irq = z80_state.nmi = FALSE; + z80_state.sched = 0; + + /* z80_state.r = 0; */ + srand(time(NULL)); /* Seed the RNG, for reading the refresh register */ +} + @@ -0,0 +1,248 @@ +/* + * Copyright (C) 1992 Clarendon Hill Software. + * + * Permission is granted to any individual or institution to use, copy, + * or redistribute this software, provided this copyright notice is retained. + * + * This software is provided "as is" without any expressed or implied + * warranty. If this software brings on any sort of damage -- physical, + * monetary, emotional, or brain -- too bad. You've got no one to blame + * but yourself. + * + * The software may be modified for your own purposes, but modified versions + * must retain this notice. + */ + +/* + Modified by Timothy Mann, 1996 + Last modified on Thu Sep 24 16:37:40 PDT 1998 by mann +*/ + +#ifndef _Z80_H +#define _Z80_H + +#include "config.h" +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#ifndef TRUE +#define TRUE (1) +#define FALSE (0) +#endif + +typedef unsigned int Uint; /* 4 bytes */ +typedef unsigned short Ushort; /* 2 bytes */ +typedef unsigned char Uchar; /* 1 byte */ + +#ifdef NOTDEF /*__GNUC__*/ +typedef unsigned long long tstate_t; +#define TSTATE_T_MID (((unsigned long long) -1LL)/2ULL) +#define TSTATE_T_LEN "Lu" +#else +typedef unsigned long tstate_t; +#define TSTATE_T_MID (((unsigned long) -1L)/2UL) +#define TSTATE_T_LEN "lu" +#endif + +struct twobyte +{ +#ifdef big_endian + Uchar high, low; +#else + Uchar low, high; +#endif +}; + +struct fourbyte +{ +#ifdef big_endian + Uchar byte3, byte2, byte1, byte0; +#else + Uchar byte0, byte1, byte2, byte3; +#endif +}; + +/* for implementing registers which can be seen as bytes or words: */ +typedef union +{ + struct twobyte byte; + Ushort word; +} wordregister; + +struct z80_state_struct +{ + wordregister af; + wordregister bc; + wordregister de; + wordregister hl; + wordregister ix; + wordregister iy; + wordregister sp; + wordregister pc; + + wordregister af_prime; + wordregister bc_prime; + wordregister de_prime; + wordregister hl_prime; + + Uchar i; /* interrupt-page address register */ + /* Uchar r; */ /* no memory-refresh register, just fetch random values */ + + Uchar iff1, iff2; + Uchar interrupt_mode; + + /* To signal a maskable interrupt, set irq TRUE. The CPU does not + * turn off irq; the external device must turn it off when + * appropriately tickled by some IO port or memory address that it + * decodes. INT is level triggered, so Z-80 code must tickle the + * device before reenabling interrupts. + * + * There is no support as yet for fetching an interrupt vector or + * RST instruction from the interrupting device, as this gets + * rather complex when there can be more than one device with an + * interrupt pending. So you'd better use interrupt_mode 1 only + * (which is what the TRS-80 does). + */ + int irq; + + /* To signal a nonmaskable interrupt, set nmi to TRUE. The CPU + * does not turn off nmi; the external device must turn it off + * when tickled (or after a timeout). NMI is edge triggered, so + * it has to be turned off and back on again before it can cause + * another interrupt. nmi_seen remembers that an edge has been seen, + * so turn off both nmi and nmi_seen when the interrupt is acknowledged. + */ + int nmi, nmi_seen; + + /* Speed control. 0 = full speed */ + int delay; + + /* Cyclic T-state counter */ + tstate_t t_count; + + /* Clock in MHz = T-states per microsecond */ + float clockMHz; + + /* Simple event scheduler. If nonzero, when t_count passes sched, + * trs_do_event() is called and sched is set to zero. */ + tstate_t sched; +}; + +#define Z80_ADDRESS_LIMIT (1 << 16) + +/* + * Register accessors: + */ + +#define REG_A (z80_state.af.byte.high) +#define REG_F (z80_state.af.byte.low) +#define REG_B (z80_state.bc.byte.high) +#define REG_C (z80_state.bc.byte.low) +#define REG_D (z80_state.de.byte.high) +#define REG_E (z80_state.de.byte.low) +#define REG_H (z80_state.hl.byte.high) +#define REG_L (z80_state.hl.byte.low) +#define REG_IX_HIGH (z80_state.ix.byte.high) +#define REG_IX_LOW (z80_state.ix.byte.low) +#define REG_IY_HIGH (z80_state.iy.byte.high) +#define REG_IY_LOW (z80_state.iy.byte.low) + +#define REG_SP (z80_state.sp.word) +#define REG_PC (z80_state.pc.word) + +#define REG_AF (z80_state.af.word) +#define REG_BC (z80_state.bc.word) +#define REG_DE (z80_state.de.word) +#define REG_HL (z80_state.hl.word) + +#define REG_AF_PRIME (z80_state.af_prime.word) +#define REG_BC_PRIME (z80_state.bc_prime.word) +#define REG_DE_PRIME (z80_state.de_prime.word) +#define REG_HL_PRIME (z80_state.hl_prime.word) + +#define REG_IX (z80_state.ix.word) +#define REG_IY (z80_state.iy.word) + +#define REG_I (z80_state.i) + +#define HIGH(p) (((struct twobyte *)(p))->high) +#define LOW(p) (((struct twobyte *)(p))->low) + +#define T_COUNT(n) (z80_state.t_count += (n)) + +/* + * Flag accessors: + * + * Flags are: + * + * 7 6 5 4 3 2 1 0 + * S Z - H - P/V N C + * + * C Carry + * N Subtract + * P/V Parity/Overflow + * H Half-carry + * Z Zero + * S Sign + */ + +#define CARRY_MASK (0x1) +#define SUBTRACT_MASK (0x2) +#define PARITY_MASK (0x4) +#define OVERFLOW_MASK (0x4) +#define UNDOC3_MASK (0x8) +#define HALF_CARRY_MASK (0x10) +#define UNDOC5_MASK (0x20) +#define ZERO_MASK (0x40) +#define SIGN_MASK (0x80) +#define ALL_FLAGS_MASK (CARRY_MASK | SUBTRACT_MASK | OVERFLOW_MASK | \ + HALF_CARRY_MASK | ZERO_MASK | SIGN_MASK) + +#define SET_SIGN() (REG_F |= SIGN_MASK) +#define CLEAR_SIGN() (REG_F &= (~SIGN_MASK)) +#define SET_ZERO() (REG_F |= ZERO_MASK) +#define CLEAR_ZERO() (REG_F &= (~ZERO_MASK)) +#define SET_HALF_CARRY() (REG_F |= HALF_CARRY_MASK) +#define CLEAR_HALF_CARRY() (REG_F &= (~HALF_CARRY_MASK)) +#define SET_OVERFLOW() (REG_F |= OVERFLOW_MASK) +#define CLEAR_OVERFLOW() (REG_F &= (~OVERFLOW_MASK)) +#define SET_PARITY() (REG_F |= PARITY_MASK) +#define CLEAR_PARITY() (REG_F &= (~PARITY_MASK)) +#define SET_SUBTRACT() (REG_F |= SUBTRACT_MASK) +#define CLEAR_SUBTRACT() (REG_F &= (~SUBTRACT_MASK)) +#define SET_CARRY() (REG_F |= CARRY_MASK) +#define CLEAR_CARRY() (REG_F &= (~CARRY_MASK)) + +#define SIGN_FLAG (REG_F & SIGN_MASK) +#define ZERO_FLAG (REG_F & ZERO_MASK) +#define HALF_CARRY_FLAG (REG_F & HALF_CARRY_MASK) +#define OVERFLOW_FLAG (REG_F & OVERFLOW_MASK) +#define PARITY_FLAG (REG_F & PARITY_MASK) +#define SUBTRACT_FLAG (REG_F & SUBTRACT_MASK) +#define CARRY_FLAG (REG_F & CARRY_MASK) + +extern struct z80_state_struct z80_state; + +extern void z80_reset(void); +extern int z80_run(int continuous); +extern void mem_init(void); +extern int mem_read(int address); /* 0 <= address <= 0xffff REQUIRED */ +extern void mem_write(int address, int value); +extern void mem_write_rom(int address, int value); +extern int mem_read_word(int address); +extern void mem_write_word(int address, int value); +Uchar *mem_pointer(int address, int writing); +extern int mem_block_transfer(Ushort dest, Ushort source, int direction, + Ushort count); +extern int load_hex(); /* returns highest address loaded + 1 */ +extern void debug(const char *fmt, ...); +extern void error(const char *fmt, ...); +extern void fatal(const char *fmt, ...); +extern void z80_out(int port, int value); +extern int z80_in(int port); +extern int disassemble(unsigned short pc); +extern void debug_init(void); +extern void debug_shell(void); + +#endif |