        .TITLE  HEXIFY
        .SBTTL  Stuart Hecht and Eric McQueen

        .LIBRARY /SYS$LIBRARY:STARLET/
        .LIBRARY /SYS$LIBRARY:LIB/

        .IDENT  /1.1.00/

;++
;This will take a task file and turn it into hexidecimal strings
;--

        .EXTRN  LIB$GET_INPUT
        .EXTRN  LIB$PUT_OUTPUT
        .EXTRN  DSC$K_DTYPE_T
        .EXTRN  DSC$K_CLASS_S
        .EXTRN  SS$_NORMAL
        .EXTRN  RMS$_EOF
        .MCALL  $FAB                    ; RMS calls
        .MCALL  $RAB
        .MCALL  $CLOSE
        .MCALL  $CONNECT
        .MCALL  $CREATE
        .MCALL  $DISCONNECT
        .MCALL  $READ
        .MCALL  $OPEN
        .MCALL  $PUT
        .MCALL  $RAB_STORE
        .MCALL  $FAB_STORE

        .SBTTL  Definitions of symbols

DWRLUN  =1                              ; Disk read LUN
DWWLUN  =5                              ; Disk write LUN
KNORMAL =0                              ; No error
EOF     =-1                             ; End of file error code
LEFTBYTE=^O377*^O400                    ; All one in left byte
HEXOFFSET=7                             ; Offset to get to 'A from '9+1
CR      =13.                            ; Carriage return
LF      =10.                            ; Line feed
; Packet types currently created
PKDATA  =0                              ; Data packet code
PKRFM   =255.                           ; Record format
PKRAT   =254.                           ; Record attributes
PKMRS   =253.                           ; Maximum record size
PKALQ   =252.                           ; File length(blocks)
PKFILNM =251.                           ; File name
PKEOF   =250.                           ; End of file


        .SBTTL  Data

        .PSECT  $PLIT$,LONG
M$FILN: .BYTE   CR,LF,LF
        .ASCII  'Input file name: '
L$FILN  =.-M$FILN

M$OFLN: .BYTE   CR,LF,LF
        .ASCII  'Output file name (or return for the default): '
L$OFLN  =.-M$OFLN

M$NEXF: .BYTE   CR,LF,LF
        .ASCII  'Press return to finish or type the name of another file'
        .BYTE   CR,LF
        .ASCII  'to append to the HEX file: '
L$NEXF  =.-M$NEXF

M$RMS:  .BYTE   CR,LF,LF
        .ASCII  'RMS ERROR'
L$RMS   =.-M$RMS
        .EVEN

        .SBTTL  RMS Data

DEFALT: .ASCIZ  'SYS$DISK:'             ; System default.
DEFALN  =.-DEFALT                       ; Size of the default device.
        .EVEN


        .SBTTL  Storage locations

        .PSECT  $OWN$,LONG
        .ALIGN  LONG

MSGDSC: .BLKW   1                       ; Data block for terminal output
        .BYTE   DSC$K_DTYPE_T
        .BYTE   DSC$K_CLASS_S
ADDR:   .ADDRESS ADDR
LNGADR: .BLKL   1

INP_STR_D:                              ; Key string desciptor
         .BLKL  1
INP_BUF: .ADDRESS ADDR

INP_STR_LEN:                            ; Key string length
        .BLKL   1


BUCOUNT: .BLKL  1                       ; Number of character available in the
                                        ;   buffer (returned from RMS)
RDCOUNT: .BLKL  1                       ; Number of characters read from buffer
WTCOUNT: .BLKL  1                       ; Number of characters written
CHCOUNT: .BLKL  1                       ; Number of characters written to buff.
NULCOUNT: .BLKL 1                       ; Number of nulls not yet written

CHKSUM: .BLKL   1                       ; Checksum for the line
ADDRESS: .BLKL  1                       ; Current address

INP.N:  .BLKB   28.                     ; Space for input file name
INP.L   =.-INP.N                        ; Length of input file name
OUT.N:  .BLKB   28.                     ; Space for output file name
OUT.L   =.-OUT.N                        ; Length of input file name

