Chapter 7
Math and Printops:

Range Checking and
Formatted Output

Math, a short subprogram, has a rather limited job. It is designed to turn the ASCII number following the + pseudo-op into a two-byte integer and to save it in the variable ADDNUM. Later, when the final RESULT is calculated by the Valdec subprogram, anything in ADDNUM will be added to RESULT. Math responds to a source code line like:

100 SCREEN = \$0400
120 LDA SCREEN+256; this would assemble as \$0500

As with the BYTE pseudo-op, the + pseudo-op allows only decimal numbers as an argument following the +.
The first loop in the Math subprogram simply looks along the LABEL buffer to locate the +. Thus, it doesn't matter if the + is right next to its label. You could write SCREEN      +256 as well as SCREEN+256. However, finding the +, the subroutine expects to find no spaces between the + and the number to be added. +256 is correct. + 256 would be incorrect. This allows us to test for a variety of end-of-number conditions. That means that you can use the + pseudo-op within such addressing modes as LDA (SCREEN+256),Y or LDA 1500+25,Y.
Each character following the + is stored in HEXBUF for later translation by Valdec. Each is also tested to see if it is a nonnumber-if it is outside the range from 47 to 58, the ASCII code for the digits 0-9. Anything outside that range ends our storage of the number to be added, and we go down to put the number into ADDNUM.
Range checking is simple enough. Just remember to test against a number which is one lower than the low end and one higher than the high end of the range. For example, to see if a number is lower than \$30, you must test against \$2F. That's because BCC tests for lower than. \$30 wouldn't be lower than \$30. The same thing works on the high end. To test for numbers higher than \$39, you CMP #\$3A.
After the number is set up in HEXBUF, we point TEMP to it, JSR to Valdec, and move the result from RESULT into the variable ADDNUM. It will wait there until, on pass 2, the Array subprogram makes the addition adjustment in line 1160.

Printops: The Output Routine
One important function performed by the Printops subprogram is raising the PC (Program Counter). A subroutine called INCSA (650) increases the PC by one for each object code byte, whether this byte is an opcode or the argument of an opcode. Printops' other main job is to send each byte of object code to one of four places: RAM memory, disk, screen, or printer.
Because each object code byte can go to any one, or all, of these four different destinations, there are a series of tests and parallel routines within Printops. For one thing, Printops has little to do on pass 1-it does raise the PC, but nothing is POKEd anywhere or printed to screen or printer until the second pass.
Also, Printops has three entry points, depending on whether the Eval subprogram has assembled a one-, two-, or three-byte logical line. An INY would only JSR from Eval to FORMAT, right at the start of Printops. FORMAT loads the OP (opcode) and stores it and prints it as required. It's a single-byte event. LDA 15 first JSRs to FORMAT to output the opcode, the numeric equivalent of LDA, then enters at PRINT2. LDA 1500 would JSR FORMAT to send the opcode, then enter at PRINT3. These entry decisions are made by Eval after it has determined whether it's dealing with a one-, two-, or three-byte addressing mode.
FORMAT (20) simply raises the PC by one. It does this with a JSR to INCSA (40) on pass 1. On pass 2, however, it also checks to see if screen printout was requested (60). If so, it restores normal I/O and prints the number (120). As we will see, PRINTNUM also prints to the printer, if that was requested. Then the opcode is POKEd to disk or RAM, if that was requested. The POKEIT subroutine performs POKES to RAM. POKEIT also leads right into INCSA to raise the PC automatically following each POKE. Finally we RTS back to Eval (160). So much for a single-byte addressing mode.

PRINT2 (180) handles LDA 15 and other two-byte addressing modes. Like FORMAT, pass 1 only results in a JSR INCSA (to raise the PC). Pass 2 follows the same pattern as FORMAT, explained above. The major difference is that the number fetched before the JSR to PRINTNUM comes from the low byte of the RESULT variable (240) rather than OP. This is a single-byte argument addressing mode.
PRINT3 (290) parallels the two previous routines, except that it handles a two-byte argument. On pass 1 it JSRs to INCSA twice to raise the PC by two.
On pass 2, it prints (370) and POKEs (390) the low byte of RESULT if requested and then prints (460) and POKEs (480) the high byte of the argument, RESULT+1. A formatting problem is handled in line 420. HXFLAG shows whether or not output to screen and printer is supposed to be in hex. If this flag is set, we don't need to print a space between the low and high bytes of the argument. The hex printing routine will do that for us. If printout is in decimal, though, we need to print a space (440).

