Various things concerning the HPUSB project.

Initial notes written 11/10/2010 (Nov.10 2010), notes added after that
will be dated. Edits to existing notes in [brackets], except for typos.

----------------------------------------------------------------------------

Testing status as of November 23 2010
=====================================

Works with my 12V "+16B DUP REG" board - inverted with control same as data.
Works with my 5V "microcircuit" board - inverted with flipped command/flag.
Works with my 12V "+8B" punch board - inverted with control same as data.

System boots properly in initial papertape reader mode (loads HPBOOT) on
both the 12V and 5V boards. Must reset adapter after powering on the HP,
at least with my microcircuit board. With my inverted punch board connected
it no longer does this.

HP-IPL/OS with VDOS works, can load binaries, save the system,
open/write/read/close files, list the disk directory, etc.
Note - must use long command delay if using old version of VDOS, also
disable IRQ's. Old VDOS also fails if too many files, upgrade or patch.

HPBASIC with embedded UDOS works, can save programs, load binaries
and access the VDRIVE prompt (which is all UDOS does).

Transparent papertape/disk switching works with both manual selected files
and UDOS, for VDOS must run a "PTRMODE" command that clears the HP out buss
before using papertape software (as intended). Was able to use vintage HP
dev software to assemble/compile/link ASM/FORTRAN/ALGOL programs.

----------------------------------------------------------------------------

Hardware notes
==============

The outputs of the circuit cannot drive the inputs of the circuit.
If that is desired then the pullups on the ULN2003 chip outputs need
to be significantly smaller than the pulldowns on the inputs, say use
3.3K pullups, leave the pulldowns 8.2K. The inputs and outputs were designed
to interface with an HP minicomputer, hopefully with both 12V and 5V cards.
3.3K pullups probably would still work for 12V cards, but will result in
more "backcurrent" into the supply when the HP is on and the adapter is off.

The 8.2K pulldowns on the inputs are sized to load 12V HP outs to about 5V to
minimize current injected into the supply through the MCP23S17 protect diodes.

The I/O supply is isolated from the main supply so that the processor and
in particular the VDRIVE will reset properly if the adapter power needs to
be cycled when the HP is on. Isolation is provided by a silicon rectifier,
a 100uF capacitor, a 1K resistor to discharge the I/O supply to 0 when
everything is off, and a big 5.1V 5 watt zener diode to absorb backcurrent
from a 12V HP card when the HP is turned on. This causes the I/O supply
voltage to vary from about 4.3V to 5.1V depending on the app, that's OK.
A Schottky-type diode would reduce variation but very risky since the
zener has a tolerance of 5% and even worse at low current (a 5.1V ZD
starts conducting a bit at about 4.8V), and cheap 7805 regulators might
be a bit higher than 5.0 volts. If tolerances collide then poof.
The 1 amp rectifier and 5 watt zener also serve to protect the main
circuitry (briefly anyway) in the event of a supply meltdown.

The 220 ohm resistor on the main supply serves to absorb backcurrent from
the control lines to ensure that the supply voltage drops to near 0 when
power-cycled. 470 ohm would probably work here, just making sure. The VDRIVE
will not reset properly if there is more than about 0.8 volts on the supply.

The 150 ohm resistors in series with the enable and SPI lines going to the
I/O section serve two important functions - they limit current flow through
the MCP23S17 input protect diodes as the I/O supply is less than the main
supply, and they limit current flow in the event the PIC is misprogrammed
with the SDI line programmed as an output. My prototype uses 220 ohms here,
no problems at all even though the SPI buss runs at 8mhz, so 150 ohms should
be fine and still limit fault current to 30ma. The 150 ohm resistor between
the VDRIVE data out line and the SDI pin also serves to limit fault current.

The resistors in series with the switch circuits and PIC pins limit
current flow from programming errors and prevent the debounce caps from
discharging a large current into the pins if the supply is suddenly shorted.
Microchip app notes also have a diode from the cap to the supply but that's
probably overkill, the 1K resistors limit the current to a few ma.

Any other oddities? Love that LCD circuit!

----------------------------------------------------------------------------

Firmware notes
==============

Porting...

Basically the code isn't portable except to very similar parts.
I use many chip-specific register and bit names as set/=/if targets.
Memory is assumed to be available at certain locations.
Assumes the compiler will allocate in a particular way.
For the bootloader, flash programming methods are chip-specific, beware.

Coding style...

There is none. I hack it until it works then leave it alone.
There are places in the code where I thought there might have been a
compiler issue, recoded more explicitly (only to find out it wasn't a
compiler bug, just one of mine), then just left it alone once it worked
to move on to implementing other things. Or I coded something very
conservatively, found out later it could be simplified, but if I did
that then I'd have to do another round of testing. Better to leave it be,
catch it later if I happen to think about it when fixing something else.

