Operating System - OpenVMS
cancel
Showing results for 
Search instead for 
Did you mean: 

Teaching myself BASIC ...

 
SOLVED
Go to solution
Art Wiens
Respected Contributor

Teaching myself BASIC ...

In order to do so, I thought a good program to write would be a replacement for FREE.EXE which reports the free/used space for mounted disks. I want to "improve" on it to not include DGA devices making up a shadow set (they show up with 0 free blocks, 100% used ... skews the numbers).

I also thought that a good learning experience would be to use the system services $DEVICE_SCAN and $GETDVIW. It took quite some time for me (a non-programmer) to get my head around how item descriptors worked, but I think I have it now (except see further down).

Before my specific issues - in the SS Ref manual, it describes the bits returned in DVI$_DEVCHAR and DVI$_DEVCHAR2 item codes are DEV$V_xxx when in reality they are DEV$M_xxx (as defined by the $DEVDEF module in BASIC$STARLET.TLB). Why is that? V means bit and M means mask and they are the same?

My first problem is that the bit in DVI$_DEVCHAR2 that should indicate a disk is a shadow set member (DEV$M_SSM 64) is not being set when $DEVICE_SCAN returns one of these $1$DGA disks in a shadow set. Not sure why. Other appropriate bits are being set eg. I can check for DEV$M_VRT if the "disk" is the DSA device ie. my code should be good. Plan B (my second problem), I read that DVI$_SHDW_MEMBER is an individual item code that can be specified. When I add this to the item descriptor list, I get a SS$_BADPARAM status:

"The item list contains an invalid item code, or the buffer address field in an item descriptor specifies less than four bytes for the return length information."

I have it set so in the program (there are other item codes above it):

GD_IL3::ITEM(4)::BUFFER_LENGTH = 4 ! the description says it returns a longword interpreted as boolean
GD_IL3::ITEM(4)::ITEM_CODE = DVI$_SHDW_MEMBER
GD_IL3::ITEM(4)::BUFFER_ADDRESS = LOC(GD_SHDWMEMBER)
GD_IL3::ITEM(4)::RETURN_LENGTH_ADDRESS = LOC(GD_IL3::ITEM(4)::BUFFER_LENGTH)

when I EXAMINE it, it shows:

(4)
BUFFER_LENGTH: 1 ! why did it change this to 1 ?
ITEM_CODE: 268 ! 268 is the correct value for DVI$_SHDW_MEMBER
BUFFER_ADDRESS: 2061900376 ! correct address of where it should put it
RETURN_LENGTH_ADDRESS: 196688 ! trying to look at this location gives:
%DEBUG-E-NOACCESSR, no read access to address 00000000010C0001
LIST_TERMINATOR: 17563649 ! points to SHARE$DEC$BASRTL+212996: ??

So either the instructor isn't very knowledgable or the student has a learning disability! ;-)

Cheers,
Art
17 REPLIES
Craig A Berry
Honored Contributor

Re: Teaching myself BASIC ...

Two things to start (these may not be the only problems).

1.) It looks like you are using the same spot in memory to hold the buffer length and the return length. You *might* get away with that under some conditions, but why risk it?

2.) You need to terminate an item list with a longword of zero. I don't see that in the code snippet you provided. One common way to do that is to make the item list array one bigger than you need and set the buffer length for the last member to zero -- easy but very slightly wasteful.

As far as "V means bit and M means mask and they are the same?", a mask is a mask of bits, so they are the same in that both look at the same thing a different way. The V codes are the names of structure members and don't make sense outside the context of the whole bit-field "structure." The M codes are masks of the entire bit field and each one has the bit of interest turned on and all the other bits turned off.

What are you using to examine the bits, LIB$EXTV?
Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

Nice! I just wrote this long reply to Craig and got:

Due to the presence of characters known to be used in Cross Site Scripting attacks, access is forbidden. This web site does not allow Urls which might include embedded HTML tags.

POC!!!!

Luckily I saved it in Worpad before I hit submit!

Let's try and remove those "known characters"...
John Gillings
Honored Contributor
Solution

Re: Teaching myself BASIC ...

Art,
regarding $V_ and $M_ - this is part of the OpenVMS naming convention.

For a field containing a bit mask of bit fields, each bit field is described by symbols using $V_ (plus possibly $S_) and $M_.

