;*******************************************************************************
;                                                                              *
;    Filename:            hellschreiber                                   *
;    Date:                2016-11-08                                          *
;    File Version:        "$Revision: 1.12 $"
;    Author:              Andy Fletcher                                        *
;    Description:         Hellschreiber Modem
;                 
    ; Bank usage:
    ;	Bank 0: main loop and debug code and data
    ;	Bank 1: receive and display code and data
    ;	Bank 2: transmit code
    ;	Bank 3: transmit data
    
    ; TODO: 
    ; FIX - the 'W' character has the last upstroke missing, 
    ;       or it might be the middle stroke
    ; FIX - don't allow UART to accept chars < 0x20 (-- convert to # ?)
    ; TODO: can the git revision be inserted?
;*******************************************************************************

    LIST R=DEC
    INCLUDE "p16f887.inc"

    ;
    ; i/o definitions
    ;
    #define TX_LED PORTA, 4
    #define PON_LED PORTA, 5
    #define OLED_RES_PIN PORTD, 0
    #define OLED_DC_PIN PORTD, 1
    #define OLED_CS_PIN PORTD, 2
    #define HELL_TX_PIN PORTE, 0
    #define HELL_RX_PIN PORTE, 1
    #define RX_TX_SWITCH PORTB, 5   ; set lo for rx, hi for tx
    
    ;
    ; OLED definitions
    ;
    #define SSD1306_LCDWIDTH                  128
    #define SSD1306_LCDHEIGHT                 64
    ; 
    ; OLED commands
    ;
    #define SSD1306_SETCONTRAST 0x81
    #define SSD1306_DISPLAYALLON_RESUME 0xA4
    #define SSD1306_DISPLAYALLON 0xA5
    #define SSD1306_NORMALDISPLAY 0xA6
    #define SSD1306_INVERTDISPLAY 0xA7
    #define SSD1306_DISPLAYOFF 0xAE
    #define SSD1306_DISPLAYON 0xAF

    #define SSD1306_SETDISPLAYOFFSET 0xD3
    #define SSD1306_SETCOMPINS 0xDA

    #define SSD1306_SETVCOMDETECT 0xDB

    #define SSD1306_SETDISPLAYCLOCKDIV 0xD5
    #define SSD1306_SETPRECHARGE 0xD9

    #define SSD1306_SETMULTIPLEX 0xA8

    #define SSD1306_SETLOWCOLUMN 0x00
    #define SSD1306_SETHIGHCOLUMN 0x10

    #define SSD1306_SETSTARTLINE 0x40

    #define SSD1306_MEMORYMODE 0x20
    #define SSD1306_COLUMNADDR 0x21
    #define SSD1306_PAGEADDR   0x22

    #define SSD1306_COMSCANINC 0xC0
    #define SSD1306_COMSCANDEC 0xC8

    #define SSD1306_SEGREMAP 0xA0

    #define SSD1306_CHARGEPUMP 0x8D

    #define SSD1306_EXTERNALVCC 0x1
    #define SSD1306_SWITCHCAPVCC 0x2

    ; Scrolling 
    #define SSD1306_ACTIVATE_SCROLL 0x2F
    #define SSD1306_DEACTIVATE_SCROLL 0x2E
    #define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3
    #define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26
    #define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27
    #define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29
    #define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A
    
    variable UART_buf_size=32
    variable UART_buf_2n=5	; UART_buf_size must equal 2^UART_buf_2n
    
    variable ajfdiag_sim=0	; set if simulating
 
    __CONFIG _CONFIG1, _WDTE_OFF & _PWRTE_OFF & _CP_OFF & _BOREN_ON & _FCMEN_ON & _MCLRE_OFF & _CPD_OFF & _IESO_ON & _FOSC_INTRC_CLKOUT & _LVP_OFF
 
    extern hell_dot_delay
 
;*******************************************************************************
; Reset Vector
;*******************************************************************************

RES_VECT  CODE    0x0000            ; processor reset vector
    goto    START                   ; go to beginning of program (bank 0)

    ;
    ; ISR
    ;
ISR	  CODE 0x0004
    movwf w_temp		    ; save context
    swapf STATUS,w		    ;put unchanged STATUS in w with nibbles
    clrf STATUS			    ; use RAM page 0
    movwf status_temp 
    movf PCLATH, w
    movwf pclath_temp
    clrf PCLATH			    ; ensure on page 0

    ;
    ; ignore interrupt unless it's from the UART
    ;
    banksel PIE1
    btfss PIE1, RCIE
      goto ISR_restore
    banksel PIE1
    btfsc PIR1, RCIF
      call UART_ISR

ISR_restore:			    ; restore context
    movf pclath_temp, w
    movwf PCLATH
    swapf status_temp, w
    movwf STATUS
    movf w_temp, w ; TODO is this ok examples have 2 x swapf
    retfie
    
    ;
    ; UART ISR
    ; store the received character into the UART buffer and adjust pointer
    ;
