
  HP/IDE/USB Interface Code Notes
  ===============================

  Contents...
  Background
  The HP/IDE disk interface
  The HP/IDE/USB modification
  USB firmware and formatting
  Configuring the interface code
  Accessing USB files from HP-IPL/OS
  Saving and restoring a disk image
  Additional debug-mode features
  Using the Auto PTR program
  About the SPI/VDRIVE2 code
  About the buffered stream code


Background
----------

HP-IPL/OS was specified in 2001 by Bob Shannon as a way to program HP21xx
minicomputers without requiring a lot of resources. Bob sent me a threaded
interpreter and drivers for PTR and PTP (punch), I learned HP21xx assembly
and by the end of 2002 we had the beginnings of a new hobby OS for HP21xx
mini's with a assembler and drivers for BACI, HPIB, 7970E magtape, dynamic
memory for MX-class machines, and even an XY display driver for drawing
graphics on an oscilloscope. In 2003 Bob engineered an interface to connect
a plain IDE hard disk to a HP21xx mini using a very simple command set which
made it easy to support other disks as well by emulating the "IDE" commands
to set the disk location and read and write multiples of 1KW of data.
In addition to the HP/IDE interface presently there are drivers for 7900
and 7906 disks to use with the SimH HP2100 simulator, due to the nature
of the system high-level disk apps don't need to know what kind of disk
is attached except for the maximum capacity. For the 7900/7906 drives
the removable and fixed platters are treated as separate drives, for IDE
drives different drive numbers add an offset to the internal location.

Initially all the system could do was save and boot a system binary and
permit data to be stored on the disk as a big array of 1KW data blocks,
we needed a real dos. Discussions began on the classiccmp mailing list in
July 2003 in the thread "Simplest (practical) file system?", eventually
leading to a specification for a Simple File System, or SFS. It truly is
simple, all files are 32KW regardless of actual size, with up to 64 files
in each "volume" (directory) and up to 64 volumes per disk. The beginning
of each disk included the original boot system. In 2004 I made a simple
dos called TDOS that supported a very limited subset of SFS (no filenames,
32 files max) to work out mundane details like how to save and load
binaries, in 2005 this "Terrible DOS" was replaced by XDOS which covered
basic tasks such as loading and saving binaries, file system maintenance
and using "alternate memory" (another 32KW bank of memory) to permit
loading IPL packages, viewing text and other basic file processing.
An additional SFS package permitted buffering up to four open files at
once, each in another 32KW bank of memory, with commands for seeking
and accessing data via "MS" (mass storage) streams.

Late in 2007 (influenced by a series of IDE builds that would not boot,
but also because it took so long to load anything via my slow PTR emulator)
I decided to make my own HP/IDE interface. Disk is nice :-) instead of many
minutes I could load stuff in seconds, and actually save my work to files.
I had already been using the 7906 simulation to store and run arbitrary
binaries such as BASIC and programs produced using BCS tools, it was a
simple matter to add both 7906 and IDE drivers to produce a disk image
that booted on both IDE and 7906 drives and enjoy running these old
programs for real. However there were minor usage hassles, the biggest
one was having to unplug the IDE disk from the interface and attach it
to a USB drive tray to transfer the IDE disk data to and from a disk
image file for the simulator using the somewhat dangerous Linux "dd"
command. There must be a better way!

In September 2008 Bob told me about the Vinculum USB disk interface
which has built-in support for a FAT-based file system, I wasted no time
getting a VDRIVE2 module and hooking it to unused port lines on my HP/IDE
interface. The solution is slow (due to having to bit-bang SPI on a 8255
I/O chip) but it solved the problem, no more IDE cable swapping and didn't
require using disk imaging software. Only the interface needed to be running
so mostly I didn't mind it taking about 12 minutes for each 4 megabyte SFS
volume to transfer... unless I needed a file I had just saved on my HP
to do something with it on my PC. To solve that problem I added stream
commands to the interface and wrote VDOS for HP-IPL/OS so I could use the
same old MS-based save/load commands that have been in place since '02.

There was one little "problem" though... the IDE disk requires a custom
bootrom or a functionally equivalent "boot" program to boot. I was swapping
my single serial connection to my PTR emulator to send the "ideboot2.abs"
program from my PC. Not any more.. now I just insert a thumbdrive containing
a "BOOT" file and load it using the stock IBL papertape boot rom. This is
implemented as a separate "Auto PTR" program, and currently it's just for
booting. If a real IDE bootrom is installed it isn't needed.