RDBUF:  .BLKB   512.                    ; Disk read buffer
WTBUF:  .BLKB   512.                    ; Disk write buffer
        .EVEN



        .SBTTL  RMS Data structures
        .ALIGN LONG
RDFAB:: $FAB    DNA=DEFALT,DNS=DEFALN,FNA=INP.N,FNS=INP.L,-
                LCH=DWRLUN,FAC=<GET,BIO>,SHR=GET

        .ALIGN LONG
RDRAB:: $RAB    FAB=RDFAB,RAC=SEQ
                                        ; Beginning of RAB block.


        .ALIGN LONG
WTFAB:: $FAB    DNA=DEFALT,DNS=DEFALN,FNA=OUT.N,FNS=OUT.L,-
                LCH=DWWLUN,FAC=PUT,SHR=NIL,ORG=SEQ,RAT=CR,RFM=VAR

        .ALIGN LONG
WTRAB:: $RAB    FAB=WTFAB,RAC=SEQ
                                        ; Beginning of RAB block.


        .SBTTL  Main line code

        .PSECT  $CODE$,LONG,EXE

        .ALIGN LONG
HEXIFY:: .WORD  ^M<>            ; For CALLS that is used from operating system
NOINP:
        MOVAB   M$FILN,R11              ; Get the input prompt address
        MOVL    #L$FILN,R12
        MOVAB   INP.N,R10               ; Get address of input and length
        MOVL    #INP.L,R1               ;
        JSB     READ                    ; Read the input file name
        TSTL    R0                      ; See if we got anything
        BEQL    NOINP                   ; If no input then try again
        MOVL    R0,R5                   ; Save length

        MOVAB   M$OFLN,R11              ; Get the address of the prompt
        MOVL    #L$OFLN,R12
        MOVAB   OUT.N,R10               ; Get address of output file name
        MOVL    #OUT.L,R1               ;  and length
        JSB     READ                    ; Read the output file name
        MOVL    R0,R3                   ; Save length
        TSTL    R3                      ; See if we got any input
        BNEQ    GOTFIL                  ; Yes so branch

; Here so use the default output file name
        MOVL    R5,R0                   ; Get the input file length back
        MOVAB   INP.N,R2                ; Get input address
        MOVAB   OUT.N,R3                ; Point at buffer
        CLRL    R1                      ; Clear the character count
2$:     CMPB    (R2),#^A/./             ; Check for an extension
        BEQL    10$                     ; If an extension then ignore rest
                                        ;   of line
        MOVB    (R2)+,(R3)+             ; Move into the output file name
        INCW    R1                      ; Increment counter
        SOBGTR  R0,2$                   ; Branch until done

10$:    MOVB    #^A/./,(R3)+            ; Write the extension for output file
        MOVB    #^A/H/,(R3)+            ;
        MOVB    #^A/E/,(R3)+            ;
        MOVB    #^A/X/,(R3)+            ;
        ADDW3   #4,R1,R3                ; Get final character count

;++
;Open files
;--
GOTFIL:
;Create output file
        MOVAL   WTFAB,R1                ; Put address of FAB into R1.
        $FAB_STORE FAB=R1,FNS=R3        ; Tell RMS file name length

        $CREATE #WTFAB                  ; Create the file
        JSB     RMSERR                  ; Check for file error
        MOVAL   WTRAB,R1                ; Put address of RAB into R1.
        $RAB_STORE RAB=R1,UBF=WTBUF,RBF=WTBUF,USZ=#512.,RSZ=#512.
                                        ; Put address of user buffer in RAB.
        $CONNECT #WTRAB                 ; Connect to record.
        JSB     RMSERR                  ; Check for file error