UART_ISR:
    banksel RCSTA
    movf RCSTA, w
    movwf UART_temp
    btfsc UART_temp, FERR
      goto UART_error
    movlw UART_buf		; get address for indirect addressing	
    addwf UART_inbufptr, w
    movwf FSR			; setup FSR
    banksel RCREG
    movf RCREG, w		; get received byte
    movwf INDF			; and store it in the buffer
    incf UART_inbufptr, f	; update input pointer
    btfsc UART_inbufptr, UART_buf_2n	; wrap as necessary
      clrf UART_inbufptr   
    incf UART_buf_count, f	; and count
    call uart_out		; echo rx'ed char to UART
    bcf PIR1, RCIF		; clear the interrupt for next time
    return
      
UART_error:			; clear UART error 
    bcf RCSTA, CREN
    nop
    nop
    bsf RCSTA, CREN
    movf RCREG, w
    return 
    
    ;
    ; main data is in bank 0
    ;
    org 0x0040
BANK_0    udata
     ; note: this UART_buf is circular but it must be emptied before it gets full
     ;       or characters will be lost
UART_buf res UART_buf_size
UART_temp res 1
UART_buf_count res 1	    ; how many characters are in the input buffer
UART_inbufptr res 1	    ; where to put next byte rx from UART
UART_outbufptr res 1	    ; the next byte for output to air 
 
hell_num res 1
hell_char_index res 1
 
; context save area
w_temp res 1
status_temp res 1
pclath_temp res 1

; delay vars
d1 res 1
d2 res 1
d3 res 1
 
;*******************************************************************************
; MAIN PROGRAM
;*******************************************************************************
 
MAIN_PROG CODE 0x0060                     ; is in bank 0

