; Kansas City Format Cassette to PC Interface ; Designed to hook up a Heathkit ET-3400A trainer with IO accessory to save files to PC rather than cassette ; Jeff Thorssell April 2009 ; Based on an idea by Ted Rossin LIST R=DEC list p=16f876 ; list directive to define processor #include "p16f876.inc" __CONFIG _CP_OFF & _DEBUG_OFF & _HS_OSC & _PWRTE_ON & _WDT_OFF & _LVP_OFF & _WRT_ENABLE_OFF & _BODEN_ON & _CPD_OFF ; Uncomment one of these baud rates #define BAUD_CONSTANT 10 ; 115.2K baud (BRGH = 1) ;#define BAUD_CONSTANT 21 ; 57.6K baud (BRGH = 1) ;#define BAUD_CONSTANT 42 ; 28.8K baud (BRGH = 1) ;#define BAUD_CONSTANT 129 ; 2400 baud (BRGH = 0) ;#define BAUD_CONSTANT 32 ; 9600 baud (BRGH = 1) ;#define BAUD_CONSTANT 64 ; 4800 baud (BRGH = 0) #define BRGH_CONSTANT 1 ;#define BRGH_CONSTANT 0 #define ACK 0x6 ;Cassette input to PORTB, 4 (Pin 25) ;Output is PORTA, 0. Run it through a 74HCT04 before going to the et3400T. ;PORTA, 1 activity LED ;Registers start at 0x20 Temp01 equ 0x20 ;Used by Decode_Command, Del_Flip Temp02 equ 0x21 ;Used by Do_Nibble, Del_Flip Temp03 equ 0x22 ;Used by Out_Str, OLT Temp04 equ 0x23 ;Used by Out_Str, OLT Command equ 0x24 OrigChar equ 0x25 ;Used by Out_Num, Do_Nibble LoByte equ 0x26 ;Used by Out_Num HiByte equ 0x27 ;Used by Out_Num BaudRate equ 0x28 Cells equ 0x29 ;Cells = BaudRate. This one is used to count down. TapeChar equ 0x30 ;Used by PC2Cass and GCC. Character to output\input to\from tape Direction equ 0x31 ;P = PC to Cassette, C = Cassette to PC Flags equ 0x32 ;Individual Flags Found_S equ 0 BitCount equ 0x35 ;Used by T_Out_Char org 0 Startup: nop nop nop goto Main org 4 retfie Main: Call Init Get_Command: bsf PORTA, 1 ;LED off call Get_Char ; Read byte movwf Command ; Save Command call Decode_Command goto Get_Command ;------------------------------------------------------------------- ;Out_Str ; Strings with a 0 on the end ; Entry with string# in W. ; Out_Str clrf Temp03 movwf Temp04 WS_2 clrc rlf Temp04, w addwf PCL, f call Str0 goto WS_3 call Str1 goto WS_3 ; call Str2 ; goto WS_3 ; ; call Str3 ; goto WS_3 ; ; call Str4 WS_3 andlw 0xFF btfsc STATUS, Z return call Out_Char incf Temp03, f goto WS_2 Str0 movf Temp03, w addwf PCL, f dt "C = Cassette to PC, Baud = ", 0 Str1 movf Temp03, w addwf PCL, f dt "P = PC to Cassettes, Baud = ", 0 ;Str2 movf Temp03, w ; addwf PCL, f ; dt "2400", 0 ; ;Str3 movf Temp03, w ; addwf PCL, f ; dt ", 2=1200, 1=2400: ", 0 ; ;Str4 movf Temp03, w ; addwf PCL, f ; dt "Test 04\n\r", 0 ;End - Out_Str ------------------------------------------------------------ Decode_Command: movf Command,w xorlw 'C' btfsc STATUS, Z ;Equal? call Cmd00 ;Yes Cassette to PC movf Command,w ;No, check another xorlw 'P' btfsc STATUS, Z ;Equal? call Cmd01 ;Yes PC to Cassette movf Command,w ;No, check another xorlw 'G' btfsc STATUS, Z ;Equal? call Cmd02 ;Yes GO ; movf Command,w ;No, check another ; xorlw '1' ; btfsc STATUS, Z ;Equal? ; call Cmd03 ;Yes Output 1200 ; ; movf Command,w ;No, check another ; xorlw '2' ; btfsc STATUS, Z ;Equal? ; call Cmd04 ;Yes Output 2400 return ;Default ;Cmd00 ------------------------------------------ ;Received a 'C' which means Cassette to PC Cmd00 movf Command, w movwf Direction call Do_CR_LF movlw 0x00 call Out_Str call Get_Baud movf BaudRate, w ;Output the rate addlw 0x30 ;add 30h to get a character call Out_Char call Do_CR_LF return ;END Cmd00-------------------------------------- ;Cmd01 ------------------------------------------ ;Received a 'P' which means PC to Cassette Cmd01 movf Command, w movwf Direction call Do_CR_LF movlw 0x01 call Out_Str call Get_Baud movf BaudRate, w ;Output the rate addlw 0x30 ;add 30h to get a character call Out_Char call Do_CR_LF return ;END Cmd01-------------------------------------- ;Cmd02 ------------------------------------------ ;Received a 'G' which means Go whichever way was already set up Cmd02 movf Direction, w xorlw 'C' btfsc STATUS, Z ;Equal? call Cass2PC ;Yes Cassette to PC movf Direction,w ;No, check another xorlw 'P' btfsc STATUS, Z ;Equal? call PC2Cass return ;;Cmd03 ------------------------------------------ ;;Received a '1' which means output a 1200Hz signal on RA0 ;Cmd03 call Out1200 ; return ; ;;Cmd04 ------------------------------------------ ;;Received a '2' which means output a 2400Hz signal on RA0 ;;Cmd04 movlw 0x01 ; Call Del_Flip ; goto Out2400 ;Output Leader\Trailer ------------------------------------- ; Outputs 5 seconds (close enough) of Ones (2400Hz) OLT movlw 0x60 movwf Temp03 OLT_3 movlw 0xFF movwf Temp04 OLT_4 movlw 0x01 call Del_Flip decfsz Temp04, f goto OLT_4 decfsz Temp03, f goto OLT_3 return ;End - OLT ---------------------------------------- ;Delay and Flip ----------------------------------------------- ;runs through a delay and then flips the output ;Enter with 1 or 2 in w to get 1200 or 2400 ;1 = 2400, 2 = 1200 ;2 trips through this at 2400 gives a cell ;1 trip through this at 1200 gives a cell Del_Flip movwf Temp02 DF_2 movlw 0xAC movwf Temp01 DF_3 nop goto $+1 decfsz Temp01, f goto DF_3 decfsz Temp02, f goto DF_2 Flip movlw 0x01 ;Flip the output the opposite way. Light too. xorwf PORTA, f return ;End Del_Flip ---------------------------------------- ;Tape Out_Char ---------------------------------------- ;Outputs a character to the tape output at 2400Hz and 1200Hz ;Entry with character in W T_Out_Char movwf TapeChar call Out_Space ;Start bit movlw 0x08 movwf BitCount TOC_2 rrf TapeChar, f btfss STATUS, C goto TOC_Space call Out_Mark goto TOC_Both TOC_Space call Out_Space TOC_Both decfsz BitCount, f goto TOC_2 call Out_Mark ;Split stop bits and get next character in between movlw ACK ;Request next character call Out_Char call Out_Mark ;2nd stop bit call Get_Char ;Get it from serial port return ;End - Tape Out_Char ---------------------------------------- ;Output a mark (1) to tape --------------------------------------- Out_Mark movf BaudRate, w movwf Cells OM_2 movlw 0x01 call Del_Flip movlw 0x01 call Del_Flip decfsz Cells, f goto OM_2 return ;End Output a mark -------------------------------------------------- ;Output a space (0) to tape ----------------------------------------- Out_Space movf BaudRate, w movwf Cells movwf Cells OS_2 movlw 0x02 call Del_Flip decfsz Cells, f goto OS_2 return ;End Output a space -------------------------------------------------- ;Read in from a sound file and outputs the data via RS232 ; Cass2PC bcf PORTA, 1 ;LED on call GCC ;Character is in TapeChar movf TapeChar, w xorlw 'S' ;Need to keep an eye out for S's btfsc STATUS, Z ;Equal? goto S_Rcd ;Yes it's an S movf TapeChar, w ;Get character back xorlw '9' ;Need to keep an eye out for 9's btfsc STATUS, Z ;Equal? goto Nine_Rcd ;Yes, it's a 9 bcf Flags, Found_S ;No S9 movf TapeChar, w ;Get character back call Out_Char ;No 9 goto Cass2PC ;Get another character S_Rcd bsf Flags, Found_S ;Tag the found S movf TapeChar, w ;Get the character back call Out_Char ;And send it goto Cass2PC ;Get another character Nine_Rcd btfsc Flags, Found_S goto Cass2PC_End ;Found an S followed by a 9 bcf Flags, Found_S ;No S9 so start over movf TapeChar, w ;Get the character back call Out_Char ;And send it goto Cass2PC ;Get another character Cass2PC_End movf TapeChar, w ;Get the character back call Out_Char ;And send it movlw 0xFF ;Send EOF call Out_Char return ;Get Cassette Character -------------------------------------- ;Uses the Baud number 1, 2, 4, 8 to set how many cells to get for each character ;300 baud gets 8 samples per bit, 600 baud is 4 cells per bit etc. GCC movf BaudRate, w movwf Cells ;rrf Cells, f ;Was to center sample on cell, but the PIC ;incf Cells, f ;comes back so fast it messes up the bit count GCC_2 call TNC ;Get a cell, looking for start bit btfsc STATUS, C ;Carry means no start bit goto GCC_2 ;Get another cell decfsz Cells, f ;Got a start start, finish the bit goto GCC_2 ;Not enough cells, get another movlw 01111111b movwf TapeChar GCC_3 movf BaudRate, w ;Get # of cells back movwf Cells GCC_4 call TNC ;Get a cell decfsz Cells, f ;Count down number of cells for the bit goto GCC_4 rrf TapeChar, f btfsc STATUS, C goto GCC_3 ;Keep going as long as there's a carry ;Get stop bit movf BaudRate, w ;Get # of cells back movwf Cells GCC_5 call TNC decfsz Cells, f goto GCC_5 movf TapeChar, w return ;End Get Cassette Character -------------------------------------- ;TNC = Take Next Cell ----------------------------------------- ;Waits for 1/2 cycle of 1200Hz or 1 cycle of 2400Hz ;Returns Carry SET if a zero ;Returns Carry CLEAR if a ONE ;80 as a cutoff point gave errors. 40 gave all 0's. Used 60 as a medium. TNC clrf TMR0 movf PORTB, w ;Read B and clear RBIF bcf INTCON, RBIF btfss INTCON, RBIF ;Wait for change on B goto $-1 movf PORTB, w ;Read B and clear RBIF bcf INTCON, RBIF movf TMR0, w ;Get timer data clrf TMR0 ; and reset it sublw 0x60 ;Subtracts W from 0x60. If carry bit is CLEAR W is over this # btfss STATUS, C return ;Clear so return btfss INTCON, RBIF ;Wait for another change on B goto $-1 movf PORTB, w ;Read B and clear RBIF bcf INTCON, RBIF movf TMR0, w ;Get timer data clrf TMR0 ; and reset it sublw 0x60 ;Subtracts W from 0x60. If carry bit is CLEAR W is over this # return ;End Take_Next_Cell ------------------------------------- ;-------------------------------------------------------- ;Get_Baud ; Get_Baud call Get_Char ; Read byte movwf BaudRate ; Save it ;Check for 300 baud xorlw '8' btfss STATUS, Z ;Equal? goto G_B_2 ;No movlw 0x08 movwf BaudRate return ;Check for 600 baud G_B_2 movf BaudRate,w ;No, check another xorlw '4' btfss STATUS, Z ;Equal? goto G_B_3 ;No movlw 0x04 movwf BaudRate return ;Check for 1200 baud G_B_3 movf BaudRate,w ;No, check another xorlw '2' btfss STATUS, Z ;Equal? goto G_B_4 ;No movlw 0x02 movwf BaudRate return ;Check for 2400 baud G_B_4 movf BaudRate,w ;No, check another xorlw '1' btfss STATUS, Z ;Equal? goto G_B_Out ;No movlw 0x01 movwf BaudRate return G_B_Out goto Get_Baud ; Get_Char ------------------------------------------------------------ ;Waits for a serial character and returns with it in w Get_Char: btfss PIR1,RCIF ; Check IRQ flag for byte goto $-1 movf RCREG,w bcf PIR1,RCIF ; Reset flag return ;End Get_Char ------------------------------------------------------------- ; InitRS232 -------------------------------------------------------------- ; InitRS232: bcf STATUS,RP0 bcf STATUS,RP1 ; Select Bank 0 bsf STATUS,RP0 ; Select Bank 1 registers bsf TRISC^0x80,7 ; PORTC[RX] is Input bcf TRISC^0x80,6 ; PORTC[TX] is Output movlw BAUD_CONSTANT movwf SPBRG^0x80 ; Set baud to 115200, 57600, 28800 or 19200 movlw (1<