Minskytron annotated / source: http://textfiles.com/bitsavers/bits/DEC/pdp1/from_peter_samson/dpys5.mac / all additional annotations by me, Norbert Landsteiner, prefixed by "//". // Code for PDP-1 Macro assembler // PDP-1 characteristics: // 18-bit words, 1's complement, deposit AC (dac) non-destructive, all values octal // address parts of instructions in lower 12 bits - absolute address range (0..7777) // opcodes in highest 2 nibbles of instruction word (bit 5 is defer bit "i", here unused) // shift instructions are micro-coded, number of high bits give number of bit-positions // as encoded in constants "s1" (1 = 1 hi-bit) .. "s9" (0777 = 9 hi-bits). // AC and IO may be combined for shift instructions to form a temporary 36-bit register // display instruction "dpy" displays a dot at x = AC, y = IO (only highest 10 bits used) // display is -512..+512 with origin at center, -x = left, -y = top. // "dpy-i" modifies the display instruction not to wait for the completion pulse from the // display,normally triggered after 50 microseconds. Since the code takes longer to run // in between display commands, there's no need to wait in this particular program. // timing: memory cycle = 5 microseconds // internal instruction = 1 mem-cycle, address-lookup adds another one (as would do a defer) // i.e.: cla (clear AC, internal instr) = 5 us, lac addr (memory instr) = 10 us, // see http://www.masswerk.at/spacewar/inside/pdp1-instructions.html for details. // The Minskytron interconnects 3 oscillators based on a code for displaying circles // as discovered by Marvin Minsky: // > Here is an elegant way to draw almost circles on a point-plotting // > display: // > // > NEW X = OLD X – epsilon * OLD Y // > NEW Y = OLD Y + epsilon * NEW(!) X // > // > This makes a very round ellipse centered at the origin with its size // > determined by the initial point. epsilon determines the angular velocity // > of the circulating point, and slightly affects the eccentricity. If // > epsilon is a power of 2, then we don't even need multiplication, let // > alone square roots, sines, and cosines! The "circle" will be perfectly // > stable because the points soon become periodic. // > // > The circle algorithm was invented by mistake when I tried to save one // > register in a display hack! Ben Gurley had an amazing display hack using // > only about six or seven instructions, and it was a great wonder. But it // > was basically line-oriented. It occurred to me that it would be exciting // > to have curves, and I was trying to get a curve display hack with // > minimal instructions. // "Item 149 (Minsky): Circle Algorithm" in HAKMEM, // http://www.inwap.com/pdp10/hbaker/hakmem/hacks.html / start at 500 for Minskytron / (uses TW) // initially read the testword and swap contents into IO // (IO will be unaffected by computations, so it serves here as an 18-bits store) 500/ lat // read testword (18 bits) into AC rcl 9s // two rotational shifts by 9 bits across combined AC and IO registers rcl 9s // effect: swap AC and IO, testword contents now in IO // assemble a shift instruction for every 3 bits of the contents of IO (from testword) m1, jsp gsh // call subroutine gsh to get first shift instruction (in AC) dac sh0 // store it in address sh0 (osc. #1, x-factor) jsp gsh // get next shift dac sh1 // store it in sh1 (osc. #1, y-factor) jsp gsh // get next dac sh2 // store it in sh2 (osc. #2, x-factor) jsp gsh // get next dac sh3 // store it in sh3 (osc. #2, y-factor) jsp gsh // get next dac sh4 // store it in sh4 (osc. #3, x-factor) jsp gsh // get next dac sh5 // store it in sh5 (osc. #3, y-factor) // set up x/y values of oscillators from initial values table m2, lac xa0 // load contents of xa0 dac xa // store it in xa lac xb0 // same for xb, xc, ya, yb, and yc dac xb lac xc0 dac xc lac ya0 dac ya lac yb0 dac yb lac yc0 dac yc // set up complete // main loop, consisting of code to drive 3 interconnected oscillators m3a, lac xa // osc. #1, load contents of xa add xb // add contents of xb to it (x from osc. #2) xct sh0 // execute shift instruction stored in sh0 add ya // add contents of ya to it dac ya // deposit contents of AC in ya sub yb // subtract contents of yb (y from osc #2) xct sh1 // execute shift in sh1 cma // complement AC add xa // add contents of xa dac xa // deposit contents of AC in address xa lio ya // load contents of ya into IO (y-coor for display) dpy-i // display a dot at x = xa, y = ya (only highest 10 bits significant) // range: -512 .. +512 (display 1024 x 1024, origin at center, -x left, -y: top) // so, we just calculated // ya += (xa + xb) >> sh0; xa -= (ya - yb) >> sh1; // and display it at ( xa >> 8 | ya >> 8 ) m3b, lac xb // osc #2: yb += (xb - xc) >> sh2; xb -= (yb - yc) >> sh3; sub xc // here we subtract the x from osc #3 (is add above)! xct sh2 add yb dac yb sub yc xct sh3 cma add xb dac xb lio yb dpy-i // display it at ( xb >> 8 | yb >> 8 ) m3c, lac xc // osc #3: yc += (xc - xa) >> sh4; xc -= (yc - ya) >> sh5; sub xa // subtraction as in osc. #2 xct sh4 add yc dac yc sub ya xct sh5 cma add xc dac xc lio yc dpy-i // display it at ( xc >> 8 | yc >> 8 ) jmp m3a // end of main loop, jump to osc. #1 and redo // here we extract the next 3 bits from the testword contents (in IO) // by rotating them into the 3 highest bits of a previously clean AC // this serves as an offset to addr gst (stored in gsc), thus selecting // an instruction for an arithmetic shift to the right by 1 to 8 bits, // put in AC as we return from the subroutine gsh, dap gsx // (subroutine) deposit return address in address part of gsx cla // clear AC rcl 3s // rotate combined AC and IO right by 3 bits (get next 3 bits into AC) add gsc // add contents of gsc to it (result: addr of gst + offset 0..7) dap .+1 // put it in address part of next location lac . // execute "load contents of address into AC" (get shift instr from table) gsx, jmp . // return gsc, gst // addr of table (starting directly below, used above) gst, sar 1s // here's a table of shift instructions to load sar 2s // containing instr. for right shifts by 1 to 8 bits sar 3s sar 4s sar 5s sar 6s sar 7s sar 8s sar 9s / not used, but was in orig. // end of shift-assembly routine // constants for initial values for oscillators // encoded as instructions, but used as numerical values. // (this may be the result of a disassembly?) // // these initial positions give the corners of an upright triangle // with the base through the center origin of the screen (+): // // b (0 | 0100) // // // (-040 | 0) a + c (040 | 0) // // (points in screen coordinates = 10 most significant bits of 18-bit words) xa0, dpy i 17770 // (730007 + 10000) + 17770 = 757777 = -020000 (1's complement!) xb0, 0 // 0 xc0, and // 020000 ya0, 0 // 0 yb0, ior // 040000 yc0, 0 // 0 // and here are the addresses for these variables xa, 0 xb, 0 xc, 0 ya, 0 yb, 0 yc, 0 // space for assembled shift instructions (x/y modifying factors) sh0, xx // values inserted by program at runtime sh1, xx // "xx" actually resolves to 760400, same as "hlt" (halt) sh2, xx sh3, xx sh4, xx sh5, xx start 500 // end of program (not in original code) // eof