.TITLE CBAF - Convert Binary to ASCII Formatted .IDENT /060983/ .ENABL LC ; ; ; Written by Bruce McClenahan ; 6-Sep-83 ; ; Version 180883/02 ; ; ;----------------------------------------------------------------------------- ; ; This module contains the CBAF macro and subroutine. The user may envoke the ; macro (or call the subroutine directly) to cause a 16-bit integer value to ; be converted to its equivalent OCTAL, DECIMAL, HEX or RAD50 representation ; in ASCII. ; ; The features of the module are ; ; A 16 bit number can be converted to its OCTAL, DECIMAL, ; HEX or RAD50 representation in ASCII. ; ; Leading zeros can optionally be displayed, suppressed or ; converted to spaces (ASCII 40). ; ; The field size (number of characters in output string) ; may be set from 1 to 7 by the caller. ; ; The output string may be left or right justified in a ; fixed field. ; ; ; While the routine may be called directly, it is strongly suggested that the ; macro be used to envoke the routine. ; ; ; Historical note: ; ---------------- ; ; This module is derived from the CNAF module in DBSLIB. ; Differences are as follows: ; ; Usage of registers R0 and R2 interchanged so: ; ; CNAF CBAF ; R0 FORMAT word binary NUMBER ; R2 binary NUMBER FORMAT word ; ; CBAF will also convert a RAD50 value to its ASCII equivalent (using ; the RAD50 character set for filenames (*,%) ) ; ; Some tidying up of the code to do RAD50 conversion and for efficiency. ; ; .SBTTL Modifications ; ; 6-Sep-83 Place code in CODE PSECT ; ; .SBTTL Documentation ... Register usage and argument passing ; ; At entry, the routine expects ; ; R0 to hold the NUMBER to be converted to ASCII ; R1 to hold the address of the output STRING ; R2 to hold the FORMAT word ; ; ; At exit ; ; R1 points to the next free byte in the output ; string (ie the byte directly following the ; last character stored in the string) ; ; PSW the "C" flag is set iff an error was detected. ; The C flag will be set if the number could not ; be output in the specified field or the field ; specifier is illegal. ; ; All other registers are preserved by the routine. ; ; ; The FORMAT word, passed over in R2 consists of 8, two bit niblets. ; Niblet 0 consist of bits 1:0, while niblet 7 consists of bits 15:14. ; ; Niblet 0 is the RADIX code, and selects the radix to be used for output. ; The radix codes are ; ; 0 --> Octal ; 1 --> Decimal ; 2 --> Hexadecimal ; 3 --> Radix-50 ; ; Niblets 7-1 are termed the field niblets. The output string can be at most ; 7 bytes long. Niblet 7 controls the left most byte, while niblet 1 controls ; the right-most byte of the string. ; ; The meaning of the different niblet codes are ; ; 00 Field not used - ie reduce output string by 1 byte ; 01 Suppress byte output if leading zero in this field ; 10 Convert leading zero to a space ; 11 Output leading zero as a '0' ; ; .SBTTL Documentation ... The FORMAT word ; ; The FORMAT word is the key to the operation of this module. The previous ; section explained how the format word is broken into niblets. Niblet 0 is ; used to determine which RADIX is to be used for output. This leaves 7 ; niblets. Each of these niblets is used to control the format of the output ; string. These niblets are effectively used as follows ; ; Starting from niblet 7, (ie bits 15:14), the possible number ; of bytes that can be output is reduced by 1 for all leading ; zero niblets. The output string can be 7 bytes long at most. ; This means that the user can make the output string from 1 ; to 7 bytes long. ; ; Once leading zero niblets have been used to determine the ; maximum number of bytes in the output string, the routine ; sees if the number can be represented in the remain number ; of bytes. If it can not, the remain field bytes are filled ; with stars (ie "*") to indicate field overflow and the ; routine exits flagging an error. ; ; The number is converted to its ASCII equivalent. If a ; leading zero is to be output, the field niblet is examined ; to see if the leading zero should be suppressed, output as a ; space, or output as a '0' digit. Note that once a digit has ; been output, the routine sets all following field specifier ; niblets to 3, thereby causing all subsequent zeros to be ; output as a '0'. ; ; .SBTTL Documentation ... CBAF macro ; ; The CBAF macro is included to simplify usage of this module. The macro can ; be extracted from this module using the LIBR.SAV program. ; ; The macro format is ; ; CBAF [FIELD] [RADIX] [NUMBER] [STRING] [REGSAV] ; ; The macro will declare CBAF as a global symbol automatically. ; If no arguments are specified, the macro defaults to a CALL to CBAF ; and the user must set up all registers. ; ; The [FIELD] argument is used to specify the format of the output string and ; the radix to be used. The [RADIX] argument can be specified only if the ; [FIELD] is specified; it is not necessary but is allowed so that the user ; may explicitly specify the radix. The [RADIX] argument can be replace with ; any one of the three strings OCTAL, DECIMAL or HEX. ; ; The [STRING] and [NUMBER] arguments may be specified to cause the macro ; to load R1 and R0 with the output string address and the number to be ; converted. Both these arguments are optional - if ommitted the user must ; set up R1 and R0 prior to calling the routine. ; ; The [REGSAV] argument, if omitted or replaced with the string "NO", causes ; the macro not to save R0, R1 and R2. This means that the contents of these ; registers may be destroyed by the macro (R1 will be changed by CBAF routine, ; R0 and R2 will be changed by the macro if the [FIELD] or [NUMBER] arguments ; are specified). If [REGSAV] is replaced by any string other than "NO", the ; macro will save the registers on the stack and replace them later. ; ; .SBTTL Documentation ... CBAF macro [FIELD] argument ; ; The [FIELD] argument is specified as a 1 to 7 character string. The string ; must be composed of the following letters ; ; LETTER MEANING ; ------ ------- ; O H D R character goes in this field ; S leading zero -> space ; F floating leading field - suppress if leading zero ; ; The size of the string determines the maximum size of the output ASCII ; string. The F and S characters should be used at the front of the string ; only, as they control what is to be done with leading zeros. The O, H and ; D characters mean that a digit is always to be output in that field position ; as well as specifying the radix ( O=octal D=decimal H=hex R=RAD50 ). ; ; The following examples demonstrate the function of the format specifier. ; (The output string is enclosed in braces so that the effect of leading ; and suppressed zeros can be seen.) ; ; Let NUMBER = R0 = 3100 (octal) = 1600 (decimal) = 640 (hex) = A (RAD50) ; ; FIELD RADIX ASCII OUTPUT ; ; OOOOOO {003100} ; DDDDD {01600} ; HHHH {0640} ; RRR {A } ; ; SSSSSO { 3100} ; FFFFFO {3100} ; SFFFFO { 3100} ; ; FFFFFFF OCTAL {3100} ; FFF RAD50 {A} ; ; SSO {***} error ; ; .SBTTL Definition CBAF Macro Convert binary --> ASCII ; ; ; The caller should envoke the CBAF routine via the ; CBAF macro. The macro format is ; ; ; CBAF [FIELD] [RADIX] [NUMBER] [STRING] [REGSAV] ; ; where ; ; [FIELD] is an optional field specifer string ; [RADIX] is an optional radix specifier ; [NUMBER] is the number to be converted (optional) ; [STRING] is the address of the output buffer (optional) ; [REGSAV] if not ommitted or "NO", causes reg preservation ; ; ; The macro functions as follows ; ; If [REGSAV] is not ommitted or identical to "NO", ; registers R0-R2 are saved on the stack and later ; restored. These registers are used for argument passing. ; ; If [NUMBER] and/or [STRING] are specified then the ; R0/R1 registers are set up. ; ; The [FIELD] string is decoded and the resulting format ; word built up. ; ; If [RADIX] is specified, the radix code is added to the ; format word. ; ; The CBAF routine is envoked via a call. ; ; .MACRO CBAF FIELD,RADIX,NUMBER=R0,STRING=R1,REGSAV=NO .GLOBL CBAF .IF DIF,REGSAV,NO MOV R0,-(SP) MOV R1,-(SP) MOV R2,-(SP) .ENDC .IIF DIF,NUMBER,R0, MOV NUMBER,R0 .IIF DIF,STRING,R1, MOV STRING,R1 .IF NB,FIELD ZZZZZZ = 0 ZZZZZB = 0 ZZZZZR = -1 .IRPC X, ZZZZZA = -1 .IIF IDN,X,O, ZZZZZA = ^B11 .IIF IDN,X,D, ZZZZZA = ^B11 .IIF IDN,X,H, ZZZZZA = ^B11 .IIF IDN,X,R, ZZZZZA = ^B11 .IF NZ,ZZZZZA+1 .IF Z,ZZZZZR+1 .IIF IDN,X,O, ZZZZZR = 0 .IIF IDN,X,D, ZZZZZR = 1 .IIF IDN,X,H, ZZZZZR = 2 .IIF IDN,X,R, ZZZZZR = 3 .ENDC .ENDC .IIF IDN,X,F, ZZZZZA = ^B01 .IIF IDN,X,S, ZZZZZA = ^B10 .IF Z,ZZZZZA+1 .ERROR FIELD ; ILLEGAL F I E L D SPECIFIER .MEXIT .ENDC ZZZZZZ = + ZZZZZA ZZZZZB = ZZZZZB + 1 .ENDR .IF GT,ZZZZZB-7 .ERROR FIELD ; F I E L D SIZE TO LARGE .MEXIT .ENDC .IF NB,RADIX .IF Z,ZZZZZR+1 .IIF IDN,RADIX,OCTAL, ZZZZZR = 0 .IIF IDN,RADIX,DECIMAL, ZZZZZR = 1 .IIF IDN,RADIX,HEX, ZZZZZR = 2 .IIF IDN,RADIX,RAD50, ZZZZZR = 3 .ENDC .ENDC .IF Z,ZZZZZR+1 .ERROR RADIX ; ILLEGAL R A D I X SPECIFIER .MEXIT .ENDC ZZZZZZ = ZZZZZZ*4 MOV #,R2 .ENDC CALL CBAF .IF DIF,REGSAV,NO MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 .ENDC .ENDM CBAF ; ; .SBTTL Declarations ; .GLOBL CBAF ; Entry point ; ; .PSECT CODE ; ====== ==== ; ; .SBTTL Routine CBAF Binary R0 -> ASCII string @R1 ; ; ; Save all registers except R1 ; CBAF: .IRP X,<0,2,3,4,5> ; ---- For all registers ---- MOV R'X,-(SP) ; Save register .ENDR ; --------------------------- ; ; ; Set up registers such that ; ; R2 = Field format ; R4 = * 2 (table offset) ; R5 -> Radix table to use ; ; MOV R2,R4 ; R4 = Format word BIC #3,R2 ; Eliminate radix specifier BEQ ABORT ; All fields null --> ABORT BIC #^C3,R4 ; Leave radix specifier only ASL R4 ; R4 = 2 * R4 MOV RADTAB(R4),R5 ; R5 -> Radix table ; ; ; ; R2 holds the field specifier. ; Each field (max of 7) has a 2 bit field specifier niblet. ; Discard all fields that have a 00 niblet, as these fields are not ; to be used. When get a non-zero niblet goto 1400$. ; 1000$: BIT #140000,R2 ; Null field? BEQ 1100$ ; Yes -> skip ; ; ; Field niblet is non-zero. See if base value is non-zero. ; If it is can then enter normal process loop. ; If is zero, then must pad field until get a non-zero base value. ; TST (R5) ; Base value = 0 ? BNE 2000$ ; No --> skip CALL ZERTIM ; Yes -> place 0 in string ; 1100$: ASL R2 ; Discard ASL R2 ; field niblet TST -(R5) ; R5 -> Next base value BR 1000$ ; Loop ; ; ; Check to ensure that number can fit in specified field. ; If it cannot, fill field with stars (as FORTRAN does) and abort! ; 2000$: TST 2(R5) ; Previous radix base = 0 ? BEQ 3000$ ; Yes -> number fits in field CMP R0,2(R5) ; Can number fit in field ? BLO 3000$ ; Yes -> skip ; 2100$: MOVB #'*,(R1)+ ; No --> indicate overflow TST -(R5) ; End of base value table ? BNE 2100$ ; No --> loop BR ABORT ; Yes -> exit ; ; ; ; In this section we want to convert the number to ASCII. ; ; This is done using R3 as an accumulator. Subtract the base value (@R5) ; from the number (incrementing R3 each time) until the base value is ; larger than the remainder. When we cannot "divide" any more, convert ; R3 to ASCII and store it in the output string. Repeat till all done. ; ; Must remember that if R3 is 0, must then use field specifier ; to see what is to be stored in output string. ; 3000$: CLR R3 ; Zero accumulator ; 3100$: CMP R0,(R5) ; R0 < base value ? BLO 3400$ ; Yes -> skip SUB (R5),R0 ; R0 = R0 - base INC R3 ; Up accumulator BR 3100$ ; Loop ; 3400$: TST R3 ; Zero value ? BNE 3500$ ; No --> skip CALL ZERTIM ; Process a zero BR 3600$ ; Skip ; 3500$: ADD CHRTAB(R4),R3 ; R3 -> Character in table BIS FMTTAB(R4),R2 ; R2 = New format MOVB (R3),(R1)+ ; Place character in string ; 3600$: ASL R2 ; Discard field ... ASL R2 ; ... niblet TST -(R5) ; End of table ? BNE 3000$ ; No --> loop ; ; ; ; Normal exit point - Clear CARRY and restore registers ; TST (PC)+ ; Clear carry and skip ; ; ; Error exit point - Set CARRY and restore registers ; ABORT: SEC ; Set carry ; ; ; Restore registers ; .IRP X,<5,4,3,2,0> ; --- For all registers --- MOV (SP)+,R'X ; restore register .ENDR ; ------------------------- ; RETURN ; Done ; ; .SBTTL Routine ZERTIM Process a zero value ; ; This routine is called when a zero must be output. ; ; It expects R1 -> Output string ; R2 = Format word ; R3 = Work register ; ; ; The routine uses the format niblet to determine what character ; should be output. ; ZERTIM: TST R2 ; Test msb of format niblet BPL 2000$ ; Zero -> nothing to output MOV #40,R3 ; Ready for a space BIT #40000,R2 ; Test lsb of format niblet BEQ 1000$ ; Zero -> output a space MOV @CHRTAB(R4),R3 ; Set --> output first char BIS FMTTAB(R4),R2 ; R2 = New format 1000$: MOVB R3,(R1)+ ; Place character in string 2000$: RETURN ; Done ; ; .SBTTL Table RADTAB Radix base values ; ; RADTAB: .WORD 10$ ; Octal .WORD 11$ ; Decimal .WORD 12$ ; Hex .WORD 13$ ; RAD50 ; ; .WORD 0 ; End of last table ; .WORD 1,10,100,1000,10000,100000 ; 10$: .WORD 0 ; Octal ; ; .WORD 1.,10.,100.,1000.,10000.,0 ; 11$: .WORD 0 ; Decimal ; ; .WORD 1,20,400,10000,0,0 ; 12$: .WORD 0 ; Hex ; ; .WORD 1,50,<50*50>,<50*50*50>,0,0 ; 13$: .WORD 0 ; RAD50 ; ; .SBTTL Tables ... FMTTAB, CHRTAB, DIGTAB, R50TAB ; ; This table defines the new format value which is to be set ; after a character is output to ensure embedded zeros are dislayed ; if required (ie numeric bases). ; ; FMTTAB: .WORD -1 ; Octal .WORD -1 ; Decimal .WORD -1 ; Hex .WORD 0 ; RAD50 ; ; ; This table determines which character set is to be used ; for each radix when converting to ASCII ; CHRTAB: .WORD DIGTAB ; Octal .WORD DIGTAB ; Decimal .WORD DIGTAB ; Hex .WORD R50TAB ; RAD50 ; ; ; When a character is to be output, these tables are indexed into ; to find the ASCII equivalent character. ; R50TAB: .ASCII / ABCDEFGHIJKLMNOPQRSTUVWXYZ$%*/ ; DIGTAB: .ASCII /0123456789ABCDEF/ ; ; .END