Classic Computer Magazine Archive A.N.A.L.O.G. ISSUE 62 / JULY 1988 / PAGE 63

BITS
&
PIECES

Enough Is Enough
    I lied. Just a little. I admit it! Last month I said you could turn your old 400/800 computer into a peripheral without any hardware changes. Well that is only 80% correct because while the computer goes untouched, you will need to build or modify cables (more on that later). Now, like I promised you, here is Atari Zucchini.
    Zucchini is a hardware project.
    Zucchini is a software project.
    Zucchini allows connection of a slave Atari to a host Atari through the serial port.
    Zucchini drives your printer directly through the joystick ports (remember them?).
    Zucchini is a 1 K machine-language program on a boot disk. This makes it modifiable to create any number of new peripherals and means it runs without DOS.
    Zucchini allows you to off load text at high speed and run the printer at low speed via its buffer.
    Zucchini allows you to print text as graphics to list your BASIC programs including all those unprintable ATASCII characters.
    Now that you know a little bit about what Zucchini is, let's look at how to build it and how it works.

The Right Connections
    Before we get into the cable modifications, let's look at the job we need the cables to do. In order for an Atari to act as a peripheral we need to make several switches. The Data-In and Data-Out lines need to be reversed, the COMMAND line from the host computer needs to be attached to the INTERRUPT pin on the slave's serial port, and the Clock In and Clock Out completely disconnected. I/O is asynchronous, and so the clock lines are not needed. You can either cross the slave's COMMAND to the host INTERRUPT or you can leave it detached (see XL/XE addendum).

Figure 1

    So that leaves us with three lines that need to be changed around. The problem is that you cannot simply exchange the pins in your cable because you need to boot load the Zucchini program before your extra computer can work as a slave. This requires the cable be ''normal" to load Zucchini and "altered" to run it. What you need to do is add a multi-pole switch to the cable that connects your Zucchini 800 to your host computer. You can flip the switch one way to load and the other way to use as a peripheral (see Figure 1).
    One other problem which you will incur: both host and slave have +5 volts available on Pin 10 of the serial plug. If both computers are connected together through these pins, then one can pull power from the other if it is turned off thereby overloading power supplies. If you simply cut this wire or disconnect the pin in your cable, you will not be able to boot load the program because the disk drive will not operate unless it senses +5 volts on Pin 10. So you also need to modify the cable that normally hooks to your host computer by cutting the lead from Pin 10. This blocks power from flowing between the two computers but it also means that you must boot up Zucchini first and that Zucchini must be powered up for you to use the disk drive with your host. Similarly the 800/400's have + 12 volts on Pin 12, but you should disconnect this since it is unused.
    If you are lazy like me, you will want to build an automatic switching mechanism. The 4053 CMOS IC suits the bill. Originally designed to be an electronic switch for audio signals, it seems to work just fine for this purpose. After all, there is not much difference between high-frequency audio and 19,200 baud.



    This circuit connects the Zucchini serial port up in normal fashion, but will switch signals automatically to the slave position when the cassette control line is turned on by ZUCCHINISOFT after it boot loads. This circuit has a diode in the power lead to block power influx from the host. In order to prevent power egress, you need to insert another diode into the first cable from the host where you cut it before. I used the circuit board and added another 13-pin serial socket to allow additional peripherals to be added (such as the 410 cassette or my ALPHACOM 42 printer which do not have their own additional receptacles). I cannibalized a disk drive cable which I happened to have laying around, but you can build your own Zucchini interface using ribbon cable and new plugs. See the end of this article for sources.
    Next you will need to build the printer cable. You need three joystick cables and a 36-pin male, Centronics printer plug. Solder the wires according to this diagram:



    You can use IDC plugs and ribbon cables, but I found the joystick replacement cables as easy as any to use. When done, plug the 36-pin plug into your printer and the joystick cables into their proper outlets on your Zucchini. Run the following program to test the cable:

10 DIM WORD$(20):WORD$="ZUCC
MINI INTERFACE"
20 PORTA=54016:PORTB=54017:P
ACTL=54018:PBCTL=54019
30 P=PEEK(PACTL):POKE PACTL,P
-4:POKE PORTA,255:POKE PACTL,
P
40 P=PEEK(PBCTL):POKE PBCTL,P
-4:POKE PORTB,1:POKE PACTL,P
50 FOR S=1 TO LEN(WORD$):POKE
PORTA,ASC(WORD$(S))
60 IF TRIG(0)=1 THEN 60
70 POKE PORTB,0:POKE PORTB,1:
NEXT S



    You should recognize how Lines 20-40 convert the joysticks to output. TRIG(0) is used as the "Busy'' indicator for the printer and will read 0 when the printer is idle and 1 if busy or disconnected. If it works, then you are ready to go on to the programming.

The Program
    If you subscribe to ANALOG on disk you can load Zucchini.BAS and skip on down a ways. But if you are not lucky enough to have the disk, then type in the BASIC listing and save it to disk as Zucchini.BAS before running. Now run the program. If the data is correct then the program will halt and give you a message to remove your program disk and insert a new, blank disk which will be formatted by your BASIC program. Then press return and the boot file will write to the disk. If you left your program disk in the drive you would have just lost everything!
    If you want to understand the program better or modify it for your own purposes, then type in the source code and list to disk as ZUCCHINI.SRC before assembling. Now assemble and check for errors. When error-free then re-list to disk. At this point format a blank disk to hold your final assembly. Now for the final assembly, change the variable ORIGIN to $0700 and assemble again. Since the program assembles into DOS's space, you cannot save, list, or otherwise use DOS once you have done your final assembly. At the end of the assembled program is a routine to transfer the program to disk as a boot file, so go to BUG and run at the address of ENDPRO. If you want to modify your program then all changes must be between START and ENDPRO. You need to set ORIGIN about 2K above the end of your source code to allow space for assembly so as to not overwrite the source. You can delete all the commentary to create more free buffer space. When your modified program is debugged, LIST to disk and follow the above procedure to create the boot disk.

Eating Zucchini
    To use your new interface, connect up your Zucchini 800 to the serial bus using your Zucchini interface and then connect the host computer with the modified regular cable.
    Load the ZUCCHINISOFT boot disk into Drive 1 and power up your Zucchini 800. It takes about two seconds to load and the console speaker will beep once when it is ready, but will beep continuously if the printer is not online. Once your Zucchini is online with the printer running, remove the disk and proceed to boot up the host computer as usual. Now you can use your printer just as you normally would using LPRINT or PRINT #1. The big advantage is the speed with which the computer is freed up. Try this:

