/ SBC6120 RAMDISK HANDLER - RLA [25-APR-00] / Copyright (C) 2000 by R. Armstrong, Milpitas, California. / / This program is free software; you can redistribute it and/or modify / it under the terms of the GNU General Public License as published by / the Free Software Foundation; either version 2 of the License, or / (at your option) any later version. / / This program is distributed in the hope that it will be useful, but / WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY / or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License / for more details. / / You should have received a copy of the GNU General Public License along / with this program; if not, write to the Free Software Foundation, Inc., / 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA / This is an OS/8 device handler, either system or non-system - you can / assemble it either way, for the SBC6120 battery backed up RAM disk device. / The SBC6120 RAM disk can contain anywhere from one to four SRAM chips of / 512Kb each for a maximum capacity of 2Mb, which is almost as big as an / RK05! Since the data is actually packed into the SRAMs using the standard / OS/8 "three bytes for two words" packing scheme, we can get the equivalent / of 1344 OS/8 blocks into each 512K chip. / / The SRAM is a strange device by PDP-8 standards since it's actually mapped / into 6120 panel memory space and isn't accessed via IOT instructions. It's / completely inaccessible to normal, main memory, programs like OS/8. This / device device handler works by invoking panel subroutines in the SBC6120 / firmware via the HM6120 PR0 (panel request #0) to transfer data between / RAM disk and main memory. / / The panel firmware and this device handler treat the four SRAM chips as / four separate, and independent, units. This makes it easy to deal with the / case where the RAM disk contains less than the maximum capacity - the units / corresponding to the missing chips are simply "off line". It doesn't make / sense to combine all four chips into a single, huge, virtual disk because / the resulting capacity, 5376 OS/8 blocks, would be larger than OS/8 allows. / You'd just have to split in it half again, the way the RK05 handler does. / / BTW, this has _got_ to be the easiest OS/8 handler I've ever written - it / manages support all four units in a single, one page, handler. This is / true even for the system version - all four units are co-resident with SYS - / and there's still plenty of locations free. Of course, having an 8K ROM / full of code that we can invoke to do all the hard work makes it easy! / / For the record, the calling sequence for the PR0 RAM disk I/O function / is: / / PR0 / DISKRW / panel function code - 1 for RAMDISK I/O / / R/W bit, page count, buffer field and unit / / buffer address / / starting page number (not block number!) / / AC == 0 if success; AC != 0 if error / / If this looks a lot like an OS/8 handler call, that's no accident! Speaking / of which, a standard OS/8 handler call (which is the way we're invoked) is: / / CDF n / where n is the current field / CIF 0 / handlers always live in field zero! / JMS I ENTRY / call the handler entry point / / R/W bit, page count and buffer field / / buffer address / / starting OS/8 block number / / return here if error / / and here if all is well / / Define this symbol (the value is unimportant) to build a system handler, / and leave it undefined to build a non-system handler. SYS=1 / Other useful symbols... VERSION="C&77 / version of this handler DEVTYP=43 / OS/8 device type used by this handler PR0=6206 / HM6120 panel request 0 RAMRW=1 / function code for disk I/O GETRDS=2 / function code for get disk size *0 / start of device header block for BUILD IFNDEF SYS < -4 DEVICE VM01; DEVICE VMA0; DEVTYP^10+4000; UNIT0&177; ZBLOCK 2 DEVICE VM01; DEVICE VMA1; DEVTYP^10+4000; UNIT1&177; ZBLOCK 2 DEVICE VM01; DEVICE VMA2; DEVTYP^10+4000; UNIT2&177; ZBLOCK 2 DEVICE VM01; DEVICE VMA3; DEVTYP^10+4000; UNIT3&177; ZBLOCK 2 > IFDEF SYS < / A 512K SRAM chip holds 1344(10) or 2500(8) OS/8 blocks, and BUILD uses / this information to zero the directory of the system device. This works / fine unless the system device happens to be a 128K chip, in which case / we're stuck. The easiest solution to this problem is to zero the 128K / RAM disk directory first, using the non-system handler, and then BUILD / a system on it without letting BUILD zero it. -4 DEVICE VM01; DEVICE SYS; DEVTYP^10+4000; UNIT0&177+2000; 0; 2500 DEVICE VM01; DEVICE VMA1; DEVTYP^10+4000; UNIT1&177+1000; 0; 2500 DEVICE VM01; DEVICE VMA2; DEVTYP^10+4000; UNIT2&177+1000; 0; 2500 DEVICE VM01; DEVICE VMA3; DEVTYP^10+4000; UNIT3&177+1000; 0; 2500 > / How to boot OS/8 (there's lots of documentation on how to write a device / handler, even a system handler, but I couldn't find a description of how / to make a bootable device anywhere!): / / The primary bootstrap for a device (the one which you have to toggle in / with the switches!) normally loads cylinder 0, head 0, sector 0 (which is / the equivalent to OS/8 logical block zero) into memory page zero field zero. / The code loaded into page zero is then started in some device specific way - / usually the primary bootstrap is overwritten by this data and the CPU just / "ends up" there. / / The first few words of block zero are called the secondary bootstrap, and / that's what you'll see in this file a little later. OS/8 BUILD writes the / secondary bootstrap to the first part of block zero when it builds the system / device. The second half of block zero contains the system handler (i.e. / what you're reading in this file!) plus some OS/8 resident code that BUILD / wraps around it. All of the second half of block zero needs to be loaded / into page 7600, field 0 by the secondary bootstrap. / / The remainder of the first half of block zero, the part after the secondary / bootstrap, contains the OS/8 resident code for field 1. This starts some / where around offset 47 (I never could find out for sure) in the first half / of block zero, and this code needs to be loaded into the corresponding / locations of page 7600, field 1. The remaining words in page 7600, field 1 / (i.e. those that correspond to locations used by the secondary bootstrap) / OS/8 uses for tables and their initial values are unimportant. It suffices / to simply copy all of the first half of block zero to page 7600, field 1. / / All this discussion presupposes a single page system handler, as we have / here. For a two page handler BUILD will put the second page in the first / half of block 66 on the system device and I believe (although I can't / guarantee it) that the second half of this block also contains an image / of the OS/8 resident code at page 7600, field 1. This would make it the / same as, excepting the bootstrap part, the first half of block zero. In / the case of a two page handler, the secondary bootstrap is also responsible / for loading the second handler page from block 66 into page 7600, field 2. / OS/8 bootstrap code (secondary bootstrap). / / Once everything has been loaded, the secondary bootstrap can simply do a / "CDF CIF 0" and then jump to location 7600 to load the keyboard monitor. / The primary bootstrap for the SBC6120 RAM disk is six words loaded in / locations 0 thru 5: / / 0000/ 6206 PR0 / execute a panel request / 0001/ 0001 RAMRW / RAM disk I/O / 0002/ 0100 0100 / read one page into field zero / 0003/ 0000 0000 / location zero / 0004/ 0000 0000 / from page zero of the device / 0005/ 7402 HLT / should never get here / / If all goes well, the HLT in location 5 is never executed - it gets / overwritten by the secondary bootstrap code before the ROM returns from / the PR0 function. / / Here's the secondary, OS/8 specific, bootstrap: IFDEF SYS < BOOT-ENDBT / legnth of the boot code, for BUILD RELOC 0 / The first five words of the secondary bootstrap overlay the primary boot / and are never actually executed. They should contain the word "BOOT" in / ASCII text to tell the SBC6120 ROM boot sniffer that this is a bootable / volume... BOOT, "B; "O; "O; "T; 0 PR0 / replaces the HLT instruction in the primary boot RAMRW / ... 0100 / read one page into field 0 7600 / page 7600 0001 / from the second half of block zero SZL CLA / this shouldn't fail, but... HLT / just give up if it does PR0 / do a second read operation RAMRW / ... 0110 / this time read one page into field 1 7600 / page 7600 0000 / from the first half of block zero SZL CLA / ... HLT / ... CDF CIF 0 / this is probably unnecessary JMP I .+1 / go load the keyboard monitor 7600 / ... ENDBT=. RELOC > *200 / end of header block for BUILD / Note that the entry point offsets for a handler are not arbitrary - the / OS/8 USER overlay requires that every file structured handler have a UNIQUE / entry point. This means, for example, that our entry points can't be at / the same offset as those used by the RX01, RK8E, RL01, or any other file / structured handler! / / For a system handler, the entry point for unit 0 (or which ever one is to / be SYS:) has to be at offset 7. And also, for a system device, those first / six words before offset 7 are off limits and we can't use them - BUILD fills / them in with code to load the KBM. IFNDEF SYS <*210> IFDEF SYS / Primary entry point (for unit 0)... UNIT0, VERSION / the convention is to put our version here CLA / clear the AC DCA DSKFUN / and set the disk unit number to zero MAKCXF, RDF / get the caller's data field TAD KCXF / and make a CXF instruction out of it DCA RETCXF / save that for when we return / Do a standard control-C check before we get started... TAD K200 / ignore the parity bit from the console KRS / read the buffer, but don't clear the flag TAD KM203 / is there a ^C in the buffer? SNA CLA / ??? KSF / yes - is the flag also set? JMP NOCTLC / nope - go start the I/O CDF CIF 0 / yes - return to the keyboard monitor JMP I K7600 / immediately / Use the caller's arguments to set up an argument list for PR0... NOCTLC, TAD I UNIT0 / get the caller's first argument word ISZ UNIT0 / (and point to argument 2) AND K7770 / save just the R/W, count and field bits TAD DSKFUN / and put the unit in bits 9-11 DCA DSKFUN / save that in the argument list for PR0 TAD I UNIT0 / get the caller's buffer address ISZ UNIT0 / (point to argument 3) DCA DSKBUF / save that for PR0 TAD I UNIT0 / get the caller's OS/8 block number ISZ UNIT0 / (point to the error return) CLL RAL / convert OS/8 blocks to pages DCA DSKPAG / and save that for the PR0 call / A little known (at least little known to me!) feature of OS/8 PIP is that / the /Z (zero directory) option tries to determine the device size by calling / the handler for a dummy read operation with -111(8) as the block number. / It expects the handler to take the error return with the negative of the / device size in the AC. SNL / was the caller's block number negative? JMP DOIO / nope - go do the I/O TAD DSKFUN / get the unit number AND K7 / and only the unit PR0 / ask the ROM how big this unit is GETRDS / ... CLL RTR / convert pages to blocks CIA / make the result negative JMP RETCXF / and take the error return now / Call the panel ROM to actually do all the work... DOIO, PR0 / trap to panel memory RAMRW / panel request function code - never changes DSKFUN, 0 / DISK I/O subfunction code DSKBUF, 0 / address of the caller's buffer DSKPAG, 0 / page number (not block number!) for I/O SNL CLA / did the call succeed? ISZ UNIT0 / yes - give the successful return RETCXF, HLT / gets a CDF CIF to the caller's field JMP I UNIT0 / we're all done / Entry point for unit #1... UNIT1, VERSION / ... CLA CLL IAC / load the AC with 1 DCA DSKFUN / and set the unit number TAD UNIT1 / save the return address in the same place SETADR, DCA UNIT0 / ... regardless of the entry point JMP MAKCXF / ... / Entry point for unit #2... UNIT2, VERSION / ... CLA CLL CML RTL / load the AC with 2 DCA DSKFUN / and set the unit number TAD UNIT2 / ... JMP SETADR / ... / Entry point for unit #3... UNIT3, VERSION / the convention is to put our version here CLA CLL CML RAL IAC / load the AC with 3 DCA DSKFUN / and set the unit number TAD UNIT3 / get the argument list and return address JMP SETADR / and join the regular code / Constants. Note that in a system handler the last few words of the page / are reserved by OS/8 and the handler can't touch them, so that means we / don't dare use literals! K200, 200 KM203, -203 KCXF, CDF CIF 0 K7600, 7600 K7770, 7770 K7, 7 $