Presently I have no intentions of trying to cram true PTR/PTP functions
into the HP/IDE/USB interface code, there's no reasonable way to make it
work with existing interrupt-driven software with only one I/O slot. The
current interface code was all "problem-driven", once the USB hardware was
in place for each thing I had problems doing, I added functions to make the
system do the things I wanted to do. Other than booting currently I don't
need true PTR/PTP emulation, I don't run antique compilers on my machine
(it is much easier to do that under simulation then just run the results).
Of course I might want true papertape emulation in the future, and others
might feel very differently about running antique software which require
papertape I/O. The USB mod only makes sense if one already has or intends
to build the HP/IDE interface, it is a natural evolution of the design
(hardware-wise it doesn't get any simpler, just connect a VDRIVE2 to it).
For someone who wants true PTR/PTP emulation or doesn't want IDE disk
then it makes more sense to implement just a USB interface and use two
slots, one for PTR and the other for PTP and commands. VDOS was written
with this in mind, all of the HP/IDE/USB-specific code is contained in
a single word that can be replaced to support an entirely new interface
provided the new system supports similar-enough functions.

The HP-IPL/OS home page is at:
http://www.infionline.net/~wtnewton/oldcomp/hp2100

A collection of ABS games is at:
http://newton.freehostia.com/hp/absgames.zip

An IDE/7906 disk image containing HP-IPL/OS and games is at:
http://newton.freehostia.com/hp/7906sim.html


The HP/IDE disk interface
-------------------------

The present HP/IDE interface is built upon a 8052-based processor with the
Paulmon "OS" burned into the internal rom, at least 8KB ram, at least 8KB
flash rom or NV ram for storing program code, and two 8255-based I/O chips.
Bob Shannon's original HP/IDE interface is based on an earlier Paul board
to which he added an additional 8255 chip. My version of the interface is
built around a Paul Rev5 development board which already has dual 8255's
32KB ram and 30KB of accessible flash rom. The Rev5 development board
is described at: http://www.pjrc.com/tech/8051/board5/index.html

An IDE disk drive interface was added to the base system using lines
from the 8052 and the first 8255, and a hex inverter to flip some of
the signals. This is essentially the same circuit as described at:
http://www.pjrc.com/tech/8051/ide/index.html

The "debug mode" menu in the HP/IDE[/USB] code is more or less a copy
of the example IDE code presented on that page with additional options
for debugging the HP interface and now for saving and restoring data
from the IDE disk to and from a USB file device. Debug mode is engaged
by resetting the interface with bit 0 of port C of the 2nd 8255 (PF.0)
pulled low by a switch or jumper, a 10K resistor from +5V keeps this
line high for normal operation as a disk interface.

To implement the interface to the HP minicomputer 4 additional buffer and
register chips are added to ports A and B of the 2nd 8255 (ports D and E)
to provide 16 input lines and 16 output lines, with lines from the 8052
selecting the input or output bank and clocking in data from the HP.
There is only one command line (connected to a pin on port F) which the
HP asserts to make a request along with a flag line (fed from an 8052 line)
which the HP/IDE interface toggles when the operation is complete, so it's
impossible to distiguish between input and output operations. The code on
both sides must be careful not to get input and output mixed up to avoid
interpreting a request for more data as a command (in practice this isn't
difficult to arrange, not a problem unless there's a bug).

The buffer input and output lines and handshaking lines can be connected
directly to a TTL-compatible HP interface card (such as a "microcircuit"),
other HP interface cards use 12V inverted outputs and inputs designed to
be driven by open-collector inverted outputs, to accommodate an array of
level shifters was added on a separate board. Once consequence of the way
my level shifters are wired is when the HP is on and the interface is off
voltage from the HP leaks into the 8052 controller's supply, enough to power
it up to a Paulmon prompt and prevent power cycle reset. Simply adding a
resistive load from 5V to ground solves that problem at the expense of a
few hundred milliwatts of wasted power. Isolating the power supplies won't
help as then it'll feed the power though the I/O lines which is worse,
the leakage needs to be absorbed. Another consequence of the inverted
I/O method is if the interface is powered with the HP off, it sees it
as all lines set including the command line which immediately sends the
interface into an error state. There's probably a software way to fix
this but it would be tricky to make work with all interface boards
(inverted or not), a more practical solution is to power the HP then
power the interface, or reset the interface after powering the HP.

A schematic and pictures of my version of the HP/IDE interface
(among things) is at: http://newton.freehostia.com/hp/

The interface code divides the disk into an array of 1KW blocks and has
simple commands to seek to a 1KW block, and read or write multiples of 1KW
from and to sequential blocks. The original (and present) code puts the disk
status on the buss after a command to permit the HP to read back without
clocking, however this method only works with "transparent" HP interfaces.
For my system I simply disable the status read command (<IDE) of stock IDE
builds by entering "<IDE" $DEFADR INC 2400 PUT then resaving the build.
This works because if a disk or command error occurs the interface becomes
unresponsive so there's little risk to data, but a side effect of disabling
status is disk reads while locked up return a garbage value and writes
may inadvertently send a reset command and possibly interpret data as a
valid command (unlikely but possible), so care has to be taken to avoid
using the disk if it's locked up. Generally this is only a concern while
debugging disk code, properly working code should never cause a lockup.
A new status request command has been added with the USB mod, to make
use of the IDE.IPL driver code will need to be modified.

The original HP/IDE interface implements the following commands:

 100xxx - resets the disk controller (if it's listening)
 101XXX - SB1 - Sets 8 lsb's of 1K word block address
 102XXX - SB2 - Sets next 8 msb's of 1K word block address
 103XXX - SB3 - Sets next 8 msb's of 1K word block address
 104XXX - SB4 - Sets two 2 bits only of 1K work block address
 110XXX - RDB - Reads XXX blocks from disk. If XXX=0 one 1KW block
  is read into the disk data buffer from the current block address.
  If XXX>0 then XXX blocks (up to a maximum of 31) is read from disk
  and then transfered to the HP.
 111XXX - WRB - Writes XXX blocks from HP to disk.  If XXX=0 the 
  contents of the disk data buffer are written to the disk at the
  current block address.  If XXX>0 then XXX blocks (up to a maximum
  of 31) is transfered from the host and written to disk.

All commands other than reset require the data for the command be in the
lower 8 bits of the command word. The seek commands set the block address
8 bits at a time, the HP-IPL/OS disk word SBLA (set block address) converts
a 32 bit input to the appropriate sequence of sets. After issuing a read
command, the HP must read EXACTLY the specified number of 16 bit words from
the interface, and after a write command must send exactly the specified
number of words. In other words if 110002 is specified then the HP must
read 2048 words. Any deviation will cause the interface to lock up in an
error state until a reset command is issued. After each read or write
the internal block address is incremented by the number of 1KW blocks
transferred, so if reading blocks sequentially it is not necessary to
send additional set-block commands.

Presently all HP-IPL/OS disk code is based on the HP/IDE interface commands,
the drivers for 7900 and 7906 drives translate the IDE commands into other
commands appropriate for the drive. In effect, except for the disk driver the
disk code doesn't care or have to know what kind of disk drive it's running
on as long as it can seek, read and write. One thing that does impact smaller
disk drives, HP-IPL/OS (at least XDOS/SFS) allocates 32KW for every file
regardless of the actual size - makes for simple code. The maximum size is
usually not a limitation but often source and text files are much smaller
leading to waste.. a 7900 drive only has room for a boot system and 36 files
on each platter. But also note there's no reason an alternative DOS can't be
written, and the limitations don't apply to files stored using the new USB
hack, stream files can be as large or small as the USB device permits.


The HP/IDE/USB modification
---------------------------

Adding USB functionality to the HP/IDE interface was essentially adding
a FTDI/Vinculum VDRIVE2 module to unused pins on port C of the 2nd 8255
(port F) and writing additional 8052 code to do things with it.
The VDRIVE2 module and internal VDAP firmware is described at:
http://www.vinculum.com/

The VDRIVE2 is connected to port C of the 2nd 8255 (port F):

Pin 1 (ground) ---- Ground
Pin 2 (data out) -- Port F bit 3
Pin 3 (power) ----- +5V
Pin 4 (data in) --- Port F bit 4
Pin 5 (clock) ----- Port F bit 5
Pin 6 (select) ---- Port F bit 6

The only other required "hardware" is if using a 12V HP I/O interface card
with level shifters make sure there's enough load on the 5V supply to soak
up leakage from the HP when the interface is off to ensure that the VDRIVE2
properly resets when powered on. To prevent problems a 100 to 150 ohm 1/2W
resistor should be connected between +5V and ground.

An optional "Auto PTR" program can be used to autostart the interface
without having to make the interface code itself autostarting, and if
a "BOOT" file exists on the USB file device then sends it to the HP and
waits for reset to be pressed again to resume normal disk operation.
To use this program a 10K pullup needs to be connected from +5V to bit 2
of the 2nd 8255 (PF.2), with a switch to ground to select auto mode.
It's probably a good idea to add 470 ohms or so in series with the
PF.0 debug and PF.2 auto switch lines to avoid driving a short should
software drive the lines high as outputs and a switch is engaged.

My interface uses a center-off toggle so that one position engages
debug mode when the interface code is run, in the middle it boots
to Paulmon (and clears the "already run" flag from Auto PTR), and
the other position engages Auto PTR mode for initial booting and
subsequent (after reset) autostarting of the disk interface code.

The USB mod is described at: http://newton.freehostia.com/hp/ideusb.html
(which is also the home for this file and associated code)

The new HP/IDE/USB interface code provides additional commands...

 105xxx - request IDE status byte with handshaking
 120xxx - read one byte from VDRIVE2 (bit 15 set lsb clear if not valid)
 121bbb - send byte bbb to the VDRIVE2 (will hang if not accepting)
 122xxx - call VDRIVE2 sync code (will hang if not accepting)
 123xxx - call code to clear buffered VDRIVE2 responses
 130xxx - open buffered input file - must follow by sending filename[cr]
 131xxx - read one byte from buffered input file (if EOF or not open returns 0)
 134xxx - open buffered output file - must follow by sending filename[cr]
 135bbb - write byte bbb to buffered output file (if not open then no-op)
 136xxx - close buffered output file (if not open then no-op)
 137xxx - return usb_error value (0 means no error, not at eof)

xxx=don't care provided < 377 octal, generally 000 for clarity.
The write commands encode the data in the lower 8 bits of the command word.
The open commands require that the USB filename be sent immediately after
sending the command followed by a CR (octal 15), filenames longer than
12 characters are truncated but the CR must be eventually sent to the
interface and no further data sent unless it's another valid command.
After issuing a read operation (including the IDE and USB status requests)
the HP must read one word from the interface, the data is contained in the
lower 8 bits. In the case of reading directly from the VDRIVE2 if the unit
is busy or no data is available 100000 octal is returned. Note that this
is different from the raw VDRIVE2 interface which returns the last stale
byte for data, it was changed because ??? (well made sense at the time).

The write to VDRIVE2 command waits for the data to be accepted before
returning control to the interface code. If for some reason the VDRIVE2
module is hung or otherwise not accepting data this command will hang.
This shouldn't ever happen but if it does the interface power probably
needs to be cycled - and check to make sure supply voltage is dropping
low enough to reset the VDRIVE2, I got this when I had too much leakage.

The read VDRIVE2 command returns as quickly as possible if there's data,
if not it waits briefly to see if valid data appears before giving up to
minimize interruption. Generally if reading a directory or file contents
the first invalid read means the end of output, however after issuing
open or close commands, inserting a disk or other operations that delay
there still may be more data pending, if important and expected the app
needs to loop until it receives the required response.

Note... other than USB file browsing and maintenance it is NOT recommended
that low-level Vinculum commands be used for file-processing unless really
needed - failure to close a write file or other mistakes can damage the
file system, usually not badly but a PC should be used to check/repair
the USB file device if a mistake occurs. The streaming operations eliminate
this possibility unless the USB disk is yanked while writing to it, and
should be used instead for applications that can get by with a simple stream
of bytes starting at the beginning. Writes are appended to the end of an
existing file, to overwrite use the Vinculum DLF command to delete the file.

The sync command clears the VDRIVE2's response buffer and sends a sequence
of "E" and "e" commands (which echo E and e) until responses match, then
clears the buffer again to make sure it's not seeing E and e from the last
sequence rather than the current one (probably not necessary but this was
tricky code to get working, I'd rather not touch it unless I have to).
Sync should be called before starting a new command sequences to ensure
that responses correspond with the command just sent.

The clear command clears immediately available responses from the VDRIVE2's
buffer, it waits briefly to make sure no more data is pending but it will
not wait long enough to clear responses from commands which delay such as
opens, close, etc, see notes above concerning the read from VDRIVE2 command.

The streaming operations provide a way to read or write sequential bytes
from or to USB files without having to specify the size of the transfer,
and without actually leaving files open. Both a read stream and a write
stream can be "open" at the same time. There is no close command for a
read stream but a write stream must be closed to write the data remaining
in the write buffer to the file.

The following error codes are returned by the USB error command:

0    successful operation
1    no disk
2    command failed (file not found or directory specified)
3    invalid filename
4    file already open (by the VDRIVE2)
5    disk full
6    streaming input buffer not open
7    streaming output buffer not open
8    past end of input file
255  unknown error occured

Not all errors are exactly parsed and are simply reported as "unknown",
particularly errors that occur during streaming. The USB error value is
undefined until a open buffer or close buffer command is issued.

The open buffered input file command stores the filename to associate with
the read buffer, checks the filename to make sure it's valid and exists, and
saves the size of the file to detect EOF. If an error occurs the USB error
value is set appropriately and the buffer is marked invalid, otherwise the
USB error value is cleared and the buffer filled with the initial contents
of the file. If the filename following the open for read command is empty
(just a CR) then the read buffer is marked invalid and abandoned.

The read byte from file command returns the next byte from the read buffer.
When all bytes in the read buffer have been sent it automatically refills
the buffer, any unexpected error during this process sets USB error to
"unknown" and invalidates the read buffer. When bytes are read past the
end of the file, USB error is set to the EOF value and 0 is sent back to
the HP. EOF can be detected by checking for 0 if text, or if binary data by
checking USB error if 0 is read. If the read buffer filename is not valid
then USB error is set and 0 is sent back to the HP.

The open buffered output file command stores the filename to associate with
the write buffer and checks the filename to make sure it's valid. If another
file was previously buffered and not closed, any remaining buffer contents
are lost. Specifying an empty filename abandons the write buffer. If the
filename appears to be valid USB error is set to 0, otherwise set the best
it can. The file isn't actually created until the buffer files or the close
command is given so not all errors can be detected such as a full disk,
but common errors such as an invalid filename are detected.

The write byte to file command puts the byte in the write buffer.
When the buffer is full it is automatically appended to the output file.
Any error that occurs during the write process sets USB error to something
(presently either disk full or unknown) and invalidates the write buffer.
Writes to an invalid buffer have no effect other than setting USB error.

The close buffered output file command writes the remaining bytes in the
write buffer to the file then marks it invalid, if no error detected then
clears USB error otherwise sets to something (disk full or unknown). If the
write buffer isn't valid then clears the USB error value but otherwise does
nothing, this can be done to make sure USB error isn't random but doing
that isn't really necessary since it's generally checked only after an
open buffer command is given.


USB firmware and formatting
---------------------------

Important: The VDAP USB firmware in the VDRIVE2 module may need to be
updated for proper operation! My VDRIVE2 came with firmware version 2.08
which did not work (inserted blocks of random data into write files).
Firmware version 3.66 works OK. To upgrade the VDRIVE2, download the
latest VDAP "FTD" firmware from http://www.vinculum.com/downloads.html
and copy the file to the USB file device with the name "FTRFB.FTL" and
insert into the powered VDRIVE2 module. Do not interrupt while the LED
is flashing. Afterwards use the FWV command from the debug-mode prompt
or VPROMPT from HP-IPL/OS to verify that the new firmware was installed.

Note... the VDAP manual version numbers do not correspond with the firmware
version numbers. Firmware version 2.08 is very old, manual version 2.05 is
the latest manual for firmware version 3.66 at the time of this writing.

The VDAP firmware supports a simple single-FAT file system using
old-style 8.3 filenames. It is compatible with FAT32 formatting with
long filenames provided the VDRIVE2 is not used to rename files copied
to the USB file device from a PC, as the PC will continue to see the
old filename (long filename fields are totally ignored) and possibly
be confused if another file was renamed with the same name.

The VDRIVE2 must see a 512-byte FAT file system in the first partition
of the USB file device. For my Sandisk 1GB Cruzer I used the U3uninstall.exe
program to remove the extra partition, used Linux's fdisk utility to add a
primary partition, then used the mkfs.msdos utility to format the partition.
Turns out this is not necessary (other than to get rid of U3), the VDRIVE2
has no problems accessing a new 2GB Sandisk Cruzer with the stock format.

Linux's fsck.msdos disk check utility always reports that "FATs differ but
appear to be intact" with a stock format. To avoid this, format the device
using Linux's mkfs.msdos utility with the -f 1 option to use a single FAT.
Windows XP's disk check function does not report this "error". I've had
no luck finding any formatting option that prevents the PC from recording
long filenames, so avoid using REN to rename files copied from a PC.
A related effect, after using DLF to delete a PC-copied file, the LFN
remains (fsck.msdos reports as an orphaned LFN), possibly resulting in
an incorrect PC name if another file is copied into its place.

The v3.66 firmware always allocates space on the drive when a file is
opened for write, if the file size is not increased then a PC disk checker
will report this as a lost cluster. This is harmless other than a bit of
wasted space but to avoid the error the HP/IDE/USB interface code does
not open a file unless it is adding data to the end of the file.

For the most part the disk checker glitches can be ignored and the
VDRIVE2 and thumbdrive just used, oblivious to these things except for
the precaution to not rename files copied to the thumbdrive from a PC.
The disk checking effects are only a factor when debugging VDAP code
which might crash and leave files open, requiring fixup to reclaim
the space and otherwise keep the file system in good condition.


Configuring the interface code
------------------------------

Presently the HP/IDE/USB interface code is supplied in two versions,
a "full" version with a separate SPI/USB debug menu, and a "light"
version which has the USB "dos prompt" but not the extra debugging
functions. The interface commands and essential debug-mode functions
are identical in both versions.

[ Eventually this should be reduced to a single version, although
  the major portion of the code is "done" except perhaps optimizing,
  the SPI/USB debug menu is a bit rough - it was programmed quickly
  for the purpose of making it all work. Perhaps the menu in the light
  version can be modified to add functions for calling sync, sending
  hex byte(s) and receiving bytes to display in hex and ascii, that's
  probably all that's really needed. Most don't need a function that
  calls subroutines, that's only useful when programming and a way to
  test pieces of code is needed. It was very handy when the SPI bit
  sets and clears were separate subroutines (now commented) and I had
  to make sure each bang produced the desired results. The simplest
  thing for me to do is supply two versions, one with extra debugging
  functions, another that's just an interface, and sort it out later. ]

Configuration of both versions is identical other than the "full"
version has an additional equate for specifying the starting location
of the USB and debugging code.

The HP/IDE/USB interface source contains an "autobaud" program at the
end to avoid having to press enter to start Paulmon and run the code.
If using a version of Paulmon with fixed baud rate then remove the
code starting with: copied and adapted from: http://www.pjrc.com/
(or it might not matter). Equates for setting the baud-rate constant
and location for the autobaud program are at the top of the source.
Note that the 9600 baud rate is for a 22.1182 Mhz crystal,
according to the formula: baud_const=256-(115200/baud_rate)
For a 11.0592 Mhz crystal use: baud_const=256-(57600/baud_rate)
(i.e. for 9600 baud @11mhz change baud_const to 250)

Here's the stock configuration settings...

.equ port1base,  0xF800   ;0x4000 for original, 0xF800 for Rev4/Rev5
.equ port2base,  0xF900   ;0x6000 for original, 0xF900 for Rev4/Rev5
.equ idebufbase, 0x2300   ;start of ide/usb buffer ram
.equ readbufsize,  2304   ;size of usb read buffer
.equ writebufsize, 2304   ;size of usb write buffer
.equ usbbufbase, idebufbase + 2560  ;put usb buffers after ide buffers
.equ usbvarbase, usbbufbase + readbufsize + writebufsize ;usb vars follow
.equ location,   0x8000   ;location of IDE code, req's about 3.0KB
.equ usblocat,   0x9000   ;location of USB code, req's about 4.6KB
.equ autolocat,  0xF000   ;location of autobaud init program
.equ baud_const, 244      ;9600 baud w/ 22.1182 MHz

"port1base" and "port2base" specify the base addresses of the first (IDE)
and second (HP) 8255 chips. Change accordingly.

"idebufbase" specifies the start of the IDE buffers, which require 2560
bytes (5 times 512). The stock setting was chosen to put all buffers and
variables between 0x2300 and 0x3F40, with USB read and write buffer sizes
of 2304 bytes each. If increasing the USB buffer sizes with port1base set
to 0x4000 then idebufbase must be reduced accordingly, for example setting
to 0x2100 would permit the USB buffers to be up to 2655 bytes before crossing
0x3FFF but that would leave no room for expansion.

"readbufsize" and "writebufsize" specify the size of the USB stream buffers.
For a Rev4/Rev5 board these can be increased to 4096 or more for better
performance. With IPL-coded functions such as ABSLOAD and SYSALL the
difference between 2304 and 4096 is only about 2.5%, machine-coded MS
streaming functions will experience a bigger difference (presently I
have no significant machine-coded MS functions so 2304 is fine by me).
Making the buffers too small however does have an impact - when the
buffers are set to 512 bytes ABSLOAD speed is 78%, and SYSALL speed
is 84% of the speed achieved with 2304 byte buffers. The data transfer
speed with 2304 byte buffers was measured at 759 bytes per second for
SYSALL and 1103 bytes per second with ABSLOAD, these are far less than
the raw SPI speed of about 8192 bytes per second. It is difficult to
predict what the rates will be when running at 11mhz but chances are
it won't be that much slower as the open/close time required by the
VDRIVE2 and the rate at which HP-IPL/OS sends and receives data are
the primary limiting factors, not the clock rate of the controller.
At least for IPL streaming... disk imaging time would be doubled.

"usbbufbase" and "usbvarbase" specify where the USB buffers and
variables are located, these are tied to formulas to place the USB
buffers after the IDE buffers, and place the USB variables after the
USB buffers. Change these only if they need to be scattered.

"location" specifies the location for the interface code, generally
set to the beginning of flash rom which is typically 0x8000.

"usblocat" is present only in the full version with a separately
runable menu for debugging the USB functions. It is set to 0x9000 for
convenience, can be changed to 0x8C00 for better packing, has to be
if only 8KB of flash rom is available. Better yet, only use the light
version of the interface code if only 8KB rom is available.

"autolocat" specifies the location of the autobaud program, this
must be changed if using and only 8KB of flash rom is available,
0x9F00 should work (the assembler will complain if it conflicts).

The interface code is set to not autostart, for a production system
either page down to the Paulmon header and comment the non-autostarting
line and uncomment the autostarting line, or use a separate autostart
program to jump to the program code (64 bytes past "location") based
on a switch input. The separate "Auto PTR" program uses input line PF2
to determine if it should exit or check the USB disk for a "BOOT" file.
After the next reset, or immediately if no boot file it jumps to 0x8040.
This preserves access to Paulmon. If not using Auto PTR a similar thing
can be done with much less code, or just make the main interface code
autostart once it has been tested.

Once edited as needed, assemble the source using the AS31 assembler,
available at: http://www.pjrc.com/tech/8051/index.html

Once assembled, send the hex file through a serial terminal to the
Paulmon prompt - flash memory must be erased first. If replacing a
previous version of the HP/IDE interface code be sure to dump and save
a copy of the previous code, or have the previous hex file handy, just
in case the new code doesn't work. This can be tricky stuff but as
embedded development environments go it doesn't get much easier than
Paulmon. One particularly handy feature is the E option to display/edit
memory, was perfect for making sure the buffers didn't overflow past
their designated bounds and examining the USB variables to make sure
the strings and bits were properly set.


Accessing USB files from HP-IPL/OS
----------------------------------

The VDOS.IPL package permits sending bytes and commands to the VDAP
firmware in the VDRIVE2, receiving response bytes from the VDRIVE2,
and redirecting MS (mass storage) streams to and from USB files.
The VDOS stream operators are simple, but to use them effectively
requires familiarity with how HP-IPL/OS' MS stream functions work.

Here's an overview of existing functions which write data to and
read data from MS streams...

LOAD - loads an IPL file from MS in
SYSALL - saves the current system to MS out in ABS format
ALTSAVE ZAM - adds the swapper at 77000 and clears alt mem
ABSLOAD - loads an ABS file from MS in into alternate memory
ALTSAVE ZAM ABSLOAD 77000 RUN - loads and executes an ABS file
"FILE" F2MS - copies a SFS file to MS out as-is (bin NOT converted to ABS)
"FILE" ABS2F - loads an ABS file from MS in and saves it to an SFS file
PTZERO - writes 20 zeros to MS out to use as ABS leader/trailer
from to ABSOUT - writes memory range to MS out as an ABS segment

altutil.ipl has...
RUNABS - loads and runs an ABS file (but prompts for cable swap)
 (if no run vector at 2/3 prompts for run address, supports HP-BASIC)
PTHEADER - writes 8 zeros to MS out for a shorter ABS leader
from to AAOUT - writes range of alt mem to MS out as an ABS segment
ALTABS - saves all or part of alt mem to MS out in ABS format
 (supports saving HP-BASIC programs, prompts to patch to autorun/re-enter)

fcam.ipl (req's altutil.ipl) has...
AM2ABS - scans for code in alt mem and saves to MS out in ABS format
"FILE" F2ABS - saves a SFS file to MS out in ABS format
Note - both of these are slow as they have to examine all of alt mem
 to determine where the code extents are then prompt for confirmation.

sioutil.ipl contains...
SIOPATCH - loads 16K SIO driver, prompts for slots, writes new driver

fed.ipl/fedutil.ipl has...
MASAVE - saves edit file in alt mem to MS out as text (adds ~TERMINATE~)
MALOAD - loads text file from MS in into alt mem as edit (req's ~TERMINATE~)
(these use ~TERMINATE~ on the last line to mark the end of transfer,
 because they trim and add space padding they are fairly slow)

ipl_notes.txt contains...
"FILE" TX2F - copies plain text from MS in to a SFS file (req's SFS)

MS stream words for writing IPL programs...
MSBIN - reads a byte from MS in and pushes to the system stack
MSWIN - reads a word (2 bytes) from MS in and pushes (1st byte in msb)
MS$IN - reads a line from MS in and pushes to X stack (line must end in CRLF)
byte MSBOUT - pops stack and writes byte to MS out
word MSWOUT - pops stack and writes 2 bytes to MS out (msb to 1st byte)
"string" MS$OUT - pops string from the X stack and writes it to MS out
MSCRLF - writes a CRLF to MS out to terminate a line of text
>MS - redirects "normal" console output to MS out (but not console prompts)
<MS - redirects MS in to console input (at the prompt equivalent to LOAD)
<>CON - restores normal console input and output
CONSOLE - restores normal console input and output AND resets MS to PTR/PTP

Anything that prints can be made to output to MS out instead by
using >MS and using <>CON to cancel the redirection, for example...

? ;List internal IPL code to MS out as an IPL file
? >MS "VARIABLE SOMEVARIABLE" $PRINT
? EXPLAIN SOMEWORD
? EXPLAIN ANOTHERWORD
? "CONSOLE" $PRINT <>CON

If using the VDOS stream ops and redirecting print output use <>CON to
restore console I/O if more data has to be written to the stream file.
IPL files normally end with CONSOLE to terminate the load and reset MS,
this is fine since it's at the end and no more file data is needed.

Configuring a VDOS build...

To access the USB file system from HP-IPL/OS the VDOS.IPL word has
to be loaded into the current system. There are a few ways to do this
on real hardware, probably the easiest is to use the CP2F word from
ipl_notes.txt - usage "VDOS.IPL" CP2F then paste the VDOS.IPL file
to the console (~TERMINATE~ ends the transfer, already in the file).
CP2F requires the SFS package be loaded to do its own stream thing.

If an editable version of VDOS.IPL is desired then load up a build
containing fed.ipl/fedutil.ipl then enter CALOAD and paste in the
VDOS.IPL file (with ~TERMINATE~) then enter "VDOS.IPL" SVFILE
to save it to a disk file in "array" editor format.

When pasting text into CP2F or CALOAD the terminal must provide
character and line delay to properly transfer. Hyperterminal has
settings for this, experiment to find numbers that give error-free
transfer and then increase the delays to make sure.

Once VDOS.IPL is on the disk as an SFS file then "VDOS.IPL" XLOAD
to load it into the current system. It contains a fair amount of
CREATE code so if page errors occur, FORGET !VDOS and load something
else first to push end-of-dictionary past the next 1KW boundary.
VARIABLE ~PAD [some number] can be used to waste memory if necessary.

Alternatively, instead of copying the .IPL source to disk it can be
loaded directly into the system by attaching to PTR and entering LOAD.
This method works fine with my "pass-through" PTR emulator, not sure
how effective the technique is with eeprom-based emulators which may
require an extra character be added to the beginning of the file
(I'm not familiar with the design but have heard that mentioned).

Once loaded enter 2 RUN or enter !VDOS to make sure it sets itself
to the current IDE I/O slot, and save to a build to disk using XSAVE.
With VDOS, getting IPL code into the system need not be a hassle,
the whole idea is to eliminate having to import files using
copy/paste, burning eeproms or other inconvenient methods.

Using VDOS...

Here are comments from VDOS.IPL (11/1/08):
; Core functions...
; !VDOS - autostarting word which patches itself to interface slot
; VSYNC - syncronize so responses match last command given
; VECS - syncs, selects extended commands and ascii numbers
; VCLEAR - removes last response if immediately available
; VPROMPT - runs a shell to enter commands and print responses
; VDIR - lists current directory of the USB drive
; "DIRNAME" VCD - changes to a new directory
; "FILE.EXT" VSHOW - lists file contents to the terminal
; "FILE.EXT" VDEL - deletes a file in the current dir
; byte >VDR - sends a byte to the VDRIVE2
; <VDR - pushes a byte from the VDRIVE2 (bit 15 set if not valid)
; "string" $>VDR - sends a string to the VDRIVE2
; "command" $>VCMD - sends a string to the VDRIVE2 plus CR
; PVDR - prints VDRIVE2 response if immediately available
; &WFVDR - wait for response and push 1st byte received
;
; Streaming functions...
; "FILE.EXT" USBAPPEND - redirects MS output to a USB file (appends)
; "FILE.EXT" USBWRITE - redirects MS output to a USB file (overwrites)
; "FILE.EXT" USBREAD - redirects a USB file to MS input
; USBCLOSE - closes a USB streaming output file
; USBSTAT - pushes USB streaming error status (0=ok)
;
; Example stream uses...
;  Load an IPL file: "FILE.IPL" USBREAD LOAD
;  Import an ABS file to disk file: "PROGNAME.ABS" USBREAD "PROGNAME" ABS2F
;  Save current system: "SYSNAME.ABS" USBWRITE SYSALL USBCLOSE
;  Append text to file: "FILE.TXT" USBAPPEND "Text" MS$OUT MSCRLF USBCLOSE
; Note... best to put USBREAD/USBWRITE/USBAPPEND commands on their own
; line so if error occurs rest of line won't run with an invalid stream.

To test for essential functionality, insert a USB file device containing
files and enter VDIR, which should list the disk directory.
VDIR is one of several "convenience" commands, others are...

"DIRNAME" VCD - changes to another directory. ".." VCD changes to the
parent directory, "/" VCD changes to the root directory. There is no
feedback if an invalid directory is specified, it just doesn't work.

"FILENAME.EXT" VDEL - deletes a file. Be careful when using this command,
there is no confirmation, and no messages are displayed either way. The
intention was to provide a command that just does it and can be used from
programs without text output or pauses to confirm.

"FILENAME.EXT" VSHOW - lists file contents to the console. There is no
filtering (it simply shells a RD command) so this command should only be
used to display plain text files.

For issuing other VDRIVE2 commands use VPROMPT but avoid using file
open and read/write commands as these require exact specifications and
the prompt was not designed to support such operations.
Useful commands at the prompt include...
DIR - lists file and directory names in the current directory
DIR FILENAME.EXT - displays size of a file as 4 hex digits, lsb to msb
DLF FILENAME.EXT - deletes a file
REN FILENAME.EXT NEWNAME.EXT - renames a file (don't rename PC files)
RD FILENAME.EXT - lists file contents to console (text only)
CD DIRNAME - changes to another directory (.. for parent, / for root)
MKD DIRNAME - creates a new directory
DLD DIRNAME - removes a directory if it contains no files
[enter] - display more VDRIVE2 output after inserting a USB disk etc
EXIT - not a VDRIVE2 command but parsed to exit VPROMPT.

The VDIR, VSHOW and VPROMPT use PVDR (print VDRIVE2 response) to
display the output from the VDRIVE2. PVDR is a simple word and does not
provide "press any key" paging prompts, so it needs to be used with a
terminal which provides a scroll-back buffer, and it does not filter
non-printable characters so if VSHOW/RD is used to list a binary file
garbage will be displayed. By default PVDR adds a LF after every CR
received for proper formatting. VSHOW and RD from VPROMPT suppress
the extra LF's since it's assumed they're already present in the text
being listed. To otherwise control use @PVLF #0 PUT to suppress the
added LF's and @PVLF #1 PUT to add LF's after CR's.

Using the stream functions...

Caution! The HP/IDE/USB interface code does not check to see if the
read and write buffers point to the same file, don't do that!
Truly preventing would take a fair amount of interface code, more than
just a simple compare as different names can map to the same filename.
A word that prompts to overwrite is presented further down which can
be used by "wrapper" words to provide at least some protection.

"FILENAME.EXT" USBREAD attaches the specified file to MS input, afterwards
any MS command that reads from MS in can be used. If the file doesn't exist
or an invalid name is specified, USB error is set and an error message is
displayed, if successful USB error is set to 0. Specifying an empty string
abandons the read file buffer and force further reads from MS input to 0.
Reads from an invalid buffer return 0's and set the USB error value.
Reads past the file end return 0's and set USB error to indicate EOF.
Note that EOF is not set until a read past the end is attempted.

"FILENAME.EXT" USBAPPEND attaches MS output to the specified file, afterwards
any MS command that writes to MS out can be used. If the file exists, data is
appended to the end of the file, otherwise a new file is created once enough
data has been written to fill the buffer or USBCLOSE is used. If the filename
is invalid or another error occurs, USB error is set an error message is
displayed, otherwise USB error is set to 0. Actual success cannot be
determined until the buffer fills or is closed and data is actually written.
Specifying an empty string abandons the write buffer, any data in the write
buffer is lost. Writes to an invalid buffer discard the byte and set the
USB error value.

"FILENAME.EXT" USBWRITE deletes the specified file if it exists using the
VDEL command, then runs USBAPPEND. Be careful when specifying the filename
as the file is deleted immediately with no confirmation.

USBCLOSE must be used after all data has been written to a file to write
the remaining buffer contents to the file, afterwards the buffer is marked
invalid to prevent further writes. The USB error value is set to 0 if
successful or no buffer is open, otherwise USB error is set and an
error message is displayed.

USBSTAT pushes the current USB error value to the stack,
USBSTAT PNUM prints the current error value.

The HP/IDE/USB interface code returns the following error codes
(listed in octal):

0    successful operation
1    no disk
2    command failed (file not found or directory specified)
3    invalid filename
4    file already open (by the VDRIVE2)
5    disk full
6    streaming input buffer not open
7    streaming output buffer not open
10   past end of input file
377  unknown error occured

If USBREAD, USBWRITE, USBAPPEND or USBCLOSE has not been run,
the error value is undefined and will likely be random. The actual
error numbers are subject to change (especially if VDOS is ported
to different interface hardware, but also just to improve reporting).
It's best (as with the IDE error code) to not rely on any particular
value other than 0 meaning no error.

The VDOS comments list "one liners" such as "FILE.IPL" USBREAD LOAD to
load an IPL file, but be careful when specifying the filename if doing
that, if the file does not exist the command will hang as LOAD loads a
perpetual string of 0's. To recover from such garbage input (as with other
cases of using a MS word with an invalid MS stream) press the halt button,
put 2 in P, store, preset, run and if the LED's on the disk interface show
a not-ready condition reset that too. To avoid having to do this, or avoid
waiting for an output word to write to nothing, use the read/write/append
command on its own line *then if no error* use the MS stream word.

Simple wrappers can be defined to automate common loads with error-checks
to ensure the filename is valid (except for empty), for example...

OCTAL DEFINE VLIPL ;usage: "FILE.IPL" VLIPL - loads an IPL file
USBREAD USBSTAT IFZ LOAD ENDIF END

OCTAL DEFINE VLABS ;usage: "FILE.ABS" VLABS - loads/runs an ABS file
USBREAD USBSTAT IFZ
 ALTSAVE ZAM ABSLOAD CRLF 77000 RUN
ENDIF END

...but these can be improved upon and combined into a single word
(similar to how XLOAD works) with additional tests to make sure it
only runs the binary if a run vector is present...

OCTAL DEFINE VLOAD ;10/25/08(A)
;usage: "FILE.EXT" VLOAD - loads something from USB
;if text loads as IPL, otherwise attempts to load/run ABS
;the ABS file must have a run vector in 2/3 or is not run
;clears alt mem first, not intended for overlays
$TRIM $LEN IFZ $DROP ELSE ;make sure a filename is specified
 $DUP USBREAD USBSTAT IFNZ $DROP ELSE ;if file exists
  MSBIN ;read one byte to test if text or binary
  USBREAD ;again with dup'd string to start from beginning again
  IFNZ ;if the initial byte is not zero
   LOAD ;load the file as IPL
  ELSE
   ALTSAVE ZAM ABSLOAD CRLF ;attempt to load the ABS file
   2 150 #1 A>CCOPY ;copy alt mem loc 2 to main mem loc 150
   150 GET 124003 SUB IFZ ;if loc 150 = 124003 (JMP 3,I) then
    "Run from 77000 to return" $PRINT CRLF ;an optional reminder
    77000 RUN ;swap and execute the binary
   ELSE  ;optional message...
    "No run address " $PRINT
   ENDIF
  ENDIF
 ENDIF
ENDIF
END

Here's a word that saves the current system to an ABS file on the
USB drive, similar to how XSAVE works except prompts to overwrite
(XSAVE just doesn't do it). The file-exists/prompt code is a bit involved
so to be able to reuse putting that code in a separate word other words
which might want to prompt to overwrite can call...

OCTAL DEFINE VOWCHECK ;10/25/08
;if file exists prompts to overwrite, string not removed from X stack
;pushes 0 if doesn't exist or if overwrite was confirmed by user
;pushes 1 if exists and overwrite not confirmed, or if no file specified
$TRIM $LEN IFZ ;if string is empty
 "No name specified " $PRINT #1
ELSE
 VSYNC "SCS" $>VCMD 221 >VDR 15 >VDR VCLEAR ;select short/binary commands
 #1 >VDR 40 >VDR $DUP $>VCMD ;send [0x01][space][filename][cr] to VDRIVE2
 &WFVDR DROP ;wait for VDRIVE2 and discard the CR
 #1 ;push a file-exists flag
 <VDR 103 SUB IFZ ;if next char is C
  <VDR 106 SUB IFZ ;if next char is F
   <VDR 15  SUB IFZ ;if next char is [cr]
    DEC ;file isn't there, decrement flag to 0
   ENDIF
  ENDIF
 ENDIF
 VECS ;clear rest of response, go back to long commands
 DUP IFNZ ;save exists flag, if file exists
  "File exists. Overwrite? " $PRINT ;print message
  CHRIN 40 PCHR ;get a char, print space after it
  131 SUB IFZ DEC CRLF ENDIF ;if Y pressed dec flag to 0 and print crlf
 ENDIF
ENDIF
END

...and here's the actual system-save word...

OCTAL DEFINE VSAVE ;10/25/08
;usage: "FILENAME.ABS" VSAVE - saves current system to an ABS file
;prompts to replace if the file exists.
VOWCHECK ;check file and prompt to overwrite if exists
IFNZ ;if file exists and overwrite not confirmed
 $DROP ;the filename string and exit
ELSE ;otherwise try to save...
 USBWRITE ;opens write stream, deletes the file if it exists
 USBSTAT IFZ ;if no error
  SYSALL ;save the current system
  USBCLOSE ;finish writing the file
 ENDIF
ENDIF
END

Here's a file copy program...

DEFINE VCOPY ;10/25/08(B)
;copies a USB file to another file in the same directory
;Usage: "SRCFILE" "DESTFILE" VCOPY
$TRIM $SWAP $TRIM ;trim spaces from names, make sure both specified
;source filename is now at the top of the X stack (swapped)
#0 ;error flag
$LEN IFZ INC ENDIF ;error if src file empty
$SWAP ;now dest fn on top of X
$LEN IFZ INC ENDIF ;error if dest file empty
$CPY ;copy dest to Y because equality test eats
$EQUAL IFNZ INC ENDIF ;error if src=dest (not perfect)
Y>>X $SWAP ;restore dest, swap to put source on top of X stack
IFNZ ;if filename error
 "Can't do that " $PRINT $DROP $DROP
ELSE ;first stage of filename checks passed, try copy...
 USBREAD USBSTAT IFNZ $DROP ELSE ;redirect src file, if no error
  VOWCHECK ;check/confirm overwrite of dest file
  IFNZ ;if overwrite not confirmed
   $DROP "" USBREAD ;drop output fn, force input invalid
  ELSE ;doesn't exist or overwrite confirmed
   USBWRITE USBSTAT IFZ ;open write buffer, if no error...
    "Copying file, please wait... " $PRINT
    DO #0 ;loop, change 0 on stack to <>0 to stop
     MSBIN ;get byte from file
     DUP IFZ USBSTAT IFNZ SWAP INC SWAP ENDIF ENDIF ;detect eof
     OVER IFNZ DROP ELSE MSBOUT ENDIF ;if not eof write to output
    UNTIL ;end of file
    USBCLOSE ;close output file
   ENDIF
  ENDIF
 ENDIF
ENDIF
END

The VCOPY word rejects the op if either name is filename is empty or if
the source and destination are the same. This detection isn't perfect and
doesn't detect case variations, missing "." or other conditions where the
two strings resolve to the same internal filename but better than nothing.
If the two names do match then the overwrite-detect will kick in providing
an opportunity to cancel the operation. Copying a large file using IPL-coded
streams is probably not that practical of an idea, the speed is limited by
how fast HP-IPL/OS can interpret the words (which is much slower than the
speed of even an 8052/8255 SPI implementation). While tempting to provide
a progress indicator, that would make it take even longer to complete.
Also, it would be practically impossible to copy a file to a different
directory using streams, and difficult using raw Vinculum commands as each
path has to be specified as a series of CD commands. A more practical way
to copy large files or across directories is to simply yank the thumbdrive
and plug it into a PC. The main reason I use VCOPY is to copy an ABS file
to "BOOT" to select the file I want to boot using the Auto PTR feature.

Wrappers can be made for USBREAD and USBWRITE which warm-boot HP-IPL/OS
if the read file doesn't exist or if the overwrite of an existing file isn't
confirmed, preventing further words on the same line from running. This makes
the functions more convienient to use at the command line but if the stock
USBxxx words did things like this they'd be useless for writing apps.
Rather, simpler base words permit modifying their behaviors as needed.

OCTAL DEFINE VREAD ;10/25/08
$TRIM $LEN IFZ ;if filename string is empty
 USBREAD "USB read disabled " $PRINT
ELSE 
 "Reading USB file " $PRINT $DUP $PRINT 40 PCHR
 USBREAD USBSTAT IFNZ ;redirect file to MS in, if error
  457 GET RUN  ;get warm-boot vector and run it to warm-boot
 ENDIF CRLF ;if no reboot newline for formatting
ENDIF
END

OCTAL DEFINE VWRITE ;10/25/08(A)
$TRIM $LEN IFZ ;if filename string is empty ask to confirm
 "Abandon write buffer? " $PRINT CHRIN 40 PCHR
 131 SUB IFZ ;if Y pressed
  USBAPPEND " Done " $PRINT ;abandon and acknowledge
 ELSE $DROP ENDIF ;otherwise drop empty string
ELSE ;filename specified
 VOWCHECK IFNZ ;if file exists and overwrite not confirmed
  457 GET RUN  ;then warm-boot HP-IPL/OS
 ELSE ;print message, set writes, if error warm-boot...
  "Writing USB file " $PRINT $DUP $PRINT 40 PCHR
  USBWRITE USBSTAT IFNZ 457 GET RUN ENDIF CRLF
 ENDIF
ENDIF
END

OCTAL DEFINE VCLOSE ;10/25/08(A)
CRLF ;most MS things don't so ? on next line. Some do making this doublespace
; but that's better than DoneClosing... it's a homemade OS with pieces written
; over a period of years so not every MS thing has consistent output, no more
; than the output of all the different command-line utilities on a PC.
"Closing output buffer " $PRINT USBCLOSE ;print confim message and close
END

Then commands like "FILE.ABS" VREAD ABSLOAD can be used to only
do the ABS load if the file actually exists, and a command like
"FILE.TXT" VWRITE MASAVE VCLOSE will only save the AEDIT data if
the file doesn't exist, or if it exists and the overwrite confirmed.

Writing raw Vinculum USB file code...

A note about long and short commands: Never count on the VDRIVE2 to be
in one mode or another, while a couple of words (VOWCHECK and UFSIZE below)
to a VECS upon exit it's mainly so code after calling these words doesn't
have to switch back to long commands (a feature that presently isn't used).
However, calls to the interface code such as USBREAD, USBWRITE, USBAPPEND
and USBCLOSE leave the VDRIVE2 set to short/binary commands, and a stream
operation may switch to short/binary if a buffer fill/flush occurs.
So ALWAYS do VECS first if issuing long commands, and execute the code
VSYNC "SCS" $>VCMD 221 >VDR 15 >VDR VCLEAR before giving short commands.
If doing that a lot can put the above in say a VSCS word but right now
it's not done enough to justify its own word.

The code for the VOWCHECK shows a bit of direct command usage, switching
to short commands and using DIR to check if a file exists. Code can also
be written to open, seek, read, write and close files for performing more
advanced tasks. Carefully study the Vinculum manual before attempting,
the responses must be parsed properly to detect errors, and output files
must be closed or lost cluster "damage" to the file system results. With
the 3.66 firmware if a file is opened for write its size must be increased
or lost clusters result, making it difficult to consider apps which
update data in the middle of a file.

To effectively write file-processing apps which use raw commands, the
operations should be encapsulated with words to open read, open write,
copy a specified number of bytes from a file into memory, copy a specified
number of bytes from memory into a file, and close the file (which requires
respecifying the filename so should be saved somewhere by the opens).
Even encapsulated such a package would be difficult to use and prone
to file-system errors, and at the moment can't think of a good reason
for doing such a thing (the stream ops were designed to do the common
stuff in a way that makes the difficulty go away). So while nothing is
prevented, I'm not interested in pursuing such a thing. If file-processing
of USB files is needed then I suggest streaming them to SFS files and
using the SFS.IPL package to do whatever needs doing. This limits files
to 64KB but processing USB files bigger than that from HP-IPL/OS would
be impractical anyway.

One thing that would be useful is a word that pushes the byte size of a
specified USB file, then the MS2F word (which requires specifying how many
bytes to transfer) can be used to copy the file as-is to a SFS file.
This can be done by shelling a DIR command and parsing the output...

OCTAL DEFINE UFSIZE ;10/25/08(A)
;usage: "FILENAME.EXT" UFSIZE - pushes size of a USB file
;string is removed from the stack so $DUP if in a program
;if size is 0 then the specified file is not valid
;(a directory, not found, not specified, empty, too big, etc)
$TRIM $LEN IFZ ;if string is empty
 $DROP #0 ;invalid
ELSE
 VSYNC "SCS" $>VCMD 221 >VDR 15 >VDR VCLEAR ;select short/binary commands
 #1 >VDR 40 >VDR $>VCMD ;send [0x01][space][filename][cr] to VDRIVE2 (pop X)
 &WFVDR DROP ;wait for VDRIVE2 and discard the CR
 #0 ;change to 1 if [space] detected, change to -1 if [cr] detected 1st
 DO ;loop until one or the other...
  <VDR CASE = 40 INC = 15 DEC ENDCASE
 DUP UNTIL
 IF<0 ;if [cr] detected then invalid..
  #0 ;flag invalid and exit
 ELSE ;size returned, parse size info...
  <VDR <VDR <VDR <VDR ;4 size bytes on stack, top of stack=msb
  <VDR DROP <VDR ;get what should be > prompt
  76 SUB IFNZ ;if not then...
   DROP DROP DROP DROP #0 ;discard size, flag invalid (this shouldn't happen)
  ELSE ;dir command returned a valid size...
   IFNZ ;if msb not 0 then
    DROP DROP DROP #0 ;drop remaining, flag as invalid
   ELSE
    IFNZ ;if next to msb not 0 then
     DROP DROP #0 ;drop remaining, flag as invalid
    ELSE
     ;msb of 16 bit size on top, lsb next on stack
     400 MUL ADD ;convert to a 16 bit byte size
     ;if greater than 64KB-4 bytes then still too big
     ;only 3 possible invalid sizes so check each one...
     DUP CASE
     = 177775 DROP #0
     = 177776 DROP #0
     = 177777 DROP #0
     ENDCASE ;size should be valid or 0 now
    ENDIF
   ENDIF
  ENDIF
 ENDIF
 VECS ;go back to long/ascii commands
ENDIF
END

With a way to get size, a direct USB to SFS file copy word can be made...

OCTAL DEFINE UF2F ;10/25/08
;usage "USBFILE.EXT" "SFSFILENAME" UF2F - copies a USB file to a disk file
;SFS file must not exist, manually delete first to replace
$TRIM $LEN IFZ $DROP $DROP ELSE ;if SFS filename specified...
 $SWAP $TRIM ;put USB filename on top of X stack, trim spaces
 $DUP UFSIZE ;get USB file size
 DUP IFZ ;if file not compatible then
  "Invalid USB file " $PRINT
  DROP $DROP $DROP ;remove 0 size, both filenames, exit
 ELSE ;valid size returned, stream to a disk file...
  USBREAD USBSTAT IFNZ ;open read buffer, if error
   DROP $DROP ;drop size and SFS filename, exit
  ELSE ;do the copy
   MS2F ;not much to it
  ENDIF
 ENDIF
ENDIF
END

The opposite SFS to USB copy is already easy enough...

OCTAL DEFINE F2UF ;10/25/08
;usage: "SFSFILENAME" "USBFILE.EXT" F2UF - copies disk file to a USB file
;uses VWRITE/VCLOSE to confirm/print messages, if not confirmed or
;USB-related error occurs will warm-boot HP-IPL/OS to cancel operation
$TRIM $LEN IFZ $DROP $DROP ELSE ;if USB filename not empty
 $SWAP $TRIM $LEN IFZ $DROP $DROP ELSE ;if SFS filename not empty
  $SWAP  ;put dest back on top of X stack
  VWRITE ;if invalid/not confirmed will warm-boot HP-IPL/OS
  F2MS   ;copy file to MS out
  VCLOSE ;finish the write
 ENDIF
ENDIF
END

This doesn't do much checking other than validating non-empty filenames
are specified, uses the VWRITE wrapper to confirm USB file overwrite and
warm-boot and cancel the operation if an error occurs. Do note that
sometimes making direct SFS<->USB file transfers isn't appropriate due
to differences data formats, for example AEDIT files are space-padded
and usually have a big chunk of zeros at the end and can't be loaded
using a PC text editor, rather "AEDITFILE" LDFILE MASAVE has to be
used to convert them to plain text. SFS binaries are 31KW memory
images offset by 2 (1st word loads into location 2), other than
hex-editing raw memory (taking the offset into account) such a format
wouldn't be much use on a PC, rather they must be converted to ABS
first (SYSALL for the current system or using altutil/fcam words)
which the simulator and other ABS-aware tools can load. However
direct transfer works file for plain text files and data files
which must remain in a particular format.

All of these extra words are collected in the vdosext.ipl file.
Since different users have different needs, and the focus here was
more to show how to use the core words rather than provide "official"
functions, it may be desirable to edit the "extra" package to take
out what's not needed, or add functions that are needed. For casual
use might just want to keep the VOWCHECK, VLOAD, VSAVE, VREAD, VWRITE
and VCLOSE words since these are useful for loading IPL/ABS, saving
the current system, and entering one-line import/export commands with
error-checking to prevent further execution of the line if a filename
isn't specified correctly. Consider VDOS and the extras as examples,
in HP-IPL/OS nothing is written in stone until enough other things
make use of the added features to make altering them not an option.


Saving and restoring a disk image
---------------------------------

The HP/IDE/USB interface code has debug-mode menu options for saving and
restoring IDE disk data to and from a USB file named "DRIVE0.DSK". The HP
minicomputer doesn't need to be running (if it is then normal disk functions
won't be available). To enter debug mode, connect a serial port to the
8052 dev board, make port F bit 0 low (the debug-mode switch) and run the
interface code. The IDE disk must be ready before the menu can appear...

[this is from the "light version", option 3 is another menu in full version]

HP1000 ATA Disk Controller Program

Model: 04VANV700-                        [garbage char]
S/N:         NV2P002BDG00AL
Cylinders: 16383, Heads: 16, Sectors: 63

LBA=0x00000000, (R)ead (W)rite (L)BA (U)p (D)own (H)exdump (I)nterface
(1)Save to USB  (2)Restore from USB  (3)USB command prompt (Q)uit

The LBA shows the current disk location, the R/W/L/U/D/H commands are
from the original example IDE code, and can be used to examine any
location on disk or copy to another disk sector (be very careful!).
The I command runs a menu added to help debug the HP interface.

To save contents of the IDE disk to a USB file named "DRIVE0.DSK"
press 1, which leads to...

Save disk image to DRIVE0.DSK file on USB device. Select...
(P) Save 7906-sized platter  (1-9) Save 1 to 9 SFS volume(s) 

Option P saves 2 SFS 64-file volumes plus some of a 3rd volume (a total
of 150 files) to save everything on a IDE disk XINIT'd to 7906 drive specs.
Typically this means originating from the 7906 disk sim image which has
both 7906 and IDE disk drivers. Options 1 to 9 save from 1 to 9 64-file
SFS volumes. Each SFS volume requires about 12 minutes to transfer, the
first volume contains the boot system and is 4.1 megabytes, additional
volumes add about 4 megabytes to the image file.

Press the key corresponding to the amount of data to save...

Save disk image to DRIVE0.DSK file on USB device. Select...
(P) Save 7906-sized platter  (1-9) Save 1 to 9 SFS volume(s) 2
This may take awhile and must not be interrupted. Continue? (Y/N) Y
Checking USB disk, please wait...
Saving disk image...
*****************************************************************
Operation complete
LBA=0x000040A0, (R)ead (W)rite (L)BA (U)p (D)own (H)exdump (I)nterface
(1)Save to USB  (2)Restore from USB  (3)USB command prompt (Q)uit

This took about 25 minutes @22mhz - if running on a 11mhz board then
the time will be doubled. Unlike MS stream ops which are limited by the
speed of IPL running on the HP, this operation runs mostly flat-out.
Do not power-down, reset or remove the thumbdrive while in progress
or damage to the USB file system may result, requiring a trip to a PC
to recover lost clusters etc.

Once the operation is complete, remove the USB file device, insert it
into a PC and copy the DRIVE0.DSK file somewhere for safe-keeping. All files
created with the VDRIVE2 have the date Dec 19 2004 (unless commands used to
create dated files but the interface code doesn't do that). To compensate
once on the PC I "touch" the file to give it a useful time/date stamp
(at least I do now.. got a dir full of disk images with the same date).

The disk image (or at least the first 150 files) can be accessed by a
7906 build of HP-IPL/OS (assuming the IDE disk has HP-IPL/OS on it)
running under the SimH HP2100 simulator and a suitable config script.

To restore a disk image, insert a USB file device with a DRIVE0.DSK
image file on it and from the menu press 2 which leads to...

Restore disk image from DRIVE0.DSK file on USB device. Continue? (Y/N) 

Transfer times are about the same with saving, only the number of
complete 1KW blocks in the image file are copied to the IDE disk.
Do not interrupt or damage to the IDE file system will result
(split/partial file at point of interruption, dup files, etc)
try again and don't interrupt.

Caution... don't test this on an IDE disk that contains valuable data!
Instead to test save a disk image, swap to a test IDE disk, then restore
the system to the test disk and make sure it still boots and everything
looks good. Data integrity depends on absolutely 100% correct code both
on my part and on the part of the VDAP firmware, previous versions of
which have been so buggy it didn't work at all, and just today I had a
weird glitch when copying a very small file that appears to be related
to the firmware. So do not depend on this working! Use another disk.

Restore disk image from DRIVE0.DSK file on USB device. Continue? (Y/N) Y
Checking USB disk, please wait...
Restoring disk image... Do not interrupt...
*****************************************************************
Operation complete
LBA=0x000040A0, (R)ead (W)rite (L)BA (U)p (D)own (H)exdump (I)nterface
(1)Save to USB  (2)Restore from USB  (3)USB command prompt (Q)uit

After a save or restore LBA should contain the number of 512 byte sectors
transferred, 40A0 hex times 512 decimal = 8,470,528 bytes, the size of a
2-volume image file.


Additional debug-mode features
------------------------------

The "full" version of the HP/IDE/USB interface code contains additional
SPI/USB debugging functions beyond the basic Vinculum prompt in the "light"
version. Here's what it looks like when I start up Paulmon...

               Welcome to PAULMON2 v2.1, by Paul Stoffregen

  See PAULMON2.DOC, PAULMON2.EQU and PAULMON2.HDR for more information.


Program Name                     Location      Type
  List                             1000      External command
  Single-Step                      1400      External command
  Memory Editor (VT100)            1800      External command
  HP1000 Disk Controller           8000      Program
  SPI/USB Functions                9000      Program
  Auto PTR                         E000      Start Up Code
  Fixed Baud Rate                  F000      Start Up Code

PAULMON2 Loc:2000 >  

The R (run) option can be used to run either the main interface code...

PAULMON2 Loc:2000 > Run program

  A - HP1000 Disk Controller
  B - SPI/USB Functions
  C - Fixed Baud Rate
run which program(A-C), or ESC to quit: A

HP1000 ATA Disk Controller Program

Model: 04VANV700-                        ?
S/N:         NV2P002BDG00AL
Cylinders: 16383, Heads: 16, Sectors: 63

LBA=0x00000000, (R)ead (W)rite (L)BA (U)p (D)own (H)exdump (I)nterface
(1)Save to USB  (2)Restore from USB  (3)SPI/USB debug menu (Q)uit

...or can be used to run the SPI/USB debug menu directly...

PAULMON2 Loc:2000 > Run program

  A - HP1000 Disk Controller
  B - SPI/USB Functions
  C - Fixed Baud Rate
run which program(A-C), or ESC to quit: B


1) Write hex byte to SPI    2) Receive hex byte from SPI
3) Send character to SPI    4) Receive character from SPI
5) Run USB DOS console      6) Run USB sync code
7) Call Subroutine(s)       8) Exit (jump to 0)
>  

If running the SPI/USB Functions directly from Paulmon the IDE disk
doesn't have to be powered. If the interface code is set to autostart
then the SPI/USB debug menu must be run from the main debug menu.

Note... these are crude functions, not optimised, for the purpose
of making this thing work. Sometimes they still get used, especially
when bugs crop up. In retrospect the Receive hex and Receive character
functions could have been combined into one function that prints both
but that would mess up the symmetry of the menu so perhaps not.

Be careful using the functions, there is no editing of entries.
Once the last digit of a byte or word address is pressed that's it,
it does it. The only recourse is reset the controller and re-run the
SPI/USB debug menu and try again. Certain erronous entries can put
the VDRIVE2 into modes that require a power cycle to recover from
but that's what this is for, much better to lock it up while testing
than have it lock up from bad app code that gives incorrect sequences.
Be especially careful if manually doing file writes, keep the disk
checker/fixer handy when testing write sequences. ALL write bytes
must be sent (however many specified in the WRF command) before the
VDRIVE2 will do anything else, and don't forget to CLF FILENAME.EXT
when done. With VDAP FW version 3.66 just opening a file then closing
it without writing anything is enough to cause a lost cluster.

Option 1 prompts for a hex byte then sends it to the VDRIVE2
and waits for it to be accepted...
> 1
Enter hex value: 0D

Option 2 reads a byte from the VDRIVE2 and displays as hex. If not valid...
> 2
Hex received = 0D
Not valid

Option 3 sends a character to the VDRIVE2 and waits. Enter may not work
with some terminals, now it's working fine but if problems use option 1
and type 0D to send a CR.

Option 4 receives a character from the VDRIVE2 and prints it,
if not valid says so.

[menu's removed]
> 3
Enter character: E
> 3
Enter character: C
> 3
Enter character: S
> 1
Enter hex value: 0D
> 4
Char received = D
> 4
Char received = :
> 4
Char received = \
> 4
Char received = >
> 4
Char received =
> 4
Char received =
Not valid

Option 5 leads to a "dos" prompt. This is from running after powerup...
> 5

Raw USB console - do NOT use file write commands!!!
Press esc at prompt to exit

Ver 03.66VDAPF On-Line:
D:\>
D:\>
: 

Often to see additional output enter has to be pressed...

:                        [thumbdrive was inserted]
Device Detected P2
:                        [enter pressed[
No Upgrade
D:\>
D:\>
D:\>
:

OK could be smoother but gets the job done...

:dir

ANRSTUFF DIR
WEB32 DIR
WEBSB DIR
FREEHO~1 DIR
DRIVE0.DSK
HPIPLOS DIR
MAZE.IPL
8052IDE DIR
VCOPY.IPL
VDOS8.ABS
VDOS7.IPL
SFSVDOS.ABS
TREK.ABS
VDOS.ABS
REVERSI.ABS
IDESTAT.IPL
VDOS8.IPL
IDEBOOT2.ABS
XDOSMENU.ABS
BOOT
VDOSEXT.IPL
VDOSEXT.ABS
TEST.FIL
TEST2.FIL
SUMMARY.TXT
D:\>
: 

Press [esc] to exit the prompt.

Option 6 runs the sync code, absorbing extra startup text...

[thumbdrive inserted]
> 6

1) Write hex byte to SPI    2) Receive hex byte from SPI
3) Send character to SPI    4) Receive character from SPI
5) Run USB DOS console      6) Run USB sync code
7) Call Subroutine(s)       8) Exit (jump to 0)
> 5