Creating an Object Program
POKEIT (490) stores the byte in the X Register at the current PC address if the POKEFLAG is up. This flag indicates that the programmer used the .O pseudo-op, requesting that object code be stored in RAM memory during assembly. For both PRINTNUM and POKEIT, the X Register is holding the opcode or argument. X is saved in the variable WORK+ 1; some of the disk management routines below will change the value of X, so we must preserve it for later use.
Then the DISKFLAG is checked (550). It indicates that the programmer used the D pseudo-op, asking that an object code program file be created on disk during assembly. If not, we just go down to raise the PC at INCSA (560).
But if an object program is being created on disk, LADS opens communication to file #2 (the write-to-disk file) and recovers the byte from WORK+ 1 (600). The PRINT in 610 will not go to screen or printer. Rather, the current channel is open to the disk object file and PRINT therefore sends the byte in the Accumulator to the disk. Then normal I/O is restored, and file #1 is accessed again. File #1 is the normal input source for LADS, the read-from-disk channel. Finally, we fall through to INCSA (650).
Although it is one of the simplest events in LADS, INCSA is also one of the most important. On both passes, INCSA raises the PC by 1 for each opcode byte and for each argument byte. Much depends on the fact that INCSA keeps the Program Counter accurate during assembly. A single ignored byte would throw off all address-type labels which followed. (The HERE in 100 HERE LDA 15 is an address-type label.) In consequence, the entire assembled object program would be useless. INCSA just adds 1 to SA (the variable which holds the LADS internal Program Counter). Notice lines 690-710. They add 0 to the high byte of SA. What's the point of that?

The 256th Increment
For every 255 increments, INCSA will have nothing to add to the high byte of SA. But on the 256th increment, it must add 1 to the high byte. How does adding 0 to the high byte add 1 to it? The carry flag. ADC means ADd with Carry. If the carry flag is set, the high byte is incremented. If the low byte is holding 255 when we add 1 to it (670), that will set the carry flag.
The rest of the routines in this Printops subprogram handle the printout of a variety of things: messages, spaces, numbers, the PC address, a carriage return, a source code line number, a source code line, or an error message. And each of these printto-screen routines has a sister routine. There is a parallel series of routines which print the same thing to the printer.
PRNTMESS (740) will print any ASCII message. There are two special requisite preconditions: The message must be pointed to by the variable TEMP, and the message must end with a 0. PRNTMESS is a simple loop, but it can print any message you want. First the Y Register is set to 0 to act as an index to the message within LADS' source code. Then the loop begins (750) by loading in a character from the message (750). If the character is 0, we exit the loop. Otherwise, the character is printed to the screen. Then we JSR to the sister routine, PTP, which will send the same character to the printer, if requested (780). The Y Register is raised, and we go back for the next character (800).
PRINTSPACE (820) simply prints a space character to the screen and then checks with its sister routine, PTP, to see if the space should also be printed on the printer.
Before printing a number, we first put it into the X variable for safekeeping. Then LADS has to make four tests: Is it printout to screen or to printer, and is it in decimal or in hex numbers? PRNTNUM (860) takes advantage of a routine in BASIC ROM if LADS' printout is in decimal (requested with the NH, no hex, pseudo-op). When you ask BASIC to list a program, it turns integer bytes into printable ASCII numbers to provide line numbers on the screen. On Commodore computers, the high byte of the integer is put into the Accumulator, the low byte into the X Register, and you JSR to within BASIC ROM where this routine resides (950). In LADS, the address of this ROM routine is called OUTNUM. It's defined for each different computer model in the Defs subprogram.

