;**********************************************************************
; useless.asm
; Software for useless machine
;
; Usage of Pins:
; GP5: on/off switch (on: contacts closed GP5 to Vss/0V)
; GP4: servo power on/off (high: on)
; GP3: -
; GP2: servo pulse (positive logic; 16ms/cycle)
; GP1: -
; GP0: -
;
; Assuming INTOSC f=4MHz
;-------------------------------
;Stephan Asmus, Germany
;(c) info@sasmus.de
;
; 02/2018
;-------------------------------
; 16.02.2018	Starting
; 19.02.2018	V 0.1 working
; 05.03.2018 modifications, emergency return
; 08.03.2018 min/max positions for prototype hardware adapted, ready
; 
;**********************************************************************
    LIST   P=PIC12F675

    #include <p12f675.inc>	; processor specific variable definitions


    errorlevel  -302			;No bank select warnings

	__CONFIG _FOSC_INTRCIO & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF & _CP_OFF & _CPD_OFF & 0x01ff

	;added & 0x01ff because of error within MPLAB X IDE Toolchain. / inc file for 12f675 
	

;***** VARIABLE DEFINITIONS
    cblock 0x20			;define a series of variables.
		W_TEMP			;context saving during interrupt
		STATUS_TEMP		;dto
		d1_temp
		d2_temp
		d1				;Register to hold Delayloop	
		d2				;dto.
		d3				;dto
		pulselength		;current pulse length
		pl_next			;pulselength Target (next Pulselength)
		pulse_wait		;wait-Parameer from current pgm
		curr_pgm			;current program
		pgm_pos			;relative mem-position of pgm pointer
    endc

; ***********Constants definitions
maxp			equ	0xBf		;maximum pulse length (position at which switch is reached)
minp			equ	0x1D		;minimum pulse length (bottom position)
max_pgm		equ	0x0f		;pgm-count (see pgmtable at 0x200)

#define PWR_SERVO	GPIO,4	;output to power servo on/off
#define SWITCH	GPIO,5	;on/off Switch
#define PULSE		GPIO,2	;Servo Pulse output


; **********************************************************************
; **********************************************************************
    ORG		0x000		; processor reset vector
    goto		init			; go to beginning of program

    ORG		0x004		; interrupt vector location					
	movwf	W_TEMP
	swapf	STATUS,w
	bcf		STATUS,RP0
	movwf	STATUS_TEMP

	movf		d1,w			;save counter registers
	movwf	d1_temp
	movf		d2,w
	movwf	d2_temp
	
	bsf		PULSE		;servo pulso high
	call		delay1ms		;1st part of servo pulse
	movf		pulselength,w	;load current pulse width
	movwf	d1
	nop					;pulse delay loop part2 - 4 cycles per count
	decfsz	d1,f			; so 0xff --> ca. 1ms
	goto		$-2
	bcf		PULSE
	
	movf		d1_temp,w	;restore counter registers
	movwf	d1
	movf		d2_temp,w
	movwf	d2
	bcf		INTCON,T0IF	;clear timer0 interrupt Flag
	
	swapf	STATUS_TEMP,w
	movwf	STATUS
	swapf	W_TEMP,f
	swapf	W_TEMP,w
	
	retfie