10 DIM WORD$(5200),AB$(26)
20 AB$="ABCDEFGHIJKLMNOPQRST
UVWXVZ"
30 FOR S=1 TO 5200 STEP26:WO
RD$(S)=AB$:NEXT S
40 OPEN #1,8,0,"P:":PRINT#1;
WORD$:CLOSE #1

    It took one minute, 22 seconds to print WORD$ with my AXIOM AT-846 interface and my Gemini 10X, but with my Zucchini 800 it took only six seconds to transfer WORD$ to the interface and only one minute, eight seconds to print. It took one minute to send last month's article to the interface and about eight or nine minutes to print. This is quite a time saver. But of even more value is this little trick. When the Zucchini interface is idle, press ESCAPE once. Now load a BASIC program into your host and then type LIST "P:''. Pretty neat, huh? In case you missed it, your program is listed verbatim to the printer in graphics, not text, in 38-column format, exactly as it appears on screen with inverse characters, graphics characters and so on. So what you say; you have several other programs to do this. Ah, but this one solves two problems. First is that it takes forever to print such a listing with a lister program while the Zucchini interface will take the program as text as fast as the host can send it. A typical program may take 30 to 90 seconds to dump but may take a half hour to print! Moreover, the carriage-return problem has finally been solved.
    What is the carriage-return problem? ATARI uses ATASCII 155 (inverse ESCAPE) as the carriage return, but the printer and the rest of the world recognizes CHR$ (13) as a carriage return. So your interface must convert any bytes of value 155 to 13. When you go to print graphics, you will still convert any bit patterns of value 155 to 13. This plays havoc with your graphics representation of the inverse "A" character and requires you to alter the character set or to replace all 155s with some other number. My AT-846 interface has a jumper option to ignore the conversion, but then you don't get any carriage returns unless you add a CHR$ (13) at the end of everything you print or by adding your own printer handler. So the only way to avoid this problem is Zucchini. To return to standard text, press ESCAPE again and when the interface is idle the conversion will occur.
    How much actual buffer space you have depends on your computer configuration. The OS uses memory up to Page 6 or 1536 bytes. Zucchini uses about 1200 bytes, so your buffer space starts at $0BB1. A stock ATARI 400 will have about 13.5K free buffer space and an 800 with 48K will actually have 46K buffer space or about 18 pages of text. I have only been able to fill up the buffer twice: once when I sent all three parts of this series at once (25 pages of text), and the other when printing a full-page poster from print shop. If you should be able to, the interface will merely cause the host computer to timeout for 28 seconds while the buffer empties a bit. When the retry occurs the host will then refill the buffer and timeout again. This continues until the host is done sending. You will also note that the printer slows down a bit while data is being transferred. About the only thing that can go wrong is if the printer is offline and the buffer fills up. Then a real timeout can occur giving ERROR 138. RESET will empty memory and restart the program with console beep and all.

Zucchini Power
    While all this is impressive enough it only touches the surface of possibilities. The real power of the Zucchini interface is that it is programmable! Examples...
    There are many possible variations on the theme of printer interfaces that would allow some interesting possibilites. You still have three trigger lines and seven PORTB pins to play with. You can attach a second printer to PORTA and use TRIG1 for BUSY and bit 2 of PORTA for a strobe. Now you have two printers on the same interface. Both the XL and XE OSs support multiple "P:" devices which have device IDs of $40 plus the printer number -1. So "P4:" would have an ID of $43. You could have one printer interface that would respond to OPEN #1,8,0,"P2:" or could drive both a P: and P2: from the same interface. But why stop there. You could probably run four printers simultaneously from the same source. My XL supports up to P9: devices! Great if you do mailings!
    You could have several keyboard selectable fonts.
    Besides the standard print mode there are two other print modes supportable by the printer handler. OPEN #1,8,68,"P:" changes the AUX1 byte of the command frame from "N"(78) to "D"(68) and sends data in 20-byte frames. Originally this was for double-wide text. Similarly, OPEN #1,8,83,"P:" sends an "S"(83) for sideways (??) printing with 29-byte frames. You can use this for software selectable special effects by adding code to recognize this.
    Your Zucchini can be programmed to convert ESCAPE or control code sequences from one format to another. Theoretically you could get your EPSON to respond just like an NEC printer or any other printer for that matter.
    Other possibilities? How about a ramdisk? You can use Device ID of $37 for "D8:". How about a slave terminal to your main computer? If you cross connect your COMMAND line from your slave to INTERRUPT on the main computer, your slave can signal the host it wants to send data. In fact it is possible to do networking or timesharing!
    You can produce an 850 emulator with four serial ports from the joysticks. Have you ever seen an 850 with a built-in 46K buffer? Or perhaps a buffer between your modem which can run out of the joystick (already available commercially).
    You can even create your own new kind of peripheral such as a large-scale security system with several "X:" devices all checking in with a central Atari computer.
    I could keep going on and on, but I am going to leave it up to you, the readers, to come up with the ideas. Please send them in. Let's create a Zucchini net.

How It Works
    If you don't want to know all the details of how Zucchini works, you can stop here, but if you have that insatiable desire to know how things work, or if you plan to modify the program then read on.
The first part of the program is all housekeeping and initialization. It sets up memory pointers and changes interrupt vectors to point to our own routines. ANTIC DMA is turned off, and we replace the keyboard handler with one of our own to detect the escape key. In addition we insert our own SIO interrupt handlers. After the initialization the printer is tested and the beep routines are used to signal readiness. RAM is partitioned with the program starting at $0700 or Page 7, the holding buffer at Page 6 and the remaining RAM above the program for the main buffer. RAMTOP is used as the top end pointer and LOMEM the bottom (LOMEM was moved during the boot to point to the end of the program).

Main Print Loop


Recieve DataRoutine



Recieve Wait Loop

    The main printer loop has two pointers which control the flow of data. Refer to the Zucchini flow chart.
    BUFEND points to the last byte loaded from the host computer and BUFPNT points to the current byte to be printed. So long as BUFPNT equals BUFEND the loop will idle. When new data is loaded by the receive routines, BUFEND is pushed up in RAM, and the printer loop begins transfer to the printer until the pointers are the same again. When the COMMAND line goes to 0 signaling a COMMAND frame, it causes an IRQ interrupt because of the connections we made in the cables and the new interrupt handlers. This interrupt sets the RCVFLG to 1. This flag will signal the main loop to break out and go to the RECEIV loop. There are two places in the main loop where this flag is tested: the idle loop at the beginning and the printer wait loop. If the flag is set at either point then the RECEIV loop is entered.

