franco@iuvax.cs.indiana.edu (10/23/87)
SPLAST A print SPooLer for the Atari ST Purpose: I never did see a decent PD spooler for the ST so I decided to write one (at least I believe it is decent). The main design objectives were 1. Easy to use 2. Uses little computational resource (space and time) 3. The file selected for printing need not stay around 4. Does not affect other critical processes like transferring files over the RS232 port. 5. Allow up to 2 dozen files to be queued for printing Description: I felt the best way to meet these objectives is to use the ETERNAL ramdisk, which I set up every time I use the ST, as buffer area. Each request for spooling results in a copy of the requested file in ramdisk under the name *xxxxtmp.prt, where * is a character from a-z. In addition, a file named pqueue.tmp contains a list of spooled files in the order they will be printed (the first file is the one currently being printed). After a .prt file is printed it is deleted, as is its namesake in pqueue.tmp. When the queue empties and all files are printed, pqueue.tmp is deleted. Performance: Seems to be pretty stable. Load on the processor is not noticeable whether or not a file is being printed. Only a slight slowdown in printing speed is noticed on an LQ800/1000. Errorless file transfers at 19200 baud using ZMDM in Gulam while the spooler is installed. Errorless file transfers at 1200 baud using ZMDM while printing (my Atari at work can go to 19200 baud but does not connect to a printer, my Atari at home connects to a printer but is limited to 1200 baud by my modem - I expect reliable transfers at much higher baud rates while printing). Installation: Run SPLAST.PRG (but not from the auto folder). You will lose about 9k of RAM to a memory resident program and the program SPOOL.TTP will appear in the ramdisk (regardless of the drive id you choose). Operation: To enqueue a file FOO.BAR run SPOOL with the command line <path>\FOO.BAR. To stop printing that file delete the .prt file associated with it (allow a few lines for the buffer to clear out). To change the order of files printed, edit pqueue.tmp (there is no reason, by the the way, why you can't simply add some file names to pqueue.tmp yourself. However, you should remember that there will be an attempt to delete those files after they are printed so write protect them if you want to keep them around). Also, each line in pqueue.tmp must be exactly 12 characters long (plus <cr><lf>). If you add a filename that is less than 12 characters long, pad the line with blanks on the right. If you create pqueue.tmp on your own you must run SPLAST again to "kick" the spooler into operation (it will not reinstall on top of itself - in fact it will display a message saying that a spooler is already in place). It is not necessary to run SPLAST before using SPOOL (which you snatched up and saved the first time you ran SPLAST) or creating pqueue.tmp on your own; the next time SPLAST is run the enqueued files will be printed. Requirements: This spooler can only be used with versions of the ETERNAL (reset survivable) ramdisk. If the ramdisk is not in place when SPLAST is run, a message indicating such will be displayed and SPLAST will not attempt to install itself. Resources: This spooler uses about 9k of RAM. It can be removed only by means of a reset (which does not affect your valuable data in the ramdisk). No other RAM space is used by the spooler outside of the ramdisk. If the queue is empty, only about a dozen print spool instructions are executed every 1/50 th of a second. What it doesn't do but should: Although I have trap 13 calls to the printer, for some reason the printing of characters is jumbled if the spooler is printing a file while the user attempts an ordinary print. I do not know why this happens. Bonus: The code below should be interesting to the novice who wishes to learn more about the ST but is unable to do so because of a dirth of documentation. For example, the code shows precisely the sequence of instructions required to send a single character to the printer port (about 25-30 instructions), and where the command line arguments are to be found (whoops...you need to disassemble SPOOL.TTP to find that out). Disclaimer: I consider myself to be a novice when it comes to computer internals and assembly language. I make no claims about how great the code below is. In fact, it is probably true that some people will vomit when they see it. Sorry. However, I have taken some pains to document the code thoroughly so it should not be too hard to change if you wish. Copyright information: I really don't care what you do with this piece of code. Just don't tell them I sent you. John Franco Bloomington, Indiana October 23, 1987 franco@iuvax.cs.indiana.edu Call the program below SPLAST.ASM and assemble with GST assembler. To assemble with the Atari assembler change SECTION CODE to .text, END to .end, and all dc & ds to .dc & .ds (and change the name to splast.s). ****************************************************************************** * * * SPLAST - a print spooler for the Atari ST * * * * Requires: An ETERNAL ramdisk (code may be modified for others) * * * * Installation: Run splast.prg (spool.ttp is created in ramdisk) * * Do not put splast.prg in the auto folder - use * * startgem.prg to install splast at bootup. * * * * Copyright: none - do whatever you want with it or to it. * * * * Disclaimer: Use at your own risk. I am not responsible for any * * damage resulting from the use of this program. * * * * John Franco * * Bloomington, Indiana * * * ****************************************************************************** SECTION CODE bra begin ****************************************************************************** * * * This section is entered during event_timer interrupts * * * ****************************************************************************** start: movem.l d0-d7/a0-a7,-(sp) move.w sr,-(sp) ori.w #$700,sr clr.l d0 cmpi.l #$0,tfile ; if 1st 4 bytes at tfile are 0 then no beq more ; existing work so check for more. Otherwise cmpi.l #$0,byteso ; if no more bytes to send ble newbuf ; then refill buffer (goto newbuf). Otherwise, cont: move.w #$6,d5 ; Loop 6 times max trying to print 2 chars V1: move.l bufptr,a6 ; print a character of text move.b (a6)+,d4 bsr put tst.l d0 ; If no character was sent then forget it blt out6 ; and try again. Otherwise, subq.l #$1,byteso ; subtract one from byteso, subq.l #$1,fsize ; subtract one from file size move.l a6,bufptr ; update buffer pointer cmpi.l #$0,byteso ; If the buffer is empty ble out ; then break out of the loop out6: tst.w d5 ; Otherwise, try again dbeq d5,V1 ; bra out newbuf: move.l clust,d0 cmpi.l #$FF0,d0 ; If this cluster is illegal or the last one bge nextf ; then get next file. Otherwise, move.l d0,d3 bsr nextcl ; compute the next cluster to get. cmpi.w #$1,d0 ; If next cluster is 1 or 0 then ble nextf ; get next file, if any. Otherwise, move.l d0,clust ; save next cluster and move.l d3,d0 bsr trans ; transfer 1k to buffer from cluster in d0 move.l #$400,byteso ; If more than 1k left to transfer cmpi.l #$400,fsize ; then set byteso to 1k otherwise bgt full move.l fsize,byteso ; set byteso to number of bytes left full: move.l #buffer,bufptr ; Set bufptr. bra out more: move.l $42E,a0 ; Check if we think that pqueue.tmp exists. move.b -(a0),d0 move.b d0,d1 andi.l #$2,d0 tst.w d0 beq out ; If we don't think so then leave. andi.l #$1,d1 tst.w d1 bne out move.l #tlext,a0 ; Open pqueue.tmp bsr findf move.l a0,qfadd ; file descriptor address move.l d1,qclus ; cluster start move.l size,qsize ; size of pqueue.tmp (bytes) tst.w d0 ; If it does not exist then leave bmi non ; Otherwise, cmpi.l #$4,qsize ; if the number of bytes in queue.tmp is less bmi erasp ; than 4 then erase pqueue.tmp. Otherwise, move.l qclus,d0 ; pqueue.tmp cluster number is parameter bsr compcl ; compute pqueue.tmp cluster address (a0) move.l #tfile,a1 ; a1 has the address of the tfile_name buffer move.l #$B,d1 ; set up loop to transfer 12 bytes B1: move.b (a0)+,(a1)+ ; Transfer bytes tst.l d1 dbeq d1,B1 move.b #$0,(a1)+ ; straighten up tfile so it terminates with 0 move.b #$0,(a1)+ move.l #tfile,a0 ; locate the tfile bsr findf tst.w d0 ; If the file specified has some problem bmi nextf ; then branch to error. Otherwise, move.l a0,descrp ; save the file descriptor address, move.l d1,clust ; save the first cluster location, and move.l size,fsize ; save file size. bra out ; Branch to out (with the file name in tfile). nextf: move.l #$C,d4 ; Form feed. bsr put move.l #pfile,-(sp) ; Delete the text file. move.w #$41,-(sp) trap #1 addq.l #6,sp move.l #tfile,a0 ; Zero out the tfile field. move.l #$0,(a0) move.l $42E,a0 ; Check if we think that pqueue.tmp exists. move.b -(a0),d0 move.b d0,d1 andi.l #$2,d0 tst.w d0 beq out ; If we don't think so then leave. andi.l #$1,d1 tst.w d1 bne out ; leave if it is move.l #tlext,a0 ; Open pqueue.tmp bsr findf move.l a0,qfadd ; file descriptor address move.l d1,qclus ; cluster start move.l size,qsize ; size of pqueue.tmp (bytes) tst.w d0 ; If pqueue.tmp does not exist bmi non ; then leave. move.l qsize,d2 ; Set up to remove first record. sub.l #$E,d2 ; d1 has the number of bytes to transfer. cmpi.l #$4,d2 ; If print queue file is empty then bmi erasp ; branch to cleanup routine. Otherwise, move.l d2,qsize ; Remove first record of pqueue.tmp: sub.l #$1,d2 move.l qclus,d0 ; pqueue.tmp cluster number is parameter bsr compcl ; compute pqueue.tmp cluster address (a0) movea.l a0,a1 ; a0 points to the beginning of pqueue.tmp add.l #$E,a1 ; a1 points to beginning of 2nd record B2: move.b (a1)+,(a0)+ ; Move bytes. tst.l d2 dbeq d2,B2 ; Result: erase first record in pqueue.tmp movea.l #qsize,a1 ; Record new size for file pqueue.tmp. movea.l qfadd,a0 ; a1 points to pqueue.tmp size field. add.l #32,a0 ; a0 points to end of pqueue.tmp descriptor. move.b (a1)+,-(a0) move.b (a1)+,-(a0) move.b (a1)+,-(a0) move.b (a1)+,-(a0) move.l $42E,a0 move.b -(a0),d0 ; record the fact that pqueue.tmp exists ori.b #$2,d0 move.b d0,(a0) bra out ; and leave. erasp: move.l #qfile,-(sp) ; delete the print queue file. move.w #$41,-(sp) trap #1 addq.l #$6,sp non: move.l $42E,a0 move.b -(a0),d0 ; Record the fact that pqueue.tmp is gone. move.b #$4,d0 move.b d0,(a0) out: move.w (sp)+,sr movem.l (sp)+,d0-d7/a0-a7 move.l event,a3 ; load event_timer vector jmp (a3) ; jump to event_timer handler ******************************************************************************* * * * This section is entered on trap 13 calls (to handle bconout and stat) * * * ******************************************************************************* trap: move.w sr,-(sp) ori.w #$700,sr move.l #stack,stack move.l a0,tmpa0 movea.l stack,a0 move.l tmpa0,-(a0) move.l d0,-(a0) move.w (a7),d0 move.w (a7)+,-(a0) move.l (a7)+,-(a0) movem.l d1-d7/a1-a7,-(a0) move.l a0,stack btst #$D,d0 ; Check if supervisor mode bne hit ; if so continue move.l usp,a7 ; Otherwise make user stack hit: move.w (a7),d0 ; The function number is in d0 cmp.w #$3,d0 ; If the function is not bconstat bne hit1 ; then continue checking for bconout tst.w $2(a7) bne out2 ; If the device is not printer, continue move.l $42E,a0 ; Otherwise, move.b -(a0),d3 move.b d3,d4 andi.l #$2,d3 tst.w d3 ; if pqueue.tmp exists bne out3 ; then return from exception andi.l #$1,d4 ; if pqueue.tmp busy tst.w d4 bne out3 ; then return from exception bra out2 ; Otherwise continue with trap hit1: cmp.w #$8,d0 ; If the function is not bconout bne out2 ; then continue with trap tst.w $2(a7) bne out2 ; If the device is not printer, continue move.l $42E,a0 ; Otherwise move.b -(a0),d3 move.b d3,d4 andi.l #$2,d3 tst.w d3 ; if pqueue.tmp exists bne out3 ; then return from exception andi.l #$1,d4 tst.w d4 ; if pqueue.tmp is busy bne out3 ; then return from exception bra out2 ; Otherwise continue with trap out2: movea.l stack,a0 movem.l (a0)+,d1-d7/a1-a7 move.l (a0)+,-(a7) move.w (a0)+,-(a7) move.l (a0)+,d0 move.l (a0)+,a0 move.w (sp)+,sr movea.l trapv,a0 jmp (a0) out3: movea.l stack,a0 movem.l (a0)+,d1-d7/a1-a7 move.l (a0)+,-(a7) move.w (a0)+,-(a7) move.l (a0)+,d0 move.l (a0)+,a0 move.w (sp)+,sr move.l #$0,d0 rte ******************************************************************************* * * * Routine to write a character to the centronics port. The character * * is placed in d4. Only d0, d4 and a0 are affected by this call. * * Returns immediately if the centronics port is busy (character not * * printed). * * * * Input: d4 - character to print * * * * Output: d0 - -1 if the character is not printed * * * * Modifies: d0,d4,a0 * * * ******************************************************************************* put: move.w sr,-(sp) ; disable interrupts ori.w #$700,sr move.l #-$1,d0 ; default status is fail move.l #$FFFFA01,a0 btst.b #$0,0(a0) bne nope move.l #$FFFF8800,a0 ; sound chip register address move.b #$7,(a0) ; select register 7 clr.l d0 move.b (a0),d0 ; read status byte to d0 ori.b #$80,d0 ; 8th bit set to high (write to centronics) move.b #$7,(a0) ; select register 7 move.b d0,2(a0) ; write new status byte to sound chip move.b #$F,(a0) ; select register 15 (B port) move.b d4,2(a0) ; write character to address move.b #$E,(a0) ; read register 14 clr.l d0 move.b (a0),d0 andi.b #$DF,d0 ; set strobe bit to low move.b #$E,(a0) ; write to register 14 (setting strobe low) move.b d0,2(a0) move.b #$E,(a0) ; read register 14 clr.l d0 move.b (a0),d0 ori.b #$20,d0 ; set strobe bit to high move.b #$E,(a0) ; write to register 14 move.b d0,2(a0) move.l #$0,d0 ; status set to succeed nope: move.w (sp)+,sr ; enable interrupts rts ******************************************************************************* * * * Compute the absolute address of a ramdisk cluster * * * * Input: d0 - the cluster number * * * * Output: a0 - the cluster absolute address * * * ******************************************************************************* compcl: move.l d0,d1 sub.l #2,d1 lsl.l #$8,d1 lsl.l #$2,d1 add.l datast,d1 ; Cluster Address = (d0-2)*1024 + data_start movea.l d1,a0 rts ******************************************************************************* * * * Transfer data from a cluster on RD to the text buffer * * * * Input: d0 - cluster number to transfer * * * * Output: none * * * ******************************************************************************* trans: cmpi.l #$FF0,d0 ; if cluster number is greater than $FF0 then bge M1 ; we are finished bsr compcl ; compute cluster absolute address (a0) move.w #$1F,d1 ; transfer 1024 bytes quickly (assume even move.l #buffer,a1 ; address boundary) M2: move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ move.l (a0)+,(a1)+ tst.w d1 dbeq d1,M2 M1: rts ******************************************************************************* * * * Compute the next cluster in a chain of FAT entries * * * * Input: d0 - Current cluster number * * * * Output: d0 - Contains the next cluster in the chain * * * * Uses: fatst - the absolute address of the start of the ramdisk FAT * * * * Modifies: d0,d1,a0 * * * ******************************************************************************* nextcl: move.l fatst,a0 btst #$0,d0 ; if d0 is even beq even ; then branch odd: move.l d0,d1 lsl.l #$1,d1 add.l d1,d0 ; d0 is multiplied by 3 lsr.l #$1,d0 ; d0 is divided by 2 add.l d0,a0 ; a0 has absolute address of low cluster byte move.b (a0)+,d0 ; d0 has next cluster low byte lsr.l #$4,d0 ; first 4 bits of d0 contain low 4 bits of clus andi.l #$F,d0 ; mask out anything else move.b (a0),d1 ; d1 has next cluster high byte lsl.l #$4,d1 ; move it left by 4 bits or.l d1,d0 ; combine high and low bytes into d0 andi.l #$FFF,d0 ; get rid of any junk rts even: move.l d0,d1 lsl.l #$1,d1 add.l d1,d0 ; d0 is multiplied by 3 lsr.l #$1,d0 ; d0 is divided by 2 add.l d0,a0 ; a0 has absolute address of low cluster byte move.b (a0)+,d0 ; d0 has next cluster low byte andi.l #$FF,d0 ; mask out anything else move.b (a0),d1 ; d1 has next cluster high byte lsl.l #$8,d1 ; move it left by 8 bits or.l d1,d0 ; combine high and low bytes into d0 andi.l #$FFF,d0 ; get rid of any junk rts ******************************************************************************* * * * Find a file in the ramdisk root directory, get address and info * * * * Inputs: a0 - name of file to compare against * * * * Output: d0 - -1 if file not found, otherwise meaningless * * d1 - cluster number of first cluster of file * * a0 - start (ram) address of file description in directory * * size - long word memory location with file size * * * * Uses: rootst - absolute address of start of root directory * * size - see above * * * * Modifies: d0,d1,d2,a1,a2,a3,a4 * * * ******************************************************************************* findf: move.l #$6F,d0 ; repeat the follwing $DF times (max number) move.l rootst,a2 ; (once for each possible file descriptor) L1: move.l a2,a1 ; let a1 have rootst and a4 have filename move.l a0,a4 ; repeat the following (once for each move.w #$7,d1 ; character in filename) L2: cmp.b (a1)+,(a4)+ ; compare characters of filename with descript bne O1 ; if some character is not equal, try another tst.w d1 ; descriptor dbeq d1,L2 move.l a2,a0 ; if all characters matched then file found add.l #26,a2 ; so locate the first cluster bytes move.b (a2)+,d1 ; and save them in d1 move.b (a2)+,d2 lsl.l #$8,d2 or.l d2,d1 andi.l #$FFFF,d1 move.l #size,a3 addq.l #$4,a2 move.b -(a2),(a3)+ ; locate file size and save in d0 move.b -(a2),(a3)+ move.b -(a2),(a3)+ move.b -(a2),(a3)+ rts O1: add.l #$20,a2 ; 32 bytes is a descriptor length tst.w d0 ; if we have not considered all descriptors dbeq d0,L1 ; then go back and consider the next one move.l #-1,d0 ; otherwise return code for failure rts ******************************************************************************* * * * Data area. Meanings are as follows: * * * * trapv: - new trap 13 vector * * supsav: - save the system stack pointer when entering SUPER mode here. * * tmpa0: - temporary save area for a0 in trap 13 routine * * bufptr: - pointer to the buffer area * * byteso: - number of bytes in buffer left to transmit to printer. * * size: - save file size here (no. bytes) when using findf subroutine * * fsize: - size gets moved here after finding a text file * * qsize: - size gets moved here after finding pqueue.tmp * * descrp: - absolute address of text file root directory descriptor * * clust: - current cluster number being transfered * * qclus: - cluster in which pqueue.tmp currently is * * qfadd: - absolute address of pqueue.tmp root directory descriptor * * rootst: - absolute address of start of root directory of ramdisk * * fatst: - absloute address of start of FAT of ramdisk * * datast: - absolute address of start of data area in ramdisk * * event: - new event_timer vector * * buffer: - file transfer buffer for creating temporary files. * * empty: - curently unused * * pfile: - space for *.prt file being printed * * tfile: - same as above except drive descriptor is missing * * tlname: - name of the file containing the print queue. * * tlext: - same as above except drive descriptor is missing * * qfile: - another way to write 'pqueue.tmp' * * reserv: - boundary of a local stack needed below * * * ******************************************************************************* trapv: ds.l $1 supsav: ds.l $1 tmpa0: ds.l $1 bufptr: ds.l $1 byteso: ds.l $1 size: ds.l $1 fsize: ds.l $1 qsize: ds.l $1 descrp: ds.l $1 clust: ds.l $1 qclus: ds.l $1 qfadd: ds.l $1 rootst: ds.l $1 fatst: ds.l $1 datast: ds.l $1 event: ds.l $1 quhand: ds.w $1 buffer: ds.b $402 empty: dc.w 0 pfile: dc.b 'm:' tfile: dc.b 0,0,0,0,0,0,0,0,0,0,0,0,0,0 tlname: dc.b 'm:' tlext: dc.b 'PQUEUE .TMP',0 qfile: dc.b 'm:pqueue.tmp',0 qufil: dc.b 'm:spool.ttp',0 text: dc.b 'Spooler already installed',$D,$A,0 text1: dc.b 'ETERNAL Ramdisk cannot be found',$D,$A,0 ccsp: dc.b 'Cannot create SPOOL.PRG',$D,$A,0 cwsp: dc.b 'Cannot write SPOOL.PRG',$D,$A,0,0 reserv: ds.l $400 stack: ds.l $1 ****************************************************************************** * * * This section installs the spooler and sets up some spooler parameters. * * The program 'queue.prg' is written to the ramdisk. * * * ****************************************************************************** begin: clr.l -(sp) ; switch to supervisor mode move.w #$20,-(sp) trap #1 addq.l #$6,sp move.l d0,supsav ; save old stack pointer move.l $42E,a0 move.w (a0),d0 cmpi.w #$200,d0 beq etern ; If the eternal ramdisk is there then continue move.l #text1,d0 ; otherwise display message bsr up bra quit ; and leave. etern: move.b -(a0),d0 andi.l #$4,d0 tst.w d0 beq all ; If spooler does not exist then continue move.b #$6,d0 ; set 'pqueue.tmp exists' bit move.b d0,(a0) ; display message move.l #text,d0 bsr up bra quit ; and leave all: movea.l sp,a5 ; COMPUTE LENGTH OF PROGRAM movea.l $4(a5),a5 ; address is $100 bytes below base page move.l $C(a5),d0 ; length of program area add.l $14(a5),d0 ; add length of initialized data area add.l $1C(a5),d0 ; add length of uninitialized data area add.l #$500,d0 ; reserve base page space move.l d0,d1 ; length of reserved area in d1 and d0 add.l a5,d1 ; $100 bytes below b.p. + program length and.l #$FFFFFFFE,d1 ; even out address movea.l d1,sp ; stack begins at top of program move.l d0,d6 ; save for later move.l d0,-(sp) ; length move.l a5,-(sp) ; start of area to reserve clr.w -(sp) move.w #$4A,-(sp) ; SETBLOCK trap #1 add.l #$C,sp move.l #$0,bufptr move.l #$0,byteso ; initialize number of bytes left to zero move.l $42E,a0 ; move.l a0,a1 ; add.l #$6,a1 ; clr.l d0 clr.l d1 clr.l d2 clr.l d3 move.w (a1)+,d0 ; d0 has root directory length in sectors move.w (a1)+,d1 ; d1 has fat length in sectors move.w (a1)+,d2 ; d2 has fat start (sector number) move.w (a1)+,d3 ; d3 has data area start (sector number) add.l #$200,a0 ; add 512 byte displacement to beginning of RD move.l a0,a1 ; lsl.l #$8,d2 ; d2 has fat start in bytes from RD beginning lsl.l #$1,d2 ; d2 has fat start in bytes from RD beginning add.l d2,a0 ; address of fat in a0 move.l a0,fatst ; fatst has address of fat start lsl.l #$8,d1 ; d1 has length of fat in bytes lsl.l #$1,d1 ; d1 has length of fat in bytes add.l d1,a0 ; a0 has root directory start in bytes from RD move.l a0,rootst ; rootst has start address of root dirctory lsl.l #$8,d3 lsl.l #$1,d3 add.l d3,a1 move.l a1,datast ; datast has start address of data area suba.l a5,a5 move.l $400(a5),event ; save event_timer vector move.l #start,$400(a5) ; set new event_timer vector move.l $42E,a0 movea.l a0,a1 add.l #21,a1 move.b (a1),d0 add.b #$41,d0 move.b d0,pfile ; set ramdisk drive id move.b d0,tlname ; set ramdisk drive id move.b d0,qfile ; set ramdisk drive id move.b d0,qufil ; set ramdisk drive id move.b #$6,d0 ; set "pqueue exists" bit * ; set "pqueue.tmp not busy" move.b d0,-(a0) ; set "spooler already there" move.l $B4(a5),trapv ; save trap 13 vector move.l #trap,$B4(a5) ; set new trap 13 vector move.w #$0,-(sp) ; create file spool.ttp move.l #qufil,-(sp) move.w #$3C,-(sp) trap #1 addq.l #$8,sp tst.w d0 bge N7 move.l #ccsp,d0 bsr up bra quit N7: move.w d0,quhand move.l #pend,d0 sub.l #pbeg,d0 ; d0 contains the bytes to move to spool.ttp move.l #pbeg,-(sp) ; write to file spool.ttp move.l d0,-(sp) move.w quhand,-(sp) move.w #$40,-(sp) trap #1 add.l #$C,sp tst.w d0 bge N8 move.l #cwsp,d0 bsr up bra quit N8: move.l supsav,-(sp) ; leave supervisor mode move.w #$20,-(sp) trap #1 addq.l #$6,sp move.l d6,-(sp) ; terminate with program resident move.w #$31,-(sp) trap #1 quit: move.l supsav,-(sp) ; leave supervisor mode move.w #$20,-(sp) trap #1 addq.l #$6,sp clr.l -(sp) ; If spooler there already then get out trap #1 up: move.l d0,-(sp) move.w #$9,-(sp) trap #1 addq.l #6,sp rts pbeg: dc.w $601A,$0000,$0424,$0000,$0000,$0000,$0000,$0000 dc.w $0000,$0000,$0000,$0000,$0000,$0000,$203C,$0000 dc.w $035D,$90BC,$0000,$0000,$D0BC,$0000,$0500,$2A6F dc.w $0004,$2F00,$2F0D,$3F3C,$0000,$3F3C,$004A,$4E41 dc.w $DFFC,$0000,$000C,$13FC,$0030,$0000,$0308,$42A7 dc.w $3F3C,$0020,$4E41,$5C8F,$23C0,$0000,$02FA,$2078 dc.w $042E,$2248,$D3FC,$0000,$0015,$1011,$D03C,$0041 dc.w $13C0,$0000,$0350,$13C0,$0000,$0340,$1020,$13C0 dc.w $0000,$033A,$08C0,$0000,$1080,$2F39,$0000,$02FA dc.w $3F3C,$0020,$4E41,$5C8F,$2F3C,$0000,$4000,$3F3C dc.w $0048,$4E41,$5C8F,$4A80,$6C00,$0010,$203C,$0000 dc.w $039D,$6100,$0252,$6000,$020C,$23C0,$0000,$02F6 dc.w $2055,$D1FC,$0000,$0081,$227C,$0000,$030A,$12D8 dc.w $0C10,$0020,$6EF8,$12BC,$0000,$3F3C,$0002,$2F3C dc.w $0000,$030A,$3F3C,$003D,$4E41,$508F,$4A40,$6E00 dc.w $0010,$203C,$0000,$0365,$6100,$020C,$6000,$01C6 dc.w $33C0,$0000,$02FE,$3F3C,$0002,$2F3C,$0000,$0340 dc.w $3F3C,$003D,$4E41,$508F,$4A40,$6B00,$000A,$5239 dc.w $0000,$0342,$60E0,$3F3C,$0000,$2F3C,$0000,$0340 dc.w $3F3C,$003C,$4E41,$508F,$4A40,$6C00,$0010,$203C dc.w $0000,$037D,$6100,$01C0,$6000,$016C,$33C0,$0000 dc.w $0300,$2F39,$0000,$02F6,$2F3C,$0000,$3F00,$3F39 dc.w $0000,$02FE,$3F3C,$003F,$4E41,$DFFC,$0000,$000C dc.w $4A80,$6C00,$0010,$203C,$0000,$03B2,$6100,$0188 dc.w $6000,$0126,$6700,$003C,$23C0,$0000,$0304,$2F39 dc.w $0000,$02F6,$2F39,$0000,$0304,$3F39,$0000,$0300 dc.w $3F3C,$0040,$4E41,$DFFC,$0000,$000C,$4A80,$6C00 dc.w $0010,$203C,$0000,$03CC,$6100,$014C,$6000,$00EA dc.w $6090,$3F3C,$0002,$2F3C,$0000,$0350,$3F3C,$003D dc.w $4E41,$508F,$4A40,$6C00,$0038,$3F3C,$0000,$2F3C dc.w $0000,$0350,$3F3C,$003C,$4E41,$508F,$4A40,$6E00 dc.w $0020,$1039,$0000,$033A,$0880,$0001,$13C0,$0000 dc.w $033A,$203C,$0000,$03EC,$6100,$00FC,$6000,$009A dc.w $33C0,$0000,$0302,$1039,$0000,$033A,$08C0,$0001 dc.w $13C0,$0000,$033A,$3F3C,$0002,$3F39,$0000,$0302 dc.w $2F3C,$0000,$0000,$3F3C,$0042,$4E41,$DFFC,$0000 dc.w $000A,$207C,$0000,$0342,$D1FC,$0000,$000C,$10FC dc.w $000D,$10BC,$000A,$2F3C,$0000,$0342,$2F3C,$0000 dc.w $000E,$3F39,$0000,$0302,$3F3C,$0040,$4E41,$DFFC dc.w $0000,$000C,$4A40,$6C00,$0010,$203C,$0000,$0407 dc.w $6100,$0084,$6000,$0022,$3F39,$0000,$0302,$3F3C dc.w $003E,$4E41,$588F,$3F39,$0000,$0300,$3F3C,$003E dc.w $4E41,$588F,$6000,$0010,$2F3C,$0000,$0340,$3F3C dc.w $0041,$4E41,$5C8F,$3F39,$0000,$02FE,$3F3C,$003E dc.w $4E41,$588F,$42A7,$3F3C,$0020,$4E41,$5C8F,$23C0 dc.w $0000,$02FA,$1039,$0000,$033A,$0880,$0000,$2078 dc.w $042E,$1220,$1080,$2F39,$0000,$02FA,$3F3C,$0020 dc.w $4E41,$5C8F,$2F39,$0000,$02F6,$3F3C,$0049,$4E41 dc.w $5C8F,$42A7,$4E41,$2F00,$3F3C,$0009,$4E41,$5C8F dc.w $4E75,$0000,$0000,$0000,$0000,$0000,$0000,$0000 dc.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000 dc.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000 dc.w $0000,$0000,$0000,$0000,$0000,$0000,$0000,$0000 dc.w $0000,$0000,$0000,$0000,$0000,$0000,$6D3A,$4158 dc.w $5858,$5854,$4D50,$2E50,$5254,$0000,$6D3A,$7071 dc.w $7565,$7565,$2E74,$6D70,$000D,$0A50,$5254,$3E20 dc.w $0053,$6F75,$7263,$6520,$6669,$6C65,$206E,$6F74 dc.w $2066,$6F75,$6E64,$0D0A,$0043,$6F75,$6C64,$206E dc.w $6F74,$2063,$7265,$6174,$6520,$3F58,$5858,$5854 dc.w $4D50,$2E50,$5254,$0D0A,$004F,$7574,$206F,$6620 dc.w $4D61,$696E,$204D,$656D,$6F72,$790D,$0A00,$4361 dc.w $6E6E,$6F74,$2072,$6561,$6420,$736F,$7572,$6365 dc.w $2066,$696C,$650D,$0A00,$4361,$6E6E,$6F74,$2077 dc.w $7269,$7465,$2064,$6573,$7469,$6E61,$7469,$6F6E dc.w $2066,$696C,$650D,$0A00,$4361,$6E6E,$6F74,$2063 dc.w $7265,$6174,$6520,$5051,$5545,$5545,$2E54,$4D50 dc.w $0D0A,$0043,$616E,$6E6F,$7420,$7772,$6974,$6520 dc.w $746F,$2050,$5155,$4555,$452E,$544D,$500D,$0A00 dc.w $0000,$0002,$0626,$1018,$0608,$0C22,$0E0E,$1614 dc.w $0E0A,$140C,$140E,$060C,$1812,$0606,$0618,$1418 dc.w $140A,$060E,$060A,$0A18,$140C,$180E,$0E12,$0E18 dc.w $0612,$0E00,$0000,$0000,$0000,$0000,$0000,$0000 pend: dc.w $0000 ******************************************************************************* *put1: move.w d4,-(sp) ; This section displays a character in d4 * * move.w #$2,-(sp) ; ( low byte ). Used for testing only. * * move.w #$3,-(sp) * * trap #13 * * addq.l #$6,sp * * * * rts * ******************************************************************************* END