Thursday, July 26, 2018

VAX/VMS Logical Name Hash Table Statistics

            Logical Name Hash Table Statistics

  VMS (and RSTS, and RSX, for that matter) provides a logical
name service, by which a string is mapped into one or more
"equivalence" strings. These logical names can be used in place
of a literal file or device specification to provide device
independence. This allows programs and data to be moved from one
device or system to another without requiring extensive
modification. They are also used to present the user with a
shorter, more manageable "abbreviation" of an actual device or
file specification.

  Everyone is, by now, pretty much sold on the value of logical
names in VMS. Many  years ago, at one of my larger sites, the
Technical Support group (the group responsible for production
system standards at this site) set a policy that ALL access to
files and devices in production systems would be by logical
names. This seemed like a reasonable idea to me, since the tape
and disk farm there tended to change configuration every few
months or so, and the users' systems were wont to drift around
from disk to disk, displaying a tropism for free space. A strict
logical name policy allowed them to change the locations of
files that were used by thousands of users without causing any
problems.

  When we started out, we elected to create any logical names
used by more than a few individuals as system wide logicals,
created at system startup. This, we reasoned, would prevent
wasting processor time creating and deleting them as process
logicals each time a user entered an application. It would also
save space, since we would have one copy of each logical,
instead of hundreds. At that time, I made a cursory
investigation of the possible impact of such a tactic. All
logical names are allocated in Paged Pool. No problem there - we
would just make sure we SYSGENed it to be large enough. I also
found that the names were located by hashing, so I reasoned that
a large number of them should not affect the time required to
locate one, since hashing tends to be much less affected by the
number of items to be searched than, say, a linked list.

  But, that was several years ago, and a great many applications
have been written there since then. With thousands of logical
names being maintained, I thought that a more detailed look for
any potential bottlenecks in this process might be in order. An

examination of the data structures involved brought to light
some interesting facts. It turns out, that all of the many
logical name tables (both system and user provided) are, in a
sense, an illusion. Typically, we think of a table as a
structure that is used to locate and select something. Logical
names are located independently from the logical name table they
reside in - AFTER being located, the system checks what logical
name table they are in, to see if it has located the correct
logical name.

  Logical names are actually located by means of hash tables (do
not confuse logical name hash tables with logical name tables).
There is a system wide hash table used to locate shared logical
names - those accessible by more that one process. Each process
has its own process private hash table. The residency of a
logical name in a logical name table (eg, LNM$_PROCESS) can be
considered to be "virtual". The logical name is first located in
one of the hash tables, and after being located, its "residence"
in a logical name table is evaluated.

  A hash table is a "semi" random access technique for storing
and retrieving data. The principle behind it is simple - that it
is very efficient to access an entry in an array, given the
index of the desired item. In the real world, some elaboration
on the idea is necessary for two reasons. The first is that most
data do not have a simple numeric quality that can be used as an
array index, so it becomes necessary to generate one. The
second, is that, in most cases, the set of possible items to be
stored is much larger than the set that actually occurs, so
allocating space in an array for every possible item would be
very wasteful.

  The index into the table is generated by applying some
algorithm to the data, to produce an integer. Typically, the
algorithm "hashes" up the data, in an attempt to produce a
random distribution of indices. Since the table is smaller that
the number of possible items, it is inevitable that some items
will generate the same index (will "hash" to the same address,
thus causing a "collision").

  A method often used to resolve these collisions (and, indeed,
the very one used in the logical name hash tables) is to have
each entry in the hash table contain the listhead of a linked
list. As items are entered in the table, all entries that hash
to the same index are linked onto the same linked list. When
looking up an item, the hash address will direct you to the
appropriate listhead. The list can then be traversed to find the
item you want. This is referred to as collision resolution by
chaining.

  The efficiency of a hash table using collision resolution by
chaining is measured by how many "probes" are necessary to
locate an item. A probe is considered to be a comparison of an
item in the linked list with the search criteria to determine if
it is the one we are after. Thus, if we locate a linked list and
find the item we want is the first item in it, then we found it
with one "probe". If we hash to a location and discover that
there are no items in this linked list, then that is zero probes
- we know immediately that it is not in the table, with no
compares performed.

  My dusty old college Data Structures textbook pointed out that