Raw USB console - do NOT use file write commands!!!
Press esc at prompt to exit
D:\>
D:\>
:

When starting the dos prompt the two D:\> prompts are from the ECS and IPA
commands given to select extended commands and ascii numbers. The prompt
from HP-IPL/OS is a bit smoother and unlike this one, allows backspace.

Option 8 jumps to location 0 to rerun Paulmon, or restart the disk interface
if the code is configured to autostart.

Option 7 is for programming, when selected displays...
> 7

Warning!!! This function directly calls subroutines from
entered addresses. There is no editing, if a mistake is made
the only way out is reset. Incorrect usage can cause data loss!
Enter address 0000 to exit this wretched debugging prompt.
Proceed with disaster? (Y/N) 

Unless you have a listing in hand press N and get out of it.
The listing produced from assembly of the 10/19/08 version of the
interface source shows that address 0x967E is a test print sub and
address 0x9691 is a sub that calls readSPIbyte 65536 times.

Proceed with disaster? (Y/N) Y
DPTR = 955D A/B/R0-R7 = 59 00 00 0D 00 08 0D 00 00 00
Enter sub address: 967E
It works!

DPTR = 9691 A/B/R0-R7 = 59 00 00 0D 00 08 0D 00 00 00
Enter sub address: 9691

Calling readSPIbyte 65536 times
Done.                                    [took about 8 seconds]

