;THIS PROGRAM, CONSISTING OF A PROM SECTION OF SUBROUTINES & MAINLINE ;TASKS AND A RAM SECTION OF BUFFERS, GENERATES AND DECODES MORSE CODE ;AS WELL AS RADIO TELETYPE (RTTY). ;THE DECODING SECTION IS DESIGNED TO BE SELF TRACKING ;AS TO CHANGING SPEEDS. DUE TO THIS SELF TRACKING ;FEATURE, A FEW ILLEGAL CODES MAY BE DECODED AS THE ;PROGRAM ATTEMPTS TO LOCK ONTO THE INPUT CODE SPEED. ;THIS PROGRAM IS DESIGNED TO RUN ON THE INTEL 8080 BASED ;BSR MODULE MANUFACTURED BY DIGITAL EQUIPMENT CORPORATION. ;WRITTEN 1977 BY BRUCE FILGATE IN PARTIAL FULFILLMENT ;FOR THE MASTERS THESIS AT WORCESTER POLYTECHNICAL INSTITUTE. ;MORSE CODE SYMBOL WEIGHTING IS 1 DASH TO 3 DOTS. ;JUST A FEW WORDS ABOUT THE STACK STRUCTURE..... ;THE STACKS USE THE FIRST LOCATION AS A COUNT OF THE ;NUMBER OF OTHER USED LOCATIONS IN THE STACK. UNLIKE ;THE MAIN PROGRAM STACK, THESE STACKS START AND PROCEED ;UPWARDS IN MEMORY ADDRESS SPACE AND ARE FIFO NOT LIFO. ;THE COMMAND REGISTER FOR THE USART AT ADDRESS 20001 (40 001) ;USES BIT 1 (DATA TERMINAL READY) AS THE CODE OUTPUT LINE ;AND BIT 7 (DATA SET READY) AS THE CODE INPUT LINE. THE USART ;PROPER, IS USED FOR THE RTTY I/O. ;KEY SENSE FOR CODE (AND RTTY) INPUT AND OUTPUT IS GROUND FOR KEY DOWN ;CONDITION AND LOGIC HIGH FOR THE KEY UP CONDITION. ;******************************************************* ; ; PROGRAM SHOULD BE STARTED AT START ; ;******************************************************* ;THE TERMINAL WILL TYPE A "?" WHENEVER A BUFFER ;OVERFLOW IS CAUSED BY THE USER. THE CHARACTER ;THAT WOULD HAVE CAUSED THE OVERFLOW IS TRAPPED ;AND DELETED. ;NORMALLY THE KEYBOARD DATA IS TRANSMITTED OUT TRANSLATED IN MORSE ;CODE, BUT THE ALT MODE (ESC) KEY MAY BE USED TO ENTER SPECIAL COMMANDS. ;THE ALT MODE IS FOLLOWED BY ONE OF THE FOLLOWING CHARACTERS TO ;ENTER THE REQUIRED FUNCTION: ; 1=INPUT AND TRANSLATE MORSE CODE, OR RTTY UNTIL, ^C IS TYPED ON CONSOLE ; 2=PRINT & OPEN "STATION" BUFFER FOR A COMMAND ; 3=PRINT & OPEN MESSAGE BUFFER "A" FOR A COMMAND ; 4=PRINT & OPEN MESSAGE BUFFER "B" FOR A COMMAND ; 5=PRINT & OPEN MESSAGE BUFFER "C" FOR A COMMAND ; ONCE ENTERED THE BUFFER OPERATIVES ARE: ; L FOLLOWED BY NEW TEXT TERMINATED WITH ESC TO LOAD TEXT ; S TO XMIT THE BUFFER IN CODE ; 6=GENERATE A QUIZ BY SENDING RANDOM CHARACTERS IN CODE AND WAITING ; FOR A KEYBOARD ANSWER. USE ^C TO EXIT FROM THIS MODE. ; THE RANDOM CHARACTERS ARE CHOSEN FROM THE CONTENTS OF ; THE "A" MESSAGE BUFFER. IF THE INCORRECT RESPONSE ; IS TYPED THE PROGRAM RESPONDS IN CODE WITH "ERROR" ; FOLLOWED BY A REPEAT OF THE MISSED CHARACTER. ; 7=GENERATE RANDOM FIVE CHARACTER CODE GROUPS AND TYPE THEM OUT ON ; THE TERMINAL. THE CHARACTERS A GENERATED FROM THE ; CONTENTS OF THE "A" MESSAGE BUFFER. ; 8=TRANSMIT "STATION" BUFFER CONCATENATED WITH MESSAGE BUFFER "B" ; 9=TRANSMIT "STATION" BUFFER CONCATENATED WITH MESSAGE BUFFER "C" ; 0=LOG THE COMPLETED QSO ; -=TRY TO FIND "STATION" IN THE LOG ; C=MORSE CODE MODE (NOT RTTY MODE) ; G=GAIN SPACE FOR MORE LOG ENTRIES BY DOING A COMPRESS. PRIOR TO ; COMPRESS, THE PART OF THE LOG WHICH HAS NOT BEEN PREVIOUSLY ; COMPRESSED, IS PRINTED OUT ON THE TERMINAL. ; M=MAINTENANCE BRANCH TO MDP VIA BRKX ; P=PRINT OUT THE ENTIRE LOG ; R=ENTER RTTY MODE (NOT MORSE CODE MODE) ; S=SET THE TIME WITH THE NEXT FOUR CHAR (HHMM) ; T=PRINT OUT THE CURRENT TIME ; W=LOAD BAUD CONSTANT FOR WPM OUTPUT. THE COMMAND IS THE LETTER ; W FOLLOWED BY ONE OF THE FOLLOWING SYMBOLS DEPENDING ON THE ; SPEED DESIRED: ; SPACE 120 WPM ; ! 89 ; " 63 ; # 48 ; $ 44 ; % 37 ; ' 27 ; ) 22 ; + 18 ; / 14 ; 3 12 ; > 7.6 ; ? 7.2 ; ; X=XMIT THE "A" MESSAGE BUFFER UNTIL ^C ; Z=ZAP (CLEAR) THE LOG AREA BUFFER ; ; ;THE DELETE (RUBOUT) KEY SERVES TWO FUNCTIONS DEPENDING ;ON WHETHER THE USER IS LOADING THE DATA BUFFER OR TYPING ;DATA TO BE IMMEDIATLY TRANSMITTED IN CODE. WHEN IN THE ;MESSAGE BUFFER LOADING MODE, THE DELETE WILL REMOVE ;ONE CHARACTER AT A TIME FROM THE BUFFER UNTIL THE BUFFER ;IS EMPTY. WHEN THE CHARACTERS ARE BEING TRANSMITTED AS ;THEY ARE TYPED, THE DELETE KEY WILL GENERATE THE ERROR ;CODE (EIGHT DOTS). ; ; ; # IS USED AS THE PROMPT FOR THIS PROGRAM ; ; ; ;BUFFER SIZE SET UP MSGSZ=377 ;MESSAGE HOLDING BUFFERS BUFOUT=60 ;CODE OUTPUT BUFSKY=100 ;KEYBOARD BUFSPN=60 ;PRINTER ;CONSTANTS, ANY OF THESE MAY BE MODIFIED BY EDITING AND REASSEMBLING THE PROGRAM TUSART=10000 ;TTY ADDRESS FOR CONSOLE RUSART=20000 ;RTTY ADDRESS FOR BAUDOT/MORSE WIDTH=117 ;PRINTER WIDTH IN OCTAL FILL=4 ;# OF FILL CHARS NEEDED FOR CR/LF FILCHR=0 ;ASCII FILL CHAR USED FOR CR/LF ERCHAR=77 ;CONSTANT FOR ERROR CHAR (ASCII "?"=77) ESC=33 ;ENTER COMMAND MODE (TV50 ESC=175,LA36=33) K=113 ;SENT IN CODE WHEN QUIZ ROUTINE IS READY ESCSYM=44 ;ECHO A $ FOR ESC QUEST=77 ;QUESTION MARK FOR BAD COMMAND DELETE=177 ;CHAR THAT REPRESENTS THE DELETE DELSYM=134 ;PRINTABLE CHAR FOR A DELETE ETX=3 ;CONTROL C EXIT TO STRT BLANK=40 ;ASCII SPACE CONSTANT MEMEND=77777 ;LAST MEMORY LOCATION (16K RAM AT PRESENT) LTRS=37 ;BAUDOT SHIFT FIGS=33 ;BAUDOT SHIFT ;EXTRA INSTRUCTIONS TO MAKE LIFE EASIER .MACRO STATUS ;GET TTY STATUS LDA TUSART+1 .ENDM .MACRO PRINT ;PRINT THE A REG STA TUSART .ENDM .MACRO READ ;FETCH THE KEYBOARD TO A LDA TUSART .ENDM .MACRO SENSE ;SET STATUS FLAGS FOR CODE INPUT STATUS LDA RUSART+1 ;GET BIT ANI 200 ;KEEP THE RELEVANT BIT .ENDM .MACRO OUTPUT ;SET THE CODE OUTPUT LINE STA RUSART+1 .ENDM .=2000 ;SET ON NEXT PROM ABOVE MDP ;START UP TIME HOUSEKEEPING START: XRA A ;A=0 STA MSGABF ;CLEAR THE MESSAGE BUFFERS STA MSGBBF STA MSGCBF STA STACAL LXI H,LOGSTR ;CLEAR THE LOG ENTRY TABLES SHLD LOGCMP ;START OF NON-COMPRESSED LOG MVI M,<15> ;CR TO HELP IN LOG LOOK UP INX H SHLD LOGPNT MOV M,A ;STORE OUT A NULL BYTE FOR END OF LOG MVI A,35 STA BAUD ;SET OUTPUT BAUD AT ABOUT 15 WPM STA BAUDI ;LIKEWISE WITH INPUT BAUD STA RTTYMD ;START UP MODE IS MORSE (NON-ZERO) LXI H,RUSART+1 ;SET UP RTTY/MORSE USART MVI M,302 ;2 STOP BITS, 5 DATA BITS, 16*CLK@45 BAUD (60 WPM) ;RESTART FOR CONTROL C START2: LXI SP,STACK ;FIX STACK POINTER MVI A,5 ;CLEAR THE MORSE CODE OUTPUT DRIVER OUTPUT XRA A STA KYFIFO ;CLEAR THE KYBD CHAR COUNT STA OTFIFO ;CLEAR THE OUTPUT BUFFER STA TWIDTH ;SET FOR CR INITIALIZATION STA PNFIFO ;INITIALIZE THE PRINTER CHAR CNT CLRMD: LXI H,CMMND ;COMMAND MODE OR DATA MODE MVI M,0 ;MAKE IT DATA MODE 1$: CALL IOALL ;OVERLAY I/O MVI B,<15> ;CR CALL PPAK JNZ 1$ ;RETRY IF ERROR 2$: CALL IOALL ;OVERLAY I/O MVI B,'# ;SET UP B REG TO PROMPT CALL PPAK ;PRINT THE PROMPT JNZ 2$ ;RETRY IF ERROR ;FALL THROUGH INTO MAIN PROGRAM LOOP ;MONITOR ENTRY AND SUPERVISOR MAIN TASK RESTRT: CALL IOALL ;CHECK PENDING I/O ON KYBRD,PNTR,CODE OUT LDA CMMND ;TEST THE MODE BYTE ORA A ;SET FLGS JNZ CMMNDR ;ENTER COMMAND MODE CALL IDLE ;NON COMMAND CHARACTER FEEDTHROUGH JMP RESTRT ;AND LOOP (?) ALONG ;ASCII TABLE OF DATA ASCTAB: .ASCII /ABCDEFGHIJKLMNOPQRSTUVWXYZ/ .ASCII /1234567890/ .ASCII ;-.,?/:()'"; .ASCII /;/ .BYTE 15 ;END OF MESSAGE(CR/LF) ASCEND: .BYTE 12 ;END OF WORK(CR/LF) ;MORSE TABLE:1=DASH,0=DOT,WITH A 1 TO END THE CHAR MORTAB: .BYTE 140 ;A .BYTE 210 ;B .BYTE 250 ;C .BYTE 220 ;D .BYTE 100 ;E .BYTE 50 ;F .BYTE 320 ;G .BYTE 10 ;H .BYTE 40 ;I .BYTE 170 ;J .BYTE 260 ;K .BYTE 110 ;L .BYTE 340 ;M .BYTE 240 ;N .BYTE 360 ;O .BYTE 150 ;P .BYTE 330 ;Q .BYTE 120 ;R .BYTE 20 ;S .BYTE 300 ;T .BYTE 60 ;U .BYTE 30 ;V .BYTE 160 ;W .BYTE 230 ;X .BYTE 270 ;Y .BYTE 310 ;Z .BYTE 174 ;1 .BYTE 74 ;2 .BYTE 34 ;3 .BYTE 14 ;4 .BYTE 4 ;5 .BYTE 204 ;6 .BYTE 304 ;7 .BYTE 344 ;8 .BYTE 364 ;9 .BYTE 374 ;0 .BYTE 206 ;- .BYTE 126 ;. .BYTE 316 ;, .BYTE 62 ;? .BYTE 224 ;(SLASH) .BYTE 342 ;: .BYTE 266 ;( .BYTE 266 ;) .BYTE 172 ;' .BYTE 112 ;" .BYTE 252 ;; .BYTE 124 ;END OF MESSAGE(CR/LF) MOREND: .BYTE 26 ;END OF WORK(CR/LF) ;RTTY TABLE, 5 LSB ARE DATA; MSB=1 IF IN LTRS, =0 IF FIGS RTYTAB: .BYTE 203 ;A .BYTE 231 ;B .BYTE 216 ;C .BYTE 211 ;D .BYTE 201 ;E .BYTE 215 ;F .BYTE 232 ;G .BYTE 224 ;H .BYTE 206 ;I .BYTE 213 ;J .BYTE 217 ;K .BYTE 222 ;L .BYTE 234 ;M .BYTE 214 ;N .BYTE 260 ;O .BYTE 226 ;P .BYTE 227 ;Q .BYTE 212 ;R .BYTE 205 ;S .BYTE 220 ;T .BYTE 207 ;U .BYTE 266 ;V .BYTE 223 ;W .BYTE 235 ;X .BYTE 225 ;Y .BYTE 221 ;Z .BYTE 027 ;1 .BYTE 023 ;2 .BYTE 001 ;3 .BYTE 012 ;4 .BYTE 020 ;5 .BYTE 025 ;6 .BYTE 007 ;7 .BYTE 006 ;8 .BYTE 030 ;9 .BYTE 026 ;0 .BYTE 003 ;- .BYTE 034 ;. .BYTE 034 ;, .BYTE 031 ;? .BYTE 035 ;/ .BYTE 016 ;: .BYTE 017 ;( .BYTE 022 ;) .BYTE 015 ;' .BYTE 021 ;" .BYTE 066 ;; .BYTE 010 ;CR RTYEND: .BYTE 002 ;LF ;SUBROUTINE TO PUT DATA IN A GENERAL STACK ;STACK POINTER IN H&L, DATA IN B, BUFFER SIZE IN C ;RETURNS WITH A=0 IF NO ERROR, =NON ZERO IF ERROR ENTPAK: MOV A,M ;CHAR COUNT TO A DCR C ;COMPUTE CHARACTER LOCATIONS IN BUFFER CMP C ;DON'T OVERFLOW THE BUFFER JZ ERROFL ;FULL... ADI 1 ;BUMP THE COUNT MOV M,A ;CHAR COUNT UPDATE TO MEM ADD L ;LOW POINTER ADDED TO A MOV L,A ;GET L VALUE UPDATED TO L REG JNC OK ;IF A CARRY, FIX THE H INR H ;FIX H REG OK: MOV M,B ;CHAR TO MEMORY XRA A ;CLEAR THE A REG RET ;DONE ERROFL: ADI 1 ;SET A REG NON ZERO ERR RETURN RET ;SYSTEM ERROR, SYSTEM MUST FIX!!!! ;POP SUBROUTINE: ENTERED WITH POINTER IN H&L, SIZE IN B POP: MOV A,M ;GET CHAR COUNT TO A REG SUI 1 ;DECREMENT RC ;IF BUFFER IS EMPTY, FORGET POP MOV M,A ;RESTORE THE NEW COUNTER TO BUFFER JMP POPY ;GET INTO POPLOP LOOP ;LOOP FOR POP POPLOP: INX H ;POINT AT CHAR TO POP MOV C,M ;GET CHAR TO C REG DCX H ;POINT TO NEW LOCATION MOV M,C ;PUT CHAR IN MEMORY MOV A,B ;B TO A REG SUI 1 ;SUBTRACT 1 POPY: RZ ;DONE? MOV B,A ;RESTORE B REG INX H ;RECOVER FROM THE DECREMENT POSITION JMP POPLOP ;NEXT PAIR POP ;SUBROUTINE TO WAIT A UNIT CODE TIME, DESTROYS A,B,C TICK: LXI H,BAUD ;POINT AT CONST MVI C,250 ;MULTIPLIER CONSTANT WAIT2: MOV B,M ;CONST TO B WAIT1: DCR B ;COUNT IT DOWN DELAY JNZ WAIT1 DCR C ;MULTIPLY IT JNZ WAIT2 CALL IOTERM ;OVERLAP WITH TERMINAL I/O RET ;DELAY OVER!!!! ;DELAY, USED FOR 8 SLICE DECODING OF INPUT CODE TICKI: LXI H,BAUDI ;POINT AT CONSTANT MVI C,25 ;8 TIMES FASTER THAN TICK JMP WAIT2 ;FINISH TICKI IN TICK ROUTINE ;SUBROUTINE TO GENERATE A DOT AND POST SPACE, DESTROYS A,B,C,H,L DOT: MVI A,2 ;SET KEY DOWN OUTPUT ;TURN ON THE KEY (DOWN) JMP FINDOT ;FINISH THE DOT IN THE DASH ROUTINE ;SUBROUTINE GENERATES DASH ITS POST SPACE, DESTROYS A,B,C,H,L DASH: MVI A,2 ;SET KEY DOWN OUTPUT ;KEY DOWN CALL TICK ;DASH CALL TICK FINDOT: CALL TICK ;ENTERED HERE TO FINISH A DOT XRA A ;CLEAR THE A REG OUTPUT ;KEY UP CALL TICK RET ;MONITOR TASK SUBROUTINE FOR HANDLING COMMANDS FROM THE KEYBOARD CMMNDR: CALL UNPAK ;GET CHAR FROM KEYBOARD JZ CMMNDR ;IF NO CHAR, WAIT... CPI ESC ;WAS IT ANOTHER ESC? JZ CLRMD ;EXIT ON ESC CPI '1 ;RECEIVER ENABLE? JZ INPREC ;SET FOR INPUT OF RTTY OR MORSE CPI '2 ;OPEN "STATION" CALL BUFFER JZ STABUF CPI '3 ;BUFFER "A" OPERATION? JZ ABUF CPI '4 ;BUFFER "B" OPERATION? JZ BBUF CPI '5 ;BUFFER "C" OPERATION? JZ CBUF CPI '6 ;WAS IT A QUIZ COMMAND? JZ QUIZ ;YES, ENTER THE QUIZ MODE CPI '7 ;WAS IT RANDOM QUIZ COMMAND? JZ RQUIZ ;YES, ENTER THE RQUIZ MODE CPI '8 ;XMIT "STATION" THEN "B" JZ MSGSTB CPI '9 ;XMIT "STATION" THEN "C" JZ MSGSTC CPI '0 ;LOG QSO JZ LOG CPI '- ;FIND LOG ENTRY JZ FIND CPI 'C ;MORSE CODE MODE SET UP? JZ CODEST ;SET UP MORSE MODE CPI 'G ;GAIN SPACE BY COMPRESSING LOG JZ COMPRS CPI 'M ;MAINT BRANCH JZ 774 ;TO BRK2 IN MDP CPI 'P ;PRINT OUT THE LOG JZ GETLOG CPI 'R ;SET UP RTTY MODE? JZ RTTYST ;SET UP FOR RTTY CPI 'S ;SET THE CLOCK JZ SCLOCK CPI 'T ;PRINT OUT THE TIME JZ CLOCK CPI 'W ;WAS IT A NEW WPM CONSTANT? JZ WPM ;YES, LOAD NEXT CHAR AS THE CONST CPI 'X ;WAS IT A XMIT TEST? JZ TEST ;YES, SEND THE BUFFER UNTIL ESC IS TYPED CPI 'Z ;Z TO ZAP, CLEAR THE LOG JZ CLRLOG ;IF HERE A BAD COMMAND MVI B,QUEST ;QUESTION MARK CALL PPAK ;TO THE PRINTER FIFO JMP CMMNDR ;TRY FOR A VALID COMMAND CHAR ABUF: LXI H,MSGABF ;A BUFFER MVI C,MSGSZ ;SIZE OF BUFFER JMP BUFDO ;DO OPERATION BBUF: LXI H,MSGBBF ;B BUFFER MVI C,MSGSZ ;SIZE OF BUFFER JMP BUFDO CBUF: LXI H,MSGCBF ;C BUFFER MVI C,MSGSZ ;SIZE OF BUFFER JMP BUFDO STABUF: LXI H,STACAL ;"STATION" BUFFER MVI C,7 ;SIZE OF BUFFER (6 CHAR + COUNTER) BUFDO: PUSH H ;SAVE POINTER PUSH B ;PRINT THE CURRENT BUFFER XRA A ;SET SOH=0 STA SOH CALL DMPSUB ;PRINT 1$: CALL UNPAK ;BUT WHAT TO DO? JZ 1$ ;WAIT FOR COMMAND POP B POP H CPI 'L ;LOAD? JZ LDNXT CPI 'S ;XMIT? JZ 2$ PUSH H PUSH B MVI B,QUEST ;QUEST MARK CALL PPAK ;FOR BAD COMMAND JMP 1$ 2$: CALL SNDNX ;XMIT JMP CLRMD ;CLEAR COMMAND MODE ;SET UP RTTY VS MORSE CODE MODE RTTYST: XRA A ;ENTERED HERE IF RTTY MODE LXI H,RUSART+1 ;SET UP SERIAL MODES MVI M,5 ;ENABLE TRANS & REC JMP STRTTY CODEST: MVI A,1 ;ENTERED HERE IF CODE MODE STRTTY: STA RTTYMD ;STORE MODE BYTE JMP CLRMD ;XMIT "STATION" THEN BUFFER B MSGSTB: LXI H,STACAL ;XMIT "STATION" CALL SNDNX LXI H,MSGBBF ;XMIT B CALL SNDNX JMP CLRMD ;XMIT "STATION" THEN BUFFER C MSGSTC: LXI H,STACAL ;XMIT "STATION" CALL SNDNX LXI H,MSGCBF ;XMIT C CALL SNDNX JMP CLRMD ;SUBROUTINE PRINT & XMIT BUFFER (POINTED TO BY HL) IN CODE SNDNX: PUSH H ;SAVE FOR XMIT PUSH B XRA A ;PRINT STA SOH CALL DMPSUB ;PRINT POP B POP H MVI A,1 ;CODE SET UP STA SOH CALL DMPSUB ;XMIT THE MESSAGE BUFFER RET ;EXIT ;ROUTINE TO LOAD THE MESSAGE BUFFER POINTED TO BY HL, SIZE IN C LDNXT: MVI M,0 ;ZERO CHAR COUNT PUSH H PUSH B 1$: CALL UNPAK ;GET KEYBOARD CHAR JZ 1$ ;WAIT POP B POP H CPI ESC ;END OF INPUT? JZ CLRMD ;EXIT PUSH H PUSH B CPI DELETE ;RUB OUT? JNZ 2$ MOV A,M ;GET CHAR COUNT ORA A ;SET FLAGS JZ 1$ ;NOTHING TO DELETE! DCR M ;DELETE ONE CHAR JMP 1$ 2$: MOV B,A ;CHAR TO B FOR ENTPAK, SIZE IN C CALL ENTPAK ;TEST FOR BUFFER OVERFLOW MVI B,ERCHAR ;TELL USER IF OVERFLOW CNZ PPAK ;BUT ONLY IF ERROR JMP 1$ ;LOOP ;SUBROUTINE FOR USER ERROR INDICATION ;BYPASSES CHARACTER POSITION COUNTER FOR THE TERMINAL WHOOPS: STATUS ;YES, GET PRINTER STATUS ANI 1 ;TBMT MASK JZ WHOOPS ;WAIT MVI A,ERCHAR ;SET ERROR INDICATION PRINT RET ;SUBROUTINE TO MOVE THE MESSAGE BUFFER CONTENTS TO LOCATION ;DEFINED BY THE SOH LOCATION. 0=PRINTER 1=TRANSMITTER ;BUFFER ADDRESS IN HL DMPSUB: XRA A CMP M ;A MESSAGE STORED? RZ ;NO MOV C,M ;YES, CHAR COUNT TO C REG 4$: INX H ;POINT AT NEXT CHAR 5$: MOV B,M ;GET CHAR TO REG B LDA SOH ;CHECK MODE ORA A ;SET FLAGS PUSH H ;SAVE POINTERS PUSH B JNZ 1$ ;XMIT MODE CALL PPAK ;PRINT MODE JMP 2$ 1$: LXI H,OTFIFO ;POINT AT XMIT BUFFER MVI C,BUFOUT ;BUFFER SIZE SET UP CALL ENTPAK ;XMIT 2$: JNZ 3$ ;ERROR POP B ;RESTORE POINTERS POP H DCR C ;UPDATE CHAR COUNT JNZ 4$ RET 3$: CALL IOALL ;CLEAR IO ERR POP B POP H JMP 5$ ;QUIZ ROUTINE TO SEND RANDOM CHARACTERS FROM THE MESSAGE BUFFER ; AND THEN WAIT FOR A KEYBOARD RESPONSE. IF THE KEYBOARD RESPONSE ;IS INCORRECT THE CHARACTER IS REPEATED. ^C WILL CAUSE ; AN EXIT FROM THIS ROUTINE. QUIZ: LXI H,RTTYMD ;SET FOR MORSE NOT RTTY MVI M,1 ;SO MODE IS NON ZERO ;OUTPUT A K TO TELL USER THE QUIZ IS IN PROGRESS MVI A,K ;CHAR TO A REG QZAGN1: LXI H,QTEMP ;PUT IN SAVE LOCATION ;IN CASE THE ANSWER IS INCORRECT MOV M,A ;AND THE LETTER MUST BE REPEATED. QZAGN: CALL OUTPAK ;TO CODE BUFFER QUIZWT: CALL IOALL ;OVERLAY I/O CALL UNPAK ;ANY RESPONSE YET? JNZ NXTRAN ;YES, GET THE ANSWER LXI H,RANCHR ;NO, UPDATE THE RANDOM POINTER INR M ;+1 JMP QUIZWT ;LOOP FOR A RESPONSE ;USER HAS RESPONDED NXTRAN: LXI H,QTEMP ;TEST THE RESPONSE CMP M ;SAME CHARACTER? JNZ CHRERR ;NO, REPEAT THE CHAR CALL NXTCHR ;YES, DO UP THE NEXT CHARACTER JMP QZAGN1 ;RUN WITH THE NEW CHAR CHRERR: MVI A,DELETE ;NO, ERROR BY USER SO TELL HIM CALL OUTPAK MVI B,DELSYM ;BACKSLASH ON THE TERMINAL CALL PPAK MVI A,BLANK ;SPACE IN TIME CALL OUTPAK LXI H,QTEMP ;REPEAT CHAR MOV A,M ;RESEND THE MISSED CHARACTER JMP QZAGN ;ROUTINE TO SEND RANDOM 5 CHARACTER CODE GROUPS GENERATED FROM ;CHARACTERS IN THE MESSAGE BUFFER. RQUIZ: LXI H,RTTYMD ;SET TO MORSE NOT RTTY MVI M,1 ;MORSE IS NON ZERO MODE LXI H,GRPCNT ;POINT AT GROUP OF 5 COUNTER MVI M,WIDTH/6 ;# OF GROUPS BEFORE A LF NEWGRP: LXI H,CHRCNT ;POINT AT CHAR/GROUP COUNTER MVI M,5 ;# OF CHARACTERS IN GROUP ;THIS SEGMENT OF CODE GENERATES A RANDOM NUMBER BY ADDING SUCCESIVELY ;ONE ELEMENT OF THE SERIES 0,1,2,3......376,377,0,1 TO THE CURRENT ;RANDOM NUMBER MODULO 377 OCTAL. RQZAGN: LDA RANCHR+1 ;INDEX TO A REG INR A ;+1 STA RANCHR+1 ;SAVE NEW INDEX LXI H,RANCHR ;POINT AT OLD RANDOM NUMBER ADD M ;ADD TO INDEX FOR NEW RANDOM # MOV M,A ;SAVE RANDOM NUMBER CALL NXTCHR ;GENERATE THE NEXT CHARACTER STA QTEMP ;SAVE IT CALL OUTPAK ;CODE IT OUT CALL OTPUT ;OVERLAY I/O LXI H,QTEMP ;GET IT BACK MOV B,M CALL PPAK ;PRINT IT CALL IOTERM ;OVERLAY TERMINAL I/O ;DONE WITH A GROUP? LXI H,CHRCNT ;POINT AT CHAR COUNTER DCR M ;-1 JNZ RQZAGN ;NOT DONE, GET THE NEXT CHAR MVI A,BLANK ;DONE WITH THE GROUP SO CALL OUTPAK ;OUTPUT SOME FREE AIR CALL OTPUT ;OVERLAY THE I/O MVI B,BLANK ;ALSO DO IT CALL PPAK ;ON THE PRINTER CALL PNTR ;OVERLAY I/O LXI H,GRPCNT ;DECREMENT THE GROUP COUNTER DCR M ;-1 JNZ NEWGRP ;NOT DONE, GET THE NEXT GROUP MVI B,<15> ;CR ON THE PRINTER CALL PPAK ;TO PRINT BUFFER CALL PNTR ;OVERLAY I/O JMP RQUIZ ;LOOP UNTIL ^C ;ROUTINE TO GET RANDOM CHAR FROM THE MESSAGE BUFFER, USED ;BY BOTH QUIZ AND RQUIZ. NXTCHR: LDA MSGABF ;CHECK CHAR COUNT NOT ZERO ORA A ;SET FLAGS JNZ 1$ ;OK, CONTINUE LXI H,ERRMSG ;NO DATA, ERROR CALL PRTMSG JMP CLRMD 1$: LDA RANCHR ;GET RANDOM COUNTER ;TO MAKE A RANDOM POINTER LXI H,MSGABF ;POINT AT # OF CHAR IN MSSG BUFFER NOKRAN: CMP M ;HAS RANCHR GOTTEN TOO LARGE? JZ OKRAN ;MAYBE NOT... JC OKRAN ;NO SUB M ;YES, SUBTRACT JMP NOKRAN ;AND TEST AGAIN OKRAN: ANA A ;SET FLGS, ZERO? JNZ AOKRAN ;NO ADI 1 ;MAKE NON ZERO AOKRAN: ADD L ;POINT AT A CHARACTER IN THE MSSG BUFFER MOV L,A ;UPDATE L REG JNC RAOK ;L OVERFLOW? INR H ;YES, BUT FIXED IN H REG RAOK: MOV A,M ;NEW CHAR TO A REG RET ;ROUTINE TO TEST OUTPUT, XMIT MESSAGE BUFFER "A" UNTIL ^C IS TYPED TEST: LXI H,SOH ;SET UP FOR SEND MODE MVI M,1 LXI H,MSGABF ;A BUFFER CALL DMPSUB ;SEND THE BUFFER JMP TEST ;AND LOOP.... ;CODE TO LOAD A NEW WPM CONSTANT INTO BAUD WPM: CALL UNPAK ;GET CHAR FROM KEYBOARD JZ WPM ;WAIT FOR CHAR ANI 37 ;MASK FOR 5 VALID BITS RLC ;MULTIPLY BY 2 ORI 1 ;SET THE LSB STA BAUD ;CONSTANT TO BAUD LOCATION JMP CLRMD ;EXIT TO MONITOR LOOP ;ROUTINE TO GET CHAR FROM KEYBOARD FIFO ;RETURNS WITH CHAR IN A REG, A REG=0 IF NO CHAR UNPAK: CALL IOALL ;TRY TO FINISH PENDING I/O LXI H,KYFIFO ;POINT AT KEYBOARD STACK XRA A ;CLEAR THE A REG ADD M ;CHAR COUNT TO A REG RZ ;IF EMPTY, RETURN A REG=0 INX H ;POINT AT CHAR MOV E,M ;GET CHAR TO E REG TEMP DCX H ;POINT AT KEYBOARD FIFO CALL POP ;OUT OF FIFO, RETURNS A=0 ADD E ;CHAR TO A REG AND FLGS RET ;RETURN WITH A REG=CHAR ;SUBROUTINE TO WAIT FOR THE TBMT FLAG WAITMT: STATUS ;GET TBMT FLAG ANI 1 JZ WAITMT ;WAIT.... RET ;OK, HAVE TBMT ;KEYBOARD HANDLER SUBROUTINE KYBD: STATUS ;GET THE SERIAL LINE STATUS ANI 2 ;MASK RZ ;NEXT TASK ;PUT KEYBOARD CHARACTER IN KYBD FIFO READ ;GET CHAR FROM KEYBOARD TO A REG ANI 177 ;GET RID OF ASCII PARITY BIT CPI ETX ;CONTROL C JNZ NETX ;NO CALL WAITMT ;SEND OUT ^C MVI A,'^ PRINT CALL WAITMT ;WAIT FOR TBMT MVI A,'C PRINT CALL WAITMT ;WAIT FOR TBMT JMP START2 ;GO RESTART FROM ALMOST ZERO NETX: LXI H,KYFIFO ;POINT AT KYFIFO CPI 141 ;LOWER CASE TRANSPOSE TO JC STUFF ;UPPER CASE CPI 173 ;BUT GET ONLY THE JNC STUFF ;LOWER CASE SUI 40 ;TRANSPOSE HERE STUFF: MOV B,A ;CHAR TO B REG MVI C,BUFSKY ;BUFFER SIZE TO C REG CALL ENTPAK ;PUT CHAR IN BUFFER CNZ WHOOPS ;IF OVERFLOW, TELL THE USER ;FALL THROUGH AND RETURN IN NEXT ROUTINE ;PUT CHAR IN PRINTER FIFO FROM B REG ;RETURNS WITH A REG=0 IF NO ERROR, =ERCHAR IF ERROR PPAK: LXI H,PNFIFO ;POINT AT PNFIFO MVI C,BUFSPN ;BUFFER SIZE TO C REG CALL ENTPAK ;PUT CHAR IN FIFO RET ;END OF THE KEYBOARD HANDLER TASK ;PRINTER HANDLER SUBROUTINE TASK PNTR: STATUS ;GET THE PRINTER STATUS ANI 1 ;MASK FOR TBMT RZ ;IF BUSY, TRY SOMETHING ELSE LDA TWIDTH ;CR/LF ? CPI 0 JZ 1$ ;AT 0, DO CR JP PRT2 ;>0, PRINT CHARS, IF ANY TO PRINT CPI -1 ;<0, DECODE FURTHER JZ 2$ ;DO LF LXI H,TFILL ;FILL CHARS REQUIRED? XRA A ADD M ;IF ZERO, DONE JZ 3$ ;SO RESET DCR M ;NOT DONE, BUT ONE LESS FILL MVI A,FILCHR ;FILL CHAR JMP PNT1 1$: MVI A,<15> ;CR JMP PNT1 2$: MVI A,<12> ;LF JMP PNT1 3$: MVI M,FILL ;RESET FILL COUNT LXI H,TWIDTH ;RESET CHAR/LINE COUNT MVI M,WIDTH RET MVI M,WIDTH ;RESET PRINT POSITION COUNT RET PRT2: LXI H,PNFIFO ;POINT AT CHAR COUNT XRA A ;CLEAR THE A REG ADD M ;CHAR COUNT TO A REG AND FLGS RZ ;NOTHING TO PRINT, NEXT TASK ;IF HERE THERE IS PRINTING TO BE DONE!!!!!!! ;POINT AT CHAR TO PRINT AND PRINT IT INX H MOV E,M ;CHAR TO E REG TEMP ;NOW UPDATE THE CHAR COUNT LXI H,PNFIFO ;POINT AT CHAR COUNT CALL POP ;RIPPLE THE FIFO MOV A,E ;CHAR BACK TO A REG CPI <12> ;IS IT A LF? JZ INCRLF ;YES, INSERT A CRLF CPI <15> ;IS IT A CR? JZ INCRLF ;YES, INSERT A CRLF CPI DELETE ;IS IT A DELETE? JZ DEL ;YES, INSERT A BACKSLASH CPI ESC ;IS IT AN ESC? JNZ PNT1 ;NO MVI A,ESCSYM ;YES, SUBSTITUTE A PRINTABLE CHAR PNT1: LXI H,TWIDTH ;UPDATE PRINT POSITION DCR M ;-1 PRINT ;PRINT THE CHAR RET ;DONE, PRINTED A CHAR INCRLF: LXI H,TWIDTH ;SET FOR CRLF NEXT PASS MVI M,0 RET DEL: MVI A,DELSYM ;SUBSTITUTE A PRINTABLE CHARACTER PRINT RET ;END TO THE PRINTER TASK ;THIS SUBROUTINE TRANSLATES A REG TO OUTPUT MODE AS DEFINED BY THE ;TABLE POINTED TO BY THE DE REG PAIR ;COMPUTE THE DISPLACEMENT IN ASCTAB XLATER: LXI H,ASCTAB ;POINT AT ASCTAB THISIT: CMP M ;IS THIS THE CHAR? JZ CONVT ;GO CONVERT THE CHAR INX H ;TRY NEXT CHAR MOV B,A ;SAVE THE CHAR IN B REG TEMPORARILY MVI A,ASCEND/400 ;GET HIGH LIMIT TO A FOR COMPARE CMP H ;PAST END OF TABLE? JC NTFUND ;PAST END OF TABLE AND NO MATCH MVI A,ASCEND ;GET LOW LIMIT TO A FOR COMPARE CMP L ;PAST END OF TABLE? JC NTFUND ;PAST END OF TABLE AND NO MATCH ;IF HERE, STILL IN TABLE. TRY CONTENTS AGAIN. MOV A,B ;RETURN CHAR TO A REG JMP THISIT ;LOOP FOR NEXT TABLE ENTRY CHECK CONVT: MVI A,ASCTAB ;TWOS COMPLEMENT TO BC CMA MOV C,A MVI A,ASCTAB/400 CMA MOV B,A ;1'S COMPL INX B ;2'S COMPL DAD B ;HL IS NOW RELATIVE DISPLACEMENT DAD D ;HL IS NOW ABSOLUTE OUTPUT LOCATION MOV A,M ;REPLACEMENT CHAR TO A REG RET ;CODE IN A REG RETURN NTFUND: MVI A,200 ;CHAR NOT FOUND LOAD OUT A 200 RET ;ERROR RETURN ;THIS SUBROUTINE TRANSLATES A REG TO PRINT MODE ;ENTERED WITH SOURCE TABLE START ADDRESS IN DE, END ADDRESS IN BC XLATR2: LXI H,MORTAB ;POINT AT MORTAB THIS: CMP M ;IS THIS THE CHAR? JZ MCONVT ;YES, GO CONVERT THE CHAR INX H ;TRY NEXT CHAR PUSH PSW ;SAVE CHAR TMP MOV A,B ;GET HIGH LIMIT TO A FOR COMPARE CMP H ;PAST END OF TABLE? JC NTFND ;YES, WITH NO MATCH MOV A,C ;GET LOW LIMIT TO A FOR COMPARE CMP L ;PAST END OF TABLE? JC NTFND ;YES, NO MATCH ;IF HERE, STILL IN TABLE. TRY CONTENTS AGAIN POP PSW ;RETURN CHAR TO A REG JMP THIS ;LOOP FOR NEXT TABLE ENTRY CHECK MCONVT: MOV A,E ;COMPUTE HL-DE+ASCTAB CMA ;TO GET ABSOLUTE ASCII TABLE ADDRESS MOV E,A MOV A,D CMA MOV D,A ;1'S COMPLEMENT INX D ;2'S COMPLEMENT DAD D ;RELATIVE DISPLACEMENT MVI D,ASCTAB/400 MVI E,ASCTAB DAD D ;ABSOLUTE ADDRESS MOV A,M ;REPLACEMENT CHAR TO A REG RET ;ASCII CODE IN A REG RETURN NTFND: POP PSW ;RESTORE STACK MVI A,BLANK ;CHAR NOT FOUND, LOAD OUT A SPACE RET ;ERROR RETURN ;SUBROUTINE TASK TO XMIT IN RTTY OR MORSE ;XMIT OUTPUT CODE HANDLER OTPUT: LXI H,OTFIFO ;POINT AT STACK XRA A ;CLEAR THE A REG ADD M ;CHAR COUNT TO A AND FLGS RZ ;NEXT TASK, IF NOTHING TO DO INX H ;GET CHAR TO STACK TMP MOV A,M PUSH PSW ;SET UP TO POP THE STACK LXI H,OTFIFO ;POINT AT STACK CALL POP ;POP THE STACK LDA RTTYMD ;RTTY OR MORSE? ORA A JZ OTRTTY ;ZERO IS RTTY MODE POP PSW ;GET THE DATA CPI DELETE ;ERROR CHARACTER? JNZ OTPUT1 ;NO, GO TRANSLATE MVI D,10 ;YES, DO 8 DOTS OTERR: CALL DOT ;1 DOT DCR D ;-1 THE COUNT JNZ OTERR ;NOT DONE....DO IT AGAIN JMP OUTEND ;DONE, POP AND EXIT OTPUT1: LXI D,MORTAB ;SET UP FOR MORSE CODE OUTPUT CALL XLATER ;TRANSLATE MOV D,A ;SAVE THE CHAR IN D REG CPI 200 ;IS IT A BAD CHAR (OR A SPACE)? JNZ GOODCH ;CHAR OK, SO DO IT UP RIGHT ;IF HERE GENERATE A CHARACTER SPACE MVI D,6 ;SET UP FOR 7 UNITS DELAY(ONE UNIT IN CODE ELEMENT) SPACE: CALL TICK ;WAIT ONE UNIT DCR D ;DECREMENT THE UNIT COUNTER JNZ SPACE ;LOOP UNTIL DONE JMP OUTEND GOODCH: CPI 200 ;IF A=200 THEN DONE JZ OUTEND ;DONE! JNC DSH ;DOT OR DASH? CALL DOT ;YES A DOT, SO KEY OUT A DOT JMP OTLOOP ;NEXT SYMBOL DSH: CALL DASH ;MUST BE A DASH, SO KEY A DASH OTLOOP: MOV A,D ;GET THE CHAR BACK TO THE A REG ANI 177 ;THROW OUT THE USED BIT RLC ;ROTATE IN AN UNUSED BIT MOV D,A ;SAVE THE NEW IMAGE IN D JMP GOODCH ;LOOP FOR OTHER SYMBOLS OUTEND: CALL TICK ;INTER LETTER SPACE CALL TICK RET ;NEXT TASK OTRTTY: POP PSW ;XMIT IN RTTY, GET CHAR LXI D,RTYTAB ;SET UP FOR RTTY CALL XLATER ;TO BAUDOT PUSH PSW ;SAVE CHAR TMP ANI 200 ;CHECK SHIFT LXI H,LETFIG CMP M ;CORRECT SHIFT? JZ 1$ ;YES MOV M,A ;NO, UPDATE SHIFT MEMORY JC 2$ ;WAS LTRS, BUT NOW FIGS MVI A,LTRS ;WAS FIGS, BUT NOW LTRS CALL XMTRTY JMP 1$ 2$: MVI A,FIGS CALL XMTRTY 1$: POP PSW ;CHAR TO A CALL XMTRTY RET XMTRTY: PUSH PSW ;SAVE CHAR TMP, XMIT IN RTTY 1$: LDA RUSART+1 ;STATUS OF ANI 1 ;TBMT JZ 1$ ;WAIT IF NEEDED POP PSW STA RUSART ;CHAR OUT ON USART SERIAL RET ;END OF THE OUTPUT ENCODED DRIVE TASK SUBROUTINE ;KEYBOARD DECODER FOR NON COMMAND MODE IDLE: CALL UNPAK ;SEE IF CHARACTER IS AVAILABLE RZ ;IF NO CHAR RDY, A=0 SO RETURN CPI ESC ;IS CHARACTER AN ESC? JZ IDLE1 ;YES ;ENTERED HERE TO PUT CHARACTER FROM A REGISTER INTO CODE BUFFER OUTPAK: LXI H,OTFIFO ;OUTPUT IN CODE THEN SET UP FOR ENTPAK MOV B,A ;DATA IN B REG MVI C,BUFOUT ;SIZE IN C REG CALL ENTPAK CNZ WHOOPS ;BUFFER FULL, TELL USER RET ;DONE IDLE1: LXI H,CMMND ;SET FOR COMMAND MODE MVI M,1 ;MODE=1 RET ;SUBROUTINE TO SERVICE RECEIVER INPUT IN MORSE OR RTTY INPREC: LDA RTTYMD ;RTTY OR MORSE? ORA A JZ INPRTY ;ZERO MODE IS RTTY INPEND: CALL IOTERM ;OVERLAY TERMINAL I/O SENSE ;GET CODE INPUT LINE JZ INPEND ;NOTHING PENDING, WAIT LXI H,INCHAR ;POINT AT HOLDING REG MVI M,1 ;SET UP TO SHIFT IN MORSE INTIME: LXI H,TIMER ;POINT AT TIMER REG MVI M,1 ;INITIALIZE FOR TIME=0 INSENS: CALL TICKI ;WAIT FOR PART OF A BAUD (1/8) LXI H,TIMER ;UPDATE TIMER INR M ;+1 SENSE ;KEY DOWN? JNZ INSENS ;WAIT FOR KEY UP ;IF HERE, KEY IS NOW UP MVI E,0 ;SET E=0 FOR DOT, FIX LATER IF DASH MOV A,M ;TIMER TO A REG (8*# OF BAUD) CPI 14 ;DASH-N%=DOT+N% JNC INDASH ;IF DASH, SERVICE DASH ;SEE IF CLOCK MUCH TOO SLOW FOR DOT CPI 5 ;SHOULD BE A 10 IDEAL JNC INPOK ;CLOCK IS GOOD ENOUGH CALL ABAUD ;HL=A*BAUDI/10 ADD L ;NEW BAUDI TO A AND FLAGS JNZ OKBAUD ;ZERO IS A NO-NO! INR A ;A =1 FOR NON-ZERO OKBAUD: STA BAUDI ;THENCE TO MEMORY JMP INPOK ;IF HERE, SYMBOL IS DASH, UPDATE BAUDI FOR TRACKING INDASH: CPI 35 ;CLOCK TOO FAST? JC OKDASH ;NO. MVI B,0 ;A=A/3 WHEN DONE HERE DIV3: INR B ;ALWAYS GOES ONCE SUI 3 ;TAKE IT OUT JNC DIV3 ;LOOP UNTIL DONE MOV A,B ;A NOW A/3 CALL ABAUD ;HL=(A/3)*BAUDI/10 MOV A,L ;BAUDI TO A AND STA BAUDI ;TO MEMORY OKDASH: MVI E,1 ;SET E=1 FOR DASH INPOK: LXI H,INCHAR ;POINT AT CHAR HOLDING REG MOV A,M ;GET PARTIAL CHAR TO A REG RAL ;SHIFT UP ONE BIT ANI 376 ;JUNK THE OLD CARRY BIT ORA E ;BRING IN NEW SYMBOL FROM E REG MOV M,A ;NEW PARTIAL CHAR TO INCHAR ;TIME THE INTERSPACE TO FIND WHAT TYPE IT IS. LXI H,TIMER ;RESET THE TIMER MVI M,1 ;TIMER RESET UPTIME: CALL TICKI ;DELAY 1/8 OF A BAUD TIME SENSE ;GET THE KEY STATUS JNZ INTIME ;KEY DOWN, GET NEXT SYMBOL LXI H,TIMER ;UPDATE THE TIME INR M ;+1 MOV A,M ;GET TIMER TO A FOR COMPARE CPI 377 ;END OF MESSAGE? JNZ NOTEOM ;KEEP LOOPING, MVI B,<12> ;END OF MESSAGE, CR-LF-CR-LF CALL PPAK CALL PPAK JMP INPEND ;TO TOP OF LOOP UNTIL MONITOR SAYS OTHERWISE NOTEOM: CPI 50 ;END OF WORD? 70 IDEAL JNZ ENDLET ;NO. MVI A,1 ;YES, OUTPUT A SPACE JMP MPAK ;DO IT OUT RIGHT. ENDLET: CPI 15 ;END OF LETTER? 30 IDEAL JNZ UPTIME ;NO, KEEP TIMING THE UP TIME LXI H,INCHAR ;POINT AT HOLDING REG MOV A,M ;MORSE FROM HOLDING REG TO A REG MVI M,1 ;RESET THE HOLDING REG FOR NEXT CHAR MPAK: STC ;SET C FLAG FOR GUARD BIT 1$: RAL JNC 1$ ;IF NO LEFT GUARD, LOOP ORA A ;IF ZERO, ERROR CHAR WAS SEEN..... JNZ 2$ ;NOT ERR CHAR MVI A,DELSYM ;SET UP FOR DELETE SYMBOL JMP 3$ 2$: LXI D,MORTAB ;SET UP FOR MORSE CONVERSION LXI B,MOREND CALL XLATR2 ;MORSE TO ASCII 3$: MOV B,A ;SET UP TO PRINT WITH PPAK CALL PPAK ;PRINT THE CHARACTER JMP UPTIME ;KEEP TIMING THE UP TIME ;RTTY SECTION FOR BAUDOT INPUT INPRTY: CALL IOTERM ;OVERLAY WITH CONSOLE IO LDA RUSART+1 ;GET STATUS OF ANI 2 ;DA BIT OF USART JZ INPRTY ;WAIT FOR DA LXI H,LETFIG ;POINT AT SHIFT MEMORY LDA RUSART ;CHAR TO A CPI LTRS ;SHIFT? JNZ 1$ ;NO MVI M,200 ;YES, SET SHIFT MODE JMP INPRTY 1$: CPI FIGS ;SHIFT? JNZ 2$ ;NO MVI M,0 ;YES, SET SHIFT MODE JMP INPRTY 2$: ORA M ;CHAR "OR" WITH SHIFT BIT LXI D,RTYTAB ;SET UP TO CONVERT TO ASCII LXI B,RTYEND CALL XLATR2 MOV B,A ;SET UP TO PRINT CALL PPAK JMP INPRTY ;LOOP UNTIL MONITOR SAYS STOP ;SUBROUTINE TO GET BAUDI TO HL AND MULTIPLY BY A REG AND DIVIDE BY 10 ABAUD: LXI H,BAUDI MOV L,M ;BAUDI TO L MVI H,0 ;ZAP H FOR DOUBLE PREC MOV C,L ;COPY BC=HL MOV B,H DCR A ;ONCE ALREADY IN HL MULT: DAD B ;ADD BC TO HL, A TIMES DCR A JNZ MULT ;FALL THROUGH AT A*HL CALL DSHFTR ;/2 CALL DSHFTR ;/4 CALL DSHFTR ;/10 RET ;SUBROUTINE TO DOUBLE PRECISION SHIFT THE HL RIGHT DSHFTR: PUSH PSW ;DO NOT DESTROY A AND FLGS ORA A ;CLEAR CY MOV A,H ;HIGH BYTE RAR ;LSB TO CY MOV H,A ;REST BACK TO H MOV A,L ;LOW BYTE RAR ;MSB FROM H LSB, L LSB DUMPED MOV L,A ;REST TO L POP PSW RET ;SUBROUTINE USED TO OVERLAY I/O FOR COMPUTE BOUND ROUTINES IOALL: CALL OTPUT ;CODE OUTPUT LINE IOTERM: CALL KYBD ;TERMINAL INPUT CALL PNTR ;TERMINAL OUTPUT RET ;ROUTINE TO ZERO THE LOG CLRLOG: LXI H,LOGSAF ;MAKE SURE CALL PRTMSG CLRWYT: CALL UNPAK ;WAIT FOR ANSWER JZ CLRWYT CPI <15> ;CR TO CONTINUE JNZ CLRLOG LXI H,LOGSF2 ;DOUBLE SURE CALL PRTMSG CLRWT2: CALL UNPAK ;WAIT FOR ANSWER JZ CLRWT2 CPI <15> ;CR TO CONTINUE JNZ CLRLOG ;ZERO OUT THE LOG LXI H,LOGSTR ;LOG AREA START MVI M,<15> ;CR TO HELP IN FILE LOOK UP INX H SHLD LOGPNT ;START IS NOW END MVI M,0 ;NULL BYTE TO END THE LOG JMP CLRMD ;SUBROUTINE TO TYPE AN ASCII STRING POINTED TO BY HL ; ASCII STRING IS TERMINATED BY A ZERO (NULL) BYTE PRTMSG: XRA A ;CLEAR A AND FLAGS ADD M ;CHAR TO A AND FLAGS RZ ;NULL BYTE, ALL DONE MOV B,A ;CHAR TO B REG FOR PPAK PUSH H ;SAVE POINTER CALL PPAK ;CHAR TO PRINT FIFO POP H ;GET POINTER JZ PRTOK ;IF NOT ERROR CONTINUE PUSH H ;SAVE POINTER CALL IOTERM ;DO SOME I/O TO EMPTY THE BUFFER POP H ;GET THE POINTER JMP PRTMSG ;TRY AGAIN PRTOK: INX H ;NEXT CHAR JMP PRTMSG ;LOOP ;SUBROUTINE TO CONVERT A REG TO TWO DECIMAL ASCII DIGITS AND RETURN ;WITH THEM IN REGS A&B (MSD IN B). A MUST BE LESS THAN 100. BINDEC: MVI B,-1 ;PRESET HIGH DIGIT COUNT MSDLOP: INR B ;BUMP TENS COUNT SUI 10. ;AND REMOVE 10. JNC MSDLOP ;LOOP IF NUMBER LARGE ENOUGH ADI 10. ;FIX IF NUMBER NOT LARGE ENOUGH ORI 60 ;MAKE REMAINDER ASCII PUSH PSW ;THEN SAVE AWHILE MOV A,B ;GET MSD TO MAKE ORI 60 ;ASCII MOV B,A ;RESTORE TO B POP PSW ;LSD BACK RET ;SUBROUTINE TO PICK UP # OF CHARACTERS DEFINED BY THE B REG ;AND PUT THEM IN BLOCK OF MEMORY STARTING AT HL FROM BLOCK OF ;MEMORY STARTING AT DE. HL RETURNS POINTING TO NEXT FREE SPACE. ;NULLS ARE BLANKED, REPLACED WITH <40> BLKIO: LDAX D ;BYTE TO A ORA A ;SET UP Z FLAG TO FIND NULLS JNZ 1$ MVI A,<40> 1$: MOV M,A ;TO BUFFER INX H ;NEXT FREE SPACE INX D ;NEXT SOURCE CHAR DCR B ;ANYMORE BYTES? JNZ BLKIO ;YES, LOOP RET ;SUBROUTINE TO CONVERT ASCII BA REG TO BINARY IN A REG DECBIN: ANI 17 ;SAVE LSB DATA PUSH PSW ;NEED A AWHILE MOV A,B ;TO FIX ANI 17 ;THE MSD MOV B,A POP PSW INR B ;SET Z FLAG IF B=0 DCR B RZ ;DONE SINCE MSD=0 1$: ADI 10. ;MULTIPLY BY MSD DCR B ;DONE? JNZ 1$ ;NO, RET ;YES ;SUBROUTINE TO PRINT THE # OF CHAR IN REG C FROM BLOCK AT HL CHRPAK: MOV B,M ;GET CHAR PUSH B ;SAVE COUNTERS PUSH H CALL PPAK ;TO FIFO JNZ 1$ ;ERR POP H ;NO ERR POP B INX H ;NEXT LOCATION DCR C ;DONE? RZ ;YES JMP CHRPAK ;NOT YET 1$: CALL IOALL ;CLEAR I/O ERR POP H POP B JMP CHRPAK ;TRY AGAIN ;SUBROUTINE TO READ CLOCK AND SET UP REGS FOR TIME RCLOCK: LDA 40162 ;GET HOUR FROM THE FOREGROUND CALL BINDEC ;CONVERT TO ASCII IN B-A LXI H,TIME ;POINT AT REGS MOV M,B ;MSD INX H MOV M,A ;LSD HOUR LDA 40163 ;GET MIN FROM THE FOREGROUND CALL BINDEC ;TO ASCII INX H MOV M,B ;MSD OF MIN INX H MOV M,A ;LSD OF MIN RET ;ROUTINE TO SET THE CLOCK, FALLS THROUGH TO ROUTINE TO READ CLOCK SCLOCK: LXI H,TIME MVI B,4 ;4 DIGITS 2$: PUSH H PUSH B 1$: CALL UNPAK ;GET CHAR FOR TIME JZ 1$ ;WAIT FOR DIGIT POP B POP H MOV M,A ;SAVE CHAR INX H ;NEXT POSITION DCR B ;GOT THE 4 DIGITS? JNZ 2$ ;NOT YET LXI H,TIME ;YES GET THE HOUR MOV B,M ;MSD INX H MOV A,M ;LSD CALL DECBIN ;CONVERT STA 40162 ;SET CLOCK HOURS INX H ;NOW DO MIN MOV B,M ;MSD INX H MOV A,M ;LSD CALL DECBIN ;CONVERT STA 40163 ;SET CLOCK MINS ;ROUTINE TO PRINT THE CURRENT TIME CLOCK: CALL RCLOCK ;SET TIME REG CALL STIME ;PRINT TIME MSG JMP CLRMD ;DONE ;CLEAR THE BUFFER AREA FOR CALL AND RST LOG: LXI H,STATNE ;FIRST BUFFER START MVI B,HSRST+3-STATNE ;# OF BYTES 10$: MVI M,<40> ;BLANK A BYTE INX H DCR B ;ONE LESS BYTE JNZ 10$ ;PICK UP THE TIME OF QSO CALL RCLOCK ;SET TIME ;PICK UP STATION CALL FROM "STATION" LXI H,STACAL ;POINT AT BUFFER MOV A,M ;ONLY 6 LETTERS IN CALL SIGNS CPI 7 ;TOO MANY CHARS? JC 1$ ;NO MVI A,6 ;YES 1$: ORA A ;ANY CHARS? JZ 8$ ;NO. LXI D,STATNE ;LOG BUFFER POINTER MOV B,A ;CHAR COUNT TO B INX H ;POINT AT DATA 2$: MOV A,M ;GET CHAR STAX D ;TO DESTINATION INX H ;NEXT SOURCE INX D ;NEXT DESTINATION DCR B ;ONE LESS CHAR JNZ 2$ ;UNLESS DONE ;PICK UP PROBABLE RS/RST FROM MSG BUFFER C 8$: LXI H,MSGCBF+1 ;POINT AT DATA IN BUFFER LXI D,HSRST ;STORE HERE MVI B,3 ;ONLY 3 CHARS MOV A,M ;ALLOW A LEADING BLANK IN THE BUFFER CPI <40> JNZ 5$ ;NOT A BLANK, GO PROCESS INX H ;BLANK, DUMP IT 3$: MOV A,M ;GET CHAR CPI <40> ;BLANK? JZ 4$ 5$: STAX D ;SAVE IN RS/RST INX H INX D DCR B ;DONE? JNZ 3$ ;GET EITHER 3 CHARS OR A BLANK ;USE HIS RS/RST AS ESTIMATE OF MY RS/RST 4$: MVI B,3 ;PICK UP 3 CHARS LXI D,HSRST ;FROM HERE LXI H,MYRST ;AND PUT HERE CALL BLKIO ;LEAVE OLD ZONE AS ESTIMATE OF NEW ZONE ;PRINT OUT PROBABLE LOG ENTRY PROLOG: CALL STIME ;TIME OF QSO CALL SSTAT ;STATION WORKED CALL SHRST ;HIS RS/RST CALL SZONE ;HIS ZONE CALL SMRST ;MY RS/RST 6$: LXI H,CORMSG ;CORRECT? CALL PRTMSG 5$: CALL UNPAK ;CR IF CORRECT, N IF ERROR JZ 5$ CPI <15> ;DONE, GO LOG ENTRY JZ ENTLOG CPI 'N ;ERROR, UPDATE LINES JZ FIXLOG JMP 6$ ;WRONG ANSWER FIXLOG: LXI H,INSMSG ;TELL USER HOW TO FIX LOG CALL PRTMSG CALL SSTAT ;CALL SIGN CHECK LXI H,STATNE MVI B,6 CALL LOGFIX CALL SHRST ;HIS RS/RST CHECK LXI H,HSRST MVI B,3 CALL LOGFIX CALL SZONE ;CALL ZONE CHECK LXI H,HSZNE MVI B,2 CALL LOGFIX CALL SMRST ;MY RS/RST CHECK LXI H,MYRST MVI B,3 CALL LOGFIX JMP PROLOG ;RECHECK ;COMPLETE THE LOG ENTRY BY PUTTING IN ACTUAL LOG ENTLOG: LHLD LOGPNT ;GET LOG LINE LXI D,TIME ;LOG TIME MVI B,4 ;4 CHARS CALL BLKIO MVI M,<40> ;SPACE TO MAKE READABLE INX H LXI D,STATNE ;LOG STATION CALL MVI B,6 CALL BLKIO MVI M,<40> INX H LXI D,HSRST ;LOG HIS RST MVI B,3 CALL BLKIO MVI M,<40> INX H LXI D,HSZNE ;LOG HIS ZONE MVI B,2 CALL BLKIO MVI M,<40> INX H LXI D,MYRST ;LOG MY RST MVI B,3 CALL BLKIO MVI M,<15> ;END OF LOG ENTRY INX H MVI M,0 ;NEW END OF LOG SHLD LOGPNT ;NEXT LOG ENTRY WILL START HERE MVI D,- ;LOG BUFFER END FLAG MVI E,- DAD D LXI H,ENDERR ;POINT AT END WARNING MESSAGE CC PRTMSG ;BUT PRINT MESSAGE ONLY IF ERROR JMP CLRMD ;NEXT COMMAND ;COMMAND TO PRINT LOG GETLOG: CALL PNTLOG JMP CLRMD ;SUBROUTINE TO PRINT OUT THE LOG PNTLOG: LXI H,LHDMSG ;LOG HEADER CALL PRTMSG LHLD LOGCMP ;START OF NON COMPRESSED LOG DATA CALL PRTMSG RET ;SUBROUTINES FOR HANDLING MESSAGES FOR LOG I/O OPERATOR HELP STIME: LXI H,TIMMSG ;TIME OF DAY FOR QSO CALL PRTMSG MVI C,4 LXI H,TIME CALL CHRPAK RET SSTAT: LXI H,STAMSG ;STATION WORKED CALL PRTMSG MVI C,6 LXI H,STATNE CALL CHRPAK RET SHRST: LXI H,HRSMSG ;HIS RS/RST CALL PRTMSG MVI C,3 LXI H,HSRST CALL CHRPAK RET SZONE: LXI H,ZNEMSG ;HIS ZONE CALL PRTMSG MVI C,2 LXI H,HSZNE CALL CHRPAK RET SMRST: LXI H,MRSMSG ;MY RS/RST CALL PRTMSG MVI C,3 LXI H,MYRST CALL CHRPAK RET ;SUBROUTINE TO PATCH A LOG FIELD IF NEEDED, ENTERED ;WITH FIELD SIZE IN B, ADDRESS IN HL. RETURNS ON CR OR FULL FIELD. LOGFIX: MOV C,B ;SAVE SIZE IN CASE OF RUBOUT PUSH B PUSH H 1$: CALL UNPAK ;CR OR NEW DATA JZ 1$ POP H POP B CPI <15> ;CR? RZ ;NO CHANGE EXIT PUSH PSW ;SAVE CHAR PUSH B PUSH H MVI D,<40> 2$: MOV M,D ;BLANK A BYTE INX H ;NEXT DCR B ;UNLESS DONE JNZ 2$ POP H POP B POP PSW ;GET THE CHAR BACK ;SERVICE RUBOUTS HERE 4$: CPI DELETE ;RUBOUT? JNZ 5$ ;NO, MOV A,C ;YES, BUT LEGAL? CMP B ;LEGAL IF NOT EMPTY FIELD JZ 6$ ;NOT LEGAL DCX H ;REMOVE THE LAST CHAR FROM FIELD INR B JMP 6$ 5$: MOV M,A ;CHAR TO FIELD INX H ;NEXT LOCATION DCR B ;FIELD FULL RZ ;YES, EXIT 6$: PUSH B ;NO PUSH H 3$: CALL UNPAK ;GET NEXT CHAR JZ 3$ POP H POP B CPI <15> ;CR? RZ ;IF YES, DONE JMP 4$ ;THIS SECTION DOES A LOG LOOK UP TO LOCATE A QSO FIND: LXI H,LOGSTR ;POINT AT LOG LXI D,STACAL ;POINT AT CALL SIGN LDAX D ;PICK UP CHAR COUNT ORA A ;SET FLGS MOV C,A ;SAVE CALL CHAR COUNT IN C REG JNZ 2$ 1$: LXI H,ERRMSG ;NOTHING TO FIND EXIT CALL PRTMSG JMP CLRMD ;EXIT 2$: XRA A ADD M ;ANYTHING IN LOG? JZ 1$ ;NO 5$: LXI D,STACAL+1 ;POINT AT DATA IN CALL MVI B,0 ;ZERO CHAR COUNTER 4$: XRA A CMP M ;NULL BYTE IN LOG? JZ NOTFND ;YES, END OF LOG MVI A,<40> ;DELIMITING BLANK? CMP M INX H ;BUMP POINT NOW, FOR LATER JNZ 4$ ;FIND THE DELIMITER FIRST LDAX D ;LOG AND CALL EQUAL? CMP M JZ 3$ ;YES INX H ;NO, TRY NEXT CHAR JMP 4$ 3$: INR B ;ONE LESS CHAR MOV A,B ;ALL CHAR IN CALL DONE? CMP C JNZ 6$ ;NOT YET INX H ;IF DONE, THIS CHAR IS A BLANK MVI A,<40> CMP M JZ FOUND ;BLANK, DONE 6$: INX H ;NO, KEEP TRYING INX D MOV A,M ;GET LOG CHAR ORA A ;NULL BYTE? JZ NOTFND LDAX D ;CALL CHAR TO A CMP M ;LOG EQUAL CALL? JZ 3$ ;YES, NEXT CHAR JMP 5$ ;NO, RESET CALL AND KEEP LOOKING ;TELL USER THE RESULT OF THE SEARCH FOUND: INR A ;CLEAR THE Z FLG NOTFND: PUSH PSW ;SAVE FLG ;ENLIGHTEN THE USER LXI H,FMSG ;STATION " CALL PRTMSG LXI H,STACAL ;CALL MOV C,M ;# OF CHAR INX H ;PICK UP DATA CALL CHRPAK ;PRINT THE CALL SIGN POP PSW ;FOUND OR NOT FOUND? JNZ FND ;FINISH FOUND LXI H,NFMSG ;NOT FOUND IN LOG CALL PRTMSG JMP CLRMD ;EXIT FND: LXI H,FNDMSG ;FOUND MESSAGE CALL PRTMSG JMP CLRMD ;COMMAND ROUTINE TO PRINT AND THEN COMPRESS THE LOG TO THE FORM ;CALL<40>CALL<40>CALL......CALL<40><0> COMPRS: CALL PNTLOG ;UPDATE THE LOG PRINTOUT LHLD LOGCMP ;POINT AT NOT YET COMPRESSED LOG MOV D,H ;POINT DE AT START OF COMPRESSED OUTPUT MOV E,L INX H ;SKIP INITIAL CR <15> 3$: LXI B,5 ;SET BC=5 FOR DAD MOV A,M ;GET LOG CHAR TO TEST FOR END LOG CPI <0> ;END? JZ 4$ ;YES, EXIT DAD B ;NO, SKIP HHMM<40> 1$: MOV A,M ;GET CALL LETTERS STAX D ;AND SAVE INCLUDING <40> DELIMITER CPI <40> ;END OF CALL SIGN YET? INX D ;NEXT PAIR INX H JZ 2$ ;END OF CALL SIGN, EXIT JMP 1$ 2$: MOV A,M ;FIND END OF LINE CPI <15> INX H ;SET UP FOR NEXT CHAR JNZ 2$ ;END OF LINE NOT FOUND YET JMP 3$ ;END OF LINE NOW FOUND 4$: MOV H,D ;CLOSE AND RESET LOG MOV L,E MVI M,<15> ;CR TO START NON-COMPRESSED LOG SHLD LOGCMP ;NEW NON-COMPRESSED LOG START INX H MVI M,<0> ;NULL TO CLOSE LOG SHLD LOGPNT ;NEW FREE SPACE POINTER JMP CLRMD ;MESSAGES TO BE USED BY THE PRTMSG SUBROUTINE LHDMSG: .ASCII <15>/ STATION WORKED MY/<15> .ASCII /TIME CALL RST ZN RST/<15> .ASCIZ /----------------------/ FMSG: .ASCIZ <15>/STATION / NFMSG: .ASCII / NOT/ FNDMSG: .ASCIZ / FOUND IN LOG./<15> STAMSG: .ASCIZ <15>/STATION WORKED: / HRSMSG: .ASCIZ <15>%HIS RS/RST: % ZNEMSG: .ASCIZ <15>/ZONE OF STATION WORKED: / MRSMSG: .ASCIZ <15>%MY RS/RST IS: % CORMSG: .ASCIZ <15>/IS THE ABOVE CORRECT? (TYPE N IF NO, CR TO LOG) / INSMSG: .ASCII <15>/IF DATA IS CORRECT "CR", ELSE TYPE / .ASCIZ /CORRECT DATA UNTIL "CR" OR FIELD IS FULL./ LOGSF2: .ASCII <15>/REALLY??/ LOGSAF: .ASCIZ <15>/ZAP THE LOG? (CR TO CONTINUE, ^C TO ABORT)/ TIMMSG: .ASCIZ <15>/TIME IS / ERRMSG: .ASCIZ <15>/BUFFER IS EMPTY!!!!/<15> ENDERR: .ASCIZ <15>/!!! WARNING !!! LOG AREA IS ALMOST FULL !!!/ ;***ANYTHING BEFORE THIS POINT CAN BE IN PROM*** ;***EVERYTHING AFTER THIS POINT MUST BE IN RAM*** .=40400 ;USE NEXT RAM BLOCK ABOVE MDP BLOCK LETFIG: .BYTE 0 ;RTTY SHIFT MEMORY:=200 FOR LTRS, =0 FOR FIGS RTTYMD: .BYTE 0 ;=0 IF IN RTTY MODE, ELSE MORSE CODE MODE CMMND: .BYTE 0 ;0=NORMAL MODE, OTHERWISE COMMAND MODE TWIDTH: .BYTE 0 ;WHEN BYTE IS ZERO, GENERATE A CR/LF TFILL: .BYTE 0 ;COUNTER FOR FILL CHARACTERS SOH: .BYTE 0 ;SUBMODE FOR DMPSUB 0=PRINT 1=SEND BAUD: .BYTE 0 ;WPM CONSTANT (SEE HEADING ON PROGRAM) BAUDI: .BYTE 0 ;INPUT WPM VALUE (GETS MODIFIED) TIMER: .BYTE 0 ;TIME BAUD *8 (10 OCTAL) COUNTER RANCHR: .BYTE 5 ;RANDOM COUNTER REGISTER FOR QUIZ AND RQUIZ .BYTE 161 ;SAVE LOCATION FOR ADDITIVE ELEMENT FOR ;THE RANDOM # GENERATOR. MUST IMMEDIATELY FOLLOW RANCHR QTEMP: .BYTE 0 ;TEMP MEMORY FOR QUIZ AND RQUIZ CHARACTER OUTPUT GRPCNT: .BYTE 0 ;COUNTER FOR RQUIZ CHAR GROUP CHRCNT: .BYTE 0 ;COUNTER FOR RQUIZ CHAR/GROUP INCHAR: .BYTE 0 ;INPUT CHAR HOLDING REG KYFIFO: .=.+BUFSKY ;INPUT BUFFER PNFIFO: .=.+BUFSPN ;PRINTER BUFFER OTFIFO: .=.+BUFOUT ;OUTPUT BUFFER FOR CODE MSGABF: .=.+MSGSZ ;MESSAGE BUFFER A MSGBBF: .=.+MSGSZ ;MESSAGE BUFFER B MSGCBF: .=.+MSGSZ ;MESSAGE BUFFER C STACAL: .=.+7 ;STATION BEING CALLED, 6 CHAR AND COUNTER TIME: .=.+4 ;ASCII TIME STRING STATNE: .=.+6 ;STATION CALL FOR LOG HSRST: .=.+3 ;STATION WORKED RST HSZNE: .=.+2 ;STATION WORKED ZONE MYRST: .=.+3 ;MY RST .=.+200 ;200 OCTAL BYTES FOR THE STACK STACK: .BYTE 0 ;FOR THE 8080 CPU LOGPNT: .=.+2 ;FREE SPACE POINTER FOR LOG LOGCMP: .=.+2 ;START OF NON COMPRESSED LOG LOGSTR: ;REST OF MEMORY IS AVAILABLE FOR LOG STORAGE .END 100400 ;100400 KEEPS DATA I/O PROM BURNER HAPPY