Retrochallenge 2009
      Summer Challenge
      Mark Wickens
      9-Jul-2009

                                          Research

      It appears that my usually verbose presence has been missed by those who
      witnessed the veritable stream of posts in my winter challenge! I can only
      apologise and say in my defence that it hasn't been for the lack of trying. I
      did do some research the other night which didn't make it onto the VAX until
      now, so here goes.

      In preparation for this challenge I had taken the following algorithm and coded
      it up in Java to educate me as to what I needed to achieve in VAX Macro. The
      algorithm is from the Clifford Pickover book:

          ALGORITHM:  Calculation of chaotic dusty curls
          Variables:  rz, iz = real, imaginary component of complex number
                      i = iteration counter
                      u, z = complex numbers
          Note:       Choose one of the three different tests for divergence.

          u = -.74 + .11 i;
          DO rrz = -1 to 1 by 0.001;
            DO iiz = -1 to 1 by 0.001;
              z = cplx(rrz, iiz);
              InnerLoop: DO i = 1 to 100;
                z = z**2 + u;
                rz = real(z); iz = imag(z);
                if sqrt(rz**2) + iz**2) > 2 then leave InnerLoop;
              END;
              color = i;
              if convergence_test = 1 then
                if rz**2 + iz**2 > 4 then PRINTDOT(rrz,iiz,color);
              if convergence_test = 2 then
                if ((abs(rz)<2) & (abs(iz)<2)) then PRINTDOT(rrz,iiz,color);
              if convergence_test = 3 then
                if rz**2+iz**2>4 & mod(i,2) = 0 then PRINTDOT(rrz,iiz,color);
            END;
          END;

      My first thought, when thinking about this algorithm in terms of VAX Macro was
      how to implement complex numbers. I had a quick look in my set of OpenVMS
      manuals and found the OpenVMS VAX RTL Mathematics(MTH$) Manual[1]. This manual
      defines a number of routines that may be helpful in developing my code to
      process complex numbers, aswell as pointing the reader to the OpenVMS RTL
      General Purpose (OTS$) Manual aswell[2].

      System utilities that might be of interest are:

        OTS$DIVCx       Perform complex division
        OTS$MULCx       Perform complex multiplication
        OTS$CNVOUT      Convert a D-, G- or H-float value to a character string
        OTS$POWCxCx     Raise a complex base to a complex floating-point exponent
        OTS$CNVOUT_G    G-float to character string (Fortran E-format)
            Argument 1: address of a G-floating point number
            Argument 2: address of a fixed length resultant string, as a descriptor
               Returns: unsigned longword condition
                                SS$_NORMAL
                                SS$_ROPRAND
                                OTS$_OUTCONNERR

        MTH$GABS        Absolute value of G-float
        MTH$GDIM        Positive difference of two G-floating parameters
        MTH$GMIN1       G-floating minimum of n G-floating parameters

      Where possible I'd like to use these utilities instead of inventing the wheel.
      Originally my challenge idea involved generating the mandelbrot to a terminal
      and I am undecided whether this will be an easier proposition than interfacing
      with my next choice, which is the venerable gnuplot[3]. This fantastic program
      takes a script, datafile or combination of both and given one of any number of
      possible targets (such as terminal, x-window, postcript printer, jpeg file,
      etc.) produces a beautifully rendered graph.

      I used gnuplot extensively whilst doing my PhD back in the mid 1990's, and
      recently called on its' services again when writing a utility to track my ADSL
      uptime. Anyone who is uses the BBS knows that I was having a hard time having
      changed over to Demon Internet with outages. Things seemed to have calmed down,
      but I figured that a picture is worth a thousand words.

      I run a couple of scripts from a cronjob on the NetBSD box running the
      retrochallenge BBS:

        $ crontab -l
        SHELL=/bin/sh
        MAILTO=msw
        @hourly /home/msw/router/getstats
        5,10,15,20,25,30,35,40,45,50,55 * * * * /home/msw/router/ping-internet.sh
        5,10,15,20,25,30,35,40,45,50,55 * * * * /home/msw/router/plot-stats.sh
        0,15,30,45 * * * *      /home/msw/router/upload-stats.sh

      Every five minutes the ping-internet.sh script attempts to ping one of the demon
      nameservers using its' IP address:

        #!/usr/pkg/bin/bash
        LOGFILE=/home/msw/router/ping-stats.txt
        TIME="`date +%C%y%m%d%H%M`"
        /sbin/ping -c 1 158.152.1.58 > /dev/null 2>&1
        if [ "$?" == "0" ]
        then
                STATUS=1
        else
                STATUS=0
        fi
        echo "$TIME $STATUS" >> $LOGFILE

      This generates a line in the LOGFILE which looks like this:

        200907091945 1
        200907091950 1
        200907091955 1
        200907092005 1
        200907092010 1
        200907092015 1
        200907092020 1
        200907092025 1
        200907092030 1
        200907092035 1
      
      where the first number is the date and time and the second number is a '1' if
      the ping was successful, or a '0' if it was not.

      The plot-stats.sh script turns these numbers into a nice graph with the help of
      GnuPlot then puts the resulting jpeg onto the local apache webserver:

        #!/usr/pkg/bin/bash
        sleep 30
        export PATH=/bin:/sbin:/usr/pkg/bin
        export RS=/home/msw/router
        export HTDOCS=/usr/pkg/share/httpd/htdocs
        export STATS_FILE=$RS/ping-stats.txt
        export JPG_FILE=ping-stats.jpg
        export SHARE_DIR=/usr/local/archive
        export HTTPD_DIR=/usr/pkg/share/httpd/htdocs
        cp $STATS_FILE $RS/ping-stats-copy.txt
        gnuplot $RS/plot-stats.gnuplot > $SHARE_DIR/$JPG_FILE
        cp $SHARE_DIR/$JPG_FILE $HTTPD_DIR

      The gnuplot script plot-stats.gnuplot that works the magic:

        set terminal jpeg large size 1024, 600
        #set terminal dumb
        set title "Demon Internet HomeOffice 8000, hostname: waldo"
        set lmargin 10
        set rmargin 10
        set tmargin 5
        set bmargin 8
        set xdata time
        set format x "%d/%m"
        set xlabel "Date, 2009" offset 0,-2
        set ylabel "ADSL Status"
        set yrange [-1:2]
        set ytics 0,1,1 ("Off" 0, "On" 1)
        set mxtics 6
        set timefmt "%Y%m%d%H%M%S"
        plot "/home/msw/router/ping-stats-copy.txt" using 1:2 with lines title
        "Status at `date`"
        #set title "ADSL status" offset 0,-10
        # - generated on %Y%m%d %H%M"
        show title

      Then once every 1/4 hour the upload-stats.sh script pushes the stats up to my
      remote web host (assuming the connection is up of course!)

        #!/usr/pkg/bin/bash
        cp /usr/local/archive/ping-stats.jpg /tmp
        cd /tmp
        ftp ftp://username:password@ftp.wickensonline.co.uk <<-END_OF_INPUT
                bin
                cd public_html
                put ping-stats.jpg
                dir
                bye
        END_OF_INPUT

      The end result being this lovely image:
      ADSL Uptime

      VAX MACRO Yeah!

      What, another digression, surely not! So, without further ado, some VAX Macro:

              TITLE:  .ASCIZ  /Computation of 6*(ALPHA)+(BETA)/<LF>
              ALPHA:  .LONG   3
              BETA:   .LONG   450
              RESULT: .BLKL   1

                      BEGIN   EXAMPLE
                      PRINTCHRS       TITLE
                      DUMPLONG        ALPHA,BETA
                      MULL3           #6,ALPHA,R5
                      ADDL3           R5,BETA,RESULT
                      DUMPLONG        RESULT
                      EXIT

                      .END    EXAMPLE

      A sample, example 5.8[4] to be precise, from the Baase Assembly Language book.
      The bits in bold are decidedly non-standard. They are macros, which must be
      linked with the code. I don't like this way of doing such things - I understand
      that it makes the examples easy to code and provides macros to do terminal input
      and output, but it hides some of the nitty gritty which you're going to have to
      come across sonner or later.

      The build script[5] for this example assembles the example together with a macro
      library:

              $ library/create/macro iomac.mlb iomac.mar
              $ macro/debug iomod+iomac/lib
              $ macro/list/debug example5_8.mar+iomac/lib
              $ link/debug example5_8,iomod

      The macros themselves are in the IOMAC.MAR[6] file:

              .MACRO  BEGIN   NAME
                      .ENTRY  NAME,^M<IV>
              ;
                      $OPEN           FAB=INFAB
                      $CONNECT        RAB=INRAB
                      $CREATE         FAB=OUTFAB
                      $CONNECT        RAB=OUTRAB
                      $OPEN           FAB=DISKFAB
                      $CONNECT        RAB=DISKRAB
                      .ENDM   BEGIN

                      .MACRO  READLINE        WHERETO
                      PUSHAB          WHERETO
                      CALLS           #1,RDLINE
              .ENDM   READLINE

              .MACRO  READRCRD        WHERETO,?LBL
                      PUSHAB          WHERETO
                      CALLS           #1,RDRCRD
                      BNEQ            LBL
                      BRW             EOF
              LBL:    .ENDM   READRCRD
              .MACRO  PRINTCHRS       STRING,LENGTH=#85
                      CVTWL   LENGTH,-(SP)
                      PUSHAB  STRING
                      CALLS   #2,PTCHRS
              .ENDM   PRINTCHRS

              .MACRO  DUMPLONG        ARG1,ARG2,ARG3,ARG4,ARG5,ARG6-
                                      ARG7,ARG8,ARG9,ARG10,ARG11,ARG12
                              CALLS   #0,STARTDUMP
                      .IRP    ARG,<ARG1,ARG2,ARG3,ARG4,ARG5,ARG6-
                              ARG7,ARG8,ARG9,ARG10,ARG11,ARG12>
                      .IF     NOT_BLANK ARG
                              MOVQ    #^A/%EXTRACT(0,8,ARG)/,-(SP)
                              PUSHL   SP
                              PUSHL   ARG
                              CALLS   #2,CVTPRT
                              ADDL2   #8,SP
                      .ENDC
                      .ENDR
                              CALLS   #0,ENDDUMP
               .ENDM   DUMPLONG

               .MACRO  EXIT
                              $CLOSE  FAB=INFAB
                              $CLOSE  FAB=OUTFAB
                              $CLOSE  FAB=DISKFAB
                              $EXIT_S
               .ENDM   EXIT
               .END

      Finally, the code behind the macros in IOMOD.MAR[7]:

                      .PSECT  IO_DATA,LONG,WRT,NOEXE

              LF = 10
              CR = 13
              BLANK = ^A/ /

              INFAB::         $FAB    FNM=<SYS$INPUT>
              OUTFAB::        $FAB    FNM=<SYS$OUTPUT>,MRS=85
              DISKFAB::       $FAB    FNM=<DATA.DAT>
              INRAB::         $RAB    FAB=INFAB,USZ=80,-
                                      ROP=PMT,PBF=PROMPT,PSZ=5
              OUTRAB::        $RAB    FAB=OUTFAB
              DISKRAB::       $RAB    FAB=DISKFAB,USZ=80
              PROMPT:         .ASCII  <LF><CR>/?? /
              ;       PROCEDURE RDLINE(WHERETO)

                      .PSECT  IO_PROCS,NOWRT

                      .ENTRY  RDLINE,0
                      MOVL    4(AP),INRAB+RAB$L_UBF
                      $GET    RAB=INRAB
                      CVTWL   INRAB+RAB$W_RSZ,R0
                      RET

              ;       PROCEDURE RDRCRD(WHERETO)

                      .ENTRY  RDRCRD,0
                      MOVL    4(AP),DISKRAB+RAB$L_UBF
                      $GET    RAB=DISKRAB
                      CMPL    DISKRAB+RAB$L_STS,#RMS$_EOF
                      BEQL    EOF
                      CVTWL   DISKRAB+RAB$W_RSZ,R0
                      RET
              EOF:    BISB2   #^X04,4(FP)
                      RET

              ;       PROCEDURE PTCHRS(STRING,MAX_LEN)

              CR_LF:  .BYTE   13,10

                      .ENTRY  PTCHRS,0
                      MOVAW   CR_LF,OUTRAB+RAB$L_RBF
                      MOVW    #2,OUTRAB+RAB$W_RSZ
                      $PUT    RAB=OUTRAB
                      MOVL    4(AP),OUTRAB+RAB$L_RBF
                      LOCC    #0,8(AP),@4(AP)
                      SUBL2   4(AP),R1
                      MOVW    R1,OUTRAB+RAB$W_RSZ
                      $PUT    RAB=OUTRAB
                      RET

              ;       PROCEDURE DUMP_MSGS

              HDR:    .ASCIZ  /** DUMPLONG OUTPUT **/
              STARS:  .ASCIZ  /** END    DUMPLOG  **/
                      .ENTRY  STARTDUMP,0
                      PRINTCHRS       HDR
                      RET
                      .ENTRY  ENDDUMP,0
                      PRINTCHRS       STARS
                      RET

              ;       PROCEDURE CVTPRT (LONG,NAME)
                       .PSECT  IO_DATA
              LONG:   .BLKL   1
              DUMP:   .BLKB   18

                      .PSECT IO_PROCS
              DESC:   .LONG   ^X010E0008
                      .ADDRESS        DUMP+10
              ARGS:   .LONG   3
                      .ADDRESS        LONG,DESC
                      .LONG   8

                      .ENTRY  CVTPRT,^M<R2,R3,R4,R5>

                      MOVC5   #0,0,#BLANK,#10,DUMP
                      LOCC    #0,#8,@8(AP)
                      SUBL3   R0,#8,R2
                      MOVC3   R2,@8(AP),DUMP
                      MOVL    4(AP),LONG
                      CALLG   ARGS,G^OTS$CVT_L_TZ
                      PRINTCHRS       DUMP,#18
                      RET

                      .END

      OK, so that's me done for the evening. Hopefully, my DECserver(s) will arrive
      shortly and I can be submerged in VT terminal heaven for the coding proper!

      
      ENDNOTES

      1.VAX RTL Mathematics Manual

      2.VAX RTL General Purpose Manual

      3.Gnuplot Home Page

      4.EXAMPLE5_8.MAR

      5.build.com

      6.IOMAC.MAR

      7.IOMOD.MAR