Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Re: Old-school programming techniques you probably don't miss

28 views
Skip to first unread message

Mike Roach

unread,
Jun 20, 2009, 9:55:14 AM6/20/09
to
TIME Magazine Person of the Year for 2006 Peter Flass
<Peter...@Yahoo.com> wrote:

>Mike Roach wrote:
>> I recall using instructions as data on a PDP-8.
>
>This was, AIUI, quite common on older systems, fortunately all before my
>time. 1401 programmers did this also. By the mid 60's memory was more
>plentiful and programming standards at least a little better.

Sure, total memory was plentiful. On the PDP-8 we could indirectly
address 4K and field switching gave us 32K which was enough for anybody,
or even a luxurious 128K on a specialty PDP-8/A. But the 8 could only
directly address 128 words of page 0 and 128 words of the current
page.

In one case I wanted to keep my execution trace program (I saw something
similar, perhaps in DECUS, and decided to write my own) down to 128
words so it could be placed in any single page other than page 0 (and it
also used no page 0 directs) in core. In this case it was good to find
constants in instructions. Unfortunately I could never get the Change
Instruction Field IOT to fit in the 128 word limitation so my trace
program never worked when execution switched to another core field. It
was great for TSS/8, though.
--
There is no time like the pleasant.

cjl

unread,
Jun 21, 2009, 3:49:42 AM6/21/09
to
On Jun 20, 9:55 am, never+m...@panix.com.invalid (Mike Roach) wrote:
> TIME Magazine Person of the Year for 2006 Peter Flass
>

In the olde days, back when the R-L/P?S/8 DECtape handler [and
related] had only been "optimized" [to "bum out" locations] say maybe
only the first fifty iterations or so, there were some "principles"
applied.

The first is known as Elekman's Theorem, and then there is Burness'
Corollary. In essence, these postulate that you can always make the
code smaller. However, the difficulty factors [and this time to
implement] are a function of how much effort you have already put in
towards prior attempts to solve the problem.

The original R-L monitor was rather straight-forward by the eventual P?
S/8 standards. It could do all of the basics we wanted in a handler:

1) Restart/reboot the system restarting at 07600. Good luck if the
AC, L, or DF weren't 0, but otherwise certainly could reboot from a
real power-clear start. Not necessarily compatible with switching
tapes to another system, but you could run a stand-alone boot program
if that was important.

2) Decent search technique; it always started backwards [which is
better than always starting forwards! There have been handler
attempts that had that problem!].

3) Had three arguments: Buffer address [better than passing buffer
address-1], two's complement of how many blocks to transfer [would be
better if not stated negated and a few other things added, but not in
this original; you had to start somewhere.], starting 12-bit block
number of the transfer.

4) If an error, HLT within the handler with the AC set to the DECtape
status register, which showed the error [or you could just look at the
blinkenlights on the TC01/08 panel, same stuff]. Most importantly,
the error condition could be retried; just press continue. Thus, a
write-protect error [probably a good thing; be prudent when you are
running untrustworthy code!] could be corrected by write-enable and
then press continue, and it totally recovered where feasible.

5) Understood that you cannot load code into 07750-07755. The last
two of the group are obvious: They are used by the DECtape
controller. The other 4 are used by various magtape controllers or
the DF/RF family. Thus, avoidance of a severe surprise: If the
machine also has one of those, your system handler doesn't crap out
should you access one of them!

6) Reserved location 07756 as a general parameter location to be
passed from the system to an ultimately running program. Both P?S/8
and OS/8 used this notion to good effect later, but in the original
there was only this value.

Back then there were no option switches, etc. Thus, many programs
resorted to using bits within the word to address their options.
Taking a cue from the original PAL III reading the switch register,
essentially the same bits passed in 07756 corresponded to the pass
controls that would be read from the switch register.

