FORTRAN 77 on PDP-11

Johnny Billquist bqt at Update.UU.SE
Sun Jul 31 14:05:42 CDT 2005


I'm replying to several postings at once here, since I'm in digest mode
anyway... We all know the subject by now, doing bignum arithmetic in F77
on a PDP-11, and all the side topics this have generated...

> From: "Jerome H. Fine" <jhfinexgs2 at compsys.to>
>
> Johnny Billquist wrote:
>
> >>Jerome H. Fine wrote:
> >
> >
> >>As for endian issues, I can define the most significant word to be
> >>at either end, but I tend to think that I will place the most significant
> >>at the high end of the word since this allows FORTRAN 77 to use
> >>octal to output the value correctly with 64 bit floating point values.
> >>
> >??? Not sure what you actually say here. Floating point values hardly have
> >anything to do with this. Pick whatever byte order you want, and stick
> >with it. Output is something you'll have to figure out anyway, and you
> >deal with any byte order you decided on then. Either way is not that
> >difficult. However, I'd recommend using little-endian, since that is the
> >"natural" order of the PDP-11, and F77 on the machine. That means you can
> >let it deal with 16-bit quantities natively without having to to byte
> >swapping.
> >
> Jerome Fine replies:
>
> Please define little-endian and big endian.

This have now been covered, so I think I can safely skip this. If you
really need some help in figuring out what little and bigendian is, then
say so, and we'll take a crash course.

> Also, I should have explained what I meant!  Specifically,
> it should be possible to use floating point variables
> as arguments to subroutines and let FORTRAN 77 take
> care of allocating the actual storage by doing so:

And I don't see the need. When you pass arrays as arguments in FORTRAN,
you only pass pointers, and actually access the parameters in the callers
address space. So no allocation is done, nor need to be done.
Using REAL*8 as a return value from the function is actually what you are
most interested in anyway when you go down this lane, since it's the
result you might want to allocate space for automatically. However, it
will not help you beond 64 bits anyway, so it's a dead end. Bite the
bullet and do it in a way that will deal with arbitrary large values, if
that's what you want.

> 1000 FORMAT ( O25, O25 )
>  REAL * 8  Value1, Value2
>  CALL I64ADD ( Value1, Value2 )
>  TYPE 1000, Value1, Value2
>
> I64ADD::
>  Mov R0,-(SP)
>  Mov R1,-(SP)
>  Mov 2(R5),R0
>  Mov 4(R5),R1
>  Add (R0)+,(R1)+
>  Adc (R1)
>  Add (R0)+,(R1)+
>  Adc (R1)+
>  Add (R0)+,(R1)+
>  Adc (R1)+
>  Add (R0)+,(R1)+
>  Mov (SP)+,R1
>  Mov (SP)+,R0
>  Return
>  .End

This code has a bug in it, as has been pointed out. You actually needs to
do the ADC for all subsequent words after each add step, since every ADC
in turn also can generate a carry.

> If I am correct, the above code assumes that the low order
> words/bytes are the least significant and the high order
> words/bytes are the most significant.  Is this little-endian
> or big-endian?

Yes, and that is little-endian.
Well, actually, your code only assumes that the *words* are little endian.
You don't look at individual bytes, and thus the CPU can have them either
little or bigendian without you knowing.

> NOTE that if the most significant bit is in the most
> significant word and byte (as per the above MACRO-11
> subroutine), then the most significant of everything
> is in the highest address of everything.  HOWEVER, from
> looking at the order of bytes, words and bits in the
> PDP-11 manual (Chapter 10 of Microcomputer Processor
> Handbook), when the most significant bit is in the
> word with the lowest address, the most significant
> bit (actually the sign bit and the exponent of real
> floating point numbers) is in byte 1 rather than byte 0.

Please stop looking at floating point data, since it's a whole other game.

I'll even repeat this: DON'T TRY TO USE FP, IT'S A WHOLE OTHER ISSUE IN
ALL ASPECTS! Even endianess don't actually apply to fp.

If you look at how 16-bit integers are stored in memory, you'll see that
the most significant bit of the 16 is stored in bit 7 of byte 1. And thus,
the least significan bit is stored in bit 0 of byte 0, which is a
small-endian machine.

[...]

> Date: Sat, 30 Jul 2005 21:54:05 -0400
> From: "Jerome H. Fine" <jhfinexgs2 at compsys.to>
> Subject: Re: FORTRAN 77 on PDP-11
> To: General Discussion: On-Topic and Off-Topic Posts
> 	<cctalk at classiccmp.org>
> Message-ID: <42EC2F3D.1080901 at compsys.to>
> Content-Type: text/plain; charset=us-ascii; format=flowed
>
>  >Johnny Billquist wrote:
>
> >>Jerome H. Fine wrote:
> >
> >
> >>More seriously, if I am using all 16 bits as unsigned integers, then
> >>the high order bit can't be easily eliminated.
> >>
> >True. And if you don't use all 16 bits, then the MUL instruction deals
> >with the sign itself anyway, so there is no need to take absolute values
> >and so on either... In short, a good suggestion in theory, but one that
> >don't work in real life.
> >
> >But of course you still haven't reflected on the fact that in FORTRAN-77
> >you can do 32-bit integer multiplication already...
> >
> Jerome Fine replies:
>
> OK. I just checked how FORTRAN 77 internally handles:
>  INTEGER * 4 ITest1, ITest2, ITest3
>  REAL * 8 RTest1, RTest2, RTest3
>  ITest1 = 65536
>  ITest2 = 10
>  ITest1 = ITest1 * ITest2
> The integer instruction Mul allows only 2 signed 16 bit numbers
> that then produce a 32 bit number.  When 2 signed 32 bit numbers
> are multiplied, FORTRAN 77 first converts them to floating point
> numbers, multiplies them as 2 real 64 bit numbers and converts the
> result back to an integer.

