Retrochallenge 2009
      Summer Challenge
      Mark Wickens
      18-Jul-2009

                                        Miller Time?

      This will have to be a short post because bed becons. After the hardware traumas
      of yesterday I stuck at what I'm best at and continued the program development.
      Actually, the title 'Mandelbrot Generator' is a bit misleading, a more accurate
      title would be 'Transcendental Function in the u plane Generator', but we won't
      split hairs, eh?

      Having done the first run which was setup to generate a data tuple for each of a
      1000x1000 real/imaginary grid I was coming to the realisation that this might
      take some time when ALLIN1 asked me 'did I really want to copy that file because
      disk space was running low' - yes, I'd filled my DATA disk in about 20 minutes.

      Note from the MONITOR SYSTEM capture shown below the program was primarly bound
      by the IO time required to write the data out in ASCII by capturing SYS$OUTPUT.
      I remembered the commands straight off to do this, even thought it must be some
      16 years since I last did it:

      $ DEFINE SYS$OUTPUT MBRT1.OUT
      $ RUN MBRT1
      $ DEASSIGN SYS$OUTPUT

      Node: ORAC                  OpenVMS Monitor Utility     18-JUL-2009 21:55:25
      Statistic: CURRENT             SYSTEM STATISTICS
                                                           Process States
                + CPU Busy (62)           -+         LEF:     7      LEFO:     0
                |****************          |         HIB:    30      HIBO:     0
      CPU     0 |--------------------------| 100     COM:     1      COMO:     0
                |*********                 |         PFW:     0      Other:    2
                +--------------------------+         MWAIT:   0
                Cur Top: _TNA3: (37)                           Total: 40

                + Page Fault Rate (0)     -+         + Free List Size (65336)   +
                ||                         |         |************              | 136K
      MEMORY  0 |--------------------------| 500   0 |--------------------------|
                |                          |         |********                  | 13K
                +--------------------------+         + Mod List Size (4394)     +
                Cur Top:  (0)

                + Direct I/O Rate (837)   -+         + Buffered I/O Rate (2)   -+
                |**************************|         |                          |
      I/O     0 |--------------------------| 500   0 |--------------------------| 500
                |**************************|         |                          |
                +--------------------------+         +--------------------------+
                Cur Top: _TNA3: (837)                Cur Top: _TNA3: (2)

      So I set my sights slightly lower with a 100x100 grid covering the same portion
      of the complex plane, and this only took a couple of minutes. In the meantime
      I'd not had a lot of success getting gnuplot[1] to compile under OpenVMS, so I
      resorted to copying the data file over to the NetBSD box running the retrobbs.
      The contents of the file look like (real imaginary value):

         -0.100000E+01    -0.100000E+01     0.100000E+01
         -0.100000E+01    -0.990000E+00     0.100000E+01
         -0.100000E+01    -0.980000E+00     0.100000E+01
         -0.100000E+01    -0.970000E+00     0.100000E+01
      My gnuplot file ended up containing:

      set terminal jpeg large size 1024, 600
      set title "MBRT1 - Chaotic Dusty Curls generator in VAX Macro"
      set xrange [-1:1]
      set yrange [-1:1]
      splot "mbrt1.out" using 1:2:3 '%lf %lf %lf' lt 3 pt 5 ps 0.25 lc palette z
      show title

      Which produces the following somewhat mysterous output:

      Gnuplot of Chaotic Dusty Curl Generator

      This is a grid data plot which is quite pretty but admittedly sucks a bit - I
      really need to now persue two immediate directions:

      1. Generate output in a format more suitable for gnuplot or persue other
         options for output.
      2. Investigate how to create the output in binary format which should speed up
         the output I/O time and make the process CPU bound.

      Apologies for missing the hunt, but hopefully a pretty graph will make up for it
      a bit.

      Finally, the current code now looks like this:

      .TITLE  Chaotic Dusty Curls Generation Using VAX Macro

      ;++
      ; This program will produce a set of coordinate and value tuples
      ; for a window on a Mandelbrot set to the standard output.
      ;
      ; References:
      ;       VAX Macro and Instruction Set Reference Manual
      ;       VMS Run-Time Library (LIB$) Reference Manual
      ;       VMS System Services Reference Manual
      ;       VMS Run-Time Math Library (MTH$) Reference Manual
      ;       Sara Baase 'VAX Assembler Language Programming'
      ;
      ; Author        : Mark Wickens
      ; Date          : 11-Jul-2009
      ;--
      ;    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;
      ;--

      ; Define symbols, constants, etc. used in this module

      ; Debugging aids
      ; Debug the inner loop by outputting each value calculated for sqrt(rz**2 + iz**2)
              DEBUG_INNER     = 0

      ; Offset in bytes in real/imaginary pair of the real and imaginary value
              COMP_REAL       = 0
              COMP_IMAG       = 4

              .PSECT MBRT1_RWDATA     WRT,NOSHR,NOEXE,PAGE
              .ALIGN LONG

      ; Real and imaginary values for constant 'u'
      U:       .F_FLOATING    0.74, 0.11

      ; Convergence test 1, comparison value
      CONV1_MAX: .F_FLOATING  4.0

      ; Real axis dimensions and increment
      RRZ_MIN: .F_FLOATING    -1.0
      RRZ_MAX: .F_FLOATING    1.0
      RRZ_INC: .F_FLOATING    0.01

      POW2:    .LONG          2

      ; Imaginary axis dimensions and increment
      IIZ_MIN: .F_FLOATING    -1.0
      IIZ_MAX: .F_FLOATING    1.0
      IIZ_INC: .F_FLOATING    0.01

      TWOF:   .F_FLOATING     2.0

      ; Number of iterations to perform in convergence loop
              LOOP_MAX        = 100

      ; Convergence test to use
              CONV_TEST_VALUE = 1

      ; Output buffer maximum size
              MAX_SIZE        = 132

      ; Fortran E Format buffer max size
              FEF_MAX_SIZE    = 16

      ; Setup a text buffer for use as an output line

      OUT_BUFFER:     .BLKB MAX_SIZE

      OUT_DESC:       .WORD MAX_SIZE
                      .BYTE DSC$K_DTYPE_T
                      .BYTE DSC$K_CLASS_S
                      .ADDRESS OUT_BUFFER

      FEF_BUFFER:     .BLKB FEF_MAX_SIZE
      FEF_DESC:       .WORD FEF_MAX_SIZE
                      .BYTE DSC$K_DTYPE_T
                      .BYTE DSC$K_CLASS_S
                      .ADDRESS FEF_BUFFER
      FEF_VAL:        .F_FLOATING 0.0

      ASC_SPACE:      .ASCII  / /
              NULL    = 00
      ASC_NULL:       .ASCII  <NULL>

      ; Allocate a longword to store any failure status value we might see

      ERROR_CODE:     .BLKL 1

      ; Real axis loop counter
      RRZ:            .BLKF 1

      ; Imaginary axis loop counter
      IIZ:            .BLKF 1

      ; Z Complex value (real/imaginary pair)
      Z:              .BLKF 2

      ; Complex temporary
      COMP_TEMP:      .BLKF 2

      RZ:             .BLKF 1
      IZ:             .BLKF 1
      RZPOW2:         .BLKF 1
      IZPOW2:         .BLKF 1
      SQRTZ:          .BLKF 1
      ZPOW2:          .BLKF 1
      COLOUR:         .BLKF 1

      ; Convergence test
      CONV_TEST:      .BYTE   CONV_TEST_VALUE
              .PSECT MBRT1_CODE NOWRT,SHR,EXE,PAGE
              .ALIGN PAGE
      ;*******************************************************************************
      ; Program Entry Point
      ;*******************************************************************************
              .ENTRY MBRT1,^M<R2,R3,R4,R5,R7>

      ; Initialize

      START:
              MOVB    #SS$_NORMAL, ERROR_CODE         ; error code to normal
              MOVB    #CONV_TEST_VALUE, CONV_TEST     ; convergence test to use
              MOVF    RRZ_MIN, RRZ                    ; init real axis value

      RRZ_LOOP:
              MOVF    IIZ_MIN, IIZ                    ; init imaginary axis value

      ; Set value of complex variable Z, stored in R2
      IIZ_LOOP:
              MOVAF   Z, R2
              MOVF    RRZ, COMP_REAL(R2)
              MOVF    IIZ, COMP_IMAG(R2)

      ; Initialize inner loop counter, stored in R3
              MOVL    #1, R3

      ; Within loop
      LOOP:           
      ; z**2
              PUSHL   POW2
              PUSHL   COMP_IMAG(R2)
              PUSHL   COMP_REAL(R2)
              CALLS   #3, G^OTS$POWCJ
              MOVAF   COMP_TEMP, R4
              MOVF    R0, COMP_REAL(R4)
              MOVF    R1, COMP_IMAG(R4)

      ;R4 now contains address of z**2, COMP_TEMP
      ;; z = z**2 + u

              MOVAL   U, R5
              ADDF3   COMP_IMAG(R4), COMP_IMAG(R5), COMP_IMAG(R2)
              ADDF3   COMP_REAL(R4), COMP_REAL(R5), COMP_REAL(R2)

      ; rz = real(z)
              MOVF    COMP_REAL(R2), RZ
      ; iz = imag(z)
              MOVF    COMP_IMAG(R2), IZ

              MULF3   RZ, RZ, RZPOW2
              MULF3   IZ, IZ, IZPOW2
              ADDF3   RZPOW2, IZPOW2, R5

      ; R5 = rz**2 + iz**2
              MOVF    R5, ZPOW2
              PUSHAF  ZPOW2
              CALLS   #1, G^MTH$SQRT
              MOVF    R0, SQRTZ

      ; R0 = SQRT(rz**2 + iz**2)

              .IF     DEFINED DEBUG_INNER
              PUSHAL  SQRTZ
              CALLS   #1, G^FLOAT2STR
              .ENDC

              CMPF    SQRTZ, TWOF
              BGTR    EXIT_LOOP       

              CMPL    #LOOP_MAX, R3
              BEQLU   EXIT_LOOP

              ADDL    #1, R3  
              JMP     LOOP

      EXIT_LOOP:
              CVTLF   R3, COLOUR

              JSB     CONVERGENCE_TEST

      ; IIZ = IIZ + IIZ_INC
              ADDF3   IIZ, IIZ_INC, IIZ
      ; IF IIZ < IIZ_MAX THEN GOTO iiz_loop
              CMPF    IIZ, IIZ_MAX
              BGTR    EXIT_IIZ_LOOP
              JMP     IIZ_LOOP

      EXIT_IIZ_LOOP:
              ADDF3   RRZ, RRZ_INC, RRZ
      ; if rrz < rrz_max then goto rrz_loop
              CMPF    RRZ, RRZ_MAX
              BGTR    EXIT_RRZ_LOOP
              JMP     RRZ_LOOP

      CONVERGENCE_TEST:
              CMPF    R5, CONV1_MAX
              BLEQ    CONVERGENCE_TEST_END
              JSB     PRINTDOT
              CALLS   #0, PRTSTR

      CONVERGENCE_TEST_END:
              RSB

      EXIT_RRZ_LOOP:
      ; Normal Exit
              MOVL    #SS$_NORMAL, ERROR_CODE
              BRB     COMMON_EXIT

      FAILURE_EXIT:
              MOVL    R0, ERROR_CODE
              MOVW    #MAX_SIZE, OUT_DESC

              PUSHAQ  OUT_DESC
              PUSHAW  OUT_DESC
              PUSHAL  ERROR_CODE
              CALLS   #3, G^LIB$SYS_GETMSG

              PUSHAQ  OUT_DESC
              CALLS   #1, G^LIB$PUT_OUTPUT

      COMMON_EXIT:
              $EXIT_S ERROR_CODE

      ;---------------------------------------------------
      PRINTDOT:
              PUSHAL  RRZ
              CALLS   #1, FLOAT2STR

      ; Append string to the output buffer
              MOVC3   #FEF_MAX_SIZE, FEF_BUFFER, OUT_BUFFER
              MOVC3   #1, ASC_SPACE, OUT_BUFFER+16

              PUSHAL  IIZ
              CALLS   #1, FLOAT2STR
              MOVC3   #FEF_MAX_SIZE, FEF_BUFFER, OUT_BUFFER+17
              MOVC3   #1, ASC_SPACE, OUT_BUFFER+33

              PUSHAL  COLOUR
              CALLS   #1, FLOAT2STR
              MOVC3   #FEF_MAX_SIZE, FEF_BUFFER, OUT_BUFFER+34
              MOVC3   #1, ASC_NULL, OUT_BUFFER+50

      ; Set the length
              MOVW    #50, OUT_DESC
              RSB

      ;---------------------------------------------------
      ; Convert float to string
              .ENTRY  FLOAT2STR,^M<>

              MOVL    @4(AP),FEF_VAL

              MOVW    #FEF_MAX_SIZE, FEF_DESC
              PUSHL   #6
              PUSHAQ  FEF_DESC
              PUSHAQ  FEF_VAL
              CALLS   #3, G^OTS$CNVOUT
              RET

      ;---------------------------------------------------
      ; Output string to terminal
              .ENTRY  PRTSTR,^M<>

              PUSHAQ  OUT_DESC
              CALLS   #1, G^LIB$PUT_OUTPUT
              RET

              .END MBRT1
                                              
      
      
      ENDNOTES

      1. gnuplot home page