An Operating System for the HP21xx MiniComputer


HP-IPL/OS Files and Related Material

Web links...

Notes, News and Other Stuff...

A mirror of these pages is here.

The 1.53 version is somewhat outdated and is not maintained, it works for what it does but doesn't include new features like erasing the last package loaded (have to forget words manually) and dictionary overflow protection (crashes if too much is loaded). New features, fixes and apps are added to the 1.6x "testing" archive, which although a bit unstable is probably better for normal usage. Or for the hard-core hacker, get the hpiplos1.asm source, assemble and run it, and add packages from the file archives or write your own IPL to build it like you want.

Refer to the HP-IPL/OS testing page for notes about changes and the latest applications.
Sometimes minor changes to the testing archive don't get mentioned here.

1/1/12 - Updated testing to 1.65, code changes are trivial (ABS2F no longer stomps user ZP memory), mostly housekeeping/docs stuff.

7/6/11 - Updated the "testing" archive to 1.64A with a few doc tweaks, updated the asm21tr.pl assembler, added extasmb.sh for reassembling the SIO/BCS stuff, added OREGONPT.txt to the BASIC apps.

6/14/11 - Updated the "testing" archive to 1.64, now includes source code for the vintage tools and reassembled binaries, fixes a few bugs relating to VDOS (UFSIZE in vdosext.ipl sometimes failed to return file size, also added to the patch for existing installs) and running vintage apps (new dms.ipl swapper clears the B register to keep the BCS linker happy, mkbcs.ipl now runs the linker properly, sioutil.ipl can now slot-patch other versions of the SIO driver, fixes to altutil.ipl and dm2.ipl to properly patch HPBASIC for re-entering, previous solution worked but didn't show the READY prompt). Updated the files section to include the latest code, was previously for 1.5x packages but was getting really outdated, it should now be possible to build and configure an up-to-date version of HP-IPL/OS using just the linked text files and a cross assembler such as Asm21.

11/28/10 - Updated the "testing" archive to 1.62, now includes key vintage software including papertape BASIC, the EXTASMB assembler, FORTRAN and ALGOL compilers and the BCS linker, along with a new "development" build and instructions for using the programs to build applications.

11/23/10 - Updated the "testing" archive to 1.61A with a new version of VDOS, UDOS for 8KW machines, files for the new PIC-based VDRIVE interface, and a new vintage.ipl package for VDOS and the new interface that semi-automates compiling and linking programs using vintage tools. The new interface uses mostly the same commands as the 8052-based VDRIVE mods to the IDE interface, but is designed to also function as a papertape emulator with punch capability with switches for manually selecting read and write files. The new vdos.ipl and vdosext.ipl files fix a couple bugs encountered while developing the new adapter, a patch can be applied to existing builds without rebuilding.

7/15/10 - Added a link to the new 1.6x "testing" archive, contents are subject to change however it seems stable enough to use. It's mainly a repackaging of the previous version with a few updated IPL packages, a new kernel that detects dictionary overflow and records the position of the last package that was loaded, and more docs, tools and source code. The updated IPL's include print.ipl, dmenu.ipl, dm2.ipl, edit.ipl, screen.ipl and hpscreen.ipl. The new package includes source code for the included MSU BASIC binary in case the version on the MSU BASIC page changes (the present version outputs rubout characters that disturb some terminals, at some point I'd like to fix that), and source and information for connecting a VDRIVE module to the IDE interface to permit accessing files on a USB thumbdrive (see the IDE USB page for more info). The matching VDOS packages are fairly generic, theoretically they can be adapted to a non-disk version of a VDRIVE interface that supports the same firmware commands. Keeping the older version 1.53 around for now, other than a few bugs in a few optional packages it was quite solid... the dictionary overflow issue wasn't exactly a bug but a documented "feature", didn't really bother me until I found myself ducking and dodging too much about what to do when the dictionary got stuffed or a partial load needed to be removed... so fixed the kernel to reduce those instructions to basically "if a dictionary overflow or other error occurs enter ERASE to remove the failed load".. better.

9/1/08 - Version 1.53 posted, with fixed dm2.ipl and a new sfslib.ipl library system (replaces lib2.ipl, a2l.ipl and ms2l.ipl, removed a few other outdated IPL's), the library file is now plain text without the inconvenient terminator byte of previous versions which caused problems if a library add not complete properly. Fixed a bug in LFORGET (only matched the specified part of specified string, like having a * wildcard after the name... not fixed in the older versions in the text archives). Summary.txt now has more SFS and library info, added a new ipl_notes.txt file describing the included IPL's, orders and dependencies, and a place to put extra IPL code. Has updated IDE builds with the new library system, the other builds didn't change.

8/9/08 - Version 1.52 posted, adding code to SFS to ensure that buffer memory actually exists before attempting to buffer a directory (otherwise the directory can be updated with all zeros... ouch). Added the MSU BASIC specific menu (dm2.ipl) and other code (binpatch.ipl, msupatch.ipl, sham.ipl) from the testing page along with msubasic.abs itself to make the archive self-sufficient, assembly source and docs can be obtained from the MSU BASIC page. After my files went away after trying to use SFS with non-existent memory (fortunately my IDE disk was just a copy of the 7906 sim disk) figured it'd be a good idea to make a file backup utility for spitting out files via the console (in bkfile.ipl), it's crude and very slow but better than nothing (I don't have PTP on my machine to use the more direct "FILE" F2MS).

4/9/08 - Version 1.51 rev J posted, fixing a bug in the editor's LDFILE word and less wiggle when moving the cursor. Demo build updated with the improved version although wasn't affected by the bug.

3/29/08 - Version 1.51 rev G posted with an updated editor and "demo" build. This version was derived from the 1.51G sim version (hence the G) and has faster screen updates, avoids file growth and fixes bugs in the utilities package. No changes to the other 1.51 builds.

2/14/08 - Version 1.51 posted, using the 1.5 kernel that uses 0's (NOP) in the interrupt vector table and adds a CLC 0 to the startup path to make sure every device starts with control bit clear. This update fixes the CREATE ASC crash bug and a couple of utility bugs. The archive now includes IPL's for the base version of an editor I made (included in the "demo" build), more docs for the build/sim scripts and Windows versions of batch files for launching ABS files under the SimH HP2100 simulator, and for using the HPASM and Asm21 cross-assemblers.

12/18/07 - Version 1.4 fixes several mostly minor (and a couple not so minor) issues and adds a printer driver and a build for 16KW machines. The IDE builds are back into the main archive, now with the essential details for building an IDE disk interface. Also rewrote some of the outdated text on this page, in particular the "Running HP-IPL/OS" section.

11/19/07 - Fixed the IDE builds, added a fast library system, v1.31 now. Working on putting together my own IDE interface for my machine.

10/14/07 - The recent 1.1 and 1.2 builds did not work on real hardware, updated to v1.3. Was working on trying to solve long-time problems with the TBG "PTIME" clock freezing after certain I/O operations, restricting it to slot 10, while at the same time dealing with other interrupt-induced issues like some binaries freezing if loaded when the TBG is active... that one seems to be fixed now by making a new PRESET word and calling it before doing binary loads.

1/28/06 - HP-IPL/OS now has floating point! Float.ipl implements basic FP functionality by wrapping existing HP instructions. Floatext.ipl implements double-int and string conversions and includes a square-root function written in IPL using an ancient algorithm, eventually this should be converted to machine code or at least optimized so this should be considered a work in progress. Ecal.ipl is a menu-driven electronic calculator program containing many of the calculations I'm always having to figure out manually. Most of these words require the double.ipl package if not already loaded.

It is easy to express complex formulaes as stack operations.. for example a calculation like 5.1/(4.5+(3.4*7.8)) can be performed in HP-IPL/OS as:

"5.1" $>FP "4.5" $>FP "3.4" $>FP "7.8" $>FP ;push FP numbers
FP* ;calculate 3.4*7.8
FP+ ;add 4.5 to above result
FP/ ;divide 5.1 by above result
FP>$ $PRINT ;print answer .16441, correct to 6 places
FP numbers are stored as two 16-bit elements on the stack, use DSWAP to swap FP numbers, and use OVER OVER to duplicate. A number can be squared by using OVER OVER FP* to multiply it by itself. Things like ^ (which would greatly simplify FSQR since a square root is n^0.5), sine, cosign, tangent, etc can eventually be implemented, there's lots of stuff like that embedded in various HP Basic asmb sources floating around.

8/13/05 - The SFS 0.69 builds contain a bug in the FCOPY and FMOVE utilities, if the destination file exists they produce errors and leave the buffers occupied. Worse, FMOVE deletes the source file even though the copy didn't take place. Both of these bugs are fixed in the new 0.70 sfs.ipl.txt and sfsutils.ipl.txt files [updated in current distro]. Besides fixing the copy/move bugs, the BCOPY word was deemed too essential to be in the utilities package and was moved to the main SFS package, a new word FDUP was added to the utilities for duplicating a file in the current directory, and the utilities were modified to use buffers 1 and 2 leaving buffer 0 alone for the user and the new experimental editor which still needs a lot of work.

7/17/05 - Bits And Pieces...

hpiplos1 assembly and 7906 BE utility in ASMB format
octapus-c.txt · original source and docs, decrypted from the Interex site

This has been an exciting week of playing around with antique software, so exciting had to delete my previous ramblings to stay on topic :) Many thanks to those who rescue tapes, configure and share systems, archive sources and issue licenses so that these historical programs can be preserved.

7/7/05 - Updated the HP photos page with new pictures of Bob's computers and peripherals, including one with many lights blazing (I don't know how many were blinking) and a cool vector graphics display.

[old moldy notes removed]


Around the beginning of 2002 I became interested in the HP2100-series of mini-computers. It started when I ran into a program called SIMH by Bob Supnik that simulated a number of vintage computers, including the PDP-8 which facinated me along with a terse multi-user DOS called TSS/8. I knew Bob Shannon owned HP mini-computers, also simulated by SIMH so I proposed writing a DOS for it too, since at the time about all I could do with it was run HPBASIC. Bob fired back with this idea of his called "HP-IPL/OS", which stands for Hewlett Packard Interpretive Programming Language / Operating System. Its philosophy is summarized in this excerpt from a Jan. 31, 2002 email from Bob...

I've been working on HP-IPL/OS since then, taking rough code and ideas and making them work. Seven months later I still don't have the DOS I originally wanted, but at least I'm closer to being able to implement it. What we do have now is a threaded language/operating system that is programmable both in high-level "IPL" and in HP assembly that can be used to implement a wide range of algorithms and applications for the HP mini-computer.

Running HP-IPL/OS

HP-IPL/OS is set up to use the following default slot assignments...

The TBG board in slot 10
The TTY console board in slot 11
The paper tape reader board in slot 12
The paper tape punch board in slot 13
The 13181 mag-tape boards in slots 14 and 15 (hard-coded)
The dual XY (scope-driver) board in slot 20 (hard-coded)
The HPIB interface board in slot 21
The BACI interface board in slot 22

(hard-coded) - slots are hard-coded in the source code, edit to change.
Unused slots up to the last installed board should contain jumper-boards.

Before running, if TTY is not on 11 then load location 355 octal with the TTY channel number. If using a 32K build you can use CONFIG to change the reader and punch channels, for the 8K and 16K builds poke location 356 with the punch channel and location 357 with the reader channel before running HP-IPL/OS. If booting builds containing TBG support and TBG isn't in slot 10, autostarting words must be disabled before running by putting 177777 in the switch register.

Run from location 2 to start HP-IPL/OS. Some builds permit reconfiguring via the switch register, put the TTY slot in bits 0-5, the BACI slot in bits 6-11, set bit 12 to select a BACI console or set bit 15 to disable autostart words, and run from location 70 (instead of 2).

Booting on real hardware, assuming slot assignments have been resolved...

Get your paper-tape reader emulator ready with the build you wish to boot
Select S register, make 001200 octal, with the 12 being your PTR slot. Store.
Preset. IBL. Preset. Run - the PTR emulator should start sending the file.
After loading, the bottom 6 bits will remain lit on the front panel if the load was good.
If successful, select P, clear display, set bit 1 (location 2), store, preset, run.

You should get the ? prompt. Load in desired options and applications by attaching to the paper-tape reader and entering LOAD at the prompt. <PTR must be used to load "IPL" files into the 8K kernel, the first files loaded when making a build from scratch should probably be extra.ipl (permits selecting the memory configuration and in addition to LOAD provides words used by most of the other IPL files), and an oct??.ipl specific to the memory requirements (in addition to Octapus provides a SYSALL which can be used to save the configured build to the paper-tape punch).

The create.ipl file (and smallcre.ipl for 16KW machines) permits loading options which include assembly source, these packages must be positioned carefully in the load order so that the assembled machine code does not cross a 1KW binary, using the WORDS command to display the current end of dictionary (EOD), try to position such loads so that EOD is right after a page boundary (42000, 44000, etc), depending on the size of the machine-coded portion. If a page boundary is crossed generally a "page error" occurs, use FORGET to remove the 1st word defined by the offending package and load something else before it to push EOD past the boundary. Arranging packages to avoid errors can be tedious at first but easy to predict by keeping track of where EOD is and estimating the size and positioning of assembly code in the IPL file. Often I simply proceed until there's a problem then FORGET and try another order.

Dictionary memory is a limited resource, do not overflow it by loading too many packages at once. There is no protection against overflowing but the situation is easy to avoid by not loading big stuff when the amount of free memory reported by WORDS is low. An overflowed dictionary can be detected after the fact by doing WORDS after the load, if word names appear as [addresses] instead of word names you're in the weeds, immediately do 0 ALLOCATE, WORDS, then FORGET the overflowing words, afterwhich do 2 ALLOCATE to restore the "safety net" provided by block memory. The DMS build has 4 blocks allocated by default, if using that as a base do 3 or 2 ALLOCATE to provide more dictionary space. CREATE and disk applications require at least 2 blocks, some applications require more.

HP-IPL/OS does not waste code protecting a user against things that simply should not be done, and does not block the execution of commands that can corrupt the system in memory. To do so would limit its usefulness, for example PUT can change any memory location which I often use to "edit" constants in words after they've been loaded and is used extensively by IPL packages to modify the setup. Using PUT incorrectly is guaranteed to damage something somewhere. Adding code to protect against dictionary overflows would unnecessarily increase build sizes for no real benefit, and trying to implement a means to support "relocateable" machine code would require so much code it'd be no longer possible to have a system able to implement programming, a disk operating system, utilities and applications all in memory at once. If such abilities are needed consider using tools such as SIO and BCS which permit constructing large applications from relocateable modules written in FORTRAN, ALGOL and assembly. HP-IPL/OS can be used as an operating system on 64KW+ machines for loading arbitrary binary applications up to 31KW from disk or papertape, and provides a means to return to HP-IPL/OS and even save programs written in paper-tape BASIC to disk files or ABS binaries.

System generation using real hardware...

If you're lucky enough to have a 13181/7970 mag-tape setup you can use the MTGEN word to create new boot files. I've captured bootable files using the BACI interface and a Visual Basic program that captures serial data to a file. I was not able to get reliable results by using Hyper-Terminal's capture option, your results may vary. Probably the best way to generate configured builds is use the SYSALL command to punch them to a paper-tape punch (PTP) emulator. These can take the output and store it in an eeprom chip or if so designed transfer the data directly to an ABS binary file on a PC. A couple of eeprom-based PTP emulators exist but I don't have one. Another option is to use CONGEN (in congen.ipl) to dump the build to the console as encoded text which can be captured to a file and converted to an ABS binary using log2abs.ipl running under simulation. 64KW+ MX machines can use the version in conalt.ipl which also provides words for directly loading text-encoded binaries via the console.

Booting and System Generation using the Simulator...

The main HP-IPL/OS archive contains SimH scripts for generating the various provided builds and running them under simulation, and the Simulating HP-IPL/OS page gives instructions for building HP-IPL/OS builds by placing the IPL files onto a simulated magtape. HP-IPL/OS builds can be made from scratch under simulation by running the SimH HP2100 program and issuing commands (or running scripts) to set the machine type and slot configuration, load the kernel and attach files to PTR to load. For example... (assuming the kernel abs and IPL's are in the current directory)

sim> set cpu 21mx
sim> set cpu 256k
sim> set clk dev=10
sim> set tty dev=11
sim> set ptr dev=12
sim> set ptp dev=13
sim> set tty1 7b
sim> load hpiplos1.abs
sim> run 2

? [ctrl-e pressed to halt]
Simulation stopped, P: 02226 (JMP 2225)
sim> attach ptr extra.ipl
sim> c
[enter pressed to show prompt but not necessary]
? <PTR
Loading Configurator
Select configuration...
1) keep existing setup
2) 16K w/ 2 blocks
3) 16K w/ 2 blocks and 2K hi-mem
4) 32K w/ 4 blocks and 4K hi-mem
> 4
Configured to 32K w/himem
Loading $EQUAL
Loading $SLICE
Simulation stopped, P: 02225 (SFS 11)
sim> attach ptr oct70.ipl
sim> c