All three related systems used the rest of the last page [07757-07777]
as a parameter list of file particulars or related, and this area
would be filled in by the command processor so that input files could
be passed to the running program [principally, but certainly not
exclusively, PAL III as modified by Richard Lary to accept input from
the contents of the files whose block numbers were passed in this
area.

An interesting feature is that this kind of list is self-terminating:
Coding techniques would invariably look something like

ISZ FILPTR /BUMP TO POINT TO NEXT FILE
TAD I FILPTR /GENERALLY, GET THE NEXT FILE PARAMETER IN THE
LIST
SNA /SKIP IF A LIVE ONE
JMP END /GO AWAY IF AT END OF LIST

Thus, any shorter-than-max list requires a sentinal 0000 to end it.
Or, if the list is maxxed out, the ISZ FILPTR instruction bumps to
0000 and the TAD I FILPTR, instead of getting the contents of 00000,
just is skipped; the AC is 0000 either way. If the hardware didn't
work this way, you would always need a wasted sentinel foreshortening
the maximum allowed.

Because not enough words were bummed out, this is all that could be
implemented. [Historical note: The code was written by hand,
assembled by hand, and toggled in by hand. As each page of the
original system was written, it was toggled in by hand, and a call was
created to call the above code while hand loaded into 07600, to write
out the code where it relatively belonged; the eventual product could
load in a viable complete keyboard monitor and editor with a 2K
buffer. Once that was done, and PAL III was modified, and the "slurp"
loader was defined and implemented, this became the rock-bottom
minimal system that was functional. From that base, "normal" code
preparation methods could be used to write new programmning. To my
knowledge, scraps of the original code actually got typed up, but no
one ever actually had a system that could create itself from source
code assembled on a working copy of itself, etc. Thus, no "bootstrap"
development as it were.

P?S/8 is based in part on many of the ideas in its forerunner, but the
code development was started on a TOPS-10 system using TECO-10, PDP-10
DECtapes, and the use of PAL-10, binary paper tape punched out, and
then brought back to an -8 with only an ASR-33. And of course a
wonderful item, an actual line-printer assembly listing from PAL10.

By applying many more iterations of effort, whole new features were
added [and this also applies in part to OS/8, where, despite otherwise
requiring 8K, no handler benefit of any major kind came about. It
would be years before the 12K kludge was resorted to, with some
disastrous consequences coupled with the repeat failures of producing
barely viable and compromised handlers for the most part; having at
most two pages of code with much reserved simply is not a very good
idea. Especially when the minimum requirement is now 64 pages of
code. P?S/8 demands 9 of them if one of them is not sufficient. Any
additional 32 pages of another field is available to applications and
user programs, not the O/S.]

The main point is that, unless the handler could be squeezed further,
the features literally could not have been implemented. [Note: OS/8
has a small "luxury" in that it can use portions of 17600-17777 to
support some of this, not much, but some. One could argue it really
didn't capitalize on this resource much.]

Thus, after still many, many, many, many more iterations of
optimization, and this also included notions and suggestions from a
whole host of -8 people, the net result is the current definition,
which additionally has one word to spare [it will be used in future
versions!].

The handler now protects itself from bad AC, L contents. The DF is
relevant because it supports cross-memory field calls using the usual
convention: CDF MYFIELD, CIF 00, JMS [to handler]; args; returns
here. The DF is used to located the caller's args and return address
past them. [This is identical in skeletal scope to OS/8; the original
really didn't know anything about extended memory; the original
machine didn't have any!]

A non-obvious problem with the original is there is no place in
07600-07777 for a way to get a program into memory. In essence, what
is needed is an internal set of locations that can call the handler
itself with loading instructions to load in the program over the
keyboard monitor, and then do a JMP I .+1 followd by a starting
address [unless you place silly restrictions such as all programs must
be written so you can start with a JMP 0177 or JMP I [some coincident
piece of code that passes in binary as a good starting address]. I'm
sure that last notion was entertained, but even the original needed a
way to state an arbitrary starting address. [Note: In P?S/8, there
is still a permanent restriction: All system programs must start
somewhere in field 0. User programs have no such restriction; they
are not system program components.]

The R-L monitor had a serious problem with this lack of space. What
was done instead was part one of an ugly kludge. [Note: One of the
features of OS/8 was the point about device-independence. P?S/8 is
also device-independent in that for a proper handler, just change the
handler and the same code works. However, this comment was aimed at
the R-L monitor with its severe hardware dependence, the most device-
DEPENDENT system perhaps ever available for the -8.]

What was done is that the keyboard monitor brought into memory what
was intended to be placed over 07600-07777. The file list was placed
into relative 157-177 of that page, the 12-bit value word placed into
relative 156 [in all three systems, this is in essence any syntax
where you can say something "=xxxx" where xxxx is just about any 12-
bit value]. Certain internal storage locations pick up the intended
loading arguments of the system program and they are stuck where they
are needed; there is no actual subroutine call; the starting address
of the program is placed after a JMP I .+1 instruction to start it
up. Then the whole shebang is moved over 07600-07777.

This code can only do two things:

a) If you restart at 07600, it can boot any DECtape system, not just
R-L, this is a very standard bootstrap, but it ain't part of a
handler.

b) You can start the rest of the code at some internal designated
address, and it accomplished the reading in of the designated
program's image; error handling was fatal, no possiblilty of
recovery. Reboot if a problem and try the whole command again.

Thus, no subroutine call mechanism of any kind, thus no compatibility
with the calls within the main system. No other usage can be made,
because it's just meant to be a one-shot loader of a single-segment
program. No ability to write anything at all. Thus, at this point
you have a program in memory, a list of files to process in
07757-07777, a general = parameter value in 07756 to bit-wise or other
way means of guiding you how to do something. And no means whatsoever
to actually accomplish any of this!

Enter kludge part 2: Each program had to reserve within itself a
captive local DECtape subroutine so it could be used to access the
contents of the files. Error recovery was servicable, and on a par
with the system's handler. The arguments to the subroutine call were
compatible with the main system, although this was a suggestion, not a
rule. [Little point in changing it, but no demands were made.]

Thus, each program had hardly any use for what was in 07600-07777.
Other than processing the file lists [if it even cared], same for the
= value in 7756, if it knew just how much to not wipe-out from 07600
down to somethiing like 07622 to maintain the ability to boot back via
07600, the program was free to destroy the rest of the last page of
memory. Reading required at least one page of memory as a file
buffer, and one page was needed to be the captive handler. The PAL
III mods also needed a one-page write buffer for binary output files
in "slurp" format written a block at a time. The output files were
implicitly at a designated place on the system device. And still no
support for tapes 1 through 7; this was strictly a one-tape system.

It should be obvious all the advantages that can be gained by spending
a few thousand extra attempts to squeeze the code down some more.
[Some could even argue it was worth the effort!] By the time it was
all over, all of the following got done [with one word to spare
according to the latest definition]:

The call to the handler is now:

JMS I (7640)
TRANSFER ADDRESS
FUNCTION WORD
STARTING BLOCK FOR TRANSFER
returns here

The transfer address is the same as in the original; so is the
starting block number. The function word has a close cousin in OS/8.
Here it means:

If bit[0] is set, this is a write [same as OS/8]
bits [1]-[5] page count for the transfer [same as OS/8] all zeroes
means 32 pages
bits [6]-[8] field for the transfer [same as OS/8]
bits [9-11] logical unit number [quite different from OS/8].

Note the use of the address 07640 as the entry point. Easily grabbed
by any caller needing to bum a word out of their code; who doesn't
have a handy dandy SZA CLA instruction in their code, or can easily
contrive one without giving up a word; if you can't, you haven't
tried! [Really, it's easy! If the AC is unknown and has to be
cleared, just place some "safe" other constant after it such as 0xxx
which can be an AND instruction [note: avoid 0410 through 0417; don't
want to accidentally disrupt an auto-index register!] Worst case, it
doesn't skip and executes an AND instruction with an AC already 0000.
And if you can guarantee it's definitely 0000, it will always skip the
next word regardless of purpose, data, a constant, a variable,
whatever.]

Cross-field calling conventions apply: CDF MYFIELD; CIF 00; JMS I
(SZA CLA);address;function;block

Errors halt as before [but if the handler is "extended" all errors get
intercepted ala IOPS4, but you need 8K to do that; this still runs on
4K machines!] and shows relevant error info in the AC, and press
continue to perform a recovery that should succeed if at all possible.

The file lists and =parameter words are the same, except that the file
list format has changed: Each file parameter has a restriction in
that the starting block of all files is always rounded up to a
multiple of 8. Thus, the data & (7770) is the block number, and the
data & (0007) is the logical unit number; files can now be on 8
devices, not merely one. Note that the logical unit bits are already
placed in the correct bits to be placed into the function word in the
call to the handler. [Note: The handler itself can transfer starting
at any block; this is a restriction of the data in the file list
only!]

In 07632 is a reserved skeleton of a call to the handler; it defaults
to:

JMS 7640
0000 /SAVE MEMORY STARTING FROM 0000
400X /WRITE OUT ALL OF FIELD 0 0000-7777.
0020 /starting on block 0020
JMP I .+1 /start up whatever
7600 /reboot the system

Thus, when a program is run, getting to 07632 means to save all of
field 0 in blocks 0020-0057. This functionality is available to user
programs in general, and affects such binary loaded user files and the
system version of ODT; control-C out of ODT jumps to 07632. The
reason the startup is indirect is that later, it has to be indirect to
allow starting anywhere, not just on this page. It's moot because
these are just the default contents to save memory when needed.

Thus, when the system bootstraps, all of memory has already been
saved, if you should care. There is a BSAVE command that takes memory
ranges and a series of output files to save memory locations more
permanently into files; thus, this aspect of things resembles pieces
of DMS and OS/8. System programs are not involved in this process;
you can safely run with write-protect on, unless there is an obvious
further reason to enable writing.

The X mentioned above becomes the system's logical unit number, and is
not necessarily 0. We defined a superset of the DECtape bootstrap
that also can boot P?S/8 from any of the 8 tape units; the X gets
properly set by the time a user program could be in memory. Other
system devices may or may not be as elaborate. [RX01 systems only use
the low-order bit because there is only support for drive 0 and drive
1]. To make this bulletproof, the manual restart at 07600 also obeys
the logical unit bit. This operation is useful when you are
developing another system that wants to be itself written out on tape
0.

When the keyboard monitor is up, it can push parameters into this
skeleton; system programs can be run this way. The only restriction
is that you cannot start a system program other than in field 0, and
the initial segment of a system program must load at least one page
into field 0. The system program is welcome to define private overlay
sections to build upon this however it wishes; the system handler is
available to do any additional loading without restriction [other than
the basic one all O/Ses share: You cannot load over the system
handler in 07600-07777!].

Thus, all of the effort actually WAS worth it! No more kludgy system
loader with its limitations and device-dependence. And no more having
to assemble into each and every program its own one-off captive
DECtape handler [which means a program is now device independent! It
can actually run on other hardware where the system device is a
differently-handled hardware device; the calling conventions must be
adhered to, but nothing else matters.]

Files can be on any of the tape units 0-7, the system programs
themselves also, and even [if the bootstap permits] the O/S itself.
This is truly a good way to use multiple tape units should you have
them. [I have machines with a pair of TU56 drive pairs.]

But we were gluttons for punishment. Thus, in what seems to be
millions and millions of additional iterations [but in reality were
probably not even ten thousand more attempts at squeezing out the
code!], we got even more features to fit:

In the original system, output had to go to implicit scratch blocks
designated for the purpose. After an assembly, you could load and
then save these areas to conventional files; the scratch areas became
named % and $ for the 2K areas defined at blocks 0020-0037 and
0040-0057 respectively. [Note: These block numbers also conform to
the must be on rounded up boundary of 8 restriction same as the file
blocks in the directory.] Thus, this wasn't all that bad.

However, it also meant you couldn't create more than 2 files, which
often isn't enough, especially for large assemblies. [For reference,
slurp binary files are slightly less than 6/7 as efficient as core-
image files; Thus, assembling for an application that could load into
say 00000-07577, this would take likely three files, assuming no
overlays or other overhead, etc. and this doesn't take into account
extended memory considerations, which are allowed. In the original R-
L PAL III, the FIELD setting was essentially ignored; in fact, as a
bug, it punched a character on the console if you attempted to use it,
a left-over from the former paper-tape version that was not properly
handled].

Thus, it became necessary to define output files. Thus, a word was
reserved to be the internal count of how many of the passed file
parameters represent output files, not input files, which are the rest
of any present; the output files are first. Add the passed output
file count to the input file pointer to start parsing the list at the
first input file block, etc.

So, now you can have anywhere from no output and 17 input files to no
input files and 17 output files. [There is a minor restriction of the
slurp binary loader making that restricted to "only" 16 files in
certain instances as a function of a loading option, but that's a
really, really big assembly! Clearly, worst case, you can do two
separate operations if you have to load just about all of 32K! As a
practical matter, this almost never is a consideration even to a
developer of a truly large project. I wrote an application that
required 32K to run, and it was modular and generated about 100K of
code, but each module loaded for generation never exceeded this limit,
etc.]

Using that =parameter was slightly tweaked: The keyboard monitor
limited user setting of it to only 0000-7776. If it is 7777, it means
the command lacked the parameter entirely. Thus, the slurp loader
knows to NOT start the user program unless an explicit address is
given.

However, what about extended memory considerations? And what about
the need to use more than a maximum of 12 bit-switches? Something had
to be added:

We imitated the TOPS10 notion of /0-/9 and /A-Z, which is awfully
convenient for a 36-bit machine to have 36 bit-switch settings. Of
course, on the PDP-8 this means we need three more 12-bit words.

Thus, after seemingly billions and billions [note: Billions and
Billions = 1 Sagan] of further optimizations, we got three more words!

Besides lots of switch options, /0 through /7 could be the means to
express an address in extended memory. A START program was defined to
use /0 through /7 as the starting field [defaulting to 0 if none
given; the use of /0 is allowed albeit redundant] and the = construct
is the starting address; it's an error to not give a starting
address. Binary loading considerations use similar conventions; P?S
PAL allows assemble, cross-reference, bitmap, load, and go in one
command. Giving slurp loader options gets the loader going. the =
parameter and /0 through /7 specify the starting address; loading is
inhibited if there are assembly errors. Just about all of the other
switches are defined to do PAL-specific stuff, such as enabling
literals at all, making off-page references not be errors, but fixed
up for, the precise definition of TEXT statements containing an even
number of characters, enabling of LINC-mode cross-assembler support,
listing output selection and optional listing adornment options [not
everybody has a lineprinter, so basics are possible too!], loading
options to pre-clear memory with 0000 everywhere or pre-HLT memory
with 7402 everywhere, whether to enable or not a BITMAP of the created
binary, etc. Good thing there are 36 switches!

Untold gazillions of attempts later, we also got one more word, which
became the system date word. Although vaguely like OS/8, it has
several advantages over that system:

1) By proper bit-significence order, the larger the number, the more
recent the date. Look at the needless overhead of first performing
bit-twiddling on the OS/8 word, then recombining to get a proper
binary weighting before you can do that.

2) Instead of lasting only 8 years, this one lasts 11-3/4 years.
That gives you almost 4 years of procrastination time to change the
base year to the next base of 8 years. P?S/8 base is 1960 and the
date will run out in 2099. [Perhaps that extra word will become the
base in memory to make this last literally for millenia. But 8 bits
would be enough; perhaps we can find something useful to do with 4
more bits than support that far into the future PDP-8 machines.]

