; ; ; TOR Display Program For Z80 System ; ; J. Vandiver, WB4LHG ; ; Revision History: ; ; 25 July 1991: Beginning of code entry ; 09 Oct. 1991: Z8530 set-up test ; 14 Oct. 1991: Simple version for attempting to receive AMTOR ; 15 Oct. 1991: Initialized lastbit ; 20 Oct. 1991: Forced disable of Z8530 external/status interrupts ; to make DCDB a simple level-sensitive input. ; 22 Oct. 1991: Further attempts to synchronize. ; 23 Oct. 1991: Improved sync and DX/RX checking. ; 24 Oct. 1991: Typos. First print of AMTOR characters! ; 26 Oct. 1991: Made restart work with "R" or "r". Made DX/RX print ; only once (was printing both character streams). ; 27 Oct. 1991: Fixed error in TORTBL; had blank at 05BH instead of ; 05CH table offset; resulted in all blanks being printed ; as *. First attempt to crudely phase-lock the sampling ; state machine. ; 28 Oct. 1991: Further work on phase-locking. ; ; 8530 SCC Addresses/bits ; ; Note: For TOR reception, the SCC is used mainly to generate interrupts ; for timing the sampling of the incoming bit stream. The "B" side is used ; only for a 2.4576 MHz crystal oscillator to drive the "A" side baud ; rate generator, which is set to generate a sampling clock. Each rising edge ; of the clock causes an interrupt to the CPU. The CPU gets the state ; (high or low) of the incoming bit stream on the DCDB input bit of the ; Z8530 SCC. The USART functions of the SCC are not used for TOR. ; .z80 ; SCC EQU 0A0H BCTRL EQU SCC ; "B" port BDATA EQU SCC+1 ACTRL EQU SCC+2 ; "A" port ADATA EQU SCC+3 TXBE EQU 04H ; TX BUFFER EMPTY RXDA EQU 01H ; RX DATA AVAILABLE DCD equ 08h ; "Data carrier detect": TOR data input ; ; Compupro Interfacer I Console UART ; sdata equ 05ch ; Console UART data and status status equ 05dh txrdy equ 01h rxrdy equ 02h warm equ 0000h intvec equ 0038h cr equ 0dh lf equ 0ah esc equ 1bh blank equ 20h ; ; TOR letters and figures shift characters ; ltrs equ 05ah figs equ 036h idlea equ 033h idleb equ 0Fh repeat equ 066h ; ; System constants ; errmax equ 16 ; Number of contiguous errors before resync phmax equ 16 ; Max count for clock sync phmid equ 8 ; Starting point, lead/lag counter phmin equ 0 ; Minimum count for clock sync aseg ; CP/M Compatible start address org 100h start equ $ ld a,0C3H ; Jump instruction ld (intvec),a ld hl,introut ; Get interrupt routine base address ld (intvec+1),hl ; Store address into vector location call ioinit ; Set up 8530 SCC to generate sampling clock ld hl,intro call msgout ; Tell operator program is alive restart equ $ ld a,phmid ; Init clock sync counter ld (sync),a ld a,0 ; Clear out shift register ld b,8 ld hl,shftrg rest1 equ $ ld (hl),a inc hl djnz rest1 ld (state),a ld (frame),a ld (lastbit),a ld (thisbit),a ld (shftbit),a ld (dxerror),a ld (errcnt),a wait equ $ ; Wait for interrupts ei nop nop di ; ld a,(state) ; add a,'0' ; call chrout call chrin ; Check for commands cp 'R' jp z,restart ; Restart? cp 'r' jp z,restart cp esc jp nz, wait di jp warm ; Back to CP/M upon exit ; ; Interrupt routine: Samples TOR data stream, synchronizes, and displays ; introut: in a,(bctrl) ; Get SCC status byte and dcd ; Mask off all but TOR data bit xor dcd ; Try flipping rr a ; Move to bit 0 rr a rr a ld b,a ; Save bit ld (thisbit),a ld a,(state) ; Get interrupt state cp 0 jp z,intr0 ; State 0: Wait for edge cp 1 jp z,intr1 ; States 1 through 3: debounce cp 2 jp z,intr2 cp 3 jp z,intr3 cp 4 jp z,intr4 ; State 4: Sample bit stream, print if needed cp 5 jp z,intr5 ; States 5 through 10: Wait cp 6 jp z,intr6 cp 7 jp z,intr7 cp 8 jp z,intr8 cp 9 jp z,intr9 cp 10 jp z,intrA cp 11 jp z,intrB ; State 11: Back to state 4 (8 state cycle) ld a,0 jp intrx ; Shouldn't be here intr0 equ $ ld a,(lastbit) ; Get bit n-1 cp b ld a,0 jp z,intrx ; No change: State <-- 0 ld a,1 ; Change: State <-- 1 jp intrx intr1 equ $ ld a,(lastbit) cp b ld a,2 jp z,intrx ; No change: State <-- 2 ld a,0 ; Change: State <-- 0 jp intrx intr2 equ $ ld a,(lastbit) cp b ld a,3 jp z,intrx ; No change: State <-- 3 ld a,0 ; Change: State <-- 0 jp intrx intr3 equ $ ld a,(lastbit) cp b ld a,4 jp z,intrx ; No change: State <-- 4 ld a,0 ; Change: State <-- 0 jp intrx intr4 equ $ ld a,b rr a ; Put new bit into carry call shift ; Shift in a TOR bit sample ld a,(frame) cp 0 jp z,intr40 cp 1 jp z,intr41 cp 2 jp z,intr42 cp 3 jp z,intr43 cp 4 jp z,intr44 cp 5 jp z,intr45 cp 6 jp z,intr46 cp 7 jp z,intr47 cp 8 jp z,intr48 cp 9 jp z,intr49 cp 19 jp m,intr4A jp z,intr4B ld a,0 jp intr4x intr40 equ $ ld a,(shftrg+6) ; Framing state 0 and 01111111B cp repeat ; Look for 066H byte ld a,1 jp z,intr4x ld a,0 jp intr4x ; Keep looking until found intr41 equ $ ; Framing state 1: delay for RX alignment push af ; Test code push bc ld a,'*' ; Print * if here call chrout pop bc pop af ld a,2 jp intr4x intr42 equ $ ; Framing state 2: delay ld a,3 jp intr4x intr43 equ $ ; Framing state 3: RX check for idleb ld a,(shftrg+2) and 01111111B cp idleb ld a,4 jp z,intr4x ; Go to next state if idleb ld a,0 jp intr4x ; Back to zero if not intr44 equ $ ; Framing states 4, 5: Delay for DX ld a,5 jp intr4x intr45 equ $ ld a,6 jp intr4x intr46 equ $ ; Framing state 6: DX check ld a,0 ld (dxerror),a ld hl,shftrg+5 call check jp z,intr461 call chrout ; Print if no error jp intr462 intr461 equ $ cp 0FFH jp nz,intr462 ld (dxerror),a ; Set DX error flag if not a TOR character intr462 equ $ ld a,7 jp intr4x intr47 equ $ ; Framing states 7, 8: Delay for RX ld a,8 jp intr4x intr48 equ $ ld a,9 jp intr4x intr49 equ $ ; Framing state 9: RX check ld a,(dxerror) ; Test only if error in DX cp 0FFH jp nz,intr492 ld hl,shftrg+1 call check jp z,intr491 call chrout ; Print if no error jp intr492 intr491 equ $ cp 0FFH jp nz,intr492 ld a,'*' call chrout ; Show error with * (DX and RX corrupted) intr492 equ $ ld a,10 ; Back to framing state 4 (DX delay) jp intr4x intr4A equ $ ; Wait for next DX character in shftrg(5) ld a,(frame) inc a jp intr4x intr4B equ $ ld a,6 ; Framing state 19: back to 6 (14 shift intr4x equ $ ; cycle) ld (frame),a ld a,5 jp intrx intr5 equ $ ld a,6 ; States 5 through 10: Sampling clock sync jp intrx intr6 equ $ ld a,(lastbit) ; Check for level change between states 5, 6 cp b ld a,7 jp z,intrx ; No change: continue ld a,(sync) inc a ; Change: increment phase error (leading) cp phmax ; Maximum lead reached? jp z,intr61 ; Yes, skip 1 state ld (sync),a ld a,7 jp intrx intr61 equ $ ld a,phmid ; Init phase error ld (sync),a ld a,8 ; Jump ahead to state 8, skip 7 jp intrx intr7 equ $ ld a,(lastbit) ; Check for level change between states 6, 7 cp b ld a,8 jp z,intrx ; No change: continue ld a,(sync) inc a ; Change: increment phase error (leading) cp phmax ; Maximum lead reached? jp z,intr71 ; Yes, prepare to skip 1 state ld (sync),a ld a,8 jp intrx intr71 equ $ ld a,phmid ; Init phase error ld (sync),a ld a,9 ; Jump ahead to state 9, skip 8 jp intrx intr8 equ $ ld a,9 ; State 8: OK, do nothing jp intrx intr9 equ $ ld a,(lastbit) ; Check for level change between states 8, 9 cp b ld a,10 jp z,intrx ; No change: continue ld a,(sync) dec a ; Change: decrement phase error (leading) cp phmin ; Minimum lead reached? jp z,intr91 ; Yes, prepare to repeat 1 state ld (sync),a ld a,10 jp intrx intr91 equ $ ld a,phmid ; Init phase error ld (sync),a ld a,9 ; Repeat state 9 jp intrx intrA equ $ ld a,(lastbit) ; Check for level change between states 9, 10 cp b ld a,11 jp z,intrx ; No change: continue ld a,(sync) dec a ; Change: decrement phase error (leading) cp phmin ; Minimum lead reached? jp z,intrA1 ; Yes, prepare repeat 1 state ld (sync),a ld a,11 jp intrx intrA1 equ $ ld a,phmid ; Init phase error ld (sync),a ld a,10 ; Repeat state 10 jp intrx intrB equ $ ld a,4 ; Back to state 4 intrx equ $ ld (state),a ; Set next state ld a,(thisbit) ld (lastbit),a ; Update last bit sample ret ; ; Check TOR character at (hl), see if printable ; ; Returns zero flag set if not printable ; a = ASCII character if printable ; a = 0FFH if error ; check equ $ ld a,(shftbit) ; Get shift bit ld b,a ld a,(hl) ; Get character and 01111111b ; Mask to 7 bits or b ; Include shift bit ld b,0 ; Build look-up table address ld c,a ld hl,tortbl ; Point to TOR <--> ASCII table add hl,bc ld a,(hl) ; Get ASCII code (FFH denotes invalid TOR) cp 0FFh ret z cp figs ; Test for shift and control characters jp nz,check1 ld a,80h ; Figures shift- set shift bit ld (shftbit),a ret check1 equ $ cp ltrs jp nz,check2 ld a,00h ; Letters shift- clear shift bit ld (shftbit),a ret check2 equ $ cp idlea ; Idle signal alpha ret z cp idleb ; Idle signal beta ret z cp repeat ; Repeat request ret ; ; Shift (new bit shifted into MSB from carry bit) ; ; Exits with LSB in carry ; ;shift equ $ ; ld hl,shftrg ; Point to first byte of register ; rr (hl) ; Shift carry into MSB, LSB to carry ; ret ; ; 7-byte (8 TOR Character) Shift (new bit shifted into MSB from carry bit) ; ; Exits with LSB in carry (allows 56 successive calls to rotate entire ; contents of the register) ; shift equ $ ld b,7 ; 7 byte (56 bit) shift register ld hl,shftrg ; Point to first byte of register shift1 equ $ rr (hl) ; Shift carry into MSB, LSB to carry inc hl ; Point to next byte djnz shift1 ; Repeat for all bytes ret ; ; Output null-terminated string directly to console port ; msgout equ $ ld a,(hl) ; Get char, check for null or a ret z inc hl call chrout jp msgout ; ; Output single character to console ; chrout equ $ ld b,a chrout1 equ $ in a,(status) and txrdy jp z,chrout1 ; Wait for transmitter ready ld a,b out (sdata),a ret ; ; Get character from console ; chrin equ $ in a,(status) ; Return data in a if ready, else 0 and rxrdy ret z in a,(sdata) ret ; ; 8530 SCC Initialization ; ioinit equ $ ld C,BCTRL ;8530 "B" side ld B,BLEN ; ld hl,BSTR otir ld C,ACTRL ;"A" side ld B,ALEN ld hl,ASTR otir ret ; ; 8530 SCC Initialization tables ; ; Format: Register numbers are given in decimal, contents follow ; in binary or hex ; ; "A" section: 800 Hz TOR sampling clock ; ASTR: DB 4 ; Write register 4 DB 00000100B ; 1X clock, 1 stop bit, no parity DB 3 ; Write register 3 DB 11000000B ; Rx 8 bits, Rx disabled DB 5 ; Write register 5 DB 11100010B ; DTR on, Tx 8 bits, no break, Tx disable, ; RTS on, Tx CRC not enabled DB 11 ; Write register 11 DB 01010110B ; No xtal, Rx clock = BR gen, Tx clock = BR gen, ; TRxC = output, TRxC output = BR gen DB 12 ; Write register 12 DB 11111110B ; Low byte, time constant (800 Hz, ; db 11111111B ; 2.4576 Mhz, TOR sampling clock--> 1534) DB 13 ; Write register 13 DB 00000101B ; High byte, baud rate generator time constant ; db 11111111B DB 14 ; Write register 14 DB 00H ; Baud rate generator source = RTxC, ; baud rate generator not enabled DB 5 ; Write register 5 DB 11101010B ; DTR on, Tx 8 bits/char, no break, Tx on, ; SDLC, RTS on, Tx CRC disable DB 3 ; Write register 3 DB 11000001B ; Rx 8 bits/char, no auto enables, no hunt ; mode, Rx CRC disable, no address search, ; sync load inhibit, Rx enable DB 14 ; Write register 14 DB 01H ; Baud rate generator source = RTxC, baud rate ; generator enabled ALEN EQU $-ASTR ; ; "B" section: 1200 baud async, plus XTAL clock for modem ; BSTR: DB 9 ; Write register 9 DB 0C0H ; Force hardware reset DB 4 ; Write register 4 DB 44H ; X 16 clock, 1 stop bit DB 1 ; Write register 1 DB 0 ; No interrupts DB 3 ; Write register 3 DB 0C0H ; Rx 8 bits/char, Rx not enabled DB 5 ; Write register 5 DB 0E2H ; DTR on, Tx 8 bits/char, no break, Tx not ; enabled, RTS on DB 11 ; Write register 11 DB 0D4H ; RTxC = XTAL, Rx clock = baud rate generator, ; Tx clock = baud rate generator, TRxC = ; output, TRxC out = XTAL out DB 12 ; Write register 12 DB 4CH ; Low byte, time constant (1200 baud * 16, ; 3 MHz) DB 13 ; Write register 13 DB 0 ; Upper byte, time constant DB 14 ; Write register 14 DB 02H ; Baud rate generator source = PCLK, ; baud rate generator not enabled DB 15 ; Write register 15 DB 0 ; Disable all external/status interrupts DB 3 ; Write register 3 DB 0C1H ; Rx enabled DB 5 ; Write register 5 DB 0EAH ; Tx enabled DB 14 ; Write register 14 DB 03H ; Baud rate generator enabled BLEN EQU $-BSTR ; ; TOR to ASCII Conversion table ; tortbl equ $ ; ; Letters case ; db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,'J' db 0FFh,0FFh,0FFh,'F', 0FFh,'C' ,'K', 0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,'W' db 0FFh,0FFh,0FFh,'Y' ,0FFh,'P' ,'Q' ,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,'G' ,figs,0FFh db 0FFh,'M' ,'X' ,0FFh,'V' ,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,'A' db 0FFh,0FFh,0FFh,'S' ,0FFh,'I' ,'U' ,0FFh db 0FFh,0FFh,0FFh,'D', 0FFh,'R' ,'E' ,0FFh db 0FFh,'N' ,ltrs,0FFh,' ' ,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,'Z' ,0FFh,'L' ,0FFh,0FFh db 0FFh,'H' ,' ' ,0FFh,lf, 0FFh,0FFh,0FFh db 0FFh,'O', 'B' ,0FFh,'T' ,0FFh,0FFh,0FFh db cr ,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh ; ; Figures case ; db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,060H db 0FFh,0FFh,0FFh,'!' ,0FFh,':' ,'(', 0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,'2' db 0FFh,0FFh,0FFh,'6' ,0FFh,'0' ,'1' ,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,'&' ,figs,0FFh db 0FFh,'.' ,'/' ,0FFh,';' ,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,'-' db 0FFh,0FFh,0FFh,007h,0FFh,'8' ,'7' ,0FFh db 0FFh,0FFh,0FFh,'$', 0FFh,'4' ,'3' ,0FFh db 0FFh,',' ,ltrs,0FFh,' ' ,0FFh,0FFh,0FFh db 0FFh,0FFh,0FFh,'"' ,0FFh,')' ,0FFh,0FFh db 0FFh,'#' ,' ' ,0FFh,lf, 0FFh,0FFh,0FFh db 0FFh,'9', '?' ,0FFh,'5' ,0FFh,0FFh,0FFh db cr ,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh,0FFh ; ; Intro message ; intro equ $ db cr,lf,'AMTOR Receive Program',cr,lf,0 state equ $ db 0 lastbit equ $ db 0 thisbit equ $ db 0 shftbit equ $ db 0 frame equ $ db 0 dxerror equ $ db 0 sync equ $ db 0 errcnt equ $ db 0 shftrg equ $ ds 8 chrbuf equ $ ds 8 end