Loading OE and OE?
Loading SYSALL
Decoding 32K OCTAPUS-E binary (70000-72352)
Simulation stopped, P: 02226 (JMP 2225)
sim> attach ptp base32k.abs
PTP: creating new file
sim> c

Simulation stopped, P: 02225 (SFS 11)
sim> detach ptp
sim> load base32k.abs
sim> run 2

HP-IPL/OS 32K V1.4

Paper Tape Reader Emulator

To get software into my HP2113 I use a little black box that Bob Shannon constructed for me. I will try to obtain plans and code for it so that I can get it on-line. The device contains a PIC (microcontroller) that interfaces between the HP's paper tape reader interface to a serial port on the PC, and uses a very simple handshake method for transfering data: when the HP is ready to receive a byte it sends a "!" character to the PC (byte 33 decimal), waits for the PC to respond then sends the received byte to the paper tape reader interface. Bob uses a custom-written Visual Basic program called "Virtual Tape" to send program files to his PTR emulator (which is different than mine, his includes rom sockets for quick booting), I used to use a home-made VB solution but unfortunately my entry-level VB compiler didn't support the serial port so I had to use a work-around called XMCOMM. Here are the binaries for an earlier version of HpLoader for Windows. Since then I upgraded to Linux which gave me back my serial port so now I use DosEmu/FreeDos to run a simple Dos/QBasic program called HPSEND to transfer files.