START

    ;
    ;   configure i/o ports
    ;   configure all as digital unless needed otherwise
    ;
    banksel ANSEL
    clrf ANSEL & 0x7F
    banksel ANSELH
    clrf ANSELH & 0x7F

    banksel CM1CON0
    clrf CM1CON0 & 0x7F			; disable comparators
    banksel CM2CON0
    clrf CM2CON0 & 0x7F

    banksel PORTA
    clrf PORTA
    banksel TRISA
    movlw b'001111'			; all of PORTA are inputs 
    movwf TRISA	& 0x7F	; except RA5 (PON LED) and RA4 (DEBUG LED)

    banksel PORTA   
    bsf PON_LED				; turn on the power-on LED   
    
    banksel TRISB
    movlw 0xFF				; all of PORTB are inputs
    movwf TRISB & 0x7F 
    
    banksel WPUB
    movlw b'00100000'			; pull-up RB5 - rx/tx switch
    movwf WPUB
    banksel OPTION_REG
    bcf OPTION_REG, NOT_RBPU		; and turn on pull-ups globally
    

    banksel TRISC
    movlw b'11010111'			; all of PORTC are inputs
    movwf TRISC & 0x7F			; except SPI ports RC3 and RC5
					; RC3 is SCK (master)
					; (BTW RC4/SDI isn't used)

    movlw 0xD0				; all of PORTD are inputs
    movwf TRISD & 0x7F			; except RD0, RD1 and RD2 (for OLED)
   
    movlw 0xFE				; all of PORTE are inputs
    movwf TRISE & 0x7F                  ; except RE0 (Hell tx)

;   set clock to 8MHz
    movlw b'1110000'			; set IRCF0-2
    banksel OSCCON
    movwf OSCCON & 0x7F

;   and tune to around 8MHz
    clrf OSCTUNE & 0x7F

;   set EUSART baud rate to 19200, assuming a 8MHz clock
    banksel TXSTA
    bsf TXSTA & 0x7F, BRGH

    banksel BAUDCTL
    bcf BAUDCTL & 0x7F, BRG16

    banksel SPBRGH
    clrf SPBRGH & 0x7F

    movlw 25
    banksel SPBRG
    movwf SPBRG & 0x7F
;   
;   enable EUSART TX
;
    banksel TXSTA
    bsf TXSTA & 0x7F, TXEN
    bcf TXSTA & 0x7F, SYNC

    banksel RCSTA
    bsf RCSTA, SPEN
        
    ;
    ; enable EUSART RX
    ;
    bcf RCSTA, RX9
    bsf RCSTA, CREN
    bcf RCSTA, ADDEN
    
    ; initialise UART input buffer
    banksel UART_inbufptr
    clrf UART_inbufptr
    clrf UART_outbufptr
    clrf UART_buf_count

    ; 
    ; enable interrupts
    ;
    banksel PIE1
    bsf PIE1, RCIE		; enable EUSART
    bsf INTCON, PEIE		; enable all peripheral interrupts
    bsf INTCON, GIE		; enable global interrupt 
   
    ;
    ; initialise the OLED
    ;
    pagesel start_OLED
    call start_OLED
    pagesel loop

;   
;   loop forever
;
loop:
    banksel PORTE
    bcf HELL_TX_PIN; idle with no tone

    ;
    ; check the rx/tx switch
    ; and transmit or receive as required
    ;
;    if ajfdiag_sim == 0
    pagesel transmit_loop
    btfsc RX_TX_SWITCH
;    endif
      goto transmit_loop
    
      ; prepare OLED display for receive
    banksel PORTD
    pagesel send_OLED_command
    movlw SSD1306_PAGEADDR;
    call send_OLED_command
    movlw 0; // Page start address (0 = reset)
    call send_OLED_command
    movlw 7; // Page end address
    call send_OLED_command
    
    movlw SSD1306_COLUMNADDR
    call send_OLED_command
    movlw 0x00; first and selected column
    call send_OLED_command
    movlw 0x7F; last column
    call send_OLED_command

    ;
    ; receive loop
    ;
receive_loop:    
    banksel PORTA   
    bcf TX_LED				; turn the transmit LED off
    pagesel receive_hell;
    goto receive_hell
    
check_tx_rx_switch:
    pagesel receive_loop
;    if ajfdiag_sim == 0
    btfss RX_TX_SWITCH
      goto receive_loop    
;    endif
    
    pagesel loop
    goto loop
    
    ;
    ; transmit loop
    ;
transmit_loop:
    ;
    ; get a character to transmit if there is one
    ; otherwise idle
    ;
    banksel PORTA   
    bsf TX_LED				; turn the transmit LED on
    if ajfdiag_sim == 1
	call UART_ISR
	call UART_ISR
    endif
    banksel UART_buf_count
    movf UART_buf_count, w	    ; get number of bytes in UART buffer
    btfsc STATUS, Z		    ; is the buffer empty?
      goto transmit_idle	    ; yes, idle
				    				; TODO shd check for ovf
    banksel PIE1
    bcf PIE1, RCIE		; disable EUSART rx interrupts
    banksel UART_buf_count	    
    decf UART_buf_count, f          ; decrement buffer counter
    banksel PIE1
    bsf PIE1, RCIE		; enable EUSART rx interrupts

    
    banksel UART_outbufptr
    movlw UART_buf		; get address for indirect addressing
    addwf UART_outbufptr, w
    movwf FSR			    ; setup FSR, it's on page 0 so IRP is ok
    movf INDF, w		    ; get the byte
    
    ;
    ; transmit the Hell character
    ;    
    movwf hell_num
    pagesel out_hell_char
    call out_hell_char
    pagesel transmit_loop

    banksel UART_outbufptr
    incf UART_outbufptr, f		; update the output pointer
    btfsc UART_outbufptr, UART_buf_2n	; wrap as necessary
      clrf UART_outbufptr   
    
    goto check_tx_rx_switch

transmit_idle:
    ; delay a column time on idle
    			;114193 cycles
	movlw	0x36
	movwf	d1
	movlw	0x5A
	movwf	d2
idle_delay_0
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	idle_delay_0

			;3 cycles
	goto	$+1
	nop

    goto check_tx_rx_switch	    
    
    ;
    ; UART routines
    ;	
uart_out:
    banksel PIR1
    if ajfdiag_sim == 0
    btfss PIR1, TXIF
      goto uart_out
    endif
    banksel TXREG
    movwf TXREG ; to UART
    return
    
					  ; Receive code and data is in bank 1
    org 0x0800

BANK_1 udata
fill_char res 1
top_col_rxed res 1
bottom_col_rxed res 1
prev_top_col_rxed res 1
prev_bottom_col_rxed res 1
hell_col_rx_index res 1
x res 1
y res 1

RECEIVE CODE 0x0810                     ; is in bank 1
 
; receive Hellschreiber
receive_hell:
    ;
    ; receive a column
    ;
    banksel bottom_col_rxed
    movf bottom_col_rxed, w     ; save off the previous column for later display
    movwf prev_bottom_col_rxed
    movf top_col_rxed, w
    movwf prev_top_col_rxed
    
    movlw 0x80			; receive the bottom 8 samples
    movwf hell_col_rx_index	; denotes bit being sampled
    clrf bottom_col_rxed	; reset the bottom column bits
rx_bottom_col:
    call in_hell_sample_delay	; only sampling once, so wait a half-pixel
    
    movf hell_col_rx_index, w	; sample the pin
    btfss HELL_RX_PIN
      iorwf bottom_col_rxed, f
    
    rrf hell_col_rx_index, f	; set up for next sample
    btfss STATUS, C             ; done all lower 8 column samples?
      goto rx_bottom_col
            
                                ; now rx the 6 samples of the top of the column
    movlw 0x80
    movwf hell_col_rx_index
    clrf top_col_rxed		; reset the top column bits
rx_top_col:
    call in_hell_sample_delay	; only sampling once, so wait a half-pixel
    
    movf hell_col_rx_index, w	; sample the pin
    btfss HELL_RX_PIN
      iorwf top_col_rxed, f
    
    rrf hell_col_rx_index, f	; set up for next sample
    movf hell_col_rx_index, w
    andlw 0x02			; done all top 6 column samples?
    btfsc STATUS, Z
      goto rx_top_col

      ;
      ; display the column
      ;    
    pagesel send_OLED_command
    movlw SSD1306_RIGHT_HORIZONTAL_SCROLL
    call send_OLED_command
    movlw 0x00; dummy
    call send_OLED_command
    movlw 0x00; start page
    call send_OLED_command
    movlw 0x00; coded frames/step
    call send_OLED_command
    movlw 0x07; end page
    call send_OLED_command
    movlw 0x00; dummy
    call send_OLED_command
    movlw 0xFF; dummy
    
    movlw SSD1306_ACTIVATE_SCROLL
    call send_OLED_command
    
        ; write the column
    bsf OLED_CS_PIN		; disable chip select
    bsf OLED_DC_PIN
    bcf OLED_CS_PIN		; enable chip select  
      
    ; TODO
    ; mash the 14 dots together with no gap at end
	; T7 T6 T5 T4 T3 T2 B7 B6  ) using current column
	; B5 B4 B3 B2 B1 B0 T7 T6  ) 
	
	; T5 T4 T3 T2 B7 B6 B5 B4  ) using previous column
	; B3 B2 B1 B0  0  1  1  0  ) 
	;              ^  ^  ^  ^  - ends with a thick line
	
    ; For the moment, fill in the top of the OLED column w/o mashing       
    
	; write top dots from this column 
    banksel top_col_rxed
    movf top_col_rxed, w
    call send_to_OLED
	
	; write bottom dots from this column
    banksel bottom_col_rxed
    movf bottom_col_rxed, w
    call send_to_OLED    
    
	; and repeat, Hellschreiber style
	    
        ; write top dots from previous column
    banksel prev_top_col_rxed
    movf prev_top_col_rxed, w
    call send_to_OLED
    
    	; write bottom dots from previous column
    banksel prev_bottom_col_rxed
    movf prev_bottom_col_rxed, w
    call send_to_OLED    
    
    ; blank the bottom of the screen 
    movlw 0
    call send_to_OLED  
    
    movlw 0
    call send_to_OLED  
    
    movlw 0
    call send_to_OLED  
    
    movlw 0
    call send_to_OLED  
    
    movlw SSD1306_DEACTIVATE_SCROLL
    call send_OLED_command

      ;
      ; check the rx/tx switch
      ;
    pagesel check_tx_rx_switch
    goto check_tx_rx_switch
        
    ;
    ; OLED routines
    ;
