; MORSE2.ASM Version 2.0 By G.F.Reding 05 Jun 86 ; ; ; Morse Program for 8008 ; Typed from BYTE Magazine Oct 1976 Page 57 ; By R.J.S. on 2/2/79 ; ; (RJS may have also done the conversion to 8080 code. ) ; ; (RJS, you neglected to give credit to original author. ) ; ;-------------------------------------------------------------- ; ; Original source code by: Bruce Filgate ; Components Application Engineering ; 01/29/76 Digital Equipment Corporation ; Marlbough, Massachusetts ; ; --- for their logic products MPS product line. ; ;-------------------------------------------------------------- ; ; The disk copy of the source code didn't contain the comments ; as was in the original source shown in Byte Magazine, so the ; magazine article was obtained from a university's microfilm ; library, then the comments were typed back in. If you refer ; to the original article, you may notice that I may have made ; some changes/improvements which I will also try to document. ; ;-------------------------------------------------------------- ; ; Commands available: ; ; Return to normal keyboard mode (exit) ; L Load message buffer - until L ; P Print the message buffer contents ; S Send message buffer, as Morse code ; T Test - do S - until typed ; W WPM speed change. Next char is speed ; (See table/notes re speed character) ; In load mode, remove previous chars ; back to the beginning of the buffer ; ;-------------------------------------------------------------- ; ; Weighting is 1 dash to 3 dots. ; ; Buffers (stacks) use the first byte as a "character count". ; ; The terminal bell will ring whenever a buffer over is caused ; by the user, and the character which caused such overflow is ; discarded. ; ; Normally the keyboard data is transmitted out translated but ; the command mode, as shown in the list, may change this for ; for other functions/effects. ; ; Delete key is used to edit buffer, and represents the error ; code in the immediate mode. ; ; Originally, the lsb of the input byte from I/O channel 2 was ; reserved to the sense line for code input. The byte on I/O ; channel 4 was used for code output. The key sense for code ; input and output was ground for key down condition and logic ; high for the key up condition. ; ;-------------------------------------------------------------- ; FALSE EQU 0 ; For conditional assembly TRUE EQU NOT FALSE ; IMSAI EQU FALSE ; Using imsai with lights IMSPT EQU 0FFH ; Addr for imsai lights ; ;-------------------------------------------------------------- ; ; Machine specific ; ; The following are your console/terminal DATA and STATUS ports ; which your terminal is connected to. (If your console is not ; on a serial port, but is memory mapped, you will have to add ; your own appropriate routines. In the latter case, please do ; make such an addition an assembly conditional, as is now the ; option for an IMSAI's lights, and bump version number of this ; program.) ; CDATA EQU 60H ; Console data port CSTAT EQU 61H ; Console status port CRBF EQU 01H ; Receive buffer full CTBE EQU 04H ; Transmit buffer empty mask ; ; The following are the interface unit DATA ports you will use. ; The interface is your user built CW modulator/demodulator, or ; commercial unit such as Heathkit Terminal Interface, HD-3030. ; MORINP EQU 70H ;62H ; Morse input data port MOROUT EQU 70H ;62H ; Morse output data port ; ; ; Buffer sizes ; MSGSZ EQU 0FFH ; Msg holding buffer BUFOUT EQU 30H ; Code output buffer BUFSKY EQU 0FFH ; Keyboard buffer BUFSPN EQU 30H ; Printer buffer ; ; ; Other constants ; WIDTH EQU 80 ; Printer width CR EQU 0DH ; Carriage return LF EQU 0AH ; Linefeed ERCHAR EQU 7 ; Error char (bell) ESC EQU 1BH ; Enter cmd mode char (escape) ESCSYM EQU 24H ; Echo "$" for esc char QUEST EQU '?' ; Bad cmd char (question mark) DELETE EQU 7FH ; Delete char DELSYM EQU 5CH ; Printable char for delete ETX EQU 3 ; Control c exit to strt UPARRO EQU 5EH ; Ascii ^ for control chars BLANK EQU 20H ; Space ; EXCHAR EQU 'X'-40H ; Quit program char (ctrl x) ; ; ;-------------------------------------------------------------- ; ; Beginning of the program. ; ; ORG 100H ; STRT: LXI H,MSSGBF ; Clear the msg buffer MOV M,A LXI H,BAUD ; Set output baud MVI M,0FFH CALL INCLH ; Same with input baud MVI M,0FFH ; ; ; Restart for ^C and clear reg A. ; STRT1: XRA A ; Zero LXI H,KYFIFO ; Clear keyboard char count MOV M,A LXI H,OTFIFO ; Clear output buffer count MOV M,A LXI H,CMMND ; Initialize the mode byte MOV M,A CALL INCLH ; Initialize char count MOV M,A ; Set for crlf initialization LXI H,PNFIFO ; Clear printer char count MOV M,A ; To clear tone and other bits OUT MOROUT ; Output to morse data port IF IMSAI CMA OUT 0FFH CMA ENDIF ; ; ; Monitor entry and supervisor main task ; RESTRT: CALL INPEND ; Try code input line CALL KYBD ; Try the keyboard task CALL PNTR ; Try the printer task LXI H,CMMND ; Point to mode byte XRA A ; Zero the reg and flags ADD M ; Add mode byte to reg a JNZ CMMNDR ; Enter command mode CALL IDLE ; Non-command char feedthrough CALL OTPUT ; Anything morse to output JMP RESTRT ; And loop ; ; ; Exit to cp/m system ; EXIT: JMP 0 ; Do warmboot to the system ; ; ; ASCII TABLE ; ASCTAB: DB 'ABCDE' ; A DB 'FGHIJ' ; To DB 'KLMNO' ; Z DB 'PQRST' DB 'UVWXYZ' ; ; NUMERALS ; DB '12345' ; 1 to 9 DB '6789' ; ; SPECIAL CHARACTERS ; DB 30H ; Zero DB 2DH ; Minus DB 2EH ; Period DB 2CH ; Comma DB 3FH ; Question mark DB 2FH ; Slash DB 3AH ; Colon DB 28H ; ( DB 29H ; ) DB 27H ; ' DB 22H ; * DB 0AH ; End of message cr/lf DB 0AH ; End of work (cr/lf) ; ASCEND: DB 3BH ; Semicolon ; ; ; MORSE TABLE ; MORTAB: DB 60H ; A DB 88H ; B DB 0A8H ; C DB 90H ; D DB 40H ; E DB 28H ; F DB 0D0H ; G DB 08H ; H DB 20H ; H DB 78H ; J DB 0B0H ; K DB 48H ; L DB 0E0H ; M DB 0A0H ; N DB 0F0H ; O DB 68H ; P DB 0D8H ; Q DB 50H ; R DB 10H ; S DB 0C0H ; T DB 30H ; U DB 18H ; V DB 70H ; W DB 98H ; X DB 0B8H ; Y DB 0C8H ; Z DB 7CH ; 1 DB 3CH ; 2 DB 1CH ; 3 DB 0CH ; 4 DB 04H ; 5 DB 84H ; 6 DB 0C4H ; 7 DB 0E4H ; 8 DB 0F4H ; 9 DB 0FCH ; 0 DB 86H ; - DB 56H ; DB 0CEH ; , DB 32H ; ? DB 94H ; / DB 0E2H ; : DB 0B6H ; ( DB 0B6H ; ) DB 7AH ; ' DB 4AH ; * DB 54H ; Eom cr/lf DB 16H ; Eow cr/lf ; MOREND: DB 0AAH ; ; ; ; ; Subroutine to put data in a general stack ; This stack pointer in HL, data in B, buffer size in C ; Returns A = zero if no error else A = erchar if error ; ENTPAK: MOV A,M ; Char count to a DCR C ; Decr size CMP C ; Test for zero JZ ERROFL ; Jump if full ADI 1 ; Else bump the count MOV M,A ; Put back into memory ADD L ; Add low pointer to a MOV L,A ; Put new value back into l 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 to non zero err return RET ; System err, system must fix.. ; ; ; Pop subroutine. Entered with pointer in HL, size in B ; PXP: MOV A,M ; Get counter to a SUI 1 ; Decrement MOV M,A ; Restore the new counter JMP PXPY ; Get into poplop loop ; ; ; Loop for pop ; PXPLOP: CALL INCLH ; Point at char to pop MOV C,M ; Get char to c CALL DCRLH ; Point to new location MOV M,C ; Put char in memory MOV A,B ; Put b into a SUI 1 ; Subtract 1 ; PXPY: RZ ; Done? MOV B,A ; Restore reg b CALL INCLH ; Recover from decrmt position JMP PXPLOP ; Next pair pop ; ; ; Subroutine to increment the HL regs ; INCLH: INR L ; Bump the l RNZ ; Return if no carry INR H ; Bump the h on carry RET ; Done ; ; ; Subroutine to decrement the HL regs ; DCRLH: MOV A,L ; Reg l to a SUI 1 ; Decrement the a MOV L,A ; Restore a to l RNC ; Return if no borrow DCR H ; Fix the h after a borrow RET ; Done ; ; ; Subroutine to wait a unit code time. ; Destroys A,B, and C. ; TICK: LXI H,BAUD ; Point at constant MVI C,28H ; Multiplier constant ; WAIT2: MOV B,M ; Constant to b ; WAIT1: DCR B ; Count it down delay JNZ WAIT1 DCR C ; Multiply it JNZ WAIT2 CALL KYBD ; Overlap with keyboard input CALL PNTR ; Same with the printer RET ; Delay over... ; ; ; Delay. Used for 8 slice decoding of input code. ; TICKI: LXI H,BAUDI ; Point at constant MVI C,05H ; 8 times faster than tick JMP WAIT2 ; Finish this in tick routine ; ; ; Subroutine to generate a dot and post space. ; Destroys A,B,C, and HL. ; DOT: MVI A,0FFH ; Set all bits (turn on - down) OUT MOROUT ; Output to morse data port IF IMSAI CMA OUT 0FFH CMA ENDIF JMP FINDOT ; Finish dot in dash routine ; ; ; Subroutine generates dash its post space. ; Destroys A,B,C, and HL. ; DASH: MVI A,0FFH ; Set all bits (turn on - down) OUT MOROUT ; Output to morse data port IF IMSAI CMA OUT 0FFH CMA ENDIF CALL TICK ; Dash CALL TICK ; Dash ; ; FINDOT: CALL TICK ; Entered here to finish a dot XRA A ; Clear all bits (turn off - up) OUT MOROUT ; Output to morse data port IF IMSAI CMA OUT 0FFH CMA ENDIF CALL TICK RET ; ; ; Monitor task subroutine for handling commands from keyboard. ; CMMNDR: CALL UNPAK ; Get char from keyboard JZ CMMNDR ; If nothing, then wait CPI ESC ; Another cmd mode char (esc)? JZ CLRMD ; If so, exit CPI 'L' ; Was it a load? JZ LDNXT ; If so, go load a message CPI 'P' ; Was it a print? JZ PRT ; If so, go print msg buffer CPI 'S' ; Was it a send of buffer? JZ SNDNX ; If so, send the message CPI 'T' ; Was it a test? JZ TEST ; If so, send the buffer ; Until an esc is typed CPI 'W' ; Was it a new wpm constant? JZ WPM ; If so, load next char/constant ; ; If here a bad command ; MVI B,QUEST ; Bad cmd char (question mark) CALL PPAK ; To the printer fifo JMP CMMNDR ; Try for a valid cmd char ; ; ; Routine to load the message buffer. ; LDNXT: LXI H,MSSGBF ; Point at current char pointer MVI M,0 ; Zero the count ; LDNXT1: CALL UNPAK ; Get keyboard char JZ LDNXT1 ; If nothing then wait CPI ESC ; End of the input message? JZ CLRMD ; Then exit, end of message MOV B,A ; Char to b for entpak LXI H,MSSGBF ; Point at the message buffer CPI DELETE ; Delete command? JNZ LDNXT2 ; If no XRA A ; If yes, clear reg a and flags ADD M ; Get count to a and flags JZ LDNXT1 ; Buffer empty, a no-no SBI 1 ; Decrement counter MOV M,A ; Restore it to memory JMP LDNXT1 ; Loop ; LDNXT2: MVI C,MSGSZ ; Get buffer size CALL ENTPAK ; Char to buffer ; Test if full CNZ WHOOP ; If full, tell user JMP LDNXT1 ; Loop until esc ; ; ; Subroutine for user error indication ; WHOOP: IN CSTAT ; Get status ANI CTBE ; Transmit buffer empty mask JZ WHOOP ; If not, then loop MVI A,ERCHAR ; Error char (bell) OUT CDATA ; Send it RET ; ; ; Routine to print the message buffer ; PRT: LXI H,SOH ; Set up print submode MVI M,0 ; Zero CALL DMPSUB ; Print the message buffer JMP CLRMD ; Exit to the supervisor ; ; ; Subroutine to move the message buffer contents to location ; defined by the SOH location. 0 = Printer 1 = Sender. ; DMPSUB: LXI H,MSSCNT ; Point at temp char pointer MVI M,0 ; Clear the pointer ; PRT1: CALL PNTR ; Try to finish printing CALL KYBD ; Try to finish keyboard input CALL OTPUT ; Try to finish transmission LXI H,MSSGBF ; Get char counter, check for 0 XRA A ; Clear reg a and flags ADD M ; Add in the char count RZ ; If no message, exit LXI H,MSSCNT ; Check count on xfer chars CMP M ; Is it all transfered? RZ ; If so, then exit MOV B,M ; Else get char count to b INR B ; Bump the count MOV M,B ; Restore it to memory ; ; ; Fetch the char from the message buffer ; PR4: MVI A,MSSGBF AND 0FFH ; Compute pointers ADD M ; By adding in buffer offset MOV L,A ; Set up the l, h yet to go MVI H,MSSGBF/256 ; H is set if no carry from l JNC PR2 ; No carry INR H ; Fix for the l carry ; PR2: MOV B,M ; Char to b XRA A ; Clear reg a LXI H,SOH ; Get the submode ADD M ; Submode in reg a and flags JNZ SOH1 ; Send submode CALL PPAK ; Print submode JMP PR3 ; SOH1: LXI H,OTFIFO ; Point at output buffer MVI C,BUFOUT ; Size to c CALL ENTPAK ; Xfer to the buffer ; PR3: JZ PRT1 ; Loop if no error CALL PNTR ; Yes, overlay the i/o CALL KYBD ; Try to finish keyboard input CALL OTPUT ; Try to finish transmission LXI H,MSSCNT ; Reset h for error recovery JMP PR4 ; Try the char again ; ; ; Routine to ship out the buffer in code ; SNDNX: LXI H,SOH ; Set up send mode MVI M,1 ; For dmpsub routine CALL DMPSUB ; Send the message buffer JMP CLRMD ; Exit ; ; ; Routine to test output, ship until escape is typed ; TEST: LXI H,SOH ; Set up for send mode MVI M,1 CALL DMPSUB ; Send the buffer CALL UNPAK ; Char from keyboard JZ TEST ; Nothing yet, do it again CPI ESC ; Cmd mode char (escape)? JZ CLRMD ; If yes, exit ; TEST1: MVI B,QUEST ; Else illegal char for this CALL PPAK ; Notify the user JNZ TEST1 ; If overflow, try again JMP TEST ; And loop ; ; ; Code to load a new wpm constant into baud ; WPM: CALL UNPAK ; Get char from keyboard JZ WPM ; If nothing, then wait ANI 1FH ; Mask for 5 valid bits RLC ; Mult by 2 RLC ; Again (*4) RLC ; Again (*8) ORI 07H ; Set the lsb LXI H,BAUD ; Point at baud location MOV M,A ; Store it in memory ; ; ; Clear the flags and exit to supervisor ; CLRMD: LXI H,CMMND ; Point to mode byte MVI M,0 ; Zero it JMP RESTRT ; To supervisor ; ; ; Routine to get char from keyboard fifo. ; Returns with char in reg A, else A = 0 if no char. ; UNPAK: CALL PNTR ; Try finish pending printing CALL KYBD ; Keyboard happy? CALL OTPUT ; Overlay the code output LXI H,KYFIFO ; Point at keyboard stack XRA A ; Clear reg a ADD M ; Char count to a RZ ; If empty, return a = 0 CALL INCLH ; Point at char MOV E,M ; Get char to reg e (temp) CALL DCRLH ; Point at keyboard fifo CALL PXP ; Out of fifo, returns a = 0 ADD E ; Char to reg a and flags RET ; Return with reg a = char ; ; ; Subroutine to wait for the transmit buffer empty flag ; WAITMT: IN CSTAT ; Get status ANI CTBE ; Transmit buffer empty mask JZ WAITMT ; Loop until empty RET ; Okay, have empty flag ; ; ; Keyboard handler subroutine ; KYBD: IN CSTAT ; Get status ANI CRBF ; Receive buffer full mask RZ ; If nothing, next task ; ; Put keyboard char in keyboard fifo ; IN CDATA ; Serial input ANI 7FH ; Strip parity CPI EXCHAR ; Exit to system? JZ EXIT ; If yes, then quit CPI ETX ; Control c? JNZ NETX ; No, skip over XRA A ; Clear all bits (turn off - up) OUT MOROUT ; Output to morse data port IF IMSAI CMA OUT 0FFH CMA ENDIF CALL WAITMT ; Wait for buffer empty MVI A,UPARRO ; Ascii ^ for control chars OUT CDATA ; Send it CALL WAITMT ; Wait for buffer empty MVI A,'C' ; Ascii c OUT CDATA ; Send it CALL WAITMT ; Wait for buffer empty JMP STRT1 ; Then restart from almost 0 ; NETX: LXI H,KYFIFO ; Point at keyboard fifo MOV B,A ; Char to reg b MVI C,BUFSKY ; Get buffer size CALL ENTPAK ; Put char in buffer CNZ WHOOP ; If overflow, tell user ; Fall thru and return in next ; ; Put char in printer fifo from B reg ; PPAK: LXI H,PNFIFO ; Point at printer fifo MVI C,BUFSPN ; Get buffer size CALL ENTPAK ; Put char in buffer RET ; ; End of the keyboard handler task ; ; ; Printer handler subroutine task ; PNTR: IN CSTAT ; Get status ANI CTBE ; Transmit buffer empty mask RZ ; If busy, try something else ; LXI H,TWIDTH ; Find print position XRA A ; Clear reg a and flags ADD M ; Count to reg a and flags JNZ PRT2 ; No line overflow MVI A,CR ; Line overflow, fix it ; OUT CDATA ; Send a c/r CALL WAITMT ; Wait until buffer empty ; MVI A,LF ; And lf OUT CDATA ; MVI M,WIDTH ; Reset print position count RET ; PRT2: LXI H,PNFIFO ; Point at char count XRA A ; Clear reg a and flags ADD M ; Count to reg a and flags RZ ; Nothing to print, next task ; ; ; If here there is printing to be done. ; Point at char to print and print it ; CALL INCLH NXTPNT: MOV E,M ; Char to reg e (temp) LXI H,PNFIFO ; Point at char count CALL PXP ; Ripple the fifo LXI H,TWIDTH ; Update print position MOV B,M ; Count to b DCR B ; Decrement it MOV M,B ; Restore to memory MOV A,E ; Char back to a CPI LF ; Is it linefeed? JZ INCRLF ; If yes, insert a crlf CPI CR ; Is it a c/r? JZ INCRLF ; If yes, insert a crlf CPI DELETE ; Is it a delete char? JZ DEL ; Yes, insert a back slash CPI ESC ; Is it cmd mode char (esc)? JNZ PNT1 ; No MVI A,ESCSYM ; Else substitute "$" for esc ; PNT1: OUT CDATA ; Print the char RET ; Done, printed a char ; INCRLF: MVI M,0 ; Set for crlf next RET ; DEL: MVI A,DELSYM ; Substitute char for delete OUT CDATA ; Print the char RET ; ; End of the printer task ; ; ; This subroutine translates A reg to output mode. ; Compute the displacement in asctab. ; XLATER: LXI H,ASCTAB ; Point at asctab ; THISIT: CMP M ; Is this the char? JZ CONVT ; Yes, go convert the char CALL INCLH ; No, try the next MOV B,A ; Save tha char in b (temp) MVI A,ASCEND/256 ; Get high limit to a for CMP H ; Compare - past table end? JC NTFUND ; If past and no match MVI A,ASCEND AND 0FFH ; Else get low limit to a for CMP L ; Compare - past table end? JC NTFUND ; If past and no match ; If still in table, try again MOV A,B ; Return char to a JMP THISIT ; And loop for next check ; CONVT: MVI A,MORTAB AND 0FFH ; Compute rel displacement low SUI ASCTAB AND 0FFH JNC OK1 DCR H ; Handle the borrow ; OK1: ADD L ; Add in the low pointer JNC OK2 INR H ; Handle the carry ; OK2: MOV L,A ; L now points in output tbl MVI A,MORTAB/256 ; Compute rel displacement high SBI ASCTAB/256 ADD H ; Add in the high pointer MOV H,A ; H now points in output tbl MOV A,M ; Replacement char to a RET ; Return with char in a ; NTFUND: MVI A,80H ; Set char not found error RET ; Error return ; ; ; This subroutine translates A reg to print mode ; XLAT: CPI 0FFH ; Set c flag (guard bit) RAL ; Rotate JNC $-1 ; If no left guard, loop ; ; If reg A contains a 000, error char was seen ; CPI 0 ; Set flags JNZ XLAT1 ; Not error, translate MVI A,DELSYM ; Substitute delete char RET ; Exit ; XLAT1: LXI H,MORTAB ; Point at mortab ; THIS: CMP M ; Is this the char? JZ MCONVT ; If yes, convert the char CALL INCLH ; Else try next char MOV B,A ; Save char in b (temp) MVI A,MOREND/256 ; Get high limit to a for CMP H ; Compare - past table end? JC NTFND ; Yes, with no match MVI A,MOREND AND 0FFH ; Get low limit to a for CMP L ; Compare - past table end? JC NTFND ; Yes, with no match ; ; If here, still in table, try contents again ; MOV A,B ; Return char to a JMP THIS ; Loop for next check ; MCONVT: MVI A,ASCTAB AND 0FFH ; Compute rel displacement low SUI MORTAB AND 0FFH JC MOK1 DCR H ; Handle the borrow ; MOK1: ADD L ; Add in the low pointer JNC MOK2 INR H ; Handle the carry ; MOK2: MOV L,A ; L now points in output tbl MVI A,ASCTAB/256 ; Compute rel displacement high SBI MORTAB/256 ADD H ; Add in the high pointer MOV H,A ; H now points in output tbl MOV A,M ; Ascii code to reg a RET ; Return with char in a ; NTFND: MVI A,BLANK ; Not found, ret with space RET ; Error return ; ; ; Subroutine task to output code ; OTPUT: LXI H,OTFIFO ; Point at stack XRA A ; Clear reg a ADD M ; Count to a and flags RZ ; If nothing, then next task CALL INCLH ; Else point at the data MOV A,M ; Get char CPI DELETE ; Error char? JNZ OTPUT1 ; No, go translate MVI D,7 ; Yes, do 8 dots ; OTERR: CALL DOT ; One dot DCR D ; Count -1 JNZ OTERR ; Not done... do it again JMP OUTEND ; Done, pop and exit ; OTPUT1: CALL XLATER ; Translate MOV D,A ; Save the char in d CPI 80H ; Is it a bad char (or space)? JNZ GOODCH ; Char ok, so do it up right MVI D,6 ; Set 7 units delay (units-1) ; SPACE: CALL TICK ; Wait one unit DCR D ; Decrement unit counter JNZ SPACE ; Loop until done JMP OUTEND ; GOODCH: CPI 80H ; If a=80h then done JZ OUTEND ; Done JNC DSH ; Dot or dash? CALL DOT ; Yes a dot, so key it JMP OTLOOP ; Next symbol ; DSH: CALL DASH ; Must be a dash, so key it ; OTLOOP: MOV A,D ; Get char back to a ANI 7FH ; 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 LXI H,OTFIFO ; Point at stack CALL PXP ; Pop the stack RET ; Next task ; ; ; Keyboard decoder for non command mode ; IDLE: LXI H,KYFIFO ; Point at char count XRA A ; Clear reg a and flags ADD M ; Count to a RZ ; If empty, try something else CALL INCLH ; Else point at char MOV E,M ; Save char in reg e (temp) CALL DCRLH ; Point at start of buffer CALL PXP ; Pop the char off the buffer MOV A,E ; Restore char to a CPI ESC ; Is it cmd mode char (esc)? JZ IDLE1 ; Yes ; Output in code LXI H,OTFIFO ; Set up for entpak MOV B,A ; Data to reg b MVI C,BUFOUT AND 0FFH ; Get buffer size CALL ENTPAK CNZ WHOOP ; If buffer full, tell user RET ; Done ; IDLE1: LXI H,CMMND ; Set for command mode MVI M,1 ; Mode = 1 RET ; ; ; Subroutine to service morse code input ; INPEND: IN MORINP ; Get morse input data ANI 1 ; We use the lsb RNZ ; If nothing pending, exit 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,0 ; Initialize time = 0 ; INSENS: CALL TICKI ; Wait for part of a baud (1/8) LXI H,TIMER ; Update timer MOV B,M ; To b INR B ; Increment it MOV M,B ; Restore to memory IN MORINP ; Get morse input data ANI 1 ; Is key down? JZ INSENS ; Wait for key up ; ; If here, key is now up ; MVI E,0 ; E=0 for dot fix later if dash MOV A,B ; Timer to a (8 * # of baud) CPI 10H JNC INDASH ; If dash, service dash ; ; See if clock much too slow for dot ; CPI 6 ; Should be 10 ideal JNC INPOK ; Clock is good enough LXI H,BAUDI ; Not good enough fix wpm const MOV A,M ; Wpm to a CPI 2 ; Is wpm too low to track? JC INPOK ; Dont try fix already too fast SBI 1 ; Decrement it MOV M,A ; Restore new to memory JMP INPOK ; ; If here, symbol is dash, update baudi for tracking ; INDASH: LXI H,BAUDI ; Point at input wpm MOV A,B ; Timer to reg a, again CPI 1CH ; Clock too fast? JC OKDASH ; No MOV A,M ; Yes CPI 0FEH ; Timer really too slow? JNC OKDASH ; Yes, bail out ADI 1 ; Else increment it MOV M,A ; Restore new to memory ; OKDASH: MVI E,1 ; Set e = 1 for dash ; INPOK: LXI H,INCHAR ; Point at char holding reg MOV A,M ; Partial char to a RAL ; Shift up one bit ANI 0FEH ; Junk the old carry bit ORA E ; Bring in new symbol from e MOV M,A ; Restore new partial char ; ; Time the interspace to find what type it is. ; LXI H,TIMER ; Reset the timer MVI M,0 ; Timer reset ; UPTIME: CALL TICKI ; Delay 1/8 of a baud time IN MORINP ; Get morse input data ANI 1 ; Is key down? JZ INTIME ; Yes, get the next symbol LXI H,TIMER ; Update the time MOV B,M ; To b INR B ; Increment it MOV M,B ; Restore to memory MOV A,B ; Get timer to a for compare CPI 0FFH ; End of message? JNZ NOTEOM ; Keep looping MVI B,LF ; End of message, cr-lf-cr-lf CALL PPAK CALL PPAK RET ; Exit to mainline ; NOTEOM: CPI 30H ; End of word? JNZ ENDLET ; No MVI A,1 ; Yes, output a space JMP MPAK ; Do it out right ; ENDLET: CPI 14H ; End of letter? JNZ UPTIME ; No, keep timing the up time LXI H,INCHAR ; Point at holding reg MOV A,M ; Morse from holding reg to a MVI M,1 ; Reset hold reg for next char ; MPAK: CALL XLAT ; Ascii to a equiv of morse MOV B,A ; Set up for ppak CALL PPAK ; Print the char JMP UPTIME ; Keep timing the up time ; ; CMMND DS 1 ; 0 = normal, else command mode TWIDTH DS 1 ; When byte is 0 generate cr/lf MSSCNT DS 1 ; Temp char count forr msg dump SOH DS 1 ; Dmpsub submode 0=print 1=send BAUD DS 1 ; Wpm constant (see notes) BAUDI DS 1 ; Input wpm val (gets modified) TIMER DS 1 ; Time baud * 8 counter INCHAR DS 1 ; Input char holding reg KYFIFO EQU $ ; Keyboard input buffer PNFIFO EQU KYFIFO+BUFSKY ; Printer buffer OTFIFO EQU PNFIFO+BUFSPN ; Output buffer MSSGBF EQU OTFIFO+BUFOUT ; Message buffer ; END