After another bout of iterations of optimization that can only barely
be calculated on near-infinite-precision computers, we got yet another
word:

The CORE word defines 4 things:

1) The actual size of the machine, up to 32K if set to 7 and 4K if
set to 0.

2) The current user-set limit of memory, up to the amount allowed, at
least 0=4K.

3) the maximum amount you can set 2) for; this is NOT the same as 1).

Each of these takes 3 bits. The reason for 3) is because the system
defines a LOGICAL CONSOLE OVERLAY, which if present, is in the field
described by 1). In that case, 3) is one less field, else they are
the same. This way, a program does not have to test for the logical
console overlay to know. Note that 1) and 2) are essentially the same
as in OS/8, although it is better to think of it as 3) and 2)
actually.

Thus, one of the three remaining bits is defined as the LOGICAL
CONSOLE is present. Console-aware programs [all P?S/8 system programs
are!] can hook into this for logical console input, console output,
logical printer output, logical reverse printer serial port for
control of the printer [only applies to serial printers], and logical
interrupt hook. All of the calls are subroutine calls to page zero
locations in implicate locations for each function. You perform CIF
to the field describefd in 1), CDF to your own field, and JMS to
implicit places on page zero of the console code. Most have the
notion of skip/don't skip return to mimic the way the real hardware
works; interrupt-driven code is obliged should it skip to hook into
the logical interrupt handler to dismiss the associated interupt that
made it skip. [Note: Certain flags and thus non-skip conditions may
be mythical. For example, the VT8E doesn't ever make you wait,
because it is a terminal simulator in memory; the device 66 line-
printer doesn't have a reverse input channel, thus the latter always
returns non-skipped and passes back 0000 so you would never try to
process a control-S or control-Q from the non-hardware. VT8E output
doesn't interrupt, which is consistent with the fact that it doesn't
have a done flag to have to wait for, etc. Console overlays are
configurable for a wide variety of devices that already exist. Truly
strange devices are easily accomdodated because the overlay "owns" 3K
of code to define its stuff.]

The last two bits are for logical abort status. This is how we get
DECmate compatibility. It is hardware impossible to support an abort
on the DECmate because there is no instruction to test a flag without
clearing it. Thus, there is no way to pass that info back to another
routine to test the flag again. Thus, these locations support logical
abort bits. Unlike OS/8, P?S/8 supports two abort keys: The usual
control-C aborts the latest program, and control-B does that and also
aborts BATCH should that be in control of the latest program. Thus,
the user has the option to either abort the latest program or the
entire batch. On machines with -8-compatible hardware, a program can
set the normal keyboard abort condition [control-C was hit and don't
clear the flag. When the keyboard monitor is reloaded, it will test
both ways for both cases, and abort appropriately. This is why OS/8
BATCH cannot run on a DECmate and why OS/8 cannot realize a program
aborted on a DECmate. Some amateurish OS/278 kludges have system
programs outputting their own "^C" to fake it.]

After an amount of iterations that truly will never be known by
mankind past present or future, additional words were found! [Brevity
prevents me from describing any details; let's just say it was a very,
very long time, a non-zero number of picoseconds!]

The words were used to enhance the value of the handler itself:

In OS/8, one of the bits is different in the call compared to P?S/8.
What it is used for is a "recommendation" for the device to search the
tape in the forward direction instead of the usual backwards one.
This notion has a few drawbacks:

1) Many devices literally don't care.

2) Many devices would like to care, but there never was enough room
in the handler to implement the feature, most notably PDP-12 LINCtape
handlers. [Note: The "extended" P?S/8 PDP-12 LINCTape handler
actually implements this, but it's really hard to do in a handler
within 07600-07777, regardless of actual O/S. That hardware really
insists on a backwards-first search unless you strangle it and shoot
it at the same time!]

3) These are few and far between. Unfortunately, dynamically
speaking, there are far more opportunities where this could be useful,
but only a small few static ones. [Isn't it better to support two
RX01 diskettes in the call than to infrequently tell the only one
supported information occasionally about initial search direction when
it is totally ignored in that hardware anyway?] The spare
implementation doesn't warrant even being done.

Thus, a lot of trouble with only a meager result at best. And it is
true that most calls really have to start backwards. But this "dead
reckoning" could help, if only it could be implemented.

In the final handler definition as it has existed since around 1982
[and we will never ever find out the final count of optimization
attempts actually attempted, let's just say that it is definitely
somewhat less than true infinity, but just how much, no one will ever
know], a word was created to hold the previous call's last transferred
block. Additional code was squeezed in to interrogate it, add on a
necessary adjustment factor due to known tape overmotion after a
previous transfer; this cannot be defeated, and initial tape search
direction is correctly reckoned for any situation. On bootup, the
initial direction is set to forward. On a DECtape, P?S/8 literally
boots up in one motion of the tape without any "shoe-shining" or other
back and forth tape motion. This has been witnessed by many who
literally cannot believe it until seen for themselves. They have had
too much negative experience with OS/8 or DMS. Start at 07600, the
tape rewinds to the end zone, turns around, does a lot of rolling, and
the command prompt comes up just as the tape is coasting to a stop.
Using an appropriate hand-toggled bootstrap and positioning the tape
already in the end-zone, the tape literally rolls forward with no
backward motion whatsoever.]

So, in summary, Elekman and Burness were right! Perhaps even they
didn't know just how much.

cjl [I'll save the story about how the P?S/8 keyboard monitor uses all
150 or so of the 128 words on page 0 for another post.]

ps: Remember, 7200 clears the AC, so does 7600; 7601 does, except on
a PDP-8/l where it does nothing at all. Execute when and where as you
will. [the wonders of self-modifying self-referential-as-data coding
techniques.]

Elliott Roper

unread,
Jun 21, 2009, 7:50:22 AM6/21/09
to
In article
<f2492519-2826-46d2...@q37g2000vbi.googlegroups.com>,
cjl <clasy...@gmail.com> wrote:

<snip>


> In the olde days, back when the R-L/P?S/8 DECtape handler [and
> related] had only been "optimized" [to "bum out" locations] say maybe
> only the first fifty iterations or so, there were some "principles"
> applied.

<snip>

What a fabulous post! a.f.c in its prime!
Thanks.

--
To de-mung my e-mail address:- fsnospam$elliott$$
PGP Fingerprint: 1A96 3CF7 637F 896B C810 E199 7E5C A9E4 8E59 E248

sidd

unread,
Jun 21, 2009, 11:58:07 AM6/21/09
to
cjl wrote on Sunday 21 June 2009 09:49 am:

a beautiful post. thanx

cjl

unread,
Jun 24, 2009, 6:03:49 AM6/24/09
to
Here is the "lost" post meant to be on a prior thread: [It lost the
cross-post to alt.sys.pdp8!]

On Jun 22, 5:09 pm, cjl <clasyst...@gmail.com> wrote:


> On Jun 21, 11:58 am, sidd <s...@situ.com> wrote:
>
> > cjl wrote on Sunday 21 June 2009 09:49 am:
>
> > a beautiful post. thanx
>