Receive Loop
    The buffer size is set for four bytes and POKEY initialized for reception. An idle loop is entered and Zucchini waits until the RECeive COMplete flag is set indicating that all four bytes and checksum have been received. Then the PA1 interrupt is altered to respond to the positive transition of COMMAND as it returns to Logic 1. Once the command frame has been received the program tests for the proper device ID number. If wrong, then you get dumped back to the main loop and there is no response to the host computer. If the ID is correct, then the program sends an ACK and tests for WRITE or STATUS. If it is a status command then a separate routine is entered that sends a CMP followed by the status frame of five bytes after which control returns to the main loop. If a WRITE was called for, then the program moves to the next phase. POKEY is then set up for a 40-byte data frame, and then you go into another holding loop until RECOMP is set again. If there are no errors in data reception, then an ACK is sent and the data is moved to the main buffer.
    As each byte is moved, a temporary pointer is advanced until the whole 40 bytes is transferred, an EOL is encountered, or you run out of free buffer space. If the process can be completed then BUFEND is set to the value of the temporary pointer and a CMP byte is sent. If the main buffer gets filled, then the temporary pointer will be equal to BUFPNT at some point during the transfer process. If this occurs, then the program returns to the main loop without sending the CMP byte or updating BUFEND. The result is that the host computer does a timeout for 28 seconds, during which up to 2000 characters can be emptied from the buffer to make more space. Without updating BUFEND, no data was really transferred to the main buffer. If a pointer runs up to the top of RAM, it is "wrapped around" to the bottom of the buffer RAM. This means that every free byte in the main buffer is always available and as material is emptied from the buffer, more space is made available for use.
    Besides the main-line program there are many interrupt routines including the VSERIN, VSEROR, VSEROC and the PA1 or INTERRUPT routines. Additionally, there is a routine to set a stage-one VBLANK timer to a half second for the timeout value so if the host computer blows up in the middle of data transfer, the program will not lock up but return to printing. You can see from this system that only a portion of time spent in data transfer is actually spent in receiving data. So long as both the host and the computer agree, you could change the host's print buffer and your Zucchini to expect 256 byte data frames or do burst I/O. This would greatly increase transfer speed.
    The interrupt routines work essentially the same as the stock ones in the O.S., and you could save some RAM by using them. For some reason, though, I always got a checksum error when I used the stock-receive interrupt but the stock send routines work okay. The description of how these interrupts work was covered in last month's article. One additional interrupt program uses POKEY's keyboard interrupt vector and senses a pressing of the ESCAPE key setting the flag to produce graphics from the text for program listings. The graphics program itself takes the ATASCII code of the text data and obtains the bit values of the letter from the character set in ROM and shuffles it around to produce the 8-bit graphics values for the printer to use. You could have several, different character sets which load from disk at boot time and are selected from the keyboard.

Final Notes
    If you are a hardware genius and you have an EPROM programmer, you could place Zucchini into an EPROM and put it in cartridge to plug into the computer, since Zucchini does not need any language. But how you assemble a program into the same space as your assembler while the assembler is running is one problem I have yet to figure out! Of course you would lose 6-7K of buffer memory because a cartridge locks out 8K of RAM even though the program uses 1 K. You can increase your buffer size slightly by using PRNBUF, the normal printer buffer at $03C0, for temporary buffer and beginning assembly at $0480 thus gaining about 600 bytes. However, you cannot use any floating point math or basic. But then you should not have the BASIC cartridge in place when you boot up Zucchini.
    This program was originally intended for use with a 400/800 computer but can be used with an XL or XE: read the addendum attached. I hope you found Zucchini as interesting and exciting as I have. I also hope you can find many new recipes. Next month we will pick up some of the remaining loose ends of the serial port and show you a few tricks with the cassette player.

Parts List

MCM Electronics
858 E. Congress Park Dr.
Centerville, OH 45459-40721
1-800-543-4330


Atari Serial Cable, 6 ft. #83-365 $5.80
Atari Serial Plug only #83-360 $1.20
Joystick Plug & Cord #83-070 $2.05
Atari Serial P.C. Socket #83-140 $1.45
36 Pin Centronics male #83-310 $1.95


All Electronics
905 S. Vermont Ave.
Los Angeles, CA 90006
1-800-826-5432
Carries all parts except the Serial plugs and cables. D1, D2, IC1, IC2, and R1 should be available at any electronics store or TV shop.

XL/XE Addendum
    If you happen to have a leftover XL or XE computer you can take advantage of the extra memory available to you. In the XL/XE you can switch out the O.S. ROM and have a large extra block of RAM available. Your program will have to work around the hardware registers though. This is okay because we really don't use any of the O.S. except the character set and the interrupts. The XE will do the same thing but also has its 64K of extra memory banks. Along with the advantages, you have several problems to contend with. First is the question of joysticks. In these computers PORTB is not available externally and is used internally to control memory bank selection. That leaves you with only two joysticks and no strobe. So since you have only eight output bits you must do one of three things:
    1) Use seven bits for data and one for strobe. This is okay for text but prevents you from printing italics or special graphics characters depending on your printer. Also most graphic dump programs are designed for eight bits, not seven. Clearly this is not a desireable solution.
    2) Rewire your Zucchini interface to free the slave's COMMAND line to use as a strobe. Once Zucchini is booted the COMMAND line is not needed and can be diverted to the printer cable and be used as the strobe. You will also need to change any programming relating to PBCTL; otherwise you could really muck up things. PBCTL will then control memory banks and so on. You will not be able to drive multiple printers with this configuration.
    3) Use the 8-bit port adapter in ANALOG #44, July 1986. This device also has the advantage of allowing two printers to be used.
    The second problem to overcome is that once the O.S. is turned off to reveal the underlying RAM, you have no interrupt handlers, and so none of the Zucchini software can work without crashing. You need to supply the interrupt vectors in the last eight bytes of RAM to point to your own interrupt processors and change all the global interrupt vectors for the IRO interrupts. If you have a copy of the O.S. source code, you are home free, so get a copy. Then you need to set RAMTOP to somewhere below your routines and turn off the BASIC and MATH ROMs. You can bypass much of this by disabling the NMI and IRQ while accessing the RAM for printing or data transfer, then returning to the normal O.S. to process the interrupts. Using these techniques you can use a 130XE with about 120 K of RAM available for buffer space. Now where in the world can you get any other printer buffer with this capacity for $150?

Listing 1
Basic


WN 0 REM ******************************
QC 1 REM * ZUCCHINI PRINTER INTERFACE *
WZ 2 REM *  by Lee S. Brilliant M.D.  *
YB 3 REM *   Converts a 4001800 to an *
ML 4 REM *    interface/buffer.       *
WS 5 REM ******************************
NL 6 REM
WI 10 DATA 0,10,0,7,6,7,169,177,141,231,2
   ,169,11,141,232,2,1305
NH 20 DATA 169,26,133,10,169,7,133,11,24,
   96,169,0,141,47,2,141,1278
FD 30 DATA 166,11,141,172,11,141,170,11,1
   41,2,211,141,3,211,169,255,1956
MA 40 DATA 141,0,211,169,52,141,2,211,169
   ,1,141,1,211,169,5,141,1765
IJ 50 DATA 3,211,162,0,169,120,141,4,2,16
   9,10,141,5,2,169,213,1521
YY 60 DATA 141,10,2,169,9,141,11,2,169,10
   4,141,14,2,169,10,141,1235
EL 70 DATA 15,2,169,41,141,12,2,169,10,14
   1,13,2,169,160,141,8,1195
VL 80 DATA 2,169,10,141,9,2,173,231,2,133
   ,283,141,156,11,173,232,1788
YM 90 DATA 2,133,204,141,157,11,169,40,14
   1,4,210,169,0,141,6,210,1738
CH 100 DATA 32,127,10,173,16,208,240,13,1
   73,172,11,240,246,169,160,32,2022
KO 110 DATA 195,10,76,144,7,169,255,32,19
   5,10,141,162,11,173,166,11,1757
