summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBranden Robinson <branden@debian.org>2006-05-14 22:21:50 -0700
committerBranden Robinson <branden@debian.org>2006-05-14 22:21:50 -0700
commit19b0c5e0755e213172625e814ef089398abd7de1 (patch)
treeec96bd66d3cf9fd4b1b37cb92a62c22e2b134e5b
Import xtrs_4.9c.orig.tar.gz
[dgit import orig xtrs_4.9c.orig.tar.gz]
-rw-r--r--ChangeLog1129
-rw-r--r--Makefile224
-rw-r--r--Makefile.local110
-rw-r--r--README227
-rwxr-xr-xcassette149
-rw-r--r--cassette.man148
-rwxr-xr-xcassette.sh127
-rw-r--r--cassette.txt144
-rw-r--r--cd.ccc76
-rw-r--r--cd.cmdbin0 -> 6109 bytes
-rw-r--r--cd6.cmdbin0 -> 6086 bytes
-rw-r--r--cmd.c77
-rw-r--r--cmd.h24
-rw-r--r--cmddump.c178
-rw-r--r--cmddump.man52
-rw-r--r--cmddump.txt52
-rw-r--r--compile_rom.c140
-rw-r--r--config.h27
-rw-r--r--cpmutil.dskbin0 -> 213504 bytes
-rw-r--r--cpmutil.html224
-rw-r--r--cpmutil.tgzbin0 -> 23163 bytes
-rw-r--r--crc.c93
-rw-r--r--debug.c943
-rw-r--r--dis.c2127
-rw-r--r--do6.jcl13
-rw-r--r--dskspec.html590
-rw-r--r--error.c63
-rw-r--r--expall.bas22
-rw-r--r--export.bas33
-rw-r--r--export.cmdbin0 -> 634 bytes
-rw-r--r--export.lst480
-rw-r--r--export.z80408
-rw-r--r--fakerom.hex11
-rw-r--r--fakerom.lst79
-rw-r--r--fakerom.z8042
-rw-r--r--hex2cmd.c54
-rw-r--r--hex2cmd.man23
-rw-r--r--hex2cmd.txt31
-rw-r--r--import.bas27
-rw-r--r--import.cmdbin0 -> 620 bytes
-rw-r--r--import.lst478
-rw-r--r--import.z80407
-rw-r--r--load_cmd.c331
-rw-r--r--load_cmd.h70
-rw-r--r--load_hex.c93
-rw-r--r--m1format.fix11
-rw-r--r--main.c140
-rw-r--r--mkdisk.c395
-rw-r--r--mkdisk.man254
-rw-r--r--mkdisk.txt255
-rw-r--r--mount.ccc104
-rw-r--r--mount.cmdbin0 -> 6798 bytes
-rw-r--r--mount6.cmdbin0 -> 6775 bytes
-rw-r--r--pwd.ccc43
-rw-r--r--pwd.cmdbin0 -> 5559 bytes
-rw-r--r--pwd6.cmdbin0 -> 5536 bytes
-rw-r--r--reed.h47
-rw-r--r--settime.ccc30
-rw-r--r--settime.cmdbin0 -> 235 bytes
-rw-r--r--settime.lst224
-rw-r--r--settime.z80201
-rw-r--r--trs.h173
-rw-r--r--trs_cassette.c1479
-rw-r--r--trs_chars.c2301
-rw-r--r--trs_disk.c3565
-rw-r--r--trs_disk.h214
-rw-r--r--trs_hard.c495
-rw-r--r--trs_hard.h210
-rw-r--r--trs_imp_exp.c682
-rw-r--r--trs_imp_exp.h267
-rw-r--r--trs_interrupt.c501
-rw-r--r--trs_io.c413
-rw-r--r--trs_iodefs.h19
-rw-r--r--trs_keyboard.c1014
-rw-r--r--trs_memory.c535
-rw-r--r--trs_printer.c34
-rw-r--r--trs_uart.c421
-rw-r--r--trs_uart.h89
-rw-r--r--trs_xinterface.c2035
-rw-r--r--truedam.ccc45
-rw-r--r--truedam.cmdbin0 -> 6137 bytes
-rw-r--r--truedam6.cmdbin0 -> 6114 bytes
-rw-r--r--umount.ccc63
-rw-r--r--umount.cmdbin0 -> 5970 bytes
-rw-r--r--umount6.cmdbin0 -> 5951 bytes
-rw-r--r--unix.ccc80
-rw-r--r--unix.cmdbin0 -> 6306 bytes
-rw-r--r--unix6.cmdbin0 -> 6279 bytes
-rw-r--r--utility.dskbin0 -> 213504 bytes
-rw-r--r--utility.jcl38
-rw-r--r--xtrs.man1067
-rw-r--r--xtrs.txt1054
-rw-r--r--xtrs8.dctbin0 -> 910 bytes
-rw-r--r--xtrs8.lst547
-rw-r--r--xtrs8.z80453
-rw-r--r--xtrsemt.ccc496
-rw-r--r--xtrsemt.h73
-rw-r--r--xtrshard.dctbin0 -> 1425 bytes
-rw-r--r--xtrshard.lst919
-rw-r--r--xtrshard.z80800
-rw-r--r--xtrsmous.cmdbin0 -> 433 bytes
-rw-r--r--xtrsmous.lst323
-rw-r--r--xtrsmous.z80256
-rw-r--r--xtrsrom4p.README89
-rw-r--r--xtrsrom4p.hex33
-rw-r--r--xtrsrom4p.lst361
-rw-r--r--xtrsrom4p.z80287
-rw-r--r--z80.c4370
-rw-r--r--z80.h248
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 = ..
diff --git a/README b/README
new file mode 100644
index 0000000..361f3e2
--- /dev/null
+++ b/README
@@ -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)
+
+
+
+Name
+ cassette - data cassette image manipulator for xtrs TRS-80 emulator
+
+Syntax
+ cassette
+
+Description
+ To control the emulated cassette used by xtrs, 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 cassette 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 cas-
+ sette 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 cas-
+ sette.sh, 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.
+
+
+Commands
+ pos generates a status message including the filename being used as the
+ cassette image and the current position within the image, in bytes.
+
+ load [filename] changes the cassette image currently being used to the
+ file specified, and resets the position counter to zero.
+
+ type 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.
+
+ rew [position] changes the position counter to the position specified.
+ If no position is given, the counter is reset to zero.
+
+ ff [position] changes the position counter to the position specified.
+ If no position is given, the counter is set to the end of the file.
+
+ quit exits the cassette shell script.
+
+Types
+ xtrs supports several different types of cassette images, each of which
+ represents cassette data in a different format.
+
+ cas 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.
+
+ 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 cas-
+ sette, so any cassette routines that even halfway worked on real hard-
+ ware should work with it.
+
+ wav 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
+ xtrs.
+
+ You can play wav files written by xtrs 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 xtrs if 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.
+
+ xtrs 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.
+
+ 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.
+
+ 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 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.
+
+
+Authors
+ 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/). 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)
diff --git a/cd.ccc b/cd.ccc
new file mode 100644
index 0000000..d3ed863
--- /dev/null
+++ b/cd.ccc
@@ -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);
+}
+
diff --git a/cd.cmd b/cd.cmd
new file mode 100644
index 0000000..b014d28
--- /dev/null
+++ b/cd.cmd
Binary files differ
diff --git a/cd6.cmd b/cd6.cmd
new file mode 100644
index 0000000..7a2a16c
--- /dev/null
+++ b/cd6.cmd
Binary files differ
diff --git a/cmd.c b/cmd.c
new file mode 100644
index 0000000..15533a2
--- /dev/null
+++ b/cmd.c
@@ -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);
+}
diff --git a/cmd.h b/cmd.h
new file mode 100644
index 0000000..dc4487e
--- /dev/null
+++ b/cmd.h
@@ -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)
+
+
+
+Name
+ cmddump - simulated TRS-80 CMD file loader
+
+Syntax
+ cmddump [flags] infile [outfile startbyte nbytes]
+
+Description
+ cmddump displays information about TRS-80 DOS binary (command) files.
+ It takes an optional set of flags (described below), an input /cmd
+ file, and an optional outfile, an optional starting offset of startbyte
+ into the /cmd file, and an optional nbytes 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.
+
+Options
+ -d print detailed map; same as -m, but don't coalesce
+
+ -i n select ISAM entry n (0x notation OK)
+
+ -m print running load map as file is parsed, coalescing adjacent
+ blocks (implies -t) (default)
+
+ -p foo select PDS entry foo (padded to 8 bytes with spaces)
+
+ -q quiet; turns off -t, -m, -d, -s (later flags can override)
+
+ -s print summary load map after file is parsed
+
+ -t print text of module headers, pds headers, patch names, and
+ copyright notices
+
+ -x ignore anything after the first transfer address
+
+Author
+ cmddump was written by Timothy Mann. This man page was generated by
+ Branden Robinson from comments in the source code.
+
+See also
+ xtrs(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
new file mode 100644
index 0000000..e469125
--- /dev/null
+++ b/cpmutil.dsk
Binary files differ
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 -&gt; system.dsk
+lrwxrwxrwx 1 user group 11 Mar 7 13:49 disk4p-3 -&gt; 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&nbsp;-f&nbsp;disk4p-1;test&nbsp;-f&nbsp;data.dsk&nbsp;&amp;&amp;&nbsp;ln&nbsp;-s&nbsp;data.disk&nbsp;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&nbsp;-f&nbsp;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
new file mode 100644
index 0000000..f87fa1a
--- /dev/null
+++ b/cpmutil.tgz
Binary files differ
diff --git a/crc.c b/crc.c
new file mode 100644
index 0000000..36e7599
--- /dev/null
+++ b/crc.c
@@ -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
diff --git a/debug.c b/debug.c
new file mode 100644
index 0000000..97bc5cd
--- /dev/null
+++ b/debug.c
@@ -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
diff --git a/dis.c b/dis.c
new file mode 100644
index 0000000..3261bdd
--- /dev/null
+++ b/dis.c
@@ -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 */
+}
diff --git a/do6.jcl b/do6.jcl
new file mode 100644
index 0000000..a52cc22
--- /dev/null
+++ b/do6.jcl
@@ -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 {
+&nbsp; SectorHeader headers1[2901];
+&nbsp; unsigned char writeprot;
+&nbsp; unsigned char data1[];
+&nbsp; SectorHeader headers2[2901];
+&nbsp; unsigned char padding;
+&nbsp; 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 {
+&nbsp; unsigned char track;
+&nbsp; unsigned char sector;
+&nbsp; 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>&nbsp;
+<TABLE BORDER COLS=8 WIDTH="100%" NOSAVE >
+<TR>
+<TD>&nbsp;</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&nbsp; 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>
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..e459448
--- /dev/null
+++ b/error.c
@@ -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
new file mode 100644
index 0000000..0581db5
--- /dev/null
+++ b/export.cmd
Binary files differ
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)
+
+
+
+Name
+ hex2cmd - convert Intel hex format to TRS-80 CMD format
+
+Syntax
+ hex2cmd [infile]
+
+Description
+ hex2cmd reads the specified infile (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.
+
+Author
+ hex2cmd was written by Timothy Mann. This man page was created by
+ Branden Robinson.
+
+See also
+ xtrs(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
new file mode 100644
index 0000000..9abc34e
--- /dev/null
+++ b/import.cmd
Binary files differ
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
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..1cb035e
--- /dev/null
+++ b/main.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 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)
+
+
+
+Name
+ mkdisk - Make a blank emulated floppy or hard disk for xtrs, or
+ add/remove an emulated write protect tab
+
+Syntax
+ mkdisk -1 filename
+ mkdisk [-3] filename
+ mkdisk -k [-s sides] [-d density] [-8] [-i] filename
+ mkdisk -h [-c cyl] [-s sec] [-g gran] filename
+ mkdisk {-p|-u} {-1|-3|-k|-h} filename
+
+Description
+ The mkdisk program is part of the xtrs(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 mkdisk does not enforce this conven-
+ tion; you can use any filename. Other extensions sometimes used for
+ emulated floppies are .jv1, .jv3, .8in, and .dmk.
+
+Making Emulated Floppies
+ With the -1 flag, mkdisk 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),
+ mkdisk makes an unformatted emulated floppy of type JV3. No additional
+ flags are accepted.
+
+ With the -k flag, mkdisk 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 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.
+
+Making Emulated Hard Drives
+ With the -h flag, mkdisk makes an unformatted emulated hard drive with
+ cyl cylinders, sec sectors, and gran granules (LDOS allocation units)
+ per cylinder. The hard drive will have cylinder dir 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 cyl, 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 sec, 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 sec
+ 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), sec 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 gran, the default value is 8, the maximum is 8, and the minimum is
+ 1. In addition, it is necessary that sec be evenly divisible by gran,
+ and that sec/gran 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 cyl and sec: it
+ can be at most cyl*sec 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) gran should always be set as large as possible
+ and (2) reducing sec, 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 xtrs users. Therefore the default parame-
+ ters have been chosen to give you the largest drive possible without
+ partitioning.
+
+Write Protection
+ With the -p flag, mkdisk turns 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, mkdisk 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 per-
+ missions. It also clears a write-protected flag inside the file
+ (except on JV1 floppies, which don't have such a flag).
+
+ mkdisk 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.
+
+
+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 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 xtrs 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 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;
+
+
+See also
+ xtrs(1)
+
+ http://www.tim-mann.org/trs80/dskspec.html
+
+Authors
+ mkdisk 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 (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 xtrs and
+ 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
new file mode 100644
index 0000000..0454ea1
--- /dev/null
+++ b/mount.cmd
Binary files differ
diff --git a/mount6.cmd b/mount6.cmd
new file mode 100644
index 0000000..f7be04c
--- /dev/null
+++ b/mount6.cmd
Binary files differ
diff --git a/pwd.ccc b/pwd.ccc
new file mode 100644
index 0000000..f3f5c11
--- /dev/null
+++ b/pwd.ccc
@@ -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);
+}
diff --git a/pwd.cmd b/pwd.cmd
new file mode 100644
index 0000000..d508b4e
--- /dev/null
+++ b/pwd.cmd
Binary files differ
diff --git a/pwd6.cmd b/pwd6.cmd
new file mode 100644
index 0000000..49bb7a9
--- /dev/null
+++ b/pwd6.cmd
Binary files differ
diff --git a/reed.h b/reed.h
new file mode 100644
index 0000000..5102db1
--- /dev/null
+++ b/reed.h
@@ -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
new file mode 100644
index 0000000..6792638
--- /dev/null
+++ b/settime.cmd
Binary files differ
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
+
+
diff --git a/trs.h b/trs.h
new file mode 100644
index 0000000..c5e7bc0
--- /dev/null
+++ b/trs.h
@@ -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
new file mode 100644
index 0000000..4e3738a
--- /dev/null
+++ b/truedam.cmd
Binary files differ
diff --git a/truedam6.cmd b/truedam6.cmd
new file mode 100644
index 0000000..05a6b79
--- /dev/null
+++ b/truedam6.cmd
Binary files differ
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
new file mode 100644
index 0000000..bc6b7c6
--- /dev/null
+++ b/umount.cmd
Binary files differ
diff --git a/umount6.cmd b/umount6.cmd
new file mode 100644
index 0000000..3e13f32
--- /dev/null
+++ b/umount6.cmd
Binary files differ
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
new file mode 100644
index 0000000..b033f30
--- /dev/null
+++ b/unix.cmd
Binary files differ
diff --git a/unix6.cmd b/unix6.cmd
new file mode 100644
index 0000000..6ca649f
--- /dev/null
+++ b/unix6.cmd
Binary files differ
diff --git a/utility.dsk b/utility.dsk
new file mode 100644
index 0000000..0884943
--- /dev/null
+++ b/utility.dsk
Binary files differ
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
new file mode 100644
index 0000000..669605e
--- /dev/null
+++ b/xtrs8.dct
Binary files differ
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
new file mode 100644
index 0000000..ce13ca9
--- /dev/null
+++ b/xtrshard.dct
Binary files differ
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
new file mode 100644
index 0000000..d5b0679
--- /dev/null
+++ b/xtrsmous.cmd
Binary files differ
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
diff --git a/z80.c b/z80.c
new file mode 100644
index 0000000..dda64d4
--- /dev/null
+++ b/z80.c
@@ -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(&REG_IX);
+ break;
+ case 0xED: /* ED.. extended instruction */
+ ret = do_ED_instruction();
+ break;
+ case 0xFD: /* FD.. extended instruction */
+ do_indexed_instruction(&REG_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 */
+}
+
diff --git a/z80.h b/z80.h
new file mode 100644
index 0000000..f077ee6
--- /dev/null
+++ b/z80.h
@@ -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