Daryl's Computer Hobby Page
  Home     SBC Circuit Boards     SBC Software     I/O Devices     Windows 65C02 Simulator     Downloads     News, Events, & History  
  FAT16 IDE Interface     65SPI - SPI Controller Chip     PC keyboard interfaces     2-Way IR Communications     Displays     Device Programmers  
  Atmel ATmega8 Programmer     PIC 16F628 Programmer     28256 EEPROM Programmer  

PIC 16F628 Microcontroller Programmer

This project describes how to build a simple programmer for the PIC 16F628 single-chip microcontroller.   This should be considered a "hobby grade" programmer as it does not use a variable Vcc and uses the "Low Voltage Programming Algorithm."   The 16F628 can be configured (through programmable fuses) to disable the low voltage Programming mode.   Once disabled, a high voltage programmer is required to re-enable the low voltage programming mode.

The programmer hardware consists of an 18 pin socket for the 16F628 and a 14 pin header for connecting to the SBC2's onboard 65C22.   Please note that this can be adapted for use with any 65C22.   The 16F628 uses 4 pins for serial programming (in addition to Vcc and Gnd):

  16F628 (DIP18)                          SBC2      65C22 (DIP40)
Port Pin Function Description J1 PIN PORT RB6 12 clk shift clock 3 2 PA0 RB7 13 data data in/out 4 3 PA1 RB4 10 prg program mode 9 8 PA6 RA5 4 CLR clear/run mode 10 9 PA7

This programmer should also work for other processors in the PIC family (with some modifications).

Here is the programmer schematic:
Schematic

Programming the 16F628 is done by placing the part into RESET and PRG mode, then applying serial commands and data to the data pin while clocking the clk pin.

There are commands to read and write program memory, EEPROM memory, and the configuration area.   Since the MPLABS Development tools create an Intel-Hex formatted output file, I decided to incorporate a modified version of Ross Archer's "Intel Hex Downloader" which allows for seemless operation.

Here is the source code for the programmer:

;=======================================================================
;                                                                      |
;          SSSSSSS    BBBBBB     CCCCCC                222222          |
;         S      S   B      B   C      C              2      2         |
;        SS         B      B   C                           2           |
;         SSSS     BBBBBBB    C          ========       2              |
;            SS   B      B   C                       2                 |
;     S      S   B      B   C      C              2                    |
;     SSSSSSS   BBBBBBB     CCCCCCC               22222222             |
;                                                                      |
;=======================================================================
;
;//****************************************************************//
;// Routine to program a PIC16F628 using 4 lines from a 65C22 VIA  //
;// By Daryl Rictor  (c) Dec 19, 2003   http://65c02.tripod.com/   //
;//****************************************************************//
;
; This program was tested and ran from my SBC-2 board running at
; 2MHz.  Any 65c02-based computer with a 6522 VIA should be able
; to use this code.
;
; VIA - PIC connections
;
; VIA  Function  PIC
;-------------------
; PA0  CLK   ->  RB6 
; PA1  DATA  ->  RB7
; PA6  PGM   ->  RB4
; PA7  MCLR  ->  RA5
;
; *** This is a hobby-grade programmer using the LV programming algorithm.  
;
;*********************************************************************
; Memory map
;*********************************************************************
; This program resided in RAM from $0800 to $0F76
;
; Program Data is loaded from $1000 - $1FFF (14 bits, w/0 in bit 14 & 15, lo, hi))
; EEPROM data is loaded from $2000 - $207F (8 bits)
; ID and configuration words are loaded from $2100-2109 (14 bits w/0 in bits 14 & 15)
;     PIC ADDR - PC ADDR
; 	$2000 <- $2100,$2101  ID word 0 (lo, hi)
; 	$2001 <- $2102,$2103  ID word 1 (lo, hi)
; 	$2002 <- $2104,$2105  ID word 2 (lo, hi)
; 	$2003 <- $2106,$2107  ID word 3 (lo, hi)
; 	$2007 <- $2108,$2109  Configuration Word (lo, hi)
;
;During compare operations, these addresses are used to store the data read from the PIC
; Program Data is stored at $3000 - $3FFF (14 bits, w/0 in bit 14 & 15, lo, hi))
; EEPROM data is stored at  $4000 - $407F (8 bits)
; ID and configuration words are stored at $4100-4109 (14 bits w/0 in bits 14 & 15)
;    PIC ADDR - PC ADDR
; 	$2000 -> $4100,$4101  ID word 0 (lo, hi)
; 	$2001 -> $4102,$4103  ID word 1 (lo, hi)
; 	$2002 -> $4104,$4105  ID word 2 (lo, hi)
; 	$2003 -> $4106,$4107  ID word 3 (lo, hi)
; 	$2007 -> $4108,$4109  Configuration Word (lo, hi)
;
;***********************************************************************
; MENU OPTIONS
;
; 1 - Erase Program Memory
; 2 - Write Program Memory
; 3 - Compare Program Memory (read back stored in $3000-$3FFF)
; 4 - Erase EEPROM Data
; 5 - Write EEPROM Data
; 6 - Compare EEPROM Data (read back stored in $4000-$407F)
; 7 - Write Configuration Data
; 8 - Compare Configuration Data (read back stored in $4100-$410F)
; 9 - Reset memory protection and erase PIC
; L - Do Intel Hex download (offset to $1000)
; Q - Quit