DK 120 DATA 240,6,32,229,8,76,173,7,165,2
   03,205,156,11,208,21,165,1905
IJ 130 DATA 204,205,157,11,208,14,173,165
   11,205,164,11,240,223,141,164,2296
PB 140 DATA 11,76,173,7,165,204,197,106,1
   44,13,173,231,2,133,203,173,2011
NB 150 DATA 232,2,133,204,76,173,7,230,20
   3,208,2,230,204,173,164,11,2252
XR 160 DATA 240,3,76,34,8,160,0,177,203,2
   01,155,208,2,169,13,32,1681
VM 170 DATA 5,8,76,173,7,141,0,211,173,16
   ,208,240,11,173,166,11,1619
IV 180 DATA 240,246,32,229,8,76,8,8,206,1
   ,211,234,238,1,211,96,2045
YJ 190 DATA 234,96,160,0,140,163,11,177,2
   03,141,171,11,201,155,208,31,2102
DV 200 DATA 172,160,11,208,3,76,249,7,169
   ,0,162,8,32,5,8,202,1472
AE 210 DATA 288,250,238,160,11,173,160,11
   201,38,208,236,76,201,8,173,2352
LK 220 DATA 160,11,208,11,162,3,189,173,1
   1,32,5,8,202,16,247,173,1611
GA 230 DATA 171,11,16,8,41,127,141,171,11
   ,206,163,11,201,96,176,12,1562
JD 240 DATA 201,32,144,6,24,105,224,76,12
   4,8,105,64,133,201,169,0,1616
EY 250 DATA 133,202,162,3,6,201,38,202,20
   2,208,249,24,173,244,2,101,2150
AQ 260 DATA 202,133,202,169,0,141,171,11,
   169,128,141,155,11,162,7,160,1962
DW 270 DATA 7,177,281,24,45,155,11,240,1,
   56,110,171,11,136,16,241,1602
EZ 280 DATA 78,155,11,173,171,11,77,163,1
   1,32,5,8,202,16,224,238,1575
WP 290 DATA 160,11,173,160,11,201,38,208,
   10,169,0,141,160,11,169,13,1635
DV 300 DATA 32,5,8,76,173,7,104,170,104,1
   68,169,0,141,166,11,169,1503
EU 310 DATA 5,141,3,211,96,152,72,138,72,
   32,127,10,169,4,141,159,1532
LK 320 DATA 11,32,174,9,169,7,141,3,211,1
   69,0,141,166,11,173,166,1583
PE 330 DATA 11,208,15,173,172,11,240,246,
   76,214,8,172,170,11,192,143,2062
OP 340 DATA 240,196,173,0,6,201,64,240,3,
   76,214,8,173,1,6,201,1802
HN 350 DATA 87,240,23,201,83,208,6,32,21,
   11,76,214,8,160,78,32,1480
AN 360 DATA 230,10,76,169,9,160,65,76,230
   ,10,32,53,9,169,40,141,1479
AX 370 DATA 159,11,32,174,9,172,170,11,24
   0,14,160,69,32,230,10,76,1569
MD 380 DATA 169,9,32,230,10,76,214,8,32,5
   3,9,160,0,162,0,173,1337
KJ 390 DATA 156,11,133,205,173,157,11,133
   206,230,205,208,2,230,206,165,2431
LT 400 DATA 205,197,203,208,9,165,206,197
   ,204,208,3,76)214,8,165,206,2474
DG 410 DATA 197,106,144,13,173,231,2,133,
   205,173,232,2,133,206,76,111,2137
VE 420 DATA 9,189,0,6,232,145,205,201,155
   ,240,4,224,48,208,202,165,2225
GW 430 DATA 205,141,15f%,11,165,206,141,15
   7,11,160,67,76,82,9,169,0,1756
KV 440 DATA 141,158,11,141,162,11,141,161
   11,141,167,11,141,170,11,32,1610
QL 450 DATA 88,11,173,167,11,208,10,173,1
   72,11,240,246,169,138,141,170,2128
TK 460 DATA 11,32,136,11,96,152,72,173,15
   ,210,141,10,210,48,5,160,1482
JJ 470 DATA 140,140,170,11,41,32,208,5,16
   0,142,140,170,11,173,158,11,1712
UI 480 DATA 240,20,173,13,210,205,161,11,
   240,5,160,143,140,170,11,238,2140
IV 490 DATA 167,11,104,168,104,64,173,13,
   210,172,162,11,153,0,6,24,1542
JH 500 DATA 109,161,11,105,0,141,161,11,2
   00,140,162,11,204,159,11,48,1634
TN 510 DATA 225,169,1,141,158,11,76,2,10,
   152,72,238,162,11,172,162,1762
EK 520 DATA 11,204,159,11,144,17,173,168,
   11,240,31,165,16,9,8,133,1500
GI 530 DATA 16,141,14,210,76,86,10,185,0,
   6,141,13,210,24,109,161,1402
UX 540 DATA 11,105,0,141,161,11,104,168,1
   04,64,173,161,11,141,13,210,1578
KR 550 DATA 169,1,141,168,11,76,86,10,169
   ,1,141,169,11,165,16,41,1375
JT 560 DATA 247,141,14,210,133,16,104,64,
   169,1,141,166,11,104,64,169,1754
YQ 570 DATA 154,141,38,2,169,10,141,39,2,
   162,0,160,30,169,1,120,1338
HD 580 DATA 32,92,228,169,0,141,172,11,88
   ,96,169,1,141,172,11,96,1619
QQ 590 DATA 173,9,210,201,28,208,19,173,1
   65,11,240,6,206,165,11,76,1901
QZ 600 DATA 186,10,238,165,li,i69,0,141,1
   60,11,104,64,202,208,253,136,2058
DH 610 DATA 208,250,96,141,159,11,141,162
   11,174,162,11,160,1,169,255,2111
NO 620 DATA 141,31,208,32,188,10,140,31,2
   08,200,174,162,11,32,188,16,1766
OF 630 DATA 206,159,11,208,228,96,152,72,
   169,0,141,169,11,141,159,11,1933
TW 640 DATA 141,162,11,32,123,11,160,2,14
   0,168,11,162,190,32,188,10,1543
HN 650 DATA 104,168,140,13,210,173,169,11
   208,8,173,172,11,240,246,76,2122
VN 660 DATA 204,9,76,209,9,160,65,32,230,
   10,160,67,32,230,10,169,1672
GI 670 DATA 128,172,170,11,240,2,169,129,
   141,0,6,173,2,6,141,1,1491
HJ 680 DATA 6,169,31,141,2,6,169,4,141,15
   9,11,169,0,141,3,6,1158
UC 690 DATA 141,162,11,141,169,11,141,168
   ,11,32,123,11,173,0,6,141,1441
RS 700 DATA 13,210,141,161,11,76,5,11,169
   ,19,141,50,2,141,15,210,1375
SR 710 DATA 141,10,210,160,224,132,16,140
   14,210,169,40,141,8,210,162,1987