the efficiency of a chained hash table is a function of two
things. One is the ability of the hashing algorithm to hash
items to a good random distribution of indices. An algorithm
that hashes to the same address for many different items will
cause the linked list for that index to be very long, and the
speed of access will degenerate to that of searching a linked
list. Testing showed that the one used for logical name hash
tables does a reasonably good (but, not perfect) job of
distributing different logical names over the hash table.

  The other factor affecting performance of a hash table is its
fill factor. The fill factor is the ratio of the total number of
entries in a hash table to the size of the table. For an
unsuccessful lookup (that is, the item we want is not in the
table) the number of probes tends to be exactly the fill ratio.
For a successful search, the number of probes is approximated by
1 + 0.5*fill factor.

  Armed with this knowledge, and a sinking feeling that the hash
tables on this system were very full, I wrote the LNMFILL
utility. This utility locates the hash tables, and travels down
their entries. For each entry in the hash table, it traverses
the linked list it points to. The utility counts slots used,
slots empty, and maximum and average lengths of the linked
lists. LNMFILL does its work in KERNEL mode, since it needs to
acquire the LNM MUTEX. It is compatible with VMS V5 and up as
well as VMS V4, since the call to SCH$LOCKR will acquire the
needed spinlocks automatically. All calculations are rounded off to
integral values, to make everything easier...

  A run of the utility on one of the systems in question
produced the following results...


   *  Shared Tables *

Total Listheads : 128
Free Listheads  : 0
Logical Names   : 2207
Ave. Chain Len. : 17
                   Top 16 Max. Chain Lens.
   157    154    151     68     64     58     54     54
     43      43      42     39     36     26     21     20

   *  Process Table *

Total Listheads : 127
Free Listheads  : 109
Logical Names   : 20
Ave. Chain Len. : 1
                   Top 16 Max. Chain Lens.
     2      2      1      1      1      1      1      1
     1      1      1      1      1      1      1      1


  The process private hash tables were in pretty good shape.
They had a fill factor of less than 1 (logical names/total
listheads), and the average chain length was 1. The maximum
chain length was 2, indicating that there was an occasional
collision, causing two items to hash to the same address. We
decided that the size of this table didn't need to be increased.
If necessary, it could be increased by SYSGEN parameter
LNMPHASHTBL. It is in units of table entries, and defaults
to 128.

  It was pretty clear that the hash table size for the shared
table needed to be increased, to get the fill factor down to
less than 1. This would cause fewer items to be linked onto
linked lists, and speed up both successful and unsuccessful
searches. Its size is controlled by SYSGEN parameter
LNMSHASHTBL, and it also defaulted to 128. This was too small by
over an order of magnitude, for this system. Fortunately, this
is not too "expensive", in terms of memory, to increase. We
raised it to 2048 - from .25 pages to 16 pages of paged pool.
After a reboot, the improvement was definitely noticeable.


   *  Shared Tables *

Total Listheads   : 2048
Free Listheads   : 1028
Logical Names   : 2203
Ave. Chain Len. : 2

                   Top 16 Max. Chain Lens.
   131    130    130     18     12     11      9      9
       9        9        9       8      8        8      8      8

   *  Process Table *

Total Listheads   : 127
Free Listheads   : 109
Logical Names   :   22
Ave. Chain Len. :     1

                   Top 16 Max. Chain Lens.
     3      2      2      1      1      1      1      1
     1      1      1      1      1      1      1      1

  This was much better, with a load factor of about 0.5, but,
the top three maximum chain lengths were surprisingly high,
about 131. A little inspection with SDA showed why there where
so many collisions at these three indices. Each job references
three logicals with the same names - SYS$SCRATCH, SYS$LOGIN, and
SYS$LOGIN_DEVICE. Since the hash address is a function of the
logical name, and since all JOB logicals are in the system
shareable hash table (since they are potentially accessed by
more than one process), each of these three hash to the same
three indices. There will be one entry per logged in user on
these three linked lists. In any case, the average chain length
was down to 2, so, for most logical names, access times were
much improved.

  If you have a small to medium site, then you probably do not
have to worry about changing the table sizes. If you have much
more than 128 logical names (the default hash table size), then,
you should consider increasing the size of LNMSHASHTABL and/or
LNMPHASHTBL, as needed to improve the speed of logical name
translations.
  To use LNMFILL, just assemble, link, and run it. Use of
LNMFILL requires CMKRNL privilege...

$ MAC LNMFILL
$ LINK LNMFILL
$ RUN LNMFILL