> Anytime.
>
> Returning to the original post:
>
> Actually, PDP-8's had a way to get to 512K words:
>
> You get to 128K past 32K by using hex-wide Omnibus boards in one of
> the 12 or 20 slot boxes for the PDP-8/a [for some reason, the original
> 10-slot doesn't work].  The sixth foot is not electrically defined,
> and not much is on the fifth foot.
>
> What you get is essentially four 32K bank selectors; all relevant -8/a
> memory supports the bank select, thus in conjunction with the KT8A
> memory controller, you can select more memory, up to 4 whole banks.
>
> CESI [makers of the SCSI controller for the Omnibus] made a compatible
> memory card, which had the entire 128K on the card, responding to all
> four bank selects.  However, it also could be made to decode all four
> bits as a select code for up to 16 banks, thus a total of 512K on four
> such modules.  [This kind of addressing mod is analogous to how the
> RK05 buss is selected on the RK8E versus the -11 and RKS8E, and why
> they can address 8 drives, not a max of 4.]  Also, this is static
> memory, so you didn't have the typical problem of variable-speed
> "jittery" -8/a memory.
>
> CESI marketed an alternate memory controller card.  It programmed
> incompatibly with the KT8A, but it supported a jumper so it ran in the
> 512K mode instead of the KT8A-hardware-compatible mode.  The
> instructions for this card were inherently able to use the same
> [incompatible with KT8A] techniques so it would be software compatible
> with itself up to 128K setup either way.  [Note:  There isn't much
> programming for more than 32K anyway, so having a non-software-
> compatible method isn't as aggregious as it might sound.]
>
> The SCSI controller also supported these bank-select bits; when you
> programmed the SCSI, you had to know which scheme to use, because it
> just loaded your 4 bits into the appropriate registers during DMA
> transfers; your code had to know the significence of the bits.  Even
> in 32K, in one scheme, you have to use 0000 while in the other you
> have to use 0001 [because 0000 selects NO memory!].
>
> cjl [Original PCs came with up to 640KBytes.  512K PDP-8 memory is
> 768KBytes!]

cjl

unread,
Jun 25, 2009, 2:43:20 AM6/25/09
to
> cjl [I'll save the story about how the P?S/8 keyboard monitor uses all
> 150 or so of the 128 words on page 0 for another post.]

To get more than your "rightful" usage out of small resources, you
resort to all sorts of "fortuitous coincidences" [an interesting
subset of general kludges] to get the code to fit. By the application
of multiple techniques, it's surprising how much more you can squeeze
in:

1) Make an instruction into data directly. On the PDP-8, this is one
of the first tricks you do. Many instructions form "interesting"
constants, especially if you allow superfluous operations in [as long
as you do not destroy the flow of the program or compensate for what
it might/does do]. A simple exampe.

To call the P?S/8 System Handler needs the following "normal" code:

SYSIO= 7640 /ENTRY POINT OF THE HANDLER; IT COULD
HAVE BEEN ANY OTHER CLOSE VALUE,
/BUT WE MADE IT THIS WAY FOR A REASON!

JMS I (SYSIO) /SOME CODE ACTUALLY DOES LOOK LIKE THIS!
IT'S ACTUALLY TRUE THAT NOT
/EVERY LINE OF CODE NEEDS TO BE ULTRA-
OPTIMIZED!

However, a lot of people really shy away from literals [which get a
bad rap; used properly, they are a fine feature; most people don't
know how to use them properly!]. As such, many programmers who
learned in the PAL III days would more likely use:

JMS I LSYSIO /SAME AS ABOVE

LSYSIO, SYSIO /POINTER TO SUBROUTINE ENTRY POINT

I developed a cross between the two to indicate an "irregularity" is
possibly in effect:

JMS I LSYSIO/(SYSIO) /SAME AS ABOVE, SORT-OF

However, the subtle difference is that I am stating that I would use
the generated literal form, if I wasn't trying to squeeze the extra
word out, and that due to squeezing, I am not. The reason lies in the
other half of the usage. In this case, somewhere in the code is:

IFNZRO SZA CLA-SYSIO <THIS IS AN ERROR, YOU CHANGED SOMETHING
YOU DON'T UNDERSTAND, STUPID!>

or possibly

IFNZRO SYSIO-7640 <AN ERROR, etc.>

Either calls your attention that in yet some other place [such as the
above in part] there is "something" going on you better not touch!

To actually save the extra word:

TAD FOO /GET SOMETHING
TAD CHECK /COMPARE TO SOMETHING ELSE
LSYSIO,SZA CLA /SKIP IF THE SAME

The source statement that was declaring the use of the literal in an
alternate universe, is instead statisfied by using a handy word of
code that is guaranteed to have the correct binary value as the
relevant address it is "pointing to" etc. It would be irresponsible
to allow this without the testing conditional code, but with it, the
kludge becomes part of a maintainable piece of code. Of course you
have to have the mathematical trap code to define the constraints on
the trick properly configured, so that allowable changes don't
complain while fatal ones do.

Constraints could be such as:

1) Must be on page zero IFNZRO .&7600

2) Must be between 0010 and 0017 /perform enough tests to ensure
whatever you need like this; doesn't matter; the conditionals are not
generating code, just perhaps error messages of you screw up.

3) Must be at certain relative places on a page, or a range on a page

Each situation has a bunch of similar rules to make the code function
with assembler-time protection. It is perfectly fine as long as the
conditional is present to enforce whatever rules the kludge depends
on. To have a random brainstorm with no explanation as to why it
works is unacceptable. You'll forget and move it yourself and break
it!

Now that programming style is described, onto the kludges themselves.

The example given was that the System I/O routines are obviously
something needed throughout a lot of pages of code within the P?S/8
keyboard monitor. The basic idea is to squeeze whatever you can into
exactly 2048 words; without doing this, a lot of features would have
never been written.

An easily saved page zero space is therefore, there is no actual page
zero location containing 7640. In each and every situation where
there ought to be one, there is always an ability to appropriate a
local word of code on the page that can be forced if necessary, but in
any case is exactly 7640. Thus, a word saved just by satisfying all
of the callers. [Note: You really need a cross-reference facility to
make sure you know what you're doing! It could be easily overlooked,
etc. otherwise.

Note that all of the class II operate inststructions can easily clear
the AC by .OR. 0200 with the instruction. You might want to just
clear the AC, so make it SZA CLA. There are potential problems, but
they are easily solved:

1) If you know the AC is always non-zero, this is harmless.

2) If you know the AC is alwys zero, then make sure some other
location follows that will be data or a constant; it doesn't matter;
it will always be skipped.

3) If you are not sure, place a "safe" "instruction" after it; a good
starting point is to use local constants less than 1000, other than
0410-0417. This becomes an innocuous AND instruction, that even if it
does execute, the AC is already 0000, so no harm done either way.
More esoteric usages could use constants that are 1xxx if you don't
mind the AC either gets dirty, or you are certain that as a TAD
instruction to a specific address, the contents are guaranteed to be
clear., 2xxx instructions can be used, as long as you are sure that
incrementing the implied contents of the effective address is
allowed; and that it won't ever be 7777 because that would skip the
next instruction [unless you protect THAT part of the code properly as
well!] 3xxx instructions the same, as long as you know you are storing
something with that DCA instruction. Avoid 4xxx, 5xxx andm 6xxx
instructions entirely! Certain 7xxx are fine, but you have to know
all of the relevant rules for each. [Most people stop at the AND
instruction level or one of the simpler cases.] Note that a JMP
instruction in a code sequence could itself be a useful constant [or
any of the others] but you cannot place it in-line if it is capable of
executing.

Sometimes, another "interesting" constant can be formed by placing a
superfluous condition into a skip-class instruction, as long as it
doesn't foul the intended logic. Always check if you need any
constant 7xxx and see if you can contrive it wherever you need it. If
you can do that on each page you need it, you have saved a location on
each relevant page, or a more valuable page zero location if you can
locally satisfy all of the usages with local "opportunities".

Other interesting principles affect AND instructions or groups of
them:

By placing a "nice" constant at a specific location, an AND
instruction becomes also a constant by referencing the instruction
that references the first AND instruction as another constant. In
essence, with careful design, you can get two for one!

P?S/8 really needs a page zero literal [7], but it has been squeezed
out. At location 00007 is a page zero storage location needed in a
lot of places, often needing an AND instruction to work with it. The
instructions to do that AND 7/TEMP generate 0007 in the process. And
yes, there are more than enough usages on each page where the [7] is
so that each page gets a local replacement, thus that one ain't on
page zero either.

A common restart address is 0200. Better still, put something
innocuous in 0177. Then instead JMP 177 to restart effectively at
0200, but without the need for a constant 0200 anywhere.

Another technique involves intimate understanding of the data. You
may want a constant such as 0300 to AND against, but perhaps you also
know that bit[2] is always zero. If that is true, you don't mind
instead performing AND (1300). Well, needless to say, that (1300) is
trivially available; just place some location at relative 0100 of
every current page where needed; the inevitable TAD 300 instruction to
access it becomes your constant 1300. ISZ 300 becomes 2300, and DCA
300 becomes 3300. Perhaps one of them is adequate, as long as you
know your mask is sufficiently "don't care" enough on the affected
bits that the usage really doesn't clear [but you know it actually
doesn't matter! Of course you better be right!]

Some of these techniques have causes some code segments to be moved
around as some previously neutral code now must conform to a specific
page or a specific relative range on any current page depending.
Pointers to the relevant routines can form all sorts of useful
constants!

7200 and 7600 both clear the AC, but the latter can point to the
system restart address. Needless to say, that's not on page zero!
Also, NOP is 7000, but so is 7400, which could be a more useful
constant when needed, etc. [And don't forget the AND instruction that
references it will likely be a useful constant itself for some other
purpose.] It is not unusual for seemingly out-of-nowhere conditionals
that force assembly errors to be sprinkled throughout the code; all
are there to protect some aspect of some other code, usually elsewhere
that needs the stated condition to be true, so that code generated
somewhere else generates a constant needed on that page, but is
affected by what goes on here, even if indirectly.

Another technique involves the fact that subroutines are only entered
serially, not recursively. You can always use a "neighbor"
subroutine's header as a temporary, as long as you are damn sure it
isn't indirectly called by you destroying the temporary!

Sometimes, the auto-index registers are partially a waste. If you
don't need them all, define an alternate usage for one or more of
them. Just make sure they are not used indirectly! 8 might be
enough, but in this code, 8 is actually a few too many.

In the case of the P?S/8 totaly squeezed 2K of code as mentioned
above, there are no actual literals, just the pseudo-literals as
described above. The use of actual literals is frowned upon, because
in all likelihood, it represents the potential for a wasted
optimization that might get overlooked. However, because there is so
much optimization, there are many lines of code containing pseudo-
literals in the comments, which adds up to small dozens of words saved
on page zero.

The usual ground rules to start coding apply: If there is only one
reference, put it on the current page. If there are two or more, put
it on page zero. Regardless of which rule is being followed, always
attempt to remove all of the ones on page zero if possible, by making
each needy page provide its own local way out of the need.

In certain circumstances, P?S/8 swaps in a section of code that by
nature is short-lived. If such code has some once-only code, handy
constants may be lurking there as well. And you can always use once-
only code for a lot of storage locations after the code is dead!