The main goal for the firmware is to function perfectly. Writing
"proper" code (whatever proper is) is desireable but not the main goal.

----------------------------------------------------------------------------

GCB notes
=========

This archive is rather huge compared to most of my projects, mainly due to
the inclusion of the Great Cow Basic compiler, and specifically due to the
massive number of chips supported by the compiler. If needed, almost 8 megs
of file space (about 18 megs of disk space with 32KB allocation) can be
saved by removing the files under the gcb/chipdata directory except for
the chip(s) that are actually needed. Another 700K or so can be saved by
removing unneeded OS-specific files - for Windows systems remove the
gcbasic (no extension) file and the scripts directory, for Linux systems
remove the gcbasic.exe file. The files are left in place so that this
distribution of GCB will be complete - a 4 meg zip file is not unusual
these days, it is still relatively small compared to other applications.

I left out some of the things included with the official distributions(s)
including the Demos directory (old code and nothing to do with this project),
the Docs directory (old information, superceded by info in the help file),
and other unneeded things. Download the official version(s) for these.

There are a few reasons why the compiler should be included here...

Presently the official version of the compiler will not compile neither
the main control firmware nor the bootloader. The fixes for this version
will likely eventually make it into the official version (the author knows,
in fact Hugh sent me a new version of gcbasic.bas that fixed the rel issue),
however that is not the case right now, and I don't want any pressure on the
author to release quickly just so some stressfull oddball code I wrote will
compile. As far as I can tell the issues don't affect average users.

Including the compiler used to compile the code presented here ensures that
the code will remain compilable even if major changes are made to the official
version. The bootloader is especially sensitive to compiler changes, if a
change increased the size of the compiled code (of v7) by more than 20 words
it runs into the saved jump and would no longer work. Unlikely but could it
happen - more robust hardware serial code, working around chip erratas that
the code already deals with, different wait code output, etc.

My code may inadvertently rely on existing bugs for proper operation.
Not that I know of, but I'm relatively new to GCB programming and might
be using incorrect casts or other things that happen to work for me.

It frees the author to release new improved versions of the compiler
without being concerned about breaking compatibility with my code.
Likewise it frees me from having to test the code for compatibility when
a new version of the compiler is released. This is a semi-large issue in
the world of normal software development, I have several versions of gcc
installed because often code I need to compile won't compile or exibits
unwanted side effects with the latest version. Same with other languages.
One of my favorite languages is QBASIC - I can count on that not changing.

Finally, according to LGPL I have to include library code with prepackaged
hex files anyway to properly credit the authors. As GCB isn't that large,
might as well include the whole compiler rather than isolated bits of it.

Bugs and support...

I'm sure there are bugs in GCB (I found some:-), all bug reports are
welcome so they can be documented if necessary, but that doesn't mean
I'll fix them in this version unless they are directly related to this
variation of GCB or can potentially affect my code. If the bug occurs
with the stock version of GCB, then please (also) report it on the
GCB forum so that it can be delt with there. From time to time I might
incorporate future bugfixes into this version - but only if they have
no impact on my code. I am not that familiar with the GCB source code
and I only use it presently for one specific chip (the PIC18F2525),
so I am unable to provide support for things I don't do. I'm more
interested in things that affect the infrastructure... stuff that
breaks even with simple code under certain conditions - for those
I'll dive in and do by best.

GCB usage notes... [11/13/10]

To compile a GCB source file using the internal assembler,
use a command file like...

[path/to/]gcbasic -a:gcasm program.gcb

...where program.gcb is the name of the source to compile. If the source
is not in the same directory as gcbasic then specify the path to gcbasic.
For Linux a path is always required, use ./gcbasic if in the gcbasic dir.
For this archive as of 11/13/10, can use gcb/gcbasic [parms] to run.

The internal assembler does not always detect incorrect code generation
resulting from syntax errors or other causes, for serious development is is
better to use the gpasm assembler from the GPUTILS package (version 0.13.7
or greater). GPUTILS is available from: http://gputils.sourceforge.net/ 

To compile using gpasm (under Linux) use a command like like...

[path/]gcbasic -a:"gpasm -i program.asm" program.gcb

...where program.asm has the same base name as program.gcb. The quotes are
necessary to cause the gpasm command to be treated as a single parameter,
I don't know if this works under Windows. The -i parameter is required to
ignore case. Under Linux a warning about the case of the chip include file
is displayed, ignore. For Windows it may be necessary to replace the gpasm
command with the name of a batch file that runs gpasm, certain environment
variables are set to indicate the filename - see gcbacic.chm for information.

