; ; picmem5.asm -- using USART on pic16f628 ; ; This version only in ASM, no C ; ; This version WORKS. Has a nice little boot message. ; ; ; include "p16F628.inc" __config B'11111110001010' ; Configuration word ;------------------------------------------------------------------------- ; Pin definitions ; ; PORTA LEDROW0 equ D'0' LEDROW1 equ D'1' LEDROW2 equ D'2' LEDROW3 equ D'3' ; PORTB LED equ D'0' ; Debugging LED SHIFTCLK equ D'0' ; Clock to shift regs RXPIN equ D'1' ; Input RS232 TXPIN equ D'2' LED2 equ D'3' ; Debugging LED SHIFTDATA equ D'3' ;LEDROW4 not used equ D'4' LEDROW4 equ D'5' LEDROW5 equ D'6' LEDROW6 equ D'7' ; Defined, but probably never used ;------------------------------------------------------------------------- ; These are defined commands from host PC CMD_READ equ D'200' CMD_SHIFTL equ D'201' CMD_SHIFTR equ D'202' CMD_SHIFTINL equ D'203' CMD_SHIFTINR equ D'204' CMD_INVERT equ D'205' ;------------------------------------------------------------------------- ; Other necessary definitions to help keep things, uh, simple? ;NUM_COLS equ D'145' NUM_COLS equ D'144' ; General purpose RAM variables ***************************************** ; -- interrupt context saving w_temp equ 0x20 ; wtemp in bank0 status_temp equ 0x21 ; status temp var addr_temp equ 0x22 ; temp for addr w_temp_b1 equ 0xA0 ; wtemp in bank1 ; -- function params ms_delay_param equ 0x70 ms_delay_param2 equ 0x71 putch_param equ 0x72 beepled_param equ 0x73 ; -- global vars i equ 0x74 c equ 0x75 rxdata equ 0x76 ; serial receive data cmd1 equ 0x77 ; first byte in incoming cmd addr equ 0x78 ; second byte in incoming command temp equ 0x79 colctr equ 0x7A rowctr equ 0x7B itemp equ 0x7C itemp2 equ 0x7D itemp3 equ 0x7E i_data equ 0x7F ; -- Here's the 145 byte screen layout ; we use 75 (0x4b) bytes in Bank0, and 70 (0x46) bytes in Bank1 ; Bank0 - 0x24 to 0x6F (36 to 111 decimal) ; Bank1 - 0xA9 to 0xEF (169 to 239 decimal) ORG 0 goto start_main_code ;------------------------------------------------------------------------- ; INTERRUPT HANDLER ;------------------------------------------------------------------------- ORG 4 interrupt movwf w_temp ; save current W value swapf STATUS, W ; W gets current status bcf STATUS, RP0 ; switch to bank 0 movwf status_temp ; save old status value movf addr, W movwf addr_temp ; save addr ;-- Serial port interrupt handler btfss PIR1, RCIF ; See if we have receive data goto int_cleanup ; If not, clean up and bail out ; NOTE*** This must be changed to handle other kinds of ; interrupts -- ie. timer!!! movf RCREG, W ; Get serial data movwf rxdata movf cmd1, F ; See if cmd is empty btfss STATUS, Z ; If zero, store new byte to cmd1 goto new_cmd2 ; Otherwise, process new cmd2 new_cmd1 movwf cmd1 ; Store new command 1 goto int_cleanup new_cmd2 movwf addr ; Store the addr byte call handle_command ; and handle the full command clrf cmd1 ; Very important to reset these clrf addr ; after handling the command ;movwf putch_param ;call putch ;-- end serial port interrupt handler int_cleanup movf addr_temp, W movwf addr ; Restore old addr swapf status_temp, W ; W gets old status value movwf STATUS ; restore to STATUS swapf w_temp, F ; swap w_temp swapf w_temp, W ; restore original W retfie interrupt_end ;------------------------------------------------------------------------- ; handle_command - called when cmd1 and addr both populated ; processes a serial port command ;------------------------------------------------------------------------- handle_command movf cmd1, F ; Make sure cmd1 not zero btfsc STATUS, Z ; If so, bump to 1 (zero is invalid "cmd") incf cmd1, F ; Increment bad evil command chk_cmds ; Special cased commands movlw CMD_READ subwf cmd1, W btfss STATUS, Z ; See if we have the CMD_READ comman goto chk_cmds2 ;---- Handle read memory command (CMD_READ) here call cmd_readmem return chk_cmds2 movlw d'146' ; Between 1-145? It's a "write" cmd subwf cmd1, W btfsc STATUS, C goto chk_cmds3 ;---- Handle write memory command here - cmd1 is address (1-145) and addr = data call cmd_writemem return chk_cmds3 movlw CMD_SHIFTL subwf cmd1, W btfss STATUS, Z goto chk_cmds4 ;---- Handle shift left command here call cmd_shiftl return chk_cmds4 movlw CMD_SHIFTR subwf cmd1, W btfss STATUS, Z goto chk_cmds5 ;---- Handle shift right command here call cmd_shiftr return chk_cmds5 movlw CMD_INVERT subwf cmd1, W btfss STATUS, Z goto chk_cmds6 ;---- Handle invert command here call cmd_invert return chk_cmds6 movlw CMD_SHIFTINL subwf cmd1, W btfss STATUS, Z goto chk_cmds7 ;---- Handle shift in left command here call cmd_shiftinl return chk_cmds7 movlw CMD_SHIFTINR subwf cmd1, W btfss STATUS, Z goto chk_cmds8 ;----- Handle shift in right command here call cmd_shiftinr return chk_cmds8 ; TODO: Handle other special cases here handle_command_done return handle_command_end ;------------------------------------------------------------------------- ; cmd_shiftinr ;------------------------------------------------------------------------- cmd_shiftinr movf addr, W movwf itemp3 ; Because cmd_shiftr uses addr, ; we have to store it movlw D'1' movwf addr call cmd_shiftl ; Shift display one byte right movf itemp3, W movwf i_data movlw NUM_COLS movwf addr decf addr, 1 call writemem return ;------------------------------------------------------------------------- ; cmd_shiftinl - shift sign left 1 byte and set screen[0] to addr ;------------------------------------------------------------------------- cmd_shiftinl movf addr, W movwf itemp3 ; Because cmd_shiftr uses addr, ; we have to store it movlw D'1' movwf addr call cmd_shiftr ; Shift display one byte right movf itemp3, W movwf i_data movlw D'1' movwf addr call writemem return ;------------------------------------------------------------------------- ; cmd_invert - inverts memory ;------------------------------------------------------------------------- cmd_invert movlw NUM_COLS movwf itemp decf itemp, F cmd_inv_loop movf itemp, W movwf addr call readmem comf i_data, F movf itemp, W movwf addr call writemem decf itemp, F btfss STATUS, Z goto cmd_inv_loop return ;------------------------------------------------------------------------- ; cmd_shiftr - shift memory right ;------------------------------------------------------------------------- cmd_shiftr movlw NUM_COLS movwf itemp2 decf itemp2, F movwf itemp movf addr, W subwf itemp, F decf itemp, F ; Our indexes here are ; itemp = low index ; itemp2 = high index (starts at NUM_COLS) shiftr_loop movf itemp, W movwf addr call readmem ; get memory at itemp2 ; i_data now contains data movf itemp2, W movwf addr call writemem ; put data at itemp decf itemp2, F decf itemp, F btfss STATUS, Z goto shiftr_loop ; If we're here, addr hit end of sign, so we need to fill zeros ; from itemp2 to beginning of sign clrf i_data shiftr_loop2 movf itemp2, W movwf addr call writemem decf itemp2, F btfss STATUS, Z goto shiftr_loop2 return ;------------------------------------------------------------------------- ; cmd_shiftl - shifts memory left ;------------------------------------------------------------------------- cmd_shiftl clrf itemp incf itemp, F ; itemp = 1 incf addr, F movf addr, W movwf itemp2 ; Our indexes here are ; itemp = low index ; itemp2 = high index shiftl_loop movf itemp2, W movwf addr call readmem ; get memory at itemp2 ; i_data now contains data movf itemp, W movwf addr call writemem ; put data at itemp incf itemp, F incf itemp2, F movf itemp2, W sublw NUM_COLS btfss STATUS, Z goto shiftl_loop ; If we're here, addr hit end of sign, so we need to fill zeros ; from itemp to end of sign clrf i_data shiftl_loop2 movf itemp, W movwf addr call writemem incf itemp, F movf itemp, W sublw NUM_COLS btfss STATUS, Z goto shiftl_loop2 return ;------------------------------------------------------------------------- ; cmd_writemem - handles the writemem command ;------------------------------------------------------------------------- cmd_writemem ; Because cmd1 is address and addr is data, we have to ; switch these around... movf addr, W movwf itemp ; switcharoo movf cmd1, W movwf addr movf itemp, W movwf i_data call writemem return; ;------------------------------------------------------------------------- ; cmd_readmem - handles the readmem command from the PC ;------------------------------------------------------------------------- cmd_readmem call readmem movf i_data, W movwf putch_param call putch return ;------------------------------------------------------------------------- ; writemem - writes value i_data to location addr. Addr will be changed ;------------------------------------------------------------------------- writemem call reformat_mem movf addr, W movwf FSR movf i_data, W movwf INDF bcf STATUS, RP0 return ;------------------------------------------------------------------------- ; readmem - reads screen memory, addr is param and will be changed ; by this function. returns the value at addr in i_data ;------------------------------------------------------------------------- readmem call reformat_mem ; Reformat memory ; at this point, addr is modified and bank settings are right movf addr, W movwf FSR movf INDF, W bcf STATUS, RP0 movwf i_data return ;------------------------------------------------------------------------- ; reformat_mem -- reformats addr to fit within memory requirements. ; BECAUSE this handles banking, STATUS bits could be changed when ; this function returns. Care must be taken! ;------------------------------------------------------------------------- reformat_mem movlw NUM_COLS subwf addr, W btfss STATUS, C ; See if the address given > 145 goto reformat_mem2 ; Too big of an address, reformat to 1 movlw D'1' movwf addr reformat_mem2 ; Now have address in range 1-145 movlw d'75' subwf addr, W ; If address > 75, have to use bank 1 btfss STATUS, C ; See if we're in bank 1 goto reformat_mem3 ; Address is in bank 1 addlw 0xA9 ; Since we didn't carry, w has diff from addr ; to 75. Tag this on to bank1 offset movwf addr bsf STATUS, RP0 return reformat_mem3 ; Address is in bank 0 movlw 0x24 addwf addr, F return reformat_mem_end ;------------------------------------------------------------------------- ; MAIN CODE ENTRY POINT ;------------------------------------------------------------------------- start_main_code ; Set up / initialization bcf INTCON, GIE ; Disable global inerrupts bcf STATUS, RP1 ; Select bank 1 bsf STATUS, RP0 clrf TRISA ; PORTA all outputs movlw b'00000010' ; RB1 is input (RX) movwf TRISB ;movlw D'129' ; Set baud rate value - 9600 movlw D'10' ; Set baud rate value - 15200 movwf SPBRG ; (taken from data sheet) bsf TXSTA, BRGH ; Use high-speed baud generator bcf TXSTA, SYNC ; Clear SYNC (use async mode) bsf TXSTA, TXEN ; Enable transmission bcf TXSTA, TX9 ; Don't use 9-bit transmission bsf PIE1, RCIE ; Enable serial receive interrupt bcf STATUS, RP0 ; Select bank 0 bsf RCSTA, CREN ; Enable serial port receiver bsf RCSTA, SPEN ; Enable serial port bsf INTCON, PEIE ; Enable peripheral interrupts (serial port) bsf INTCON, GIE ; Enable global inerrupts movlw D'7' movwf CMCON ; Disable PORTA comparators ;---- Variable inits --- movlw b'00001111' movlw b'11110111' ; PORTB initial state (dbgled2 off) movwf PORTB clrf c clrf cmd1 clrf addr ; Load the on-boot message clrf colctr bsf colctr, 0 loadbootmsg movf colctr, W movwf addr movlw HIGH bootmsg ; load PCLATH with hi address of table movwf PCLATH movf colctr, W call bootmsg movwf i_data call writemem incf colctr, F movlw NUM_COLS subwf colctr, W btfss STATUS, Z goto loadbootmsg ; Display loop...shift out bits, toggle rows main_loop rows_start clrf rowctr ; rowctr is actually a bit mask... bsf rowctr, 0 each_row cols_start clrf colctr ; Start off colctr at 1 incf colctr, F ; which is first mem location main_loop2 ; TODO: Put input data on shift register input movf colctr, W movwf addr call reformat_mem movf addr, W movwf FSR movf INDF, W ; Read using pointer value in FSR bcf STATUS, RP0 ; Ensure back in bank 0 movwf temp ; Find out if we need to shift in 1 or 0 bsf PORTB, SHIFTDATA movf temp, W ; W has memory value andwf rowctr, W ; Do the mask btfsc STATUS, Z ; btfsc temp, 0 ; Fix this...just testing bcf PORTB, SHIFTDATA ; Do clock pulse to shift regs bcf PORTB, SHIFTCLK bsf PORTB, SHIFTCLK incf colctr, F movlw d'145' ; huh??? subwf colctr, W btfss STATUS, Z goto main_loop2 ;-------- ; btfsc rowctr, 0 ; Fix this, TESTING!!! ; bcf PORTA, LEDROW0 ; Fix this, testing!!! ; movf rowctr, W ; Find out if we're targeting PORTA or PORTB movlw b'00001111' andwf rowctr, W btfss STATUS, Z ; If zero, then we're on PORTB goto rowporta rowportb comf rowctr, W movwf temp rlf temp, W ; THIS IS A BIG FAT RB4 hack!!! ; andlw b'11110000' andwf PORTB, F goto row_is_on rowporta comf rowctr, W ; andlw b'00001111' andwf PORTA, F row_is_on ; this is just debug shit movlw D'1' ; This yields right around 70Hz display refresh ;movlw D'200' movwf ms_delay_param call ms_delay ; bsf PORTA, LEDROW0 ; Fix this, testing!!! movlw b'00001111' iorwf PORTA, F movlw b'11100000' iorwf PORTB, F movf rowctr, W addwf rowctr, F ; Adding to itself = x+x = 2*x = << 1 btfss rowctr, d'7' ; If bit 7 is set, we're done with all rows goto each_row goto rows_start main_end ;------------------------------------------------------------------------- ; putch -- writes a character to the serial port. ; will block until TXIF bit flag is clear and TXREG can be loaded with ; new data. Uses value in putch_param, or W if you call putch_w ;------------------------------------------------------------------------- putch movf putch_param, W ; Load param into W putch_w btfss PIR1, TXIF ; Wait for TXIF high goto putch_w movwf TXREG ; Load TXREG return putch_end ;------------------------------------------------------------------------- ; beepled -- blinks the LED for the number of times passed in beepled_param ; there is a 100ms delay between beeps ; This can probably be taken out for production YO ;------------------------------------------------------------------------- beepled bsf PORTB, LED ; Turn on LED movlw D'100' movwf ms_delay_param call ms_delay bcf PORTB, LED movlw D'100' movwf ms_delay_param call ms_delay decfsz beepled_param, F ; Decrement counter goto beepled return beepled_end ;------------------------------------------------------------------------- ; Delays for milliseconds -- no clue just how accurate this is ; should probably be checked for accuracy ; Delay value is passed in ms_delay_param ;------------------------------------------------------------------------- ms_delay label_0002 movlw D'185' movwf ms_delay_param2 nop label_0003 nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop decfsz ms_delay_param2, F goto label_0003 nop decfsz ms_delay_param, F goto label_0002 nop return ms_delay_end ;------------------------------------------------------------------------- ; defines the initial boot message ; (it currently says: http://noisybox.net) ;------------------------------------------------------------------------- org d'1024' bootmsg addwf PCL, F dt 0 dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x04,0x04,0x38,0x00,0x04,0x1F dt 0x24,0x20,0x00,0x04,0x1F,0x24,0x20,0x00,0x7C,0x24,0x24,0x18,0x00,0x00,0x36,0x36 dt 0x00,0x00,0x10,0x08,0x04,0x02,0x00,0x10,0x08,0x04,0x02,0x00,0x3C,0x04,0x04,0x38 dt 0x00,0x18,0x24,0x24,0x18,0x00,0x24,0x3D,0x20,0x00,0x28,0x2C,0x34,0x14,0x00,0x0C dt 0x50,0x20,0x1C,0x00,0x3F,0x24,0x24,0x18,0x00,0x18,0x24,0x24,0x18,0x00,0x24,0x18 dt 0x18,0x24,0x00,0x00,0x30,0x30,0x00,0x00,0x3C,0x04,0x04,0x38,0x00,0x18,0x34,0x2C dt 0x08,0x00,0x04,0x1F,0x24,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 128 bytes so far, not including initial zero dt 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ; 16 * 9 = 144 END