;TEACH MORSE CODE       (file: EXTEACH.ASM)
;
; 
;
; REVISION HISTORY
;
;	10/02/76 INITIAL VERSION ADAPTED FROM 6800
;	10/07/76 DOUBLE WEIGHT IF AVERAGE > 90 PCT. ADDED.
;	10/07/76 LETTER PROBABILITY NEVER ZERO ADDED.
;	12/06/76 REVISED RANDOM AND REMOVED PUNCTUATION
;		 TO SAVE SPACE IN 512 BYTE ROM.
;	03/23/77 CORRECT TYPO IN DOUBLE WEIGHT PROBABILITY.
;	06/03/77 ADDED SWITCHES FOR CPM, MORORW, IMSAI, AND HAL
;	09/08/78 ADDED SWITCH FOR SHERWOOD ASSEMBLY
;	04/18/82 ADDED SWITCHES AND BEEP CONTROL FOR OSBORNE1
;	05/01/82 CHANGED EXIT TO WARM START ON ^C FOR CP/M
;	03/06/83 Revised Osborne shadow mode control   /jw
;	01/17/85 added Osborne Ex - Geo. Walker - Columbia, md.
;
; ASSEMBLY CONSTANTS
;
FREQ	EQU	4000	;MACHINE CYCLES PER MSEC
WPM	EQU	15	;SEND SPEED=15 MIN, OR OVRFLO IN SEND ROUTINE
IDLE	EQU	3*50	;IDLE RESPONSE WAIT
MAXNUM	EQU	26+10	;MAXIMUM NUMBER OF CHARACTERS (LET+FIG)
PPIM	EQU	0FH	;8255 MODE SELECT
;
FALSE	EQU	0
TRUE	EQU	NOT FALSE
;
IMSAI	EQU	FALSE	;USES PORT 255
HAL	EQU	FALSE	;ORIGINAL VERSION
CPM	EQU	TRUE	;USE BDOS I/0
MOROWS	EQU	FALSE	;USE EI/DI INSTEAD OF I/O PORT
SHERWOOD EQU	FALSE	;USE SONALERT FOR TONE ON PORT 203
OSBORNE1 EQU	FALSE	;USE SONALERT FOR TONE ON 2C02H, BIT 5
OsEx	equ	TRUE	;speaker on port 2 bit 3 - GLW 01/17/85
;
	IF	NOT OSBORNE1
LINE	EQU	62	;TERMINAL LINE LENGTH
	ENDIF
;
	IF	OSBORNE1
LINE	EQU	51	;TERMINAL LINE LENGTH
	ENDIF
;
; MONITOR SUBROUTINE ENTRIES
;
	IF	HAL
CO	EQU	8009H	;COMMAND OUTPUT
CI	EQU	8003H	;COMMAND INPUT
CSTS	EQU	8012H	;COMMAND INPUT STATUS
;
	ORG	40H	;RAM
	ENDIF
;
	IF	CPM
	ORG	100H
	ENDIF
;
	IF OSBORNE1
	JMP	4000H
	ORG	4000H	;ABOVE SHADOW ROM SPACE
	ENDIF
;
	IF	NOT HAL
	JMP	BEGIN
	ENDIF
;
;
;  GLOBAL DATA
;
SEED	DW	1234H	;RANDOM NUMBER (MUST BE NON-ZERO)
NUM	DW	ERROR+4	;SIZE OF CURRENT ALPHABET
GIVE	DB	IDLE	;RESPONSE WAIT TIME IS SEC/50
TOLD	DB	0	;255 IF STUDENT WAS TOLD LETTER, 0 OTHERWISE
COLUMN	DB	LINE	;PRINT POSITION REMAINING ON LINE
;
;  OBJECT TIME STACK
;
	DS	30
STACK	DS	0
;
;  ERROR RATE FUNCTION
;
;
	IF	HAL
	ORG	100H	;MUST ORG AT A MULTIPLE OF 100H
	ENDIF
;
	IF	NOT HAL	;ORG TO NEXT MULTIPLE OF 100H
	ORG	($ OR 255) + 1
	ENDIF