Thursday, March 22, 2018

Banner Text on RSX11

  My first exposure to computers was back in the dim dark ages, in my college days, when you stood in line for a chance to use a card punch machine to compose your program, and then handed the resulting card deck in at the service window, where a tape ape would eventually load it into a card reader, and it would get to run on an enormous piece of whirring and buzzing machinery (that is, a mainframe computer). Eventually, much later (much, much,much later if the operators overheard you calling them tape apes) you would get your printed output back, stacked  in the output bins. It would be printed on greenbar paper, typically 14 7/8 inches wide, 132 characters wide at 10 characters/inch.

  So far this sounds pretty boring. Well it definitely was. Tedious, painful, annoying - all of these things. Editing a program on a card punch was even less fun that typing it in in the first place. And it was bad to drop a card deck - very bad. But one part I did enjoy. The output printout had what was called  a flag page, which printed out your user name and the name of your job in large characters, like a banner formed from individual characters. When I saw that, it made me feel like I was involved in some serious computer activity. I don't have any examples of output from that old machine (it was a Burroughs, by the way - at Rice University everyone hated it when the Burroughs was replaced by an IBM system), but here's a sample from an RSX11 batch job flag page....


Sample RSX flag page

  I was reading some DECUS code a few days back - a GREP utility for RSX, written in MACRO-11 by Bruce Mitchell, one of the giants in the RSX11 game. He had decorated each routine's documentation block with its name in the same banner font used by the RSX11 flag page. It looked pretty snazzy. It got me to wondering, what would it take to write a utility to convert text strings to output in that banner font format. I could have done it the hard way and laid out the bitmaps for each letter needed, along with code to raster it out line by line. That approach did not appeal to me. That sounded a lot like work, and this is a hobby. I got to wondering how and where RSX generated the text for flag pages. By coincidence, the very DECUS utility that got me to thinking about doing this in the first place, GREP, was just the ticket for finding the code that generates the banner text. File [121,10]FLGEN.MAC is the module that does the business for flag pages. 

  But,  I didn't want to just rip off the DEC character generating code and stick it into my utility. That is just not sporting. I figured, though, that figuring out how to call it from my code would be pretty much OK. Initially, that looked promising. FLGEN had all the logic for assembling an entire flag page, but it had one particular routine in it,  GEN, that takes a byte character as input and returns the equivalent multi line banner character. I wrote a small front end routine to call that routine - but it didn't work. The first problem was that, FLGEN itself was part of a bigger task, so it referred to a slug of variables and routines found in the other modules that made up the print spooler. When just linking my front end and FLGEN's object file, without the other modules, these symbols were all unresolved. I took care of that problem by getting the list of unresolved symbols from the TKB failure, and defining them as locations in my front end program. That way they resolved and the task built successfully. They would not have the right values in them, but, I wasn't really planning to use them - I just wanted to generate characters, not make an entire flag page.

This just got me to the next problem... I had failed to notice that subroutine GEN was not declared as global, so TKB couldn't resolve it at link time. Well, that was discouraging. But then I had a look at FLGEN and realized that I could call the main entry point of the whole thing, and, with the dummied up variables and routine entry points I had already set to zero (or return, in the case of routines), my inputs would pass into FLGEN, get passed to GEN and cause banner characters to be created. OK, we're most of the way home here. It turned out I got another break - FLGEN takes the results from GEN and passes them to an external  routine called SNDMSG, which normally QIOs them out to the line printer. All I had to do now was create a new SNDMSG routine in my code (and a couple other routines it called), and my routine would get called with each line of output, to display or save.

  Slap on the usual GCML and CSI command switch stuff and FCS file IO stuff, and Bob's your uncle...


banner.mac

To create....

>mac banner=banner
>mac flgen=[121,10]flgen
>tkb
banner=banner,flgen
/
task=...BAN
libr=FCSRES:RO   (optional)
//

To use..

Run banner, or install and invoke as BAN
Banner syntax is the usual, outfile=infile. The default file extension is .txt. For just seeing it on the screen, or inputting characters from the keyboard instead of from a file, use TI: as the input and/or output filename.

>run banner
BAN>ti:=ti:
ABCDE

     AAAAAA        BBBBBBBB            CCCCCCCC   DDDDDDDD       EEEEEEEEEE
     AAAAAA        BBBBBBBB            CCCCCCCC   DDDDDDDD       EEEEEEEEEE
