;///////////////////////////////////////////////////////////////////////////////////
; Microterm TMX v0.5 by Retrocomputacion.com
; Author: Jorge Castillo (pastbytes)
; Last modified: 29-May-2025
; www.pastbytes.com/microterm#timex
; www.pastbytes.com/wifimodem#timex
; Compile using: Pasmo --tapbas uterm2068v0.5.asm uterm2068.tap
;///////////////////////////////////////////////////////////////////////////////////

	ORG	32768

Start:
	JP	MainPrg			; Starts in MainPrg

; System variables

; Constants
ATTR_P		EQU	$5C8D		; Permanent current colours
ATTR_T		EQU	$5C8F		; Temporary current colours
LAST_K		EQU	$5C08		; Stores newly pressed key
BORDCR		EQU	$5C48		; Border colour * 8; also contains the attributes normally used for the lower half of the screen
FLAGS		EQU	$5C3B		; Various flags to control the BASIC system
TVFLAG		EQU	$5C3C		; Flags associated with the television
DFCC		EQU	$5C84		; Address in display file of PRINT position
SCRCT		EQU	$5C8C		; Scroll counter
PSGCTRL		EQU     $FFF5		; PSG (AY-3-8912) control port
PSGDATA		EQU	$FFF6		; PSG (AY-3-8912) data port

CANCELTIME	EQU	171		; Time to wait before canceling reception: 2 milliseconds (171 x 41 cycles = 7011 cycles)

; Variables

;///////////////////////////////////////////////////////////////////////////////////
; MAIN PROGRAM
;///////////////////////////////////////////////////////////////////////////////////

MainPrg:
	LD	A,0			; Selects the upper half of the screen
	LD	(TVFLAG),A
	LD	A,$07			; FLASH 0: BRIGHT 0: INK 7: PAPER 0
	LD	(BORDCR),A
	CALL	CLS			; Clears screen
	LD	A,0			; BORDER 0
	OUT	($FE),A
	LD	HL,StartMsg		; Prints start message
	CALL	PrintText
	LD	HL,(DFCC)		; HL = Address in display file of PRINT position
	CALL	HRToAttr		; Points HL to the corresponding address in the display attribute memory
	SET	7,(HL)			; Activates the flash attribute for that character
ChkKeyb:
	HALT				; Waits for the interrupt routine to complete
	DI				; Disables interrupts
	LD	HL,FLAGS		; Checks if a key was pressed
	BIT	5,(HL)
	JR	Z,RcvChar1		; If no key was pressed (B5=0), continues at RcvChar1
	RES	5,(HL)			; If a key is pressed, it clears bit 5 to wait for another key
	LD	A,(LAST_K)		; A = ASCII value of the key
	CP	226			; If STOP (Symbol+A) is pressed, it exits to BASIC
	JR	NZ,SendKey
	EI				; Enables interrupts
	RET
SendKey:
	CP	12			; If A=DELETE, then A=8 (ASCII BACKSPACE)
	JR	NZ,SendKey_
	LD	A,8
SendKey_:
	CALL	SendByte		; If a key is pressed, it sends the character
RcvChar1:
	CALL	ReadByte		; Tries to receive a byte
	OR	A			; If didn't receive one, it continues at EndLoop
	JR	Z,EndLoop
	EI				; Enables interrupts
	LD	A,L			; A = L (received byte)
	CP	8			; If A=BACKSPACE (valid char), it continues at PrintChar1
	JR	Z,PrintChar1
	CP	13			; If A=CR (valid char), it continues at PrintChar1
	JR	Z,PrintChar1
	CP	32			; If A<32 (invalid char), it continues at RcvChar2
	JR	C,RcvChar2
	CP	124			; If A=STICK (invalid char), it continues at ChkKeyb
	JR	Z,ChkKeyb
	CP	126			; If A=FREE (invalid char), it continues at ChkKeyb
	JR	Z,ChkKeyb
	BIT	7,A			; If A>127 (invalid char), it continues at RcvChar2
	JR	NZ,RcvChar2
PrintChar1:
	LD	B,A			; Saves the received character in B
	LD	HL,(DFCC)		; HL = Address in display file of PRINT position
	CALL	HRToAttr		; Points HL to the corresponding address in the display attribute memory
	RES	7,(HL)			; Deactivates the flash attribute for that character
	LD	A,B			; Recovers the received character from B
	RST	$10			; Prints the character in A
	LD	HL,(DFCC)		; HL = Address in display file of PRINT position
	CALL	HRToAttr		; Points HL to the corresponding address in the display attribute memory
	SET	7,(HL)			; Activates the flash attribute for that character
	LD	A,255			; Reinicia el contador de scroll
	LD	(SCRCT),A