;
ERROR	DB	255	;TOTAL ERROR RATE
	DS	MAXNUM
;
;  ENTRY POINT (FWA ROM)
;
;
	IF	HAL
	ORG	200H
	ENDIF
;
BEGIN	JMP	TEACH
;
;  CHARACTER CODE TABLES
;
MORSE	EQU	$-1
	DB	11011000B,11000100B,11001000B,11010000B
	DB	11111100B,11110100B,11100100B,11110000B
	DB	01111100B,01111000B,01101000B,01110000B
	DB	01001000B,01010000B,01100000B,11100000B
	DB	10000100B,10001000B,10011000B,10010000B
	DB	10111000B,10101000B,10110000B,10100000B
	DB	00111100B,00011100B,00101000B,00110000B
	DB	00001100B,00000100B,00011000B,00001000B
	DB	00010000B,00100000B,11000000B,01000000B
;
ASCII	DB	25H	;OVERALL PERCENTAGE
	DB	'Q7ZG'
	DB	'098O'
	DB	'1JPW'
	DB	'LRAM'
	DB	'6BXD'
	DB	'YCKN'
	DB	'23FU'
	DB	'45VH'
	DB	'SITE'
;
; MAIN PROGRAM TEACH
;
;	ENTRY VIA JUMP.  STACK POINTER IS REINITIALIZED.
;	INTERUPTS ARE NOT USED.
;
TEACH:	LXI	SP,STACK
	CALL	PRESET
TEA1:	CALL	GRAPH
TEA2:	CALL	SELECT
	MVI	A,0
	STA	TOLD	;CLEAR STUDENT TOLD FLAG
TEA3:	CALL	SEND
	PUSH	D
	CALL	CSTS	;LOOK FOR ENTRY WHILE SENDING
	ANA	A
	JZ	TEA4
	CALL	CI	;DISCARD CHARACTER
TEA4:	MVI	B,0	;CLEAR RESPONSE TIME COUNTER
	MVI	C,0	;CLEAR CORRECT RESPONSE FLAG
TEA5:	PUSH	B
	CALL	RANDOM	;MIX UP NUMBERS
	CALL	CSTS
	ANA	A
	JZ	TEA6	;IF NO RESPONSE YET
	CALL	CI	;GET CHARACTER
	POP	B
	POP	D
	CPI	0DH	;CARRIAGE RETURN
	JZ	TEA1	;GIVE STUDENT A GRAPH
	LXI	H,ASCII-ERROR
	DAD	D
	CMP	M
	PUSH	D
	JNZ	TEA5	;IF WRONG GUESS
	MOV	C,A	;SET CORRECT RESPONSE FLAG
	JMP	TEA7
TEA6:	MVI	C,20
	CALL	WAIT	;KILL SOME TIME
	POP	B
	MOV	A,B
	INR	A	;GAD***** IS THIS SILLY
	MOV	B,A	;WHY NOT INR B *********
	LDA	GIVE
	CMP	B
	JNZ	TEA5	;IF SOME TIME LEFT
	LXI	H,TOLD
	MVI	M,255	;RECORD TIMEOUT
TEA7:	MOV	A,B	;COMPUTE NEW SPEED
	POP	D
	STC
	CMC
	RAL		;RESPONSE TIME * 2
	JNC	TEA8
	MVI	A,255	;SET TO MAXIMUM ON OVERFLOW
TEA8:	LXI	H,GIVE
	MOV	B,M
	CALL	WEIGHT	;ADJUST SPEED
	MOV	M,A
	LXI	H,ASCII-ERROR
	DAD	D
	PUSH	B	;SAVE ANSWER
	MOV	C,M
	CALL	PRINT	;OUTPUT CORRECT ANSWER
	MVI	C,' '
	CALL	PRINT	;DOUBLE SPACE
	MVI	C,250
	CALL	WAIT
	POP	B
	MOV	A,C
	ANA	A
	JZ	TEA3	;IF INCORRECT TRY IT OVER
	CALL	GRADE
	JMP	TEA2
