HP-IPL/OS Distro Version 1.53 (9/1/08) ====================================== This archive contains the HP-IPL/OS 1.5 kernel, assembly and ipl source code, binary builds with various options, and build tools. Directories... abs contains binaries for loading via papertape ide contains IDE-specific binaries and interface details asm contains assembly source for the kernel and other parts ipl contains source files for loading into HP-IPL/OS ptremu contains details for a pass-through PTR emulator scripts contains scripts for building and running HP-IPL/OS The main documentation is in the summary.txt file, covers most of the words available in the various packages (at least the more common ones). Details about the provided binaries and creating custom disk builds are in the buildinfo.txt file in the abs directory. The hpos_disk.txt file is outdated but covers the development of XDOS and how the disk drivers were written, still useful information. The ipl_notes.txt file contains a list of the included IPL files with one-line descriptions, notes concerning dependencies, and IPL listings which are not in their own files. See changes.txt for specific changes. The following sections outline some of the abilities of HP-IPL/OS but do not go into detail on the exact code. Refer to the summary.txt file for the exact calling procedures of the available words, and also read the IPL files themselves which often go into greater detail. About HP-IPL/OS --------------- HP-IPL/OS is an interpreted programming language and operating system for HP 21xx minicomputers ranging from a bare 8KW 2114 with only papertape to a 192KW+ HP2113 with a 7900, 7906 or homebrew IDE disk, BACI serial, HPIB instrumentation, 7970 magtape drive and for kicks and grins an oscilloscope or a HP2645 graphics terminal for graphics. HP-IPL/OS was invented so that these interesting machines can be programmed to do something, especially when it involves homemade hardware (or software) not supported by vintage HP operating systems. HP-IPL/OS can be used to load other software (including vintage programs) on a 64KW+ machine, before running another application in main memory, HP-IPL/OS is copied to alternate memory so it can be instantly recalled when done using the application, after which whatever was in main memory now is in alternate memory where it can be saved, modified, examined, or have additional code added to it and swapped back to main memory for further processing. This permits programs and data such as HP BASIC and a program entered into it to be saved to a disk file or punched to an "ABS" binary file (the default papertape load mechanism for HP 21xx computers). If something can exist and run in 31KW memory usually HP-IPL/OS can save and reload it. If nothing else is available, a utility can be used to encode whatever's in alternate memory as text and spit it out over the console where a terminal program can capture it to a PC file, other utilities can convert the encoded version back into an ABS file, or the text-encoded binary can be loaded into alternate memory where it can be swapped to main memory and run. HP-IPL/OS is mainly a programming system, which was used to write the other things HP-IPL/OS does (like alt mem utilities and disk and magtape operating systems). Everything but the kernel is optional, and even pieces of that can be removed or modified by editing and loading a text file. Each package adds "words" (memory-resident HP-IPL/OS programs) which can be used to build other programs. The most commonly used words are those in extra.ipl, create.ipl (the assembler), double.ipl (32 bit math), and float.ipl and floatext.ipl (floating point math), the interfaces to these words (and the kernel) are locked as the rest of the IPL code depends on them. Other aspects with relatively few dependent applications (such as disk operating systems) are more free to evolve as better methods are developed, maintaining interface stability only within the application family and imposing no requirement that the provided systems be used at all. HP-IPL/OS is a stack-based system, providing a system stack for numbers, a mostly hidden return stack, and X, Y and Z stacks for whatever is needed. The system stack is limited to 112 16-bit values, the other stacks can hold up to 128 values. Strings are generally pushed to and consumed from the X stack, string-aware operators can transfer X to and from the Y and Z stacks. Pretty much everything in HP-IPL/OS is either a 16-bit value, a string, or the name of a word. Floating point numbers are usually specified as strings which are converted to double values for processing, then back to strings for display. For speed only the first 4 chars of a word's name plus the length is significant, this saves much memory and permits easy searching of the "dictionary" of words using less code. By default HP-IPL/OS operates in OCTAL mode, DECIMAL and BINARY radix modes are provided for data constants and display but it is recommended the system always be returned to OCTAL since that's how the documented memory locations and values are specified. In addition to storage of values on the stacks, data can also be stored directly to memory using [address] [value] PUT, and retrieved and pushed to the system stack using [address] GET - be very careful when using PUT since there is absolutely nothing to prevent overwriting something important, and the ill effects of an errant PUT might not be immediately obvious. Locations 100 to 177 octal are always available for program use, although locations above 140 may be used by some system system utilities for temporary storage and should not be counted on to remain unchanged. Some areas in high memory aren't used by anything but before using any memory make sure something else isn't already using it. To permit "safer" data storage the VARIABLE command permits defining one or more locations in the dictionary, for example VARIABLE NAME to define a single location, or VARIABLE NAME 5 to define an array of 5 locations. When the variable or array name is used at the prompt or in a word it pushes a memory address which can be used in PUT and GET statements. For data that won't change there's CONSTANT NAME which defines a word that pushes the value itself. For larger amounts of data, HP-IPL/OS provides data "blocks" in 1KW increments, the number of blocks reserved can be changed by entering [number] ALLOCATE, generally set from 2 to 4. Blocks are numbered from 0. Blocks are very useful for storing program data but make sure the words being called don't also use those blocks (they're programs too). In particular disk operations usually use the top block (if 3 blocks allocated that would be block number 2) to buffer disk data, and sometimes use other lower-numbered blocks as well. To use block storage the programmer uses the @BLK word, which pushes the address of the start of block memory, it is up to the programmer to use the returned address wisely. The PNUM word pops and prints the value at the top of the system stack in the currently selected number radix, $PRINT pops and prints the string on the top of the X stack. For debugging DMPS prints all values on the system stack, for more serious debugging there's DEBUG which permits single-stepping through high-level IPL code. A simple math example... calculate in decimal (3+5)*(30-17): DECIMAL 3 5 ADD 30 17 SUB MUL PNUM OCTAL (prints 104) A floating point math example... calculate 1/((1/4.7)+(1/3.3)) 1 FLT 1 FLT "4.7" $>FP FP/ 1 FLT "3.3" $>FP FP/ FP+ FP/ FP>$ $PRINT (prints 1.93875) The DEFINE word define new words from existing words, for example to make a program that takes two strings and computes the above parallel resistance calculation... OCTAL DEFINE $2R #1 FLT #1 FLT $>FP FP/ #1 FLT $>FP FP/ FP+ FP/ FP>$ $PRINT END ...then you can enter "4.7" "3.3" $2R or any other pair of string numbers to calculate the result. Note that #1 is used instead of 1, #1 is a word that pushes 1, saving one memory location (literal pushes take two locations, one that says "push" and the number itself). 1 and 0 occur frequently so #1 and #0 were defined, saving a significant amount of memory. Definitions often begin with OCTAL as a habit to avoid mistakes. The dictionary of word names can be displayed using WORDS, EXPLAIN WORDNAME displays the code for words defined using DEFINE. RENAME WORD NEWNAME renames a word (doesn't matter if already defined code uses them since once entered words are called by address, not by name), FORGET WORDNAME removes a word and all words that follow it from the dictionary. Individual words in the middle of the dictionary cannot be removed but can be hidden from view by using the DELETE WORDNAME command (only "deletes" the name), UNDELETE can be used to restore names to deleted word names. EXPLAIN, RENAME, FORGET and DELETE are examples of "command-prompt only" words which parse the input buffer for parameters appearing after the word name, most other operations require that constant or string parameters appear before the word name so that they can be used by programs that push the required values to the system or X stack. Machine coded words can be created using CREATE, a very trivial example... OCTAL CREATE HLT HLT 55 END ...which halts the machine. Actually quite useful for swapping cables. The CREATE "language" is similar to standard HP assembler with a few exceptions - labels can be up to 6 characters long (and not begin with END), numbers are interpreted according to the selected base (trailing B's are ignored) unless in an OCT or DEC "constant" marker (thus OCTAL CREATE), and multi-word instructions have to be assembled using additional OCT statements to define the additional parameters. A JMP END instruction jumps to the end of the word which is always an indirect jump to the "next" code which terminates the word and fetches the next word to run, or to save a couple clock cycles a word can be terminated using JMP ZNXT,I which is one of many pre-defined labels that can be used when writing code for CREATE, see the create.ipl file's code for the complete list. CREATE words can access labeled code or data in previous CREATE code by using the /K switch. Primitive words in the kernel or made using CREATE cannot be examined, however the WHEREIS WORDNAME command displays the memory range used by a word, and DUMP or OE? (Octapus) used to examine that area of memory. The "word address" (WA, called ENSEC by EXPLAIN) is the location that contains the memory address of the machine code that will be run when the word runs, for high-level DEFINE words this address points to the "enter secondary" code inside the kernel, for primitive words it usually points to the next location containing the actual machine code (CREATE code begins at WA+1). The 4 locations preceding WA are for the word's header - the length of the word's name (0 marks the end of the dictionary), the first 4 characters of the name, and a link to the next word. Some words use tricks to change the link to skip words so that variables and subroutines are hidden from view, these can't be directly examined using EXPLAIN but can still be examined if needed using WHEREIS, DUMP and PDEF. HP-IPL/OS I/O Redirection ------------------------- DEFINE definitions, CREATE creations and immediate commands (such as VARIABLE and PUT to set the variables to default values) can be put into a text file (usually called an IPL file) then redirected into the console, to restore the prompt CONSOLE must be at the end followed by a CRLF line end. These files can be attached to the paper-tape reader (PTR) device and loaded using the prompts if general input is not equal to the default input vector. Whatever is being redirected into the console must return the console to the default input source when done, why IPL files typically end with the CONSOLE word. Finally a pair of vectors is defined for "mass storage" (MS in and MS out), these are set to PTR and PTP when HP-IPL/OS starts or when CONSOLE is used (<>CON is available for resetting just the console vectors without affecting the MS vectors, CONSOLE resets both console and MS to defaults so that at the end of redirected IPL loads MS is reset to the normal papertape default). When MS WORDS <>CON outputs a WORDS listing to whatever MS is defined to be. The STASH and FETCH words use MS to save and load words from block memory, 0 "WORDNAME" STASH sets MS out to block 0 then runs EXPLAIN to list the code adding a CONSOLE at the end, 0 FETCH sets MS in to block 0 then does FILE >MS ;redirect MS to file, redirect console to MS EXPLAIN WordOne EXPLAIN WordTwo ;etc "CONSOLE" $PRINT CRLF ;if a library app use "LIBEND" instead CONSOLE ;undirect console and reset MS back to papertape 0 CLOSE 0 RELEASE ;close the file and release the buffer For custom applications which do not require operation functions, SFS can be configured to not require XDOS - uncomment the indicated subroutines in the sfs.ipl source, add in only the XDOS words and dependents needed by the application, or write new functions that do whatever is needed. Configuring a HP-IPL/OS System ------------------------------ All of the supplied ABS binaries can be loaded via papertape and run from location 2, and require that the "TTY" console terminal be in slot 11. Once most builds are booted CONFIG can be used to set the slot assignments of other devices if not in the default slots (see build_info.txt). Some builds include a mechanism which permits the TTY and BACI slots be specified by switch register settings, for these builds the TTY slot can be specified in SR bits 0-5, the BACI slot in bits 6-11, bit 12 set to use BACI as the console, and bit 15 set to disable autostarting things like !TBG (unless the TBG board is in slot 10) then run from location 70 (instead of 2), afterwhich CONFIG can be used to configure the rest of the slot assignments. Once configured 2 RUN to restart the system in the new configuration and use SYSALL to punch a configured build to the paper-tape punch (PTP) device. If PTP isn't available then consider using the SimH HP2100 simulator program to generate a build configured as required. Builds without CONFIG (such as the kernel and the 16K build) require that certain memory locations be changed to define the TTY, PTR and PTP slots, these locations are documented in the summary.txt file. If using HP-IPL/OS only under simulation then use or adapt one of the "run" scripts in the scripts directory, these set up the HP2100 simulator to use the default slot assignments of the provided builds. To use as they sit, install the HP2100 simulator in a "path" directory (or specify the location as part of the command), open a command shell (dos prompt) and change to the location of the scripts, then enter (say): hp2100 run_dms.sim That would run the standard "DMS" build. To load say the MAZE game, press control-E and at the sim prompt enter: attach ptr ../ipl/maze.ipl Then enter c to continue the sim and enter LOAD to load the program. Once loaded enter MAZE to start. When done playing it can be removed by entering FORGET MAZE and typing Y. See the notes about over-stuffing the dictionary, only so many things can be loaded at once, do WORDS frequently to monitor the amount of free space. Be aware that under sim, if you type an invalid file name in the attach command SimH will create an empty file of whatever name was entered - if that happens you'll have to remove the errant file. For this reason it's usually better to pull the desired build and IPL files out of the archive directory structure into a working directory, modifying the startup script as needed to remove/change the paths. This way files to be attached can be in the current directory and the ../ipl/ part doesn't have to be specified (and typo files easily removed). When a build with the desired dictionary contents has been made it can be punched to an ABS file by pressing control-E then at the sim prompt attach ptp file.abs (substitute the desired name), entering c to continue then enter SYSALL to write the new ABS binary. A copy of a .sim script can be edited to load it, or perhaps the binary produced can be booted up on a real HP minicomputer. To set up a 7906 disk simulation, "run" the run_7906_be.sim to create a 7906 disk image with a boot extension on it and load the 7906 build. It prints instructions for what to do next. Once set up, the boot_7906.sim script can be "run" to boot up the disk. Note.. always halt the simulation to the sim> prompt before exiting to ensure buffered disk writes are actually written. Ideally a HP-IPL/OS user would load up the 8KW kernel then load in only the desired IPL files, usually starting with "DEBUG TEST" $PRINT > DEBUG > 123 123 ADD PNUM END ? TEST DEBUG TEST IR:054436 `q.. SP:000620 RP:001001 XP:001200 YP:001400 ZP:001600 BUG>? C-CONTINUE S-STEP R-REGISTERS D-DUMP STACK T-RET.STACK BUG>D BUG>S IR:054440 `q.. SP:000621 RP:001001 XP:001200 YP:001400 ZP:001600 BUG>D 000123 BUG>S IR:054442 ADD SP:000622 RP:001001 XP:001200 YP:001400 ZP:001600 BUG>D 000123 000123 BUG>S IR:054443 PNUM SP:000621 RP:001001 XP:001200 YP:001400 ZP:001600 BUG>D 000246 BUG>S IR:005017 $STR SP:000621 RP:001002 XP:001200 YP:001400 ZP:001600 BUG>S IR:005020 $PRI SP:000620 RP:001002 XP:001205 YP:001400 ZP:001600 BUG>S 000246 IR:005021 `q.. SP:000620 RP:001002 XP:001200 YP:001400 ZP:001600 BUG>S IR:005023 PCHR SP:000621 RP:001002 XP:001200 YP:001400 ZP:001600 BUG>D 000040 BUG>C ? The IR location and word name at the prompt is the word about to execute, dumping the stack shows the items that will be present when the word executes. Constant and string pushes and other headerless code present in some kernel words are encoded by "garbage" characters from memory just preceding the code. The "`q.." sequence is a constant push, string pushes show up as "....". The R option dumps the return stack (RP), there are no options to dump the X/Y/Z stacks, their correctness has to be inferred by the XP/YP/ZP pointers. Under sim halt and use commands e 1200-1217 etc to examine the other stacks, enter c to resume debugging. Hint.. get/write something like oct_tool.bas (in the hpos_util.zip file) for reconstructing strings from double-chars. The D option simply redisplays the IR: word SP: RP: etc without executing. When IR: shifts to a lower address it is executing other IPL words, output is mixed with the debugging output. In the above example it was just about to print the trailing space when C was pressed to continue normal execution. Fixed/Known/Possible Bugs ------------------------- The previous library system (lib2.ipl) had some issues, including matching part of a filename when removing segments and failure if the maximum size was exceeded. The new sfslib.ipl library system should be more robust as it no longer depends on there being a terminating byte at the end of the library file. Overflowing the library now only results in the last segment being incomplete. However there is no warning message, use LDIR to make sure the maximum size of 177774 bytes is not approached, if that value then most likely the last segment is corrupt, use LFORGET to remove it. The new library is compatible with the old format with the terminator byte, but if adding to it the terminator is not removed, leaving an embedded 128 byte. To convert a library to the new format, use SETLIB to specify/open it, copy the last segment to alt mem with L>A, LFORGET it, then use A>L to restore the segment. The dm2.ipl menu had a few issues such as not properly skipping zero leader bytes when importing text from papertape, a misformatted prompt and leaving strings on the stack, should be OK now but it's a complex chunk of code. The updated dm2.ipl makes an effort to keep the keypress prompt neat if invalid keys pressed, but some PC keys still trigger unintended selection such as the cursor keys which include A B C D in the sequences they send. Same "bug" applies to dmenu.ipl... the "fix" is don't do that. Versions of SFS before 0.71 (included in 1.52) could wipe out files if the specified buffer memory did not exist. The new version of SFS includes a test in the DIRECTORY word to make sure the buffer memory actually works before buffering a directory. This should provide adequate protection, hopefully the parity error halt will catch cases of failed memory (if not disabled). Unfortunately for the previous SFS code, parity of 0 is 0 so reads from non-existent memory cause no error at all, just reads back as zero which happens to be a valid empty directory block... ouch. The AEDIT editor supports "graphical" characters on HP terminals (or the QCTerm emulator). The intention is to be able to press control-N to select the graphical character set and press control-O to select regular characters. However, HP terminals don't work in such a linear fashion and interpret the control-N/O keystrokes as positional attribute sets, leading to odd (but predictable) behavior, particularly when cursoring. If using this feature disable graphics before cursoring over text or changing pages (in graphics mode a control-N is sent at every cursor position), and avoid cursoring through graphics. Cursoring forward restores graphical characters so starting at the end of the line and cursoring back then forward will restore, to fully restore the screen flip pages with graphics disabled (control-O control-V control-C). No plans to fix this "bug", once the effect is understood it's easy to work around and I don't want to do anything that slows down the editor's primary purpose - to edit text. Also on the subject of AEDIT on HP-compatible terminals... don't use the cursor arrow keys, use control A/W/S/Z to move the cursor, make sure the screen words are set to HP codes (enter TERMINAL until it says HP), and if using QCTerm make sure it's set to pass control codes to the host. The 1st access of a 7900 returns invalid status, possibly a sim thing, maybe bad code. To work around the 7900 driver does a dummy status call, however this will cause a lockup if the drive isn't ready or not present. The 7900 and 7906 disk driver code does not perform any error-checking, it is up to the calling code to check status to see if an error occured. If a real disk error were to happen the system might hang and not even make it to the part that reads the disk status.. I don't know, simulated disks generally don't throw errors unless given bad parms (except the first 7900 access, no idea why). The present 7900 and 7906 drivers have not been verified to function on real hardware (please let me know if tried). SFS requires that interrupts be enabled at least momentarily after cold-starting HP-IPL/OS or else buffer writes fail. This is evidence that a deferred interrupt can interfere with operations which don't appear to use interrupts in any way, yet the system still clogs up if whatever it is isn't serviced. All major builds enable interrupts to avoid this. Was hoping this would be fixed by 1.5's added CLC 0 but no such luck. Here's a test program... DEFINE SFSTEST $VOL 0 0 DIRECTORY "SFSBUGTEST" 0 CNF "SFSBUGTEST" 0 OPEN >FILE "BUGS!" MS$OUT MSCRLF MSPAPER 0 CLOSE 0 RELEASE END ...with !IRQ renamed to +IRQ (and !100MS disabled), after cold-booting (no interrupts) this writes a 0 byte file. If +IRQ entered then -IRQ entered (on separate lines) then it works fine and creates a 6 byte file. At least that's what happens under SimH HP2100... not verified on hardware. Not a problem unless attempting to make an interrupt-less SFS build. The magtape ops usually rest with control set and flag clear, backwards from "normal" default but doesn't seem to hurt anything under sim. At one time (before adding CLF 6 and other code to keep from stopping the clock) the magtape words worked on a real 7970E magtape drive, they "should" still work but status in the present configuration is unknown. MTSTATUS was changed from LIA slot to LIA slot,C to avoid blocking interrupts, at least after the magtape operation completes. To return to the original code enter: "MTSTATUS" $DEFADR INC 102515 PUT (and put TBG in a slot below the magtape interface, change the 15 if MT not in slots 14/15). The XY display driver functions in the n_rocks.ipl demo require that the display list be at least a certain size due to timing issues, -XYDISPLAY makes it go away but may not fully shut down the board (attempted a fix while fixing another bug but still does not return to 0/0 output). Points need to be plotted double-intensity to show up on my E machine. The XY functions appear to work OK for the included demo but if writing new code might have to work around these or other issues. There may still be problems with using the TBG board in higher-numbered slots, as far as I know it "should" now work in any slot but there might still be unresolved control/flag mixups that might affect interrupts. A similar thing occurs in the stock HP "BBL" papertape bootrom, why "Preset" should be pressed after using it (or under sim, reset after boot ptr). Many things still generate interrupts which are directed to nowhere, an issue to address in a future release... or not - experiments seem to indicate TTY PTR PTP generate interrupts no matter what. Update... this only happens under sim.. still need to do more testing but it appears that it IS possible to write code that generates no interrupts. However, especially now that HP-IPL/OS defaults to NOPs in the int.vector table, requires more execution time. There's also the worry that messing with control or flag bits before doing the LIA might break some interfaces. So... leaving it be. The old ZEROBLOCK word in the kernel redirects MS to block memory but doesn't restore MS afterwards. It's been that way since the earliest days, written to occupy as little memory as possible (and still a good reason to not mess with it). Things that use it already do MS redirections and/or implement saving/not saving MS as needed by the app. If using ZEROBLOCK from the console just be aware that you'll need to do MSPAPER first before punching something to papertape. Which isn't a bad idea anyway. Nothing bad happens if forgotten.. just says MEMORY OVERFLOW (and overwrites further blocks but blocks are for temporary storage, not for persistent data). There may be undiscovered bugs or regressions in rarely used features, these usually get fixed, removed or documented when/if noticed. Leftovers and workarounds ------------------------- Previous MTDUMP word left parm on stack, "fixed" by removing the word. If you really need to dump MT records for debugging use ALTDUMP from altutil.ipl and define a better MTDUMP: (offsets by 100 octal) OCTAL DEFINE MTDUMP ;call by #words MTDUMP MTINIT 100 OVER UDMA MTREAD MTWAIT SDMA 100 SWAP 77 ADD ALTDUMP END To use, seek to the desired file and record (file# MTSEEK FS1R etc) then enter #words MTDMP - do not specify more words than there are in the magtape record or it'll lock up. File headers for non-binary files are 40 octal in length, size of data record is listed by MTDIR. It's not easy to determine the size of binary/system magtape files but if you must, copy out the MTWC word machine code from mtbackup.ipl, see above. To avoid lockups in a multi-drive setup, the following IPL prevents CHDRV'ing to an invalid drive... (no need for this if only one drive) VARIABLE MAXDRV MAXDRV 1 PUT ;indicate maximum drive number OCTAL DEFINE CHDRV ;redefine the CHDRV word DUP 77773 AND ;clear bit 2 (platter) and bit 15 (stupidity) ;note: for 7906 drive use 77767, for IDE drive use 77777 MAXDRV GET SWAP SUB IF<0 "INVALID DRIVE " $PRINT DROP ELSE CHDRV ;valid parameter detected, do original CHDRV