; PDP11 Tube Parasite Code
; ========================
; Copyright (C)1989 J.G.Harston


; Memory
; ======
LOMEM	EQU	1		; Lowest availale memory address DIV 256
HIMEM	EQU	255		; Highest available memory address DIV 256


; Set TUBEIO to address of Tube ULA
; =================================
TUBEIO	EQU	0		; Address of base of Tube I/O registers

TUBE1	EQU	TUBEIO+1	; Tube Register 1
TUBE2	EQU	TUBEIO+3	; Tube Register 2
TUBE3	EQU	TUBEIO+5	; Tube Register 3
TUBE4	EQU	TUBEIO+7	; Tube Register 4
TUBE1S	EQU	TUBEIO+0	; Tube Status 1
TUBE2S	EQU	TUBEIO+2	; Tube Status 2
TUBE3S	EQU	TUBEIO+4	; Tube Status 3
TUBE4S	EQU	TUBEIO+6	; Tube Status 4


; OSWRCH - Send character in R0 to Tube Register 1
; ================================================
WRCH:
MOV  R0,-(SP)			; Save R0
SEND_R1LP:
MOVB @TUBE1S,R0		; Read Tube R1 status
BIC  #&BF,R0			; Check b6 of status
BEQ  SEND_R1LP			; Loop until b6 set
MOV  (SP)+,R0			; Get R0 back
MOV  R0,@TUBE1			; Send byte to Tube R1
RTS  PC


; Send text string at R1 to Tube Register 1
; =========================================
SEND_TXT:
MOV  (R1)+,R0			; Get byte from R1, increment R1
JSR  PC,WRCH			; Send to WRCH via Tube R1
CMP  R0,#0			; Test current character
BNE  SEND_TXT			; Loop until &00 sent
RTS  PC


; Send byte in R0 to Tube Register 2
; ==================================
SEND_R2:
MOV  R0,-(SP)			; Save R0
SEND_R2LP:
MOVB @TUBE2S,R0		; Read Tube R2 status
BIC  #&BF,R0			; Check b6 of status
BEQ  SEND_R2LP			; Loop until b6 set
MOV  (SP)+,R0			; Get R0 back
MOV  R0,@TUBE2			; Send byte to Tube R2
RTS  PC


; Send <cr>-string at R1 to Tube Register 2
; =========================================
SEND_STR:
MOV  (R1)+,R0			; Get byte from R1, increment R1
JSR  PC,SEND_R2		; Send byte via Tube R2
CMP  R0,#13			; Test current character
BNE  SEND_STR			; Loop until <cr> sent
RTS  PC


; Send block at R1 to Tube Register 2
; ===================================
SEND_BLK:
ADD  R1,R2			; Add length of control block to R1
SEND_LP
MOV  -(R1),R0			; Decrement R1, get byte from R1
JSR  PC,SEND_R2		; Send byte via Tube R2
SUB  #1,R2			; Decrement count of bytes to send
BNE  SEND_BLK			; Loop until all bytes sent
RTS  PC


; OSRDCH - Wait for character from input stream
; =============================================
; Tube data: &00  --  Carry Char
;
; On exit: R0=char, Cy=carry
;
RDCH:
MOV  #0,R0
JSR  PC,SEND_R2		; Send command &00 - OSRDCH
GET_CHAR:
JSR  GET_R2			; Get returned byte
ADD  #&FF80,R0			; Copy b7 into carry
				; Continue to fetch byte from Tube R2

GET_R2:
MOVB @TUBE2S,R0		; Read Tube R2 status
BIC  #&7F,R0			; Check b7 of status
BEQ  GET_R2			; Loop until b7 set
MOVB @TUBE2,R0			; Get byte from Tube R2
RTS  PC


; Wait for block at R1 from Tube Register 2
; =========================================
GET_BLK:
ADD  R1,R2			; Add length of control block to R1
GET_LP
JSR  PC,GET_R2			; Wait for byte via Tube R2
MOV  R0,-(R1),R0		; Decrement R1, store byte to R1
SUB  #1,R2			; Decrement count of bytes to send
BNE  GET_LP			; Loop until all bytes received
RTS  PC