AA               AA   BB              BB   CC                       DD               DD   EE
AA               AA   BB              BB   CC                       DD               DD   EE
AA               AA   BB              BB   CC                       DD               DD   EE
AA               AA   BB              BB   CC                       DD               DD   EE
AA               AA   BBBBBBBB       CC                       DD               DD   EEEEEEEE
AA               AA   BBBBBBBB       CC                       DD               DD   EEEEEEEE
AAAAAAAAAA   BB              BB   CC                       DD               DD   EE
AAAAAAAAAA   BB              BB   CC                       DD               DD   EE
AA               AA   BB              BB   CC                       DD               DD   EE
AA               AA   BB              BB   CC                       DD               DD   EE
AA               AA   BBBBBBBB            CCCCCCCC   DDDDDDDD       EEEEEEEEEE
AA               AA   BBBBBBBB            CCCCCCCC   DDDDDDDD       EEEEEEEEEE

^Z
>

Or specify files to input and or output to/from a file

>run banner
BAN>outfile=infile
>

It's upper case only, so any lower case chars in the input come out as spaces. The  character set is
ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890$.;:[],'!-.   Anything else prints out as a blank.
  
  This routine will handle input strings up to around 20 characters or so - bigger than that and FLGEN returns garbage.

If you specify a longish line and direct output to the ti:, , word wrap will make it look like the dog's breakfast..

Monday, February 19, 2018

RSX Utility that lists the home block and updates the checksums on ODS-1 real and virtual disks


Lately, I've had occasion to do a lot of work on ODS-1 virtual disks on RSX. Specifically, I've been playing around with the contents of the home blocks.

  ODS-1 home blocks are the real root of a Files-11 ODS-1 file system. It tells you where the index file is, and that's where all the file headers are.

  The home block is located on the second logical block of a disk or virtual disk file. There are provisions in the FIles-11 code for locating it somewhere else, in case block 2 is bad, but really, how likely is that? I've never seen it occur, so I don't worry about it. For my purposes, it's always at block 2.

  RSX doesn't really provide any tools for listing the contents of the home block. There's DMP, of course, but, unless you're looking at PDP11 instructions (which naturally is grouped three bits at a time), octal is not the easiest format for looking at the bit contents of numbers. I wrote HOMCHK (home block check), which lists the home block contents in hex (hex allows you to easily see what bits are set or cleared, by inspection, much easier than octal or decimal).

  The home block also contains two checksums - one about three quarters of the way into the block, and one at the end of the block. Why are there two checksums? Beats me. Each one of these is the sum of all the preceding words, up to the checksum word. I was experimenting with changing the home block values, which meant that the checksums would need to be updated, to allow the disk file to be mounted as a virtual disk - MOU gets upset when the checksums aren't right. In order to deal with this, I added the ability to HOMCHK to indicate the state of these checksums (good or bad), and to update them if you want, by using the /UP switch. The other supported switch is /DU - it will produce a hex  dump of the whole home block..

  Anyway...don't use this unless you know what you're doing...it will definitely blast two checksums into block 2 of any file or disk...

 Here's the code.....

homchk.mac 



  To make...
  mac homchk=homchk
  tkb
  TKB>homchk/pr:0=homchk
  TKB>/
  Enter Options:
  TASK=...HCK
  LIBR=FCSRES:RO ;optional, can save memory on a busy system
 //

  Note the use of the /PR:0 switch on the task output file. This builds the task as privileged, but with no mapping of the IO page. This allows the task to do QIOs to a mounted disk, without consuming task address space for the IO page.

  To use...
  >run HOMCHK
  HCK>infile
  or install it 
  >ins HOMCHK
  >HCK infile

 HOMCHK will work on real disks as well as virtual disks. Be sure and put a colon at the
 end of the device spec, or it will think it's a filename.

 HCK>du4:

 

  

Tuesday, February 13, 2018