;//****************************************************************
; CONSTANTS
;//****************************************************************
LDCONF		=	$00			; Load Configuration Command
LDPM		=	$02			; Load Data for Program Memory Command
LDDM		=	$03			; Load Data for Data Memory Command
INCADD		=	$06			; Increment Program Counter Command
RDPM		=	$04			; Read Program Memory Command
RDDM		=	$05			; Read Data memory Command
BPOC		=	$18			; Begin Program Only Cycle Command
BEPC		=	$08			; begin Erase & Program Cycle Command
BEPM		=	$09			; Bulk Erase Program Memory Command
BEDM		=	$0B			; Bulk Erase Data Memeory Command
BES1		=	$01			; Bulk Erase Setup #1 Command
BES2		=	$07			; Bulk Erase Setup #2 Command
OUTP		=	$FF			; Set all 8 bits as outputs (65C22)
INP		=	$00			; set all 8 bits as inputs (65C22)

;//****************************************************************
; SBC-2 VIA address Map
;//****************************************************************
Via1DDRA	=	$7F53			; Data Direction Register
Via1PRA		=	$7F51			; Port A Data Register
CLK		=	$01			; Port A - clock pin
DATA		=	$02			; Port A - data pin
PGM		=	$40			; Port A - program pin
MCLR		=	$80			; Port A - /MCLR pin

;//****************************************************************
; Zero Page Variables
;//****************************************************************
memptr		=	$30			; 2 byte memory pointer
shift		=	$32			; 2 byte temp shift word
spsav		= 	$ee			; sav SP (restores stack on exit)
						; needed by abort code in HEX upload cmd
;//****************************************************************
; init code
;//****************************************************************
	 	*=  	$0800			; beginning of code

		tsx
		stx	spsav			; save the stack pointer
		lda	#OUTP
		sta	Via1DDRA		; set all pins as outputs
		jsr	SetMCLR			; set PIC in RESET mode
;//****************************************************************
; Main Program
;//****************************************************************
main		ldx	#$00			; set display pointer
prtmenu		lda	menudat,x		
		beq	getcmd
		jsr	output			; print then command menu
		inx
		bra	prtmenu
getcmd		jsr	input			; wait for a keypress
		cmp	#"Q"			; is it Q?
		bne	getc1
		jsr	SetMCLR			; ensure pic is reset
		rts				; exit program
getc1		cmp	#"1"			; is it 1?	
		bne	getc2
		jsr	epm			; erase program memory
		bra	main
getc2		cmp	#"2"			; is it 2?
		bne	getc3
		jsr	wpm			; write program memory
		bra	main
getc3		cmp	#"3"			; is it 3?
		bne	getc4
		jsr	cpm			; compare program memory
		bra	main
getc4		cmp	#"4"			; is it 4?
		bne	getc5
		jsr	eem			; erase data memory (EEPROM)
		bra	main
getc5		cmp	#"5"			; is it 5?
		bne	getc6
		jsr	wem			; write data memory (EEPROM)
		bra	main
getc6		cmp	#"6"			; is it 6?
		bne	getc7
		jsr	cem			; compare data memory (EEPROM)
		bra	main
getc7		cmp	#"7"			; is it 7?
		bne	getc8
		jsr	wcd			; write Configuration data
		bra	main
getc8		cmp	#"8"			; is it 8?
		bne	getc9
		jsr	ccd			; compare Configuration data
		bra	main
getc9		cmp	#"9"			; is it 9?
		bne	getcL			; 
		jsr	unp			; remove code protection and erase PIC
		bra	main
getcL		cmp	#"L"			; is it L?
		bne	getcmd			; no, not valid key
		jsr	HexDnLd			; Do Intel Hex download
		bra	main

