list	p=16f88
        include <p16f88.inc>
        include <coff.inc>

        __CONFIG  _CONFIG1, _CP_OFF & _WDT_OFF &  _INTRC_IO & _PWRTE_ON & _LVP_OFF & _BODEN_OFF & _MCLR_OFF
        __CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

	;; The purpose of this program is to test gpsim's ability to simulate a pic 16F88.
	;; Specifically, the a/d converter is tested.

        errorlevel -302 

; Printf Command
.command macro x
  .direct "C", x
  endm

;----------------------------------------------------------------------
;----------------------------------------------------------------------
GPR_DATA                UDATA_SHR

x  RES  1
t1 RES  1
t2 RES  1
avg_lo RES  1
avg_hi RES  1
w_temp RES  1
status_temp RES  1


;----------------------------------------------------------------------
;   ********************* RESET VECTOR LOCATION  ********************
;----------------------------------------------------------------------
RESET_VECTOR  CODE    0x000              ; processor reset vector
        movlw  high  start               ; load upper byte of 'start' label
        movwf  PCLATH                    ; initialize PCLATH
        goto   start                     ; go to beginning of program

	;; 
	;; Interrupt
	;; 
	movwf	w_temp
	swapf	STATUS,W
	movwf	status_temp

	bcf	STATUS,RP0	;adcon0 is in bank 0

	btfsc	INTCON,ADIE
	 btfsc	PIR1,ADIF
	  goto	check
   .assert "\"FAILED 16F88 unexpected interrupt\""
	nop

;;	An A/D interrupt has occurred
check:
	bsf	t1,0		;Set a flag to indicate we got the int.
	bcf	PIR1,ADIF	;Clear the a/d interrupt

	swapf	status_temp,w
	movwf	STATUS
	swapf	w_temp,F
	swapf	w_temp,W
	retfie



;----------------------------------------------------------------------
;   ******************* MAIN CODE START LOCATION  ******************
;----------------------------------------------------------------------
MAIN    CODE
start:

   .sim "module library libgpsim_modules"
   ; Use a pullup resistor as a voltage source
   .sim "module load pullup V1"
   .sim "V1.resistance = 100.0"
   .sim "module load pullup V2"
   .sim "V2.resistance = 100.0"

   ; V3 and node na0 required for A/D to see pin voltage
   ; this may be a bug RRR 5/06

   .sim "module load pullup V3"	
   .sim "V3.resistance = 10e6"

   .sim "node na0"
   .sim "attach na0 V3.pin porta0"
   .sim "node na1"
   .sim "attach na1 V1.pin porta1"

   .sim "node na3"
   .sim "attach na3 V2.pin porta3"


	;; Let's use the ADC's interrupt
    ; RA1 is an Analog Input.
    ; RA0, RA2 - RA6 are all configured as outputs.
    ;
    ; Use VDD and VSS for Voltage references.
    ;
    ; PCFG = 1110  == AN0 is the only analog input
    ; ADCS = 110   == FOSC/64
    ; ADFM = 0     == 6 LSB of ADRESL are 0.
    ;
                                                                                
	bsf	STATUS,RP0	;adcon1 is in bank 1
        movlw   3
	movwf	ANSEL		; select AN0, AN1
        movwf   TRISA
	
        movlw   (1<<ADCS2) 	; A/D clock divided by 2
        movwf   ADCON1
        bsf     PIE1,ADIE       ;A2D interrupts
	bcf	STATUS,RP0	;adcon0 is in bank 0
        movlw   (1<<ADCS1) | (1<<ADON) | (1<<CHS0); Fosc/64, A2D on, Channel 1
        movwf   ADCON0
                                                                                
        bsf     INTCON,GIE      ;Global interrupts
        bsf     INTCON,PEIE     ;Peripheral interrupts

	
	call	Convert
   .assert "adresh == 0xff, \"FAILED 16F88 inital test\""
	nop

	;; The next test consists of misusing the A/D converter.
	;; TRISA is configured such that the I/O pins are digital outputs.
	;; Normally you want them to be configued as inputs. According to
	;; the data sheet, the A/D converter will measure the voltage produced
	;; by the digital I/O output:	 either 0 volts or 5 volts (or Vdd).
	;; [I wonder if this would be a useful way of measuring the power supply
	;; level in the event that there's an external reference connected to
	;; an3?]
	

	movlw   0
	bsf	STATUS,RP0
	movwf	TRISA		;Make the I/O's digital outputs
	movwf	ADCON1		;Configure porta to be completely analog
	bcf	STATUS,RP0
	bcf	ADCON0,CHS0	;select AN0
	movwf	PORTA		;Drive the digital I/O's low

	;;
	;; First do a few conversion with porta configured as a digital output
	;; that is driving low
	;;
	
	call	Convert

  .assert "adresh == 0x00, \"FAILED 16F88 Digital low\""
	nop

	;;
	;; Now do some with the digital output high
	;;

	movlw	0xff
	movwf	PORTA
	
	call	Convert

  .assert "adresh == 0xff, \"FAILED 16F88 Digital high\""
	nop
	;;
	;; Now make the inputs analog (like they normally would be)
	;;


	bsf	ADCON0,CHS0	;select AN1
	bsf	STATUS,RP0
	movlw	0xff
	movwf	TRISA
	bcf	STATUS,RP0

	call	Convert

  .assert "adresh == 0xff, \"FAILED 16F88 AN1=5V\""
	nop

  .command "V1.voltage=1.0"

	call	Convert

   .assert "adresh == 0x33, \"FAILED 16F88 AN1=1V\""
	nop

	;;
	;; Now let's use the external analog signal connected to AN3
	;; as the voltage reference

	bsf	STATUS,RP0
	bsf	ADCON1,VCFG1
	bcf	STATUS,RP0

  .command "V2.voltage=2.0"

	call	Convert

   .assert "adresh == 0x80, \"FAILED 16F88 AN1=1V Vref+=2V\""
	nop

  .assert  "\"*** PASSED 16F88 a2d test\""
	
	goto	$-1



Convert:

	clrf	t1		;flag set by the interrupt routine
	
	bsf	ADCON0,GO	;Start the A/D conversion

	btfss	t1,0		;Wait for the interrupt to set the flag
	 goto	$-1

        movf    ADRESH,W                ;Read the high 8-bits of the result

	return

	end


syntax highlighted by Code2HTML, v. 0.9.1