; STARTUP
; =======
; Tube data: via R1: string &00  --  via R2: &7F or &80
;
BANNER:
EQUB 10
EQUS "Acorn TUBE PDP-11 64K"
EQUB 10
EQUB 10
EQUB 13
EQUB 0
STARTUP:
MOV  BANNER,R1			; Point to startup banner
JSR  PC,SEND_TXT		; Print it via Tube WRCH protocol
BRA  CLI_WAIT			; Wait for result byte


; OSCLI - Send command line to host
; =================================
; Tube data: &02 string &0D  --  &7F or &80
;
; On entry: R1=>command string
;
CLI:
MOV  #2,R0
JSR  PC,SEND_R2		; Send command &02 - OSCLI
JSR  PC,SEND_STR		; Send string at R1
CLI_WAIT:
JSR  PC,GET_R2			; Wait for result via Tube R2
CMP  R0,#&80			; Check return code
BNE  CLI_DONE			; Nothing to execute, return
; call program
CLI_DONE:
RTS PC


; OSBYTE
; ======
; Tube data: &04 X A    --  X
;            &06 X Y A  --  Cy Y X
;
; On entry: R0,R1,R2=OSBYTE parameters
; On exit:  R0 preserved
;           If R0<&80, R1=returned value
;           If R0>&7F, R1, R2, Carry=returned values
;
BYTE:
CMP  R0,#&80
BCC  BYTE_HI
MOV  R0,-(SP)			; Save R0
MOV  #4,R0
JSR  PC,SEND_R2		; Send command &04 - Short BYTE
MOV  R1,R0
JSR  PC,SEND_R2		; Send second parameter
MOV  (SP),R0			; Get first parameter from top of stack
JSR  PC,SEND_R2		; Send first parameter
JSR  PC,GET_R2			; Wait for response
MOV  R0,R1			; Pass to R1
MOV  (SP)+,R0			; Restore R0
RST  PC

; OSBYTE >&7F
; -----------
BYTE_HI:
CMP  R0,#&82
BEQ  MEM82			; Fetch address high word
CMP  R0,#&83
BEQ  MEM83			; Fetch low memory limit
CMP  R0,#&84
BEQ  MEM84			; Fetch high memory limit
MOV  R0,-(SP)			; Save R0
MOV  #6,R0
JSR  PC,SEND_R2		; Send command &06 - Long BYTE
MOV  R2,R0
JSR  PC,SEND_R2		; Send third parameter
MOV  R1,R0
JSR  PC,SEND_R2		; Send second parameter
MOV  (SP),R0			; Get first parameter from top of stack
JSR  PC,SEND_R2		; Send first parameter
CMP  R0,#&9D			; Was it Fast BPUT?
BEQ  BYTE_DONE			; Don't wait for respsonse
JSR  PC,GET_R2			; Wait for response
ADD  R0,#&FF80			; Copy b7 into Carry
JSR  PC,GET_R2			; Wait for response
MOV  R0,R2			; Pass to R2
JSR  PC,GET_R2			; Wait for response
MOV  R0,R1			; Pass to R1
BYTE_DONE:
MOV  (SP)+,R0			; Restore R0
RST  PC

MEM82:				; Memory address high word
MOV  R1,#0
MOV  R2,#0
RTS  PC

MEM83:				; Lowest available memory address
MOV  R1,#0
MOV  R2,#LOWPAGE
RTS  PC

MEM84:				; Highest available memory address
MOV  R1,#0
MOV  R2,#HIGHPAGE
RTS  PC


; OSWORD
; ======
; On entry: R0=OSWORD number
;           R1=>control block
;
CMP  R0,#0
BEQ  RDLINE
;
; Tube data: &08 in_length block out_length  --  block
;
MOV  R0,-(SP)			; Save R0
MOV  #8,R0
JSR  PC,SEND_R2		; Send command &08 - OSWORD
;
; send param block, etc.
;
RTS  PC