;//****************************************************************
;  Erase Program Memory
;//****************************************************************
epm		jsr	SetPRG			; set PRG mode
		ldy	#$3F			; hi data word
		ldx	#$FF			; low data word
		lda	#LDPM			; Load Data for PM cmd
		jsr	sendcmd			; send it
		jsr	senddata		; send data word
		lda	#BEPM			; bulk erase PM cmd
		jsr	sendcmd			; send it
		lda	#BPOC			; begin Program only cmd
		jsr	sendcmd			; send it
		lda	#10			;
		jsr	delay_ms		; wait 10 ms for erase to be done
		jsr	SetMCLR			; Reset PIC
		rts				; done

;//****************************************************************
;  Write Program Memory
;//****************************************************************
wpm		lda	#$00			;
		sta	memptr			; init mem pointer
		lda	#$10			; to start of ram table
		sta	memptr+1		; 	
		jsr	SetPRG			; set PRG mode
wpm1		lda	(memptr)		; get data byte (lo half of word)
		pha				; save it
		tax				; save lo byte
		jsr	incptr			; advance pointer
		lda	(memptr)		; get data byte (hi half of word)
		pha				; save it
		tay				; save hi byte
		jsr	incptr			; advance pointer
		lda	#LDPM			; Load Data for PM cmd
		jsr	sendcmd			; send it
		jsr	senddata		; send data word
		lda	#BPOC			; begin Program only cmd
		jsr	sendcmd			; send it
		lda	#10			; 10ms max prog cycle
		jsr	delay_ms		; wait 6 ms for erase to be done
		lda	#RDPM			;
		jsr	sendcmd			; read data from PM cmd
		jsr	readdata		; read back PM values are returned as yyxx
		pla				; get hi byte
		cmp	shift+1			; compare with hi byte read back
		beq	wpm2			; match?
		jsr	Hierr			; no! -write fail error, stop
		pla				; clean stack
		rts
wpm2		pla				; yes restore lo byte
		cmp	shift			; compare with lo byte read back
		beq	wpm3			; match?
		jsr	Loerr			; no! write fail error, stop
		rts
wpm3		lda	memptr			; yes get pointer low byte
		cmp	#$00			; is it 0?
		bne	wpm4			; no, inc PIC PC
		lda	memptr+1		; get pointer hi byte
		cmp	#$20			; is hi page = $20xx
		beq	wpm5			; yes, we're done
wpm4		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		bra	wpm1			; prog next word
wpm5		jsr	SetMCLR			; done, Reset PIC
		rts				; done

;//****************************************************************
;  Compare Program Memory
;//****************************************************************
cpm		lda	#$00			;
		sta	memptr			; init mem pointer
		lda	#$30			; to start of ram table
		sta	memptr+1		; 	
		jsr	SetPRG			; set program mode
cpm1		lda	#RDPM			;
		jsr	sendcmd			; read data from PM cmd
		jsr	readdata		; read back PM values into shift variable
		lda	shift			;
		sta	(memptr)		;	
		jsr	incptr			;
		lda	shift+1			;
		sta	(memptr)		;
		jsr	incptr			;
cpm3		lda	memptr			; yes get pointer low byte
		cmp	#$00			; is it 0?
		bne	cpm4			; no, inc PIC PC
		lda	memptr+1		; get pointer hi byte
		cmp	#$40			; is hi page = $40xx
		beq	cpm5			; yes, we're done
cpm4		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		bra	cpm1			; prog next word
cpm5		lda	#$00			;
		sta	memptr			; init mem pointer to PIC mem table
		sta	shift			; init shift to Desired mem table
		lda	#$30			; 
		sta	memptr+1		;
		lda	#$10			;
		sta	shift+1			;
		ldy	#$00			;
cpm6		lda	(memptr)		; get PIC data
		cmp	(shift),y		; is equal to ourt data?
		beq	cpm7			; yes, do next
		lda	memptr			; no - print error
		tax	
		lda	memptr+1		;
		and	#$0F			; mask hi bits
		jsr	print2byte		; if mismatched then print error
		lda	#":"			;
		jsr	output			; format is:
		lda	(memptr)		;	
		jsr	print1byte		; AAAA:DD-CC
		lda	#"-"			;
		jsr 	output			; where AAAA is the byte address
		lda	(shift)			; DD is the PIC memory value
		jsr	print1byte		; CC is the expected value	
		jsr	print_cr		;
cpm7		jsr	incptr			; inc mem pointers
		inc	shift			;
		bne	cpm8			;
		inc	shift+1			;
