;**********************************************************************
;* Filename:	main.asm
;* Project:		EPROM emulator
;* Date:		05 June 2005
;* Version:		0.1
;* Author:		Philip Pemberton
;* 
;* Main program loop for EPROM emulator.
;**********************************************************************

	include	"config.inc"		; Device configuration

;**********************************************************************
;** HEADERS
;**********************************************************************

	include	"pins.inc"			; Pin definitions
	include	"mac_table.inc"		; Table handling defines / macros

;**********************************************************************
;** CONSTANTS
;**********************************************************************

lbank		equ		0	;register bank for the local state of this module
lbankadr	equ		bankadr(lbank)	;address within local state register bank

;**********************************************************************
;** RAM VARIABLES
;**********************************************************************

; Global state.
	defram	gbankadr

; Local state
	defram	lbankadr
flags		res		1				; Flags

sTemp1		res		1				; Subroutine temporary variable #1
sTemp2		res		1				; Subroutine temporary variable #2

temp		res		1				; Temporary storage
loop		res		1				; Loop variable

chkAcc		res		1				; Checksum accumulator

#define	flag_Running	flags, 0

	extern	addr_High, addr_Low

;**********************************************************************
;** MACROS
;**********************************************************************

;**********************************************************************
;** PROGRAM ENTRY POINT (AFTER SETUP)
;**********************************************************************
.main		CODE				; Code segment for

;----------------------------------
; Subroutine:	main_Run
; Inputs:
;	None
; Outputs:
;	None
; Function:
;	Main program loop
;
	glbsub	main_Run
	dbankif	lbankadr				; Select RAM bank

cmd_loop:
	gcall	uart_Get				; Get the command byte
	dbankif	lbankadr
	movwf	temp					; Save the command byte
	movwf	chkAcc					; Initialise checksum

	xorlw	0x00					; =0x00 (NOP)?
	skpnz
	goto	cmd_loop

	movf	temp,		W			; Get command byte
	xorlw	'D'						; 'D' ==> Debug
	skpnz
	goto	cmd_Debug

	movf	temp,		W			; Get command byte
	xorlw	'G'						; 'G' ==> Get hardware ID string
	skpnz
	goto	cmd_GetID

	movf	temp,		W			; Get command byte
	xorlw	'M'						; 'M' ==> Set Mode
	skpnz
	goto	cmd_SetMode

	movf	temp,		W			; Get command byte
	xorlw	'V'						; 'V' ==> Version Request
	skpnz
	goto	cmd_Version

	movf	temp,		W			; Get command byte
	xorlw	'W'						; 'W' ==> Write Block
	skpnz
	goto	cmd_WriteBlock

	goto	error_BadCmd			; Bad/unrecognised command

	leave


;**********************************************************************
;** COMMAND HANDLERS
;**********************************************************************

;----------------------------------------------------------------------
; 'D': Debug command
cmd_Debug:
	gcall	addr_Reset
	movlw	0x08
	dbankif	lbankadr
	movwf	loop
dbug_loop:
	gcall	ram_Read
	dbankif	lbankadr
	movwf	temp
	swapf	temp,		W
	call	toHex
	gcall	uart_Put
	dbankif	lbankadr
	movf	temp,		W
	call	toHex
	gcall	uart_Put

	movlw	" "
	gcall	uart_Put

	gcall	addr_Inc

	dbankif	lbankadr
	decfsz	loop,		F
	goto	dbug_loop
	
	goto	cmd_loop

;----------------------------------------------------------------------
; 'G': Get hardware ID string
cmd_GetID:
	gcall	uart_Get				; Get checksum byte
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator
	skpz							; Chksum ACC = 0?
	goto	error_Checksum			; If not, then checksum error

	movlw	0x00					; E00: No errors.
	gcall	uart_Put

	clrf	loop					; Clear loopvar
id_print_loop:
	LoadTblOfs	table_IDString		; Set up table to read ID str.
	movf	loop,		W			; Loopvar ==> W
	gcall	table_Get				; Get the table byte
	iorlw	0x00					; Update flags
	skpnz							; If byte = 0 then end of string
	goto	id_print_end			; byte = 0 so end
	gcall	uart_Put				; byte != 0 so send
	incf	loop,		F			; Loop++
	goto	id_print_loop			; Keep going

id_print_end:
	movlw	0x00					; Terminating null
	gcall	uart_Put
	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; 'M': Set Mode
cmd_SetMode:
	gcall	uart_Get				; Get mode byte
	dbankif	lbankadr
	movwf	sTemp1
	addwf	chkAcc,		F			; Update checksum
	gcall	uart_Get				; Get device ID byte
	dbankif	lbankadr
	movwf	sTemp2
	addwf	chkAcc,		F			; Update checksum

	gcall	uart_Get				; Get checksum byte
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator
	skpz							; Chksum ACC = 0?
	goto	error_Checksum			; If not, then checksum error

	movf	sTemp2,		W			; What type of ROM?
	xorlw	0x00					; 00: 2764?
	skpnz
	goto	setmode_rt_2764			; 2764 selected

	movf	sTemp2,		W			; What type of ROM?
	xorlw	0x01					; 01: 27128?
	skpnz
	goto	setmode_rt_27128		; 27128 selected

	movf	sTemp2,		W			; What type of ROM?
	xorlw	0x02					; 02: 27256?
	skpnz
	goto	setmode_rt_27256		; 27256 selected

	movf	sTemp2,		W			; What type of ROM?
	xorlw	0x03					; 03: 27256?
	skpnz
	goto	setmode_rt_27512		; 27512 selected

	goto	error_InvalidDevice		; Invalid device code

