         Title    Remote Terminal program for Jupiter II
*        This program is used to make a Jupiter II computer into...
*        a remote terminal, for use with a modem
*        A major assumption: the modem port is operating at a rate
*        much slower than the console, so the console can eat characters
*        just as fast as they arrive!
*        (this program intended for operation at 1200 baud on the modem)
*        Can't we do this whole thing with the activation set stuff?


consoleaciactl equ $FFC0
consoleaciadata equ $FFC1

modemaciactl equ   $FFC8
modemaciadata equ $FFC9

         org       $20                 page zero stuff

modeminterruptoff  $15                 modem control: selects no ints, 300 baud
modeminterruptok   $95                 modem control: allow interrupts
consoleinterruptoff $15                console control: select no interrupts
consoleinterruptok  $95                console control: allow interrupts

DataForDisplay     0                   <>0 --> character to give to display
OperatorKeytoSend  0                   <>0 --> next character to give to modem
XmitFileOpenFlag   0                   <>0 --> xmit file is open
XmitEnable         0                   <>0 --> ok to transmit
XmitBufferPointer  #XmitBuffer
XmitBufferEndPointer #XmitBuffer       Start Xmit buffer out empty

         org       $100                the normal place to start...

Wireloop ; wait for next activity request
         ldaa      modemaciactl        DCD loss ?
         bita      #$4
         beq       WireLoop0
         ldaa      modemaciadata       yes, read data register to ack DCD loss
WireLoop0
         ldaa      #1                  mask for "rcvr register full"
         bita      modemaciactl        data arriving from modem ?
         bne       ModemDataArrived    b/ yes, go handle
         bita      consoleaciactl      data arriving from keyboard ?
         bne       OperatorKeystroke   b/ yes, go service
         rola                          mask for "xmitter register empty"
         bita      modemaciactl        modem ready for another byte ?
         beq       Wireloop1           b/ nope, see if display is ready
         ldaa      OperatorKeytoSend   a keystroke available to send ?
         beq       WireLoop2           b/ no, see if Xmit file is enabled
         staa      Modemaciadata       yes, give to modem
         clr       OperatorKeytoSend   make keystroke go away
         bra       WireLoop1           and continue processing

WireLoop2 ; modem is ready, see if Xmit file is enabled
         tst       XmitEnable          is modem output frozen ?
         bne       GiveFileDatatoModem b/ no, try to give modem some work
WireLoop1 ; No data to give to modem
         ldaa      DataforDisplay      any data to give to display ?
         beq       WireLoop            b/ no, see if more data arrived
         ldab      #2                  = mask for "tx data register empty"
         bitb      consoleaciactl      display ready for another byte?
         beq       WireLoop            b/ no, loop again!
         clr       DataforDisplay      flag 'no data for display'
         staa      consoleaciadata     and give the data to the display
         bra       WireLoop            see if more work to do

ModemDataArrived ; Data byte has arrived from modem
         ldaa      Modemaciadata       get the byte
         anda      #$7F                mask off parity bit
         cmpa      #'S-$40             Control-S ?
         beq       HostSaysStopXmit    b/ yes, go service
         cmpa      #'Q-$40             Control-Q ?
         beq       HostSaysStartXmit   b/ yes, go service
         staa      DataforDisplay      save as next thing to give to display
         tst       LogFileOpenFlag     is logging active ?
         beq       WireLoop            b/ no, all done!
         tst       LogNowFlag          yes, is logging suspended ?
         beq       WireLoop            b/ yes, do no more...
         cmpa      #ascii:lf           ignore this guy ?
         beq       WireLoop            b/ ignore LF.
         ldx       LogBufferFillPtr    place character in log buffer
         staa      0,x
         inx
         stx       LogBufferFillPtr    update pointer
         cpx       #LogBufferEnd+1     is log buffer full ?
         bne       WireLoop            b/ no
         jsr       DumpLogBuffer       yes, dump to log file
         jmp       WireLoop

SpecialRequestJ
         jmp       SpecialRequest      b/ null --> operator wants something

OperatorKeyStroke ; Operator hit a key
         ldaa      consoleaciactl      read status
         bita      #%00010000          framing error?
         bne       SpecialRequestJ     b/ yes, operator wants to do something
         ldaa      consoleaciadata     get the keystroke