cpm8		lda	memptr			; 
		cmp	#$00			; is equal to 0?
		bne	cpm6			; no, get next
		lda	memptr+1		; 
		cmp	#$40			; top of table?
		bne	cpm6			; no, get next
cpm9		jsr	SetMCLR			; done, Reset PIC
		rts				; done

;//****************************************************************
;  Erase EEPROM Memory
;//****************************************************************
eem		jsr	SetPRG			; set PRG mode
		ldy	#$3F			; hi data word
		ldx	#$FF			; low data word
		lda	#LDDM			; Load Data for DM cmd
		jsr	sendcmd			; send it
		jsr	senddata		; send data word
		lda	#BEDM			; bulk erase DM cmd
		jsr	sendcmd			; send it
		lda	#BPOC			; begin Program only cmd
		jsr	sendcmd			; send it
		lda	#10			;
		jsr	delay_ms		; wait 6 ms for erase to be done
		jsr	SetMCLR			; Reset PIC
		rts				; done
;//****************************************************************
;  Write EEPROM Memory
;//****************************************************************
wem		lda	#$00			;
		sta	memptr			; init mem pointer
		lda	#$20			; to start of ram table
		sta	memptr+1		; 	
		jsr	SetPRG			; set PRG mode
wem1		lda	(memptr)		; get data byte (lo half of word)
		pha				; save it
		tax				; save lo byte
		ldy	#$00			; hi byte is $00
		inc	memptr			; advance pointer
		lda	#"."			; visual status
		jsr	output			; send to SBC output port
		lda	#LDDM			; Load Data for DM cmd
		jsr	sendcmd			; send it
		jsr	senddata		; send data word
		lda	#BPOC			; begin Program only cmd
		jsr	sendcmd			; send it
		lda	#10			; 8ms max programming time
		jsr	delay_ms		; wait 6 ms for erase to be done
		lda	#RDDM			;
		jsr	sendcmd			; read data from DM cmd
		jsr	readdata		; read back PM values are returned as yyxx
		pla				; get hi byte
		cmp	shift			; compare with lo byte read back
		beq	wem3			; match?
		jsr	Loerr			; no! write fail error, stop
		rts
wem3		lda	memptr			; yes get pointer low byte
		cmp	#$80			; is it 0?
		beq	wem5			; yes, we're done
wem4		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		bra	wem1			; prog next word
wem5		jsr	SetMCLR			; done, Reset PIC
		rts				; done

;//****************************************************************
;  Compare EEPROM Memory
;//****************************************************************
cem		lda	#$00			;
		sta	memptr			; init mem pointer
		lda	#$40			; to start of ram table
		sta	memptr+1		; 	
		jsr	SetPRG			; set PRG mode
cem1		lda	#RDDM			;
		jsr	sendcmd			; read data from DM cmd
		jsr	readdata		; read back DM values into shift variable
		lda	shift			;
		sta	(memptr)		;	
		inc	memptr			;
		lda	#"."			; visual status
		jsr	output			; send to SBC output port
cem3		lda	memptr			; yes get pointer low byte
		cmp	#$80			; is it 80?
		beq	cem5			; yes, we're done
cem4		lda	#$06			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		bra	cem1			; prog next word
cem5		jsr	print_cr		; 
		lda	#$00			;
		sta	memptr			; init mem pointer to PIC mem table
		sta	shift			; init shift to Desired mem table
		lda	#$40			; 
		sta	memptr+1		;
		lda	#$20			;
		sta	shift+1			;
		ldy	#$00			;
cem6		lda	(memptr)		; get PIC data
		cmp	(shift),y		; is equal to ourt data?
		beq	cem7			; yes, do next
		lda	memptr			; no - print error
		tax				; sav lo byte
		lda	#$00			; hi byte=0
		jsr	print2byte		; if mismatched then print error
		lda	#":"			;
		jsr	output			; format is:
		lda	(memptr)		;	
		jsr	print1byte		; AAAA:DD-CC
		lda	#"-"			;
		jsr 	output			; where AAAA is the byte address
		lda	(shift)			; DD is the PIC memory value
		jsr	print1byte		; CC is the expected value	
		jsr	print_cr		;
cem7		inc	memptr			; inc mem pointers
		inc	shift			;
cem8		lda	memptr			; 
		cmp	#$80			; is equal to 80?
		bne	cem6			; no, get next
cem9		jsr	SetMCLR			; done, Reset PIC
		rts				; done