;
; SUBROUTINE PRESET
;
;	INITIALIZE I/O AND GLOBAL VARIABLES
;
PRESET:	LXI	H,ERROR+MAXNUM
PRE1:	MVI	M,255	;SET ERRORS TO MAXIMUM
	DCR	L
	JNZ	PRE1
	MVI	M,256*30/100	;GUESS AVERAGE OF 70 PERCENT
	LXI	H,ERROR+4
	SHLD	NUM	;MINIMUM ALPHABET
	MVI	A,IDLE
	STA	GIVE	;AVERAGE SPEED
	LXI	H,1234H
	SHLD	SEED	;NON-ZERO RANDOM SEED
;
	IF	HAL
	MVI	A,0A6H
	OUT	PPIM	;PROGRAM MODE 1 OUTPUT
	ENDIF
;
	IF	SHERWOOD
	XRA	A	;INIT FRONT PANEL PIA
	OUT	200
	OUT	202
	CMA
	OUT	201
	OUT	203
	MVI	A,4
	OUT	200
	OUT	202
	MVI	A,10H
	OUT	203	;INITIALLY TURN OFF TONE
	ENDIF
;
	RET
;
;
; SUBROUTINE RANDOM
;
;	TAUSWORTH GENERATOR USING PRIMITAVE TRINOMIAL
;	  X**15 + X + 1 WITH PERIOD 2**15-1.
;	RETURNS 0 < (D,E) < 2**15.
;
RANDOM:	LXI	H,SEED
	MOV	A,M
	STC
	CMC
	RAL		;HE SHOULD HAVE USED ORA A ***
	MOV	D,A
	INX	H
	MOV	A,M
	RAR
	XRA	M
	MOV	M,A
	MOV	A,D
	DCX	H
	XRA	M
	JNC	RAN1
	XRI	60H
RAN1:	MOV	M,A
	MOV	E,A
	RET
;
;
; SUBROUTINE SELECT
;
;	ON EXIT (D,E) CONTAINS A POINTER TO THE SELECTED
;	CHARACTER IN THE ERROR TABLE.  PRESERVES NO REGISTERS.
;
SELECT:	CALL	RANDOM
	MOV	A,D
	CMA		;MAKE RANDOM NUMBER NEGATIVE
	MOV	D,A
SEL1:	LHLD	NUM	;BEGIN SCAN
SEL2:	MOV	C,M	;GET ERROR RATE
	MVI	B,0
	INX	B	;INSURE PROBABILITY IS NEVER ZERO
	XCHG
	DAD	B	;ADD ERROR TO RANDOM NUMBER
	RC		;STOP LOOP ON CARRY OUT
	XCHG
	DCR	L
	JNZ	SEL2	;SCAN REMAINDER OF TABLE
	JMP	SEL1	;START SCAN OVER
;
;
; SUBROUTINE SEND
;
;	THE CHARACTER REFERENCED BY (D,E) IS SENT AS MORSE CODE.
;	ON EXIT D,E ARE PRESERVED.
;
SEND:	LXI	H,MORSE-ERROR
	DAD	D
	MOV	B,M	;GET MORSE LETTER
SEN1:	MOV	A,B
	STC		;HE SHOULD HAVE USED ORA A ***
	CMC
	RAL		;GET NEXT ELEMENT IN CARRY
	MOV	B,A
	MVI	C,1200/WPM	;ONE DIT TIME IN MSEC
	JNC	SEN2
	MVI	C,3*1200/WPM	;ONE DAH TIME
SEN2:	ANA	A
	RZ		;IF LAST BIT WAS STOP BIT
	CALL	BEEP
	MVI	C,1200/WPM	;INTER ELEMENT SPACE
	CALL	WAIT
	JMP	SEN1	;LOOP FOR REMAINING BITS