start_OLED:    
    ;
    ; enable SPI
    ;
    movlw B'01000000'		; SPI, CKE=1
    banksel SSPSTAT
    movwf SSPSTAT & 0x7F
    banksel SSPCON		; CKP=1 SPI Master Mode, 1/4 Fosc bit time
    movlw B'00110000'
    movwf SSPCON		; SSP is on
    
    ; power up OLED
    banksel PORTD
    bsf OLED_RES_PIN
    
    movlw SSD1306_DISPLAYOFF
    call send_OLED_command
    
    movlw SSD1306_SETDISPLAYCLOCKDIV	; set display clock divide ratio
    call send_OLED_command
    movlw 0x80
    call send_OLED_command
    
    movlw SSD1306_SETMULTIPLEX
    call send_OLED_command
    movlw SSD1306_LCDHEIGHT - 1
    call send_OLED_command
    
    movlw SSD1306_SETDISPLAYOFFSET
    call send_OLED_command
    movlw 0x00;                                   no offset
    call send_OLED_command
    
    movlw SSD1306_SETSTARTLINE | 0x00;             line #0
    call send_OLED_command
    
    movlw SSD1306_CHARGEPUMP
    call send_OLED_command
    movlw 0x14
    call send_OLED_command
    
    movlw SSD1306_MEMORYMODE
    call send_OLED_command
    movlw 0x01;                                  vertical addressing
    call send_OLED_command
    
    movlw SSD1306_SEGREMAP | 0x01
    call send_OLED_command
    
    movlw SSD1306_COMSCANDEC
    call send_OLED_command
    
    movlw SSD1306_SETCOMPINS                    
    call send_OLED_command
    movlw 0x12
    call send_OLED_command
    
    movlw SSD1306_SETCONTRAST
    call send_OLED_command
    movlw 0xCF
    call send_OLED_command
    
    movlw SSD1306_SETPRECHARGE
    call send_OLED_command
    movlw 0xF1
    call send_OLED_command
    
    movlw SSD1306_SETVCOMDETECT
    call send_OLED_command
    movlw 0x40
    call send_OLED_command
    
    movlw SSD1306_DISPLAYALLON_RESUME
    call send_OLED_command
    
    movlw SSD1306_NORMALDISPLAY
    call send_OLED_command
    
    movlw SSD1306_DISPLAYON; turn on OLED panel
    call send_OLED_command
   
    movlw SSD1306_DEACTIVATE_SCROLL
    call send_OLED_command
    
    movlw SSD1306_COLUMNADDR
    call send_OLED_command
    movlw 0;   // Column start address (0 = reset)
    call send_OLED_command
    movlw SSD1306_LCDWIDTH - 1; // Column end address (127 = reset)
    call send_OLED_command
    
    movlw SSD1306_PAGEADDR;
    call send_OLED_command
    movlw 0; // Page start address (0 = reset)
    call send_OLED_command
    movlw 7; // Page end address
    call send_OLED_command

    movlw 0AAh
    banksel fill_char
    movwf fill_char
    call fill_OLED_screen; clear the screen
    
    return
    
    ;
    ; send character in WREG to OLED
    ;
send_to_OLED:
    banksel SSPBUF
    movwf SSPBUF		; start SPI transfer
    
    banksel SSPSTAT		; poll for transfer complete
send_OLED_char:
    if ajfdiag_sim == 0
    btfss SSPSTAT & 0x7F,BF		; buffer full?
      goto send_OLED_char		; if not, check again
    endif   
    banksel SSPBUF		; throw away input data as it isn't used
    movf SSPBUF, w
    
    return
    
    ;
    ; send command in WREG to OLED
    ;
send_OLED_command:
    bsf OLED_CS_PIN		; disable chip select
    bcf OLED_DC_PIN
    bcf OLED_CS_PIN		; enable chip select

    call send_to_OLED
    
    bsf OLED_CS_PIN		; disable chip select
    
    return
 
    ;
    ; fill the OLED screen with the character in 'fill_char'
    ;