RcvChar2:
	DI				; Disables interrupts
	CALL	ReadByte		; Tries to receive a byte
	OR	A			; If didn't receive one, it continues at EndLoop
	JR	Z,EndLoop
	EI				; Enables interrupts
	LD	A,L			; A = L (received byte)
	CP	8			; If A=BACKSPACE (valid char), it continues at PrintChar2
	JR	Z,PrintChar2
	CP	13			; If A=CR (valid char), it continues at PrintChar2
	JR	Z,PrintChar2
	CP	32			; If A<32 (invalid char), it continues at ChkKeyb
	JR	C,ChkKeyb
	CP	124			; If A=STICK (invalid char), it continues at ChkKeyb
	JR	Z,ChkKeyb
	CP	126			; If A=FREE (invalid char), it continues at ChkKeyb
	JR	Z,ChkKeyb
	BIT	7,A			; If A>127 (invalid char), it continues at ChkKeyb
	JR	NZ,ChkKeyb
PrintChar2:
	LD	B,A			; Saves the received character in B
	LD	HL,(DFCC)		; HL = Address in display file of PRINT position
	CALL	HRToAttr		; Points HL to the corresponding address in the display attribute memory
	RES	7,(HL)			; Deactivates the flash attribute for that character
	LD	A,B			; Recovers the received character from B
	RST	$10			; Prints the character in A
	LD	HL,(DFCC)		; HL = Address in display file of PRINT position
	CALL	HRToAttr		; Points HL to the corresponding address in the display attribute memory
	SET	7,(HL)			; Activates the flash attribute for that character
	LD	A,255			; Restarts the scroll counter
	LD	(SCRCT),A
	JP	ChkKeyb			; Continues in ChkKeyb
EndLoop:
	EI				; Enables interrupts
	JP	ChkKeyb			; Continues in ChkKeyb

;///////////////////////////////////////////////////////////////////////////////////
; Takes a memory address of the graphic area and returns the memory address of the
; corresponding attribute.
; https://espamatica.com/zx-spectrum-screen/#attribute-address-from-graphic-area-address

HRToAttr:
	LD	A,H			; Loads the upper part of the address in A
	AND	$18			; Keeps the bits of the third
	RRCA
	RRCA
	RRCA				; Passes them to bits 0 and 1
	OR	$58			; Add the fixed part
	LD	H,A			; A = 010110TT
	RET				; HL = attribute memory address

;///////////////////////////////////////////////////////////////////////////////////
; Sends the byte contained in A through the joystick port

; There are 93.2173177083 cycles per bit

SendByte:
	LD	B,A			; Saves the received character in B
	LD	C,8			; We will send 8 bits

	LD	A,7			; 7 Selects register 7 (ENABLE)
	OUT	(PSGCTRL),A		; 11
	LD	A,%01000000		; 7 A = 01000000 (PORT A configured as output)
	OUT	(PSGDATA),A		; 11

SendStart:
	LD	A,14			; 7 Selects register 14 (IOA)
	OUT	(PSGCTRL),A		; 11
	LD	A,%00000010		; 7 A = 00000000 (Bit 1: RTS = 1, Bit 0: TX = 0)
	OUT	(PSGDATA),A		; 11 Writes A to IOA (start)
					; *** Start bit ***
	LD	DE,0			; 10 Spends 48 cycles
	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	NOP				; 4
	NOP				; 4
SBLoop:
	LD	A,0			; 7 Spends 11 cycles
	NOP				; 4

	LD	DE,0			; 10 Spends 32 cycles
	LD	DE,0			; 10
	NOP				; 4
	NOP				; 4
	NOP				; 4

	RR	B			; 8 Carry flag = next bit to send
	RL	A			; 8 Copies carry flag to A (bit 0: TX)
	OR	%00000010		; 7 RTS = 1 (bit 1)

	OUT	(PSGDATA),A		; 11 Writes A to IOA (bit 0)
					; *** Data bit ***
	DEC	C			; 4 Decrements C
	JR	NZ,SBLoop		; 12 If C<>0, continues at SBLoop
					; 7 If C=0, JR uses 7 cycles

	LD	DE,0			; 10 Spends 64 cycles
	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	NOP				; 4
	LD	A,%00000011		; 7 RTS = 1, TX = 1
