;
; 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