fill_OLED_screen:    
    ; clear screen 128 x 64 = 8192
    bsf OLED_CS_PIN		; disable chip select
    bsf OLED_DC_PIN
    bcf OLED_CS_PIN		; enable chip select

    ;8188 cycles
    movlw	0x65
    movwf	d1
    movlw	0x07
    movwf	d2
Delay_clear
    movf fill_char, w
    call send_to_OLED

    decfsz	d1, f
    goto	$+2
    decfsz	d2, f
    goto	Delay_clear

		    ;4 cycles 
    movf fill_char, w
    call send_to_OLED    
    movf fill_char, w
    call send_to_OLED 
    movf fill_char, w
    call send_to_OLED
    movf fill_char, w
    call send_to_OLED

    return
    
    ;
    ; delay for long enough so that a receive loop is 
    ; one Hellschreiber dot time long
    ;
in_hell_sample_delay:
    			;8118 cycles
	movlw	0x57
	banksel d1
	movwf	d1
	movlw	0x07
	movwf	d2
ihsDelay_dot
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	ihsDelay_dot

			;2 cycles
	goto	$+1
    
        return
	
    ;	
    ; Transmit code and data are in banks 2 and 3
    ;
    
TRANSMIT CODE 0x1000                     ; is in bank 2
    ;
    ; Hellschreiber output routines
    ;
    
    ;
    ; send character in hell_num to output pin, Hellschreiber style
    ;
out_hell_char: 
    movlw 0
    movwf hell_char_index
    movf hell_num, w   
    call get_hell_addr
out_hell_column:
    ; handle bottom 8 dots and top 6 dots separately
    call get_hell_half_col

    movlw 0x80
    movwf hell_col_index    
hell_bottom_col_out:    
    call out_hell_dot
    rrf hell_col_index, f
    btfss STATUS, C             ; done all lower 8 column dots?
      goto hell_bottom_col_out
            
                                ; now do the six dots of the top of the column 
    movlw 0x80
    movwf hell_col_index
    incf hell_char_index, f
    call get_hell_half_col

hell_top_col_out:
    call out_hell_dot
    rrf hell_col_index, f 
    movf hell_col_index, w
    andlw 0x02			; done all top 6 column bits?
    btfsc STATUS, Z
      goto hell_top_col_out
                       
				; set up for next column, unless
				; finished with character						        				   
    call get_hell_half_col
    movf curr_half_col, w
    andlw 0x01
    btfss STATUS, Z       
      goto hell_char_finished
    
    incf hell_char_index, f
    goto out_hell_column
    
hell_char_finished:
    return
    
    ;
    ; output a dot (or blank) for a dot time length
    ;
out_hell_dot:
    banksel PORTE
    bcf HELL_TX_PIN
    banksel curr_half_col
    movf curr_half_col, w
    andwf hell_col_index, w
    btfss STATUS, Z
      goto print_dot

out_hell_dot_delay:

    			;8118 cycles
	movlw	0x57
	banksel d1
	movwf	d1
	movlw	0x07
	movwf	d2
Delay_dot
	decfsz	d1, f
	goto	$+2
	decfsz	d2, f
	goto	Delay_dot

			;2 cycles
	goto	$+1

    return
    
print_dot:
    banksel PORTE
    bsf HELL_TX_PIN
    goto out_hell_dot_delay
    
    ; expects character ISO8859-1 value in WREG, 
    ; on return address is in hell_index_[hi|lo]
get_hell_addr:
    banksel hell_index_lo
    movwf hell_index_lo
    movwf hell_index_hi
    
    movlw 0xF0
    andwf hell_index_hi, f
    swapf hell_index_hi, f
    movlw HIGH hell_chars
    addwf hell_index_hi, f
    
    movlw 0x0F
    andwf hell_index_lo, f
    swapf hell_index_lo, f
     
    return    

    ; expects char to get in hell_index_hi and hell_index_lo
get_hell_half_col:
    movf hell_index_hi, w
    movwf PCLATH
    movf hell_index_lo, w
    addwf hell_char_index, w
    call hell_chars
    movwf curr_half_col
    movlw HIGH get_hell_half_col
    movwf PCLATH 
    return
    
    ; 
    ; transmit data is in bank 3
    ;
    
hell_chars: org 0x1800
    movwf PCL

BANK_3 udata
hell_index_hi res 1
hell_index_lo res 1
hell_col_index res 1
curr_half_col res 1
 
    ; Hellschreiber dot arrays
    ; no more than 0x7E arrays are allowed
    ; characters are by column going up, starting at LHS of pixel matrix
    ; character values under SPC are unused, hence the org below
hell_SPC: org 0x1A00
   dt 00H, 00H,  000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_SHREIK:
   dt 00H, 00H,  0cfH, 0f0H, 0cfH, 0f0H, 000H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_QUOTE:
   dt 00H, 00H,  000H, 070H, 000H, 000H, 000H, 070H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_HASH:
   dt 00H, 00H,  00cH, 0c0H, 03fH, 0f0H, 00cH, 0c0H, 03fH, 0f0H, 00cH, 0c0H,  00H, 01H, 00H, 00H