SendStop:
	OUT	(PSGDATA),A		; 11 Writes A to IOA
					; *** Stop bit ***
	INC	IY			; 10 Spends 51 cycles
	DEC	IY			; 10
	INC	IY			; 10
	DEC	IY			; 10
	LD	A,0			; 7
	NOP				; 4

	LD	A,7			; 7 Selects register 7 (ENABLE)
	OUT	(PSGCTRL),A		; 11
	XOR	A			; 4 A = 00000000 (PORT A configured as input)
	OUT	(PSGDATA),A		; 11
	RET				; 10 Returns

;///////////////////////////////////////////////////////////////////////////////////
; Receives a byte from the joystick port, and stores it in RXBYTE

; There are 93.2173177083 cycles per bit
; Reception timeout: 2 ms = 7000 cycles

ReadByte:
	LD	A,7			; 7 Selects register 7 (ENABLE)
	OUT	(PSGCTRL),A		; 11
	LD	A,%01000000		; 7 A = 01000000 (PORT A configured as output)
	OUT	(PSGDATA),A		; 11

	LD	A,14			; 7 Selects register 14 (IOA)
	OUT	(PSGCTRL),A		; 11
	LD	A,%00000001		; 7 A = 00000001 (Bit 1: RTS = 0, Bit 0: TX = 1)
	OUT	(PSGDATA),A		; 11

	LD	A,7			; 7 Selects register 7 (ENABLE)
	OUT	(PSGCTRL),A		; 11
	XOR	A			; 4 A = 00000000 (PORT A configured as input)
	OUT	(PSGDATA),A		; 11

	LD	A,14			; 7 Selects register 14 (IOA)
	OUT	(PSGCTRL),A		; 11
	LD	L,CANCELTIME		; We will wait CANCELTIME (2 milliseconds) before canceling the reception
WaitStrt:
	IN	A,(PSGDATA)		; 11 Reads RX input
	AND	%10000000		; 7 (bit 7)
	JR	Z,StartBit		; 12 If RX=0, continues at StartBit
					; 7 else
	DEC	L			; 4 decrements L
	JR	NZ,WaitStrt		; 12 If L<>0, continues at WaitStrt (41 cycles loop)
					; 7 else
	XOR	A			; 4 If L=0 (reception timeout), then A = 0 (no byte received)
	LD	L,0			; 7
	RET				; 10 Returns

StartBit:				; *** StartBit ***
	LD	H,8			; 7 We will receive 8 bits
	; Add delay if necessary to fall into bit 0
	LD	DE,0			; 10 Spends 20 cycles
	LD	DE,0			; 10
	LD	DE,0			; 10 Spends 20 cycles
	LD	DE,0			; 10

	SCF				; 4
	RET	NC			; 5
	NOP				; 4
	NOP				; 4	64
ReadBit:
	IN	A,(PSGDATA)		; 11 Reads RX input (bit 7)
	RL	A			; 8 Carry flag = received bit
	RR	L			; 8 Appends the received bit to L

	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	SCF				; 4
	RET	NC			; 5

	DEC	H			; 4 Decrements H
	JR	NZ,ReadBit		; 12 If H<>0, continues at ReadBit
					; 7 If H=0, JR uses 7 cycles

	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	DE,0			; 10
	LD	A,1			; 7 A = 1 (byte received)
	RET				; 10 Returns

;///////////////////////////////////////////////////////////////////////////////////
; Prints a string terminated with $FF and located at (HL)

PrintText:
	LD	A,(HL)			; Gets a character
	CP	$FF			; Compares it with $FF
	JP	Z,PrintEnd		; If $FF, continues at PrintEnd
	RST	$10			; else prints the character in A
	INC	HL			; Points HL to the next character
	JP	PrintText		; Continues at PrintText
PrintEnd
	RET				; Returns

;///////////////////////////////////////////////////////////////////////////////////
; Clears the screen

CLS:    PUSH	AF
	LD	HL, $4000
	LD	DE, $4001
	LD	BC, $1800
	XOR	A
	LD	(HL),A
	LDIR
	POP	AF
	LD	(ATTR_P),A
	LD	(ATTR_T),A
	LD	(HL),A
	LD	BC, $02FF
	LDIR
	RET

;///////////////////////////////////////////////////////////////////////////////////
; Messages
;///////////////////////////////////////////////////////////////////////////////////

StartMsg:
	DEFB $12,0,$13,0,$14,0,$10,7
	DEFM 'Microterm TMX v0.5 - 38400,8N1',$0D
	DEFM $7F,' 2025 by Retrocomputacion.com',$0D
	DEFB 255


	END 32768