fac$V_thing defines the bit position of "thing" within a field (counting from 0). If it's more than one bit fac$S_thing will define how many bits. fac$M_thing is the corresponding bit mask. It's a value in which the bits corresponding to the bit field are set, and all other bits are clear.

Consider a VMS status code. The lowest bit is called "SUCCESS", the low 3 bits are called "SEVERITY". Bits 3 through 15 are "CODE" and the 12 bits starting at bit 16 are "FAC_NO" (Facility Number). This is described as:

STS$V_SUCCESS = 0
STS$M_SUCCESS = 1

STS$V_SEVERITY =0
STS$S_SEVERITY =3
STS$M_SEVERITY =7

STS$V_CODE =3
STS$S_CODE =12
STS$M_CODE =%X7FF8

STS$V_FAC_NO =16
STS$S_FAC_NO =12
STS$M_FAC_NO =%XFFF0000

There is no STS$S_SUCCESS because it's just a single bit. (there are more things in the status code, see $STSDEF for the whole picture).

Now, how you use these symbols to examine bits depends on your preference, the language you're using, and possibly the specific circumstances of the test.

Use $M_ with bitwise AND to leave just the bits in the field you want. You can then check for 0 or non-zero. So:

IF (value AND STS$M_SUCCESS) <> 0
THEN
ok
ELSE
fail
ENDIF

This is usually the simplest and fastest if all you want to do is check a single bit.

If you want to get the numeric value of a field, use LIB$EXTV (or similar language construct):

field=LIB$EXTV(fac$V_thing,fac$S_thing,value)

So, for example, to check for a warning in a condition code, use

IF LIB$EXTV(STS$V_SEVERITY,STS$S_SEVERITY,conditon) = STS$K_WARNING

(another naming convention $K_ and $C_ represent constant values)

I thought I could include a reference to the manual which explains the whole naming convention, but to my astonishment, I can't find it anywhere in the standard document set (I'd have thought an early chapter in the Programming Concepts manual would be most appropriate).

There's an explanation in Appendix D of "OpenVMS AXP Internals and Data Structures", but that's a bit obscure for something so fundamental. Perhaps someone else can find a more accessible reference?
A crucible of informative mistakes
Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

I'll include my whole code as it sits this second. I'm "learning" and trying many odd things at an "amazing" rate ;-). You must promise not to roast it too badly, I fully acknowledge it's nowhere near "complete". I'll say it again ... I am not a programmer by trade (although I have taken some formal training over the decades). I consider myself a good hacker.

Learning BASIC in our environment will help me help the app programmers who have been sheltered from "system level" programming for many years. Anyways ...

"1.) It looks like you are using the same spot in memory to hold the buffer length and the return length."

I read that in the Programming concepts manual:

http://h71000.www7.hp.com/doc/731FINAL/5841/5841pro_034.html#32_itemlists

"For item_list_3 entries, you can set the Return Length Address field to point to the Buffer Length field in the item list entry itself. As a result, the "descriptor-like" portion of the entry is updated by the routine returning the value."

Sounded like a real "easy" way to understand that!

"2.) You need to terminate an item list with a longword of zero"

I did, see my code.

"very slightly wasteful"

There's 8G of memory on this box ... it's ok ;-)

Have a look at my code if you could.

Thanks,
Art
Phil.Howell
Honored Contributor

Re: Teaching myself BASIC ...

if you google for
"OpenVMS Demos: Programmer's Corner"
you will find some examples of vms basic calling system services
Phil
Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

Yes, thanks. It's one of the few BASIC/system service examples pages I found. I learned some of what I did from Neil's pages.

Surprisingly very few examples out there ... I "Google'd my brains out"! A few more for C, and some of their syntax helped me. I'm hoping my little piece of work might serve some purpose some day.

Cheers,
Art
Robert Brooks_1
Honored Contributor

Re: Teaching myself BASIC ...

A couple of quick points:

The documentation can be a bit out of date or incomplete.

To wit, the SSM bit is NEVER set anymore for a shadow set member. That bit was set for older, controller-based shadow sets. Controller-based shadowing was replaced by host-based volume shadowing 15ish years ago. For HBVS, the relevant bit is SHD. I suppose the SSM bit is relevant if you are still using a very old version of OpenVMS VAX, but
controller-based shadowing never made it to OpenVMS Alpha, I believe.

