; ; Brad and Lawrence's PIC Alice 2 interface chip. The PIC ; is responsible for keyboard, serial, and clock interfaces. ; ; An internal queue keeps command-data sequences for the CPU. ; The command bytes are the constants pic_???_cmd. ; The timer command has no data byte, and the serial and keyboard ; bytes are followed by one byte of data. ; ; June 21st, 1997 ; maclib 'p65.inc' device pic16c65,xt_osc,wdt_off,pwrt_off,protect_off w_temp equ 020h status_temp equ 021h ; ; Keyboard I/O constants ; init_stop_count equ 2 ; # of stop bits per byte (+ 1) stop_count equ 022h ; storage for stop bits left init_data_bits equ 10 ; start bits + data bits (+ 1) data_bits equ 023h ; storage for kbd data shifts left ; (1st bit shifted in gets pushed off end) kbd_byte equ 024h ; storage for keyboard byte ; ; Serial I/O constants ; baud_rate_code equ 00fh ; 19200 baud at 20 MHz, BRGH=0, 15 decimal ; ; Alice I/O queue constants ; outq_tail equ 025h ; output queue tail pointer outq_head equ 026h ; output queue head pointer (actually ; head+1) outq_length equ 027h ; length in bytes of current output queue ; (head - tail) outq_byte_pop equ 028h ; byte popped off output queue outq_byte_push equ 029h ; byte to push on output queue outq_storage equ 040h ; first byte of output queue storage outq_size equ 040h ; number of bytes in output queue outq_wrap_mask equ 03fh ; mask for wrapping queue pointers ; ; Queue command constants ; pic_non_cmd equ 000h ; no command yet (not used on the pic) pic_ser_cmd equ 001h ; serial byte pic_kbd_cmd equ 002h ; keyboard byte pic_tim_cmd equ 003h ; timer (no byte) ; ; Timer1 constants ; ten_hz_low equ 0dch ; low byte of 10 hz counter start ten_hz_high equ 00bh ; high byte of 10 hz counter start ; together these make 0xbdc, which means ; upward-counting tmr1 does 62500 ticks, ; and at 5 Hz (clk/4) with 1:8 scaling, ; that's 10 overflows per second. reset org 0 ; on reset goto start intsvc org 4 ; on interrupt goto interrupt start bcf rp0 ; bank 0 bcf pspif ; clear PSP interrupt flag ; Serial interface bcf portc,6 ; clear USART transmit pin bcf portc,7 ; clear USART receive pin bcf rcif ; clear USART receive interrupt flag ; keyboard interface movlw init_stop_count movwf stop_count ; set up # of stop bits in kbd data movlw init_data_bits movwf data_bits ; set up # of actual shifts to make bsf rp0 ; bank 1 ; Alice 2 bus interface bcf trisa,0 ; bit 0 port A is output bcf trisa,1 ; bit 1 port A is output ; Alice 2 bus interface bsf trise0 ; /RD is input bsf trise1 ; /WR is input bsf trise2 ; /CS is input ; Serial interface ; don't need to set trisc, default is 0xff (input) bsf trisc, 6 ; transmit is output bsf trisc, 7 ; receive is input ; Keyboard interface ; don't need to set trisb, default is 0xff (input) bsf intedg ; RB0/INT triggers on low-to-high ; Alice 2 bus interface bsf pspie ; enable PSP interrupts bsf pspmode ; enable PSP mode ; Serial interface movlw baud_rate_code ; default baud rate movwf spbrg ; set baud rate generator bcf sync ; asynchronous serial bsf txen ; enable USART transmitter bcf rp0 ; bank 0 bsf spen ; enable USART receiver bsf rp0 ; bank 1 bsf rcie ; enable USART receive interrupt bcf rp0 ; bank 0 bsf cren ; enable USART receive next byte ; Alice 2 bus interface bsf porta,0 ; don't interrupt CPU on IRQ 0 bcf porta,1 ; clear debug pin ; init Alice output queue movlw outq_storage movwf outq_head movwf outq_tail clrf outq_length ; init timer 1 bsf t1ckps1 bsf t1ckps0 ; set tmr1 prescaler to 8 movlw ten_hz_low movwf tmr1l movlw ten_hz_high movwf tmr1h bsf tmr1on ; Serial and Alice 2 bus interface bsf rp0 ; bank 1 bsf peie ; enable peripheral interrupts ; Timer interrupt bsf tmr1ie ; enable tmr1 interrupt ; Keyboard interface bcf intf ; clear RB0/INT interrupt flag bsf inte ; enable RB0/INT interrupt bsf gie ; enable all interrupts bcf rp0 ; bank 0 loop goto loop interrupt ; save W and STATUS movwf w_temp ; save W swapf status, w ; save status in W bcf rp0 ; bank 0 movwf status_temp ; save status btfsc tmr1if ; test timer 1 flag goto timer_int ; if timer int, then go handle timer btfss pspif ; test PSP interrupt flag goto not_psp_intr ; not PSP bsf rp0 ; bank 1 btfss ibf ; did we get a write? goto psp_was_read ; nope, jump to read ; we got a write ; send on to serial bcf rp0 ; bank 0 movf portd, w ; get byte movwf txreg goto end_psp_interrupt ; return timer_int bcf tmr1on ; turn off temporarily movlw ten_hz_low movwf tmr1l movlw ten_hz_high movwf tmr1h bsf tmr1on ; reset and turn on movlw pic_tim_cmd call send_byte ; alert Alice of timer interrupt ; fall through to endtimer_interrupt endtimer_interrupt bcf tmr1if ; clear TMR1 interrupt flag goto finish_interrupt psp_was_read call check_queue ; send next byte if there is one goto end_psp_interrupt not_psp_intr ; either RB0/INT or RC/USART btfss intf ; skip next if is definitely INT interrupt goto serial_intr ; it's a byte from the serial port decfsz data_bits, 1 ; skip next if done with data bits goto got_kbd_bit ; have a good bit on port B decfsz stop_count, 1 ; skip next if done with keyboard cycle goto more_stops ; not done yet, have stop bits got_kbd_byte movlw pic_kbd_cmd ; put the kbd cmd into the queue call send_byte ; send data to CPU movf kbd_byte, w ; get kbd byte call send_byte ; send data to CPU movlw init_stop_count movwf stop_count ; set up # of stop bits in movlw init_data_bits movwf data_bits ; set up # of actual shifts goto end_rb0_interrupt ; done with sending byte more_stops ; only stop bits left clrf data_bits incf data_bits ; set remaining data_bits to 1 ; next loop through dec's data_bits, it's 0, ; skips to decfsz stop_count goto end_rb0_interrupt ; finish with rb0/int interrupt got_kbd_bit ; shift in a bit from keyboard bsf c ; set carry btfss portb, 1 ; if kbd data bit is set, skip next bcf c ; clear carry rrf kbd_byte, 1 ; rotate data bit into MSB of kbd_byte ; fall through to end_rb0_interrupt end_rb0_interrupt bcf rp0 ; bank 0 bcf intf ; clear RB0/INT interrupt flag goto finish_interrupt ; pop processor context end_psp_interrupt bcf rp0 ; bank 0 bcf pspif ; clear PSP interrupt flag goto finish_interrupt serial_intr bcf rp0 ; bank 0 movlw pic_ser_cmd ; put the serial cmd into the queue call send_byte ; send to CPU movf rcreg, w ; get USART in call send_byte ; send to CPU ; fall through into end_ser_interrupt end_ser_interrupt bcf rp0 ; bank 0 bcf rcif ; clear USART receive interrupt ; fall through to finish_interrupt finish_interrupt ; restore W and STATUS swapf status_temp, w ; put old status in W movwf status ; put into status swapf w_temp, 1 ; swap w_temp in place swapf w_temp, w ; put old W into W retfie send_byte ; put byte that's in w into the out queue ; assumes interrupts are disabled (called from int routine) ; push on queue bcf rp0 ; bank 0 movwf outq_byte_push movlw outq_size subwf outq_length, w btfsc c return ; if q length >= size (overflow), then ; return movf outq_head, w movwf fsr movf outq_byte_push, w movwf indf ; *head = outq_byte_push incf outq_head movf outq_head, w andlw outq_wrap_mask addlw outq_storage movwf outq_head ; head = storage + (head - storage + 1) % size ; i.e. head++ incf outq_length ; length ++ call check_queue return check_queue ; if there is a byte in the queue, get it and send it on to the ; Alice 2 through port D ; assumes interrupts are disabled (called from int routine) bsf rp0 ; bank 1 btfss obf ; skip if byte already on portd goto do_another bcf rp0 ; bank 0 return bsf porta, 0 ; turn off interrupt to CPU do_another bcf rp0 ; bank 0 movf outq_length ; move to itself btfsc z ; skip if not zero return ; if length == 0 return movf outq_tail, w movwf fsr movf indf, w movwf outq_byte_pop ; outq_byte_pop = *tail movwf portd ; send on to Alice bcf pspif ; clear PSP interrupt flag bcf porta, 0 ; turn on interrupt to CPU incf outq_tail movf outq_tail, w andlw outq_wrap_mask addlw outq_storage movwf outq_tail ; tail = storage + (tail - storage + 1) % size ; i.e. tail++ decf outq_length ; length -- return