Being a rather simple “blinkenlights” program, this toggle toy is the smaller of the two. (The puzzle setup for LightsOut wound up becoming rather complex.) So this is probably a useful example for someone who is mildly curious about the look of PDP-8 assembly language source code.
Since most SBC6120/FP6120 owner interest will likely be in equipping systems with this front panel utility, the source code can be skipped by jumping down to the "Installing DeepThought into your SCB6120/FP6120" section below.
PDP-8 / SBC6120/FP6120 “DeepThought” Source Code
/=============================================================================== / "Deep Thought" | The Ultimate PDP-8 Front Panel "Blinkenlights" Program / | SBC6120 version assuming 32K RAM, KL8, and no EAE present. /------------------------------------------------------------------------------- / / This reads the front panel switch register to determine the speed and speed / variation of the flashing lights. It implements a simple but sufficient linear / congruential pseudo random number generator (LCPRNG) to generate visually / unpredictable values for the AC and Memory Address to contain while we wait / for the next serial port (KL8) interrupt. (See detailed explanation below.) / /------------------------------------------------------------------------------- / Contemporary PDP-8 FREEWARE written and placed / into the PUBLIC DOMAIN by Steve Gibson, 2009 /=============================================================================== / Implementation notes - This program uses an SBC6120/FP6120-specific feature / that will not work on a regular PDP-8: The program manually takes over control / of the data lights. This is done because the 30hz sampling of the accumulator / sometimes catches the AC at a bad time causing the display to flicker annoyingly. / This would NOT be a problem on a real PDP-8, since its AC display is not being / sampled and held. CALLBIOS=6206 / Call SBC6120 BIOS function, function code follows call LIGHTS=12 / BIOS function to take over the data display lights / Since we don't have programmatic control over the memory lights, if we want to / control them it's necessary for us to actually GO somewhere and remain there. / This program functions by filling fields 1-7 (not field 0) with nothing but / "JMP ." (jump to yourself) instructions. Thus, when we jump somewhere we loop / at that single location until a serial port interrupt brings us back to location / zero. At that point we determine whether we've waited long enough, and either / immediately return to waiting at the same location, or generate new randomness. RTF=6005 / This "Restore Flags" opcode, which we are defining here, / is unknown to the PAL-8 assembler because it is only / valid when the PDP-8 is equipped with extended memory. / It is used for cross-field interrupt returns by loading / fields of the AC into the extended memory field regs. / whose effects are deferred until the next jump. inst. /------------------------------------------------------------------------------- *0 / load at loc 0 IntReturn, 0 / interrupt return loc / this is our super-minimal inner loop to keep the address / lights as fully on or off as possible. We do the bare minimum / here in order to get right back to our waiting location ISZ AccumLow / bump our inner-loop counter & probably JMP Continue / get back to waiting immediately... CLA OSR / get the current switch register CIA / negate it for a change detection compare TAD LastSwitchReg / for mid-delay changes in the timing SZA / if no change, just keep delaying JMP ChangeLights / the register changed, abort this cycle ISZ AccumHigh / bump our outer-loop counter JMP Leave / restore values and resume waiting / either our time is up for changing the lights (with both the / inner "AccumLow" loop and the outer "AccumHigh" loop having / wrapped around to zero) ... *OR* the user has changed the / switch register to change the blinking characterization, in / either event, we compute a new set of lights and light-delay. ChangeLights, JMS GetRand / get a new random value DCA IntReturn / save new waiting location. we will / return indirectly to this location / once everything else is set up JMS GetRand CALLBIOS / call the SBC6120 BIOS LIGHTS / to set the data register lights 1 / under our program's control JMS GetRand / establish the next instruction field AND CInstField / retain only bits 6-8 (IF) SNA / if we're not in page 0 TAD C0010 / collision possible, so set to field 1 TAD C0200 / turn on the Interrupt Enable bit /-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- SKP / skip over 0026, our startup entry. Our / funky SBC6120 OS/8 boot requires this JMP RunFromHere / fixed 0026 entrypoint, but it's easily / accommodated with a SKP instruction /-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- DCA LoopField / set next loop field to 4 OSR / read the switches DCA LastSwitchReg / save for mult & mid-delay testing TAD LastSwitchReg / get the switch reg AND C0077 / mask the six speed control bits IAC / make sure it's not zero DCA SpeedControl / save for the random # multiply TAD LastSwitchReg / get the switch register again BSW / swap 6-bit halves AND C0077 / keep the lower 6 bits DCA SpeedVariation / save the amount of variation JMS GetRand / pickup another 12-bit random number AND SpeedVariation / retain only the lower specified bits IAC / now we have AC = 1 to ... JMS MUY / Multiply: AC <- LSB (AC * [.+1]) SpeedControl, 0 / the multiply subroutine's multiplicand CMA / 1's compliment for an up-counter DCA AccumHigh / save the new outer-loop delay count Leave, TAD CBaudDelay / reset our inner-loop counter DCA AccumLow Continue, TLS / clear interrupt and start sending / a (nother) character out to the TTY TAD LoopField / put the RTF's memory field into AC RTF / set the restored pending field & ION JMP I IntReturn / return to the place we'll be waiting / with memory field and location set / GetRand ---------------------------------------------------------------------- / This is the simplest way I know of to generate highly random / looking 12-bit values. It's a Linear Congruential Pseudo Random / Number Generator (LCPRNG). Each time it's called, it evaluates / the expression: NextRand = LastRand * 5545 + 541 (all octal) GetRand, 0 / subroutine return TAD LastRand / get the last PRNG value JMS MUY / multiply by the following constant: 5545 / 2917 base 10 - LCPRNG multiplicand TAD CRandAdd / sum in our LCPRNG addend DCA LastRand / save this for next time TAD AccumHigh / return the HIGH 12-bits as our result JMP I GetRand / return the AC to the caller / MUY -------------------------------------------------------------------------- / This is a full 12x12 multiply, needed because the HD6120 PDP-8 / emulation chip used by the SBC6120 inexplicably lacks the EAE / "Extended Arithmetic Element" multiplier. Annoying as this is, / it does mean that these "ToggleToys" will be usable on ALL real / PDP-8 systems, including those without EAE's. / / On Entry: AC contains Multipler & the word after the call has Multiplicand / Return: least significant 12-bits in AC, most significant in AccumHigh. MUY, 0 / subroutine return DCA Multiplier / save the multiplier for shifting TAD C7764 / setup our -12 loop counter DCA PhaseCount DCA AccumLow / clear our 24-bit results accumulator DCA AccumHigh MuyShift, TAD Multiplier / get a bit from the multiplier CLL RAL / move the high-bit into LINK DCA Multiplier / put the updated multiplier back SNL / we do need to add-in the multiplicand JMP Iterate / no multiplicand add-in TAD I MUY / add the multiplicand into accumulator TAD AccumLow / this *may* overflow, clearing the LINK DCA AccumLow / either way, put the updated low 12 back SNL / if LINK is still '1', no overflow ISZ AccumHigh / bump the high-half if we carried out Iterate, ISZ PhaseCount / see whether we've done all 12 bits JMP Shift24 / not done, so shift and iterate again CLL CLA / return the lower 12-bits in AC TAD AccumLow ISZ MUY / return to the instruction after multiplier JMP I MUY Shift24, TAD AccumLow / get the lower 12-bit half CLL RAL / shift it left, high bit into LINK DCA AccumLow / put back the new low half TAD AccumHigh / get the upper 12-bit half RAL / shift it left, LINK into low bit DCA AccumHigh / put back the new high half JMP MuyShift /------------------------------------------------------------------------------ / At program startup we call the SBC6120's BIOS to inform it that we'll be / taking over display of the data lights and to shut-down its 30hz sampling / of the AC. We then fill the seven 4K word memory fields (1-7) with nothing / but "JMP ." (jump to yourself) instructions so that when we jump out to any / such location we'll simply remain there (with the memory address lights frozen) / until we're yanked back by the completion of the serial port character that we / always start sending just before we jump out to never-never land. RunFromHere, CLA DCA LastRand / setup the start of our filling TAD CFirstDatField / init to data field zero DCA NewDataField NewDataField, CDF 10 / set our new storage target data field CLA / (need to clear after switching fields) / fill memory with current page "JMP ." instructions FillMem, TAD LastRand / get the next target location AND C0177 / keep only the target loc's page bits TAD CJmpCurrentPg / convert into a "JMP ." instruction DCA I LastRand / save the "JMP ." inst @ TargetLoc ISZ LastRand / bump to next target location JMP FillMem / we've wrapped into the next 4K word field TAD NewDataField / get the current data field TAD C0010 / bump to the next data field DCA NewDataField / save the next data field TAD NewDataField / get back the new data field AND CInstField / did we wrap out of the last one? SZA / if zero, we're all done JMP NewDataField / let's keep filling in the next field CDF 00 / and leave us back at data field 0 JMP ChangeLights / ... and off we go ... /------------------------ Initialized Constant Values ------------------------- C0010, 0010 / constant 0010 for data field inc C0077, 0077 / constant 0077 for masking 6-bits of SW C0177, 0177 / constant 0177 for masking page bits C0200, 0200 / constant 0200 for RTF's ION bit C7764, 7764 / constant 7764 for -12 multiply counter CInstField, 0070 / GTF/RTF instruction field bitmask CRandAdd, 541 / 353 base 10 CJmpCurrentPg, 5200 / inst for jmp to current page loc 0 CFirstDatField, CDF 10 / "change to data field 1" CBaudDelay, 7770 / baud rate compensation value / 7740 - 38400 / 7760 - 19200 / 7770 - 9600 baud / 7774 - 4800 / 7776 - 2400 / 7777 - 1200 or slower /-------------------------- Uninitialized Variables --------------------------- SpeedVariation, 0 / 6-bits of switch register PhaseCount, 0 / our multiplier-shift counter AccumLow, 0 / low 12-bits of 12x12 mult AccumHigh, 0 / high 12-bits of 12x12 mult Multiplier, 0 / temp used by multiplication LoopField, 0 / the memory Field while waiting LastRand, 0 / our previous random value LastSwitchReg, 0 / the last SW Reg reading /------------------------------------------------------------------------------ $ The assembled listing for this source code, DeepThought.lst, is also available. |
Due to the size of the OS/8 operating system, and the fact that three hours of 9600 baud transfer is required to move it in hex ASCII format from a PC to the SBC6120, I wrote the OS/8 Windows utilities to make that process instantaneous by allowing the SBC6120's IDE drive to be attached through any means available to a Windows system, and to then perform a raw physical transfer of the OS/8 partition data to the SBC's drive.
However, the “DeepThought” and “LightsOut” toggle toys, the custom boot sector, and the sector read/write utilities are all short enough to be feasibly entered through a terminal console. This also means that there is no need for a Windows-centric approach, so Apple Mac and Linux/Unix users will not be disadvantaged.
The following instructions will allow you to deposit the “DeepThought” program in RAM, then write it to any one or more IDE drive partitions of your choosing. Then the modified multi-boot sector will allow you to load and run the program any time you wish using only the front panel switches. This code was developed on SBC6120's having an attached eight gigabyte drive, yielding more than 4300 (octal) partitions. So, for ease of switch register use, the toggle toys were written to partitions 4000 and 4001 (octal). Before proceeding, you should determine where — into which partition(s) — you wish to write these toggle toys. (You should probably have already installed the custom multi-boot sector before installing the “DeepThought” and “LightsOut” toggle toys since their subsequent loading and running requires the multi-boot sector.)
If the SBC6120 is currently running (the front panel RUN light is illuminated), briefly lower and raise the front panel HALT switch to return control to the BIOS and console. Establish communication with the BIOS so that pressing “Enter” on your terminal or terminal emulator returns the BIOS '>' prompt.
Begin by either resetting the SBC6120 or performing a master reset (mr) command to bring the SBC6120 into a known state:
>mrThen clear all of memory. This will take a few seconds, during which the front panel's address register will count all the way up to 77777 as zeroes are being written into the PDP-8's RAM:
>cmYou can verify that memory has been cleared by displaying the first sector's-worth of RAM with the examine memory (e) command. You'll receive a nice block display of zeroes, not shown here:
>e 0-377Now we need to deposit the DeepThought program into RAM. The commands to do this are a series of deposit (d) commands shown below. You can enter each line manually, but terminal emulation programs often understand “copy & paste” semantics which make copying the commands from this page and pasting them into the terminal for sending to the SBC6120 much quicker, easier, and less error prone.
If you are a Windows user, the free, open source, and very nice “Tera Term” terminal emulator can be used. With Tera Term, the keystroke combination “Alt-V” will paste the Windows' clipboard into the terminal window while simultaneously sending it to the SBC6120, exactly as if it had been manually entered. (Here is a locally archived copy in case the file's home site ever disappears: TeraTerm_v2.3.zip.)
The following series of SBC6120 deposit (d) commands will place the DeepThought program into SBC RAM:>d 00000 0000,2165,5053,7604,7041,1172,7440,5012 >d 00010 2166,5051,4057,3000,4057,6206,0012,0001 >d 00020 4057,0156,7450,1151,1154,7410,5124,3170 >d 00030 7404,3172,1172,0152,7001,3046,1172,7002 >d 00040 0152,3163,4057,0163,7001,4067,0000,7040 >d 00050 3166,1162,3165,6046,1170,6005,5400,0000 >d 00060 1171,4067,5545,1157,3171,1166,5457,0000 >d 00070 3167,1155,3164,3165,3166,1167,7104,3167 >d 00100 7420,5107,1467,1165,3165,7420,2166,2164 >d 00110 5115,7300,1165,2067,5467,1165,7104,3165 >d 00120 1166,7004,3166,5075,7200,3171,1161,3130 >d 00130 6211,7200,1171,0153,1160,3571,2171,5132 >d 00140 1130,1151,3130,1130,0156,7440,5130,6201 >d 00150 5012,0010,0077,0177,0200,7764,0070,0541 >d 00160 5200,6211,7770Verify the accuracy of the data you just entered by asking the BIOS to compute the checksum (ck command) of the first two PDP-8 pages of RAM, locations 00000 through 00377:
>ck 0-377You should receive the reply:
Checksum = 3116Next, we need to write these first two pages of PDP-8 RAM to non-volatile storage so that we can reload and run it at any time with just a few flips of our front panel switches. The partition writer (PartWrite) code given below is quite short. Its source code and documentation can be found on the custom OS/8 boot sector page. So, as before, deposit the partition writer into RAM (starting at location 00400) using the following sequence of SBC6120 BIOS deposit (d) commands:
>d 00400 7604,7450,7402,3223,6206,0010,0000,0423 >d 00410 4000,0027,0001,6206,0004,4207,0000,0000 >d 00420 7430,7402,7402With the partition writer code in RAM at location 00400, we're ready to write DeepThought to its own partition. FIRST set the front panel data switches to the address of the target partition. If you have a very large drive, you might use something like 4000, but 0007 or 0010—or whatever—would be fine too). Verify that the front panel's “HALT” switch is UP (not enforcing a halt), then, with the front panel switches set to the target partition, start the partition writer with the BIOS start (st) command:
>st 0400Note that to prevent any chance of inadvertently overwriting partition zero, where most users will have installed the OS/8 operating system, the little partition writer will not write to partition zero. So if the switches were inadvertently set to 0000, you will receive:
?Halted at 00403 PC>0403 PS>1000 AC>0000 MQ>0000 SP1>0000 SP2>0000
(That's NOT what you want.)
With a non-zero partition number set into the switches, you should receive:?Halted at 00423 PC>0423 PS>1000 AC>0000 MQ>0000 SP1>0000 SP2>0000You can (and should) verify that the two-page sector was written to the IDE drive by dumping the target partition's first sector with the following command, where <partition> is replaced with the number of the partition where the sector was written:
>dd <partition> 0 1So, for example, assuming that “DeepThought” had been saved into partition 4000, you would give the following command and receive the following dump:
>dd 4000 0 1 0000.000/ 0000 2165 5053 7604 7041 1172 7440 5012 0000.010/ 2166 5051 4057 3000 4057 6206 0012 0001 0000.020/ 4057 0156 7450 1151 1154 7410 5124 3170 0000.030/ 7404 3172 1172 0152 7001 3046 1172 7002 0000.040/ 0152 3163 4057 0163 7001 4067 0000 7040 0000.050/ 3166 1162 3165 6046 1170 6005 5400 0000 0000.060/ 1171 4067 5545 1157 3171 1166 5457 0000 0000.070/ 3167 1155 3164 3165 3166 1167 7104 3167 0000.100/ 7420 5107 1467 1165 3165 7420 2166 2164 0000.110/ 5115 7300 1165 2067 5467 1165 7104 3165 0000.120/ 1166 7004 3166 5075 7200 3171 1161 3130 0000.130/ 6211 7200 1171 0153 1160 3571 2171 5132 0000.140/ 1130 1151 3130 1130 0156 7440 5130 6201 0000.150/ 5012 0010 0077 0177 0200 7764 0070 0541 0000.160/ 5200 6211 7770 0000 0000 0000 0000 0000 0000.170/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.200/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.210/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.220/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.230/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.240/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.250/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.260/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.270/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.300/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.310/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.320/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.330/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.340/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.350/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.360/ 0000 0000 0000 0000 0000 0000 0000 0000 0000.370/ 0000 0000 0000 0000 0000 0000 0000 0000 4266The “4266” at the end is a checksum of the block just dumped, and it (and everything else) should match what's shown above.
You're all done!
You now have the “DeepThought” program safely written, and verified, to an IDE drive partition of your choosing. If you have already installed the modified FlexBoot sector, you can fire up the “DeepThought” toggle toy by setting the target partition into the switch register then depressing the front panel “BOOT” switch twice in succession.
Have fun enjoying all of the variations made possible
by DeepThought's characterization switch settings.
If your serial terminal is NOT set for 9600 baud, you will probably want to make a change in the program: DeepThought gets its underlying timing from serial terminal interrupts. It needs to do this so that it's able to spend most of its time idling out at various locations in RAM waiting for an interrupt to bring it back to the program to see whether it has waited long enough.
If you look near the end of the source code you'll find a label CBaudDelay ('C' stands for Constant). This is an inner loop “up-counter” whose initial value can be tuned to fit the baud rate of individual SBC6120 serial ports. To make it easy to find, the counter initialization value is the last non-zero word of the program. You can see it's default value of '7770' in the dump block above . . . at location 0162 of the program. You can also see it at the end of the program's file listing. The source code, above, shows which values can be used to compensate for specific baud rates.
This ZIP file (352kb) contains all of the recently-written bits and pieces of SBC6120 & FP6120 PDP-8 code for DeepThought, LightsOut, the custom OS/8 boot sector, and other PDP-8 code snippet utilities I created for this project. |
CLICK HERE to learn how YOU can acquire & build one of these complete PDP-8 systems for yourself !!! |
Gibson Research Corporation is owned and operated by Steve Gibson. The contents of this page are Copyright (c) 2024 Gibson Research Corporation. SpinRite, ShieldsUP, NanoProbe, and any other indicated trademarks are registered trademarks of Gibson Research Corporation, Laguna Hills, CA, USA. GRC's web and customer privacy policy. |
Last Edit: Jan 27, 2016 at 17:58 (3,154.35 days ago) | Viewed 7 times per day |