Reversed-engineered documentation for the pass-through PTR emulator is on the HP Mini Projects page, along with details for constructing an IDE disk interface.

HP-IPL/OS Concepts

HP-IPL/OS implements an indirect-threaded interpreter engine with five stacks, the System stack which is used for general storage and passing parameters, a Return stack used to hold return addresses and loop points, the X stack for passing string data, and Y and Z stacks to assist with string manipulation and general data storage. In addition to stacks, HP-IPL/OS programs can use any unused area of memory for data storage, or use "blocks" of memory which can be allocated in 1K-word increments. HP-IPL/OS' "instruction" pointer is called the Word Address, or WA. Word as in a word like NOT or XOR to execute, not to be confused with a 16-bit word. The contents of the location addressed by WA is called the Code Address and points to machine code to execute. After executing the machine code normally exits by jumping to a routine called NEXT, which increments WA and interprets the next instruction. If the CA points to a routine called ENSEC, or Enter Secondary, the next WA in the "thread" is saved on the return stack, and CA is pointed to the location following ENSEC. Execution follows the new thread until the CA for RTSEC, or Return from Secondary, is encountered, then the previous thread's WA is popped and execution resumes at the location following the call to the secondary word.

New words can be defined by combining existing words, in this way a complex language can be devised from primitive low-level words. To express literal data, special CA's that push integers and strings are used, these parse the data and advance WA (or the input buffer pointer in the case of console entry) past the data. This is handled automatically, you just enter the number to push it to the S (system) stack, or a string surrounded by quote marks to push it to the X stack.

