home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
trs80model4.zip
/
m4add.asm
next >
Wrap
Assembly Source File
|
1986-10-22
|
18KB
|
583 lines
; m4add/asm
;
; Input a line from a take file
;
DOTAKE EQU $
LD HL,CMDBUF ;WHERE TO PUT THE TEXT
LD B,0 ;CHARACTER COUNTER
CALL CONIN
IFA 128,TAKERR ;Jump if break was pressed
TAKLOP LD DE,TFCB ;FCB FOR TAKE FILE
CALL XGET ;Call @GET svc
JR NZ,TAKERR ;QUIT ON AN ERROR
LD (HL),A ;SAVE THE CHARACTER
INC HL ;POINT TO THE NEXT
INC B ;ADD TO CHARACTER COUNT
IFA CR,TAK020 ;Go if at the end of string
LD C,A
LD A,(TAKLOG) ;IS DISPLAY OF TAKE FILES ON?
OR A
LD A,C
CALL NZ,CONOUT
JR TAKLOP
TAK020 RET ;RETURN WITH THE STRING
TAKERR LD DE,TFCB ;GET READY TO CLOSE THE FILE
CALL XCLOSE ;Close the file
XOR A ;TURN THE TAKE FLAG OFF
LD (TAKFLG),A ;RESET IT
LD A,29
CALL CONOUT
SCF ;SET CARRY FOR A PARSE ERROR
RET
;
; This routine is used by the command parser routines to input a
; string from the keyboard. It recognizes the TAKFLG which tells
; it to input a string from the active take file.
; Input to the routine is HL which points to the string buffer,
; and B which has an initial count of the number of characters
; already in the buffer.
;
GETSTR LD A,(TAKFLG) ;Is there a TAKE file active
OR A
JP NZ,DOTAKE ;If so than go fill buffer from file.
LD IY,(FLAGS) ;Get the flags address.
BIT 5,(IY+18) ;Check if JCL active.
JR Z,SKIP_JCL ;Jump if JCL not active.
LD HL,CMDBUF ;Get the input buffer.
LD BC,5000H ;Set B to max count, and C to zero.
JP XKEYIN ;Get a line from JCL and return.
;
SKIP_JCL PUSH HL ;Save the registers
PUSH DE
LD A,B ;Save the number of character already in
LD (TEMP4),A ;the buffer for later
GET010 CALL XKEY ;Get a character from the keyboard
JP NZ,GETEXT ;Humm, what do we do on an error
IFANOT 8,GET030 ;Go if not backspace
LD E,A ;Save the KEY we got
LD A,B ;Get the number of characters
IFZ GET010 ;Go if no place to backspace over
DEC B ;At least one space left
DEC HL ;Back up the buffer pointer
LD A,E ;Get the character
CALL CONOUT ;Display it
JR GET010 ;Go get a new key
GET030 IFA CR,GET040 ;Go if enter pressed
IFANOT 24,GET036 ;Jump if not SHIFT <-
GET035 LD A,B ;NUMBER LEFT
IFZ GET010 ;Go if all were erased
LD A,8 ;BACKSPACE
CALL CONOUT ;PRINT IT
DEC B ;ONE MORE LEFT
DEC HL ;BACKUP IN THE BUFFER
JR GET035 ;KEEP LOOPING
GET036 IFA ESC,GET040 ;Go if it was escape
IFANOT 129,GET038 ;Go if not F1 (Also escape)
LD A,ESC ;GET AN ESCAPE
JR GET040 ;JOIN THE CODE FOR EXIT
GET038 IFA 128,GET040 ;Go it it was a break
IFALT 20H,GET010 ;Ignore other control keys
GET040 INC B ;ONE LESS CHARACTER LEFT
LD (HL),A ;PUT THE CHARACTER IN THE BUFFER
JR NZ,GET050 ;WHEN nZ IS SET, NO MORE CHARS LEFT
DEC B ;SAY WE STILL HAVE ONE
JR GET060 ;MIGHT BE '?' WHICH IS IGNORED AS LAST
GET050 IFA CR,GETEXT ;Finish up if CR pressed
IFA ESC,GETEXT ;Same for escape
IFA 128,GETEXT ;Same for break
INC HL ;ONE MORE IN THE BUFFER
CALL CONOUT ;PRINT WHAT WAS TYPED
GET060 CP '?' ;WAS IT A '?' ?
JP NZ,GET010 ;GET A NEW KEY IF NOT
LD A,8 ;GET A BACKSPACE
CALL CONOUT ;PRINT IT TO REMOVE THE '?'
DEC HL ;POINT AT THE '?'
PUSH HL ;HL -> IY
POP IY
LD A,' ' ;LOOK FOR A PREVIOUS SPACE
IFA (IY-1),GETEXT ;Look for a previous space
LD A,B ;ARE WE AT THE START OF THE LINE
DEC A
;IF Z IS SET THAN THE CURSOR IS AT HOME ON THE LINE. NO SPACE WILL
;BE ADDED IN THIS CASE.
LD A,' ' ;PUT A SPACE ON THE DISPLAY
CALL NZ,CONOUT ;PRINT IT
GETEXT LD A,B ;GET NUMBER ON THE LINE
DEC A
JR NZ,GET110 ;AT LEAST ONE CHAR ON THE LINE
PUSH AF ;PUT AF INTO BC
POP BC
LD A,(HL) ;WAS '?' THE TERMINATOR?
CP '?'
LD A,B ;GET THE LENGTH IN A
JR NZ,GET120
GET110 INC A ;COUNT THE TERMINATOR IF IT IS '?'
GET120 LD B,A ;SAVE THE LENGTH
LD A,(HL) ;GET THE TERMINATOR
IFA ESC,GET130 ;Skip normal return if escape
SLA A ;CHECK FOR BREAK (128 IS NEGATIVE)
JR C,GET130 ;ALSO GIVES C STATUS LIKE @KEYIN
SRL A ;FIX IT BACK
OR 10H ;MAKE CR, CHR$(29), '?' IS UNDISTURBED
CALL CONOUT ;PRINT THE CHARACTER
GET130 POP DE ;RESTORE THE REGS
POP HL
RET ;RETURN TO THE CALLER
;
STDOUT LD A,16 ;REVERSE VIDEO ON
JP CONOUT ;USE THE RETURN ON THE STACK
STDEND LD A,17 ;REVERSE VIDEO OFF
JP CONOUT ;USE THE RETURN ON THE STACK
;
;ENTRY POINT FOR INTERUPT DRIVEN RECEIVE CHARACTER ROUTINE.
;
GETINT JR NZ,GTIN10 ;NO CHARACTER AVAILABLE
LD HL,(CURCHR) ;CURRENT POS IN BUFFER
LD (HL),C ;CHARACTER IS IN C, STORE IT
INC L ;POINT TO NEXT WRITE POSITION
LD A,(NXTCHR) ;CHECK FOR OVERFLOW
IFA L,GTIN10
LD (CURCHR),HL ;SAVE ADDRESS OF NEXT STORE
LD A,(MAXCNT)
LD E,A
LD HL,INCNT ;Get the address of the count
INC (HL) ;Add one to the count
LD A,(HL) ;Get the count
IFALT E,GTIN10 ;Jump if below count
LD A,(FLOFLG) ;ARE WE DOING FLOW CONTROL?
IFZ GTIN10 ;Go if not doing flow control
LD A,C ;GET THE CHARACTER JUST RECEIVED
IFA XOFF,GTIN06 ;Jump if XOFF received
IFANOT XON,GTIN07 ;Jump if not time for XON
XOR A
GTIN06 LD (XOFREC),A ;Store the new flag value
GTIN07 LD A,(XFFLG) ;SEE IF WE ALREADY SENT IT
IFNZ GTIN10 ;Go if XOFF already sent
INC A ;MAKE IT NON-ZERO
LD (XFFLG),A ;SET IT
LD E,XOFF ;Get an XOFF character
CALL OUTCHR ;SENT IT OUT
GTIN10 RET
;
;LOG THE CHARACTER IN THE LOG FILE
;
LOGIT PUSH AF ;Save the character to log
LD A,(LOGFLG) ;Check if logging active
IFANOT 2,LOGITO ;Go if logging not on, or file not open
POP AF ;Get the character back
PUSH AF ;Put the character back
IFANOT LF,LOGT0 ;Is it LINE FEED? Jump if not
LD A,(PREVLCHR) ;Check if previous character was <CR>
IFA CR,LOGITO ;Throw out <LF> if previous was <CR>
LD A,LF ;Get a line feed character back
LOGT0 PUSH DE ;Save the registers
LD (PREVLCHR),A ;Save the previous character
LD DE,LFCB ;Get the FCB address
LOGT1 CALL XPUT ;@PUT the character to the file
JR Z,LOGEXT ;Exit if no errors on output
LOGT2 CALL XERROR0 ;Print the system message given in A
LD DE,LFCB ;Get the FCB back
CALL XCLOSE ;Make sure that the file is closed
STROUT ERMS20 ;Say that the file was closed
XOR A ;Reset the logging flag
LD (LOGFLG),A ;Store the new value
LOGEXT POP DE ;Restore DE
LOGITO POP AF ;Restore the character
RET ;Back to caller
;
; These are the TRSDOS 6.x SVC definitions
;
XPARAM DOSVC 17 ;@PARAM SVC (Parse ()'d argument list).
XFSPEC DOSVC 78 ;@FSPEC SVC (Convert filename to TRSDOS)
XINIT DOSVC 58 ;@INIT SVC (Create a file on disk device)
XOPEN DOSVC 59 ;@OPEN SVC (Open a file)
XREAD DOSVC 67 ;@READ SVC (Read a record)
XSKIP DOSVC 72 ;@SKIP SVC (Skip a logical record)
XCLOSE DOSVC 60 ;@CLOSE SVC (Close a file)
XVER DOSVC 73 ;@VER SVC (Write with verification)
XCTL DOSVC 5 ;@CTL SVC, perform control operation
XPRT PUSH BC ;Send character to printer
PUSH AF
LD C,A
LD A,6
JR SVCSAV
XVDCTL DOSVC 15 ;@VDCTL SVC (Perform video operations)
XGET DOSVC 3 ;@GET SVC (Get a character from a file)
;
; SVC'S that require the character passed in A to be in C should
; look like this.
;
XPUT PUSH BC ;SAVE BC
PUSH AF ;SAVE AF
LD C,A
LD A,4 ;@PUT SVC
SVCSAV RST 28H ;CALL AN SVC
POP BC ;POPPING BC DOES NOT AFFECT THE FLAGS
LD (XERRNO),A ;Store last error number
LD A,B ;B HAS OLD A SO RESTORE
POP BC ;GET OLD BC
RET ;RETURN TO THE CALLER
XKEYIN DOSVC 9 ;Get a string from *DO (@KEYIN SVC)
XHIGH DOSVC 100 ;@HIGH$ SVC
XLOAD DOSVC 76 ;@LOAD SVC (Load an executable)
XCHKDRV DOSVC 33 ;@CHKDRV SVC (Check if drive is ready)
XTIME DOSVC 19 ;@TIME SVC (Get current system time)
XADTSK DOSVC 29 ;@ADTSK SVC (Add a task to the table)
XCKTSK DOSVC 28 ;@CKTSK SVC (Check if task slot available)
XRMTSK DOSVC 30 ;@RMTSK SVC (Remove task from table)
XKLTSK DOSVC 32 ;@KLTSK SVC (Kill current exec task)
XPAUSE DOSVC 16 ;@PAUSE SVC (Delay for count in BC)
XFLAGS DOSVC 101 ;@FLAGS SVC (Get system flags address)
XREMOVE DOSVC 57 ;@REMOV SVC (Remove opened file)
XKILL EQU XREMOVE
XDECHEX DOSVC 96 ;@DECHEX SVC (Convert ASCII to binary)
XHEXDEC DOSVC 97 ;@HEXDEC SVC (Convert binary to ASCII)
XKBD DOSVC 8 ;@KBD SVC (Get a keyboard char, no wait)
XKEY DOSVC 1 ;@KEY SVC (Get a keyboard char, wait)
XCMNDR DOSVC 25 ;@CMNDR SVC (Execute system command)
XMUL16 DOSVC 91 ;@MUL16 SVC (Multiply 8 bit by 16 bi&t)
XDSP PUSH BC ;SAVE BC
PUSH AF ;SAVE AF
LD C,A
LD A,2 ;@DSP SVC
JP SVCSAV
XGTDCB DOSVC 82 ;@GTDCB SVC (Get the DCB for given device)
XEXIT LD L,A ;A HAS OUR RETURN CODE
LD H,0 ;MAKE HL THE RETURN CODE
DOSVC 22 ;Do @EXIT SVC
XERROR0 LD A,0 ;Get the last error number
XERRNO EQU $-1
XERROR PUSH BC ;SAVE BC AND AF
PUSH AF
LD IY,(FLAGS) ;GET @FLAGS ADDRESS
RES 6,(IY+2) ;RESET ALL SPECIAL BITS
RES 7,(IY+2)
RES 6,(IY+18)
OR 0C0H ;MAKE SIMPLE MESSAGES
LD C,A ;C NEEDS THE ERROR NUMBER
CALL NEWLIN ;Get a new line for the messages
LD A,26 ;@ERROR SVC (ISSUE A SYSTEM MESSAGE)
JP SVCSAV ;GO CALL IT
;
; Set up interrupt and device information
;
SETINT EQU $
;
; This code sets up the interrupt driven receiver. It calls @CTL
; to pass the address of the "GETINT" routine, which is called
; each time a character is received by the UART
;
LD IY,GETINT ;Where to transfer control to
LD DE,(CLDCB) ;Which device to get
LD C,4 ;Ctl function 4
CALL XCTL ;Do @CTL
;
; Install *FO and *FI devices
;
LD DE,0 ;Find a new DCB for this device
CALL XGTDCB
JR NZ,NODCB ;Abort if an error occured
LD (FOTDCB),HL ;Save the pointer to the DCB
PUSH HL ;Move it to IX to do indexing
POP IX
LD (IX+6),'F' ;Store the name in the DCB
LD (IX+7),'O'
LD A,2 ;Select @PUT capabilities only
LD (IX),A
LD HL,FILOUT ;Get the entry point
LD (IX+1),L ;Store it
LD (IX+2),H
LD DE,0 ;Get a new DCB for the input device
CALL XGTDCB
JR NZ,NODCB ;Abort if an error occurs
LD (FINDCB),HL ;Save the address of the DCB
PUSH HL ;Put the address in IX for indexing
POP IX
LD (IX+6),'F' ;Store the device name in the DCB
LD (IX+7),'I'
LD A,1 ;Select @GET capabilites only
LD (IX),A
LD HL,FILIN ;Get the entry point
LD (IX+1),L ;Store the address
LD (IX+2),H
RET ;Done!
;
; Print no DCB's available message
;
NODCB STROUT NONDCB ;Print NO NEW DCB's message
JP EXIT1 ;Exit from KERMIT
;
; Get a new line to print on
;
NEWLIN PUSH AF ;Save the acumulator
LD A,CR ;Get a <CR><LF> equivilent
CALL CONOUT ;Output it to the screen
POP AF ;Restore A
RET ;Return to caller
;
; Set the default disk drive command
;
SETDSK EQU $
LD A,CMNUM ;PARSE A NUMBER
CALL COMND
JP KERMT3
OR A ;If no number than say bad command
JP Z,KERMT2
LD A,D ;Check for number above 255
IFNZ SETDRV_2 ;Jump if number bigger than 255
LD C,E ;Get the lower order byte
CALL XCHKDRV ;Check the disk drive
JR NZ,SETDRV_2 ;Jump if TRSDOS doesn't like it
PUSH BC ;Save the binary number
JR NC,SETDRV_1 ;If carry set the drive is write protected
STROUT WRTPROT ;Say drive is write protected
SETDRV_1 STROUT DSKOK ;Say default drive changed
POP BC ;Get the drive number back
LD A,C ;Get the binary value into a
ADD A,48 ;Make it and ASCII digit
LD (DEFDSK),A ;Save it as the new default
JP KERMIT ;Get a new command
;
; Print bad drive message
;
SETDRV_2 LD A,32 ;Get the illegal drive message number
CALL XERROR ;Have TRSDOS print the message
JP KERMIT ;Go get a new command
;
; Clear any buffered characters from input buffer
;
CLRPRT PUSH AF
LD A,(NXTCHR) ;Set the next, and current positions
LD (CURCHR),A ;within the buffer to be the same
XOR A ;Set buffered character count to zero
LD (INCNT),A
LD (XOFREC),A ;Reset XOFF'd state
LD (XFFLG),A ;Haven't sent XOFF either
POP AF
RET
;
; Display the directory
;
;This code makes an educated guess as to whether it is running on a
;6.1 or a 6.2 TRSDOS machine. This is to determine if we should
;issue the DIR command for 6.1 or the CAT command for 6.2
;
DIR EQU $
LD A,CMTXT ;Get text for DIR/CAT command
LD DE,DIRBUF
CALL COMND
JP KERMT3
IFNZ DIR1C ;Jump if some text given
DIR1B LD A,(DEFDSK) ;Otherwise, use default drive
LD (DIRBUF),A ;Put in the text
LD A,CR ;Put in the terminator
LD (DIRBUF+1),A
DIR1C CALL NEWLIN ;Get new lines
CALL NEWLIN
LD IY,(FLAGS) ;Check if using 6.1, or 6.2 TRSDOS
LD A,(IY+27) ;Get the version number
AND 0FH ;KEEP JUST THE X OF 6.X
LD HL,DO62DR ;Get 6.2 command (CAT)
IFA 2,DIR1E ;Jump if 6.2
LD HL,DO61DR ;Get 6.1 command (DIR)
DIR1E LD DE,DIRCMD ;Where to put the text
LD BC,4 ;How much to move
PUSH DE ;Save the start
LDIR ;Move the command
POP HL ;Restore the start
CALL XCMNDR ;Let TRSDOS do it for us
DIR1D JP KERMIT ;Get a new command
;
; Erase a file from the system
;
ERA EQU $
LD A,CMTXT ;Get some text
LD DE,MFNBUF
PUSH DE ;Save the buffer address
CALL COMND
JP KERMT3
IFZ ERA3 ;Jump if no text given
CALL NEWLIN ;Get a newline
POP HL ;Get the address back
LD (MFNPTR),HL
XOR A
LD (LOGNAMES),A ;Reset the log names flag
CALL BUILDF ;Do wild carding
LD A,(FILECNT) ;Check if anything found
IFZ ERA3
LD (MFNPTR),HL ;Save the start of the list
ERA2 CALL MFNAME ;Get one filename from the buffer
JR C,ERA3 ;Jump if no more
LD DE,MFREQ ;Get the name
LD B,0 ;Select 256 LRL
LD HL,DATA ;Get the address of a temp buffer
CALL XOPEN ;Try to open it
JR Z,ERA1 ;Jump if opened
CP 42 ;Was it an LRL error message?
JR Z,ERA1 ;Jump if it was
ERA0 CALL XERROR0 ;Issue the message indicated in A
JR ERA3 ;Get a new command
;
; Print the removing message, then remove it
;
ERA1 LD HL,FCB ;Convert ETX to EOS in FCB
LD BC,32 ;Max distance
LD A,3 ;What to find
CPIR ;Look
DEC HL ;Ignore errors
LD (HL),EOS ;Change it
STROUT REMOVSTR ;Output 'Removing: ' message
STROUT FCB ;Output the filename
CALL NEWLIN ;Get a newline
LD DE,MFREQ ;Get the FCB
CALL XKILL ;Remove the file
JR Z,ERA2 ;Abort if error occurs
JR ERA0 ;Get the next one
;
ERA3 JP KERMIT ;Get a new command
;
; This routine sets up KERMIT/INI as initialization. The trick
; is to just make it look like the user typed 'TAKE KERMIT/INI'.
;
CHKTAK CALL XFLAGS ;Get the flags area into IY
LD A,(IY+10) ;Get the KFLAG$
AND 0F8H ;Reset the <ENTER>, <BREAK> and
LD (IY+10),A ;the <PAUSE> bits, and store it
LD B,46 ;Pause for a good deal of time
CALL XPAUSE
LD A,(IY+10) ;Get the KFLAG$ mask back
AND 7 ;Any of the keys pressed?
RET NZ ;Stop if BREAK, ENTER, or PAUSE
LD DE,TFCB ;LOAD THE FCB ADDRESS
LD HL,TKNAME ;NAME OF THE FILE
LD BC,TKLEN ;LENGTH OF THE STRING
LDIR
LD DE,TFCB ;GET IT AGAIN
LD HL,TBUF ;GET THE INPUT BUFFER
LD B,0 ;256 BYTE LRL
CALL XOPEN ;OPEN THE FILE
JR Z,CHKT_1 ;Jump if everything OK
CP 42
RET NZ ;Return if NOT LRL error
CHKT_1 LD A,1 ;SET THE TAKE COMMAND ACTIVE FLAG
LD (TAKFLG),A
RET ;GO DO IT.
;
; Print ESCAPE character sequence as CONTROL-?
;
ESCPR LD A,(ESCCHR) ;GET THE ESCAPE CHAR.
ESCPR1 IFAGE ' ',ESCPR2 ;IS IT A CONTROL CHAR?
PUSH AF
STROUT INMS10 ;Output CONTROL-
POP AF ;GET THE CHAR BACK
OR 100O ;DE-CONTROLIFY.
ESCPR2 CALL CONOUT
RET
;
; Swap screens between connect mode, and KERMIT command modes
;
SWAPIN LD A,15 ;cursor off
CALL CONOUT ;Tell *DO to turn it off
LD HL,2600H ;Get the destination
LD B,6
CALL XVDCTL ;Move screen image to temp space
LD HL,SWTBUF ;Move the buffer to the screen
LD B,5
CALL XVDCTL ;Move it
LD HL,2600H ;Now move temp buffer to swap buffer
LD DE,SWTBUF
LD BC,1920
LDIR ;Move it
LD A,14 ;cursor back on
CALL CONOUT
RET ;Return to the caller
;
; Put connect command screen in view
;
SCRCON EQU $
CALL SWAPIN ;Swap screens
LD B,4 ;Select get cursor position option
CALL XVDCTL ;SVC @VDCTL to get position
LD (CMDCRS),HL ;Save the cursor
LD HL,(CONCRS) ;Get the connect cursor postion
LD B,3 ;Select SET cursor position
CALL XVDCTL ;SVC @VDCTL to set it
LD A,14 ;Make sure that the cursor is ON
CALL CONOUT
RET ;Return to caller
;
; Swap in Kermit command screen
;
SCRCMD EQU $
CALL SWAPIN ;Swap screens
LD B,4 ;Get the current cursor position
CALL XVDCTL ;SVC @VDCTL to get it
LD (CONCRS),HL ;Save it
LD HL,(CMDCRS) ;Get the Kermit command cursor position
LD B,3 ;Set the current cursor position
CALL XVDCTL ;SVC @VDCTL to set it
LD A,14 ;Make sure the cursor is on
CALL CONOUT
RET ;Return
;
; *FI Device driver is here
;
; Valid memory module headers are used here, but are not necessary
; since the drivers are not in the High or Low memory chain of
; modules.
;
FILIN JR INPFIL ;Jump to code
DW ENDIN ;End of module
DB 4 ;Module name length
DB 'INP$'
FINDCB DW 0 ;Pointer to our DCB
DW 0
PRECHR DB 0 ;Previous character received
INPFIL CALL INPORT ;Get an input character
JR Z,INP04 ;Jump if successful
CALL CONIN ;Failed, read the keyboard
IFZ INPFIL ;Jump if no key pressed
IFANOT 128,INP03 ;Jump if not break
LD A,28 ;Return at EOF indicator
OR A ;Set NZ status
RET ;return to caller
INP03 LD E,A ;Get the keyboard character
CALL OUTCHR ;Output it to the port
JR INPFIL ;Read the port again
INP04 LD C,A ;Get the input character
LD A,(FLOFLG) ;Are we doing flow control?
IFNZ INP05 ;Jump if not doing flow control
LD A,C ;Get the character back
IFA XON,INPFIL ;Ignore XON if sent
IFA XOFF,INPFIL ;Ignore that also
INP05 LD A,(PRECHR) ;Get the previous character
CP CR ;Was it CR?
LD A,C ;Get the current character
JR NZ,INP09 ;Jump if not CR
LD A,(FILTYPE) ;Check if should convert
OR A ;Is it binary mode?
LD A,C
JR NZ,INP09 ;It is, so do not translate
CP LF ;Is the current character LF?
LD (PRECHR),A ;Store new previous
JR Z,INPFIL ;Ignore LF after CR
INP09 CP A ;Set Z status
RET ;Return byte to caller in A
ENDIN EQU $
;
; *FO Device driver is here
;
FILOUT JR FIOUT ;Jump to code
DW OUTEND-1 ;Normal header
DB 4
DB 'OUT$'
FOTDCB DW 0
DW 0
FIOUT LD E,C ;Get the character to send
FIOUT1 CALL OUTCHR ;Output it. We will hang till output
LD A,C ;Get the character sent
CP A ;Set the Z flag
RET ;Return the byte sent out, and Z status
OUTEND EQU $
; end of file