Hex Default
LADS' default, and probably the most common way to print out numbers during an assembly, is hex. LADS itself handles hex printing. If the HXFLAG is up (870), we JSR to HEXPRINT, a subroutine at the end of the Printops subprogram. We'll get to it in a minute. It's the opposite of the HEX subroutine within the Indisk subprogram which changes hex numbers in ASCII format into integers. The HEXPRINT routine will take an integer and turn it into hex ASCII characters for printout.
After the number has been printed to the screen, we JSR to the sister routine PTPNU (910) to also print it to the printer if necessary. Then the number is restored to the X Register from the X variable (920) before returning to the caller.
PRNTSA (990) is similar to PRNTNUM. The main difference is that PRNTNUM always prints the single byte sent to it in the X Register. By contrast, PRNTSA prints the two bytes in SA, the Program Counter variable. The same four possibilities are tested: printer, screen, hex, or decimal. PRNTSA's sister routine, PTPSA, is called upon from both the hex (1050) and the decimal (1100) versions of this routine.
PRNTCR (1120) prints a carriage return; the 13 is the ASCII code for carriage return on both the screen and a printer. PRNTLINE (1160) prints out a line number from the source code. As each physical line is drawn into view by LADS, its line number is stored in the LINEN variable. This routine also uses that OUTNUM routine from BASIC ROM which prints BASIC's line numbers during a LIST. Line numbers, in BASIC or LADS, are always decimal. PTPLI (1190) is the sister routine for printer printouts.
PRNTINPUT (1210) prints the contents of the main buffer. Those contents will be the most recent logical line of source code as it appeared in the source code. It uses the PRNTMESS routine which sends to the screen any ASCII message which is pointed to by the TEMP variable. The line must end in 0. PRNTMESS (740) handles the printer with the PTP, single-character, test. There is no need for a sister routine within PRNTINPUT.

ERRING (1280) performs the preliminaries to an error message printout. Such messages as SYNTAX ERROR or NAKED LABEL are triggered at various places within LADS. But most of them JSR to ERRING before printing out their particular messages. ERRING rings the bell first. The number 7 is the ASCII code which rings any bells attached to computers or printers. (This works on Apple and PET/CBM computers; the 7 is changed to 253 in the Atari version to produce the same result. The VIC and Commodore 64 have no "bell," so the character 7 will have no effect on those computers.) The purpose of the bell is to alert the programmer that an error has been detected. True, the error message will appear onscreen, but during an assembly of a large program, the programmer might well miss silent error messages sliding up the screen.
On Commodore computers, the character 18 reverses the field of all subsequent characters on a line. This, too, highlights errors. Next (1320), the logical line of source code where the error appears is printed, followed by a carriage return.
It would be simple to make error reports more dramatic. You could stop assembly at that point with a key-testing loop that required the programmer to hit any key to continue. You could JSR FIN and exit to BASIC mode, aborting all further assembly. You could JSR PRNTLINE to emphasize the line number in the source code where the error happened. You could ring the bell ten times. As with all other aspects of LADS, you can make it do what's efficient for you, what's responsive to your own style of programming. Add some special effects here if you wish. Then reassemble your customized version of LADS.

Sister Print Routines
The next few routines are the printer routines: Each is a parallel, sister routine to one of the screen routines discussed above. Each tests the PRINTFLAG and returns if the flag is down, indicating that the user did not request a printout on paper. If the PRINTFLAG is up, output is redirected to the printer (1450-1470) by opening a file channel to the printer. On Commodore computers, the printer is device #4. Then OUTNUM or PRINT or HEXPRINT sends the characters or numbers to the printer (1490, 1680, 1720, 1900, 1960, 2130). After that, normal I/O is restored (1500) and a channel is reopened to file #1, the inputsource-code-from-disk mode.
To follow the logic of PTP (1380), PTPNU (1560), PTPSA (1780), or PTPLI (2020), just look at the parallel routines which JSR to them. The purpose, the tests, and the logic are the same. The only difference is that the sister routines described above route their characters to the screen. These routines send characters to a printer.

Printing Hex Numbers
The subprogram Printops concludes with HEXPRINT, an interesting routine which converts a one-byte integer into an ASCII hex string that can be printed to screen or printer.
HEXPRINT operates on a single byte at a time. The byte is first stored temporarily on the stack with PHA (2200). Let's use \$4A as an example. The four high bits are stripped off with AND #\$0F, leaving \$0A. That's one of the characters we need to print. Then we can use a short, simple lookup table to extract the character by its position in the table. In the Tables subprogram is a minitable called HEXA (270). It looks like this:

270 HEXA BYTE "0123456789ABCDEF