; Read a line of text
; -------------------
; Tube data: &0A block  --  &FF or &7F string &0D
;
RDLINE:
MOV  #10,R0
JSR  PC,SEND_R2		; Send command &0A - RDLINE
ADD  R1,#2
MOV  R2,-(SP)			; Save R2
MOV  #3,R2
JSR  PC,SEND_BLK		; Send 3-byte control block
MOV  (SP)+,R2			; Restore R2
MOV  #7,R0
JSR  PC,SEND_R2		; Send &0700
MOV  #0,R0
JSR  PC,SEND_R2
JSR  GET_R2			; Wait for response
ADD  R0,#&FF80			; Copy b7 into Carry
BCS  RD_DONE
SUB  #2,R1			; Point to start of control block
MOV  R1,(R1)			; Get address to store text
READ_STR:
MOV  #0,R2			; Set count of received bytes
JSR  GET_R2			; Wait for byte from Tube R2
STRB R0,(R1)+			; Store it
INC  R2			; Increment number of bytes
CMP  R0,#13			; Check current byte
BNE  READ_STR			; Loop until <cr>
DEC  R2			; R2 is length of string
CLC				; Clear Carry
RD_DONE:
RTS  PC


; OSARGS - Read info on open file
; ===============================
; Tube Data: &0C handle block function  --  result block
;
; On entry: R0=function
;           R1=>control block
;           R2=handle
; On exit:  R0=returned value
;           R1 preserved
;           R2 preserved
;
ARGS:
MOV  R2,-(SP)			; Save handle
MOV  R0,-(SP)			; Save function
MOV  #&0C,R0
JSR  PC,SEND_R2		; Send command &0C - OSARGS
MOV  R2,R0
JSR  PC,SEND_R2		; Send handle
MOV  #4,R2
JSR  PC,SEND_BLK		; Send four-byte control block
MOV  (SP)+,R0
JSR  PC,SEND_R2		; Send function
JSR  PC,GET_R2			; Wait for returned result
MOV  R0,-(SP)			; Save result
MOV  #4,R2
JSR  PC,GET_BLK		; Wait for four-byte control block
MOV  (SP)+,R0			; Get result back
MOV  (SP)+,R2			; Get original handle back
RTS  PC


; OSBGET - Get a byte from open file
; ==================================
; Tube data: &0E handle --  Carry byte
;
; On entry: R1=handle
; On exit:  R0=byte read
;           R1=preserved
;           Cy set if EOF
;
BGET:
MOV  #&0E,R0
JSR  PC,SEND_R2		; Send command &0E - OSBGET
MOV  R1,R0
JSR  PC,SEND_R2		; Send handle
BRA  GET_CHAR			; Wait for Carry, Byte


; OSBPUT - Put a byte to an open file
; ===================================
; Tube data: &10 handle byte  --  &7F
;
; On entry: R0=byte to write
;           R1=handle
; On exit:  R0=preserved
;           R1=preserved
;
BPUT:
MOV  R0,-(SP)			; Save R0
MOV  #&10,R0
JSR  PC,SEND_R2		; Send command &10 - OSBPUT
MOV  R1,R0
JSR  PC,SEND_R2		; Send handle
MOV  (SP),R0			; Get R0 back
JSR  PC,SEND_R2		; Send byte to Tube
JSR  PC,GET_R2			; Wait for acknowledgement
MOV  (SP)+,R0			; Restore R0
RTS  PC


; OSFIND - Open of close a file
; =============================
; Tube data: &12 function string &0D  --  handle
;            &12 &00 handle  --  &7F
;
; On entry: R0=function
;           R1=handle or =>filename
; On exit:  R0=zero or handle
;
FIND:
MOV  R0,-(SP)			; Save R0
MOV  #&12,R0
JSR  PC,SEND_R2		; Send command &12 - OSFIND
MOV  (SP)+,R0			; Get R0 back
JSR  PC,SEND_R2		; Send function
CMP  R0,#0			; Check function
BEQ  CLOSE			; Jump to deal with CLOSE
JSR  PC,SEND_STR		; Send string at R1
JSR  PC,GET_R2			; Wait for returned handle
RTS  PC
CLOSE:
MOV  R1,R0
JSR  PC,SEND_R2		; Send handle to Tube
JSR  PC,GET_R2			; Wait for acknowledgement
MOV  #0,R0			; Zero R0
RTS  PC