; ------------------------------------------------------------------------------
; START OF CODE to initialize the processor (Variables, I/O ...)
; ------------------------------------------------------------------------------
init
    bcf		STATUS,RP0		; Select Bank 0

    ;initialise variables
    clrf		GPIO			; Init I/O-Port by clearing the outp. latches.
	movlw	0x07
	movwf	CMCON		;Digital IO

    bsf		STATUS, RP0		;Switch to BANK 1
	clrf		ANSEL			;digital IO
    movlw	b'00101000'		;configure in/output (GP5, GP3: input. others output)
    movwf	TRISIO			;
	movlw	b'00100000'		;weak pullup on GP5 (on/off Switch)
    movwf	WPU				;weak pullup
    movlw	b'00100000'		;interrupt on change for GP5
    movwf	IOC
	movlw	b'00000101'		;configure Timer0: Prescaler 64--> Overflow every 16ms
	movwf	OPTION_REG

	;calibrating the oscillator...
    call		3FFh 			;Get the cal value
    movwf	OSCCAL 			;Calibrate it

    bcf		STATUS, RP0		;Back to BANK 0
    bsf		INTCON,GIE		;global interrupts
	bsf		INTCON,T0IE		;Timer0 Interrups
    bcf		INTCON,GPIE		;Interrupt on Change disabled
    bcf		INTCON,GPIF		;clear interrupt flag
	; init conditions
    bcf		PWR_SERVO			;servo off
	bcf		PULSE			;pulse off
	clrf		curr_pgm			;current program =0
	movlw	minp				;pulselength (minimum)
	movwf	pulselength
        
; ******************************************************************************
; MAIN, here we go...
; 
; ******************************************************************************
startup
    bsf		PWR_SERVO		;servo on
	call		delay1s		;wait for power to stabilize

mainloop
	movf		curr_pgm,w	;load current program
	movwf	pgm_pos
	bcf		STATUS,C		;clear carry
	rlf		pgm_pos,f	; x 16 (16 Locations per pgm)
	rlf		pgm_pos,f
	rlf		pgm_pos,f
	rlf		pgm_pos,f
	
handle_pgm
	movlw	high pgmtable	;prepare computed goto
	movwf	PCLATH
	movf		pgm_pos,w	;get curr. pgm position
	call		pgmtable		;get pgm data
	andlw	0xff			;check if 0
	btfsc	STATUS,Z		;if 0 then this is the last entry of this pgm.
	goto		return_arm	; return arm in sleep position
	movwf	pl_next		;otherwise save next pulseloop value
	incf		pgm_pos,f	;next pgm position
	
	movlw	high pgmtable	;prepare computed goto
	movwf	PCLATH
	movf		pgm_pos,w	;get curr. pgm position
	call		pgmtable		;get time/delay
	movwf	pulse_wait	;save
	incf		pgm_pos,f	;next pgm position

	movf		pl_next,w	;check if pl_next is 255
	xorlw	0xff
	btfsc	STATUS,Z
	goto		delay_only	;if 255 (Z=0) then wait only
	
pulse_loop
	movf		pulse_wait,w	;load delay
	movwf	d3
	incf		pulselength,f	;
	btfsc	SWITCH		;test if Switch is off for some reason (switch=set=high)
	goto		return_arm	;emergency return...
	call		delay1ms		;run delay loop
	decfsz	d3,f
	goto		$-2
	movf		pulselength,w
	xorwf	pl_next,w
	btfss	STATUS,Z		;if equal, then ready
	goto		pulse_loop	;next iteration otherwise
	goto		handle_pgm	;next pgm position
	
delay_only				;enter here to just wait (no pulse change)
	call		delay100ms	;100ms delay per step
	btfsc	SWITCH		;test if Switch is off for some reason (switch=set=high)
	goto		return_arm	;emergency return...
	decfsz	pulse_wait,f	;process register, we dont need it later
	goto		delay_only
	goto		handle_pgm

;code to return arm in sleep position
return_arm
	call		delay100ms	;wait for Servo
	call		delay100ms	;wait for Servo
	call		delay100ms	;wait for Servo
	
	;check if switch is really off (wait for servo)
	btfss	SWITCH				;wait for switch to go off
    goto	$-1
	
	incf		curr_pgm,f	;switch to next pgm
	movf		curr_pgm,w	;check if last one
	xorlw	max_pgm
	btfsc	STATUS,Z
	clrf		curr_pgm		;yes, then start over at 0
	