RSX utility for converting Track, Sector order virtual RX50 disks to LBN order.


  My collection of PDP11s and VAXen include several members of the PRO family. These desktops systems were fielded, too little and too late, to compete with the IBM PC. The system software, called P/OS, was based on RSX11m+.  

  DEC apparently wanted to prevent any "fratricide" to sales of conventional PDP11 systems, so they initially limited the user interface to a series of  annoying menus. Later on, the Pro Tool Kit, intended for use by developers,  included  a command line interface that produces a very RSX DCL/MCR  sort of user experience, making the PRO family a pretty good way to have an 11 at home.

  The DEC PRO family included two entries that were  systems based on the F11 chipset - the PRO-325 (floppy drive only) and the PRO-350, which had floppies and a hard disk. I've never even seen a PRO-325, so I'm figuring, there couldn't have been all that many of them. There was also the PRO-380, which had a J11 CPU. Although it was speed limited to 10 MHz, it was still considerably faster than the PRO-350.

  PRO-380s came along late and didn't sell all that well. DEC continued the tradition of using  machines from previous generations as console processors for new products, and used the left over PRO-380s as consoles for several VAXen in the 8000 series. In order to communicate with the VAX processors, these PRO-380s were equipped with a relatively exotic card - the  RTI (real time interface) card. These provided 2 RS-232 serial lines, I IEEE-488 port, and 24 bidirectional TTL level IO pins.

 A goodly while back, I found a PRO-380 on EBAY, that had apparently been a VAX Console processor - it had a VAXConsole badge instead of a PRO-380 badge, and had an RTI card installed.  But, it proved difficult to find any software to drive the RTI card.

  I asked around, and Wolfgang Eichberger was generous enough to share some floppy images that contained some RTI software. This included diskette BL-X9978-BK, titled "PRO/RTI MAINT V2.0". That sounds promising...bound to be some RTI software on there. If I at least got a driver, I could pull it apart and see what I could tell it to do.

  I tried mounting it as an RSX virtual disk, using VCP. No soap, it failed with a "MOU - no home block found or structure not supported" error message. LD on VMS was similarly not well pleased with this disk image. Well, that was a little disappointing. A look at the dsk file with DMP showed that MOU was right - block 2, the most likely place for a home block, was nothing like a home block. 

  A little more DMP investigation showed that this disk image had been saved in track, sector order, instead of logical block order. The difference is, that images in track, sector order will reflect the sector interleave and track skew of the physical disk. Interleave and skew are used to give the drive's physical mechanisms time to get ready before the next logical block comes around. Interleave involves writing the logical blocks on the physical disk, for example, in the order 1,3,5,7,9,2,4,6,8. If you need to read blocks 1 and 2, you have time to get 1 read and handled, before block 2 comes around. This avoids not being ready in time and having to wait a complete revolution for 2 to come around again. In like wise, at each track step, the start order is skipped over a couple of blocks, to allow you to read the last block of a track, and have a little time to get ready for the first sector of the next track to come by.

Using track,sector order on a virtual disk preserves the  interleave and skew of a normal disk, but is not what the virtual disk utilities want to see - they want the blocks to start with the first block, and proceed in sequential order. When I tried to mount this sector, track order image, MOU effectively was looking at block 700 something when it thought it was looking at block 2.

  But, not a real problem. I had recently written a utility that unpacked RX50 teledisk images to LBN order virtual disk files. Part of that utility was a MAP function that, given a block number of a track, sector image, would return where that block should go in an LBN disk image. I took that function and added a little code around it, to make the TSTOLBN utility (Track, sector to LBN).  

  TSTOLBN takes the name of a track, sector dsk file as an input, and the output file name of an LBN order dsk file to be created. Like this...

>run tstolbn
TTL>lbnfile.dsk=tsfile.dsk

  The output file is then readable by VCP and LD.

  Here's an actual example. First, I try and VCP connect and mount the original track and sector order virtual disk file

>vcp conn prorti.dsk
VCP - Device VF0: has been assigned.
>mou vf0: /ovr
MOU - no home block found or structure not supported

OK, so I disconnect it.

>vcp  disco vf0:

Then I create a new file, in LBN order, VCP connect it...and mount it successfully.
I included part of the  directory listing to show that everything is all roses now.
>
>run tstolbn
TTL>prortilbn.dsk=prorti.dsk
>vcp conn prortilbn.dsk
VCP - Device VF0: has been assigned.
>mou vf0: /ovr
>dir vf0:[*,*]


Directory VF1:[ZZUPDATE]
12-FEB-18 16:46

D00038.TSK;2        24.     C  25-SEP-84 13:47
UPDATE.INS;1        1.      C  25-SEP-84 13:48
D00038.MSG;1        4.      C  25-SEP-84 13:48
HG0100.TSK;2        29.     C  25-SEP-84 13:48