All of the "obvious" constants are on page zero, except they actually
aren't there! You can easily create 0001, 0002, 0004, 0010, 0020,
0040, 0100, etc. by accessing other page zero locations using AND
instructions and the like.

I may be leaving out a few other more esoteric things, but that is in
the main why P?S/8 has more page zero [equivalent] locations than the
hardware would ordinarily allow. A lot of handlers in OS/8 and P?S/8
use a few of these tricks, but once you add page zero potential into
the mix, you can get quite a lot more bummed-out words.

cjl [If your code occupies no memory whatsoever, you can consider it
optimized, but only conditionally]

David Powell

unread,
Jun 27, 2009, 12:31:18 PM6/27/09
to
In article
<a8921b66-551a-4c63...@q37g2000vbi.googlegroups.com>,
cjl <clasy...@gmail.com> in alt.folklore.computers wrote:

> IFNZRO SZA CLA-SYSIO <THIS IS AN ERROR, YOU CHANGED SOMETHING
>YOU DON'T UNDERSTAND, STUPID!>
>

If using PAL-8, make really sure by including a DTORG 7 pseudo-op in
the conditional, so that ABSLDR aborts the buggy binary with a
checksum error. Those error messages scrolled, unnoticed, off the top
of STUPID's 12 line VT50 display a few seconds after he typed EG$$ to
TECO, when he was halfway down the corridor to the drinks machine.
Might as well get a cuppa than sit watching the DECtapes click and
whirr, thought STUPID, who *knew* that his edit was bug free.

Happy days!

Regards,

David P.

cjl

unread,
Jun 28, 2009, 7:29:53 AM6/28/09
to
On Jun 27, 12:31 pm, David Powell <ddotpow...@icuknet.co.uk> wrote:
> In article
> <a8921b66-551a-4c63-b3cd-72e8ad55d...@q37g2000vbi.googlegroups.com>,

> cjl <clasyst...@gmail.com> in alt.folklore.computers wrote:
>
> > IFNZRO SZA CLA-SYSIO <THIS IS AN ERROR, YOU CHANGED SOMETHING
> >YOU DON'T UNDERSTAND, STUPID!>
>
> If using PAL-8, make really sure by including a DTORG 7 pseudo-op in
> the conditional, so that ABSLDR aborts the buggy binary with a
> checksum error. Those error messages scrolled, unnoticed, off the top
> of STUPID's 12 line VT50 display a few seconds after he typed EG$$ to
> TECO, when he was halfway down the corridor to the drinks machine.
> Might as well get a cuppa than sit watching the DECtapes click and
> whirr, thought STUPID, who *knew* that his edit was bug free.
>
> Happy days!
>
> Regards,
>
> David P.

PAL assembly tips.

Actually, I have a more general routine to handle the way I develop
code, that can include yours:

P?S/8 PAL cannot implement the concept of a bad checksum because you
really don't have the notion to assume that the binary is checksummed
at the assembler output level. As long as P?S/8 binary files are read
from error-free blocks, there is no concept of bad binary. However,
there are other techniques that can be made to work:

You can define a pass variable that defines what pass you are in:


IFNDEF PASS <PASS=1>

And at the end of each assembly

IFNZRO PASS <PASS=PASS+1>

In between, you can do pass-specific stuff that in turn depends on
which assembler you are using.

IFNDEF PQS <PQS=0>


IFZERO PQS <Do stuff only other than P?S PAL can
meaningfully do, such as DTORG 7;P?S PAL makes this a non-functional
like NOBITS and ENBITS included merely for PAL10 compatibility; thus
you can deliberately screw up the binary in PAL10 or PAL8 only.>

IFNZRO PQS <Do stuff unique to P?S PAL>

A good collection of more useful stuff is available:

IFZERO PASS-2 <Do pass II stuff here>

and similar statements can be used to output stuff as necessary, or
pass 1 if you wish, but you probably don't need this complication.

PAL-8 outputs nothing at all during pass 1 unless it's a rather
serious problem. Pass 2 outputs a whole lot of references to
everything that went wrong, and yes, it can get lost in a sea of
output.

Thus, if this is PAL8 assembly, the best scenario would likely be to
make the error get seen only in pass 1. If this is pass 2, abort the
assembly using the conditional assembly of a line containing just the
$ character, since all output is ignored after that, with the
exception of the fact that the rest of the line past the $ is treated
as a comment; no need even for / since it just prints as it flushs.
All following lines don't print at all in pass 3.

Thus, presuming this is a well-established source file that previously
assembled error free, and a noob came along and proceeded to rape it
ignorantly, thus the source file is so hosed, assembling it is
essentially useless now, the lines seen on the screen are hopefully
few in number, but in any case, once the code got to pass 2, the last
thing you see on the screen is the result if the screwup, since the
assembly aborted at that point. You should be creative enough to
create an appropriate putdown string:

US IDIOT @FOOBAR+0000
US YOU @FOOBAR+0000
US RAPED @FOOBAR+0000
US THIS @FOOBAR+0000
US CODE @FOOBAR+0000

And then the assembly aborted. The FOOBAR+0000 part will let him know
where to look for his mistakes, etc.

I don't really do any of that, because my usage of PAL8 has never been
about anything within OS/8 itself, other than the development of the
Kermit-12 project, or various OS/8 handlers, or truly system-
independent stuff such as the SCSI factory-mode diagnostics/
utilities. I have never really been an OS/8 developer per se, but I
have looked at a lot of its code, and I find it wanting in various
ways, especially in terms of source code preparation conventions and
commenting scantily and sloppily, etc.

However, at this stage of P?S/8 development, I perfer to use TECO in
OS/8 to edit the files themselves, putting up with PAL8's restrictions
to get the code to work; where required, P?S PAL-specific extensions
are used. [In the future, after a lot more P?S/8 development, I can
abandon OS/8 entirely. As a PAL developer, it is offering me a
dwindlng level of utility. On some of my commercial application
projects, it really has a lot of sore-points that get in the way.
Someone suggested I adapt P?S PAL to work with OS/8, but I am not that
desparate!]

In P?S PAL, there are other ways to go:

The ERROR pseudo-op bumps the count of assembly errors, thus ensuring
it will never automatically load, go, and start because the automatic
chain to P?S BIN is aborted. [Bitmap options will be allowed to chain
to P?S BITMAP [a slight superset of OS/8 BITMAP], but not allowed to
load via BIN. [Of course I cannot prevent the fool from loading the
defective binary in P?S/8, but how much handholding do you need to
give to an idiot?]

The console output is something like

ER abcd at FOOBAR+0200 (0400)

where abcd is the value optionally passed to the ERROR pseudo-op
[defaults to 0000 if no argument].

Additionally, [as I remember it, does PAL10 also have something like
this?], P?S PAL has the PAUSE pseudo-op enhanced to take an argument.

PAUSE

is ignored, but

PAUSE 1234

causes console output of:

PA 1234 @FOOBAR+200 (0400)

This is not an error, You can get some dynamic info about
approximately where in your long assembly you are just by watching for
a short collection of progress statements. The easiest way to do it
would be:

PAUSE .

sprinkled lightly throughout the code, to get a handle on just how
long it's gonna take. And of course, you can make the messages pass-
dependent if appropriate.

And of course $ will abort the assembly, after at most outputting the
rest of the latest [ignored past the $] source line. This works on
all three assemblers, so you can abort the idiot's output if you want.

Assembler pass considerations.

P?S PAL does NOT support the automatic second pass. The default
assembly is for one pass only. The vastly superior nature if this is
taken from the original PAL III idea:

What's the point if early on in a program development, you have say
2000 references to say 300 symbols, all undefined. PAL8 will do what
you suggested, just printing out long screenfuls of noise.
Invariably, all you can do is waste paper, time, or both, and get a
pass 3 listing and waste your time pinning down enough key factors to
slowly converge on a dull roar of a manageable number of errors? Just
plain stupid; no excuses, guys, PAL III predates P?S PAL and was used
in the original R-L version; we updated what we had, and didn't throw
anything away merely because it was "old".

The beauty here is that what you get is a sorted out quick printout of
just those symbols that are undefined, with an approximate address of
where in the flow of code they were first encounted. In the symbol
table, if a symbol is not present, it is placed in the table, flagged
as undefined, and then given the "value" of . as encountered at that
point. [Note: That value is imprecise in the sense that when things
are really badly undefined, . may be itself dependent on something
else that was meant to be, but is itself undefined, thus the origin
setting just auto-incremented the best it could until some more stable
course correction, such as PAGE moved it to the next page, etc., or an
explicit origin setting in terms only of arithmetics and fully defined
symbols at that point in the asssembly, etc.]

You can even specify the /N "neatness" option which in turn makes the
output paginated on the console with the cute and familiar tear-off
"--------" lines at the top of each page, page numbers, date, PAL
version, etc. associated with full listings, despite this is only a
[hopefully!] short listing, obviously miniscule to the monster
associated with a full listing of a program of any size, regardless of
error-full or error-free state, etc. Additionally, the /N makes PAL
check for an LPT: so if available, it will even print that nice neat
short and to the point summary listing on the system line printer.
[Note: 4K "un-enhanced" P?S/8 requires the real device 66 line-
printer, precisely the way OS/8 12K BATCH also does; yes, neither
system is truly device-independent at all!]. However, if you have 8K
or more, the recommended way to run P?S/8 is to enable the system
console overlay. Various configurations exist for whatever you would
need. The configuration can trivially be updated so that the printer
is logically off-line; all calls to the printer will time-out because
the logical equivalent of how the real hardware times out works
precisely the same way without changing the program attempting to find
the line-printer optionally. [That is the way virtually all P?S
programs seeking a line-printer work; they could easily be defined to
complain if there is no lpt:, but the standard implementation
recommendation is to work the way OS/8 12K BATCH does, namely if you
can find the printer use it, otherwise use the console;of course you
could instead abort with a console message "printer not available" or
somesuch, but no one ever wanted such an option; trivial to implement
if desired, etc.]

During pass 2, P?S/8 must be doing either generating binary files,
performing a listing or both; else there wouldn't have even been a
pass 2, unless using an explicit switch to force a second pass, but
implied by perfoming some second-pass function[s]. There is also an
option to obtain merely a symbol table, which could thus be after pass
1 or after pass 2. Since it has that really nice concise error report
[sort of an UN-symbol table!] only more developed programs even want
to perform a pass 2!

3 pass disaster considerations.

Since there is no pass 3 in P?S PAL, you cannot get into trouble with
pass-cumulative accidental incremental definition updates in P?S/8, a
problem unique to PAL8. In essence, by violating some of the rules,
you can get a perfectly good looking listing. Too bad the binary and
listing do not agree! And of course no error messages.

An easy way for this to happen is to allow code that generates
literals to cause where an origin effectively goes to be dependent on
just exactly how many literals were actually generated. Since it will
be pass-dependent should you reference a consequence of an address
forward of the point in the assembly at the moment, the literals can
generate specifically differently, but no error message by the end of
the pass because it gets fixed up for, but too late. Because the
passes are cumulative, the listing shows what you wanted to happen by
magic. But the binary, since it happens earlier than the listing,
doesn't match what the listing shows! This little tidbit was
published way back when in one of the prevailing newsletters; I
believe it was an article by Jim Roth, the only person to ever make
any material development for both PAL8 and P?S PAL.

In essence, you do not have the right to create literals based on a
symbol not yet defined and forward of the reference point, unless its
definition will be totally resolved during pass 1. By violating this,
the exact address of generated literals and the code they generate
will be a function of how many passes the assembler has to
cumulatively fix up what you are attempting. Since even PAL8 cannot
guarantee pass equivalency between the binary and the listing, done in
separate passes, it is conceptually impossible to fix the problem
posed here. At least P?S PAL guarantees that the binary you get is
the same as is on the listing. You might discover the error of your
ways, while PAL8 is guaranteed to hide it leaving you scratching your
head because it looks totally correct, yet isn't!

P?S/8 console overlay considerations.

Console overlays can define anything you need for the hardware
available:

Console input device; doesn't make sense unless it has a keyboard.
Thus, for example, a VT8E makes a fine choice here for that. So does
some terminal hooked to another serial port such as a PT08 connected
to device 10/11 or 40/41.

Console output device: Doesn't make any sense unless it can actually
output somewhere. This could include dual outputting to say the 04
console and optionally use the LPT: if you are debugging something,
but usually wants to be the 04 console by itself. Or use the VT8E for
output, because in that version, the VT8E includes a superfast
terminal emulator [a sort-of psycho VT05 with some extensions that
make the P?S/8 console output while in editing mode nicer to look at;
you can do all sorts of customizations if desired; lower-case is
simulated by outputting all uppercase at full brightness and all lower-
case as upper-case at regular brightness; you could reverse which does
which, but personally I leave it at that! We did a VT8E patch to OS/8
TECO and it works the same way, and we chose the same way there as
well!]

Of course you can also use an alternate console device pair such as a
device 10/11 PT08 to output to an alternate console device if
permitted. [Note: As an implementation restriction, the LINC-8 4K
version of P?S/8 does not support the logical console abort switch.
Thus, on that system alone, you must use the actual console. The
precise definition is this: All systems must support control-C and
control-B handling correctly, the same way OS/8 does, as long as the
hardware on the machine is totally PDP-8 compatible. This is the
hardware abort mode and is mandatory. However, in some system device
cases, such as in the 4K-oriented LINCtape handler for the LINC-8, it
is not possible to preserve the soft abort bits across the reboot it
causes, thus that information is lost; the hardware information is
preserved. As a practical matter, since the LINC-8 was only made in
4K and 8K sizes, this is a marginal restriction at best. The logical
console bits can only apply on a PDP-8 compatible machine that has 8K
to even implement an alternate console that would even entertain the
notion of an alternate input device. Thus, this is the only known
case where the logical abort bits would even be relevant, and also
fail to implement. When the system handler is the variant that
requires no hardware modifications, but requires a minimum of 8K, the
problem ceases to exist, as that system reboots without destruction of
the soft abort bits at all.

Soft console aborts as opposed to hardware-created abort characters
pressed.

The only two purposes for the soft abort bits are:

1) The console keyboard device is not device 03, and thus cannot
output hardware abort status within the context of the KSF instruction
being able to in fact be retested via another KSF skipping and then
KRB reads in the aborting character. This is required in both OS/8
and P?S/8 to detect an abort; in the case of OS/8 this is a general
abort even if BATCH is running; in the P?S/8 case, control-C is *NOT*
the general abort character, that is instead control-B. However, as
in OS/8 control-C will abort the latest program, however only in P?S/8
BATCH situations, is it relevant, as it would not abort the BATCH
itself, since control-B does that. Thus, P?S/8 allows better user
control over the situation. When BATCH? is not a consideration,
control-C is the same in both systems, and control-B is a P?S/8-
specific superset feature also aborting the [non-batched] running
program, etc.] All machines from the [unsupported] PDP-5 [including
the unsupported PDP-8/s with requisite device 03/04 PT08] through the
PDP-8/a support this compatible hardware. On any of these machines,
programs running under either P?S/8 or OS/8 are instructed to run the
following code:

KSF /FLAG UP?
JMP NOABORT /NO, FORGET IT
KRS /YES, READ IT IN, BUT DO NOT DISTURB THE FLAG
TAD (-3) /COMPARE TO CONTROL-C
SNA CLA /SKIP IF OTHER
JMP I (7600) /ABORT ON CONTROL-C, THE SYSTEM WILL DO THE
REST
KCC /OPTIONAL, ETC. COULD ALSO BE KRB IF RELEVANT
TO SOME INPUT ROUTINE

The P?S/8 extension for programs knowing they are running there is the
same except add a check for control-B as well; do the same action for
either. After the TAD (-3):

SZA /SKIP IF ALREADY CONTROL-C
IAC /CHECK FOR CONTROL-B ALSO

Thus, any program that wants to do it the P?S/8 way could also abort
on control-B. However, you don't obtain any special consideration in
OS/8 for doing that, while in P?S/8, both acknowledge the abort and
BATCH also terminates its automatic operations as well if the abort
was control-B.

Major implementation note:

P?S/8 BATCH runs on ANY P?S/8 system, even 4K. It runs anything and
everything and has no restrictions whatsoever. Most importantly, it
runs across reboot operations! This can be really handy when running
a suite of paper-tape-oriented diagnostics, at worst destroying the
bootstrap, and requiring a manual reboot procedure; the batch will
keep on runnning [unless you hit control-B].

Thus, it is crucial to get these control checks to work!

When the console device has become a logical call to a piece of code
instead of a piece of I/O hardware, you need a logical bit to indicate
the abort condition happened. In that case, two bits have been
reserved [the two low-order bits if the word mostly giving core-size
information and the presence or not of the logical console itself.
Note that this entire subject is beyond the scope of OS/8 entirely at
the conceptual level! I have recommended for years that OS/8 needs a
do-over, and this is perhaps the highest priority thing to add. Not
the overlay, the soft abort bit; it's the only way it well ever be bug-
free on a DECmate! Look at all of the broken programs on the kludgy
OS/278 victim of 2) below.]

2) If the hardware of the console is the DECmate's incompatible
hardware, P?S/8 checks for the DECmate problem directly [unless the
logical console applies as in 1) above]. Thus, the code self-modifies
and in essence is forced to perform a KRB, but knows the keyboard
monitor cannot know what to do. [Note: In certain kludged OS/278
programs, they each fake a "^C" console output message and then exit
to 07600; BATCH never notices it!] As appropriate the two low-order
bits of the CORE word are set accordingly; when the keyboard monitor
reloads, it will know how to handle the logical abort even if the
hardware abort is impossible.

Note: It is illegal on the DECmate to perform KSF and *NOT* then
perform KRB if the instructruction skipped. The reason is that the
KSF skips on and clears the flag. All of the "odd" device 03
instructions are real, while all of the "even" instructions are
trapped! Thus, KRB is trapped and emulated in the control-panel
memory of all DECmates. The AC is filled with the latest character;
on the DECmate I, this is not very well buffered, and there are
reports of occassional dropped characters associated with esc sequence
keys if the code is not responsive enough. One of the design details
of the DM II, III, III+ is to well-buffer the actual input from the
real keyboard device, which is actually an internal serial port that
communicates with the real keyboard bi-directionally [so it can make
the keyboard update the lights on the keyboard!] and all of that I/O
only is available in control-panel memory [which runs even if the
machine is halted! It interrupts to CP memory anyway, and then halts
again!]. Thus, you have to have the trapped KRB to keep the machine
going. Else it will indefinitely hang and you have to kill the power
to get out of it! Some OS/278 programs are that flawed, especially
ODT which hangs the machine on the LF command until you shut it off
and on again!]