SendOperatorKey ; (A) = operator keystroke to send
         oraa      #$80                set parity bit to ensure non-zero char
         staa      OperatorKeytoSend   set up to send byte when convenient
         jmp       WireLoop

StopTransmission ; stop transmission of data to host
         tst       XmitfileOpenflag
         beq       SendOperatorKey
HostSaysStopXmit
         clr       XmitEnable
WireLoopJ3
         jmp       WireLoop

StartTransmission ; start transmission of file to host
         tst       XmitfileOpenflag
         beq       SendOperatorKey
HostSaysStartXmit
         sec
         rol       XmitEnable
         jmp       WireLoop

GiveFileDatatoModem ; give data from file to send to host
         ldx       Xmitbufferpointer
         cpx       XmitbufferEndPointer
         beq       GiveFileDataBufferEmpty
         ldaa      0,x                 get byte from buffer
         staa      modemaciadata       send to host
         inx                           bump xmit pointer
         stx       Xmitbufferpointer
         jmp       WireLoop

GiveFileDataBufferEmpty ; buffer is empty, get next buffer from disk
         ldaa      XmitFileOpenFlag
         beq       HostSaysStopXmit    b/ xmit file not open, don't send!
         ldx       #Xmitbuffer         = buffer base
         stx       Xmitbufferpointer
         ldx       #Readasmuchaspossible
         jsr       syscall$
         bcs       GiveFileDataError
GiveFileDataLastBuffer ; compute pointer past last byte to send
         ldaa      #Xmitbuffer/256
         ldab      #Xmitbuffer\256
         addb      Readasmuchaspossible+scblk:rplen+1
         adca      Readasmuchaspossible+scblk:rplen
         staa      XmitbufferendPointer
         stab      XmitbufferendPointer+1
         jmp       WireLoop

GiveFileDataError ; Error occurred while reading file data
         cpx       #Err:EofHit
         bne       CroakDie            a disaster! give up...
         ldx       Readasmuchaspossible+Scblk:rplen
         bne       GiveFileDataLastBuffer b/ there is still some more data...
         ldx       #CloseXmitFile
         jsr       DoSyscall
         clr       XmitFileOpenFlag    remember that no file is open
         bra       GiveFileDataLastBuffer

OpenFileforXmit ; operator wishes to send file to host
         tst       XmitFileOpenFlag
         bne       GiveErrortoDisplay  b/ already open, just yell!
         ldaa      consoleinterruptok  re-enable the console port
         staa      consoleaciactl
         ldx       #PrintXmitFileRequest
         jsr       DoSyscall
         ldx       #AskFileName
         jsr       DoSyscall
         ldaa      consoleinterruptoff disable the console port interrupts
         staa      consoleaciactl
         ldx       #OpenXmitFile
         jsr       DoSyscall
         inc       XmitFileOpenFlag    remember that Xmit file is open
         jmp       WireLoop

DoSyscall ; execute syscall and croak if error occurs
         jsr       syscall$
         bcs       CroakDie
         rts

Quit ; operator wants to stop!
         tst       LogFileOpenFlag     are we logging ?
         beq       Quit1               b/ no.
         jsr       DumpLogBuffer       yes, dump the last bufferful of text
Quit1    ldx       #0                  do normal exit
CroakDie ; error code in (X), give up!
         ldaa      consoleinterruptok  re-enable the console port
         staa      consoleaciactl
         ldaa      modeminterruptok
         staa      modemaciactl
         stx       ErrorExit+Scblk:params
         ldx       #ErrorExit
         jsr       Syscall$
         bcs       CroakDie
         bra       *                   ?????
         page
GiveErrortoDisplay
         ldaa      #ascii:bel          tell operator it goofed
         staa      DataforDisplay
         jmp       WireLoop