; OSFILE - Operate on whole files
; ===============================
; Tube data: &14 block function string <cr>  --  result block
;
; On entry: R0=function
;           R1=>control block
; On exit:  R0=result
;           R1 preserved
;           control block updated
;
FILE:
MOV  R2,-(SP)			; Save R2
MOV  R1,-(SP)			; Save R1
MOV  R0,-(SP)			; Save function
MOV  #&14,R0
JSR  PC,SEND_R2		; Send command &14 - OSFILE
ADD  #2,R1			; Point to control block contents
MOV  #16,R2
JSR  PC,SEND_BLK		; Send 16-byte control block
MOV  (SP)+,R0
JSR  PC,SEND_R2		; Send function
SUB  #2,R1			; Point to filename pointer
MOV  (R1),R1			; Get filename pointer to R1
JSR  PC,SEND_STR		; Send filename string
JSR  PC,GET_R2			; Wait for returned result
MOV  (SP),R1			; Get control block pointer back
MOV  R0,-(SP)			; Save result
ADD  #2,R1			; Point to control block contents
MOV  #16,R2
JSR  PC,GET_BLK		; Wait for 16-byte control block
MOV  (SP)+,R0			; Get result back
MOV  (SP)+,R1			; Get control block pointer back
MOV  (SP)+,R2			; Get R2 back
RTS  PC


; OSGBPB - Multiple byte read and write
; =====================================
; Tube data: &16 block function  --   block Carry result
;
; On entry: R0=function
;           R1=>control block
; On exit:  R0=returned value
;              control block updated
;
GBPB:
MOV  R2,-(SP)			; Save R2
MOV  R0,-(SP)			; Save function
MOV  #&16,R0
JSR  PC,SEND_R2		; Send command &16 - OSGBPB
MOV  #13,R2
JSR  PC,SEND_BLK		; Send 13-byte control block
MOV  (SP)+,R0
JSR  PC,SEND_R2		; Send function
MOV  #13,R2
JSR  PC,GET_BLK		; Wait for four-byte control block
MOV  (SP)+,R2			; Get R2 back
BRA  GET_CHAR			; Get Carry and result byte



; Host->Client communication via interupts
; ========================================
;
GET_R1:
MOVB @TUBE1S,R0		; Read Tube R1 status
BIC  #&7F,R0			; Check b7 of status
BEQ  GET_R1			; Loop until b7 set
MOVB @TUBE1,R0			; Get byte from Tube R1
RTS  PC

GET_R4:
MOVB @TUBE4S,R0		; Read Tube R4 status
BIC  #&7F,R0			; Check b7 of status
BEQ  GET_R4			; Loop until b7 set
MOVB @TUBE4,R0			; Get byte from Tube R4
RTS  PC

; Interupt handler
; ================
; When Host sends a byte to R1 or R4 it generates a Client INT
; Within the interupt handler PSW has been saved on the stack
;  and further interupts are disabled
;
INT:
MOV  R0,-(SP)			; Save R0
MOVB @TUBE4S,R0		; Read Tube R4 status
BIC  #&7F,R0			; Check b7 of status
BNE  INT_R4			; R4 generated the interupt
MOVB @TUBE1S,R0		; Read Tube R1 status
BIC  #&7F,R0			; Check b7 of status
BNE  INT_R1			; R1 generated the interupt
MOV  (SP)+,R0			; Get R0 back
JMP  USERINT			; Something else generated the interupt