Total of 58./61. blocks in 4. files



  So, now I have some RTI software to investigate.

   Here's the source code for TSTOLBN.

tstolbn.mac


To make...
 >mac tstolbn=tstolbn
 >tkb
 TKB>tstolbn=tstolbn
 TKB>/
 Enter Options:
 TASK=...TTL
 LIBR=FCSRES:RO       ;this is optional - saves memory on a busy system
 //
>

 To use...
 >run tstolbn
 TTL>outfile=infile

 or install it and use direct from the command line

 >ins tstolbn
 >TTL outfile=infile

 Outfile extension defaults to .dsk, infile extension to .dsk


  

Sunday, January 21, 2018

Adding a 3.5 inch floppy to a Microvax/VaxStation 20000

  A while back, I needed to read some old 3.5 inch floppies. None of the machines here have 3.5 inch floppies on them anymore. The Windows box I have (used only for browsing the web and hosting SIMH) doesn't even have a controller for a floppy anymore.  I got to thinking, and it occurred to me that the controller in a Microvax 2000 that can drive an RX33 (5.25 inch floppy) should be able to drive a 3.5 inch floppy drive - Almost all of the signals are the same, except for some drive select hoodoo.

  I dug an old 3.5 inch floppy drive  out of the junk box and took apart one of the Microvax 2000s  and started to install it. I did't get very far - the connector on the MicroVAX 2000, for the RX33 is a card edge connector. The connector for a 3.5 inch floppy is a normal IDC pin socket sort of affair. Here's a picture of the stock cable. The card edge connector for the floppy drive is the bottom one.




  The cabling for the hard drives and floppy drive on an MV2000 has a 50 pin connector on one end, and three card edge connectors on the other. Two of the card edge connectors are for connecting to an ST506 hard drive. The third section is for the RX33. A little study showed I just needed to cut off the card edge connector for the RX33 and  crimp on a normal IDC pin connector, with the wires attached in the same order as they were on the connector removed. That is, wires 1 and 2 go to pins 1 and 2. Skip pins 3,4,5,6. Connect wires 3,4,5,6,7,8 to the next 6 pins, then skip pins 13 and 14. Then connect the rest of the wires to the rest of the pins, in order. You can see which wires go where and what pins to skip if you look at the existing MV2000 RX33 cable connector - it's card edge connector is pin for pin wired the same as the IDC connector you'll be substituting for it.Here's a stock connector with the floppy edge connector cut off, and the IDC connector that will replace it.




 This worked, but, it didn't leave me a lot of cable slack, which makes putting the thing back together a real  exercise in manual dexterity. I decided to make a complete new cable. This was easy for me, since I am not using an RD series hard disk (I use SCSI for the system disk), so I can leave off the other two cables and connectors.

  So, like I said, the 50 pin connector from the MV2000 is split into three parts. The first part we've been talking about already, the floppy drive part. The other two parts lead to two card edge connectors for the RD series hard drive that MV2000s used for system drives. I didn't need those, so I left them off, and just crimped a 28 pin cable into pins 1 through 28 on the 50 pin connector. If you are still using an RD drive for the system disk, you'll need to add cable for these two connectors, and two card edge connectors as well. Here is my new cable, with the 50 pin connector already connected, and the 34 pin connector that gets crimped on to the other end.



  This picture doesn't show the cable end separated up for the skipped pins yet.

  There's good pinout poop about all this at http://home.iae.nl/users/pb0aia/vax/vs2khw.html. It's got pretty much all the info you need. I've probably muddled things up by trying to explain it...

 OK, maybe all the above is obvious, but it took me several days to figure it out.

  One thing I discovered in this process, is that the convention used on 5.25 inch floppy drivers, and most hard drives, that pin 1 on the interface cable is located nearest to the power connector, does not apply to all 3.5 inch floppy drives. It turned out the floppy drive I was using had pin 1 on the far side from the power connector rather than the near side. You'll know if you get it wrong and plug in backwards - the floppy drive activity LED will come on constantly, and the drive won't work.

  Another tip - in order to work, you have to jumper the floppy drive as DS0. Then it shows up as the third drive, DUA2:. Why? I dunno. Drive select technology for  floppy drives is a long and boring complicated subject, and since I have a solution that works, I choose not to analyze it in depth.

  Alrighty then - that takes care of the hardware. At this point the 3.5 inch floppy drive will show up...as an RX33. That means it will be 1.2 MB in size. An interesting situation, but, since this whole computer rodeo got started because I wanted to read some existing 1.44 MB floppies, not that useful a situation. As well, I'd like to be able to write floppies that other 3.5 inch floppy drives could read, so...not good...

  Not a real problem, though. A little time spent searching Google showed that someone else had already addressed this issue. Way back in 1995, Peter Coghlan wrote some patches to DVDRIVER, the MicroVAX 2000 floppy driver, that take care of this problem. His patches cover VMS versions 5.5-1 and 5.5-2. I'm on VMS 7.3, though. A little study of the device driver and his patches enabled me to find out where to make the corresponding changes in the new version. Peter has given me permission  to post his work on this, with my 7.3 changes added. Here's the file, called PATCHDVDRIVER.TXT. It's a VMS command file - the .TXT extension is required because Google sites is allergic to the .COM file extension.