LogTransactions ; record received modem data in an SDOS file
         tst       LogFileOpenFlag     already logging ?
         bne       GiveErrortoDisplay  b/ yes, beep at operator
         ldaa      consoleinterruptok  re-enable the console port
         staa      consoleaciactl
         ldx       #PrintLogFileRequest
         jsr       DoSyscall
         ldx       #AskFileName
         jsr       DoSyscall
         ldaa      consoleinterruptoff disable the console port interrupts
         staa      consoleaciactl
         ldx       #CreateLogFile
         jsr       DoSyscall
         sec                           indicate that log file is now open
         rol       LogFileOpenFlag
RestartLogging ; Restart logging
         tst       LogFileOpenFlag     is log file open ?
         beq       LogSendOperatorKey  b/ no, send keystroke to host
         ldaa      #1
         staa      LogNowFlag          start logging now
         jmp       WireLoop

LogSendOperatorKey
         jmp       SendOperatorKey

SuspendLogging ; Stop logging temporarily
         tst       LogFileOpenFlag     is log file open ?
         beq       LogSendOperatorKey  b/ no, send keystroke to host
         clr       LogNowFlag
         bsr       DumpLogBuffer
         jmp       WireLoop

EndLogTransactions ; Stop transaction recording to file
         tst       LogFileOpenFlag     is log file open ?
         beq       LogSendOperatorKey  b/ no, send keystroke to host
         bsr       DumpLogBuffer
         ldx       #CloseLogFile
         jsr       DoSyscall
         clr       LogFileOpenFlag     no log file is open
         jmp       WireLoop

DumpLogBuffer ; Dump Log buffer to file
         ldx       #0                  set up 1 second max delay
         ldaa      #2                  wait for modem ready for another byte
DumpLogBuffer1l
         dex                           delay exhausted ?
         beq       DumpLogBuffer1      b/ yes, proceed anyway
         bita      modemaciactl
         beq       DumpLogBuffer1L
         ldaa      #'S-$40             send XOFF to host to handle long write
         staa      modemaciadata
DumpLogBuffer1
         ldaa      LogBufferFillPtr    compute # bytes to dump
         ldab      LogBufferFillPtr+1
         subb      #LogBuffer\256
         sbca      #LogBuffer/256
         staa      WriteLogBuffer+Scblk:wrlen
         stab      WriteLogBuffer+Scblk:wrlen+1
         ldx       #WriteLogBuffer
         jsr       DoSyscall
         ldx       #LogBuffer          yes, reset buffer pointer to start
         stx       LogBufferFillPtr
         ldx       #0                  set up 1 second max delay
         ldaa      #2                  wait for modem ready for another byte
DumpLogBuffer2l
         dex                           delay exhausted ?
         beq       DumpLogBuffer2      b/ yes, proceed anyway
         bita      modemaciactl
         beq       DumpLogBuffer2L
         ldaa      #'Q-$40             send XON to host to wake him up again
         staa      modemaciadata
DumpLogBuffer2
         rts
         page
ErrorExit ; system call to perform error exit
         syscall:errorexit,errorexit:sclen
         fdb       changed

PrintXmitFileRequest ; syscall to print file name request
         syscall:writea,writea:sclen
         0,ignored
         #PrintXmitFileString,#PrintXmitFileStringEnd-PrintXmitFileString

PrintXmitFileString fcc "Name of file to send to host? "
PrintXmitFIleStringEnd

AskFileName ; syscall to ask for filename from operator
         syscall:reada,reada:sclen
         0,1       linemode desired
         #ignored,#ignored
         #changed
         #FileNameBuffer,#FileNameBufferEnd-FileNameBuffer

OpenXmitFile ; system call to open xmit file
         syscall:open,open:sclen
         1,ignored
         #FileNameBuffer,#FileNameBufferEnd-FileNameBuffer
         #changed
         #Scratch,2

scratch  #changed  place to put file name length

Readasmuchaspossible ; syscall to fill the Xmit buffer
         syscall:readb,reada:sclen
         1,0       line-mode off!
         #ignored,#ignored
         #changed  amount of data read
         #Xmitbuffer,#Xmitbufferend-Xmitbuffer

CloseXmitFile ; system call to close xmit file
         syscall:close,close:sclen,1

LogFileOpenFlag    0                   <>0 --> log file is open
LogNowFLag         0                   <>0 --> logging not suspended