DPTR = 96D8 A/B/R0-R7 = 08 00 00 00 00 08 0D 00 00 00
Enter sub address: 0000

1) Write hex byte to SPI    2) Receive hex byte from SPI
3) Send character to SPI    4) Receive character from SPI
5) Run USB DOS console      6) Run USB sync code
7) Call Subroutine(s)       8) Exit (jump to 0)
> 

The sub-caller can only call subroutines that end with "RET", registers
are displayed but there is no provision for changing them. The idea is
to add temporary code in the form of a subroutine that exercises code
to test, then use the sub-caller to call the test subroutine.

Note... if there ever is a "final" version of the HP/IDE/USB code,
most likely it won't have the sub-caller. Perhaps instead it can be
expanded into a more capable mini-monitor loaded as its own program.


Using the Auto PTR program
--------------------------

The autoptr.asm source contains a program that permits booting the IDE
disk without having to have a boot rom. Previously I had to send the
"ideboot2.abs" bootstrap program via a PTR emulator, involving swapping
my single serial cable to the emulator to use "HPSEND" then swapping the
cable back to use the console. With Auto PTR, my startup procedure is
to power the HP, switch the disk interface to "auto" mode, power the
disk interface with a thumbdrive containing a "BOOT" file containing
the bootstrap ABS binary which immediately starts blinking indicating
it's ready to send, then I use IBL the usual way except pointing to the
disk interface, press reset on the disk controller to cancel PTR mode,
and run the bootstrap and the system boots.