;//****************************************************************
;  Write Configuration Words
;//****************************************************************
wcd		lda	#$00			;
		sta	memptr			; init mem pointer
		lda	#$21			; to start of ram table
		sta	memptr+1		; 	
		jsr	SetPRG			; set PRG mode
		ldy	#$00			; init data word to $0000
		ldx	#$00			;
		lda	#LDCONF			; Load Configuration cmd
		jsr	sendcmd			; send it
		jsr	senddata		; send data=0000
wcd1		lda	(memptr)		; get data byte (lo half of word)
		pha				; save it
		tax				; save lo byte
wcd11		jsr	incptr			; advance pointer
		lda	(memptr)		; get data byte (hi half of word)
		pha				; save it
		tay				; save hi byte
		jsr	incptr			; advance pointer
		lda	#"."			; visual status
		jsr	output			; send to SBC output port
		lda	#LDPM			; Load Data for PM cmd
		jsr	sendcmd			; send it
		jsr	senddata		; send data word
		lda	#BPOC			; begin Program only cmd
		jsr	sendcmd			; send it
		lda	#10			;
		jsr	delay_ms		; wait 10ms for erase to be done
		lda	#RDPM			;
		jsr	sendcmd			; read data from PM cmd
		jsr	readdata		; read back PM values are returned as yyxx
		pla				; get hi byte
		cmp	shift+1			; compare with hi byte read back
		beq	wcd2			; match?
		jsr	Hierr			; no! -write fail error, stop
		pla				; clear stack
		rts
wcd2		pla				; yes restore lo byte
		cmp	shift			; compare with lo byte read back
		beq	wcd3			; match?
		jsr	Loerr			; no! write fail error, stop
		rts
wcd3		lda	memptr			; yes get pointer low byte
		cmp	#$08			; is it < 8?
		bpl	wcd4			; yes, inc PIC PC
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		bra	wcd1			; prog next word
wcd4		bne	wcd5			; if not 8, then we're done
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	(memptr)		; get data byte (lo half of config word)
		ora	#$80			; ensure LV programming is always enabled!!!
		pha				; save it
		tax				; save lo byte
		bra	wcd11			; get hi byte & write the configuration word
wcd5		jsr	SetMCLR			; done, Reset PIC
		rts				; done

;//****************************************************************
;  Compare Configuration Words
;//****************************************************************
ccd		lda	#$00			;
		sta	memptr			; init mem pointer
		lda	#$41			; to start of ram table
		sta	memptr+1		; 	
		jsr	SetPRG			; set PRG mode
		LDY	#$00			; init data word to $0000
		LDX	#$00				;
		lda	#LDCONF			; Load Configuration cmd
		jsr	sendcmd			; send it
		jsr	senddata		; data=0000
ccd1		lda	#RDPM			;
		jsr	sendcmd			; read data from PM cmd
		jsr	readdata		; read back PM values into shift variable
		lda	shift			;
		sta	(memptr)		; save lo byte	
		inc	memptr			;
		lda	shift+1			;
		sta	(memptr)		; save hi byte
		inc	memptr			;
		lda	#"."			; visual status
		jsr	output			; send to SBC output port
		lda	memptr			; yes get pointer low byte
		cmp	#$08			; is it >=8?
		bpl	ccd4			; yes, get config word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		jmp	ccd1			; read next word
ccd4		bne	ccd5			; <>8 means were done
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to next word
		lda	#INCADD			; no, INC PC command
		jsr	sendcmd			; inc PIC's PC to conguration word
		jmp	ccd1			; read it		
ccd5		jsr	print_cr		; send  to SBC output port
		lda	#$00			;
		sta	memptr			; init mem pointer to PIC mem table
		sta	shift			; init shift to Desired mem table
		lda	#$41			; 
		sta	memptr+1		;
		lda	#$21			;
		sta	shift+1			;
		ldy	#$00			;
ccd6		lda	(memptr)		; get PIC data
		cmp	(shift),y		; is equal to ourt data?
		beq	ccd7			; yes, do next
		lda	memptr			; no - print error
		tax				; save lo byte
		lda	#$00			; set high byte to 0
		jsr	print2byte		; if mismatched then print error
		lda	#":"			;
		jsr	output			; format is:
		lda	(memptr)		;	
		jsr	print1byte		; AAAA:DD-CC
		lda	#"-"			;
		jsr 	output			; where AAAA is the byte address
		lda	(shift)			; DD is the PIC memory value
		jsr	print1byte		; CC is the expected value	
		jsr	print_cr		;
ccd7		inc	memptr			; inc mem pointers
		inc	shift			;
ccd8		lda	memptr			; 
		cmp	#$0A			; is equal to A?
		bne	ccd6			; no, get next
ccd9		jsr	SetMCLR			; done, Reset PIC
		rts				; done