While there may be some dispute about this, I would claim that "best practices" would not attempt to decode by hand the various status longwords returned by GETDVI. Rather, there should be an equivalent item code for each bit of interest. If there's not, then one should be added. While no longer a member of VMS Engineering, I still have the ability to make changes, or at least twist the right arm to make it happen.

As I suspect others have already mentioned, the "v" as bit vector and "m" as bit mask techniques are really aimed at MACRO programmers and can be somewhat confusing in a high-level language such as BASIC.

I happen to think that DEC's BASIC is an absolutely wonderful language, and it still my language of choice for user-mode programming. While I have done some kernel-mode stuff in BASIC, it's a pain without the internal data structures in a BASIC-specific .TLB

-- Rob
John Gillings
Honored Contributor

Re: Teaching myself BASIC ...

Art,

I'm not sure how BASIC handles field alignment. I doubt it's an issue, but just in case, I like to examine my item lists in binary to ensure they really are what I expect.

Compile and link your program with /DEBUG, then run under debug control.

Now set a break point before your system service call and STEP/INTO the system service. Obviously you won't be able to single step through the kernel mode instructions, but you can step into the dispatcher. That's useful because you can check your arguments. On a VAX you can examine the argument list with:

DBG> EXAMINE @AP:@AP+@@AP*4

On Alpha, you can examine the first 6 arguments as R16 through R21. In your case, the SYS$DEVICE_SCAN item list is the 4th argument, so:

DBG> EXAMINE/HEX/LONG/NOSYMBOLIC @R19

will show you the first longword of your item list. Subsequent EXAMINE commands will step through the structure. Or, you can use a range:

DBG> EXAMINE/HEX/LONG/NOSYMBOLIC @R19:@R19+20

will display 8 longwords. /HEX because you need to see WORD fields, /LONG because an item list is more long word oriented than quadword and /NOSYMBOLIC because you don't want the compiler to give you symbolic names (as this will hide the true alignment of the object).

Now confirm you can interpret the bits as a valid item list.

Here's an example from a real $GETDVI call.
DBG> ex/hex/long @r19:@r19+20
000000007ADB9AC0: 00080004
000000007ADB9AC4: 00020000
000000007ADB9AC8: 00000000
000000007ADB9ACC: 00000000
000000007ADB9AD0: 00010000
000000007ADB9AD4: 00000000
000000007ADB9AD8: 80275F5C
000000007ADB9ADC: 0FFFFFFFF
000000007ADB9AE0: 95CAD4B0

So, the buffer length is 4, the code is 8 (DVI$_DEVBUFSIZ), the buffer address is 20000. You can then symbolise any addresses to see where they point. For example:

DBG> ex %HEX(20000)
BITL\DVI_DEVBUFSIZ: 0

If it's a pointer to a string descriptor, you can check the length and address fields in the descriptor, then follow them to the string. It will look something like this:

DBG> ex/hex/long 20004
0000000000020004: 010E0003
DBG> ex
0000000000020008: 0002000C

010E is the class (1) and type (0E) fields of a valid scalar string descriptor. 3 is the length and 2000C is the address of the string. Examine the contents as:

DBG> EX/ASCII:3 %HEX(2000c)
000000000002000C: 'EQL'

Although this is real nuts and bolts debugging, I've often found obscure bugs that can't be found any other way! Especially in item lists and other system service structures.