The exact procedure is...
Insert a thumbdrive into the HP/IDE/USB disk interface containing
  a file named BOOT containing a copy of the ideboot2.abs file,
  make sure "auto" switch is engaged (normally I leave it set)
Power the HP minicomputer and wait for self-test to complete
Power the disk interface, USB adapter's LED starts blinking
Select S register, set to 002300 (23=IDE slot), press Store
Press Preset, IBL, Preset, Run (bits 0-5 light up to indicate loaded)
Reset the disk interface, USB adapter's LED quits blinking
Select the P register, set to 077600, press Store, Preset, Run

Other than resetting the disk interface to turn it back into a disk
interface again, the procedure is similar to a normal boot-up procedure.

To use Auto PTR, the main interface code must not be set to autostart,
and must have an origin of 0x8000 (code at 0x8040), if not the jump in
autoptr.asm must be changed appropriately. If not running on a Rev4/Rev5
development board then the port2base equate must be changed to the base
I/O address of the 2nd 8255, and ensure the memory address specified by
the locat equate is in valid flash rom and does not conflict with the
main interface code or the "autobaud" init program (if used).

Auto PTR reads port F (port C of 2nd 8255) bit 2 to determine auto mode.
If the bit is high (pulled up by a 10K resistor) it clears the "already run"
flag and returns to Paulmon. If the bit is low (pulled down by a switch)
it checks the already-run flag, if set then jumps straight to the interface
code otherwise it sets the already-run flag and checks the USB adapter for
the BOOT file. If BOOT not found jumps to the interface code otherwise sends
the file to the HP. The only way out of the loop is to reset the interface.