Simple examples... (PNUM is a definition that pops the S stack and prints the number, ? is the HP-IPL/OS prompt)

? 2 2 ADD 3 SUB PNUM
? 5 5 MUL PNUM
000031 (default is octal numbers, 31 octal = 25 decimal)
? DEFINE HELLO (prompt will change to >)
> "Hello World!" $PRINT END
Hello World!
Hello World!Hello World!Hello World!

Now for a shocker...

@TB1 @TB2 @ANVxx @ENSxx @CLH @LITxxxx @STRxxx @RTSxx @DIC @USR @BLK @END
@DIPxx HEADxxx PDEF EXPLxxx ADDCxxx ADDHxxxxx ADDHxxxxxx FIXLxxxx ADDMxxxx
BACIxxx TTYCxx ABSLxxx MSUSxx MS_Sxxx MS_Rxxxxxx $DEFxxx WHERxxx VERSxxx
!100xx T>>S TIMExxx PTIMx HELLx HELLxx
EOD=026547 FREE=021230

WORDS displays the machine names of all the words in the "dictionary", or collection of words that can be executed or used to define new words. At the end is the HELLO and HELLOS definitions, or at least what HP-IPL/OS calls them. Only the first four characters and the length of a definition's name are significant, in listings the unknown characters are indicated by the xxx markings. By doing it this way, HP-IPL/OS needs only three 16 bit words to store the name of any definition no matter how long its length is, and dictionary search and manipulation code is greatly simplified. Be careful when choosing names, TEMP1 and TEMP2 are both TEMPx to HP-IPL/OS. This does not mean you can't have long readable names, just avoid names of the same length with the same 1st four characters, and keep your source code.