The additional console functions defined are:

Logical line-printer reverse serial channel. Will always fail to take
a skip return on hardware that is parallel and output only. But on
serial ports [such as on the DECmates and the VT78] you have to
perform control-S and control-Q to make the printer function at all.
Same would go for other devices on other machines with second serial
ports to serial line-printers such as PT08 or KL8E or KL8-JA, etc.

Logical line-orinter output. Can be configured to always fail to take
a skip return if the printer is actually present but off-line, thus
making it work the same way as the real device 66 printer would. If
the printer is actually a real device 66 printer, this is just a pass-
through in theory, but the actual code implements a print buffer that
makes the printer run at full speed! OS/8 can never make a real
printer run faster than something like 300 LPM due to its feeble
design and not overlapping I/O properly. [In theory, this could be
fixed, if you can kludge something in, but it would be ugly and
failure-prone.] P?S/8 has been tested on 1200 LPM printers and runs
them at full speed.

This can also be configured to make the printer permanently off-line
via a trivial patch.

Basic P?S/8 console overlay configuration.

P?S/8 console design requires intimate knowledge of just how P?S/8
works, however utilizing it is quite simple:

.CONSOLE Binfile or binfiles

The file[s]s if present, are sanity checked. First they must actually
be P?S PAL-generated binary files, secondly they have to conform to
the overall loading restrictions of the console overlay itself. The
files have to have specific FIELD settings; the actual binary is not
loaded where the code indicates, that's just for validity checking.
The code is dynamically loaded into the highest existing physical
field locations 0000-5777 only, as this is the area reserved for the
entire overlay definition. If the console overlay is on, the CORE
command will change the maximum memory field available to one less
than the physical actual hardware limit. [They are both set to the
physical maximum if the overlay is turned off.] [Note: The internal
loading function of the console enabling utility is the same as the P?
S/8 slurp loader, except the so-called "virtual" variant is used.
However, there is no actual swapping, because loading is restricted to
the area of memory reserved for the console overlay in the highest
existent field of memory, which must be field 1 [8K] through field 7
[32K]. The actual code doing the loading runs from field 0; all
loading is redirected and restricted to the actual highest field
physically present and only allowed in the range of 0000-5777. Thus,
attempts to corrupt the console overlay with valid, albeit
inappropriate binary files just causes the system to invalidate the
console overlay and reload everything after a reboot.]

Switches exist to enable and disable the overlay. You can pass a
small patch binary file, which will update the overlay image and not
otherwise change it in the overall sense, nor will it enable the
console overlay itself if it is currently disabled nor disable it if
it is currently enabled.

The command also supports:

CONSOLE ON

and

CONSOLE OFF

to enable the overall overall or disable it. Thus, an overlay can be
configured even when disabled. If enabled while being changed, it
will be reloaded. The code includes internal checksumming so that if
the console overlay gets corrupted, it will automatically be reloaded;
the system knows to check if the overlay is enabled, etc. [Note: To
invalidate the overlay, the checksum is deliberately destroyed. This
forces it to reload, which makes the next bootup take longer.]

Thus, it is possible to patch the overlay by passing a small binary
file:

CONSOLE NOLPT

or

CONSOLE LPT

This would work generically, because of the way the internals of the
console overlay work [it's part of the design to enable and disable
the logical LPT;in this case LPT and NOLPT would be binary files of
convenient name.]

Note: As an implementation restriction, the CONSOLE function cannot
be passed a file named "ON" or "OFF" for fairly obvious reasons! The
alternative would have been to have option switches to implement
whether it was coming on or being turned off, and it was felt it was
better to make it more obvious. The console enabling utility is
capable of self-parsing the command buffer to allow passing either
files or inline arguments [other than this minor restriction where a
file named "ON" or "OFF" could thwart the logic! For the true
psychopaths out there, you could write binary files to mimic the
functions of the inline arguments, or if you are also evil, make the
files do the opposite of the intended function!]

P?S/8 versus OS/8 command structure concepts and performance
considerations.

OS/8 has two methods of running: No CCL; you have to R or RUn
everything, and CCL enabled. If so, additional commands are allowed,
but must be enterred sufficiently to avoid anomaly. The entire CCL
package is so cumbersome that it is not recommended to be enabled on
tape-based systems! It is noticeable that there is a small delay even
on an RF08-based system.

In P?S/8, the basic command decoder works somewhat differently. There
are programs that can be initiated with the R or RUn command, each
works somewhat differently, and each supports all of the other
commands, except the ones that would become worthless as a result.
However, the rest of the commands act as a sort-of built-in CCL
without any overhead whatsoever. The reason for this is that the
command processor also supports a built-in line editor for 2K sized
files loaded into the 2K not occupied by the 2K system. [Yes, the
entire code is only 2K long! That includes the system handler and the
directory of the latest system logical unit 0-7. The other 2K is
reserved for the latest file to edit.]

Some of these commands are directed specfically for editing the file.
In the latest P?S/8, all of the commands have been made compatible
with COS-310 where relevant. Thus, you have commands such as FEtch
filename and WRite filename.

Historical Note.

COS-310 is itself based on DIBOL-8 which is in turn a minimally
disguised version of the R-L monitor, the direct forebear of P?S/8.
Remember, P?S/8 was offered to DEC, not once, but twice! They turned
it down the second time because someone there decided that, despite
the fact that it makes better usage of more memory than OS/8 does, and
that for any serious work, most people need 16K on either system to do
anything really useful, perhaps more, the fact that it perhaps could
run on a 4K-only machine, would "discourage memory sales".

Eventually, I sold a limited royalty license to Intersil and P?S/8 was
marketed as IFDOS for the 8K or more Intercept I or the Pacific
CyberMetrix PCM-12 or PCM-12A which were essentially the same machine
except third-party marketed, not by Intersil, and in different
chassis; most notably sold as a semi-kit.

The same machines could run OS/8, but required 12K to do so; the
system device is the DSD-210 RX01 superset; totally compatible,
supports up to 4 drives, not 2, supports write-protect both on the
media and via front-panel write-protect switches, and most importantly
can FORMAT actual 8" media!

There NEVER was a DEC product that could format a diskette! [Rainbow
users, go away! It was unreliable to try it on the RX50; the results
were a diskette others could not read in many instances. In the case
of the RX50, it was the drive that was the problem, not the controller
hardware; in any case, all of the other machines with RX50 didn't even
bother, and certainly never on anything made for the -11 or the -8.
By contrast, the DSD third-party RX-compatible controllers ALWAYS
formatted, and generally were hooked up to decent drives so it made
sense. The BEST way to format RX50 media is on a PC with a TEAC
FD55GFV/GFR drive using 22DISK, TELEDISK or related software that is
intimately familiar with the vagaries of RX50 format, and more
importantly performed on a decent drive! If anyone asks, I can re-
open that topic, but it should be in the archives of alt.sys.pdp8
itself, since I did write about this subject exhaustively quite a
number of years ago, etc. In any case, a large collection of RX50
diskette images are available on some prevailing PDP-8 archives in the
format needed by TELEDISK, etc.]

P?S/8 command internals.

There are certain commands that aid the resident editor, but are not
necessarily resident. For example, there is a FOCAL, 69-like one-line
search/edit/replace function to edit an existing line without having
to type it over. Where possible, it uses the same relevant keyboard
characters as FOCAL. [No need to learn anything new for no good
reason!]