;
;
; SUBROUTINE WAIT
;
;	DELAY C MSEC.  ON EXIT A AND C ARE ZERO.
;	ALL OTHER REGS ARE PRESERVED.
;
WAIT:	MVI	A,FREQ/16
WAI1:	DCR	A
	JNZ	WAI1	;LOOP FOR A MSEC
	DCR	C
	JNZ	WAIT	;IF MORE MSEC TO GO
	RET
;
;
; SUBROUTINE BEEP
;
;	DELAY C MSEC WHILE GENERATING A 1000HZ SIDETONE.
;	OUTPUT IS ON 8255 PORT PC4.
;	ENTRY/EXIT SAME AS WAIT (SEE ABOVE).
;


BEEP:
	IF 	OSBORNE1
	di		;enter shadow mode
	out	0
	LDA 	2C02H	;GET PIA STATUS
	ORI	20H	;SET BIT 5
	STA	2C02H	;BELL ON
	out	1
	ei		;leave shadow mode
	ENDIF
;
	IF	SHERWOOD
	MVI	A,20H
	OUT	203	;TONE ON
	ENDIF
;
BEE9:	MVI	A,FREQ/16/3
BEE1:	DCR	A
	JNZ	BEE1	;LOOP FOR 1/2 MSEC
;
	if	OsEx	;Osborne Executive
	in	2
	ori	8	;speaker hi (set bit 3)
	out	2	; -GLW-  01/17/85	
	endif
;
	IF	HAL
	MVI	A,1001B	;SET PC4
	OUT	PPIM	;LO TO HIGH ON OUTPUT
	ENDIF
;
	IF	MOROWS
	EI
	ENDIF
;
	IF	IMSAI
	MVI	A,1000$0000B
	OUT	0FFH
	ENDIF
;
BEE2:	MVI	A,FREQ/16/3
BEE3:	DCR	A
	JNZ	BEE3	;LOOP FOR SECOND 1/2 MSEC
;
	if	OsEx	;Osborne Ex
	in	2
	xri	8	;speaker lo (clr bit 3)
	out	2
	endif
;
	IF	HAL
	MVI	A,1000B	;CLEAR PC4
	OUT	PPIM	;HIGH TO LO ON OUTPUT
	ENDIF
;
	IF	MOROWS
	DI
	ENDIF
;
	IF	IMSAI
	MVI	A,0000$0000B
	OUT	0FFH
	ENDIF
;
	DCR	C
	JNZ	BEE9	;IF MORE MSECS TO GO
;
	IF 	OSBORNE1
	di		;enter shadow mode
	out	0
	LDA	2C02H	;GET PIA STATUS
	ANI	0DFH	;RESET BIT 5
	STA	2C02H	;BELL OFF
	out	1
	ei		;leave shadow mode
	ENDIF
;
	IF	SHERWOOD
	MVI	A,255
	OUT	203
	ENDIF
;
	RET
;
;
; SUBROUTINE WEIGHT
;
;	COMPUTES A WEIGHTED MOVING AVERAGE
;	AS FOLLOWS, A = .875 * B + .125 * A
;	ALL OTHER REGS ARE PRESERVED.
;
WEIGHT:	ADD	B
	RAR		;(A+B)/2
	ADD	B
	RAR		;((A+B)/2+B)/2
	ADD	B
	RAR		;(((A+B)/2+B)/2+B)/2
	RET
;
;
; SUBROUTINES RETURN, PRINT
;
;	OUTPUT CR/LF OR CHARACTER IN C.
;	ON EXIT D,E AND A ARE PRESERVED.
;
RETURN:	LXI	H,COLUMN
	MVI	M,LINE+2
	MVI	C,0DH	;CR
	CALL	PRINT
	MVI	C,0AH	;LF   FALL THROUGH TO PRINT
PRINT:	PUSH	D
	PUSH	PSW
	CALL	CO
	POP	PSW
	POP	D
	LXI	H,COLUMN
	DCR	M
	JZ	RETURN	;LOCAL CR/LF
	RET