You can list high-level words (defined using existing words) using the console command EXPLAIN DefName. Word names with lengths greater than 4 will be printed using the same 1ST4xxx encoding used by WORDS. Definitions are not stored in text form, rather they are compiled as entered and decompiled when listed by EXPLAIN. You cannot edit dictionary definitions, that would be like trying to edit Pascal's P-code. Rather you should retain your human-readable source code and edit that, then reload it back into the HP-IPL/OS dictionary to effect changes. Multiple copies of definitions are allowed, when defining new definitions if multiple copies are present, the last one will be used. However any definitions using the previous copy will not be changed, it will still contain a call to the original. To effect a global definition change (like fixing bugs in an existing definition already being used) the definition must be fixed at the HPASM assembly source level. Most of the later high-level definitions were written in high-level form, when making changes to these I use the MKHPASM.IPL application to output HPASM source which I paste over the existing definition (along with any required editing fixes, MKHPASM is a great help but isn't perfect) then reassemble HP-IPL/OS.

Several console commands are available to assist in dictionary maintenence. RENAME DefName NewName does as expected, DELETE DefName renames a definition to " ", not a true delete but avoids confusion when developing. HIDEDUPS DefName performs this function after new definitions with the same name have been entered, all duplicates will be renamed to a single space. With both of these there will still be artifacts (extra spaces) in the WORDS display to let you know the dictionary contains wasted space. If you decide you liked the original better you can UNDELETE to search for and give a name to deleted definitions. You can truly delete definitions from the end of the dictionary using FORGET [DefName]. If no parameter is specified the last definition is removed from the dictionary after confirming. If a name is specified, that and everything after it will be removed. You cannot forget base definitions that exist in the HPASM assembly source, however you can hide them using delete or hidedups. To erase all user-entered definitions (anything not in the HPASM) use the ERASE command.

Auto-Execute Words

Any definition (word) whose name begins with the "!" character will auto-execute whenever HP-IPL/OS restarts for any reason. This is a relatively new feature and still subject to change - presently loading 177777 into SR before running HP-IPL/OS will disable auto-start. While running you can turn auto-execution on and off using +AUTO and -AUTO. If you want autostarting to occur only once in a session no matter how many restarts, simply define something like...


... then regenerate the system using SYSALL or whatever method you employ. When the new system boots, all the words beginning with ! will execute including !NOAUTO which turns auto-execution off. Don't forget to do a +AUTO before generating new builds after doing this. The autostart flag is located at 465 octal, make 0 to disable.

Console vs. DEFINE

The console is the ? prompt that you type commands directly to HP-IPL/OS, DEFINE defines a new thread that you can execute. At the console flow-control words like +DO IFZ etc are totally disabled. Even things like $DEFADR or WHEREIS cannot find them since the start of dictionary pointer is moved past them when at the console. When you use DEFINE to define a new definition the start of dictionary pointer is set to the true beginning, allowing flow control words to be used. At the prompt there is no pointer like WA and no return addresses, only a text buffer pointer and a function called TOKEN that gets the next word from the text buffer. While you can't use conditionals and loops at the prompt, you can define definitions that obtain parameters After the command word, in normal fashion rather than the reverse notation normally forced by threaded languages. At the prompt you can have things like RENAME DEFNAME NEWNAME and WHEREIS DEFNAME and FORGET MYDEF.