;//****************************************************************
; unp - Unprotect and erase the PIC
;//****************************************************************
unp		jsr	SetPRG			; set PRG mode
		ldx	#$00			; low data word
		ldy	#$00			; hi data word
		lda	#LDCONF			; Load Configuration cmd w/$0
		jsr	sendcmd			; send it
		jsr	senddata		; send data word=0000
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#INCADD			; Inc PC cmd
		jsr	sendcmd			; send it
		lda	#BES1			; Bulk Erase setup 1 cmd
		jsr	sendcmd			; send it
		lda	#BES2			; Bulk Erase setup 2 cmd
		jsr	sendcmd			; send it
		lda	#BEPC			; begin Erase/Program cmd
		jsr	sendcmd			; send it
		lda	#20			; 14ms max wait time
		jsr	delay_ms		; wait 14 ms for erase to be done
		lda	#BES1			; Bulk Erase setup 1 cmd
		jsr	sendcmd			; send it
		lda	#BES2			; Bulk Erase setup 2 cmd
		jsr	sendcmd			; send it
		jsr	SetMCLR			; Reset PIC
		rts

;###################################################################
;
; Subroutines
;
;###################################################################

;//****************************************************************
; sendcmd - send a programming command to the PIC
;//****************************************************************
sendcmd		sta	shift			; save cmd
		phx				; save x reg
		ldx	#06			; number of command bits
sendcmdlp	lsr	shift			; get lsb of cmd	
		bcc	sendcmd1		; is it 0?
		lda	#$F3			; no, set data=1, clk=1
		.byte	$2c			; bit abs opcode (skip cmd)
sendcmd1	lda	#$F1			; yes, set data=0, clk=1
		sta	Via1PRA			; Raise clk
		and	#$FE			; mask out clk bit
		sta	Via1PRA			; drop clk
		dex				; done all 6 yet
		bne	sendcmdlp		; no, do next
		lda	#$F0			; yes 
		sta	Via1PRA			; drop data & clk
		plx				; restore x reg
		rts

;//****************************************************************
; senddata - send a data word to the PIC from YYXX
;	     YYXX = 14 bits, right aligned (00yyyyyy xxxxxxxx)
;	     sent centered ( 0yyyyyyx xxxxxxx0)	
;//****************************************************************
senddata	phx				; save x
		phy				; save y
		stx	shift			; save lo byte
		tya				; get hi byte
		and	#$3F			; force 14 bits only
		sta	shift+1			; save hi byte
		ldx	#16			; # of bits
		bra	senddata1		; first bit is always 0
senddatalp	lsr	shift+1			; adds 0's to top end 
		ror	shift			; 15th & 16th shift will send 0's
		bcc	senddata1		;
		lda	#$F3			; set data=1, clk=1
		.byte	$2c			; bit abs opcode
senddata1	lda	#$F1			; set data=0, clk=1
		sta	Via1PRA			; Raise clk
		and	#$FE			; 
		sta	Via1PRA			; drop clk
		dex				; done all 16 yet
		bne	senddatalp		; no, do next
		lda	#$F0			; yes 
		sta	Via1PRA			; drop data & clk
		ply				; restore y
		plx				; restore x
		rts

;//****************************************************************
; readdata - read a 16 bit data word from the PIC into shift vairable
;	     14 bits, right aligned (00yyyyyy xxxxxxxx)
;//****************************************************************
readdata	lda	#$F1			;
		sta	Via1PRA			; raise clk
		ldx	#$FD			; bit 1 now input
		lda	#$F0			; 
		sta	Via1PRA			; lower clk
		stx	Via1DDRA		; set DDRA
		stz	shift			;	
		stz	shift+1			; zero variable
		ldx	#14			; # of data bits
readdatalp	lda	#$F1			;
		sta	Via1PRA			; raise clk
		nop				; wait for valid data
		lda	Via1PRA			; read data port
		and	#$02			; mask data pin
		clc				; clr carry
		adc	#$FF			; set carry if data bit = 1
		ror	shift+1			; shift carry bit into storage
		ror	shift			; shift second byte
		lda	#$F0			; 	
		sta	Via1PRA			; lower clk
		dex				; done all 14 yet
		bne	readdatalp		; no, do next
		nop				;
		lda	#$F1			;
		sta	Via1PRA			; raise clk
		lda	#$FF			; bit 1 now output again
		sta	Via1DDRA		; set DDRA
		lda	#$F0			; 
		sta	Via1PRA			; lower clk
		lsr	shift+1			; shift carry bit into storage
		ror	shift			; shift second byte
		lsr	shift+1			; shift carry bit into storage
		ror	shift			; shift second byte
		rts