hell_DOLLAR:
   dt 00H, 00H,  033H, 0c0H, 066H, 060H, 0ffH, 0f0H, 066H, 060H, 03cH, 0c0H,  00H, 01H, 00H, 00H
hell_PERCENT:
   dt 00H, 00H,  0e0H, 070H, 018H, 070H, 006H, 000H, 0e1H, 080H, 0e0H, 070H,  00H, 01H, 00H, 00H
hell_AMPERSAND:
   dt 00H, 00H,  07cH, 0e0H, 0c3H, 030H, 0ccH, 0e0H, 0f0H, 000H, 0c0H, 000H,  00H, 01H, 00H, 00H
hell_39:
   dt 00H, 00H,  000H, 0f0H, 000H, 0f0H, 000H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_OPEN_BRACKET:
   dt 00H, 00H,  01fH, 080H, 039H, 0c0H, 060H, 060H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_CLOSE_BRACKET:
   dt 00H, 00H,  060H, 060H, 039H, 0c0H, 01fH, 080H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_STAR:
   dt 00H, 00H,  066H, 060H, 01fH, 080H, 00fH, 000H, 01fH, 080H, 066H, 060H,  00H, 01H, 00H, 00H
hell_PLUS:
   dt 00H, 00H,  006H, 000H, 006H, 000H, 03fH, 0c0H, 006H, 000H, 006H, 000H,  00H, 01H, 00H, 00H
hell_COMMA:
   dt 00H, 00H,  080H, 000H, 098H, 000H, 0f8H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_DASH:
   dt 00H, 00H,  006H, 000H, 006H, 000H, 006H, 000H, 006H, 000H, 006H, 000H,  00H, 01H, 00H, 00H
hell_DOT:
   dt 00H, 00H,  070H, 000H, 070H, 000H, 000H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_SLASH:
   dt 00H, 00H,  070H, 000H, 01cH, 000H, 007H, 000H, 001H, 0c0H, 000H, 070H,  00H, 01H, 00H, 00H
hell_0:
   dt 00H, 00H,  01fH, 0e0H, 03cH, 030H, 033H, 030H, 030H, 0f0H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_1:
   dt 00H, 00H,  000H, 0c0H, 000H, 060H, 03fH, 0f0H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_2:
   dt 00H, 00H,  038H, 060H, 03cH, 030H, 033H, 030H, 031H, 0e0H, 030H, 000H,  00H, 01H, 00H, 00H
hell_3:
   dt 00H, 00H,  030H, 030H, 030H, 030H, 033H, 030H, 033H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_4:
   dt 00H, 00H,  00fH, 0f0H, 00cH, 000H, 00cH, 000H, 07fH, 0e0H, 00cH, 000H,  00H, 01H, 00H, 00H
hell_5:
   dt 00H, 00H,  060H, 000H, 063H, 0f0H, 063H, 030H, 033H, 030H, 01eH, 030H,  00H, 01H, 00H, 00H
hell_6:
   dt 00H, 00H,  07fH, 0c0H, 0ccH, 060H, 0c6H, 030H, 0c6H, 000H, 07cH, 000H,  00H, 01H, 00H, 00H
hell_7:
   dt 00H, 00H,  070H, 030H, 01cH, 030H, 007H, 030H, 001H, 0f0H, 000H, 070H,  00H, 01H, 00H, 00H
hell_8:
   dt 00H, 00H,  079H, 0e0H, 0cfH, 030H, 0c6H, 030H, 0cfH, 030H, 079H, 0e0H,  00H, 01H, 00H, 00H
hell_9:
   dt 00H, 00H,  001H, 0e0H, 063H, 030H, 063H, 030H, 033H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_COLON:
   dt 00H, 00H,  077H, 000H, 077H, 000H, 000H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_SEMICOLON:
   dt 00H, 00H,  000H, 020H, 033H, 0a0H, 0f3H, 080H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_LT:
   dt 00H, 00H,  006H, 000H, 00fH, 000H, 019H, 080H, 030H, 0c0H, 060H, 060H,  00H, 01H, 00H, 00H
hell_EQ:
   dt 00H, 00H,  019H, 080H, 019H, 080H, 019H, 080H, 019H, 080H, 019H, 080H,  00H, 01H, 00H, 00H
hell_GT:
   dt 00H, 00H,  060H, 060H, 030H, 0c0H, 019H, 080H, 00fH, 000H, 006H, 000H,  00H, 01H, 00H, 00H
hell_QMARK:
   dt 00H, 00H,  000H, 060H, 000H, 030H, 0ceH, 030H, 003H, 030H, 001H, 0e0H,  00H, 01H, 00H, 00H
hell_AT:
   dt 00H, 00H,  01fH, 0e0H, 030H, 030H, 067H, 030H, 067H, 030H, 063H, 0e0H,  00H, 01H, 00H, 00H
hell_A:
   dt 00H, 00H,  03fH, 0e0H, 006H, 070H, 006H, 030H, 006H, 070H, 03fH, 0e0H,  00H, 01H, 00H, 00H
hell_B:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 033H, 030H, 033H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_C:
   dt 00H, 00H,  01fH, 0e0H, 038H, 070H, 030H, 030H, 030H, 030H, 018H, 060H,  00H, 01H, 00H, 00H