RENAME, WHEREIS and FORGET are examples of console-only words that obtain parameters from the text buffer. Console-only words should normally not be used in definitions, if you absolutely insist then they will obtain their parms from the input buffer, you cannot define their normal usage. What you can do though, is redirect MS (mass-storage) output to a memory block (0 OUTBLOCK), write your commands to execute (using MS$OUT and MSCRLF), follow with a CONSOLE[crlf], redirect MS input to that same block (0 INBLOCK), then redirect MS into the console (<MS).

I/O Redirection

"Streams" as applied in the following refer to 8 bit data quantities passed by sending or receiving the A register by calling a machine-coded subroutine vector. You don't have to know that to use streams but that's what's going on behind the scenes. The provided input and output words generally send or receive information from either Standard Input/Output or Mass Storage Input/Output (MS). By poking subroutine addresses into the vectors which are called, the data can be directed to/from any device that a driver can be programmed for. HP-IPL/OS comes with driver code for the TTY 2400 baud interface, paper tape reader and punch (note.... punch code works fine under simulation but Bob tells me it won't work on a real punch board unless modified - I hope those mods don't affect the simulation!) and the BACI serial interface. PTR (reader), PTP (punch), BACI or TTY can be freely assigned to Standard Input, Standard Output, MS Input or MS Output by poking driver sub addresses into those vectors.

Standard Input is read by the console itself and the following...
$IN · accepts user input up to but not including CR and pushes to X. LFs are ignored.
CHRIN · accepts one character from user and pushes to S
and anything using those words to get user input.

Standard Output is written to by the console and...
$PRINT · pops a string from X and prints it
PCHR · pops an ascii code from S and prints it
PWRD · pops a double-character from S and prints it (high byte is the 1st character printed)
CRLF · prints a Carriage Return followed by a Line Feed
PNUM · prints a number in the selected radix (binary, octal or decimal)
...and anything using those words to display user output. Note that the console prompt (CRLF then ?) is not sent to Standard Output but rather to a console vector which typically is set to TTY or BACI.

Mass Storage Input is read by...
MS$IN · inputs a string from MS up to but not including CR and pushes to X. LF's ignored.
MSBIN · inputs one byte from MS and pushes to S
MSWIN · inputs two bytes (or one word) from MS and pushes to S (1st char goes in the high byte)
...and anything using those words to obtain MS input, including ABSLOAD.

Mass Storage Output is written to by...
MS$OUT · pops string from X and outputs to MS
MSCRLF · outputs CRLF pair to MS
MSBOUT · pops byte from S and outputs to MS
MSWOUT · pops word from S and outputs to MS (as two bytes, the high byte 1st)
...and anything using those words, including ABSOUT and things that use it like SYSALL.

The following built-in definitions redirect one way or another...
<>CON · sets standard input and output to the TTY console
CONSOLE · sets standard input/output to TTY and sets MS input/output to PTR/PTP
<PTR · sets standard input to PTR, this is usually the easiest way to load HP-IPL/OS code
>PTR · sets standard output to PTP, useful for directing a program's output to a file under simulation
<BACI · sets standard input to the BACI interface
>BACI · sets standard output to the BACI interface
MSPAPER · sets MS input and output to PTR and PTP
MSBACI · sets MS input and output to the BACI interface
INBLOCK · pops stack and sets MS input to that memory block
OUTBLOCK · pops stack and sets MS output to that memory block
MS_SAVE · saves MS input and output vectors
MS_RESTORE · restores MS vectors saved by MS_SAVE
MSUSER · sets MS input and output to the user memory map using the top of stack as a pointer

Note that < > etc have no significance (not parsed) other than being part of the word's name. < implies directing into (HP-IPL/OS) and > implies directing out of. These symbols are used in some word names to imply flow from one thing to another, such as S>Z to transfer an item from the S stack to the Z stack, or X>>Y to transfer a string on X to Y. Other words like IF<0 and the CASE tags use < and > to indicate less than and greater than.

Presently, HP-IPL/OS only has what I call "raw" MS drivers that always work on byte quantities. Why bytes? Because that is the smallest unit of transfer and the only way to guarantee no state-switching problems when redirecting is if the system also, at the lowest level also transfers one byte at a time. Each raw MS driver subroutine must maintain its own internal state when dealing with 16 bit or blocked data to assure nothing bad happens when going back and forth between things. The user should not have to worry about this at all, and you don't have to unless you're actually writing driver code.

Writing HP-IPL/OS Programs

This is a (rough) collection of syntax and usage notes grouped by function, refer to the summary.txt file for a list of all of the stock words and (terse) descriptions of what they do.

Unless otherwise noted, all numbers are expressed in octal and the stack is the system stack. TOS means "top of stack", or the last value pushed.

Manipulating Stacks and Memory...