pulse_loop_2
	movlw	0x02			;load delay
	movwf	d3
	decf		pulselength,f	;
	call		delay1ms		;run delay loop
	decfsz	d3,f
	goto		$-2
	movf		pulselength,w
	xorlw	minp
	btfss	STATUS,Z
	goto		pulse_loop_2	;next iteration otherwise
	call		delay100ms	;wait for Servo
	call		delay100ms	;wait for Servo
	call		delay100ms	;wait for Servo
	call		delay100ms	;wait for Servo

	;check if switch is really off (user could have switched it on again)
	btfss	SWITCH		;wait for switch to go off
    goto		mainloop		;else start over, no sleep required
	
	bcf		PWR_SERVO		;disable servo power
    
    goto		do_sleep		;now sleep
;-----------------------------------------------------------------
; Do Sleep
; Sets PIC to sleep 
;-----------------------------------------------------------------
do_sleep
	bcf		INTCON,GIE	;global interrupts disabled
	bsf		INTCON,GPIE	;Interrupt on Change enabled
    bcf		INTCON,GPIF	;clear INT Flag
    movf		GPIO,w		;read GPIO to disable mismatch condition
    btfsc	INTCON,GPIF	;really clear?
    goto	$-3
	
    sleep				;now goto sleep
						;we sleep until SWITCH is changed (GP5)
    nop
    movf		GPIO,w		;read GPIO to disable mismatch condition
    bcf		INTCON,GPIF	;clear INT Flag
	bcf		INTCON,GPIE	;Interrupt on Change disabled
	bsf		INTCON,GIE	;global interrupts enabled

    goto		startup		;and go on
; ------------------------------------------------------------------------------
; delay: several delay routines used in the program
; ------------------------------------------------------------------------------
delay1s
			;999990 cycles
    movlw	0x07
    movwf	d1
    movlw	0x2F
    movwf	d2
    movlw	0x03
    movwf	d3
delay1s_0
    decfsz	d1, f
    goto	$+2
    decfsz	d2, f
    goto	$+2
    decfsz	d3, f
    goto	delay1s_0
			;6 cycles
    goto	$+1
    goto	$+1
    goto	$+1
			;4 cycles (including call)
    return
;----
delay100ms
			;99993 cycles
    movlw	0x1E
    movwf	d1
    movlw	0x4F
    movwf	d2
delay_1
    decfsz	d1, f
    goto	$+2
    decfsz	d2, f
    goto	delay_1
			;3 cycles
    goto	$+1
    nop
			;4 cycles (including call)
    return
;----
delay1ms
				;998 cycles
    movlw	0xC7
    movwf	d1
    movlw	0x01
    movwf	d2
delay_0
    decfsz	d1, f
    goto	$+2
    decfsz	d2, f
    goto		delay_0

			;2 cycles
    goto		$+1
    return
; *****************************************************************
	
	org 0x0200			;pgm data definition - enter pgm count in the definitions "max_pgm"
;Usage:
;one program loop is 7 steps maximum, consisting of 8 2-byte-pairs of data (so each pgm uses 16 Bytes)
;each pgm-step is one pair of data: "next servo position"; "delay".
;next servo position is 0...254 and has to be in ascending order and <maxp
;delay is 1ms per count, so bigger delay is slower servo positioning
;if "next servo position" is 255 (0xff), then no new posion is achieved but a delay is implemented
;in this case each step of "delay" is 100ms
;if "next servo position" is 0 (0x00), then the program is finished, so you don't have to use all 7 steps.
;a maximum of 16 Program definitions is allowed. please adapt max_pgm (see definitions) accordingly
	
pgmtable
	addwf	PCL,f
	dt 0x60,0x08,0xff,0x05,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0x80,0x05,0xff,0x0A,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0x40,0x04,0xff,0x05,0x90,0x02,0xff,0x0A,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0x60,0x08,0xff,0x05,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0xff,0x14,0x90,0x05,0xff,0x0A,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0x90,0x05,0xff,0x20,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0xff,0x20,0x30,0x04,0xff,0x20,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0xff,0x30,maxp,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt maxp,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
	dt 0xff,0x14,0x90,0x05,0xff,0x0A,maxp,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
    END					;'end of program''