What FORTRAN 77 are you using??? You gotta be kidding. Here is my small
example code:

-----------

PDP-11 FORTRAN-77 V5.4-26       20:35:33    31-JUL-05           Page 1
TMUL.FTN;1
0001            PROGRAM TEST

0002            INTEGER*4 X,Y,Z

0003            X = 42
0004            Y = 4711
0005            Z = X*Y

0006            END
PDP-11 FORTRAN-77 V5.4-26       20:35:33    31-JUL-05           Page 2
TMUL.FTN;1
                .TITLE  TEST
                .IDENT  31JUL

000000          .PSECT  $CODE1
000000          JSR     PC,OTI$
000004          MOV     #76400,-(SP)
000010          MOV     #76733,R4
000014          JSR     R4,@$NAM$
                                                ; 0003
000020          MOV     #-3,$SEQC
000026          MOV     #52,X
000034          CLR     X+2
                                                ; 0004
000040          MOV     #11147,Y
000046          CLR     Y+2
                                                ; 0005
000052          MOV     X+2,-(SP)
000056          MOV     X,-(SP)
000062          MOV     Y+2,-(SP)
000066          MOV     Y,-(SP)
000072          JSR     R4,MLJS$
000076          MOV     (SP)+,Z
000102          MOV     (SP)+,Z+2
                                                ; 0006
000106          JSR     PC,EXIT$
------------

Notice how the compiler pushes two 32-bit integers on the stack and calls
MLJS$

Where did you find any FP stuff???

The rest of your questions about F77 are irrelevant, since you are asking
things on the compiler, but the compiler clearly don't do what you say.

> Johnny (or anyone else), can you please comment on if it is
> necessary for FORTRAN 77 to use the stack?  If it is not
> needed, why does FORTRAN 77 use this approach?

The reason for using the stack is that FORTAN-77 have a calling convention
using R4 as the argument pointer. And since the function for doing 32-bit
multiplications is just a function, the parameters needs to be passed
somehow, and the stack is a very good choise.

As for the algoritm for the sieve, I'll skip that part, since I haven't
given it enough thought to see if what you say make sense or not. And I'm
not overly interested in primes, so please don't ask me. I remember from
my math classes that a sieve is both memory inefficient and slow. Doing
factorization is rather fast in comparision, so unless you want to print
all primes you can think of, it would be faster. Doing them all will be
slower, but will use a fixed, very small amount of memory anyhow.

[---]

> Date: Sat, 30 Jul 2005 22:28:28 -0400 (EDT)
> From: der Mouse <mouse at Rodents.Montreal.QC.CA>
> Subject: Re: FORTRAN 77 on PDP-11
> To: "General Discussion: On-Topic and Off-Topic Posts"
> 	<cctalk at classiccmp.org>
> Message-ID: <200507310254.WAA18849 at Sparkle.Rodents.Montreal.QC.CA>
> Content-Type: text/plain; charset="iso-8859-1"
>
> > Please define little-endian and big endian.
>
> Little-endian and big-endian apply whenever you have a multi-digit
> value broken up into smaller ordered multi-digit units.  (Usually this
> is for base-2 digits, but it doesn't have to be.)  Most often, the
> smaller units are 8-bit bytes, with the order being memory addressing,
> but this doesn't have to be so.  (An example that violates all of those
> assumptions is sending multi-bit bytes over a bit-serial line, where
> the "smaller..units" are individual bits, ordered by chronology on the
> wire.)

[...]
Good enough that we hopefully can terminate that question?

> The PDP-11 is an odd case.  The hardware is little-endian, but the
> hardware is 16-bit.  Some languages store 32-bit integers in what we
> might call "schizophrenic endian": that 12345678 value is stored in
> four consecutive bytes as 34 12 78 56: it is broken into two 16-bit
> words with the more-significant one stored first, but those 16-bit
> words are then stored little-endian in the bytes making them up.

The (I believe) official term is "middle-endian". :-)
And like I believe I said before, FORTRAN-77 on the PDP-11 is pure
little-endian. FORTRAN IV however, is middle-endian.
I think I've written a small test program in most languages that have
32-bits integers on the PDP-11, which shows what byte order they use, if
someone is interested.

[...]

> > I64ADD::
> >  Mov R0,-(SP)
> >  Mov R1,-(SP)
> >  Mov 2(R5),R0
> >  Mov 4(R5),R1
> >  Add (R0)+,(R1)+
> >  Adc (R1)
> >  Add (R0)+,(R1)+
> >  Adc (R1)+
> >  Add (R0)+,(R1)+
> >  Adc (R1)+
> >  Add (R0)+,(R1)+
> >  Mov (SP)+,R1
> >  Mov (SP)+,R0
> >  Return
>
> I see two problems here.  First, you have to eliminate the
> postincrement on the second and third ADC instructions.

Yup.

>  Second, this
> loses carries resulting from the ADCs.

Yup.

> (Also,
> the return instruction is normally spelled "ret", not "return".)

Nope.
The PDP-11 don't have a RET instruction. RETURN is actually a macro, and
is the usual name used for this.
If you want the actuall assembler mnemonic, it's "RTS PC" :-)

CALL is the same deal... Don't exist either, and is instead a macro, so
"CALL nnn" gets translated to "JSR PC,nnn"

	Johnny

Johnny Billquist                  || "I'm on a bus
                                  ||  on a psychedelic trip
email: bqt at update.uu.se           ||  Reading murder books
pdp is alive!                     ||  tryin' to stay hip" - B. Idol


More information about the cctalk mailing list