hell_D:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 030H, 030H, 030H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_E:
   dt 00H, 00H,  03fH, 0f0H, 033H, 030H, 033H, 030H, 030H, 030H, 030H, 030H,  00H, 01H, 00H, 00H
hell_F:
   dt 00H, 00H,  03fH, 0f0H, 003H, 030H, 003H, 030H, 000H, 030H, 000H, 030H,  00H, 01H, 00H, 00H
hell_G:
   dt 00H, 00H,  01fH, 0e0H, 038H, 070H, 030H, 030H, 033H, 030H, 01fH, 030H,  00H, 01H, 00H, 00H
hell_H:
   dt 00H, 00H,  03fH, 0f0H, 003H, 000H, 003H, 000H, 003H, 000H, 03fH, 0f0H,  00H, 01H, 00H, 00H
hell_I:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 030H, 030H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_J:
   dt 00H, 00H,  03cH, 000H, 030H, 000H, 030H, 000H, 030H, 000H, 03fH, 0f0H,  00H, 01H, 00H, 00H
hell_K:
   dt 00H, 00H,  03fH, 0f0H, 003H, 000H, 007H, 080H, 00cH, 0c0H, 038H, 070H,  00H, 01H, 00H, 00H
hell_L:
   dt 00H, 00H,  03fH, 0f0H, 030H, 000H, 030H, 000H, 030H, 000H, 030H, 000H,  00H, 01H, 00H, 00H
hell_M:
; AJFDIAG   dt 00H, 00H,  03fH, 0f0H, 000H, 060H, 000H, 0c0H, 001H, 080H, 000H, 0c0H,  00H, 01H, 00H, 00H
   dt 00H, 00H,  03fH, 0f0H, 000H, 060H, 000H, 0c0H, 001H, 080H, 000H, 0c0H, 000H, 060H, 03FH, 0F1H
hell_N:
   dt 00H, 00H,  03fH, 0f0H, 000H, 0e0H, 003H, 080H, 00eH, 000H, 03fH, 0f0H,  00H, 01H, 00H, 00H
hell_O:
   dt 00H, 00H,  01fH, 0e0H, 030H, 030H, 030H, 030H, 030H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_P:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 033H, 030H, 003H, 030H, 001H, 0e0H,  00H, 01H, 00H, 00H
hell_Q:
   dt 00H, 00H,  01fH, 0e0H, 030H, 070H, 03eH, 030H, 078H, 070H, 0cfH, 0e0H,  00H, 01H, 00H, 00H
hell_R:
   dt 00H, 00H,  03fH, 0f0H, 003H, 030H, 003H, 030H, 00fH, 030H, 039H, 0e0H,  00H, 01H, 00H, 00H
hell_S:
   dt 00H, 00H,  031H, 0e0H, 033H, 030H, 033H, 030H, 033H, 030H, 01eH, 030H,  00H, 01H, 00H, 00H
hell_T:
   dt 00H, 00H,  000H, 030H, 000H, 030H, 03fH, 0f0H, 000H, 030H, 000H, 030H,  00H, 01H, 00H, 00H
hell_U:
   dt 00H, 00H,  01fH, 0f0H, 030H, 000H, 030H, 000H, 030H, 000H, 01fH, 0f0H,  00H, 01H, 00H, 00H
hell_V:
   dt 00H, 00H,  03fH, 0f0H, 01cH, 000H, 007H, 000H, 001H, 0c0H, 000H, 070H,  00H, 01H, 00H, 00H
hell_W:
   dt 00H, 00H,  01fH, 0f0H, 030H, 000H, 018H, 000H, 00fH, 0f0H, 018H, 000H,  00H, 01H, 00H, 00H
hell_X:
   dt 00H, 00H,  038H, 070H, 00cH, 0c0H, 007H, 080H, 00cH, 0c0H, 038H, 070H,  00H, 01H, 00H, 00H
hell_Y:
   dt 00H, 00H,  000H, 070H, 001H, 0c0H, 03fH, 000H, 001H, 0c0H, 000H, 070H,  00H, 01H, 00H, 00H
hell_Z:
   dt 00H, 00H,  038H, 030H, 03eH, 030H, 033H, 030H, 031H, 0f0H, 03cH, 070H,  00H, 01H, 00H, 00H
hell_OPEN_SQ_BRACKET:
   dt 00H, 00H,  03fH, 0f0H, 030H, 030H, 030H, 030H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_BSLASH:
   dt 00H, 00H,  000H, 070H, 001H, 0c0H, 007H, 000H, 01cH, 000H, 070H, 000H,  00H, 01H, 00H, 00H
hell_CLOSE_SQ_BRACKET:
   dt 00H, 00H,  030H, 030H, 030H, 030H, 03fH, 0f0H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_CARAT:
   dt 00H, 00H,  007H, 000H, 001H, 0c0H, 000H, 070H, 001H, 0c0H, 007H, 000H,  00H, 01H, 00H, 00H
hell_USCORE:
   dt 00H, 00H,  000H, 030H, 000H, 030H, 000H, 030H, 000H, 030H, 000H, 030H,  00H, 01H, 00H, 00H
hell_BACKTICK:
   dt 00H, 00H,  000H, 0f0H, 003H, 0f0H, 003H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_a:
   dt 00H, 00H,  03fH, 0c0H, 006H, 060H, 006H, 030H, 006H, 060H, 03fH, 0c0H,  00H, 01H, 00H, 00H