;//****************************************************************
; incptr - increment the memory pointer
;//****************************************************************
incptr		inc	memptr			; inc low byte
		bne	incptr2			; if not zero, done
		inc	memptr+1 		; if if zero, inc hi byte
incptr1		lda	#"."			; visual status
		jsr	output			; send to SBC output port
		lda	#$60			; page $60xx
		cmp	memptr+1		; compare with hi byte
		bpl	incptr2			; if below $60xx, done
		brk				; memptr too high - error, stop
incptr2		rts				; done

;//****************************************************************
; delay_ms - delay milliseconds in A reg
;//****************************************************************
delay_ms	phx				; 
		phy				;  1MHz clk = $01C0 
delayms1	ldy	#$02			;  2MHz clk = $0288 
		ldx	#$88			;  4MHz clk = $0416 
delayms2	dex				;  8MHz clk = $0734 
		bne	delayms2		; 10MHz clk = $08C3
		dey				; 14MHz clk = $0BE0
		bne	delayms2
		dec
		bne	delayms1
		ply
		plx
		rts	

;//****************************************************************
;  Compare Error on High Byte
;//****************************************************************
HiErr		jsr	SetMCLR			; reset the PIC
		jsr	print_cr		; new line
		lda	memptr+1
		ldx	memptr
		jsr	print2byte		; print SBC address pointer
		ldx	#$00
herr		lda	herrtxt,x
		beq	herr1
		jsr	output
		inx
		bra	herr
herr1		rts
herrtxt		.byte	" - Hi Byte Write Error!", $0d, $0a, $00

;//****************************************************************
;  Compare Error on LO Byte
;//****************************************************************
LoErr		jsr	SetMCLR			; reset the PIC
		jsr	print_cr		; new line
		lda	memptr+1
		ldx	memptr			; *** adj 2 bytes backward
		jsr	print2byte		; print SBC address pointer
		ldx	#$00
Lerr		lda	Lerrtxt,x
		beq	Lerr1
		jsr	output
		inx
		bra	Lerr
Lerr1		rts
Lerrtxt		.byte	" - Lo Byte Write Error!", $0d, $0a, $00

;//****************************************************************
;  SetMCLR - Set the PIC into RESET mode
;//****************************************************************
SetMCLR		lda	#$00
		sta	Via1PRA			; set PIC into RESET mode
		lda	#$80			; raise MCLR
		sta	Via1PRA			; set PIC into RUN mode
		lda	#$01			; 1ms
		jsr	delay_ms		; pause
		lda	#$00			; lower MCLR
		sta	Via1PRA			; set PIC into RESET mode
		rts

;//****************************************************************
;  SetPGM - Set the PIC into PROGRAM mode
;//****************************************************************
SetPRG		lda	#$F0
		sta	Via1PRA			; set PIC into PROGRAM mode
		ldx	#$04
setpgm1		dex
		bne	setpgm1			; delay 5uS
		rts

;//****************************************************************
;  Menudat - Programming Menu text 
;//****************************************************************
menudat		.byte	$0d, $0a
		.byte	"Program Memory", $0d, $0a
		.byte	" 1 - Erase", $0d, $0a
		.byte	" 2 - Write", $0d, $0a
		.byte	" 3 - Compare", $0d, $0a
		.byte	"EEPROM Data", $0d, $0a
		.byte	" 4 - Erase", $0d, $0a
		.byte	" 5 - Write", $0d, $0a
		.byte	" 6 - Compare", $0d, $0a
		.byte	"Configuration Data", $0d, $0a
		.byte	" 7 - Write", $0d, $0a
		.byte	" 8 - Compare", $0d, $0a
		.byte	" 9 - Unprotect & Erase PIC", $0d, $0a
		.byte	"(L)oad Intel Hex File", $0d, $0a		
		.byte	"(Q)uit Program", $0d, $0a, $0d, $0a, $00

;//****************************************************************
;  Jump Table into the SBC Monitor's Jump Table (v5.0 and above)
;//****************************************************************
output		jmp	$e824			; send to SBC output port
input		jmp	$e821			; wait for chr from SBC input port
scan		jmp	$e81e			; get chr from SBC input port (no wait)
print_cr        jmp	$e803			; print CR-LF to SBC output port
print2sp        jmp	$e809			; print 2 spaces
print1sp        jmp	$e806			; print 1 space
print1byte      jmp	$e812			; print one byte (AA) to SBC output port
print2byte      jmp	$e815			; print one word (AAXX) to SBC output port