PrintLogFileRequest ; syscall to ask for rcv file name
         syscall:writea,writea:sclen
         0,ignored
         #PrintLogFileString,#PrintLogFileStringEnd-PrintLogFileString

PrintLogFileString fcc "Enter name of Log file: "
PrintLogFileStringEnd equ *

CreateLogFile ; system call to create log file
         syscall:Create,Create:sclen
         2,ignored
         #FileNameBuffer,#FileNameBufferEnd-FileNameBuffer
         #changed
         #Scratch,2

CloseLogFile ; system call to close log file
         syscall:Close,Close:Sclen,2

WriteLogBuffer ; syscall to write contents of Log buffer to Log file
         syscall:writea,writea:sclen
         2,ignored
         #Logbuffer,#changed

         Ifund     Debug
Debug    equ       1
         Fin

SpecialRequestBreakContinues ; FRAMING ERROR ("BREAK") is still active
         ldab      consoleaciadata     gobble garbage character, ack FRAMEERROR
;        bita      #%00010000          framing error?
;        bne       SpecialRequest      ignore character with framing error
SpecialRequest ; Operator pushed "BREAK", he wants to do something
; since ACIA will continue to give us TDRE+FRAMINGERROR for the duration
; of the BREAK, we sit around in this loop gobbling up everthing that appears
; up to 100 milliseconds after the last seen framing error; THEN we
; wait for the operator request character.
         ldx       #100*1000/(4+2+4+4+4) 100 mS. delay
SpecialRequestWaitBreakGone ; loop here to wait for Break request to be gone
         ldaa      consoleaciactl      (4~) read status
         bita      #%00000001          (2~) has a character arrived ?
         bne       SpecialRequestBreakContinues (4~) b/ yes, break still active
         dex                           (4~) down count 100mS delay
         bne       SpecialRequestWaitBreakGone (4~) b/ wait longer to be sure

         ldab      #(15*(1000/256)*(1000/256))/((4+4+4+2+4))                          set up for 15 second delay
SpecialRequestWaitLoop ; wait for operator keystroke
         dex                           (4~) down count delay
         bne       SpecialRequestWait1 (4~) b/ not expired
         decb
         beq       PrintMenu           b/ delay expired, go show menu
SpecialRequestWait1
         ldaa      consoleaciactl      (4~) new keystroke arrive ?
         bita      #1                  (2~) ...?
         beq       SpecialRequestWaitLoop (4~) b/ next keystroke not available yet
SpecialRequestArrived ; Operator has signalled his request
         bita      #%00010000          framing error?
         bne       SpecialRequest      ignore character with framing error
         ldaa      consoleaciadata     get key corresponding to desired fn
         anda      #$7f                mask off confusing parity bit
         cmpa      #'?                 is operator confused ?
         beq       PrintMenu           yes, go show him the menu
         cmpa      #$20                a printing character ?
         bcc       SendOperatorKeyJ    b/ yes, send it to remote host
         tab                           preserving (A)
         aslb                          convert to word index
         stab      OperatorKeyTablePtr+1 and branch on it
         ldx       OperatorKeyTablePtr
         ldx       OperatorKeyTable&$FF,x
         jmp       0,x

SendOperatorKeyJ Jmp  SendOperatorKey

SWITrap  ; place to go when ^D hit
         swi                           wake the debugger
         jmp       WireLoop            done with debugger, continue!

PrintMenuDone ; Menu is printed, now just wait for response
         ldaa      consoleaciactl      new keystroke arrive ?
         bita      #1                  mask to detect presence of next keystroke
         beq       PrintMenuDone       b/ next keystroke not available yet
         bra       SpecialRequestArrived

Wire     ldaa      #3                  reset the modem port
         staa      modemaciactl
         ldaa      modeminterruptoff    enable for use in poll mode
         staa      modemaciactl
         ldaa      consoleinterruptoff enable for use in poll mode
         staa      consoleaciactl      ditto for console
PrintMenu ; Print Menu of functions so people don't need ref document
         ldx       #MenuString         get pointer to menu