; Data present in Tube R1 generated an interupt
;
INT_R1:
MOVB @TUBE1,R0			; Get byte from Tube R1
CMP  R0,#&80			; Is b7 set?
BCC  INT_ESCAPE		; Change Escape state
;
; R1<&80 - Host event being passed to client
; Tube data: via R1: &00 Y X A
;
MOV  R1,-(SP)			; Save R1
MOV  R2,-(SP)			; Save R2
JSR  PC,GET_R1			; Wait for byte via Tube R1
MOV  R0,R2			; Pass to R2
JSR  PC,GET_R1			; Wait for byte via Tube R1
MOV  R0,R1			; Pass to R1
JSR  PC,GET_R1			; Wait for byte via Tube R1
JSR  INT_EVENT			; Call event vector
MOV  (SP)+,R2			; Restore registers
MOV  (SP)+,R1
MOV  (SP)+,R0
RTI				; Return from interupt

INT_EVENT:
RTS  PC			; Need to jump to a vector

; R1>&7F - Host changing Escape state
; Tube data: via R1: flag, b7=1, b6=state
;
INT_ESCAPE:
ADD  R0,R0			; Move b6 into b7
MOVB @ESCFLG,R0		; Store Escape flag
MOV  (SP)+,R0			; Restore R0
RTI				; Return from interupt

; Data present in Tube R4 generated an interupt
;
INT_R4:
MOVB @TUBE4,R0			; Get byte from Tube R4
CMP  R0,#&80			; Is b7 set?
BCS  INT_DATA			; Jump to to data transfer

; R4>&7F - Error occured
; Tube data: via R4: &FF, via R2: &00 err string &00
;
MOV  R1,-(SP)			; Remove this when errjmp done
JSR  PC,GET_R2			; Wait for an initial byte from R2
MOV  @ERRBLK,R1		; Point to error buffer
JSR  PC,GET_R2
MOV  R0,(R1)+			; Store error number
INT_R4LP:
JSR  PC,GET_R2			; Wait for byte of error string
MOV  R0,(R1)+			; Store in error buffer
CMP  R0,#0			; Check current character
BNE  INT_R4LP			; Loop until terminating &00 received
MOV  @ERRBLK,R0		; Point to error block
; Need to enable interupts, then generate client error
MOV  (SP)+,R1			; For the moment, restore all
MOV  (SP)+,R0
RTI				;  and return

; R4<&80 - Data transfer
; Tube data: via R4: action ID block sync, via R3: data
;
INT_DATA:
; R0=transfer type
;
MOV  R0,-(SP)			; Save transfer type
JSR  PC,GET_R4			; Wait for caller ID
MOV  (SP)+,R0			; Get transfer type back
CMP  R0,#5			; Is it 'release'?
BEQ  INT_DONE
JSR  PC,GET_R4			; Get data address byte 4
JSR  PC,GET_R4			; Get data address byte 3
JSR  PC,GET_R4			; Get data address byte 2
JSR  PC,GET_R4			; Get data address byte 1
JSR  PC,GET_R4			; Get sync byte
; Need to set up pointers for transfer via NMI
INT_DONE:
MOV  (SP)+,R0			; For the moment, restore all
RTI				;  and return


; Command line prompt
; ===================
; Allow user to enter *command
;
PROMPT:
EQUS "11*"			; Command prompt
EQUB 0
CLICOM:
JSR  PC,INITERR		; Initialise error handler
MOV  @PROMPT,R1
JSR  PC,SEND_TXT		; Display prompt
MOV  @COM_BLK,R1		; Point to control block
MOV  #0,R0
JSR  PC,WORD			; Read a line of text
BCS  COM_ESC			; Escape pressed
MOV  @CLI_LINE
JSR  PC,CLI			; Execute command
BRA  CLICOM			; Loop back for another line
;
COM_ESC:
MOV  #124,R0
JSR  PC,BYTE			; Acknowledge Escape
; Should generate a local error
MOV  @ESCAPE,R1
JSR  PC,SEND_TXT		; Print "Escape" message
BRA  CLICOM
ESCAPE:
EQUS "Escape"
EQUB 10
EQUB 13
EQUB 0
;
COM_BLK:
EQUW CLI_LINE
EQUB 255
EQUB 32
EQUB 255

