.TITLE CNAF .IDENT /1408.1/ ; ; ; ; Written by Ray Di Marco ; 14-Aug-81. ; ; ; Version 140881/01. ; ; ;_______________________________________________________________________ ; ; ; This module contains the "CNAF" macro and routine. The user may envoke ; the macro (or the routine directly) to cause a 16 bit integer value ; to be translated to its equivalent OCTAL, DECIMAL or HEX ascii ; representation. The features of the module are ; ; A 16 bit number can be converted to OCTAL, DECIMAL or ; HEX ascii representation. ; ; Leading zeros can optionally be 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 envoked directly by the caller, it is strongly ; suggested that the macro be used to envoke the routine. ; ; ; ; ; ; .SBTTL DOCUMENTATION - REGISTER USAGE AND ARGUMENT PASSING ; ; ; At entry, the routine expects ; ; R0 to hold the format word ; R1 to hold the address of the output string ; R2 to hold the number to be converted to ascii ; ; At exit ; ; R1 points to the next free byte in the output ; string (ie the byte dirrectly 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 R0 consists of 8, two bit niblets. Niblet ; 0 consist of bits 1:0, while niblet 7 consists of bits 15:14. Niblet 0 ; is termed the RADIX code, and selects the radix to be used for output. ; Niblets 7-1 are termed the field niblets, and determine which fields are ; used etc. The radix codes are ; ; 0,3 --> octal ; 1 --> decimal ; 2 --> hexadecimal ; ; 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 - CNAF MACRO ; ; ; The CNAF 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 ; ; CNAF [FIELD] [RADIX] [NUMBER] [STRING] [REGSAV] ; ; If no arguments are specified, the macro defaults to a CALL to CNAF. ; The macro will declare CNAF asa global symbol automatically. In the ; event of no arguments being specified, the user must set up all ; registers. ; ; The [STRING] and [NUMBER] arguments may be specified to cause the macro ; to load R1 and R2 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 R2 prior to calling the routine. ; ; The [REGSAV] argument, if ommitted 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 CNAF 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 then later. ; ; 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. ; ; ; ; ; ; .SBTTL DOCUMENTATION - CNAF 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 digit 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). ; ; The following examples demonstate the function of the format specifiers. ; The output string is enclosed in brackets so that the effect of leading ; and suppressed zeros can be seen ; ; NUMBER FIELD SPECIFIER ASCII OUTPUT ; 1!10 OOOOOOO {0000001} ; FFFFFFF {1} ; SSSSSSS { 1} ; SSFFFOO { 01} ; 1000!10 DDD {***} error ; 256!10 HHH {100} ; ; 1!10 SSFFFFD { 1} ; 10!10 SSFFFFD { 10} ; 100!10 SSFFFFD { 100} ; 1000!10 SSFFFFD { 1000} ; ; ; ; ; .SBTTL DOCUMENTATION - SPECIAL CASE - ZERO FORMAT WORD ; ; ; ; If R0 is zero at entry (ie ZERO format word specified), the routine ; will use the contents of the CNAF$$ word as the format control specifier. ; This mechanism is implement to allow DEAFULT output to be performed. A ; main routine may set up CNAF$$ to hold the required format word. Subordinate ; rourines may then envoke CNAF with R0 set to 0. This will cause the default ; format to be used. ; ; ; ; ; ; .SBTTL DECLARATIONS ; ; ; .GLOBL CNAF ; ENTRY POINT .GLOBL CNAF$$ ; DEFAULT FORMAT ; ; ; .PSECT CODE ; OPEN CODE SECTION ; ====== ==== ; ; ; ; ; ; .SBTTL MACRO - "CNAF" ... NUMBER --> ASCII ; ; ; ; THE CALLER SHOULD ENVOKE THE "CNAF" ROUTINE VIA THE ; "CNAF" MACRO. THE MACRO FORMAT IS ; ; ; CNAF [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 PRESIVATION ; ; ; 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 ; R2/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. ; ; "CNAF" IS ENVOKED VIA A CALL. ; ; ; .MACRO CNAF FIELD,RADIX,NUMBER,STRING,REGSAV=NO .GLOBL CNAF .IF DIF,REGSAV,NO MOV R0,-(SP) MOV R1,-(SP) MOV R2,-(SP) .ENDC .IIF NB,NUMBER,MOV NUMBER,R2 .IIF NB,STRING,MOV STRING,R1 .IF NB,FIELD ZZZZZZ = 0 ZZZZZB = 0 ZZZZZR = 0 .IRPC X, ZZZZZA = -1 .IIF IDN,X,H,ZZZZZA = 3 .IIF IDN,X,D,ZZZZZA = 3 .IIF IDN,X,O,ZZZZZA = 3 .IF NE,ZZZZZA+1 .IF EQ,ZZZZZR .IIF IDN,X,H,ZZZZZR=2 .IIF IDN,X,D,ZZZZZR=1 .ENDC .ENDC .IIF IDN,X,S,ZZZZZA = 2 .IIF IDN,X,F,ZZZZZA = 1 .IF EQ,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 ZZZZZZ = ZZZZZZ*4 .IF NB,RADIX ZZZZZR = -1 .IIF IDN,RADIX,OCTAL, ZZZZZR = 0 .IIF IDN,RADIX,DECIMAL, ZZZZZR = 1 .IIF IDN,RADIX,HEX, ZZZZZR = 2 .IF EQ,ZZZZZR+1 .ERROR RADIX ; ILLEGAL R A D I X SPECIFIER .MEXIT .ENDC .ENDC MOV #ZZZZZZ!ZZZZZR,R0 .ENDC CALL CNAF .IF DIF,REGSAV,NO MOV (SP)+,R2 MOV (SP)+,R1 MOV (SP)+,R0 .ENDC .ENDM CNAF ; ; ; .SBTTL ENTRY - "CNAF" ... BINARY R2 -> ASCII STRING @ R1 ; ; ; SAVE ALL REGISTERS EXCEPT R1 ; CNAF: .IRP X,<0,2,3,4,5> ; ---- FOR ALL REGISTERS ---- MOV R'X,-(SP) ; SAVE REGISTER .ENDR ; --------------------------- ; ; ; ; IF R0 = 0 THEN NO FORMAT HAS BEEN SPECIFIED. IN THAT ; EVENT USE DEFAULT FORMAT STORED IN "CNAF$$" VARIABLE! ; ; TST R0 ; R0 == 0? BNE 100$ ; NOT 0 --> 100$ MOV CNAF$$,R0 ; USE DEFAULT FORMAT ; ; ; ; SET UP REGISTERS SUCH THAT ; ; R0 == FIELD FORMAT ; R1 == STORE POINTER ; R2 == NUMBER TO CONVERT ; R4 == MAXIMUM FIELD SIZE ; R5 == RADIX TABLE TO USE ; ; 100$: MOV R0,R5 ; FORMAT WORD -> R5 BIC #3,R0 ; ELIMINATE RADIX SPECIFIER BEQ ABORT ; ALL FIELDS NULL -> ABORT BIC #^C3,R5 ; LEAVE RADIX SPECIFIER ONLY ASL R5 ; 2*R5 -> R5 MOV RADTAB(R5),R5 ; R5 --> RADIX TABLE MOV #7,R4 ; R4 === MAX FIELD SIZE ; ; ; ; ; ; ; R0 HOLDS FIELD SPECIFIER. EACH FIELD (MAX OF 7) HAS A ; 2 BIT FIELD SPECIFIER NIBLET. DISCARD ALL FIELDS THAT ; HAVE A 0 NIBLET, AS THESE FIELDS ARE NOT TO BE USED. ; WHEN GET A NON-ZERO NIBLET GOTO 1400$. ; ; 1000$: BIT #140000,R0 ; NULL FIELD? BNE 1400$ ; NO -> SKIP 1200$: TST (R5)+ ; NEXT BASE VALUE ASL R0 ; DISCARD NULL ... ASL R0 ; ... FIELD NIBLET DEC R4 ; DOWN COUNTER BR 1000$ ; LOOP ; ; ; ; 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 UNTILL GET A NON-ZERO BASE VALUE. ; ; 1400$: TST (R5) ; BASE = 0? BNE 2000$ ; NO -> SKIP CALL ZERTIM ; STUFF A 0 IN OUTPUT STRING BR 1200$ ; NEXT FIELD ; ; ; ; ; ; ; 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) ; PREVIOS RADIX BASE = 0? BLOS 3000$ ; YES -> NUMBER MUST FIT IN FIELD CMP R2,-2(R5) ; CAN NUMBER FIT IN FIELD? BLO 3000$ ; YES -> SKIP 2100$: MOVB #'*,(R1)+ ; INDICATE THAT ... SOB R4,2100$ ; ... HAVE OVERFLOW BR ABORT ; ABORT TIME ; ; ; ; ; ; IN THIS SECTION WANT TO CONVERT NUMBER TO ASCII. DO THIS ; BY USING R3 AS AN ACCUMULATOR. SUBTRACT BASE VALUE (@R5) ; FROM NUMBER (INCREMENT R3 EACH TIME) UNTILL BASE VALUE IS ; LARGER THAN REMAINER. WHEN CANNOT "DIVID" ANY MORE CONVERT ; R3 TO ASCII AND STORE IN 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. NATURALLY, ONCE ; HAVE PRODUCED ONE NON-ZERO DIGIT FIELD SPECIFIER IS IRRALEVANT. ; ; ; 3000$: CLR R3 ; ZERO ACCUMULATOR 3100$: CMP R2,(R5) ; R2 > OR = BASE VALUE ? BLO 3400$ ; YES -> SKIP SUB (R5),R2 ; R2 - BASE --> R2 INC R3 ; UP ACCUMULAROR BR 3100$ ; LOOP ; 3400$: TST R3 ; R3 = 0? BNE 3500$ ; NO -> SKIP CALL ZERTIM ; TIME TO PROCESS A ZERO BR 3600$ ; RE-JOIN MAIN-LINE ; 3500$: MOVB DIGTAB(R3),R3 ; CHARACTER -> R3 MOV #-1,R0 ; RESET FIELD SPEC MOVB R3,(R1)+ ; SAVE CHARACTER ; 3600$: ASL R0 ; DISCARD FIELD ... ASL R0 ; ... NIBLET TST (R5)+ ; NEXT BASE VALUE SOB R4,3000$ ; LOOP TILL ALL DONE ; ; ; ; ; ; ; NORMAL EXIT POINT - RESTORE REGISTERS AND LEAVE WITH THE ; "C" FLAG CLEAR. ; ; ; ; .IRP X,<5,4,3,2,0> ; --- FOR ALL REGISTERS --- MOV (SP)+,R'X ; RESTORE REGISTER .ENDR ; ------------------------- CLC ; CLEAR ERROR FLAG RETURN ; EXIT ; ; ; ; ; ABORT EXIT POINT - RESTORE REGISTERS AND LEAVE WITH THE ; "C" FLAG SET. ; ; ABORT: .IRP X,<5,4,3,2,0> ; --- FOR ALL REGISTERS --- MOV (SP)+,R'X ; RESTORE REGISTER .ENDR ; ------------------------- SEC ; SET FAIL FLAG RETURN ; EXIT ; ; ; ; ; ; .SBTTL PRIMITIVE - "ZERTIM" ... ENCOUNTER A ZERO IN OUTPUT STREAM ; ; ; THIS ROUTINE IS CALLED IF A ZERO MUST BE OUTPUT. IT EXPECTS ; ; R0 == FORMAT WORD ; R1 == OUTPUT POINTER ; R3 == SPARE WORK REGISTER ; ; THE ROUTINE USES THE FORMAT NIBLET (MSB:MSB-1 OF R0) TO ; INDEX INTO THE 'ZERTAB' TABLE TO SEE HOW THE ZERO SHOULD ; BE OUTPUT. IF THE ZERO IS TO BE OUTPUT AS A ; ; [NULL] NOTHING IS OUTPUT ; <40> A SPACE IS OUTPUT ; '0 A '0' IS OUTPUT ; ; THE FORMAT WORD IS UPDATE BY THIS ROUTINE SO THAT THE ; NIBLET FOR THE NEXT FIELD CAN BE ACCESSED. ; ; ; ; ZERTIM: MOV R0,R3 ; FIELD SPEC -> R3 ROL R3 ; MSB --> C ROL R3 ; MSB --> LSB ROL R3 ; MSB --> LSB+1 BIC #^C3,R3 ; IGNORE RUBBISH MOVB ZERTAB(R3),R3 ; CHARACTER -> R3 BEQ 1000$ ; NULL -> SKIP MOVB R3,(R1)+ ; OUTPUT CHARACTER CMP R0,#'0 ; OUTPUT = '0' BNE 1000$ ; NO -> SKIP MOV #-1,R0 ; OUTPUT 0 FROM NOW 0N FOR ZERO 1000$: RETURN ; EXIT ; ; ; ; ; ; .SBTTL TABLE - "RADTAB" ... RADIX BASE TABLES ; ; ; RADTAB IS INDEXED VIA RADIX CODE SPECIFIED IN FORMAT ; WORD TO SELECT ONE OF THE 4 POOSIBLE RAXIX TABLES. THE ; SELECTED TABLE IS THEN USED (INSTEAD OF DIVISIONS) TO ; CONVERT THE NUMBER TO ASCII. ; ; ; ; ; RADTAB: .WORD 10$,11$,12$,13$ ; RADIX CODE = 0,1,2,3 ; 10$: .WORD 0,100000,10000,1000,100,10,1 11$: .WORD 0,0,10000.,1000.,100.,10.,1. 12$: .WORD 0,0,0,10000,400,20,1 13$: .WORD 0,100000,10000,1000,100,10,1 ; ; ; ; ; .SBTTL CONVERSION TABLE - "ZERTAB" AND "DIBTAB" ; ; ; ; WHEN A ZERO IS TO BE OUTPUT, 'ZERTIM' INDEXS INTO ; "ZERTAB" USING THE FIELD NIBLET TO DETERMIN WHAT ; SHOULD BE OUTPUT. ; ; ; ; ZERTAB: .BYTE 0,0,40,'0 ; ; ; ; ; WHEN A DIGIT IS TO BE OUTPUT, THIS TABLE IS INDEXED ; INTO TO FIND THE ASCII EQUIVALENT OF THE DIGIT. ; ; DIGTAB: .ASCII /0123456789ABCDEF/ ; ; ; ; ; ; .SBTTL VARIABLES ; ; ; .PSECT $SCRTH ; OPEN SCRATCH AREA ; ====== ====== ; ; CNAF$$: .WORD ^B0011111111111100 ; DEFAULT OUTPUT FORMAT ; ; .END