LM 720 DATA 6,169,160,157,1,210,202,202,1
   6,249,96,160,208,169,35,141,2181
EC 730 DATA 50,2,141,15,210,76,101,11,169
   ,192,133,16,141,14,210,169,1650
VH 740 DATA 0,162,6,157,1,210,202,202,16,
   249,96,8,0,0,0,8,1301
XM 750 DATA 0,0,1,0,0,0,0,0,0,0,0,0,0,1,4
   8,75,125
BG 760 DATA 27,169,10,141,162,11,169,1,14
   1,1,3,169,87,141,2,3,1237
KU 770 DATA 169,8,141,4,3,169,7,141,5,3,1
   69,1,141,10,3,169,1135
UX 780 DATA 8,141,11,3,32,83,228,48,30,17
   3,4,3,24,185,128,141,1154
CA 790 DATA 4,3,173,5,3,185,8,141,5,3,238
   ,18,3,288,3,238,1142
NJ 800 DATA 11,3,206,162,11,208,221,104,9
   6,8,0,0,8,0,0,0,1022
UU 1000 ? "":FOR LINE=10 TO 800 STEP 10:
   TOTAL=0:? "TESTING LINE ";LINE:FOR N=1
    TO 16:READ D:TOTAL=TOTAL+D:NEXT N
IB 1010 READ D:IF TOTAL=D THEN 1030
YM 1020 ? "    ** ERROR IN LINE ";LINE
   ;" **":STOP
DD 1030 CKSUM=CKSUM+TOTAL:NEXT LINE:IF CK
   SUM<>134459 THEN ? "  ** CHECSUM ER
   ROR **":END
KG 1040 ? "  DATA IS CORRECT. INSERT
    BLANK DISK AND PRESS .":? :? "
   "
GG 1050 POKE 764,255
YS 1060 IF PEEK(764)<>12 THEN 1060
HC 1070 ? :? "    ":XIO
    254,#1,0,0,"D:":? :? "    
   "
YZ 1200 RESTORE :POKLOC=1792:FOR TIME=1 T
   0 80:FOR S=1 TO 16:READ D:POKE POKLOC,
   D:POKLOC=POKLOC+1
KN 1210 NEXT S:READ D:GOSUB 2000:NEXT TIM
   E:? :? "   ":A=USR(299
   3):? :? "    *** DONE ***":END
AS 2000 IF PEEK(755)=2 THEN POKE 755,0:RE
   TURN
WR 2010 POKE 755,2:RETURN


Listinig 2:
Assembly