100 777 PUT writes 777 into memory location 100
100 GET pushes the contents of location 100 to the stack
S>X pops a value from the stack and pushes to the X stack, or transfers from S to X
X>S transfers a value from X to S
S>Y Y>S S>Z and Z>S do similar transfers to and from the Y and Z stacks
X>>Y transfers a string from the X stack to the Y stack (using the Z stack as a buffer)
Y>>X X>>Z and Z>>X work similarly
DUP duplicates the top stack entry by pushing it again, 2 DUP yields 2 2 on the stack
SWAP swaps the two top entries, 2 5 SWAP yields 5 2 on the stack
OVER duplicates the next-to-top stack entry, 2 3 OVER yields 2 3 2 on the stack
ROT rotates the two stack items over the top one, 1 2 3 ROT yields 2 1 3 on the stack
DROP removes a stack entry without doing anything else
10 ALLOCATE allocates 10 (8 decimal) 1K-word memory blocks
0 100 777 BPUT writes 777 to block 0 offset 100
0 100 BGET pushes the contents of block 0 offset 100
@BLK pushes the address of a variable containing the actual start of memory blocks
therefore @BLK GET pushes the actual start of memory blocks

Variables and Constants...

VARIABLE and CONSTANT are console commands, they cannot be used inside a definition. Of course the variables themselves can be used inside definitions or there wouldn't be much of a point in having them. You can hide variables and subroutine definitions between ~ and ~~ markers using MARKCON, GLOBAL and SETCON if you want to clean up the WORDS display and prevent other definitions (besides the one using them) from being able to see the names. Variables push the address of the variable's memory location when run, constants push the actual number.

VARIABLE ABC creates a variable named ABC
VARIABLE ABC 100 creates a variable named ABC with room for 100 entries
ABC 555 PUT puts 555 into the ABC variable
then ABC GET pushes the contents of ABC to the stack
CONSTANT TCON 123 creates a constant named TCON with a value of 123
then TCON pushes 123 to the stack.


2 2 ADD pushes 4 to the stack
5 2 SUB pushes 3 to the stack
2 3 MUL pushes 6 to the stack
6 2 DIV pushes 3 to the stack
INC increments the item at the top of the stack (TOS), DEC decrements it
ASL shifts TOS left by one bit, filling bit 0 with a zero bit
ASR shifts TOS right by one bit, filling bit 15 with a zero bit
ROL and ROR rotate the TOS left or right by one bit
NOT inverts the TOS, 1 bits become 0 and vice-versa
2CPL does a 2's complement on the TOS, inverting the sign
AND pops two stack items, does a bit-wise AND function and pushes the results
OR and XOR do bit-wise OR and XOR functions

The stack makes the parenthesis of algebraic languages unnecessary but with only integers you probably won't be doing much algebra anyway... but to illustrate using an integer example, something like (3+5) * (2+6) can be computed by...

3 5 ADD 2 6 ADD MUL

Flow Control...

IFZ, IFNZ and IF<0 pop the stack and branch accordingly.

IFZ code that runs if TOS was 0 ELSE code that runs if TOS wasn't 0 ENDIF
IFNZ code that runs if TOS was not 0 ELSE code that runs if TOS was 0 ENDIF
IF<0 code that runs if bit 15 of TOS is set (negative) ELSE etc ENDIF

IF varients can be nested as deep as you want but there must be a matching ENDIF for every IF. ELSE and the "not-true" code are optional.

CASE pops the stack and branches according to comparisons with constants, variables or a literal number. The general form is...

optional structures [inside brackets]
number to examine is on the stack, popped by CASE
condition is one of the following: = <> < > <= >=
comparison is a literal number, a variable or a constant

condition comparison code to run
[condition comparison more code to run [...]]
[DEFAULT code that runs if no condition is true]

Example CASE usage...

= CN_A "Constant A detected" $PRINT
= CN_B "Constant B detected" $PRINT
= 0 "Zero detected" $PRINT
DEFAULT "No constants detected" $PRINT
123 TST
Constant A detected
456 TST
Constant B detected
Zero detected
555 TST
No constants detected.

If more than one condition is true only the first occurence is acted upon.

String Manipulation...

"Hello" " World" $CAT yields "Hello World" on the X stack
"Hello" $DUP yields "Hello" "Hello" on the X stack
"Hello" "World" $SWAP yields, you guessed it, "World" "Hello" on X
"Hello" $HEAD yields 110 (octal) on the system stack (ascii "H") and "ello" on X
"Hello" $TAIL yields 157 on the system stack and "Hell" on X
"Hello" 41 $APPEND yields "Hello!" on X (41 is octal ascii "!")
"Hello" $LEN pushes 5 to the system stack (string is unaffected)
"Hello!" 5 $GET pushes 41 to the system stack (string unaffected, positions begin at 0)
"Hello!" 5 56 $PUT yields "Hello." on X (56 is octal ascii for ".")
"Hello" $ADR pushes the address of the "He" part to the system stack
$XTEST verifies that the address at the TOS lies within the top string, else STRING ERROR
"123" $VAL yields 123 on the system stack, string is removed
DECIMAL "123" $VAL OCTAL yields 173 octal on the system stack and returns default to octal
123 $STR yields "123" on X, number is popped

Input and Output...

See the section above about redirection.