If I need to reboot my machine without cycling the power I can disengage the
auto switch, reset the interface, flip the switch to auto and reset again and
the unit goes back into PTR-emulation mode.

Of course if a real IDE bootrom is available then AutoPTR is not
necessary, and either the interface code made auto-starting or a
much simpler auto program used that simply jumps to the interface code
if PF.2 is low, such as this "Auto PF2" program...

; autopf2.asm 10/16/08
; This autostarting Paulmon program checks port F bit 2 and if
; low (such as a switch to ground) jumps to location 8040 to
; run an application program. If port F bit 2 is high (such as
; a 10K pullup resistor) then returns to Paulmon. This permits
; debugging an app designed to be autostarted without configuring
; the app itself to autostart and removing access to the monitor.
;
.org	0xE000
.db     0xA5,0xE5,0xE0,0xA5  ;signiture bytes
.db     253,255,0,0  ;id, 253=startup 35=don't start
.db     0,0,0,0
.db     0,0,0,0
.db     0,0,0,0
.db     0,0,0,0
.db     0,0,0,0  ;user defined
.db     255,255,255,255	  ;length and checksum (255=unused)
.db     "Autostart PF2",0
.org    0xE040            ;executable code begins here
	mov dptr,#0xF903  ;ports D-F control register
	mov a,#0x93       ;D and E input, F 0-3 input 4-7 output
	movx @dptr,a
	mov dptr,#0xF902    ;port F data register
	movx a,@dptr
	anl a,#00000100b    ;mask bit 2
	jz auto_switch_set
	ret