;
;
; SUBROUTINE GRADE
;
;	INDIVIDUAL AND TOTAL ERROR RATES ARE
;	COMPUTED.  THE SIZE OF THE ALPHABET IS
;	INCREASED IF PERFORMANCE IS ADAQUATE.
;	ON ENTRY D,C POINTS TO CHARACTER ERROR TABLE
;	ENTRY, (TOLD) IS 0 FOR CORRECT OR 255 FOR INCORRECT.
;
GRADE:	LDA	TOLD
	XCHG
	MOV	B,M
	CALL	WEIGHT	;ADJUST INDIVIDUAL RATE
	MOV	M,A
	XCHG
	LXI	H,ERROR
	LDA	TOLD
	MOV	B,M
	CALL	WEIGHT	;ADJUST AVERAGE RATE
	MOV	M,A
	CPI	256*30/100
	JNC	GRA3	;IF BAD AVERAGE
	CPI	256*10/100
	JNC	GRAD	;IF GOOD, BUT NOT GREAT
	XCHG
	MOV	B,M
	LDA	TOLD
	CALL	WEIGHT	;COUNT SCORE TWICE IF HOT
	MOV	M,A
GRAD:	LHLD	NUM
	MVI	A,256*40/100
GRA1:	CMP	M
	JC	GRA3	;IF BAD INDIVIDUAL RATE
	DCR	L
	JNZ	GRA1
GRA2:	LXI	H,NUM	;CHECK FOR COMPLETE ALPHABET
	MVI	A,MAXNUM
	CMP	M
	JZ	GRA3
	INR	M	;ADD LETTER
GRA3:	RET		;GAD* THIS IS SILL, WHY JUMP TO A RETURN
;
;
; SUBROUTINE GRAPH
;
;	THE PREDICTOR FUNCTION IS DISPLAYED.
;	ALL THE REGISTERS ARE DESTROYED.
;
GRAPH:	LHLD	NUM
	XCHG
GPH1:	CALL	RETURN
	LXI	H,ASCII-ERROR
	DAD	D
	MOV	C,M	;GET ASCII FOR CURRENT CHARACTER
	CALL	PRINT
	MVI	C,' '
	MOV	H,D
	MOV	L,E
	MOV	A,M	;B IS ERROR RATE
GPH2:	CALL	PRINT	;PRINT BAR GRAPH
	MVI	C,'*'
	SUI	255/LINE+1
	JNC	GPH2	;IF A IS STILL POSITIVE
	MVI	C,250	;SLOW DOWN FOR FAST DISPLAYS
	CALL	WAIT
	DCR	E
	JP	GPH1	;LOOP FOR ALL BARS
	CALL	CI
	CPI	1BH	;ESC CHAR-WANT NEW RUN AT IT?
	JZ	TEACH	;YUP-START OVER
;
	IF 	CPM
	CPI	3	;IF CONTROL-C
	JZ	0	;WARM START
	ENDIF
;
	IF	NOT CPM
	CPI	1	;JUMP TO MONITOR ON ^A
	JZ	0FC00H	;YUP GOTO ODT
	ENDIF
;
	JMP	RETURN
;
;
; ADDED I/O DRIVERS
;
	IF	CPM
CO:	PUSH	H!	PUSH	D!	PUSH	B
	PUSH	PSW
	MOV	E,C
	MVI	C,2
	CALL	BDOS	;console out
	POP	PSW
	POP	B!	POP	D!	POP	H
	RET
;
BDOS	EQU	5H
;
CI:	PUSH	H!	PUSH	D!	PUSH	B
	CALL	CICPM
	POP	B!	POP	D!	POP	H
	RET
;
CICPM:	LHLD	1	;POINTER TO BIOS WARM START
	LXI	D,6
	DAD	D	;POINT TO CONSOLE INPUT ROUTINE
	PCHL		;JUMP TO CONSOLE INPUT ROUTINE
;
CSTS:	PUSH	H!	PUSH	D!	PUSH	B
	CALL	CSTSCP
	ANI	1
	POP	B!	POP	D!	POP	H
	RET
;
CSTSCP:	LHLD	1
	INX	H!	INX	H!	INX	H
	PCHL
	ENDIF
;
	END  TEACH
H	B
	CALL	CSTSC