"Hello" $PRINT prints Hello to standard output
123 PNUM prints 123 to standard output
41 PCHR prints ! to standard output
52116 PWRD prints TN to standard output
CRLF sends carriage return / line feed to standard output
"Hello" MS$OUT sends Hello to MS output
123 MSBOUT sends the byte 123 to MS output
123456 MSWOUT sends the word 123456 to MS output
$IN accepts a string from standard input, up to but not including return, and pushes to X
CHRIN accepts one character from standard input and pushes to the system stack
MS$IN works about the same way but gets a line from MS input
MSBIN gets one byte from MS and pushes to the system stack
MSWIN gets one word (2 bytes) from MS and pushes to the system stack
from to ABSOUT encodes memory and sends to MS in ABS format
PTZERO sends 20 zeros to MS for terminating ABS files
SYSALL encodes HP-IPL/OS and Octapus to ABS and sends to MS

Environment Control...

OCTAL sets number interpretation and printing (radix) to octal notation
DECIMAL and BINARY to set radix to decimal or binary notation
RADIX pushes the current radix to the stack
WORDS displays the (encoded) names of all words in the dictionary (except for hidden words)
EXPLAIN DefName displays the contents of high level definition DefName
"DefName" $DEFADR PDEF does the same thing but can be done from inside a definition
"DefName" 0 STASH saves the definition in text form to memory block #0
0 FETCH interprets whatever is in block #0, if a stashed definition it is re-entered
FORGET DefName removes DefName and everything after it from the dictionary
just FORGET removes the last definition
ERASE removes all definitions that have been entered, leaving only the stock dictionary
RENAME DefName NewDefName renames a definition
DELETE DefName doesn't really delete, but effectively hides a definition by renaming to space
UNDELETE scans for deleted definitions and if directed prompts for a name to "undelete" it
HIDEDUPS DefName hides all occurances of DefName except for the last one
CONFIG lets you reassign the PTR, PTP slots
DEFINE defines new high-level definitions, END returns control back to HP-IPL/OS
CREATE creates new machine-coded words
DEBUG turns on the thread debugger
OE runs the Octapus-E utility
OE? displays a help screen then runs Octapus-E


This is an assembler which is used to create low-level words for HP-IPL/OS. It is mostly HPASM compatible but differs in several ways...

Up to 6 characters can be used for labels, which cannot begin with "END".
Multiple OCT numbers are not supported.
No ORG - you're creating a word so origin is always right after the new word's header.
Except for ASC and DEC, all literal numbers in instructions are interpreted as octal. Any trailing-B is ignored.
Compound instructions should be correctly generated, HPASM has trouble with some combinations
Only one bit-modifier such as ,I for indirect can be specified. Z/C determined automatically by context so normally you do not need to specify unless you're writing something really weird.
Partially supports extended instructions, you have to manually specify the extra words using DEF/OCT etc.

Anything beginning in the 1st column is a label, instructions must begin in the 2nd column or later. Same as with HPASM. Although CREATE isn't exactly HPASM-compatible it is relatively easy to convert from one to the other if not a straight cut and paste.

Many HP-IPL/OS locations and subroutine vectors are pre-defined...

JSB ZSPSH,I pushes A to the system stack
JSB ZSPOP,I pops the system stack into A
TMP1 TMP2 TMP3 and TMP4 are available for temps
the stack pointers are SP RP XP YP and ZP
JSB ZCOUT,I sends the low byte of A to standard output
JSB ZPTWD,I sends both bytes in A to standard output
JSB ZCRLF,I sends a CRLF to standard output
JSB ZPBFL,I writes a string of double-characters to standard output, #words in A, address in B
JSB ZCHIN,I returns one byte from standard input in A
JSB ZMOUT,I sends a byte in A to MS
JSB ZMINP,I returns a byte from MS in A
JSB ISAVE,I saves processor state and turns off interrupts
JSB IREST,I restores processor state and turns on interrupts
JMP ZNXT,I returns control back to HP-IPL/OS
END when used as an instruction parameter points to the end of the created word, when it gets to it.
END starting in the first column assembles a JMP ZNXT,I and terminates CREATE.

CREATE takes care of creating the header and the code address (DEF *+1 in HPASM) and all that, you only need to enter the instructions you want to run. For example...


...creates a word called SETO that turns on the overflow flag.

CREATE uses memory blocks 0 and 1 by default for storing symbols and fixup points. Although single-pass in that you only need to feed the source in once, it makes another pass to put addresses and equates into locations that refer to labels. Forward references are not a problem so long as you eventually add the refered label and code. If not you'll get error messages during the fix-up phase. Not much code is devoted to idiot-proofing, this thing had to be small enough to include in the standard build. You'll get an error if you use an opcode it doesn't understand but the parser is very stupid, it only looks for [label] opcode [parameter[,modifier]] and makes no attempt to determine if the opcode actually takes parameters. Rather, parameters are always OR'd into the instruction. NOP label,I dutifully produces the low 10 bits of the address with the current and indirect bits set. Put comments at least 10 characters removed from the opcode to avoid having them interpreted as parameters.

9/21/02 modifications - call by CREATE DefName /KEEP (or /K) to keep and add to the previous create's symbols, very useful for defining common subroutines and data which have to be accessable by other definitions. The two blocks used by create is enough for ~200 symbols, if you think your program contains more then allocate 4 or more blocks and call by CREATE DefName /LARGE (or /L).

Last modified January 1 2012
Terry Newton <wtn90125@yahoo.com>