auto_switch_set:
	ljmp 0x8040


About the SPI/VDRIVE2 code
--------------------------

If working on a 8052/8255-based project that needs USB-based storage,
perhaps the VDRIVE2 and some of the code here might be of use, or can
provide ideas for a custom implementation. First though a disclaimer:

The SPI/USB subroutines appear to work, however carry no warrantee.
The author (me) is not responsible for any damages resulting from the
provided code not functioning correctly, and no matter how "perfect"
the code is (which it is not) success of the project depends on the
VDAP firmware functioning properly (but bugs have been noted), and
the ability of the programmer to use the subroutines to send VDAP
commands correctly and successfully to achieve the desired goal.
The software is provided for hobby purposes only!

The firmware in the VDRIVE2 should be updated to the latest version,
I'm using v3.66 which seems to work very well. There was however one
"incident" where I was using the VCOPY function from the VDOS "extra"
words where a small test file had its size correctly updated but the
contents were not written. The problem occured after several hours of use
on a fairly full 1GB thumbdrive, debugging was going on but no errors noted.
In further tests I reformatted the thumbdrive using XP's format option (plain
FAT but that does NOT prevent a PC from recording LFN's), and performed 256
loops of a copy of one file then another file to the same destination file
using VCOPY (using VDEL first to avoid the overwrite prompt) then using VSHOW
to verify contents, using both full and light versions of the interface code
(a total of 1024 copies). Also created 1024 small files with unique content
then displayed them - this uncovered some delay-sensitive display bugs in
my code (fixed) but all files were copied without error. Whatever it was
the system seems quite solid now. Filed under "who knows".

For the purposes of recycling the SPI/USB driver code, it's probably easier
to scarf from the smaller autoptr.asm source. Except for commenting a few
"paranoid" delays the code is identical to that in the hpideusb.asm source.

The code uses one pointer to access both the output and input bits of
the bit-banged SPI port, so the VDRIVE2 must be attached to port C of
the 8255 chip. The base address of the 8255 is specified by the port2base
equate, 0xF900 in the Rev4/Rev5 dev board to access the 2nd 8255. This is
refered to as port "F" in the dev board docs. The code sets ports A/B
(D/E on the dev board) to input mode but this can be altered if other
outputs need to be maintained.

The code assumes the VDRIVE2 is connected as follows...

Pin 1 (ground) ---- Ground
Pin 2 (data out) -- Port C/F bit 3
Pin 3 (power) ----- +5V
Pin 4 (data in) --- Port C/F bit 4
Pin 5 (clock) ----- Port C/F bit 5
Pin 6 (select) ---- Port C/F bit 6

In addition to the port2base equate, the following equates define
port bit usage and configuration...

.equ  spicon,  port2base+3 ;control register for SPI port
.equ  spiport, port2base+2 ;data register for SPI port
.equ  spiconv, 0x93        ;control value to access SPI port
.equ  spidi,   00001000b   ;AND mask for read data bit
.equ  spido1,  00010000b   ;OR mask to set write data bit
.equ  spido0,  11101111b   ;AND mask to clear write data bit
.equ  spiclk1, 00100000b   ;OR mask to set clock bit
.equ  spiclk0, 11011111b   ;AND mask to clear clock bit
.equ  spisel1, 01010000b   ;OR mask to select SPI device (sets data too)
.equ  spisel0, 10101111b   ;AND mask to deselect SPI device (clears data too)

Change spiconv if port A and/or B need to be configured as outputs,
or if the I/O split of port C needs to be reversed. Change the bit
patterns if the port C pinout needs to be different.

The following subroutines implement the core SPI/VDRIVE2 functionality...

initSPI - initializes the SPI port, sets DPTR to point to the SPI port,
          clears R7 and copies it to the port output pins, setting the
          select, write data and clock lines low. Call initSPI initially
          and before using readSPIxxx and writeSPIxxx if anything has
          disturbed dptr or R7. There are a few delay jumps for settling,
          these are probably not needed.

SPIdelay - uses R3 to execute 200 djnz instructions. Used by the USBclear
          subroutine to wait a bit for the VDRIVE2 to say something.

writeSPIbyte - writes the contents of R4 to the SPI port. Returns 0 in
          the A register if the byte was accepted. Assumes R7 is clear
          upon entry and DPTR points to the SPI port, clears R7 before
          returning. R3 and R6 used for temp variables, not restored.
          R4 is restored to the original data value so if sending the
          same byte again R4 does not have to be reloaded.

writeSPIwait - calls writeSPIbyte in a tight loop until the data is accepted.

readSPIbyte - reads the SPI port and returns the byte in R4. Returns 0 in
          the A register if the data is valid, if non-zero the data is stale.
          Assumes R7 is clear upon entry and DPTR points to the SPI port,
          clears R7 before returning. R3 is used for a temp, not restored.

readSPIwait - calls readSPIbyte until valid data is returned.

USBclear - calls initSPI then calls readSPIbyte until invalid data is
          returned, waiting 100 iterations of calling the SPIdelay and
          readSPIbyte subroutines for the data to become valid again.
          This clears the buffer of immediate responses but does not
          clear responses which may come after an internal delay, use
          readSPIwait to wait for a response if one is expected.
          Uses R6 for temp plus A, DPTR, R3, R4 and R7 as used
          by the subs it calls, none restored.

USBsync - the tricky one :-) calls USBclear, sends "E" and "e" commands
          until "E" and "e" responses received (if not clears and tries
          again), then calls USBclear again for good measure. Uses DPTR,
          A, R3, R4, R6, R7 as used by the subs, none restored.

SelectSCSIPH - calls USBsync to select the SPI port and clear any
          pending responses, sends commands to select the short (binary)
          command set with binary numbers then clears the response buffer.
          This can be called before sending commands to the VDRIVE2 to
          ensure it's ready, set for binary commands, and responses will
          correspond to the commands just sent.

Registers R1, R2 and R5 are not affected by these subroutines.

That pretty much explains the low-level code I used to talk to the VDRIVE2,
using the subroutines to successfully make the thing work is a much more
involved subject. The Vinculum firmware manual goes into great detail as to
what bytes to send and what bytes should be expected in response. Except for
a couple of omissions ("CD /" changes to the root, "DIR string" can return
a "Filename Invalid" response so it can be used to validate filenames)
the Vinculum manual is very useful and should be printed out to refer
to while coding.