If desired the gcbasic.ini file can be enabled and edited to simplify the
command line by specifying the assembler and other options. Under Windows
this can permit dragging source files to a shortcut to gcbasic.exe or
associating GCB source files to the gcbasic.exe binary (haven't tried).
Under Linux I use bash scripts to integrate into Gnome, I determine the name
of the assembler file for gpasm to assemble from the source name using the
head utility (see the scripts directory). The environment variable specifying
the asm file does not get set under Linux (there is no export) so it has to
be determined using other methods.

GCB tips, features, known bugs...

Avoid using HSerReceive immediately after HSerPrint or HSerSend,
include a bit of delay between. This may be a chip hardware issue.

A related effect, wait a bit after changing the clock frequency before
using the serial to let the chip clock stabilize. My firmware apps runs
at 32mhz most of the time (baud spec'd at 2400 baud so it'll really be
9600 baud) but the LCD timing doesn't know about the PLLEN bit, so I
need to frequently pop it back and forth between 8mhz and 32mhz.
Without waits garbage text might get sent to the serial port.

A common error - make sure no variables in the program reuse register
or bit names or variables used in library subs. Same with labels.
If in doubt use names not likely to be used elsewhere. Remember that in
GCB all variables are global - there is no scope, Sub vars are not local.

These doesn't work...
Sub Somesub(in var1, optional out var2)
Sub Somesub(in var1, optional out var2 = 0)
Sub Somesub(in var1, optional var2)

These work...
Sub Somesub(in var1, optional var2 = 0)
Sub Somesub(in var1, optional var2 as byte = 0)

optional and in/out cannot be mixed, and the = default value is required.

Don't do calculations in #define or other constants. Use #script instead.
There is no #else in #ifdef blocks.

Don't forget to use "loop" when looping... do ... while doesn't work.
Must be do ...lines of code... loop while etc. Personally I prefer
if ... then goto label. Says precisely what it does, no confusion.

Variables are allocated (on a PIC18) from 0 and up.
Strings are allocated from just under the SFR's down.
Check the asm output, put direct access memory arrays in between.

Peek is broken, produces code generation/assembly errors.
I use my own peek/poke subs.

Be careful using fsr0l fsr0h etc - other things like prints change it.

Casts are funny... if not right then results will depend on random memory.
The cast should refer to what kind of variable it is. For example if
word1 is a word and temp is a byte, do word1 = [byte]temp to correctly
clear the high byte.

It seems that casts cannot be used to force just a low byte to a
certain value, if that needs to be done maybe try something like...

set tempvar = wordvar_h
wordvar = somebyte
wordvar_h = tempvar

[11/13/10] bit conditions cannot be combined using And and Or (or if
they can I haven't figured out how), only Not can be used to affect the
bit after the Not.

This does not work: If somebit And anotherbit Then ...
Instead code like:  If somebit Then
                     If anotherbit Then ...
For Or, it may be necessary to use Goto around the target code.
Also, don't mix bit and byte comparisons in the same if clause.
I am not sure if not being able to use and/or with bit or mixing bit/byte
comparisons is really a bug or just incorrect usage - the compiler doesn't
complain but the resulting code doesn't work. [end 11/13/10]

Of all listed so far, the only real bug is peek (and look at poke carefully).
The rest is just figuring out how the compiler parses the code.
[11/24/10 - gcb peek/poke should now be fixed - modified stdbasic.h]

Note - these notes are not authoritive, sometimes something appears to
be one thing but really is another, so it is possible I was fooled.

Here is a program that can be used to explore compiler/syntax behavior...

------- begin comptest2a.gcb [minor edits 11/24/10] -----------------
' for checking compiler stuff
#chip 18F2620, 8
#config OSC = INT, BODEN = OFF, WDT = OFF, MCLRE = ON, LVP = OFF
#define USART_BLOCKING true
#define USART_BAUD_RATE 9600
#define STATLED portb.1
#define RAMLOC 0x400

'reduce clock rate after running via bootloader
'Set PLLEN off  'that's a bug... bootloader should do that!
'fixed the bootloader

Dim string1 as string
Dim string2 as string
Dim word1 as word
Dim word2 as word

'no inverting serial interface, just series resistors...
Set RXDTP 1
Set TXCKP 1

'check timing
Dir STATLED out
Set STATLED on
Wait 1 s
Set STATLED off
Wait 1 s
Set STATLED on
Wait 1 s
Set STATLED off

HSerPrint "Test Stuff" : docrlf

'-----------------------------------
temp = 50
TestOptParm 111
HSerPrint temp : docrlf
TestOptParm 222, temp
HSerPrint temp : docrlf
HSerPrint "Should be 111, 50, 222, 255" : docrlf

'string1 = "blah"
'string2 = "blah"

string1(0) = 1
string1(1) = 65
string1(2) = 66

string2(0) = 1
string2(1) = 65
string2(2) = 65

If string1 <> string2 Then
 HSerPrint string1
 HSerPrint " is not the same as "
 HSerPrint string2 : docrlf
End If

If string1 = string2 Then
 HSerPrint string1
 HSerPrint " is the same as "
 HSerPrint string2 : docrlf
End If

word1 = [word]777
word2 = [word]777

If word1 = word2 Then
 HSerPrint word1
 HSerPrint " = "
 HSerPrint word2 : docrlf
End If

If word1 < word2 Then
 HSerPrint word1
 HSerPrint " < "
 HSerPrint word2 : docrlf
End If

If word1 <= word2 Then
 HSerPrint word1
 HSerPrint " <= "
 HSerPrint word2 : docrlf
End If

If word1 > word2 Then
 HSerPrint word1
 HSerPrint " > "
 HSerPrint word2 : docrlf
End If

If word1 >= word2 Then
 HSerPrint word1
 HSerPrint " >= "
 HSerPrint word2 : docrlf
End If

If word1 <> word2 Then
 HSerPrint word1
 HSerPrint " <> "
 HSerPrint word2 : docrlf
End If

'cast checks
word1 = [word]777
word1 = 1
HSerPrint word1 : docrlf
word1 = [word]777
word1 = [word]1
HSerPrint word1 : docrlf
temp = 1
word1 = [word]777
word1 = temp
HSerPrint word1 : docrlf
temp = 1
word1 = [word]777
word1 = [byte]temp                 'works
HSerPrint word1 : docrlf
temp = 1
word1 = [word]777
word1 = [word]temp                 '??? in high byte
HSerPrint word1 : docrlf

HSerPrint "peek/poke" : docrlf
Poke [word]RAMLOC, 55
'MyPoke [word]RAMLOC, 55
word1 = [word]RAMLOC
'temp = peek(word1)
MyPeek word1, temp
HSerPrint "ram loc "
HSerPrint word1
HSerPrint " = "
HSerPrint temp : docrlf
word1 = word1 + [word]1
Poke word1, 77
'MyPoke word1, 77
'temp = peek(word1)
MyPeek word1, temp
temp1 = fsr0h
temp2 = fsr0l
HSerPrint "ram loc "
HSerPrint word1
HSerPrint " = "
HSerPrint temp : docrlf
HSerPrint temp1 : docrlf
HSerPrint temp2 : docrlf

HSerPrint "low/high bytes" : docrlf

word1_h = 1
'try to set word1 low byte without changing high byte...
set temp = 1
set temp1 = word1_h
word1 = temp
word1_h = temp1
HSerPrint word1 : docrlf
HSerPrint "Should be 257" : docrlf

'-----------------------------------

'uncomment the following lines if not using gcboot
'hang:
'goto hang

'comment the following lines if not using gcboot
HSerPrint "Press keys to exit "
Wait 20 ms
HSerReceive nothing
Wait 20 ms
HSerReceive nothing
Wait 20 ms
HSerReceive nothing
asm  goto 0xB000

Sub TestOptParm(in mysubv1, optional mysubv2 = 0)
Wait 100 ms
HSerPrint "mysubv1 = "
HSerPrint mysubv1 : docrlf
Wait 100 ms
mysubv2 = 255
End Sub

Sub docrlf
Wait 10 ms : HSerSend 13 : HSerSend 10 : Wait 10 ms
End Sub

Sub MyPeek(subramaddress as word, subramdata as byte)
fsr0l = subramaddress
fsr0h = subramaddress_h
subramdata = indf0
End Sub

Sub MyPoke(subramaddress as word, subramdata as byte)
fsr0l = subramaddress
fsr0h = subramaddress_h
indf0 = subramdata
End Sub
------- end comptest2.gcb -------------------------------------------

Output of this program...

Test Stuff
mysubv1 = 111
50
mysubv1 = 222
255
Should be 111, 50, 222, 255
A is the same as A
777 = 777
777 <= 777
777 >= 777
1
1
1
1
49665  [this varies depending on ram contents]
peek/poke
ram loc 1024 = 55
ram loc 1025 = 77
4
1
low/high bytes
257
Should be 257
Press keys to exit 

----------------------------------------------------------------------------