hell_b:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 033H, 030H, 033H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_c:
   dt 00H, 00H,  01fH, 0e0H, 038H, 070H, 030H, 030H, 030H, 030H, 018H, 060H,  00H, 01H, 00H, 00H
hell_d:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 030H, 030H, 030H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_e:
   dt 00H, 00H,  03fH, 0f0H, 033H, 030H, 033H, 030H, 033H, 030H, 033H, 030H,  00H, 01H, 00H, 00H
hell_f:
   dt 00H, 00H,  03fH, 0f0H, 003H, 030H, 003H, 030H, 000H, 030H, 000H, 030H,  00H, 01H, 00H, 00H
hell_g:
   dt 00H, 00H,  01fH, 0e0H, 038H, 070H, 030H, 030H, 033H, 030H, 01fH, 030H,  00H, 01H, 00H, 00H
hell_h:
   dt 00H, 00H,  03fH, 0f0H, 003H, 000H, 003H, 000H, 003H, 000H, 03fH, 0f0H,  00H, 01H, 00H, 00H
hell_i:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 030H, 030H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_j:
   dt 00H, 00H,  01cH, 000H, 030H, 000H, 030H, 000H, 030H, 000H, 01fH, 0f0H,  00H, 01H, 00H, 00H
hell_k:
   dt 00H, 00H,  03fH, 0f0H, 003H, 000H, 007H, 080H, 01cH, 0e0H, 038H, 070H,  00H, 01H, 00H, 00H
hell_l:
   dt 00H, 00H,  03fH, 0f0H, 030H, 000H, 030H, 000H, 030H, 000H, 030H, 000H,  00H, 01H, 00H, 00H
hell_m:
   dt 00H, 00H,  03fH, 0f0H, 000H, 060H, 000H, 0c0H, 001H, 080H, 000H, 0c0H, 000H, 060H, 03FH, 0F1H
hell_n:
   dt 00H, 00H,  03fH, 0f0H, 000H, 0e0H, 003H, 080H, 00eH, 000H, 03fH, 0f0H,  00H, 01H, 00H, 00H
hell_o:
   dt 00H, 00H,  01fH, 0e0H, 030H, 030H, 030H, 030H, 030H, 030H, 01fH, 0e0H,  00H, 01H, 00H, 00H
hell_p:
   dt 00H, 00H,  030H, 030H, 03fH, 0f0H, 033H, 030H, 003H, 030H, 001H, 0e0H,  00H, 01H, 00H, 00H
hell_q:
   dt 00H, 00H,  01fH, 0e0H, 030H, 070H, 03eH, 030H, 078H, 070H, 0cfH, 0e0H,  00H, 01H, 00H, 00H
hell_r:
   dt 00H, 00H,  03fH, 0f0H, 003H, 030H, 003H, 030H, 00fH, 030H, 039H, 0e0H,  00H, 01H, 00H, 00H
hell_s:
   dt 00H, 00H,  031H, 0e0H, 033H, 030H, 033H, 030H, 033H, 030H, 01eH, 030H,  00H, 01H, 00H, 00H
hell_t:
   dt 00H, 00H,  000H, 030H, 000H, 030H, 03fH, 0f0H, 000H, 030H, 000H, 030H,  00H, 01H, 00H, 00H
hell_u:
   dt 00H, 00H,  01fH, 0f0H, 030H, 000H, 030H, 000H, 030H, 000H, 01fH, 0f0H,  00H, 01H, 00H, 00H
hell_v:
   dt 00H, 00H,  03fH, 0f0H, 01cH, 000H, 007H, 000H, 001H, 0c0H, 000H, 070H,  00H, 01H, 00H, 00H
hell_w:
   dt 00H, 00H,  01fH, 0f0H, 030H, 000H, 018H, 000H, 00fH, 0f0H, 018H, 000H,  00H, 01H, 00H, 00H
hell_x:
   dt 00H, 00H,  038H, 070H, 00cH, 0c0H, 007H, 080H, 00cH, 0c0H, 038H, 070H,  00H, 01H, 00H, 00H
hell_y:
   dt 00H, 00H,  000H, 070H, 001H, 0c0H, 03fH, 000H, 001H, 0c0H, 000H, 070H,  00H, 01H, 00H, 00H
hell_z:
   dt 00H, 00H,  038H, 030H, 03eH, 030H, 033H, 030H, 031H, 0f0H, 03cH, 070H,  00H, 01H, 00H, 00H
hell_OPEN_CURLY:
   dt 00H, 00H,  006H, 000H, 07fH, 0e0H, 0c0H, 030H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_VERTBAR:
   dt 00H, 00H,  000H, 070H, 001H, 0c0H, 007H, 000H, 01cH, 000H, 070H, 000H,  00H, 01H, 00H, 00H
hell_CLOSE_CURLY:
   dt 00H, 00H,  0c0H, 030H, 07fH, 0e0H, 006H, 000H, 000H, 000H, 000H, 000H,  00H, 01H, 00H, 00H
hell_TILDE:
   dt 00H, 00H,  000H, 0c0H, 000H, 060H, 000H, 030H, 000H, 060H, 000H, 0c0H,  00H, 01H, 00H, 00H    
    END