;Open input file
AGAINSAM:
        MOVAL   RDFAB,R1                ; Put address of FAB into R1.
        $FAB_STORE FAB=R1,FNS=R5        ; Tell RMS file name length
        $OPEN   #RDFAB                  ; Open the file
        JSB     RMSERR                  ; Check for file error
        MOVAL   RDRAB,R1                ; Put address of RAB into R1.
        $RAB_STORE RAB=R1,UBF=RDBUF,RBF=RDBUF,USZ=#512.,RSZ=#512.
        $CONNECT #RDRAB                 ; Connect to record.
        JSB     RMSERR                  ; Check for file error


;++
;Do the actual work
;--
        MOVZWL  #512.,RDCOUNT           ; Initialize buffer pointers
        MOVZWL  #512.,BUCOUNT           ;
        CLRL    WTCOUNT                 ;
        CLRL    ADDRESS                 ; Initialize the address
        CLRL    NULCOUNT                ; Initialize the number of nulls
        MOVAL   RDFAB,R5                ; Get the FAB address
;Get the Record format (FIX, VAR, ...)
        MOVZBL  #PKRFM,R10              ; Set packet type to record format
        JSB     HEADER                  ; Output the header

        MOVZBL  FAB$B_RFM(R5),R10
        JSB     CVTH                    ; Put the record format code into buff
        INCL    CHCOUNT                 ; Increment counter
        JSB     PUTLIN                  ; Write the line out
;Get the record type (CR, ...)
        MOVZBL  #PKRAT,R10              ; Set packet type to record type
        JSB     HEADER                  ; Output the header
        MOVZBL  FAB$B_RAT(R5),R10
        JSB     CVTH                    ; Put the record type into buffer
        INCL    CHCOUNT                 ; Increment counter
        JSB     PUTLIN                  ; Write the line out
;Get the maximum record size (512. for tasks)
        MOVZBL  #PKMRS,R10              ; Set packet type to max record size
        JSB     HEADER                  ; Output the header
        MOVZWL  FAB$W_MRS(R5),R10
        PUSHL   R10                     ; Save for low order
        EXTZV   #8.,#8.,R10,R10         ; Get high order byte
        JSB     CVTH                    ; Put the record size into buffer
        INCL    CHCOUNT                 ; Increment counter
        POPL    R10                     ; Get size back
        JSB     CVTH                    ; Put the record size into buffer
        INCL    CHCOUNT                 ; Increment counter
        JSB     PUTLIN                  ; Write the line out
;Get the file length (in blocks)
        MOVZWL  #PKALQ,R10              ; Set packet type to file length
        JSB     HEADER                  ; Output the header
        MOVL    FAB$L_ALQ(R5),R10
        PUSHL   R10                     ; Save for low order
        EXTZV   #8.,#8.,R10,R10         ; Get high order byte
        JSB     CVTH                    ; Put the allocation into buffer
        INCL    CHCOUNT                 ; Increment counter
        POPL    R10                     ; Get allocation back
        JSB     CVTH                    ; Put the low order into the buffer
        INCL    CHCOUNT                 ; Increment counter
        JSB     PUTLIN                  ; Write the line out
;Get the file name
        MOVZBL  #PKFILNM,R10            ; Set packet type to file name
        JSB     HEADER                  ; Output the header
        MOVZBL  FAB$B_FNS(R5),R4
        MOVAB   INP.N,R3                ; Get the input file name address
25$:    MOVZBL  (R3)+,R10               ; Get the next character
        JSB     CVTH                    ; Buffer the next character of the name
        INCL    CHCOUNT                 ; Increment counter
        SOBGTR  R4,25$                  ; Repeat until all done
        JSB     PUTLIN                  ; Write the line out


;++
; Start moving real data
;--
NEXLIN:
        JSB     GET                     ; Get a character from the buffer
        CMPL    R10,#EOF                ; Check for end of file
        BEQL    FINISH                  ; If at end the finish up
        TSTL    R10                     ; Check for null character
        BNEQ    DOLIN                   ; Not null so just do regular stuff
        INCL    ADDRESS                 ; Point to next location
        BRB     NEXLIN                  ;  save space and try again
DOLIN:  PUSHL   R10                     ; Save the character we have
        MOVZWL  #PKDATA,R10             ; Set packet type to plain old data
        JSB     HEADER                  ; Put the standard header into buffer

        POPL    R10                     ; Get the original character back
