*

FDD_DEMO.ASM


It's possible that errors have been introduced to this code during the conversion to HTML. To be sure of the correct code you should download the PIC Assembler ZIP file from the homepage.

;**********************************************************************
;* Filename : fdd_demo.asm
;*
;* Used to test and demonstrate the IDC (I2C Disk Drive Controller).  
;*
;* Receives commands from the UART and converts them to I2C commands.
;*
;* The results of the commands are converted into hexadecimal strings
;* and sent back out of the serial port.
;*
;* The serial port settings are 9600 8-N-1.
;*
;* NOTE : Two big string tables are used, so any code changes have to
;*        take the position of the tables into account.
;*
;* Author       : Craig Dunn (craig_d73@hotmail.com)
;* Date Started : 15 April 2004
;* Tab Setting  : 9
;* Studio       : MPLAB 6.1
;* Clock        : 8MHz (clock dependent - I2C speed, RS232 Baudrate)
;*
;* All of the design and code in this module is my own work.  No 
;* design or code has been borrowed or copied from any source.
;**********************************************************************
#include "p16f874.inc"
	
	ERRORLEVEL 0, -302		; Turn off memory bank warnings
	LIST   P=PIC16F874
        

;**********************************************************************
;* Defines
;**********************************************************************
#define	SET_BANK0	BCF	STATUS, RP0
#define	SET_BANK1	BSF	STATUS, RP0
#define	EEPROM_W	0xA0
#define EEPROM_R	0xA1
#define	RX_BIT		0x01

#define CMD_FAT_ENTRY	0x46
#define CMD_FAT_VALUE	0x47
#define CMD_SECTOR	0x4F
#define CMD_DATA	0xF0
#define CMD_STATUS	0x53
#define CMD_DIR_ENTRY	0x88
#define CMD_DIR_VALUE	0x99

#define IDC_R		0x03
#define IDC_W		0x02

#define	CR		0x0D
#define	FF		0x0C

#define	GP_REF_OFF	0x20


;**********************************************************************
;* Variables
;**********************************************************************
track			EQU	(GP_REF_OFF + 0x00)
side_sector		EQU	(GP_REF_OFF + 0x01)
uart			EQU	(GP_REF_OFF + 0x02)
rx_reg			EQU	(GP_REF_OFF + 0x03)
tx_reg			EQU	(GP_REF_OFF + 0x04)
nibble			EQU	(GP_REF_OFF + 0x05)
hex_temp		EQU	(GP_REF_OFF + 0x06)
i2c_rx_reg		EQU	(GP_REF_OFF + 0x07)
byte_offset		EQU	(GP_REF_OFF + 0x08)
str_id			EQU	(GP_REF_OFF + 0x09)
x_cnt			EQU	(GP_REF_OFF + 0x0A)
hex_reg			EQU	(GP_REF_OFF + 0x0B)
y_cnt			EQU	(GP_REF_OFF + 0x0C)
hex_value		EQU	(GP_REF_OFF + 0x0D)
hex_cnt			EQU	(GP_REF_OFF + 0x0E)
fat_entry_hi		EQU	(GP_REF_OFF + 0x0F)
fat_entry_lo		EQU	(GP_REF_OFF + 0x10)
fat_value_hi		EQU	(GP_REF_OFF + 0x11)
fat_value_lo		EQU	(GP_REF_OFF + 0x12)
dir_entry		EQU	(GP_REF_OFF + 0x13)
tempw			EQU	(GP_REF_OFF + 0x14)
tempst			EQU	(GP_REF_OFF + 0x15)


;**********************************************************************
;* MACRO's
;*
;* MOVLF  - Move a literal to a file
;* MOVFF  - Move a file to a file
;* SUBLF  - Subtract a literal from a file
;* ADDLF  - Add a literal to a file
;* DCFSNZ - Decrement File Skip if Not Zero
;* ADDFF  - Add file to file
;**********************************************************************
MOVLF	MACRO	literal, file
	MOVLW	literal
	MOVWF	file
	ENDM
	
MOVFF	MACRO	file_a, file_b
	MOVFW	file_a
	MOVWF	file_b
	ENDM
	
SUBLF	MACRO	literal, file
	MOVLW	literal
	SUBWF	file, F
	ENDM
	
ADDLF	MACRO	literal, file
	MOVFW	file
	ADDLW	literal
	MOVWF	file	
	ENDM
	