(regarding lost ITRC messages... down here the success rate for posting a response is aeround 50%, so I routinely copy the contents of the reply buffer before hitting submit... sad that HP have such good technology, but don't seem to be able to use it properly!)
A crucible of informative mistakes
Steve-Thompson
Regular Advisor

Re: Teaching myself BASIC ...

Hi Art

I had the same idea a while ago.
I filtered out DG's CD's and any disk that wasn't mounted.
For better or worse the code works.

here's the code:

si2sdt@vigia2> ty diskstat.bas
1 option type = explicit
%include "$ssdef" %from %library "sys$library:basic$starlet"
%include "$devdef" %from %library "sys$library:basic$starlet"
%include "$dvidef" %from %library "sys$library:basic$starlet"
%include "$dvsdef" %from %library "sys$library:basic$starlet"
%include "$dcdef" %from %library "sys$library:basic$starlet"

external long function lib$getdvi
external long function sys$getdvi
external long function sys$device_scan
external long constant ss$_NoMoreDev

declare quad iosb
declare long efn, stts1, stts2, stts3, Zero
declare long RetLen, RetVal1, RetVal2, RetVal3
declare long dev_type
declare quad Ctx
declare string Disk, Linea
declare gfloat MB, FB
declare decimal(19,2) Full

Record DSItList
word IcoLen
word Icode
Long BufAdd
long RetLen
long Cero
End Record
declare DSItList DSItem

Record GDVItList
word IcoLen1
word Icode1
Long BufAdd1
long RetLen1
word IcoLen2
word Icode2
Long BufAdd2
long RetLen2
word IcoLen3
word Icode3
Long BufAdd3
long RetLen3
long Cero
End Record
declare GDVItList GDVItem

dev_type = DC$_DISK
Zero = 0%
efn = 0%
stts1 = 1%
Ctx = 0%
Disk = space$(64)
linea = "-------------------------------" !

! DSItem --- DiskScan Items
DSItem::IcoLen = 4%
DSItem::Icode = DVS$_DEVCLASS
DSItem::BufAdd = loc(dev_type)
DSItem::RetLen = Zero
DSItem::Cero = Zero

! GDVItem ---
GDVItem::IcoLen1 = 4%
GDVItem::ICode1 = dvi$_devchar
GDVItem::BufAdd1 = loc(RetVal1)
GDVItem::IcoLen2 = 4%
GDVItem::ICode2 = dvi$_mnt
GDVItem::BufAdd2 = loc(RetVal2)
GDVItem::IcoLen3 = 4%
GDVItem::ICode3 = dvi$_swl
GDVItem::BufAdd3 = loc(RetVal3)
GDVItem::Cero = 0%
Start:
While stts1
stts1 = sys$device_scan(Disk, RetLen, "*", DSItem , Ctx)
goto Done if stts1 = ss$_NoMoreDev
call lib$stop(stts1 by value) if (stts1 AND 1%) = Zero
stts2 = sys$getdvi (efn by value, , Disk, GDVItem, iosb, , , )
call lib$stop(stts2 by value) if (stts2 AND 1%) = Zero
stts3 = RetVal2 AND stts2 AND (NOT RetVal3)
! print "stts2 + stts3 + Disk + RetVals23: ";stts2;stts3;trm$(Disk);RetVal2;RetVal3
gosub Percent if stts3
next
percent:
stts2 = lib$getdvi (dvi$_freeblocks, ,trm$(Disk), RetVal1, ,)
FB = RetVal1
stts2 = lib$getdvi (dvi$_maxblock, ,trm$(Disk), RetVal1, ,)
MB = RetVal1
Full = 100 - ( ( FB / MB ) * 100 )
if (Full < 50% ) AND (MB>0)
then
print " Less than 50% - ";trm$(Disk);" is ";format$(Full, "###.##");"% full."
end if
if (Full < 90%) AND (Full >= 50%) AND (MB>0)
then
print " Less than 90% - ";trm$(Disk);" is ";format$(Full, "###.##");"% full."
end if
if (Full >= 90%) AND (MB>0)
then
print "GT than 90% - ";trm$(Disk);" is ";format$(Full, "###.##");"% full."
end if
return
Done:
end


Jon Pinkley
Honored Contributor

Re: Teaching myself BASIC ...

Art,

You may want to consider modifying the source to use the VOLSIZE instead of MAXBLOCK as well.

I fixed my local copy of free to do the following: Use DVI$_VOLSIZE instead of DVI$_MAXBLOCK. Ignore members of HBVS shadowsets.

I wanted to make it dynamic so the same image would work on a version of VMS that supported VOLSIZE or not, but I never got around to that, so it just uses VOLSIZE. The oldest version of VMS I run is 7.3-2 so it isn't an issue for me, but it would "be nice" if the image tried VOLSIZE and if that failed, then use MAXBLOCK, but that is what I never did.

Attached is the output from Free V2.2 and the output from my modified version. Also included is the .SLP file, created using the command:

$ differences FREE.B32;1 ; /slp/out=free.slp

If you have the unmodified versio of free.B32 from Hunter's FREE V2.2 kit, you can recreate my version with the command:

$ edit/sum free.b32;1 /update=free.slp

Jon
it depends
Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

Robert: Thanks. It would have saved me some time if I had known the SSM bit isn't set anymore! This is v7.3-2 and that's

the manual I'm using. I did look at the 8.3 version quickly, but I didn't see a difference. I am trying to use

DVI$_SHDW_MEMBER, but it's not working.

John: Thanks for all that too! Nuts and bolts is fine ... I used your techniques and it would seem that my program fails

after the first iteration through the loop. It get's back to the SYS$GETDVIW ss and the item list is "corrupt" ie. the

buffer_length has changed from 4 to 1 on the DVI$_SHDW_MEMBER item.

If I step/into the ss, and step it from there, it shows 3 points in SHARE$SYS$SSISHR at which point it's back to my code,

shows the current disk totals and breaks at the ss again. See attached. If after the third SHARE$SYS$SSISHR I step/into and keep stepping and checking, it's good up until SHARE$SYS$SSISHR+70528, the next step to SHARE$SYS$SSISHR+70532 it's "broken".

Steve: Kind of scary in the similarities ... I guess we read the same pages/manuals ;-) I haven't had a chance to read it too intently, but I didn't seem to see where you get rid of DGA devices which are part of a shadow set.