setmode_rt_2764:					; 2764 or ...
setmode_rt_27128:					; ... 27128
	dbankif	SELA_PORT
	bsf		SELA
	dbankif	SELB_PORT
	bsf		SELB
	goto	setmode_rt_end

setmode_rt_27256:
	dbankif	SELA_PORT
	bsf		SELA
	dbankif	SELB_PORT
	bcf		SELB
	goto	setmode_rt_end

setmode_rt_27512:
	dbankif	SELA_PORT
	bcf		SELA
	dbankif	SELB_PORT
	bcf		SELB
	goto	setmode_rt_end

setmode_rt_end:
	dbankif	lbankadr
	movf	sTemp1,		W			; Run mode?
	skpz
	goto	setmode_run				; Yes.

	bcf		flag_Running			; No. Clear "Running" flag

	dbankif	ADRL_TRIS
	clrf	ADRL_TRIS				; ADRL => output mode
	dbankif	DATA_TRIS
	clrf	DATA_TRIS				; DATA => output mode

	dbankif	nAHOE_PORT
	bcf		nAHOE					; Enable ADRH buffer

	dbankif	nREAD_PORT
	bsf		nREAD					; Disable RAM reads

	dbankif	RESET_PORT
	bsf		RESET					; Reset the target
	dbankif	nRUN_PORT
	bsf		nRUN					; Load mode on

	goto	setmode_r_end

setmode_run:
	dbankif	lbankadr
	bsf		flag_Running			; Set "Running" flag

	dbankif	ADRL_TRIS
	movlw	0xFF
	movwf	ADRL_TRIS				; Tristate ADRL
	dbankif	DATA_TRIS
	movwf	DATA_TRIS				; Tristate DATA

	dbankif	nAHOE_PORT				; Disable ADRH buffer
	bsf		nAHOE

	dbankif	nREAD_PORT
	bcf		nREAD					; Enable RAM reads

	dbankif	nRUN_PORT
	bcf		nRUN					; Run mode on
	dbankif	RESET_PORT
	bcf		RESET					; Run the target

setmode_r_end:
	movlw	0x00
	gcall	uart_Put

	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; 'V': Version Request
cmd_Version:						
	gcall	uart_Get				; Get checksum byte
	addwf	chkAcc,		F			; Add to checksum accumulator
	skpz							; Chksum ACC = 0?
	goto	error_Checksum			; If not, then checksum error

	movlw	0x00					; E00: No error
	gcall	uart_Put
	movlw	MAJOR_VER				; Major version
	gcall	uart_Put
	movlw	MINOR_VER				; Minor version
	gcall	uart_Put
	movlw	HARDWARE_ID				; Hardware ID
	gcall	uart_Put

	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; 'W': Write Block
cmd_WriteBlock:
	btfsc	flag_Running			; Target in run mode?
	goto	error_Running			; Yes, we can't load in Run mode

	gcall	uart_Get				; Get high address
	dbankif	gbankadr
	movwf	addr_High
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator

	gcall	uart_Get				; Get low address
	dbankif	gbankadr
	movwf	addr_Low
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator

	gcall	addr_Update				; Load the address into the buffers

	gcall	uart_Get				; Get byte count
	dbankif	lbankadr
	movwf	loop
	addwf	chkAcc,		F			; Add to checksum accumulator

	gcall	uart_Get				; Get first data byte
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator

	gcall	ram_Write				; Write to RAM
	gcall	addr_Inc				; Increment address

write_loop:
	gcall	uart_Get				; Get data byte
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator

	gcall	ram_Write				; Write to RAM
	gcall	addr_Inc				; Increment address
	
	decfsz	loop,		F			; Decrement loop counter
	goto	write_loop				; If >0 then keep going

	gcall	uart_Get				; Get checksum byte
	dbankif	lbankadr
	addwf	chkAcc,		F			; Add to checksum accumulator
	skpz							; Chksum ACC = 0?
	goto	error_Checksum			; If not, then checksum error

	movlw	0x00					; E00: No error
	gcall	uart_Put
	goto	cmd_loop

;**********************************************************************
;** ERROR HANDLERS
;**********************************************************************

;----------------------------------------------------------------------
; E01: Checksum error
error_Checksum:
	movlw	0x01
	gcall	uart_Put
	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; E02: Bad command
error_BadCmd:
	movlw	0x02
	gcall	uart_Put
	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; E03: Invalid mode
error_InvalidMode:
	movlw	0x03
	gcall	uart_Put
	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; E04: Invalid device code
error_InvalidDevice:
	movlw	0x04
	gcall	uart_Put
	goto	cmd_loop				; Back to the command loop

;----------------------------------------------------------------------
; E05: Can't access RAM in RUN mode
error_Running:
	movlw	0x05
	gcall	uart_Put
	goto	cmd_loop				; Back to the command loop

;**********************************************************************
;** SUBROUTINES
;**********************************************************************

;----------------------------------
; Subroutine:	toHex
; Inputs:
;	W	=	Data nibble
; Outputs:
;	W	=	Hex ASCII version of data nibble
; Function:
;	Converts a data nibble into its hexadecimal representation.
;
	locsub	toHex
	andlw	0x0F					; Mask off low nibble
	movwf	sTemp1					; Save data nibble
	LoadTblOfs	table_Hex			; Load the table offset
	movf	sTemp1,		W			; Data nibble => W
	gcall	table_Get				; Get the byte from the table
	leave							; Exit with W = ascii of nibble

;**********************************************************************
;** DATA TABLES
;**********************************************************************

table_IDString:						; ID string
	dt		"512kbit EPROM emulator",0
table_Hex:							; Hex->Asc conversion table
	dt		"0123456789ABCDEF"

	END