PATCHDVDRIVER.TXT

    There's lots o' instructions in the file. Basically, save it as a file with the .COM extension, then execute it. It will patch DVDRIVER.EXE to support the floppy as an RX23 instead of an RX33. Reboot, and  Bob's your uncle - you'll see a device DUA2, and it will show up as an RX23, appropriately sized.. 

Saturday, January 20, 2018

Fixes for DISM32 VAX/VMS disassembler

  A couple of weeks back, I had occasion to need to modify DVDRIVER, the VMS device driver that controls floppy drives and ST506 hard drives on MicroVAX/VAXstation 2000s. (see my previous post about 3.5 inch floppies on MV2000s). Peter Coghlan had researched the changes required to make this happen, but his work was for VMS Version 5.5-1 and 5.5-2 - I needed the equivalent mods for VMS 7.3. I needed to find the proper offsets for his changes, in the later VMS version.

  The traditional tool for pulling a driver apart has been DISM32. This utility, written by Andy Pavlin, has been invaluable over the years. It does an incredible job at converting all types of executable files back into MACRO. But, it's an old tool - first written in 1982, and last updated in 1991. It considerably predates VMS 7.3, which is what I'm using.

  I figured, waddahell, let's download it and give it a try. It only took a few minutes Googling to find DISM32 V4.6, the last version, and download it. It came as  a zip file worth of Fortran sources, and a command file to compile and link them all. 

  Right out of the box, it had a problem. The compile failed due to referencing some undefined fields for structure CRFDEF3. A little  research showed that the fields were just FILL positions in the structure. The code wasn't so much using them as checking for a value of 0 - which they should be all the time. So I commented those lines out.

  So, now it compiled, with a slug of informational messages - mostly complaining about unused variables. That didn't bother me - they most likely got added as part of future changes that never got done. It did have a few messages indicating that an unsafe branch  into a loop or block had occurred. That worried me a little more. I checked the indicated spots in the source, and determined that those branches weren't going to cause any problems. So, I tried my freshly linked DISM32 on a few simple executables, and it seemed to still be doing the business.

  Flush with the feeling of success, I gave it a try on DVDRIVER.EXE. No soap - DISM32 blew up and exited after producing only the header part of a MACRO source file. A little more research showed that DISM32 would work fine on some drivers, and blow up on others.

  The error message indicated that a number was overflowing the FORMAT specification (remember FORMAT statements? I barely do...) in a routine that prints out the versions of system libraries that were used.  I increased an I1 spec to an I2, and that problem was over with.

  But, it still didn't complete successfully - now it blew up with a message that indicated that a write to the MACRO output file overflowed its buffer. This error took a little more study, and some time with the debugger. It turned out that the write in question involved the name of the driver, extracted from the Driver Prologue Table. There was support in DISM32 for new style and old style DPTs, which varied in length, and, more importantly, the location of the counted string that holds the driver name. The write statement in question, however, unlike other parts of DISM32 that use the DPT, had not been conditionalized to pick the correct  DPT style - it always used the old style DPT layout . This meant that it wound up using a byte four bytes before the actual counted string, as the length of the name. In this driver, the byte it picked had a large value, so the write failed. In other drivers, that byte happened to contain a smaller value - so those drivers could be decoded successfully.

  I replaced the old style name variable with a variable that would have the correct (old or new) style offset, as required, , and now DISM32 successfully completes.

  On the off chance that there are any other VAX hobbyists out there that need DISM32, I've produced  V4.7, that incorporates the changes described above.

dism32_47.zip

  So, there ya have it...