Formatted Output for ATARI Basic
Joseph J. Wrobel
Many folks tell me that they must struggle to produce nicely formatted output when using ATARI Basic due to the lack of the TAB function and the PRINT USING command. Struggle no more. When used together, the two subroutines presented in this article can provide formatted output simply and directly in ATARI Basic. Both numerics and strings are supported. The type, arrangement and format of variables which appear on one output line are controlled on a line-by-line basis by the main program. The number of variables in one output is limited only by the character width of the output device. The output device can be the TV screen or any type of printer, ATARI or otherwise.
The approach to formatted output used here employs two subroutines. The purpose of the first is to construct a line for output in a string variable set aside for this purpose. Each time the subroutine is called, it inserts the data sent to it by the main program at the selected position in the string and in the format requested. When all the data to be printed in the current line has been positioned, the second subroutine is called. This subroutine merely prints the output line string on the appropriate device, then clears the string (fills it with spaces) to prepare it for the next line of data.
A sample program using the routines is given in Listing 1. Line 10 is required to set aside the strings to be used in the subroutines. LINE$ is the string which will ultimately contain the formatted output line. It is dimensioned to a size one less than the character width of the output device. In the example, for the 38 character wide default screen size, it is dimensioned to hold up to 37 characters by setting NC to 37. Line 20 initializes LINE$ to a string of spaces. N$ is a string used to temporarily store each data item. It should be big enough to hold the largest of your data items. To be on the safe side, its length is set equal to that of LINE$.
The actual subroutines of interest reside in lines 1000-1070 and lines 2000-2010 respectively. The latter routine, as noted above, simply prints LINE$ (line 2000) then fills it with spaces (line 2010) in preparation for the next output line. If, instead of the screen, a printer is the output device, then the PRINT of line 2000 may simply be replaced by an LPRINT or a PRINT # command, whichever is appropriate.
The routine starting at line 1000 actually does the formatting and has two different entry points depending on whether the data item is a string or a numeric. The case of a string is the simpler of the two, so let's consider it first. To position a string you must place the string in variable N$, specify the column position (RC) against which it is to be right justified, then GOSUB 1060. Three examples of this calling sequence are given in lines 100-120. At line 1060 the program first calculates LC, the leftmost character position of the data item. Then, if the column boundaries are acceptable, it inserts N$ in LINE$ and RETURNs.
To position a numeric, you must put the data item into variable N, specify ND, the number of digits to the right of the decimal point you wish printed, specify RC as defined above, then GOSUB 1000. See lines 150-170 for examples of this calling sequence. In lines 1000-1010 N is rounded to the appropriate number of decimals. In 1020 the string N$ is defined. If the number is to be printed as an integer (ND = 0), N$ is correct as is and the jump is taken to 1060 to insert N$ into LINE$. For non-integer formats, the decimal point and any trailing zeroes which were dropped in forming the string representation of the number must be restored. This is done in lines 1030-1050. N$ is then ready for insertion into LINES.
Output from the sample program as printed on an ATARI 820 printer (using LPRINT in 2010) is shown in Figure 1. Note that the numbers are rounded for presentation with the requested precision, that the decimal points of each column neatly line up, that all trailing zeroes are present and that negative numbers are also accommodated. Also note how easy it is to line up the column headings with the data by simply specifying the appropriate value of RC when printing them (see lines 100 & 150, 110 & 160, 120 & 170).
The routines run fairly rapidly, but if you need some extra speed, the loop in line 2010 can be avoided. To do this, dimension a string, let's call it MT$, the same length as LINE$ and fill it with spaces just once at the start. Then, line 2010 can be replaced by 2010 LINE$ = MT$:RETURN
If you have a slow printer like a teletype, you may also gain some speed by stripping the trailing spaces from LINE$ before printing it. This can be done by replacing line 2000 with the three lines given below.
2000 FOR I = NC TO 1 STEP -1:IF LINE$(I,I)< >" " THEN 2004 2002 NEXT I 2004 PRINT LINE$(1,I)
1 REM ** FORMATTED OUTPUT EXAMPLE ** 2 REM JOE WROBEL, ROCHESTER, NY 3 REM SUBROUTINE VARIABLES - I, LC, N, NC, ND, NZ, RC LINE$, N$ 10 NC=37: DIM LINE$(NC), N$(NC) 20 GOSUB 2010 100 N$="X": RC=7 :GOSUB 1060 110 N$="X/32": RC=17: GOSUB 1060 120 N$="SIN(PI*X/8)": RC=33: GOSUB 1060 130 GOSUB 2000 140 FOR X=0 TO 15 150 N=X: ND=0: RC=7: GOSUB 1000 160 N=X/32: ND=3: RC=17: GOSUB 1000 170 N=SIN(4*ATN(1)*X/8): ND=7: RC=32: GOSUB 1000 180 GOSUB 2000: NEXT X 190 STOP 1000 I=INT(10^ND+0.5) 1010 N=INT(I*N+0.5)/I 1020 N$=STR$(N): IF ND=0 THEN 1060 1030 IF N=INT(N) THEN N$(LEN(N$)+1)="." 1040 NZ=ND+1+LEN(STR$(INT(N)))-LEN(N$) 1050 IF NZ>0 THEN FOR I=1 TO NZ: N$(LEN(N$)+1)="0": NEXT I 1060 LC=RC+1-LEN(N$): IF LC<=RC AND RC<=NC AND LC>=1 THEN LINE$(LC,RC)=N$ 1070 RETURN 2000 PRINT LINE$ 2010 FOR I=1 TO NC: LINE$(I,I)=" ": NEXT I: RETURN