This is not available during BATCH operations [and is the only command
so restricted during BATCH; however, BATCH does allow line replacement
and then save to effect editing from BATCH anyway, just not in a
character-at-a-time mode.

Additionally, there is a SEARCH command that will print out all
occurreces in the 2K file of any arbitrary string. Thus, this
command, which has to be swapped in, must preserve the latest file
being edited to be of any use. This is accomplished by loading over
the page where the one-line editor normally goes, thus when done
causes reloading of the relevant system block. This works because the
starting address is specified as within the keyboard monitor itself,
and it is defined as only one block [page] long. In theory, more
could be written; some of the commands may have additional overhead.

One such utility was written to cache all of the logical drives'
directories into memory, thus requiring field one space free of other
P?S/8 usage [easily achieved if 8K or 12K if either the system handler
or console overlay considerations raised the requirement for this
program to also be used. This made all directory operations
instantaneous. All attempts to exit the system first wrote out any
"dirty" directory copies, etc.

There are other such commands; the only rule is that they have to have
the first two characters unique [which is partially why COS-310 was
modified from the R-L-like DIBOL-8; all of these systems have a common
ancestry].

Thus, the CONSOLe command was created as a convenience to the user to
take advantage of the command mechanism associated with preventing
destruction of the latest file, even though it has no file
considerations either way. This also allows the BATCH to turn the
console on and off during a BATCH session. [Note: Console changes
force a reboot, but BATCH runs across reboots! Thus, as a rstriction,
the current file is not automatically protected, unless certain other
options are enabled: There are other command options to automatically
save the current file into the $ pseudo-file located at block
0020-0037 of the system device whenever anything might destroy it
[such as executing the CONSOLe command, the R command or the RUn
command or anything else in that category; to use the MS-DOS analogy,
these would be the "external" commands while the "internal" commands
wouldn't force this even if it were set. A companion option exists to
automatically perform a load operation after a reboot; implementing
both operations is thus the same as performing a phantom:

WRite $

then do that which would make it lose control and/or boot or run
something else, etc.

reboot here

FEtch $

thus, the current editing file is preserved.

For quick assembly noodling around:

Type up a file.

Invoke PAL passing to it the $ file, knowing it will be saved. PAL
assembles the short file, reboots. The file is automagically
reloaded; just continue editing as before.

You can do the same thing for P?S/8 FOCAL, etc.

Note: The P?S/8 SET command has many options, most fall into groups
otherwise unrelated. Commands exist to customize the command prompt
[just CR/LF for retro R-L compatibility, CR/LF. for TOPS-10 or OS/8
cosmetics, CR/LF{current system unit number}> for the "intelligent"
prompt stolen by CP/M-80 and later MS-DOS. [P?S/8 did it before
either of these systems existed; they did it with letters, but we did
it first with numbers!], or set the default TAB command mode [tab
expansion options include output all tabs as spaces, output all tabs
as actual <HT> characters, output <HT> characters with the special
handling required by a model 35 teletype, and output the correct
number of spaces to align on multiples of 8 boundaries. The actual
TAB command can temporarily override this setting, or enable/disable
the commands to auto-load the $ file after a reboot or auto-save a
file before running anything that would tend to destroy it. The
command is open ended, and the only restriction is that the SEt
command itself doesn't use files, just the rest of the command line,
similar to the way the CONSOLe command works when there are no actual
files passed.

Efficient system layout considerations.

The CONSOLe operation can be forced to be non-destructive of the in-
memory latest 2K file being edited, if so desired. This happens at
small overhead, because the file is adjacent to the loaded-in system:

Block 00 The bootstrap and system handler
Blocks 01-17 The rest of the 2K of the keyboard monitor and
integrated editor, including the directory of user files on this
system device [if applicable].
Blocks 20-37 The % file, which is also general purpose. Options in
PAL and BITMAP and BIN can use this as a default binary output file if
desired.
Blocks 40-57 The $ file.
Blocks 60 The embodiment of the /I code that makes the slurp
loader reload the system handler over the slurp loader automagically
as discussed in a prior post in more detail.

Everything past that is system-configuration dependent. Thus, even if
these options are enabled, it still loads quite fast due to clever
placement of all of the relevant material at adjacent blocks.

Historical note about the R-L monitor.

This system layout, with only minor revamping by many people over the
years, is still substantially what Richard Lary literally hand-
assembled, and hand toggled into memory on the original straight PDP-8
with 4K and one TU55 and TC01, and is the same machine I had to
personally repair by changing each and every one of those worn-out
toggle switches. [I doubt if any other machine ever suffered the
consequences of so much manual input!] [Note: In P?S/8 none of the
actual code was reused, but the structure in the main is a masterpiece
of design of a system to get maximal performance out of truly meager
hardware and make it useful.

The base was a major achievement done under grossly sub-standard
conditions. Once the base was established, it was a matter of major
development to get even more performance. In a crude sense, the
entire development of P?S/8 substantiates Elekman's Theorem and
Burness' corollary. However, the base structure was and still remains
a remarkable achivement for the PDP-8. Richard Lary can be "excused"
for merely coming up with the forerunner, developed by hand, and on a
machine not really otherwise powerful enough to develop itself from in
any other way.

We resorted to using much more powerful machines to advance the
system, but neve forgot the minimum hardware requirement itself! In
any case, for the record, none of this code was ever written by any
DEC employee; it predates Richie's employment at DEC;I never worked
for DEC. Other contributors either never worked for DEC or did what
they did before joining DEC later, etc.

More P?S/8 command considerations.

Thus, the CONSOLe command enjoys the low-overhead aspects of the other
commands that are oriented towards editing the in-memory file, and
thus runs with less overhead. This facility is extended also to
FOCAL, PAL, BIN, GET, BSAVE and other commands. The only restriction
is that each of them must have a unique first two letters as anomaly
situations are not allowed. The R and RUn commands also belong to
this set. [One letter commands would also be unique; currently R is
the only such command.] There is a directory that contains all of the
particulars of these "external" commands as opposed to the "internal"
commands that are in the in-memory list.

An unimplemented command causes a harmless error, after first
performing a small amount of swapping. [Note: The directory for the
files can be unloaded, if so, it gets reloaded later. Thus, as a
restriction, the directory for the external commands that do not
require the R or RUn commands, yet are not actually in-memory
commands, must fit over the 2 pages of that directory, and must be one-
page long; the other page long is the code to actually parse the
command itself. As a result, there is a maximum of 16 such commands,
since each entry in this special internal directory takes 8 words
each. If the keyboard monitor does not get kicked out as a result,
the command is such as the SEARCH command described above.

BATCH itself is invoked this way as well; when it starts up, it also
gets loaded into the same area; eventually the original directory is
loaded in over the system once BATCH or whatever else was using the
two pages for the then-current operation. Since all of the affected
blocks are so close together near the front of the tape, there is
really no significent overhead to any and all of ths worst-case.
[Compare this to virtually any OS/8 operation, running in 8K or more,
with attendant high overhead or worse in the CCL class.]

The scope of the R and RUn commands are far greater, and represent a
part of an open-ended upgrade path for P?S/8. For compatibility, both
can run the same programs as the external command usage, thus:

R PAL

is the same as PAL

and both can have the same exact command switches, files, etc.
optionally passed to them.

However, the R also supports another file structure that can take the
equivalent of an entire OS/8 file, passed through the CONVRT command,
into a single full length file to be passed as a single file, not in
2K editing "chunks" etc. Thus, using the R command, PAL could be
passed as many as 17 potentially huge source files, making the scope
of its ability to assemble source code actually somewhat larger than
OS/8 which is restricted to the concatenation of a somewhat fewer set
of input definitions.

However, as a practical matter, most developers of large programs
never really use the itty-bitty module approach in the PAL world on
either system. People who tend to abuse this are invited to use
MACREL and go out for lunch while waiting for their code to assemble.

As a temporary restriction, the RUn command is just a clone/alias of
the R command, but the intention is to support the non-system P?S/8
handlers and define a more sophisticated file structure past the first
three [the 2K file directory controls blocks past the external command
directory blocks; the external command directory is itself a little
further up the device, but the referenced programs follow it
immediately, then come the 2K files, if any. The rest of the disk
space is presently used by the R directory, but it can also be defined
shorter, or even null; the rest of the device would be where the new
tentative directory would go. The intention is to do something
snazzy, such as requiring 16K minimum, and have subdirectories with
files in 12.4 filename format. OS/8's file structure, at 6.2 filename
format is a bit limiting; these days, everyone has been exposed to 8.3
MS-DOS format or even longer, so something like 12.4 filename format
has been proposed, etc. The non-system handlers themselves are
already defined, but are only used by the excellent BLKCPY program, an
appropriate subject for an independent post.].

Conclusions.

PAL8 has some pathetic restrictions, and OS/8 itself has some others.
While PAL8 itself can [and perhaps should] be rewritten [and be made
more compatible with the P?S PAL extensions, some of which are shared
with PAL10], some other problems stem from the painted-into-the-corner
aspects of some poorly conceived OS/8 restrictions. To fix that
requires reworking some of it, which in turn will break a limited few
programs, or at least relegate them to only work on machines that are
not DECmates for the want of a few nails worth of application rework.

P?S/8 has already shown the way, by totally embracing the DECmates as
an odd, but manageable variant machine capable of running any suitably
modified program. Once repaired, OS/8 [tentatively to be known as OS/
8 Version 5] should be able to be run on all models, eliminating the
"balkanization" of versions that only [attempt to] run on certain
ranges of models, etc.

Implementing the soft abort convention.

I am willing to revamp P?S/8 sufficiently to allow any reasonable
compatibility with something that can be grafted onto OS/8's base; it
is rather hard to make each system's handler have a common location in
memory to do the same exact thing. In any case, here is the current P?
S/8 method that works on any PDP-8 or DECmate:

SFTWRD= 7611 /Where the P?S/8 CORE, ETC. WORD IS

ABRTCHK,KSF /FLAG UP?
JMP NOABORT /NO, JUST KEEP GOING
KRB /YES, WE MUST READ IT IN IF THIS IS A DECMATE
TAD (-3) /COMPARE TO CONTROL-C
SZA CLA /SKIP IF IT IS CONTROL-C
JMP SOMEWHERE /NO, NOT OUR PROBLEM
CLA IAC /NL0001
AND I (SFTWRD) /GET THE STATUS WORD
SNA CLA /SKIP IF SOMEONE ELSE ALREADY SET THE BIT!
ISZ I (SFTWRD) /ELSE SET IT
JMP I (7600) /TAKE THE STANDARD EXIT

This code will work on P?S/8, or on a suitably modified OS/8 that
would support it. Again, it is crucial to find a location that can
reliably be used somewhere in 07600-07777 such that the low-order bit
would preserve the abort condition.

The conventional hardware check would also continue to work.
Unmodified programs would continue to work, but only on non-DECmates.
Each system has to do the dual check of the soft abort bit[s] as well
as the hardware [KSF skips again, KRB reads in the character, clearing
the flag]. In the case of P?S/8, the hardware checks for control-B
also, and the bit[10] of the same word for the soft version, etc.

This would also allow programs to fake an abort: For example, some
lab program could be performing something deemed to have failed
needing to abort. The program could then set the bit itself and
restart at 07600 despite not even hitting any actual physical
keyboard.

Thus, this would be the first change to the long-standing PDP-8
convention and would bring DECmates "into the fold" instead of being
treated as "wierd" relatives.

cjl

0 new messages