28     .TITLE    ZUCCHINI PRINTER
INTERFACE BUFFER CONVERSION
30 ;
40 ;*************************
50 ;* BY LEE BRILLIANT M.D. *
60 ;*************************
70 ;
80 ;
98 ;PROGRAM CONVERTS A 400/800
95 ;COMPUTER TO A PRINTER INTERFACE-
0100 ;BUFFER AND PROGRAM LISTER.
0110 ;PRESS ESC TO CHANGE TO GRAPHICS
0115 ;PRINTING OF TEXT.
0120 ;
0130 ;USES SERIAL PORT TO CONNECT WITH
0135 ;MAIN SYSTEM.
0140 ;USES JOYSTICK PORTS TO INTERFACE
0145 ;WITH PRINTER.
0150 ;
0160 ;
0170 ;*******************
0180 ;* RAM ASSIGNMENTS *
0190 ;*******************
0200 ;
0210 AUDC1 = $D201
0220 AUDCTL = $D288
0230 AUDF3 = $D204
0240 AUDF4 = $D206
0250 BUFF = $0600
0260 BUFPNT = $CB
0270 CDTMAI = $0226
0280 CHBAS = $02F4
0290 CHLOC = $C9
0360 CIOV = $E456
0310 CONSOL = $D01F
0320 CRITIC = $42
0330 DOSVEC = S0A
0340 ICCOM = $0342
0350 IROEN = $020E
0360 KBCODE = $D209
0370 MEMLO = $02E7
0380 PACTL = $D302
0390 PBCTL = $D303
0400 POKMSK = $10
0410 PORTA = $D300
0420 PORTS = $D301
0438 RAMTOP = $6A
0440 SDMCTL = $022F
0450 SERIN = $D20D
0460 SEROUT = $D20D
0470 SETVBV = $E45C
0480 SKREST = $D20A
0490 SKCTL = $D20F
0580 SKSTAT = SD20F
0510 SSKCTL = $0232
0520 TBUFPT = $CD
0530 TRIGS = $D010
0540 VINTER = $0204
8550 VKEYBD = $0208
0560 VSERIN = $020A
0570 VSEROC = $020E
0580 VSEROR = $020C
8590 ;
0600 ;
0610 ;**********
8620 ;* VALUES *
8630 ;**********
8640 ;
8650 ;
0660 ACK =    $41
0670 CHKERR = $8F
8680 CMPLET = $43
0690 EOL =    $9B
0700 ERR =    $45
0710 FRMERR = $8C
0720 LENGTH = $26
0730 NAK =    $4E
0740 OVRRUN = $8E
0750 TOUTER = $8A
0760 ;
0770 ORIGIN = $6600
0780 ;
0790 ;
0800 ;*****************
0810 ;* BEGIN PROGRAM *
8820 ;*****************
0830 ;
0840     *=  ORIGIN
0850 ;
0860     .BYTE 0    ;HEADER
0865 ; NUMBER OF SECTORS TO BOOT
0870     .BYTE ENDPRO-ORIGIN+127/128
0880     .WORD ORIGIN ;BOOT LOCATION
0890     .WORD RESET ;INIT WARMSTART
0900 ;
0910 RESET
0915     LDA #ENDPRO&255 ;RESRVE SPACE
0920     STA MEMLO    ;FOR PROGRAM
0930     LDA #ENDPRO/256
8940     STA MEMLO+1
0950     LDA #START&255 ;PLACE IN
0955     ;           RESET CHAIN
0960     STA DOSVEC
0970     LDA #START/256
0980     STA DOSVEC+1
0990     CLC
1000     RTS
1010 START LDA #0    ;INITIALIZE O.S.
1020     STA SDMCTL
1030     STA RCVFLG  ;ZERO ALL FLAGS
1040     STA TIMFLG
1850     STA STATUS
1060     STA PACTL   ;RESET PORTS
1070     STA PBCTL
1080     LDA #255    ;ALL PINS OUTPUT
1090     STA PORTA
1100     LDA #52     ;FIX OUTPUTS AND
1105     ;           SET INTERRUPT
1110     STA PACTL
1120     LDA #1
1130     STA PORTB   ;ONE PIN OUT FOR
1135     ;            STROBE
1140     LDA #5       ;FIX OUTPUTS AND
1145     ;            SET INTERRUPT
1150     STA PBCTL
1160     LDX #0
1170     LDA #COMINT&255 ;CHANGE
1175     ;           INTERRUPT VECTORS
1180     STA VINTER  ;FOR COMMND FRAME
1190     LDA UCOMINT/256
1200     STA VINTER+1
1210     LDA URCVINT&255
1220     STA VSERIN  ;SERIAL IN READY
1230     LDA URCVINT/256
1240     STA VSERIN+1
1250     LDA USNDINT&255
1260     STA VSEROC  ;SERIAL OUT
1265     ;           COMPLETE
1270     LDA #SNDINT/256
1280     STA VSEROC+1
1290     LDA #SNDFRM&255
1300     STA VSEROR  ;SERIAL OUT RESET
1310     LDA #SNDFRM/256
1320     STA VSEROR+1
1330     LDA #KEYBD&255
1340     STA VKEYBD  ;KEYBOARD
1345     ;           INTERRUPT VECTOR
1350     LDA VKEYBD/256
1360     STA VKEYBD+1
1370     LDA MEMLO   ;SET BUF POINTERS
1380     STA BUFPNT
1390     STA BUFEND
1400     LDA MEMLO+1
1410     STA BUFPNT+l
1420     STA BUFEND+1
1430     LDA #$28    ;SERIAL BAUD RATE
1440     STA AUDF3
1450     LDA #$00
1460     STA AUDF4
1470 PRNTON JSR SETVBX ;SET 1/2
1475     ;           SECOND TIMEOUT
1480 PR1 LDA TRIG0    ;IS PRINTER ON?
1490     BEQ BEEP     ;YES 50 BRANCH
1500     LDA TIMFLG   ;WAIT FOR TIMEOUT
1510     BEQ PR1
1520     LDA #$A0     ;SET BEEP TONE
1530     JSR TONE
1540     JMP PRNTON   ;TRY AGAIN
1550 BEEP LDA #$FF    ;READY BEEPER
1560    JSR TONE
1570     STA COUNT
1580 ;
1590 ;
1600 ;-----------------------
1610 ; MAIN LOOP FOR PRINTER
1620 ;-----------------------
1630 ;
1640 MNLOOP LDA RCVFLG ;INCOMING DATA?
1650     BEQ TSTPNT
1660     JSR RECEIV
1670     JMP MNLOOP
1680 TSTPNT LDA BUFPNT ;ALL DATA SENT?
1690     CMP BUFEND ;COMPARE END DATA
1695 ;           WITH CURRENT POINTER
1700     BNE TSTEND
1710     LDA BUFPNT+1
1720     CMP BUFEND+1
1730     BNE TSTEND
1740     LDA GRWANT  ;GRAPHIC OR TEXT?
1750     CMP GRFLG
1760     BEQ MNLOOP
1770     STA GRFLG   ;YES, SET FLAG
1780     JMP MNLOOP  ;NO, GO BACK
1790 TSTEND LDA BUFPNT+l ;END OF RAM?
1800     CMP RAMTOP
1810     BCC INCPNT  ;OK
1820     LDA MEMLO   ;MOVE POINTERS
1825     ;           TO START OF RAM
1830     STA BUFPNT
1840     LDA MEMLO+1
1850     STA BUFPNT+1
1860     JMP MNLOOP  ;LOOP BACK
1870 INCPNT INC BUFPNT ;INC POINTER
1880     BNE GRTST
1890     INC BUFPNT+1
1900 GRTST LDA GRFLG ;GRAPHICS?
1910     BEQ GETCH   ;NO, PRINT TEXT
1920     JMP GRPRNT  ;YES, GRAPHICS
1930 GETCH LDY #0    ;GET A CHARACTER
1940     LDA (BUFPNT),Y
1950 PREOL CMP #EOL  ;CONVERT ATASCII
1955     ;           EOL TO PROPER EOL
1960     BNE PRNTC1
1970     LDA #$0D
1980 PRNTC1 JSR PRINT ;PRINT & RETURN
1990     JMP MNLOOP
2000 PRINT STA PORTA ;BITS TO PRINTER
2010 WTBUSY LDA TRIGS ;PRINTER BUSY?
2020     BEQ PRNTCH  ;NO, BRANCH
2830     LDA RCVFLG  ;YES, TEST FOR
2035     ;           INCOMING DATA
2040     BEQ WTBUSY
2850     JSR RECEIV  ;GO RECEIVE
2860     JMP WTBUSY
2070 PRNTCH DEC PORTB ;SET STROBE
2080     NOP         ;WAIT FOR PRINTER
2090     INC PORTB   ;RESET STROBE
2100     RTS
2110 DELAY NOP       ;DUMMY DELAY
2120     RTS
2130 GRPRNT LDY #0   ;PRINT TEXT
2135     ;           AS GRAPHICS
2140     STY INVFLG
2150     LDA (BUFPNT),Y ;GET A CHAR
2160     STA TEMP
2170     CMP #EOL    ;EOL?
2180     BNE TSTCNT  ;NO BRCH TO PRINT
2190     LDY CCOUNT  ;YES-LINE FULL?
2200     BNE LINFIL  ;NO SO FILL
2210     JMP PREOL   ;YES SO PRINT EOL
2220 LINFIL LDA #0   ;FILL OUT PRINTER
2225     ;           LINE WITH 0s
2230     LDX #8
2240 FILOOP JSR PRINT ;8 0'S PER CHAR
2250     DEX
2260     BNE FILOOP
2270     INC CCOUNT
2280     LDA CCOUNT
2290     CMP #LENGTH ;ALL LINE SENT?
2300     BNE LINFIL
2310     JMP EXIT
2320 TSTCNT LDA CCOUNT ;NEW LINE?
2330     BNE CONVRT  ;NO 50 BRANCH
2340     LDX #3
2350 SNDCOD LDA CODEX ;SEND ESCPE SEQ
2360     JSR PRINT
2370     DEX
2380     BPL SNDCOD
2390 CONVRT LDA TEMP ;ASCII TO ATASCII
2400     BPL CONVR1
2410     AND #$7F
2420     STA TEMP
2430     DEC INVFLG
2440 CONVR1 CMP #96
2450     BCS X8
2460     CMP #32
2470     BCC ADD64
2480     CLC
2490     ADC #224
2500     JMP X8
2510 ADD64 ADC #64
2520 X8  STA CHLOC   ;MULTIPLY X8 TO
2525     ;           GET CHSET OFFSET
2530     LDA #0
2540     STA CHLOC+1
2550     LDX #3
2560 ROT ASL CHLOC
2570     ROL CHLOC+1
2580     DEX
2590     BNE ROT
2600     CLC
2610     LDA CHBAS    ;ADD TO CHBASE TO
2615     ;    FIND LOC. IN RAM
2620     ADC CHLOC+1
2630     STA CHLOC+1
2640 SHIFT LDA #0    ;CREATE NEW BYTE
2645     ;    FROM BITS OF
2650     STA TEMP    ;EACH BYTE IN CHR
2660     LDA #$88
2670     STA BITMSK ;SELECT WHICH BIT
2680     LDX #7
2690 SLOOP1 LDY #7
2700 SLOOP2 LDA (CHLOC),Y ;SAME BIT IN
2705    ;            EACH BYTE
2710     CLC
2720     AND BITMSK
2730     BEQ NXTBIT
2740     SEC
2750 NXTBIT ROR TEMP
2760     DEY
2770     BPL SLOOP2
2780     LSR BITMSK ;NEXT BIT
2790     LDA TEMP
2800     EOR INVFLG ;INVERSE CHAR?
2810     JSR PRINT
2820     DEX
2830     BPL SLOOP1
2840     INC CCOUNT
2850     LDA CCOUNT  ;ALL CHARS SENT?
2860     CMP #LENGTH
2870     BNE EXIT2
2880 EXIT LDA #0    ;RESET CCOUNT
2890     STA CCOUNT
2900     LDA #$0D   ;SEND EOL
2910     JSR PRINT
2920 EXIT2 JMP MNLOOP
2930 ;
2940 ;
2950 ;**********
2960 ;* RETURN *
2970 ;**********
2980 ;
2990 RETURN PLA    ;RESTORE REGS
3000     TAX
3010     PLA
3020     TAY
3030     LDA #0
3040     STA RCVFLG   ;ZERO FLAG
3050     LDA #5
3060     STA PBCTL    ;RESTORE VINTER
3070     RTS
3680 ;
3090 ;
3100 ;*******************
3110 ;* RECEIVE ROUTINE *
3120 ;*******************
3130 ;
3140 ;
3150 RECEIV TYA      ;RECEIVE A DATA
3155     ;           FRAME
3160     PHA         ;SAVE REGISTERS
3170     TXA
3180     PHA
3190     JSR SETVBX  ;SET TIMEOUT
3200     LDA #4
3210     STA BUFSIZ  ;# OF BYTES IN
3215     ;           IN COMMAND FRAME
3220     JSR GETFRM
3230 CNGINT LDA #7   ;CHANGE VINTER TO
3235     ;           RESPOND
3240     STA PBCTL  ;TO + TRANSITION
3250     LDA #0
3260     STA RCVFLG
3270 INTWAT LDA RCVFLG ;WAIT FOR END
3275 ;    OF COMMAND FRAME INTERRUPT
3280     BNE TSTDEV  ;YES SO BRANCH
3290     LDA TIMFLG  ;TIMEOUT?
3300     BEQ INTWAT  ;NO-KEEP WAITING
3310     JMP RETURN  ;YES-RETURN
3320     LDY STATUS  ;OPERATION OK?
3330     CPY #CHKERR ;YES SO BRANCH
3340     BEQ RETURN  ;ERROR SO GO BACK
3350 TSTDEV LDA BUFF ;FIRST BYTE IS
3355     ;           DEVICE ID
3360     CMP #$40    ;RIGHT DEVICE ID?
3370     BEQ THISDV  ;YES SO BRANCH
3380     JMP RETURN  ;NO SO RETURN
3390 THISDV LDA BUFF+1 ;CHECK COMMAND
3400     CMP #$57    ;WRITE?
3410     BEQ FRMOK   ;YES SO GO ON
3420     CMP #$53    ;STATUS?
3430     BNE WNGCOM  ;NO-WRONG COMMAND
3440     JSR GDSTAT  ;SEND STAT FRAME
3450     JMP RETURN  ;BACK TO MNLOOP
3460 WNGCOM LDY #NAK ;WRONG COMMAND
3470     JSR SDSTAT
3480     JMP COMPLT  ;SEND A COMPLETE
3485     ;           AND RETURN
3490 SNDACK LDY #ACK ;SEND AN ACK
3500     JMP SDSTAT
3510 FRMOK JSR SNDACK ;COMMND FRAME OK
3520     LDA #40     ;DATA BUF SIZE=40
3530     STA BUFSIZ
3540     JSR GETFRM  ;GO RECEIVE FRAME
3550     LDY STATUS
3560     BEQ MOVBUF  ;FRAME OK-BRANCH
3570     LDY #ERR
3580     JSR SDSTAT
3590     JMP COMPLT
3600 SNDSTS JSR SDSTAT ;NO 50 SEND
3605     ;           ERROR AND RETURN
3610     JMP RETURN
3620 MOVBUF JSR SNDACK ;ACK FRAME
3630     LDY #0
3640     LDX #0
3650     LDA BUFEND  ;SET TEMP POINTER
3655 ;          TO END OF MAIN BUFFER
3660     STA TBUFPT
3670     LDA BUFEND+1
3680     STA TBUFPT+1
3690 INBFPT INC TBUFPT
3700     BNE MOVBF1
3710     INC TBUFPT+1
3715 MOVBF1
3720     LDA TBUFPT  ;MAIN BUFF FULL?
3730     CMP BUFPNT
3740     BNE MOVBYT  ;NO SO BRANCH
3750     LDA TBUFPT+1
3760     CMP BUFPNT+1
3770     BNE MOVBYT
3780     JMP RETURN  ;MAIN BUFFER
3785     ;           FULL SO RETURN
3790 ;RETURN WITHOUT CMP SENT CAUSES
3795 ;HOST TO TIMEOUT FOR UP TO 56
3790 ;SECONDS.
3800 ;THIS GIVES TIME FOR BUFFER TO
3805 ;EMPTY SOME.
3810 ;CAN BE CHANGED BY ALTERING
3815 ;COMMAND RESPONSE FRAME BYTE 3
3820 MOVBYT LDA TBUFPT+1 ;END OF RAM?
3830     CMP RAMTOP
3840     BCC GETBYT  ;NO SO BRANCH
3850     LDA MEMLO   ;RESET POINTER
3855 ;           TO START OF BUFFER
3860     STA TBUFPT
3870     LDA MEMLO+1
3880     STA TBUFPT+1
3890     JMP MOVBF1
3900 GETBYT LDA BUFF,X ;MOVE A BYTE
3905     ;           FROM TEMP
3910     INX
3920 STOBYT STA (TBUFPT),Y
3930     CMP #EOL    ;CARRIAGE RETURN?
3940     BEQ ENDMOV  ;YES-STOP DATA
3950     CPX #40     ;NO THEN WHOLE
3955     ;           BUFF MOVED?
3960     BNE INBFPT  ;NO-GET NEXT BYTE
3970 ENDMOV LDA TBUFPT ;YES SO UPDATE
3975     ;           BUFFER POINTERS
3980     STA BUFEND
3990     LDA TBUFPT+1
4000     STA BUFEND+1
4010 COMPLT LDY #CMPLET ;SEND COMPLETE
4015     ;           BYTE, AND RETURN
4020     JMP SNDSTS
4030 ;-------------
4040 ; GET A FRAME
4050 ;-------------
4060 ;
4070 GETFRM LDA #0   ;GET A DATA FRAME
4080     STA BUFRFL  ;ZERO VARIABLES
4090     STA COUNT
4100     STA CHKSUM
4110     STA RECOMP
4120     STA STATUS
4130     JSR RECVEN  ;ENABLE RECEIVE
4140 CKTIME LDA RECOMP ;RECV COMPLETE?
4150     BNE GOBACK  ;YES, RETURN
4160     LDA TIMFLG  ;TIMEOUT?
4170     BEQ CKTIME  ;NO SO WAIT AGAIN
4180 TIMOUT LDA #TOUTER ;TIMEOUT
4190     STA STATUS
4200 GOBACK JSR DISABLE
4210     RTS
4220 ;
4230 ;
4248 ;--------------------
4250 ; INTERRUPT ROUTINES
4260 ;--------------------
4270 ;RECEIVE INTERRUPT DRIVEN
4275 ; BY VSERIN
4280 ;
4290 RCVINT TYA      ;EACH TIME THERE
4295     ;           IS A BYTE READY
4300     PHA         ;THIS ROUTINE
4305     ;           EXECUTES.
4310     LDA SKSTAT  ;CHECK FOR SERIAL
4315     ;           ERRORS
4320     STA SKREST  ;RESET ERR REGS
4330     BMI NTFRM
4340     LDY #FRMERR
4350     STY STATUS
4360 NTFRM AND #$20
4370     BNE NTOVRN  ;OVERRUN ERR?
4380     LDY #OVRRUN
4390     STY STATUS
4400 NTOVRN LDA BUFRFL
4410     BEQ NOTDON  ;TEMP BUF FULL?
4420     LDA SERIN   ;CHECKSUM OK?
4430     CMP CHKSUM
4440     BEQ SRETRN
4450     LDY #CHKERR
4460     STY STATUS
4470 SRETRN INC RECOMP ;SET RECEIVE
4475     ;           COMPLETE FLAG
4480 INTDON PLA      ;RESTORE Y
4490     TAY
4500     PLA
4510     RTI         ;RETURN FROM
4515     ;           INTERRUPT
4520 NOTDON LDA SERIN ;GET A BYTE
4530     LDY COUNT
4540     STA BUFF,Y  ;PUT IN TEMP BUF
4550     CLC
4560     ADC CHKSUM  ;TOTAL CHECKSUM
4570     ADC #0
4580     STA CHKSUM
4590     INY
4600     STY COUNT   ;INC BUFF COUNTER
4610     CPY BUFSIZ
4620     BMI INTDON  ;BUFF FULL?
4630     LDA #1      ;YES SET FLAG
4640     STA BUFRFL
4650     JMP INTDON
4660 ;
4670 ;
4680 ;SERIAL OUTPUT DATA REQUEST
4690 ;DRIVEN BY VSEROR
4695 ;
4700 SNDFRM TYA     ;SAVE Y
4710     PHA
4720     INC COUNT  ;ALL DATA OUT?
4730     LDY COUNT
4740     CPY BUFSIZ
4750     BCC ENDSND ;NO SO SEND
4760     LDA CHKSNT ;CHECKSUM SENT?
4770     BEQ SNDCHK ;NO SO SEND
4780     LDA POKMSK ;YES SO CHANGE
4785     ;          INTERRUPTS
4790     ORA #$08
4800     STA POKMSK
4810     STA IRGEN
4820     JMP IRETRN
4836 ENDSND LDA BUFF,Y ;SEND A BYTE
4840     STA SEROUT
4850     CLC
4860     ADC CHKSUM ;ADD TO CHECKSUM
4870     ADC #0
4880     STA CHKSUM
4890 IRETRN PLA     ;RETURN FROM
4895     ;          INTERRUPT
4900      TAY
4910      PLA
4920      RTI
4930 SNDCHK LDA CHKSUM ;SEND CHECKSUM
4940     STA SEROUT
4950     LDA #1      ;AND SET FLAG
4960     STA CHKSNT
4970     JMP IRETRN
4980 ;
4990 ;SERIAL OUTPUT INTERRUPT
5000 ;DRIVEN BY VSEROC
5005 ;
5010 SNDINT LDA #1   ;SETS FLAG AFTER
5015     ;           CHECKSUM SENT
5020     STA SNDFLG
5030     LDA POKMSK  ;RESTORE INTRRPTS
5040     AND #$F7
5050     STA IRQEN
5060     STA POKMSK
5070     PLA
5080     RTI
5090 ;
5100 ;
5118 ;COMMAND FRAME INTERRUPT
5120 ;DRIVEN BY VINTER
5125 ;
5130 COMINT LDA #1   ;SET FLAG WHEN
5135 ;      WHEN COMMAND FRAME SET
5140     STA RCVFLG
5150     PLA
5160     RTI
5170 ;
5180 ;
5190 ;SET TIMEOUT INTERRUPT
5200 ;
5205 SETVBK
5210     LDA #STIMOT&255 ;SET POINTER
5215     ;           TO ROUTINE
5220     STA CDTMA1
5230     LDA #STIMOT/256
5240     STA CDTMAI+1
5250     LDX #0      ;SET VB INTERRUPT
5260     LDY #30     ;1/2 SECOND WAIT
5270     LDA #1
5280     SEI
5290     JSR SETVBV
5300     LDA #0
5310     STA TIMFLG  ;ZERO TIMEOUT FLG
5320     CLI
5330     RTS
5340 ;
5350 STIMOT LDA #1   ;SETS FLAG AFTER
5355     ;           TIMEOUT
5360     STA TIMFLG
5370     RTS
5390 ;
5400 ;NEW KEYBOARD INTERRUPT HANDLER
5410 ;SETS FLAG ONLY IF ESC PRESSED
5415 ;
5420 KEYED LDA KBCODE ;GET ICODE
5425     ;           FOR KEYPRESS
5430     CMP #28     ; ESCAPE?
5440     BNE KRETRN  ;NO, RETURN
5460     BEQ WANTGR
5470     DEC GRWANT
5480     JMP KRETRN
5490 WANTGR INC GRWANT
5500     LDA #0
5510     STA CCOUNT ;CHAR COUNT=0
5520 KRETRN PLA
5530     RTI
5540 ;
5550 ;TIMER
5560 ;
5565 CONTDN
5570 CONTDN DEK     ;SHORT INTERVAL
5575     ;          TIMER
5580     BNE CONTDN
5590     DEY
5600     BNE CONTDN
5610     RTS
5620 ;
5630 ;
5640 ;SPEAKER TONE ROUTINE
5650 TONE STA BUFSIZ
5660     STA COUNT
5670 TONE1 LDX COUNT ;SET VALUES
5680     LDY #1      ;FOR TIMER
5690     LDA #255

5700     STA CONSOL  ;MOVE SPEAKER

5710     JSR CONTDN  ;TIME DELAY
5720     STY CONSOL  ;MOVE OTHER WAY
5730     INY
5740     LDX COUNT   ;DELAY
5750     JSR CONTDN
5770     BNE TONE1   ;KEEP GOING
5780     RTS
5790 ;
5800 ;
5810 ;************************
5830 ;************************
5840 ;
5850 SDSTAT TYA      ;SAVE Y
5860     PHA
5880     STA SNDFLG
5890     STA BUFSIZ
5900     STA COUNT
5910     JSR SENDEN  ;ENABLE POKEY
5920     LDY #2      ;1 MILLISECOND
5925     ;           DELAY
5930     STY CHKSNT