DCFSNZ	MACRO	file
	DECFSZ	file, F
	GOTO $+3	; Not zero
	GOTO $+1	; Zero 
	ENDM
	
ADDFF	MACRO	file_a, file_b
	MOVFW	file_a
	ADDWF	file_b, F
	ENDM


;**********************************************************************
;* Vector table
;**********************************************************************
	ORG	0x00
	GOTO	init
	ORG	0x04
	GOTO	int_srv
	
	
;**********************************************************************
;* Main implementation.
;**********************************************************************
init:	; Setup the ports
	;------------------------------------------------------------------
	SET_BANK1
	CLRF	TRISB			; Set PORTB as an output (LED's)
	MOVLF	0xFF, TRISC		; Set PORTC as an input (I2C, UART)
	SET_BANK0


	; Setup the I2C
	;------------------------------------------------------------------
	SET_BANK0
	MOVLF	0x38, SSPCON		; Enable I2C Master mode
	SET_BANK1
	MOVLF	0x00, SSPSTAT
	MOVLF	0x04, SSPADD		; 400KHz I2C
	SET_BANK0
	
	
	; Setup the UART
	;------------------------------------------------------------------
	SET_BANK1
	MOVLF	0x33, SPBRG		; 9600 baud rate
	BSF	TXSTA, BRGH		; High speed mode
	BSF	TXSTA, TXEN		; Enable transmit
	SET_BANK0
	BSF	RCSTA, SPEN		; Enable the serial port
	BSF	RCSTA, CREN		; Continuous receive
	SET_BANK1
	BSF	PIE1, RCIE		; Enable receive interrupt
	SET_BANK0
	BSF	INTCON, PEIE		; Enable unmasked peripheral interrupts
	BSF	INTCON, GIE		; Enable interrupts
	
	
	; Main implementation
	;------------------------------------------------------------------
main:	MOVLF	0x00, track
	MOVLF	0x81, side_sector
	MOVLF	0x00, dir_entry
	MOVLF	0x00, fat_entry_hi
	MOVLF	0x00, fat_entry_lo
	MOVLF	0x00, fat_value_hi
	MOVLF	0x00, fat_value_lo
	
refresh:
	; Display the menu
	;------------------------------------------------------------------
	MOVLF	FF, tx_reg
	CALL	uart_tx			; Clear the display (Form Feed)
		
	MOVLF	(menu_str - str_table_start), str_id
	CALL	uart_tx_str
	
	MOVLF	(current_track_str - str_table_start2), str_id
	CALL	uart_tx_str2
	MOVFF	track, tx_reg
	CALL	uart_tx_hex
	
	MOVLF	(current_sector_str - str_table_start2), str_id
	CALL	uart_tx_str2
	MOVFF	side_sector, tx_reg
	CALL	uart_tx_hex

	
	; Wait here until the user makes a menu choice
	;------------------------------------------------------------------
wait_cmd:
	BCF	uart, RX_BIT		; Allows us to receive a byte
wait_new_cmd:
	BTFSC	uart, RX_BIT
	GOTO	process_cmd
	GOTO	wait_new_cmd
	
	
	; Menu choice case statement
	;------------------------------------------------------------------
process_cmd:	
	MOVFW	rx_reg
	XORLW	'0'
	BTFSC	STATUS, Z
	GOTO	refresh			; Refresh the menu
	
	MOVFW	rx_reg
	XORLW	'1'
	BTFSC	STATUS, Z
	GOTO	cmd_read_sector		; Read a sector
	
	MOVFW	rx_reg
	XORLW	'2'
	BTFSC	STATUS, Z
	GOTO	cmd_write_sector	; Write a sector
	
	MOVFW	rx_reg
	XORLW	'3'
	BTFSC	STATUS, Z
	GOTO	cmd_read_data		; Read data from the controller
	
	MOVFW	rx_reg
	XORLW	'4'
	BTFSC	STATUS, Z
	GOTO	cmd_write_data		; Write data to the controller
	
	MOVFW	rx_reg
	XORLW	'5'
	BTFSC	STATUS, Z
	GOTO	cmd_read_fat_entry	; Read a FAT entry
	
	MOVFW	rx_reg
	XORLW	'6'
	BTFSC	STATUS, Z
	GOTO	cmd_read_fat_value	; Read a FAT value
	
	MOVFW	rx_reg
	XORLW	'7'
	BTFSC	STATUS, Z
	GOTO	cmd_write_fat_entry	; Write a FAT entry
	
	MOVFW	rx_reg
	XORLW	'8'
	BTFSC	STATUS, Z
	GOTO	cmd_write_fat_value	; Write a FAT value
	
	MOVFW	rx_reg
	XORLW	'A'
	BTFSC	STATUS, Z
	GOTO	cmd_read_dir_entry	; Read a directory entry
	
	MOVFW	rx_reg
	XORLW	'B'
	BTFSC	STATUS, Z
	GOTO	cmd_write_dir_entry	; Write a directory entry
	
	MOVFW	rx_reg
	XORLW	'C'
	BTFSC	STATUS, Z
	GOTO	cmd_read_dir_value	; Read a directory value
	
	MOVFW	rx_reg
	XORLW	'D'
	BTFSC	STATUS, Z
	GOTO	cmd_write_dir_value	; Write a directory value
		
	MOVFW	rx_reg
	XORLW	'9'
	BTFSC	STATUS, Z
	GOTO	cmd_status		; Read the status
	
	MOVFW	rx_reg
	XORLW	'T'
	BTFSC	STATUS, Z
	GOTO	cmd_track		; Set the track
	
	MOVFW	rx_reg
	XORLW	'S'
	BTFSC	STATUS, Z
	GOTO	cmd_sector		; Set the side / sector
		
	GOTO	wait_cmd


	; Read a directory entry
	;------------------------------------------------------------------
cmd_read_dir_entry:
	MOVLF	(dir_entry_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	uart_rx_hex
	MOVFF	hex_value, dir_entry
	CALL	idc_read_dir_entry
	GOTO	wait_cmd


	; Write a directory entry
	;------------------------------------------------------------------
cmd_write_dir_entry:
	CALL	idc_write_dir_entry
	GOTO	wait_cmd

	
	; Read a directory value
	;------------------------------------------------------------------
cmd_read_dir_value:
	CALL	idc_read_dir_value
	GOTO	wait_cmd
	
	
	; Write a directory value
	;------------------------------------------------------------------
cmd_write_dir_value:
	MOVLF	(value_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	idc_write_dir_value
	GOTO	wait_cmd


	; Read a sector
	;------------------------------------------------------------------
cmd_read_sector:
	CALL	idc_read_sector
	GOTO	wait_cmd
	
	
	; Write a sector
	;------------------------------------------------------------------
cmd_write_sector:
	CALL	idc_write_sector
	GOTO	wait_cmd
	
	
	; Read data
	;------------------------------------------------------------------
cmd_read_data:
	CALL	idc_read_data
	GOTO	wait_cmd
	
	
	; Write data
	;------------------------------------------------------------------
cmd_write_data:
	MOVLF	(value_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	idc_write_data
	GOTO	wait_cmd
	
	
	; Read FAT entry
	;------------------------------------------------------------------
cmd_read_fat_entry:
	MOVLF	(fat_entry_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	uart_rx_hex
	MOVFF	hex_value, fat_entry_hi
	CALL	uart_rx_hex
	MOVFF	hex_value, fat_entry_lo
	CALL	idc_read_fat_entry
	GOTO	wait_cmd


	; Read FAT value
	;------------------------------------------------------------------
cmd_read_fat_value:
	MOVLF	(fat_value_str - str_table_start2), str_id
	CALL	uart_tx_str2
	CALL	idc_read_fat_value
	MOVFF	fat_value_hi, tx_reg
	CALL	uart_tx_hex
	MOVFF	fat_value_lo, tx_reg
	CALL	uart_tx_hex	
	GOTO	wait_cmd
	
	
	; Write FAT entry
	;------------------------------------------------------------------
cmd_write_fat_entry:
	MOVLF	(fat_entry_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	uart_rx_hex
	MOVFF	hex_value, fat_entry_hi
	CALL	uart_rx_hex
	MOVFF	hex_value, fat_entry_lo
	CALL	idc_write_fat_entry
	GOTO	wait_cmd
	
	
	; Write FAT value
	;------------------------------------------------------------------
cmd_write_fat_value:
	MOVLF	(fat_value_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	uart_rx_hex
	MOVFF	hex_value, fat_value_hi
	CALL	uart_rx_hex
	MOVFF	hex_value, fat_value_lo
	CALL	idc_write_fat_value
	GOTO	wait_cmd


	; Status
	;------------------------------------------------------------------
cmd_status:
	MOVLF	(status_str - str_table_start2), str_id
	CALL	uart_tx_str2
	CALL	idc_read_status
	MOVWF	tx_reg
	CALL	uart_tx_hex
	GOTO	wait_cmd
	
		
	; Track
	;------------------------------------------------------------------
cmd_track:
	MOVLF	(track_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	uart_rx_hex
	MOVFF	hex_value, track	
	GOTO	refresh
	
	
	; Sector
	;------------------------------------------------------------------
cmd_sector:
	MOVLF	(sector_str - str_table_start2), str_id
	CALL	uart_tx_str2	
	CALL	uart_rx_hex
	MOVFF	hex_value, side_sector
	GOTO	refresh
	
	
;**********************************************************************
;* Generate an I2C START condition.
;**********************************************************************
i2c_start:
	BCF	PIR1, SSPIF		; Clear the completed flag
	SET_BANK1
	BSF	SSPCON2, SEN		; Generate a START
	SET_BANK0
	BTFSS	PIR1, SSPIF		; Wait until the START is complete
	GOTO	$-1
	RETURN
	
	
;**********************************************************************
;* Generate an I2C STOP condition.
;**********************************************************************
i2c_stop:
	BCF	PIR1, SSPIF		; Clear the completed flag
	SET_BANK1
	BSF	SSPCON2, PEN		; Generate a STOP
	SET_BANK0
	BTFSS	PIR1, SSPIF		; Wait until the STOP is complete
	GOTO	$-1
	RETURN
	

;**********************************************************************
;* Send the byte in W out on the I2C bus.  Doesn't return until the
;* the transmission is complete.
;**********************************************************************
i2c_send:
	BCF	PIR1, SSPIF		; Clear the completed flag
	MOVWF	SSPBUF			; Send the byte
	BTFSS	PIR1, SSPIF		; Wait here until the byte is sent
	GOTO	$-1
	RETURN
	
	
;**********************************************************************
;* Receive a byte from the I2C bus and put it in i2c_rx_reg.
;**********************************************************************
i2c_recv:
	BCF	PIR1, SSPIF		; Clear the completed flag
	SET_BANK1
	BSF	SSPCON2, RCEN		; Receive the byte
	SET_BANK0
	BTFSS	PIR1, SSPIF		; Wait here until the byte is received
	GOTO	$-1
	MOVFF	SSPBUF, i2c_rx_reg	; Put the received byte in i2c_rx_reg
	RETURN
	

;**********************************************************************
;* Send an ACK out on the I2C bus.
;**********************************************************************	
i2c_ack:
	BCF	PIR1, SSPIF		; Clear the completed flag
	SET_BANK1
	BCF	SSPCON2, ACKDT
	BSF	SSPCON2, ACKEN
	SET_BANK0
	BTFSS	PIR1, SSPIF		; Wait here until the ACK is received
	GOTO	$-1
	RETURN
	

;**********************************************************************
;* Send a NACK out on the I2C bus.
;**********************************************************************	
i2c_nack:
	BCF	PIR1, SSPIF		; Clear the completed flag
	SET_BANK1
	BSF	SSPCON2, ACKDT
	BSF	SSPCON2, ACKEN
	SET_BANK0
	BTFSS	PIR1, SSPIF		; Wait here until the ACK is received
	GOTO	$-1
	RETURN
		
		
;**********************************************************************
;* Read the status from the IFC.
;**********************************************************************
idc_read_status:
        CALL    i2c_stop
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_status_nack
	SET_BANK0
	
	MOVLW	CMD_STATUS
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_status_nack
	SET_BANK0
	
	CALL	i2c_recv		; Receive the status
	CALL	i2c_nack		; Send a NACK	
	
idc_read_status_nack:
	SET_BANK0
	CALL	i2c_stop		; STOP
	RETURN
	
	
;**********************************************************************
;* Write a new FAT entry to the IDC.
;**********************************************************************
idc_write_fat_entry:
	CALL	i2c_start		; START
	MOVLW	IDC_W
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_entry_nack
	SET_BANK0
	
	MOVLW	CMD_FAT_ENTRY
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_entry_nack
	SET_BANK0
	
	MOVFW	fat_entry_hi
	CALL	i2c_send		; High entry
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_entry_nack
	SET_BANK0
	
	MOVFW	fat_entry_lo
	CALL	i2c_send		; Low entry
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_entry_nack
	SET_BANK0
		
idc_wr_fat_entry_nack:
	SET_BANK0
	CALL	i2c_stop		; STOP
	RETURN


;**********************************************************************
;* Write a new value to the current directory entry.
;**********************************************************************	
idc_write_dir_value:
	CALL	i2c_start		; START
	MOVLW	IDC_W
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_dir_value_nack
	SET_BANK0
	
	MOVLW	CMD_DIR_VALUE
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_dir_value_nack
	SET_BANK0
	
	; Keep reading in bytes until the CR is detected
next_dir_byte:
	BCF	uart, RX_BIT
	BTFSS	uart, RX_BIT
	GOTO	$-1
	
	MOVFF	rx_reg, tx_reg
	CALL	uart_tx
	MOVFW	rx_reg
	XORLW	CR
	BTFSC	STATUS, Z
	GOTO	write_dir_finished
	MOVFW	rx_reg
	CALL	i2c_send
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_dir_value_nack
	SET_BANK0
	GOTO	next_dir_byte
	
write_dir_finished:
idc_wr_dir_value_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN

	
;**********************************************************************
;* Write a new FAT value to the IDC (hi_value, lo_value).
;**********************************************************************	
idc_write_fat_value:
	CALL	i2c_start		; START
	MOVLW	IDC_W
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_value_nack
	SET_BANK0
	
	MOVLW	CMD_FAT_VALUE
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_value_nack
	SET_BANK0
	
	MOVFW	fat_value_hi
	CALL	i2c_send		; High value
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_value_nack
	SET_BANK0
	
	MOVFW	fat_value_lo
	CALL	i2c_send		; Low value
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_fat_value_nack
	SET_BANK0
		
idc_wr_fat_value_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN
	
	
;**********************************************************************
;* Read the FAT value from the IDC.
;**********************************************************************
idc_read_fat_value:
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_fat_value_nack
	SET_BANK0
	
	MOVLW	CMD_FAT_VALUE
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_fat_value_nack
	SET_BANK0
	
	CALL	i2c_recv		; Receive the hi value
	CALL	i2c_ack			; Send an ACK	
	MOVFF	i2c_rx_reg, fat_value_hi
	
	CALL	i2c_recv		; Receive the lo value
	CALL	i2c_nack		; Send an NACK	
	MOVFF	i2c_rx_reg, fat_value_lo
	
idc_read_fat_value_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN


;**********************************************************************
;* Instruct the IDC to read a directory entry.
;**********************************************************************
idc_read_dir_entry:
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_rd_dir_entry_nack
	SET_BANK0
	
	MOVLW	CMD_DIR_ENTRY
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_rd_dir_entry_nack
	SET_BANK0
	
	MOVFW	dir_entry
	CALL	i2c_send		; Entry
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_rd_dir_entry_nack
	SET_BANK0
	
idc_rd_dir_entry_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN
	
	
;**********************************************************************
;* Instruct the IDC to write the current directory entry.
;**********************************************************************
idc_write_dir_entry:
	CALL	i2c_start		; START
	MOVLW	IDC_W
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_dir_entry_nack
	SET_BANK0
	
	MOVLW	CMD_DIR_ENTRY
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_wr_dir_entry_nack
	SET_BANK0
	
idc_wr_dir_entry_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN


;**********************************************************************
;* Instruct the IDC to read a sector (track, side_sector).
;**********************************************************************
idc_read_sector:
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_sector_nack
	SET_BANK0
	
	MOVLW	CMD_SECTOR
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_sector_nack
	SET_BANK0
	
	MOVFW	track
	CALL	i2c_send		; Track
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_sector_nack
	SET_BANK0
	
	MOVFW	side_sector
	CALL	i2c_send		; Side / Sector
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_sector_nack
	SET_BANK0

idc_read_sector_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN
	
	
;**********************************************************************
;* Instruct the IDC to write a sector (track, side_sector).
;**********************************************************************
idc_write_sector:
	CALL	i2c_start		; START
	MOVLW	IDC_W
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_sector_nack
	SET_BANK0
	
	MOVLW	CMD_SECTOR
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_sector_nack
	SET_BANK0
	
	MOVFW	track
	CALL	i2c_send		; Track
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_sector_nack
	SET_BANK0
	
	MOVFW	side_sector
	CALL	i2c_send		; Side / Sector
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_sector_nack
	SET_BANK0

idc_write_sector_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN
	
	
;**********************************************************************
;* Read the contents of the IDC's RAM, which contains a sector and
;* display the results as hex strings.
;**********************************************************************
idc_read_data:
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_data_nack
	SET_BANK0
	
	MOVLW	CMD_DATA
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_data_nack
	SET_BANK0
	
	MOVLF	CR, tx_reg		; Start the display on a new line
	CALL	uart_tx
	
	MOVLF	0x21, y_cnt		; 32 lines of 16 bytes

data_new_line:
	MOVLF	0x10, x_cnt		; 16 bytes per line
	DCFSNZ	y_cnt
	GOTO	data_finish

data_next_byte:
	CALL	i2c_recv		; Get the byte from the I2C bus
	CALL	i2c_ack

	MOVFF	i2c_rx_reg, tx_reg
	CALL	uart_tx_hex		; Display the byte as a hex string
	
	MOVLF	' ', tx_reg		; Put a space between the hex strings
	DCFSNZ	x_cnt
	MOVLF	CR, tx_reg		; End each line with a carriage return
	CALL	uart_tx
	
	MOVFW	x_cnt
	ADDLW	0x00
	BTFSC	STATUS, Z		; If the end of the line then start a new line
	GOTO	data_new_line
	GOTO	data_next_byte	
data_finish:

	; All receiving operations from the slave end in a
	; NACK.  Putting it here makes the above loop simpler.
	CALL	i2c_recv
	CALL	i2c_nack
	
idc_read_data_nack:
	SET_BANK0
	CALL	i2c_stop		; STOP
	RETURN

	
;**********************************************************************
;* Write data to the IDC.
;**********************************************************************	
idc_write_data:
	CALL	i2c_start		; START
	MOVLW	IDC_W
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_data_nack
	SET_BANK0
	
	MOVLW	CMD_DATA
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_data_nack
	SET_BANK0
	
	; Keep reading in bytes until the CR is detected
next_data_byte:
	BCF	uart, RX_BIT
	BTFSS	uart, RX_BIT
	GOTO	$-1
	
	MOVFF	rx_reg, tx_reg
	CALL	uart_tx
	MOVFW	rx_reg
	XORLW	CR
	BTFSC	STATUS, Z
	GOTO	write_data_finished	
	MOVFW	rx_reg
	CALL	i2c_send
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_write_data_nack
	SET_BANK0
	GOTO	next_data_byte
	
write_data_finished
idc_write_data_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN
	
	
;**********************************************************************
;* Instruct the IDC to read a FAT entry.
;**********************************************************************	
idc_read_fat_entry:
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_fat_entry_nack
	SET_BANK0
	
	MOVLW	CMD_FAT_ENTRY
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_fat_entry_nack
	SET_BANK0
	
	MOVFW	fat_entry_hi
	CALL	i2c_send		; FAT high entry
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_fat_entry_nack
	SET_BANK0
	
	MOVFW	fat_entry_lo
	CALL	i2c_send		; FAT low entry
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_read_fat_entry_nack
	SET_BANK0


idc_read_fat_entry_nack:
	SET_BANK0
	CALL	i2c_stop
	RETURN


;**********************************************************************
;* Read a 32 byte directory entry from the IDC and display the
;* results as hex strings.
;**********************************************************************
idc_read_dir_value:
	CALL	i2c_start		; START
	MOVLW	IDC_R
	CALL	i2c_send		; Control byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_rd_dir_value_nack
	SET_BANK0
	
	MOVLW	CMD_DIR_VALUE
	CALL	i2c_send		; Command byte
	SET_BANK1
	BTFSC	SSPCON2, ACKSTAT	; Check for ACK
	GOTO	idc_rd_dir_value_nack
	SET_BANK0
	
	MOVLF	CR, tx_reg
	CALL	uart_tx			; Start a new line before display the Dir
		
	MOVLF	0x03, y_cnt		; Two lines of 16 bytes

dir_new_line:
	MOVLF	0x10, x_cnt		; Sixteen bytes per line
	DCFSNZ	y_cnt
	GOTO	dir_finish		; Displayed the whole directory

dir_next_byte:
	CALL	i2c_recv		; Get the byte from the I2C bus
	CALL	i2c_ack
	
	MOVFF	i2c_rx_reg, tx_reg
	CALL	uart_tx_hex		; Display the byte as a hex string
	
	MOVLF	' ', tx_reg		; Put a space between the bytes
	DCFSNZ	x_cnt
	MOVLF	CR, tx_reg		; Put a CR at the end of each line
	CALL	uart_tx
	
	MOVFW	x_cnt
	ADDLW	0x00
	BTFSC	STATUS, Z		; If x_cnt is start a new line
	GOTO	dir_new_line
	GOTO	dir_next_byte
	
dir_finish:
	; All receiving operations from the slave end in a
	; NACK.  Putting it here makes the above loop simpler.
	CALL	i2c_recv
	CALL	i2c_nack
	
idc_rd_dir_value_nack:
	SET_BANK0
	CALL	i2c_stop		; STOP
	RETURN


;**********************************************************************
;* Interrupt service routine.
;*
;* If a byte has been received by the UART and uart, RX_BIT is clear
;* put the byte in rx_reg.
;**********************************************************************
int_srv:
	MOVWF	tempw			; Store W and STATUS
	SWAPF	STATUS, W
	SET_BANK0
	MOVWF	tempst

	BTFSS	PIR1, RCIF		; Check the interrupt is from the UART
	GOTO	int_srv_ret
	
	BTFSC	uart, RX_BIT		; If RX_BIT is set then rx_reg
	GOTO	int_srv_ret		; hasn't been processed yet
	
	MOVFF	RCREG, rx_reg		; Store the received byte
	BSF	uart, RX_BIT		; Signal a new byte has arrived
	MOVFW	RCREG			; There could be a second byte
	
	BCF	RCSTA, CREN
	BSF	RCSTA, CREN
	
int_srv_ret:
        MOVFW	RCREG			; There could be a second byte
        MOVFW	RCREG			; There could be a second byte
	SWAPF	tempst, W		; Restore W and STATUS
	MOVWF	STATUS
	SWAPF	tempw, F
	SWAPF	tempw, W
	
	RETFIE


;**********************************************************************
;* Send whatever is in tx_reg out of the serial port.
;**********************************************************************
uart_tx:
	BTFSS	PIR1, TXIF		; Wait for the serial port to become free
	GOTO	$-1
	MOVFF	tx_reg, TXREG		; Send the byte
	RETURN
	
	
;**********************************************************************
;* Convert the value in tx_reg to a 2 byte ASCII hex string and send
;* it out of the UART.
;**********************************************************************
uart_tx_hex:
	MOVFW	tx_reg
	MOVWF	hex_temp
	ANDLW	0xF0
	MOVWF	hex_reg
	SWAPF	hex_reg, F
	CALL	to_hex
	MOVWF	tx_reg
	CALL	uart_tx			; Top nibble
	
	MOVFW	hex_temp
	ANDLW	0x0F
	MOVWF	hex_reg
	CALL	to_hex
	MOVWF	tx_reg
	CALL	uart_tx			; Bottom nibble
	RETURN


;**********************************************************************
;* Convert the lower nibble of hex_reg to it's hex ASCII Equivalent.
;* The result is in W.
;**********************************************************************
to_hex:
	INCF	hex_reg, F
	MOVLW	LOW hex_table
	ADDWF	hex_reg, F
	MOVLW	HIGH hex_table
	BTFSC	STATUS, C
	ADDLW	0x01
	MOVWF	PCLATH
	MOVFW	hex_reg
	CALL	hex_table
	RETURN
	
	
;**********************************************************************
;* The to_hex lookup table.
;**********************************************************************
hex_table:
	MOVWF	PCL
	RETLW	'0'
	RETLW	'1'
	RETLW	'2'
	RETLW	'3'
	RETLW	'4'
	RETLW	'5'
	RETLW	'6'
	RETLW	'7'
	RETLW	'8'
	RETLW	'9'
	RETLW	'A'
	RETLW	'B'
	RETLW	'C'
	RETLW	'D'
	RETLW	'E'
	RETLW	'F'


;**********************************************************************
;* Transmit tx_reg as a hex string.
;**********************************************************************
uart_rx_hex:
	BCF	hex_cnt, 0
	
wait_rx_byte:
	BCF	uart, RX_BIT		; Allows us to receive a byte
	BTFSS	uart, RX_BIT		; wait for a byte from the UART
	GOTO	$-1
	
	MOVFF	rx_reg, tx_reg
	CALL	uart_tx

	MOVFF	rx_reg, hex_temp
	MOVLW	'A'
	SUBWF	hex_temp, F
	BTFSC	STATUS, C
	GOTO	alpha
	
	; Numeric
	SUBLF	'0', rx_reg
	GOTO	continue
	
alpha:	SUBLF	'A', rx_reg
	ADDLF	0x0A, rx_reg
	
continue:
	BTFSC	hex_cnt, 0
	GOTO	finished
	BSF	hex_cnt, 0
	MOVFF	rx_reg, hex_value
	SWAPF	hex_value, F
	GOTO	wait_rx_byte
	
finished:
	ADDFF	rx_reg, hex_value
	RETURN	
	
	
;**********************************************************************
;* Transmit the string identified by str_id out of the UART.
;**********************************************************************
uart_tx_str:
	INCF	str_id, F
next_byte:
	MOVFF	str_id, byte_offset
	CALL	get_str_byte
	ADDLW	0x00
	BTFSC	STATUS, Z
	GOTO	uart_tx_str_ret
	MOVWF	tx_reg
	CALL	uart_tx
	INCF	str_id, F
	GOTO	next_byte
	
uart_tx_str_ret:
	RETURN


;**********************************************************************
;* Transmit a string from thex second string table.
;**********************************************************************	
uart_tx_str2:
	INCF	str_id, F
next_byte2:
	MOVFF	str_id, byte_offset
	CALL	get_str_byte2
	ADDLW	0x00
	BTFSC	STATUS, Z
	GOTO	uart_tx_str_ret2
	MOVWF	tx_reg
	CALL	uart_tx
	INCF	str_id, F
	GOTO	next_byte2
	
uart_tx_str_ret2:
	RETURN
	
	
;**********************************************************************
;* Read a byte from the string table.  The byte is identified by
;* byte_offset.
;**********************************************************************
	ORG 0x400
get_str_byte:
	MOVLW	LOW str_table
	ADDWF	byte_offset, F
	MOVLW	HIGH str_table
	BTFSC	STATUS, C
	ADDLW	0x01
	MOVWF	PCLATH
	MOVFW	byte_offset
	CALL	str_table
	RETURN
	
	
;**********************************************************************
;* The string table.  Used by uart_tx_str.
;**********************************************************************	
str_table:
	MOVWF	PCL
str_table_start:
menu_str:	
	DT "IDC Menu", CR, CR
	DT "1. RD sector", CR
	DT "2. WR sector", CR
	DT "3. RD data", CR
	DT "4. WR data", CR
	DT "5. RD FAT Entry", CR
	DT "6. RD FAT Value", CR
	DT "7. WR FAT Entry", CR
	DT "8. WR FAT Value", CR
	DT "A. RD Dir Entry", CR
	DT "B. WR Dir Entry", CR
	DT "C. RD Dir Value", CR
	DT "D. WR Dir Value", CR	
	DT "9. Status", CR
	DT "T. Track", CR
	DT "S. Side / Sector", CR
	DT "0. Refresh", CR, CR, 0x00
	
	
;**********************************************************************
;* Get a byte from the second string table.
;**********************************************************************
	ORG 0x500
get_str_byte2:
	MOVLW	LOW str_table2
	ADDWF	byte_offset, F
	MOVLW	HIGH str_table2
	BTFSC	STATUS, C
	ADDLW	0x01
	MOVWF	PCLATH
	MOVFW	byte_offset
	CALL	str_table
	RETURN
	

;**********************************************************************
;* The string table.  Used by uart_tx_str2.
;**********************************************************************	
str_table2:
	MOVWF	PCL
str_table_start2:
status_str:	
	DT CR, "STATUS : 0x", 0x00
track_str:
	DT CR, "New Track : 0x", 0x00
sector_str:
	DT CR, "New Side / Sector : 0x", 0x00
fat_entry_str:
	DT CR, "FAT Entry : 0x", 0x00
fat_value_str:
	DT CR, "FAT Value : 0x", 0x00
dir_entry_str:
	DT CR, "DIR Entry : 0x", 0x00
current_track_str:
	DT "Track : 0x", 0x00
current_sector_str:
	DT CR, "Side / Sector : 0x", 0x00
value_str:
	DT CR, "Value : ", 0x00
	
	END


BackHome