LINAGA: JSB     CVTHEX                  ; Convert the character to hex codes
        INCL    ADDRESS                 ; Point to next location
        INCL    CHCOUNT                 ; Increment the character count
        CMPL    CHCOUNT,#^O36           ; Check to see if we should finish
        BNEQ    LINMOR                  ;  this line
        JSB     PUTLIN                  ; Put the whole line to disk
        BRW     NEXLIN                  ; Go do the next line

LINMOR: JSB     GET                     ; Get the next character
        CMPL    R10,#EOF                ; Is it an end of file?
        BNEQ    LINAGA                  ; No, then just handle normally
;       JSB     PUTLIN                  ; Yes, write the current line
        DECL    ADDRESS                 ; Reset address to correct value
        BRW     FIN1                    ; Finish up


        .SBTTL  Finish up

;++
;Finish up
;--
FINISH:
        MOVZBL  #PKDATA,R10             ; Set packet type to plain old data
        JSB     HEADER                  ; Insert the header so the extra
                                        ;  nulls are seen
FIN1:   TSTL    NULCOUNT                ; See if no nulls left
        BEQL    FIN                     ; If none then branch
        CLRL    R10                     ; Get a null
        DECL    NULCOUNT                ; Decrement the counter
        JSB     CVTH                    ; Convert to HEX (w/o null compression)
FIN:    JSB     PUTLIN                  ; Put the current buffer to disk
; Write out the end of task file line
        CLRL    CHCOUNT                 ; Clear character count
        MOVZBL  #PKEOF,R10              ; Get end of task file packet type
        JSB     HEADER                  ; Make the header
        JSB     PUTLIN                  ; Write the line
; Close the input (task) file
        MOVAL   RDFAB,R1                ; Get the FAB for input file
        $CLOSE  R1                      ; Close input file
        JSB     RMSERR                  ; Check for file error
; See about another file to append
        MOVAB   M$NEXF,R11              ; See if another file should be
        MOVL    #L$NEXF,R12             ;   appended to the HEX file
        MOVAB   INP.N,R10               ;
        MOVL    #INP.L,R1               ; Get address of input and length
        JSB     READ                    ; Read the input file name
        TSTL    R0                      ; See if we got anything
        BEQL    LEAVE                   ; If no input then leave
        MOVL    R0,R5                   ; Put the length in R5 for the open
        JMP     AGAINSAM                ; Repeat process for this file
; Write out end of hex file line
LEAVE:  CLRL    CHKSUM                  ; Clear the checksum for this line
        CLRL    CHCOUNT                 ; Clear character count
        MOVZBL  #^A/</,R10              ; Get the start character
        JSB     BUFFER                  ; Put it into the buffer
        MOVZBL  #12.,R5                 ; Get the number of nulls needed
FINREP: MOVZBL  #^A/0/,R10              ; Set the character to 'null'
        JSB     BUFFER                  ; Put it into the buffer
        SOBGTR  R5,FINREP               ; Repeat if not done
        JSB     PUTLIN                  ; Write the buffer to disk
; Close the HEX file
        MOVAL   WTFAB,R1                ; Get FAB for output file
        $CLOSE  R1                      ; Close output file
        JSB     RMSERR                  ; Check for file error

END:
        MOVL    #SS$_NORMAL,R0          ; Set up successful completion
        RET                             ; Exit program

        .SBTTL  Put a data line
;++
;Finish a line up by inserting the length and the checksum and doing a PUT
;--

PUTLIN:
        MOVL    CHCOUNT,R10             ; Get the actual character count
        SUBL2   NULCOUNT,R10            ; Don't include the nulls since we
                                        ;  won't write them
        CLRL    NULCOUNT                ; Clear the null count since the
                                        ;  address will serve to insert nulls
        PUSHL   WTCOUNT                 ; Save it on the stack
        MOVZBL  #1,WTCOUNT              ; Move a one into the char count to get
        JSB     CVTH                    ;  to the length and then put length in
        POPL    WTCOUNT                 ; Restore the correct count

        MNEGL   CHKSUM,R10              ; Negate it
        JSB     CVTH                    ; Put the negative checksum into buffer

        JSB     PUT                     ; Put the line to disk
        RSB                             ; Return to sender


        .SBTTL  Create the header for the data line