Since the number \$0A (10 decimal) is also the tenth character in this table, we can just move the ANDed \$0A over to the Y Register (2220) and load HEXA,Y to fetch the ASCII character for \$0A, which would be 65 (the letter A). We can stick this character into the X Register; X isn't being used elsewhere in this routine, so it can save the character for us while we look at the high bits.
this time we move the four high bits right over on top of the four low bits. This takes four logical shifts right (2270-2300). After LSRing \$4A we get \$04. Again, we TAY and load the character 4 from the table (it's 52 decimal). We print this. In \$4A, the 4 comes first. Then we recover the A character from the X Register and print it right after the 4 (2350).

Program 7-1. Math

10 ; "MATH" THIS ROUTINE HANDLES +    IT COMES FROM EVAL AFTER INDISK
40 MATH LDY #0; SET INDEXES TO ZERO
50 LDX #0
60 MATH1 LDA LABEL,Y; LOOK FOR LOCATION OF "+" SYMBOL--------
70 CMP #43
80 BEQ MATH2
90 INY
100 JMP MATH1;------------- NOW POINT TO 1ST NUMBER FOLLOWING +
110 MATH2 INY
120 LDA LABEL,Y
130 JSR RANGECK; CHECK TO SEE IF THIS IS BETWEEN 48 - 58 (ASCII FOR 0-9)
140 BCS VALIT; IF NOT, EXIT THIS ROUTINE (WE'VE STORED THE NUMBER AND HAVE
150 STA HEXBUF,X; LOCATED SOMETHING OTHER THAN AN ASCII NUMBER)
160 INX; KEEP STORING VALID ASCII NUMBERS IN HEXBUF BUFFER
170 JMP MATH2;-------------------
180 RANGECK CMP #58;-------------- IS THIS >47 AND <58
190 BCS MATH3
200 SEC
210 SBC #48
220 SEC
230 SBC #208; IS IT > 47 & < 58
240 MATH3 RTS
250 VALIT LDA #0;---------- TURN IT FROM ASCII INTO A 2-BYTE INTEGER
260 STA HEXBUF,X; PUT ZERO AT END OF ASCII NUMBER (AS DELIMITER)
270 LDA #<HEXBUF; POINT "TEMP" POINTER TO ASCII NUMBER IN BUFFER
280 STA TEMP
290 LDA #>HEXBUF
300 STA TEMP+1
310 JSR VALDEC; ROUTINE WHICH TURNS ASCII NUMBER INTO INTEGER IN "RESULT"
340 LDA RESULT+1
370 .FILE PRINTOPS

For the Atari version of Math, change line 370 to:

370 .FILE D:PRINTOPS, SRC

Program 7-2. Printops

10 ; "PRINTOPS"    PRINTS & POKES VALUES (BOTH OPCODES & ARGUMENTS)
20 FORMAT LDA PASS; ON PASS 2, IGNORE INCSA (RAISES PC) SINCE
30 BNE PRM; ON PASS 2, WE JSR TO POKEIT (IT GOES TO INCSA)
40 JSR INCSA; BUT ON PASS 1, WE DON'T PRINT OR POKE ANYTHING, WE JUST
50 RTS; RAISE THE PC AND RETURN --------------
60 PRM LDA SFLAG; SHOULD WE PRINT TO SCREEN
70 BEQ PRMX; IF NOT, SKIP THIS NEXT PART (PRINT TO SCREEN)
80 JSR CLRCHN; OTHERWISE, RESET NORMAL I/O CONDITION
90 LDX #1; (FILE #1 FOR INPUT, SCREEN FOR OUTPUT)
100 JSR CHKIN
110 LDX OP; LOAD THE OPCODE
120 JSR PRNTNUM; PRINT IT
130 JSR PRNTSPACE; PRINT A SPACE
140 PRMX LDX OP;----------- NOW POKE THE OPCODE INTO RAM/DISK MEMORY
150 JSR POKEIT
160 RTS;---------------------------------------
170 ; PRINT TWO BYTES (THE OPCODE AND A 1-BYTE ARGUMENT) -----------------
180 PRINT2 LDA PASS; ON PASS 2, WE SKIP INCSA (SEE LINE 20 ABOVE)
190 BNE P2M
200 JSR INCSA
210 RTS;--------------------
220 P2M LDA SFLAG; IF SCREEN PRINT FLAG IS DOWN, SKIP PRINTING TO SCREEN
230 BEQ P2MX
240 LDX RESULT; OTHERWISE PRINT THE LOW-BYTE OF "RESULT" (THE ARGUMENT)
250 JSR PRNTNUM
260 P2MX LDX RESULT; AND ALSO POKE THE LOW-BYTE TO RAM/DISK MEMORY
270 JMP POKEIT; A JMP TO POKEIT WILL RTS US BACK TO THE CALLER------------
280 ; PRINT THREE BYTES (THE OPCODE AND A 2-BYTE ARGUMENT) -----------------
290 PRINT3 LDA PASS; ON PASS 2, SKIP INCSA (SEE LINE 20 ABOVE)
300 BNE P3M
310 JSR INCSA; RAISE PC BY 2
320 JSR INCSA
330 RTS;--------------------
340 P3M LDA SFLAG; SHOULE WE PRINT TO SCREEN
350 BEQ P3MX
360 LDX RESULT; PRINT AND POKE LOW BYTE OF ARGUMENT
370 JSR PRNTNUM
380 P3MX LDX RESULT
390 JSR POKEIT
400 LDA SFLAG; SHOULD WE PRINT TO SCREEN
410 BEQ P3MXX
420 LDA HXFLAG; ARE WE PRINTING OPCODES AND ARGUMENTS IN HEX
430 BEQ P3MX2; IF SO, DON'T PRINT A SPACE HERE
440 JSR PRNTSPACE; OTHERWISE, PRINT A SPACE
450 P3MX2 LDX RESULT+1; PRINT AND POKE THE HIGH BYTE OF THE ARGUMENT
460 JSR PRNTNUM
470 P3MXX LDX RESULT+1
480 JMP POKEIT; AND A JUMP TO POKEIT WILL RTS US BACK TO CALLER
490 POKEIT STX WORK+1;------------- POKE IN A BYTE TO RAM/DISK---------------
500 LDA POKEFLAG; ARE WE SUPPOSED TO POKE TO RAM
510 BEQ DISP; IF NOT, SKIP IT
520 LDY #0; OTHERWISE, SEND THE BYTE TO RAM MEMORY AT CURRENT PC ADDRESS (SA)
530 TXA
540 STA (SA),Y;----------------
550 DISP LDA DISKFLAG; ARE WE SUPPOSED TO POKE TO A DISK OBJECT FILE
560 BEQ INCSA; IF NOT, SKIP IT
570 JSR CLRCHN; IF SO, ALERT FILE #2 (WRITE FILE ON DISK)
580 LDX #2
590 JSR CHKOUT
600 LDA WORK+1; PUT THE BYTE TO BE SENT TO DISK IN THE A REGISTER
610 JSR PRINT; PRINT (AFTER LINES 550-570 ABOVE) PRINTS TO DISK FILE #2
620 JSR CLRCHN; RESTORE NORMAL I/O (PRINT TO SCREEN AND
630 LDX #1; READ FROM FILE #1
640 JSR CHKIN
650 INCSA CLC;------------------- RAISE THE PC COUNTER (SA) BY 1 --------
660 LDA #1
680 STA SA
690 LDA #0
710 STA SA+1
720 RTS
730 ;------------------------ PRINTOUT ROUTINES (TO SCREEN) ----------
740 PRNTMESS LDY #0; PRINT A MESSAGE (ERRORS USUALLY) TO THE SCREEN
750 MESSLOOP LDA (TEMP),Y; THESE MESSAGES ARE DELIMITED BY 0 AND ARE POINTED
760 BEQ MESSDONE; TO BY THE VARIABLE "TEMP"
770 JSR PRINT
780 JSR PTP; AFTER PRINTING A CHARACTER TO SCREEN, CHECK TO SEE IF IT SHOULD
790 INY;    ALSO BE PRINTED TO THE PRINTER
800 JMP MESSLOOP
810 MESSDONE RTS;----------------------------
820 PRNTSPACE LDA #32; PRINT A SPACE CHARACTER
830 JSR PRINT
840 JSR PTP; SEE IF IT SHOULD ALSO GO TO THE PRINTER
850 RTS;---------------------------------------------
860 PRNTNUM STX X; PRINT A NUMBER (LOW BYTE IN X, HIGH BYTE IN A)
870 LDA HXFLAG; IF WE'RE PRINTING IN HEX, NOT DECIMAL, THEN
880 BEQ PRNTNUMD; USE THE HEXPRINT SUBROUTINE. OTHERWISE, GO TO PRNTNUMD
890 TXA
900 JSR HEXPRINT
910 JSR PTPNU; CHECK IF NUMBER SHOULD BE PRINTED TO PRINTER AS WELL
920 LDX X; RESTORE NUMBER IN X BEFORE
930 RTS; RETURNING TO CALLER-------------------------
940 PRNTNUMD LDA #0; PRINT A DECIMAL NUMBER
950 JSR OUTNUM; BASIC'S LINE NUMBER PRINTOUT ROUTINE
960 JSR PTPNU; SHOULD WE ALSO PRINT IT TO PRINTER
970 LDX X; RESTORE VALUE IN X BEFORE
980 RTS; RETURNING TO THE CALLER ----------------------------------
990 PRNTSA LDA HXFLAG; PRINT THE SA (PC, PROGRAM COUNTER)
1000 BEQ PRNTSAD; IF NOT HEX PRINTOUT, THEN USE DECIMAL ROUTINE BELOW
1010 LDA SA+1; OTHERWISE, PRINT LOW AND HIGH BYTES OF SA (AS HEX)
1020 JSR HEXPRINT; HIGH BYTE 1ST
1030 LDA SA
1040 JSR HEXPRINT
1050 JSR PTPSA; SHOULD WE ALSO PRINT SA TO PRINTER
1060 RTS;----------------
1070 PRNTSAD LDX SA; PRINT SA (DECIMAL VERSION)
1080 LDA SA+1
1090 JSR OUTNUM
1100 JSR PTPSA; PRINT TO PRINTER, TOO
1110 RTS;-------------------------------------------------
1120 PRNTCR LDA #13;    PRINT A CARRIAGE RETURN
1130 JSR PRINT
1140 JSR PTP; SHOULD WE DO IT ON THE PRINTER TOO
1150 RTS;------------------------------------------------
1160 PRNTLINE LDX LINEN;    PRINT A SOURCE CODE LINE NUMBER
1170 LDA LINEN+1
1180 JSR OUTNUM; BASIC ROUTINE (LOW BYTE IN X, HIGH IN A)
1190 JSR PTPLI; SHOULD WE ALSO PRINT LINE NUMBER TO PRINTER
1200 RTS; -----------------------------------------------
1210 PRNTINPUT LDA #<LABEL;    PRINT CONTENTS OF MAIN INPUT
1220 STA TEMP;    BUFFER ("LABEL")
1230 LDA #>LABEL; POINT "TEMP" TO THE BUFFER AND THEN
1240 STA TEMP+1
1250 JSR PRNTMESS; USE GENERAL MESSAGE PRINTING ROUTINE
1260 RTS
1270 ;------------------------    ERROR PRINTOUT PREPARATIONS
1280 ERRING LDA #7; RING BELL
1290 JSR PRINT
1300 LDA #18; TURN ON REVERSE PRINTING TO HIGHLIGHT ERROR
1310 JSR PRINT
1320 JSR PRNTINPUT; PRINT CONTENTS OF MAIN INPUT BUFFER
1330 LDA #13; PRINT A CARRIAGE RETURN
1340 JSR PRINT
1350 RTS
1360 ;------------------------------------- PRINTOUT (TO PRINTER)
1370 ;(PTP PRINTS A SINGLE CHARACTER TO THE PRINTER).
1380 PTP LDX PASS; ON PASS 1, DO NO PRINTING TO PRINTER
1390 BNE PTP1
1400 RTS
1410 PTP1 LDX PRINTFLAG; IF PRINTFLAG IS DOWN, DO NOTHING, RETURN TO CALLER
1420 BNE MPTP
1430 RTS;---------
1440 MPTP STA A; SAVE CONTENTS OF ACCUMULATOR
1460 LDX #4
1470 JSR CHKOUT
1480 LDA A; RECOVER A
1490 JSR PRINT; PRINT TO PRINTER
1500 JSR CLRCHN; RESTORE NORMAL I/O
1510 LDX #1
1520 JSR CHKIN
1530 RETT LDA A; RECOVER A
1550 ;--------------------    NUMBERS TO PRINTER
1560 PTPNU LDX PASS; SAME LOGIC AS LINES 1350+ ABOVE
1570 BNE PTPN1
1580 RTS
1590 PTPN1 LDX PRINTFLAG
1600 BNE MPTPN
1610 RTS
1620 MPTPN JSR CLRCHN
1630 LDX #4
1640 JSR CHKOUT
1650 LDA HXFLAG; HEX OR DECIMAL MODE
1660 BEQ MPTPND
1670 LDA X
1680 JSR HEXPRINT
1690 JMP FINPTP
1700 MPTPND LDA #0
1710 LDX X
1720 JSR OUTNUM
1730 FINPTP JSR CLRCHN
1740 LDX #1
1750 JSR CHKIN
1760 RTS
1770 ;------------------ SA TO PRINTER
1780 PTPSA LDX PASS; SAME LOGIC AS LINES 1350+ ABOVE
1790 BNE PTPS1
1800 RTS
1810 PTPS1 LDX PRINTFLAG
1820 BNE MPTPSA
1830 RTS
1840 MPTPSA JSR CLRCHN
1850 LDX #4
1860 JSR CHKOUT
1870 LDX HXFLAG; HEX OR DECIMAL PRINTOUT
1890 LDA SA+1
1900 JSR HEXPRINT
1910 LDA SA
1920 JSR HEXPRINT
1930 JMP FINPTPSA
1950 LDX SA
1960 JSR OUTNUM
1970 FINPTPSA JSR CLRCHN
1980 LDX #1
1990 JSR CHKIN
2000 RTS
2010 ;------------------ LINE NUMBER TO PRINTER
2020 PTPLI LDX PASS; SAME LOGIC AS LINES 1350+ ABOVE
2030 BNE PTPL1
2040 RTS
2050 PTPL1 LDX PRINTFLAG
2060 BNE MPTPL
2070 RTS
2080 MPTPL JSR CLRCHN
2090 LDX #4
2100 JSR CHKOUT
2110 LDA LINEN+1
2120 LDX LINEN
2130 JSR OUTNUM
2140 JSR CLRCHN
2150 LDX #1
2160 JSR CHKIN
2170 RTS
2180 ;---------------------------------- HEX NUMBER PRINTOUT
2190 ; PRINT THE NUMBER IN THE ACCUMULATOR AS A HEX DIGIT (AS ASCII CHARS.)
2200 HEXPRINT PHA; STORE NUMBER
2210 AND #\$0F; CLEAR HIGH BITS (10101111 BECOMES 00001111, FOR EXAMPLE)
2220 TAY; NOW WE KNOW WHICH POSITION IN THE STRING OF HEX NUMBERS ("HEXA")
2230 LDA HEXA,Y; THIS NUMBER IS. SO PULL IT OUT AS AN ASCII CHARACTER
2240 ; (HEXA LOOKS LIKE THIS: "0123456789ABCDEF")
2250 TAX; SAVE LOW-BITS VALUE INTO X
2260 PLA; PULL OUT THE ORIGINAL NUMBER, BUT THIS TIME
2270 LSR;SHIFT RIGHT 4 TIMES (MOVING THE 4 HIGH BITS INTO THE 4 LOW BITS AREA)
2280 LSR; (10101111 BECOMES 00001010, FOR EXAMPLE)
2290 LSR
2300 LSR
2310 TAY; AGAIN, PUT POSITION OF THIS VALUE INTO THE Y INDEX
2320 LDA HEXA,Y; PULL OUT THE RIGHT ASCII CHARACTER FROM "HEXA" STRING
2330 JSR PRINT; PRINT HIGH VALUE (FIRST) (A HOLDS HIGH VALUE AFTER LINE 2280)
2340 TXA; (X HELD LOW VALUE AFTER LINE 2210)
2350 JSR PRINT; PRINT LOW VALUE
2370 .FILE PSEUDO

Program 7-3. Printops, Atari Modifications

To create the Atari version of Printops, change the following lines in Program 7-2:

610 JSR OBJPRTNT
1280 ERRING LDA #253
2370 .FILE D:PSEUDO.SRC