;//****************************************************************
; END
;//****************************************************************


;//****************************************************************
; Ross Archer's Intel Hex downloader added to allow direct import
; of the *.hex file generated by the MPLAB assembler.
;//****************************************************************
;  add an Intel-Hex Downloader to get program code for target PIC
;  I'm using Ross Archer's code, with slight modifications to use
;  some of the SBC's commands.
;
; zero page variables (Its ok to stomp on the monitor's zp vars)
;
;
reclen    	=   	$39        	; record length in bytes
chksum    	=   	$3A        	; record checksum accumulator
start_lo  	=   	$3b
start_hi  	=   	$3c
rectype   	=   	$3d
dlfail    	=   	$3e     	; flag for upload failure
temp      	=   	$3f     	; save hex value
strptr      	=     	$40
strptrh		=     	$41		; temporary string pointer (not preserved across calls)

;
;  tables and constants
;
CR		=	13
LF		=	10
ESC         	=     	27          ; ESC to exit

HexDnLd		jsr	print_cr
		lda    	#0
        	sta	dlfail          ;Start by assuming no D/L failure
HdwRecs 	jsr     GetSer          ; Wait for start of record mark ':'
        	cmp     #":"
        	bne     HdwRecs         ; not found yet
        	; Start of record marker has been found
IHex    	jsr     GetHex          ; Get the record length
        	sta     reclen          ; save it
       	 	sta     chksum          ; and save first byte of checksum
        	jsr     GetHex          ; Get the high part of start address
        	sta     start_hi
        	clc
        	adc     chksum          ; Add in the checksum       
        	sta     chksum          ; 
        	jsr     GetHex          ; Get the low part of the start address
        	sta     start_lo
        	clc
        	adc     chksum
        	sta     chksum  
		clc
		lda	#$10
		adc	start_hi
		sta	start_hi	; adjust storage base 
        	jsr     GetHex          ; Get the record type
        	sta     rectype         ; & save it
        	clc
        	adc     chksum
        	sta     chksum   
        	lda     rectype
        	bne     HdEr1           ; end-of-record
        	ldx     reclen          ; number of data bytes to write to memory
        	ldy     #0              ; start offset at 0
HdLp1   	jsr     GetHex          ; Get the first/next/last data byte
        	sta     (start_lo),y    ; Save it to RAM
        	clc
        	adc     chksum
        	sta     chksum          ; 
        	iny                     ; update data pointer
        	dex                     ; decrement count
        	bne     HdLp1
        	jsr     GetHex          ; get the checksum
        	clc
        	adc     chksum
        	bne     HdDlF1          ; If failed, report it
        	; Another successful record has been processed
        	lda     #"#"            ; Character indicating record OK = '#'
		jsr	output
        	jmp     HdwRecs         ; get next record     
HdDlF1  	lda     #"F"            ; Character indicating record failure = 'F'
        	sta     dlfail          ; upload failed if non-zero
		jsr	output
        	jmp     HdwRecs         ; wait for next record start
HdEr1   	cmp     #1              ; Check for end-of-record type
        	beq     HdEr2
		cmp	#4
		beq	HdwRecs		; skip this type (configuration data word???)
		lda	#>MsgUnknownRecType
		ldx	# upload has failed
        	jsr     Print1Byte      ; print it
		jsr	print_cr
		jmp	HdwRecs
		; We've reached the end-of-record record
HdEr2   	jsr     GetHex          ; get the checksum 
        	clc
        	adc     chksum          ; Add previous checksum accumulator value
        	beq     HdEr3           ; checksum = 0 means we're OK!
		lda	#>MsgBadRecChksum
		ldx	#MsgUploadFail
		ldx	#MsgUploadOK
		ldx	# 255
PrintStrAXX1    pla
		tay
		rts   
;
; Checksum messages
;					
MsgUnknownRecType  
		.byte   CR,LF,CR,LF
      		.byte   "Unknown record type $"
		.byte	0		; null-terminate every string
MsgBadRecChksum .byte   CR,LF,CR,LF
                .byte   "Bad record checksum!"
        	.byte   0		; Null-terminate  
MsgUploadFail   .byte   CR,LF,CR,LF
                .byte   "Upload Failed",CR,LF
                .byte   "Aborting!"
                .byte   0               ; null-terminate every string or crash'n'burn
MsgUploadOK	.byte   CR,LF,CR,LF
                .byte   "Upload Successful!"
        	.byte   0         	

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
; END OF PROGRAM
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

All info provided "as-is" and is Copyright 2003.