;++
;This routine will put the starting stuff into the buffer
;R10 contains the record type
;--

HEADER: CLRL    CHKSUM                  ; Clear the checksum for this line
        CLRL    CHCOUNT                 ; Clear character count
        PUSHL   R10                     ; Save the record type
        MOVZBL  #^A/</,R10              ; Move a less-than into first char
        JSB     BUFFER                  ;   position of the buffer
        CLRL    R10                     ; Move a fake length into the buffer
        JSB     CVTH                    ;
        MOVZBL  ADDRESS+3,R10           ; Get the highest order byte of the
        JSB     CVTH                    ;  address and put into the buffer
        MOVZBL  ADDRESS+2,R10           ; Get the 2nd highest order byte of the
        JSB     CVTH                    ;  address and put into the buffer
        MOVZBL  ADDRESS+1,R10           ; Get the 2nd lowest order byte of the
        JSB     CVTH                    ;  address and put into the buffer
        MOVZBL  ADDRESS,R10             ; Get the lowest order byte of the
        JSB     CVTH                    ;  address and buffer it
        POPL    R10                     ; Get the line record type
        JSB     CVTH                    ;  and buffer the code
        RSB                             ; Return to sender

        .SBTTL  Output and input to/from terminal
;++
; Write data to terminal.
;       R10     Address of data to output
;       R1      Length of data
;--
WRITE:
        MOVW    R1,MSGDSC               ; Store the length in the descript blk
        MOVL    R10,ADDR                ; Store the address of the ASCII
        PUSHAQ  MSGDSC                  ; Push the descriptor block address
        CALLS   #1,G^LIB$PUT_OUTPUT     ; Do the output
        RSB                             ; Return to sender

;++
; Read from the terminal
;       R10     Address of buffer
;       R1      Number of characters to read
;       R11     Input prompt address
;       R12     Length of prompt
;
;Returned:
;       R0      Number of characters read
;--
READ:
        MOVL    R1,INP_STR_D            ; Store the buffer length in desc block
        MOVL    R10,INP_BUF             ; Store the buffer address in desc blk
        MOVL    R11,ADDR                ; Store prompt address in desc block
        MOVW    R12,MSGDSC              ; Store length in desctriptor block
        PUSHAB  INP_STR_LEN             ; Address for string length
        PUSHAQ  MSGDSC                  ; Push address of prompt descriptor blk
        PUSHAB  INP_STR_D               ; String buffer descriptor
        CALLS   #3,G^LIB$GET_INPUT      ; Get input string value
        MOVL    INP_STR_LEN,R0          ; Get actual input length back
        RSB                             ; Return to sender


        .SBTTL  RMS error routine
;++
;Check for RMS error
;--
RMSERR:
        BLBC    R0,60$                  ; If error, go check it out
        MOVL    #KNORMAL,R0             ; Set up a successful return code.
        RSB                             ; Return to caller

; Here if there is an error
60$:    CMPL    #RMS$_EOF,R0            ; Check for EOF
        BNEQ    70$                     ; If not then branch
        MOVL    #EOF,R0                 ; Tell sender we have end of file
        RSB                             ; Return

; Here if there is an RMS error we don't know how to handle
70$:    PUSHL   R0                      ; Save the error code
        MOVAB   M$RMS,R10               ; Get the address and length of the
        MOVL    #L$RMS,R1               ;   message to output
        JSB     WRITE                   ; Output it
        POPL    R0                      ; Get the error code back
        RET                             ; Exit program


        .SBTTL  Get a character from the file
;++
;Get a character from the input file.
;
;       Returned:
;               R10     Contains the character if not at end of file
;                       Contains #EOF if at end of file
;--