PrintMenuLoop ; Print a byte of menu
         ldab      #2                  "tx data register empty" mask
         bitb      consoleaciactl      ready for another byte ?
         beq       PrintMenuLoop       b/ no, wait some more
         ldaa      0,x                 fetch menu byte
         beq       PrintMenuDone       b/ end of menu, go wait for operator
         inx                           advance menu string pointer
         staa      consoleaciadata     send byte to console
         bra       PrintMenuLoop       and then go send another

MenuString ; Text to tell operator what special functions he has available
         fcb       $0D,$0A             CR,LF
         fcb       $0D,$0A             CR,LF
         fcc "*** Menu for Remote Terminal interface program, v1.0b. ***"
         fcb       $0D,$0A             CR,LF
         fcc "BREAK allows operator to enter any of the following keys:"
         fcb       $0D,$0A             CR,LF
         fcb       $0D,$0A             CR,LF
         fcc "?       Prints this menu."
         fcb       $0D,$0A             CR,LF
         fcc "^A      Exit back to SDOS."
         fcb       $0D,$0A             CR,LF
         fcc "^B      Open log file and begin logging."
         fcb       $0D,$0A             CR,LF
         fcc "^D      Wake up local debugger."
         fcb       $0D,$0A             CR,LF
         fcc "^E      Close log file."
         fcb       $0D,$0A             CR,LF
         fcc "^F      Specify file to be transmitted."
         fcb       $0D,$0A             CR,LF
         fcc "^Q      Unfreeze file transmission (must do after ^F)"
         fcb       $0D,$0A             CR,LF
         fcc "^S      Freeze transmission of file."
         fcb       $0D,$0A             CR,LF
         fcc "^[      Stop logging, but don't close log file."
         fcb       $0D,$0A             CR,LF
         fcc "^]      Continue logging."
         fcb       $0D,$0A             CR,LF
         fcc "any other key re-enables WIRE mode and is sent to host."
         fcb       $0D,$0A             CR,LF
         fcb  0       End of MenuString

OperatorKeyTablePtr fdb OperatorKeyTable pointer used to effect indexed jump
OperatorKeyTable ; pointers to routines to handle operator control keys
         fdb       SendOperatorKey     :0, Null
         fdb       Quit                :1, ^A -- Operator wants to exit
         fdb       LogTransactions     :2, ^B begin recording transactions
         fdb       SendOperatorKey     :3, ^C *** Always send to host ***
         If        Debug
         fdb       SwiTrap
         else
         fdb       SendOperatorKey     :4, ^D trigger debugger ?
         fin
         fdb       EndLogTransactions  :5, ^E end recording transactions
         fdb       OpenFileforXmit     :6, ^F send a file to host
         fdb       SendOperatorKey     :7, ^G
         fdb       SendOperatorKey     :8, ^H
         fdb       SendOperatorKey     :9, ^I
         fdb       SendOperatorKey     :A, ^J
         fdb       SendOperatorKey     :B, ^K
         fdb       SendOperatorKey     :C, ^L
         fdb       SendOperatorKey     :D, ^M
         fdb       SendOperatorKey     :E, ^N
         fdb       SendOperatorKey     :F, ^O
         fdb       SendOperatorKey     :10, ^P
         fdb       StartTransmission   :11, ^Q Restart file transmission
         fdb       SendOperatorKey     :12, ^R
         fdb       StopTransmission    :13, ^S Suspend file transmission
         fdb       SendOperatorKey     :14, ^T
         fdb       SendOperatorKey     :15, ^U
         fdb       SendOperatorKey     :16, ^V
         fdb       SendOperatorKey     :17, ^W
         fdb       SendOperatorKey     :18, ^X
         fdb       SendOperatorKey     :19, ^Y
         fdb       SendOperatorKey     :1A, ^Z
         fdb       SendOperatorKey     :1B, ESC
         fdb       RestartLogging      :1C, ^[
         fdb       SendOperatorKey     :1D, ^\
         fdb       SuspendLogging      :1E, ^]
         fdb       SendOperatorKey     :1F, ^_

FileNameBuffer rmb 132
FileNameBufferEnd equ *

LogBufferFillPtr fdb  LogBuffer
LogBuffer rmb 25000
LogBufferEnd equ *

XmitBuffer rmb 1000
XmitBufferEnd equ *

         End       Wire