When opening files for writing (at least with the v3.66 firmware)
make sure something is written that increases the size of the file,
or a "lost cluster" file system error occurs. Eventually these kinds
of errors can consume disk space if not repaired using a utility.
This impacts applications which write data to a file but do not
increase the size of the file, such as a disk emulation, but
I have not verified if the lost clusters accumulate.

See "USB firmware and formatting" for notes concerning PC compatibility,
essentially to avoid problems if using FAT32 don't use the VDAP firmware
to rename files copied from a PC (or even delete them then write new files
in their place), and if possible format the USB file system so that only
a single FAT is used.

When reading files, usually it's necessary to use a "DIR filename"
command to obtain the size of the file so EOF can be determined.
DIR always returns a [cr] first (wait for it), afterwards returns
either an error message or the filename, a space, then the file size.
The file size is returned lsb to msb, remember that data is always
returned lsb to msb (reversed) but always send data msb to lsb.
In short mode it's easier to parse DIR output since if an error
then [cr] will occur before space. There will always be at least
one character before the space. For example...

bytes size3, size2, size1, size0
bytes error1, error2

call SelectSCSIPH to sync, select short/binary, clear buffer
send 0x01 0x20 FILENAME.EXT 0x0D
wait for byte, if not 0x0D then error (optional check)
wait for byte, store in error1
wait for byte, store in error2
DirResponseLoop:
if byte = 0x20 goto GotSize
if byte = 0x0D goto GotError
wait for byte
goto DirResponseLoop
GotError:
parse error1 and error2 to determine error and exit
GotSize
wait for byte, store in size0 (least significant)
wait for byte, store in size1
wait for byte, store in size2
wait for byte, store in size3 (most significant)
wait for byte, if not 0x0D then error (optional check)
wait for byte, if not ">" then error
wait for byte, if not 0x0D then error (or just clear remaining)
exit with size in size0-size3 (if all 0 then it's a directory)

I think that's about right, not exactly like my code but similar.
DIR is probably the hardest command to parse, most of the other
commands (in short mode) return either >[cr] if successful or
XX[cr] if error where XX is the error code. If the range of
possible errors is limited it's often possible to distinguish
sufficiently by examining just one of the error bytes. To avoid
having to resync clear off all response bytes although sometimes
in my code I just took a speed hit and called the clear code
to clear the remaining bytes once I had enough of a response.

One way to parse the response would be to wait for a byte,
store it in error1, wait for another byte, store in error2,
if error1 = ">" then the command was successful otherwise
wait for one more byte (the [cr] from the error code) and parse
error1/2 to determine the cause of failure.

When writing bytes to a file, a response is not sent until all of
the bytes specified in the WRF command have been sent, even if an
error occurs. Don't forget to close the file even if an error occured
to avoid an open file error later (and possible lost clusters).

When reading bytes from a file, all the bytes requested are sent
even if after the end of file (which are padded with a value which
shouldn't be counted on), then the success/error response is sent.
In a situation where filling a buffer with a certain number of bytes,
a Command Failed (CF[cr]) response is generated if some of the bytes
are past the end of the file, in that case it's not really an error.
In my buffered read stream code I store the file size, keep a count
of how many bytes have been read, then when the count = size then
that's the end of the file. Lazy I guess, didn't bother to do 32 bit
math to calculate the last partial buffer fill, just requested the
number of bytes the buffer will hold, not caring if past EOF.


About the buffered stream code
------------------------------

Implementing read and write buffers permits applications to read and
write files without regard to file size, without using complex command
sequences, with less danger of file system damage should the task be
interrupted, and permits both read and write files to be "open" at
the same time. While the app code is running, no files are actually
open, rather the stream code opens and closes the specified file as
needed to fill and empty the stream buffers. Streaming functionality
can be implemented using just six commands...

"open" a read buffer by specifying a filename
read the next byte from the read buffer (if no more then 0 is returned)
"open" a write buffer by specifying a filename
write a byte to the write buffer (if the buffer is "open", else discard)
"close" the write buffer, writing the remaining contents to the file
return the last error to use to detect open success, EOF, etc

Not much to it. The details can vary but this scheme matches the
papertape-like I/O streams used by HP-IPL/OS, old computers, or for
that matter just about any app that merely needs to read and write
files without regard to seeking or other special needs. For simplicity
in my version all writes are appended to the end of an existing file,
to overwrite a file it is simply deleted first. This ensures no problems
with lost clusters, as anything that writes increases the file size, and
also ensures that bad app code won't make a mess of the file system.
This is especially important when providing an interface to be used
by users to specify their own operations.

Most of all, the stream buffers provide a bridge between low-level commands
and high-level file loads and saves. Practically all of the file apps I use
read or write files sequentially, and when random access is needed it's done
by loading the file into memory, processing the data, and writing the data
back to a file once it has been processed. This includes HP-IPL/OS' SFS
operations which appear to randomly access data, but in fact the data is
buffered in memory and is not written back to disk until the file is closed.
One of the few exceptions to this mindset would be devices such as media
players which need to seek back and forth or other applications involving
files too large to fit in memory, but apps like that should be hard-coded
using low-level commands, not a simplistic high-level abstraction like this.
Rather, this is for the common apps that merely need to load and save files
without the complication of having to use low-level commands to do that.

The stream code in the HP/IDE/USB interface code would take a bit of work
to adapt to other apps, it isn't copy/paste-usable like the SPI/VDRIVE2 code.
It is not implemented as subroutines but jumps back to the IDE interface code
as needed and is full of calls to subroutines for sending and receiving data
to and from the HP minicomputer. It is also a tangle of spaghetti and has
some minor inefficiencies like needless register copies to make a comparison.
My goal was to save and load USB files from my HP mini, I wasn't thinking in
terms of code re-use at the time. Still, if applying to another 8052-based
project it might be faster to adapt than to start over, depending on what
the application is.

The following is a reformatted copy of descriptions of the buffering code
from the ideusb.html page. It is specific to the HP minicomputer app but
for general use replace the phrase "the HP" with "the application".

----------- begin copied text ------------------------
To open a buffered read file:
 Get the filename up to CR and put it in a read filename string
   (truncate past 12 characters but make sure there's a CR after the string).
 If an empty string was sent (just CR) then exit (abandons the read buffer).
 Call the sync code and select short/binary commands.
 Use the DIR command on the filename to validate the filename,
    this is a bit tricky but if a CR response is received before space
    then it's an error, size follows space.
 If error returned by DIR then invalidate the read filename,
    set usb_error and exit.
 Store numbers returned by DIR after space in read file size (4 bytes).
 If read file size = all 0 then it's a directory,
    invalidate the read filename, set the usb_error value and exit.
 Set the read file position to 0 (4 bytes).
 Call the buffer fill code, if an error occurs then exit.
 Mark the read filename as valid.
 Exit.

The buffer fill subroutine:
 Call the sync code and select short/binary commands.
 Open the read file, if an error occurs invalidate filename,
    set usb_error and return.
 Seek to read file position, if error close the file, invalidate,
    set usb_error and return.
 Read exactly enough bytes to fill the buffer, parse response to account
    for EOF response (don't set usb_error if "command failed" indicating
    EOF but detect other errors).
 Set buffer pointer to the beginning of the read buffer.
 Close the read file, if error occurs invalidate read filename,
    set usb_error and return.
 Return.

To get a byte from a buffered read file:
 If the read filename is empty or not valid then
    send 0 to the HP, set usb_error and exit.
 If read file position = read file size (4 byte compare) then
    send 0 to the HP, invalidate read file, set usb_error and exit.
 Get byte from the read buffer at location read buffer pointer
    and send to the HP.
 Increment read file pointer, increment read buffer pointer.
 If read buffer pointer is past the end of the read buffer then
    call the buffer fill code.
 Exit.

To open a buffered write file:
 Get the filename up to CR and put it in a write filename string
   (truncate past 12 characters but make sure there's a CR after the string).
 If an empty string was sent (just CR) then exit (abandons the write buffer).
 Call the sync code and select short/binary commands.
 Use the DIR command to validate the write filename (see open read comments).
 If "command failed" returned that's OK (just means the file doesn't exist).
 If another error occurs or if an all-0 size returned (directory) then
    invalidate write filename, set usb_error and exit.
 Set write buffer pointer to the beginning of the write buffer.
 Set write buffer count to 0 (needed to know how many final bytes to write).
 Mark the write filename as valid.
 Exit.

To write a byte to a buffered write file:
 If the write filename is empty or not valid then set usb_error and exit.
 Store byte in write buffer at position write buffer pointer.
 Increment write buffer pointer, increment write buffer count.
 If write buffer pointer is past the end of the write buffer
    call the buffer flush code.
 Exit.

The buffer flush subroutine:
 If write buffer count = 0 then just return, nothing to do.
 Call the sync code and select short/binary commands.
 Open the write file, if error invalidate write file,
    set usb_error and return.
 Write buffer count bytes from buffer, if error then
    close write file, invalidate write filename, set usb_error and return.
 Set write buffer pointer to beginning of write buffer,
    set write buffer count to 0.
 Close the write file, if error then invalidate write filename,
    set usb_error.
 Return.

To close a buffered write file:
 Set usb_error to 0.
 If write filename is empty or not valid then exit.
 Call the buffer flush code.
 Invalidate the write filename.
 Exit.

The command to request the usb_error value just sends it back to the HP.
----------- end copied text --------------------------

Here's the actual variables refered to (generically) by the algorithms...

readbufterm    equate equal to the end of the read buffer plus 1
writebufterm   equate equal to the end of the write buffer plus 1
readbuffer     buffer for read stream, size in equate readbufsize
writebuffer    buffer for write stream, size in equate writebufsize
readfilename   13 bytes for read filename followed by [cr]
readfilechk    3 bytes to detect uninitialized/invalid read filename
writefilename  13 bytes for write filename followed by [cr]
writefilechk   3 bytes to detect uninitialized/invalid write filename
readfilesize   4 bytes with size of read file (lsb to msb)
readfileptr    4 bytes with current read pointer (lsb to msb)
readbufptr     2 bytes with buffer address of next read (lsb,msb)
writebufptr    2 bytes with buffer address of next write (lsb,msb)
writebufcnt    2 bytes with #bytes in write buffer (to avoid math)
usb_error      error code for USB operations

To avoid streaming with uninitialized variables, the readfilechk string
must contain "VRF" and the writefilechk string must contain "VWF". The
filenames themselves are also checked to see if they begin with [cr],
setting a buffer to an empty name prevents it from being used. This scheme
isn't perfect but should prevent a stream read or write from being attempted
unless a filename was actually set. Extra protection is provided on the
HP-IPL/OS application end, which doesn't redirect data to a buffer unless
a buffer open has been run, making it practically impossible for data to
reach a buffer unless the buffer variables have been initialized.

Subroutines are used for FillReadBuffer, GetFilenameFromHP and
FlushWriteBuffer (not counting low-level subroutines called by these).
The main operations jumped to by the interface code are dc_openreadfile,
dc_readfilebyte, dc_openwritefile, dc_writefilebyte, dc_closewritefile
and dc_return_usb_error. The write-file-byte and close-write-file code
do not send or receive extra data and exit by jumping to dx_done in the
IDE interface code, the others exit by jumping to hp_disk to fetch the
next command sent to the interface.


Feel free to email if you have any comments, questions, bugs, etc.
------------------------------------------------------
Last mod 11/1/08 by Terry Newton (wtn90125@yahoo.com)