GET:    MOVL    RDCOUNT,R10             ; Get the offset into the buffer
        CMPL    R10,BUCOUNT             ; Check to see if we are past the end
        BNEQ    10$                     ; If not then branch
        MOVAL   RDRAB,R1                ; Get the RAB address
        $RAB_STORE RAB=R1,UBF=RDBUF,USZ=#512.
        $READ   R1                      ; Get the next buffer of data.
        JSB     RMSERR                  ; Check for file error
        CMPL    R0,#EOF                 ; Check for end of file error
        BNEQ    5$                      ; If not then branch
        MOVL    R0,R10                  ; Move the status to the correct spot
        RSB                             ; Return with error code
5$:
        MOVZWL  RAB$W_RSZ+RDRAB,R10
        MOVL    R10,BUCOUNT             ; Save the record size
        CLRL    R10                     ; Clear the pointer
        CLRL    RDCOUNT                 ; . . .
10$:    MOVZBL  RDBUF(R10),R10          ; Get the next character
        INCL    RDCOUNT                 ; Increment the offset into the buffer
        RSB                             ; Return to sender

        .SBTTL  Buffer a character of the data line
;++
; Buffer the character in R10
;--

BUFFER: PUSHL   R10                     ; Save the character on the stack
        MOVL    WTCOUNT,R10             ; Get the offset into the buffer
        CMPL    #512.,R10               ; Get too big?
        BGTR    BUFOK
        NOP
BUFOK:  MOVB    (SP),WTBUF(R10)         ; Move the character to the buffer
        TSTL    (SP)+                   ; Remove the junk
        INCL    WTCOUNT                 ; Increment the pointer
BUFRTS: RSB                             ; Return to sender


        .SBTTL  Put a record to the file
;++
;Write the record
;--

PUT:
        MOVL    WTCOUNT,R10             ; Get the count
        MOVAL   WTRAB,R1                ; Get the RAB address
        $RAB_STORE RAB=R1,RSZ=R10
        $PUT    R1                      ; Output the record
        JSB     RMSERR                  ; Check for file error
        CLRL    WTCOUNT                 ; Clear the counter for next record
        RSB                             ; Return


        .SBTTL  Convert to Hexadecimal ASCII digits
;++
; Convert a word to 2 ASCII hexadecimal digits
;--

CVTHEX:
        TSTL    R10                     ; See if this is a null
        BNEQ    CVTH                    ; If not then just branch
        INCL    NULCOUNT                ; A null so just increment the count
        RSB                             ;  for later and leave

; Convert a word to 2 ASCII hexadecimal digits without null compression
CVTH:   PUSHL   R10                     ; Save the character on the stack
10$:    TSTL    NULCOUNT                ; Check to see if there are nulls
        BEQL    20$                     ; If not then just branch
        CLRL    R10                     ; Put a null in R10
        JSB     CVT1                    ; Put the null in the buffer
        DECL    NULCOUNT                ; Decrement null counter
        BRB     10$                     ; Repeat
20$:    POPL    R10                     ; Get the original value back

CVT1:   ADDL2   R10,CHKSUM              ; Add the value to the checksum
        MOVL    R10,R1                  ; Save the value
        EXTZV   #4,#4,R10,R10           ; Get high order digit
        JSB     HEX                     ;   in place and convert to Hex
        JSB     BUFFER                  ; Buffer the Hex character
        EXTZV   #0,#4,R1,R10            ; Get right digit
        JSB     HEX                     ; Convert to Hex
        JSB     BUFFER                  ; Buffer the Hex character
        RSB                             ; Return to sender

HEX:    MOVL    R10,R2                  ; Move the base to R2
        CMPL    R2,#9.                  ; Check to see if above '9
        BLEQ    1$                      ; If not then branch
        ADDL2   #HEXOFFSET,R10          ; Add offset to alphabet
1$:     ADDL2   #48.,R10                ; Make ASCII
        RSB                             ; Return to sender


        .SBTTL  End of Hexify

        .END    HEXIFY