Jon: Thanks for the tip on VOLSIZE

Cheers,
Art
Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

Steve: You must have posted an old version or you don't actually filter out the DGA's in a shadow set:

$ run/nodebug diskstat
Less than 50% - _VMSTA2$DKC0: is 32.25% full.
Less than 90% - _DSA1: is 80.65% full.
Less than 50% - _DSA2: is 17.47% full.
Less than 90% - _DSA3: is 72.00% full.
Less than 50% - _DSA4: is 21.13% full.
Less than 50% - _DSA5: is 19.18% full.
Less than 50% - _DSA6: is 16.88% full.
Less than 50% - _DSA7: is 17.12% full.
Less than 50% - _DSA8: is 1.39% full.
Less than 50% - _DSA9: is 1.39% full.
Less than 50% - _DSA10: is 1.39% full.
Less than 50% - _DSA11: is 1.39% full.
Less than 50% - _DSA12: is 1.39% full.
Less than 90% - _DSA20: is 50.38% full.
Less than 50% - _DSA21: is 45.17% full.
Less than 50% - _DSA22: is 0.46% full.
Less than 50% - _DSA23: is 0.46% full.
GT than 90% - _$1$DGA306: is 100.00% full.
GT than 90% - _$1$DGA307: is 100.00% full.
GT than 90% - _$1$DGA309: is 100.00% full.
GT than 90% - _$1$DGA310: is 100.00% full.
GT than 90% - _$1$DGA312: is 100.00% full.
GT than 90% - _$1$DGA313: is 100.00% full.
GT than 90% - _$1$DGA314: is 100.00% full.
GT than 90% - _$1$DGA315: is 100.00% full.
GT than 90% - _$1$DGA317: is 100.00% full.
GT than 90% - _$1$DGA318: is 100.00% full.
GT than 90% - _$1$DGA320: is 100.00% full.
GT than 90% - _$1$DGA321: is 100.00% full.
GT than 90% - _$1$DGA322: is 100.00% full.
GT than 90% - _$1$DGA323: is 100.00% full.
GT than 90% - _$1$DGA325: is 100.00% full.
GT than 90% - _$1$DGA326: is 100.00% full.
GT than 90% - _$1$DGA328: is 100.00% full.
GT than 90% - _$1$DGA329: is 100.00% full.
GT than 90% - _$1$DGA330: is 100.00% full.
GT than 90% - _$1$DGA331: is 100.00% full.
GT than 90% - _$1$DGA333: is 100.00% full.
GT than 90% - _$1$DGA334: is 100.00% full.
GT than 90% - _$1$DGA336: is 100.00% full.
GT than 90% - _$1$DGA337: is 100.00% full.
GT than 90% - _$1$DGA338: is 100.00% full.
GT than 90% - _$1$DGA339: is 100.00% full.
GT than 90% - _$1$DGA341: is 100.00% full.
GT than 90% - _$1$DGA342: is 100.00% full.
GT than 90% - _$1$DGA344: is 100.00% full.
GT than 90% - _$1$DGA345: is 100.00% full.
GT than 90% - _$1$DGA346: is 100.00% full.
GT than 90% - _$1$DGA347: is 100.00% full.
GT than 90% - _$1$DGA349: is 100.00% full.
GT than 90% - _$1$DGA350: is 100.00% full.
GT than 90% - _$1$DGA352: is 100.00% full.
GT than 90% - _$1$DGA353: is 100.00% full.
GT than 90% - _$1$DGA384: is 100.00% full.
GT than 90% - _$1$DGA385: is 100.00% full.
GT than 90% - _$1$DGA387: is 100.00% full.
GT than 90% - _$1$DGA388: is 100.00% full.
GT than 90% - _$1$DGA389: is 100.00% full.
GT than 90% - _$1$DGA391: is 100.00% full.
GT than 90% - _$1$DGA392: is 100.00% full.
GT than 90% - _$1$DGA394: is 100.00% full.
GT than 90% - _$1$DGA395: is 100.00% full.
GT than 90% - _$1$DGA396: is 100.00% full.
GT than 90% - _$1$DGA397: is 100.00% full.
GT than 90% - _$1$DGA399: is 100.00% full.

Art
Robert Brooks_1
Honored Contributor

Re: Teaching myself BASIC ...

Two more quick points . . .

1) the posted code using SYS$$GETVI, LIB$GETDVI, and $DEVICE_SCAN does not properly check statuses. One should always check the returned status and the IOSB status against ss$_normal (or low bit set).

2) dvi$_shdw_member must work. If you are not getting the answer you expect, then there must be a coding error. As a quick check, try the lexical variant from DCL.

An example:

$ write sys$output f$getdvi( "$1$dkb100:", "shdw_member" )
TRUE

In this example, the above member is part of the three-member shadow set DSA0

$ write sys$output f$getdvi( "dsa0", "shdw_member" )
FALSE

-- Rob

Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

The verification works:

$ show dev dsa1

Device Device Error Volume Free Trans Mnt
Name Status Count Label Blocks Count Cnt
DSA1: Mounted 0 EVA_PROD1 12981856 1 1
$1$DGA306: (VMSTA2) ShadowSetMember 0 (member of DSA1:)
$1$DGA307: (VMSTA2) ShadowSetMember 0 (member of DSA1:)
$1$DGA309: (VMSTA2) ShadowSetMember 0 (member of DSA1:)

$ write sys$output f$getdvi("dsa1:","shdw_member")
FALSE

$ write sys$output f$getdvi("$1$DGA306:","shdw_member")
TRUE

I'll stare at my program some more. Perhaps that little return_length_address trick doesn't always work?

Thanks Robert.
Hein van den Heuvel
Honored Contributor

Re: Teaching myself BASIC ...

The return length address trick is a silly one trick pony, you can 'exploit' is to double-book a part of an itemlist as a string descriptor of sorts. But between the two of us we already waste more bytes writing about it than you'll ever save :-).

First of all, if you 'know' the return length, then you should not ask for the return length. An integer is an integer and is 4 byte (or 8 :-) and stays that way.
Just a specify NULL (0) as return address pointer.

For some strings you may want getjpi to return the length, but more often than not the space filled structure of the original size is fine.

In the case of a loop through mutlipel GETJPI you want to keep the buffer descriptor a constannt. Just point to a proper string length variable, or into the length of a statis descriptor.
Use that to TRIM or SEGMENT.

Good luck!
Hein.
Art Wiens
Respected Contributor

Re: Teaching myself BASIC ...

It was the RETURN_LENGTH_ADDRESS trick. I changed it to the address of actual words and it all works as expected.

Craig - I guess you "win", being the first to point out this "coding error".

I knew no good would come from "fancy book learnin'"!

Cheers,
Art
Hoff
Honored Contributor

Re: Teaching myself BASIC ...

Apropos of a side-comment made earlier...

For locating BASIC source code examples for OpenVMS, HP apparently doesn't allow Googlebot into some of the corners of the HP web site. For a relevant and useful corner of the darknet, look here:

http://www12.itrc.hp.com/service/james/CPQsearch.do?rn=25&searchtext=vax+basic+example&searchcriteria=allwords
http://www12.itrc.hp.com/service/james/CPQsearch.do?rn=25&searchtext=dec+basic+example&searchcriteria=allwords

The same general path in via the James search tool also holds when seeking source code examples for C, Fortran, COBOL and a whole pile of other OpenVMS languages.