; Copyright 2003 Richard Cappels, projects@cappels.org
 ;*********************************************************************
 ;* AT90S2313-10 2313 waveform capture & send to terminal
 ;* file: wfcao030326.asm
 ;* More information/updates may be posted at http://www.projects.cappels.org
 ;* Feedback welcome at " projects at cappels.org" (Please replace "at" with "@")
 ;*********************************************************************
 ;This is firmware for a minimum mass waveform capture system
 ; based on the Atmel AT90S2313-10 microcontroller. 
 ;
 ;
 ;
 ; HOW TO USE THIS FIRMWARE;
 ; 
 ; The controller needs to operated from a 10 MHz clock - the
 ; on-chip crystal oscillator was used in the prototype
 ; applications. Inverting EIA-232 buffers, such as the Maxim
 ; MAX232 are expected on pin 3 (TXD) and pin 2 (RXD). To obtain
 ; an input signal range of 0 to 1 volt D.C., connect one end a
 ; 330k resistor to pin 15, the PWM output, and connect the
 ; other end of the 330k resistor to pin 13 (the comparator
 ; inverting input), an 82k resistor to ground, and a .047 uf
 ; capacitor to ground. Connect the signal to be captured to
 ; pin 12 (the comparator's noninverting input) and the trigger
 ; signal to pin 11 (PD6). Remember to supply +5 volts and a
 ; decoupling capacitor to pin 20 and ground pin 10. Transient
 ; and over voltage protection on the input pins is
 ; recommended. Connect an EIA-232 ASCII terminal to the
 ; EIA-232 lines, switch on the power, and you are ready to
 ; capture waveforms. The terminal should be set to 9600 baud
 ; no parity, and either one or two stop bits are ok.
 ; 
 ; Upon application of power, the chip will send a greeting
 ; message that looks very much like the one below:
 ; ----------------------------
 ; *
 ; Waveform capture 03.03.26   Dick Cappels, projects@cappels.org
 ;
 ; C..Capture & dump 
 ; I..Increase sample time 
 ; D..Decarese sample time 
 ; T..Triggered mode 
 ; F..Free run mode
 ; P..Trig. Polarity toggle
 ; V..Voltage res
 ; L..Settle time toggle 
 ; E..Toggle line feed 
 ; M..Save settings
 ; R..Recall saved settings
 ; !..Default settings 
 ; S..Status 
 ; ?..Menu & status
 ;
 ; 5 us 255 steps  F + Stl=5 Linefeed  = 1:
 ; ----------------------------
 ; Here is a line-by-line breakdown of the contents of the
 ; screen.
 ; 
 ; * 
 ; The asterisk, if present, indicates that previously stored
 ; operating parameters: sampling rate, trigger polarity,
 ; voltage resolution (6,7, or 8 bits), and PWM DAC settling
 ; time have been retrieved from the chips EEPROM and used to
 ; preset the system to the state that was last saved. A
 ; checksum of relevant EEPROM data is compared with the stored
 ; checksum before writing the operating parameters.
 ; 
 ; Waveform capture 03.03.26   Dick Cappels, projects@cappels.org
 ; projects@cappels.org
 ; A heading will be typed. The date and version of the
 ; firmware is contained in the header, as well as the URL of
 ; my projects web site. 
 ; 
 ; The next 13 entries are the command menu:
 ; 
 ; C..Capture & dump 
 ; When C is received, the waveform capture cycle will be
 ; started. At the end of the cycle, the contents will be
 ; dumped to the screen in ASCII format. A terminal program
 ; with a text capture feature can capture the tab-delimited
 ; data for display on a spreadsheet. The cycle can be
 ; interrupted before completion by pressing any key after the
 ; cycle has started. If interrupted, the capture cycle will
 ; abort and the contents of the buffer will be dumped to the
 ; terminal.
 ;  
 ; I..Increase sample time 
 ; The letter I increases the sampling time. The possible
 ; values are in a 1-2-5 sequence from 1 microsecond to 10
 ; milliseconds. After the maximum value of 10 milliseconds per
 ; sample is reached, it will not increment further.
 ; 
 ; D..Decarese sample time 
 ; The letter D decreases the sampling time. The possible
 ; values are in a 1-2-5 sequence from 1 microsecond to 10
 ; milliseconds. After the minimum, value of 1 microsecond per
 ; sample is reached, it will not decrement further.
 ; 
 ; T..Triggered mode 
 ; When T is received, the waveform capture cycle is
 ; synchronized with the  trigger signal on pin 11.
 ; 
 ; F..Free run mode
 ; When F is received, the waveform capture cycle free runs
 ; without regard to the trigger signal. This is useful in
 ; checking DC level.
 ; 
 ; 
 ; P..Trig. Polarity toggle
 ; The P key toggles between triggering on the positive and
 ; negative edges of the trigger signal.
 ; 
 ; V..Voltage res
 ; Voltage resolution cycles among the three choices, 64 step,
 ; 128 step, and 255 steps each time the V key is pressed. The
 ; cycling of these values depends on correct values being in
 ; memory. If, for some reason this data becomes corrupted,
 ; control can be regained by pressing ! to restore the
 ; default settings. 
 ; 
 ; L..Settle time toggle 
 ; The L key toggles between 3 milliseconds per sample (1X) and
 ; 15 milliseonds (5X) settling times after each change of the
 ; PWM voltage. 
 ; 
 ; E..Toggle line feed 
 ; Pressing E in response to the command prompt toggles the line
 ; feed turning the data dump on and off. When on (flag = 1), line 
 ; feeds are sent after each data value and tabs are inserted between
 ; the sample number and the data value. When off (flag = 0), linefeeds
 ; are suppressed and spaces are inserted between the sample number and
 ; the data value. Linefeed on works well with most terminal programs
 ; and linefeed off makes the captured text file more compatible with
 ; spreadsheet programs.
 ; 
 ; M..Save settings
 ; Pressing M in response to the command prompt causes the
 ; current operating parameters  (sampling rate, trigger
 ; polarity, voltage resolution (6,7, or 8 bits), and PWM DAC
 ; settling time) to be stored in EEPROM, overwriting any
 ; previously stored information. 
 ; 
 ; R..Recall saved settings
 ; Pressing the R key causes previously stored operating
 ; parameters: sampling rate, trigger polarity, voltage
 ; resolution (6,7, or 8 bits), and PWM DAC settling time have
 ; been retrieved from the chips EEPROM and used to preset the
 ; system to the state that was last saved. A checksum of
 ; relevant EEPROM values from the EEPROM will not be written
 ; to the operating parameters.
 ; 
 ; !..Default settings 
 ; Pressing ! causes the default operating parameters to be set
 ; and the operating parameters to be typed to the screen. The
 ; default parameters are 5 microsecond sampling time, 255 step
 ; voltage resolution, free run (DC measurements only),
 ; positive edge for triggering when triggering is selected,
 ; and settling time of 5 times.
 ; 
 ; S..Status 
 ; Displays the operating parameters (see Menu & status below)
 ; 
 ; ?..Menu & status
 ; When the question mark is pressed, the menu will be
 ; displayed and the operating parameters will be typed. The
 ; operating parameters (status) display is shown below:
 ; 
 ; 10 us  255 steps  T - Stl=5 :
 ; 
 ; 10 us means the sampling period is 10 microseconds
 ; 255 steps means the voltage resolution is 8 bit (255 steps)
 ; T means waveform capture is synchronized with the trigger
 ; signal
 ;      (if F it would mean free run, not synchronized)
 ; - means that it will trigger on the negative edge of the
 ; trigger signal
 ;      (if + it would mean triggering will be on positive
 ; edge)
 ; stl=5 means that the steeling time is 5X (if it said stl=1
 ; then settling
 ;      time would be 1X)
 ; 
 ; A header is sent at the start of each data dump.
 ; Waveform capture 03.03.26  Dick Cappels, projects@cappels.org
 ; 1 us,  MINIMUM 255 steps  T - Stl=5    Meas ID=1.52
 ; 
 ; The header includes the version of the firmware, the operating parameters,
 ; and a measurement ID which is kept in EEPROM and incremented every time
 ; a waveform capture cycle is initiated. The digit on the left of the decimal point
 ; is the number of times the digit to the right overflowed beyond 255. The sample
 ; header above shows that this was this chip's 308th capture cycle.
 ; 
 ; All characters sent to the terminal are deliberately delayed
 ; so as not to
 ; overrun input buffers on low performance computers when
 ; dumping data.
 ;
 ;Good luck in your experimentation. See my web site for 
 ;contact information http://www.projects.cappels.org
 ;
 ;
 ; Start of Minimum Mass Waveform Capture demonstration program
 
 .include "2313def.inc"   
 
 
 
 ;The main routines are:
 ;	scanspan -This is routine that organizes the collection of the waveform.
 ;	capture - Does the actual waveform capture.
 ; 	MainLoop - Interprets control panel commands and returns status.
 ;	dumparray - Formats and dumps data to terminal.
 
 
 	;UART baud rate calculation
 .equ	clock = 10000000	;clock frequency
 .equ	baudrate = 9600		;choose a baudrate
 .equ	baudconstant = (clock/(16*baudrate))-1
 
 	;Assign space for array of sample values in RAM
 .equ	arraystart	= $60	;First byte of data array
 .equ	arrayend	= $C3	;Last byte of data array
 
 .equ	defaultflags	= $0F
 
 	;Assign working registers
 	
 .def	delayselect	= r02	;index into list of sampling delays
 .def	dx1		= r03	;delay counter
 .def	dx2		= r04	;delay counter
 .def	dx3		= r05	;delay counter
 
 
 .def	temp 		= r16	;general purpose variable
 .def	innerrloopc 	= r17	;delay counter, innermost loop
 .def	outerloopc 	= r18	;delay counter, outer loop
 .def	flagreg		= r19	;Flags for control. See definitions below
 .def	pwmval		= r21	;PWM value
 .def	H		= r22	;Used to send decimal ascii
 .def	T		= r23	;Used to send decimal ascii
 .def	U		= r24	;Used to send decimal ascii
 .def	pwmstartval	= r25	;Intial value for voltage measurement. 
 
 ;Assignment of flagreg bits
 ;bit 0	if set, linefeeds are sent during dump to screen and spaces ($20) instead of a tab char.
 ;bit 1	if set, selects for settling time to be 5X, if clear, 1X.
 ;bit 2	if set, selects for triggering on positive edge of trigger signal
 ;bit 3	if set, selects for free run (non-triggered operation)
 ;bit 4	
 ;bit 5
 ;bit 6
 ;bit 7
 
 
 ;Assign EEPROM locations	;Lowest bytes avoided to reduce chances of corruption.
 .equ	checksumE	= $20
 .equ	pwmstartvalE	= $21
 .equ	flagregE	= $22
 .equ	delayselectE	= $23
 .equ	serialhighE	= $30
 .equ	seriallowE	= $31
 
 
 .org	$000	
 init:     
 
 	ldi     temp,low(ramend)
 	out     spl,temp     		;Set spl.
 	ldi     temp,baudconstant     
 	out     ubrr,temp     		;Load baudrate.
 	sbi	ucr,rxen		;Enable WAUT receive
 	sbi	ucr,txen		;Enable UART Transmit
 	ldi	pwmval,$FF
 	rcall	pwm8setup		;Start up PWM DAC.
 	ldi	temp,$02		;Default scan rate is 10 us per sample
 	mov	delayselect,temp
 	ldi	flagreg,defaultflags	;Set flag to free run, trigger set to positive edge,
 					;and settling time of 5X (5 ms filter)
 	ldi	pwmstartval,255		;Initialize voltage resolution	
 	rcall	readsettings									
 	rcall	menu			;Send Hello message
 	rjmp	MainLoop		;Main sampling control and I/O loop.
 
 
 pwm8setup:				;Set up AT90S2313 Timer 1 as 8 bit PWM.
 	ldi	temp,$81		
 	out	TCCR1A,temp		;Preload with $FF
 	ldi	temp,$01
 	out	TCCR1B,temp
 	ldi	temp,$00
 	out	OCR1AH,temp
 	ldi	temp,$FF	
 	out	OCR1AL,temp		;Write PWM data 
 	sbi	DDRB,3			;Set PWM output pin as output
 	ret				;To update PWM value, write a byte to OCR1AL
 
 
 
 assignsampledelay:	;Set pointer to sample delay entry point		
 	ldi	ZH,high(2*delaylist)	;Load high part of byte address into ZH
 	ldi	ZL,low(2*delaylist)	;Load low part of byte address into ZL
 	mov	temp,delayselect
 	lsl	temp
 	add	ZL,temp
 	brcc	nca			;If carry results, take care of it.
 	inc	ZH		
 nca:
 	lpm				;Get address from pointer (r0) into ZH.ZL
 	push	r0
 	adiw	ZL,1
 	lpm	
 	mov	ZH,r0
 	pop	ZL
 	ret
 
 
 
 delaylist:		;Pointer table to entrances to generic delay routine. Used to select 
 			;time scale for waveform sampling. The delay is delay time between
 			;individual samples.
 	.dw	delay1us
 	.dw	delay2us
 	.dw	delay5us
 	.dw	delay10us
 	.dw	delay20us
 	.dw	delay50us
 	.dw	delay100us
 	.dw	delay200us
 	.dw	delay500us
 	.dw	delay1000us
 	.dw	delay2000us
 	.dw	delay5000us
 	.dw	delay10000us
 
 delay2us:	;Total delay for 2.00 us (based on 10 mhz clock).
 	nop
 	nop
 	ldi	innerrloopc,$02
 l2:	dec	innerrloopc
 	brne	l2	
 	rjmp	delay1us
 
 
 delay5us:	;Total sample delay for 5.00 us (based on 10 mhz clock), 
 	nop	;Before terminal priority accomodated two noops removed
 	ldi	innerrloopc,$01
 	ldi	outerloopc,$07	
 	rjmp	genericdelay
 
 delay10us:	;Total sample delay for 10.0 us (based on 10 mhz clock). 
 	nop	;For terminal priority accomodatioon 6 nops added
 	nop
 	nop
 	nop
 	nop
 	nop
 	nop
 	ldi	innerrloopc,$01 ;Before terminal priority accomodated was $01
 	ldi	outerloopc,$12 	;Before terminal priority accomodated was $12
 	rjmp	genericdelay
 
 delay20us:	;Total sample delay for 20.0 us (based on 10 mhz clock). 
 	nop	;Before terminal priority accomodated was just one nop
 	nop
 	nop
 	ldi	innerrloopc,$01
 	ldi	outerloopc,$2C	;Before terminal priority accomodated was $2D	
 	rjmp	genericdelay
 
 delay50us:	;Total sample delay for 50.00 us (based on 10 mhz clock). 
 	nop	;Before terminal priority accomodated was just one nop
 	nop
 	nop
 	ldi	innerrloopc,$01
 	ldi	outerloopc,$77	;Before terminal priority accomodated was 78
 	rjmp	genericdelay
 
 
 delay100us:	;delay for 100.1 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$01
 	ldi	outerloopc,$F5	;Before terminal priority accomodated was $F6
 	rjmp	genericdelay
 	
 delay200us:	;delay for 199.4 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$02
 	ldi	outerloopc,$F6	
 	rjmp	genericdelay	
 
 delay500us:	;delay for 500.1 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$05
 	ldi	outerloopc,$F8	
 	rjmp	genericdelay
 	
 	
 delay1000us:	;delay for 1006 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$0C	
 	ldi	outerloopc,$D0	
 	rjmp	genericdelay
 
 delay2000us:	;delay for 2010.4 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$18	
 	ldi	outerloopc,$D0	
 	rjmp	genericdelay
 
 
 delay5000us:	;delay for 5023.6 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$3C	
 	ldi	outerloopc,$D0	
 	rjmp	genericdelay
 
 
 
 delay10000us:	;delay for 10045.6 us (based on 10 mhz clock. Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$78	
 	ldi	outerloopc,$D0	
 	rjmp	genericdelay		;This instruction not really needed but kept for uniformity.
 
 
 		
 genericdelay:	;Actual sampling delay routine as set up by calling routine
 					;Enter with outer loop value in innerrloopc and
 					;outer loop value in outerloopc.
 outergeneric:
 	sbic	usr,rxc			;Check for byte in UART from terminal
 	rjmp	delay1us		;and if char is waiting, shorten loop ; 
 	mov	temp,outerloopc	
 innergeneric:
 	nop
 	dec	temp
 	brne	innergeneric
 	dec	innerrloopc
 	brne	outergeneric
 	nop
 	rjmp	delay1us
 
 
 wait1ms:	;delay for 1.003 ms (based on 10 mhz clock). including call and return). Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$0D
 outerloop1:
 	ldi	outerloopc,0	
 innerloop1:
 	dec	outerloopc
 	brne	innerloop1
 	dec	innerrloopc
 	brne	outerloop1
 	ret
 
 
 wait5ms:	;delay for 5.0122 ms (based on 10 mhz clock). including call and return). Trash innerrloopc and outerloopc.
 	ldi	innerrloopc,$41
 outerloop3:
 	ldi	outerloopc,0	
 innerloop3:
 	dec	outerloopc
 	brne	innerloop3
 	dec	innerrloopc
 	brne	outerloop3
 	ret
 	
 
 wait1000ms:	;delay for 1000ms (based on 10 mhz clock). including call and return). Trash innerrloopc and outerloopc.
 
 	ldi	temp,$36
 mostouter:
 	ldi	innerrloopc,$0
 outerloop31:
 	ldi	outerloopc,0	
 innerloop31:
 	dec	outerloopc
 	brne	innerloop31
 
 	dec	innerrloopc
 	brne	outerloop31
 	dec	temp
 	brne	mostouter
 ret
 
 
 	
 FillArray:	;Fill array with pwmstartval
 	ldi	YL,arraystart
 	ldi	YH,$00				;Corrected -was ldi YH,$FF
 	mov	temp,pwmstartval
 fillmore:
 	st	Y+,temp
 	cpi	YL,arrayend+1
 	brne	fillmore
 	ret	
 
 
 scanspan:	;Fill array with analog measurements
 	rcall	assignsampledelay	
 	rcall	capture
 	rcall	dumparray
 	rjmp	MainLoop
 	
 							
 capture:				;Enter with ZH,ZL pointing to delay routine		
 					;For 1 microsecond sampling interval, point to oneus
 					;Values of samples are stored in RAM from location
 					;arratystart to arrayend.
 					
 	rcall	FillArray		;Preset array.
 	mov	pwmval,pwmstartval	;PWM = 64
 	ldi	temp,$00
 	out	OCR1AH,temp		;Zero high register
 	mov	temp,pwmval
 	cpi	pwmstartval,255		;If 255 steps, don't shift left
 	breq	ds1	
 	lsl	temp
 	cpi	pwmstartval,$128	;If 128 steps, shift left once
 	breq	ds1
 	lsl	temp			;If 64 steps, shift left twice
 ds1:						
 	out	OCR1AL,temp		;Set pwm value
 	rcall	wait5ms
 	sbrs	flagreg,1
 	rjmp	us2
 	rcall	wait5ms
 	rcall	wait5ms
 	rcall	wait5ms
 	rcall	wait5ms
 us2:
 
 decpwm:	dec	pwmval		 	;PWM = PWM 1 1.
 	cpi	pwmval,$FF		;If PWM = -1 then finshed capturing.
 	breq	capturefinsihed		
 	ldi	temp,$00
 	out	OCR1AH,temp		;Zero high register
 	mov	temp,pwmval
 	cpi	pwmstartval,255	;If 255 steps, don't shift left
 	breq	ds2	
 	lsl	temp
 	cpi	pwmstartval,128	;If 128 steps, shift left once
 	breq	ds2
 	lsl	temp			;If 64 steps, shift left twice	
 ds2:
  	out	OCR1AL,temp		;Set pwm value
 	rcall	wait1ms			;Wait for small step settling time
 	sbrs	flagreg,1
 	rjmp	us1
 	rcall	wait1ms
 	rcall	wait1ms
 	rcall	wait1ms
 	rcall	wait1ms
 us1:	
 	ldi	YH,$00			;Y = array start.
 	ldi	YL,arraystart-1		
 	rcall	waitingfortrigger 
 
 nextydelay:				;Includes NOPs come here if pwmval is not to be stored on this cycle
 	inc	YL			;Inc YL and see if array boundary has been exceeded.
 	cpi	YL,arrayend+1
 	breq	decpwm			
 	nop				;Equalize time between samples for pwmval saved and not saved
 	nop
 
 nexty:					
 	ijmp				;Jump to delay routine - address already loaded into Z reigster
 delay1us:
 	sbic	ACSR,$05		;CAPTURE COMPARITOR OUTPUT STATE
 	rjmp	nextydelay		
 	st	Y+,pwmval
 	cpi 	YL,arrayend+1
 	breq	decpwm
 	rjmp	nexty		
 	
 capturefinsihed:
 	ret
 
 
 
 waitingfortrigger:
 	sbrc	flagreg,3
 	rjmp	notriggerneeded
 	sbrc	flagreg,2
 	rjmp	waitingforpostrigger	
 
 
 waitingfornegtrigger:
 waitforhigh1:
 	sbic	usr,rxc			;Check for byte in UART from terminal
 	rjmp	quittrigger 		; and return if char waiting (no trigger)
 	sbis	pind,$06		
 	rjmp	waitforhigh1
 waitforlow1:
 	sbic	usr,rxc			;Check for byte in UART from terminal
 	rjmp	quittrigger 		; and return if char waiting (no trigger)
 	sbic	pind,$06
 	rjmp	waitforlow1
 	ret
 
 
 waitingforpostrigger:
 waitforlow:
 	sbic	usr,rxc			;Check for byte in UART from terminal
 	rjmp	quittrigger 		; and return if char waiting (no trigger)
 	sbic	pind,$06
 	rjmp	waitforlow
 waitforhigh:
 	sbic	usr,rxc			;Check for byte in UART from terminal
 	rjmp	quittrigger 		; and return if char waiting (no trigger) 
 	sbis	pind,$06
 	rjmp	waitforhigh
 	ret
 
 
 notriggerneeded:	;Come here when in free run
 	ret
 
 quittrigger:		;Quit trigger routine and  data collection
 	ldi	YL,arrayend		;Set YL to look like all points tried
 	ret
 
 
 
 MainLoop:
 	ldi	temp,':'		;Emit prompt
 	rcall	emitchar
 mlw:	
 	sbis	usr,rxc			;Check for byte in UART from terminal
 	rjmp	mlw
 
  					;Get and handle incoming byte from terminal.
 					;Results in rage 0..$D  use as index to select sampling 
 					;delay and results above $D interpret as command.
 	in	temp,udr		;Read byte
 	rcall	emitchar		;Echo byte
 	andi 	temp,$5F		;Upper-case character
 	cpi	temp,'C'		;Is it an "C"?
 	brne	nc2
 	rjmp	scanspan		;If C capture waveform
 nc2:
 	cpi	temp,'I'		;Is it an "I"?
 	brne	nc3
 	rjmp	incrementds		;If I Increment delayselect
 nc3:
 	cpi	temp,'D'		;Is it an "D"?
 	brne	nc4
 	rjmp	decrementds		;If D decrement delayselect
 nc4:
 	cpi	temp,'P'		;Is it an "P"?
 	brne	nc5
 	rjmp	toggletrigger		;If P toggle trigger polarity
 nc5:	
 	cpi	temp,'F'		;Is it an "F"?
 	brne	nc6
 	rjmp	setfreerun		;If F set to free run
 nc6:
 	cpi	temp,'V'		;Is it an "V"?
 	brne	nc7
 	rjmp	changeres		;If V change voltage resolution
 nc7:
 	cpi	temp,'T'		;Is it an "T"?
 	brne	nc8
 	rjmp	settriggered		;If T set trigger/free run flag to triggered
 nc8:
 	cpi	temp,'S'		;Is it an "S"?
 	brne	nc9
 	rcall	status			;If S display status
 	rjmp	MainLoop
 nc9:
 	cpi	temp,'L'		
 	brne	nc10	
 	rjmp	togglesettle		;If L toggle settling time between 1X and 5X
 nc10:
 	cpi	temp,$1F		;If ? show menu and status
 	brne	nc11
 	rcall	menu
 	rjmp	MainLoop
 nc11:
 	cpi	temp,'M'		;If M save settings
 	brne	nc12
 	rjmp	savesettings
 nc12:
 	cpi	temp,$01		;If ! restore default settings
 	brne	nc13
 	rjmp	restoredefault
 nc13:
 	cpi	temp,'R'		;If R recall stored settings
 	brne	nc14
 	rcall	readsettings
 	rcall	status
 	rjmp	MainLoop
 nc14:
 
 	cpi	temp,'E'		
 	brne	nc15	
 	rjmp	togglelinefeed		;If E toggle linefeed during dump on/off
 nc15:
 	rcall	crlf
 	rjmp	MainLoop
 
 
 decrementds:
 	rcall	crlf
 	mov	temp,delayselect
 	cpi	temp,$00
 	brne	ns6
 	rjmp	ns7
 ns6:
 	dec	delayselect
 ns7:
 	ldi     ZH,high(2*timebasemessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*timebasemessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rcall	sendtimebasestring
 	rjmp	MainLoop
 	
 
 
 incrementds:
 	rcall	crlf
 	mov	temp,delayselect
 	cpi	temp,$0C
 	brne	ns4
 	rjmp	ns5
 ns4:
 	inc	delayselect
 ns5:
 	ldi     ZH,high(2*timebasemessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*timebasemessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rcall	sendtimebasestring
 	rjmp	MainLoop
 
 
 togglesettle:	;Toggle settling time between 1X and 5X
 	rcall	crlf
 	sbrs	flagreg,1
 	rjmp	nsx8
 	andi	flagreg,$FD
 	rjmp	nsx9
 nsx8:	
 	ori	flagreg,$02
 nsx9:	ldi     ZH,high(2*settletimemessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*settletimemessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	ldi	temp,'5'
 	sbrc	flagreg,1
 	rjmp	notfive
 	ldi	temp,'1'
 notfive:
 	rcall	emitchar
 	ldi	temp,$20
 	rcall	emitchar
 	rjmp	MainLoop
 
 
 toggletrigger:	;Topggle trigger polarity 
 	rcall	crlf
 	sbrs	flagreg,2
 	rjmp	ns8
 	andi	flagreg,$FB
 	rjmp	ns9
 ns8:	
 	ori	flagreg,$04
 ns9:	ldi     ZH,high(2*triggermessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*triggermessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	ldi	temp,'+'
 	sbrc	flagreg,2
 	rjmp	notminus
 	ldi	temp,'-'
 notminus:
 	rcall	emitchar
 	ldi	temp,$20
 	rcall	emitchar
 	rjmp	MainLoop
 
 
 
 togglelinefeed:	;Topggle linefeed character during dump on/off
 	rcall	crlf
 	sbrs	flagreg,0
 	rjmp	nis8
 	andi	flagreg,$FE
 	rjmp	nis9
 nis8:	
 	ori	flagreg,$01
 nis9:	ldi     ZH,high(2*lfmessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*lfmessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	ldi	temp,'1'
 	sbrc	flagreg,0
 	rjmp	notminusi
 	ldi	temp,'0'
 notminusi:
 	rcall	emitchar
 	ldi	temp,$20
 	rcall	emitchar
 	rjmp	MainLoop
 
 
 setfreerun:	;Set to Free Run
 	rcall	crlf
 	ori	flagreg,$08
 	ldi     ZH,high(2*freerunmessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*freerunmessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rjmp	MainLoop
 
 
 settriggered:	;Set to Free Run
 	rcall	crlf
 	andi	flagreg,$F7
 	ldi     ZH,high(2*triggeredmessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*triggeredmessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rjmp	MainLoop
 
 	
 emitchar:	;Send character with two stop bits + extended delay
 	push	temp
 	sbis	usr,udre  		;wait until the register is cleared
 	rjmp	emitchar     
 	cbi	ucr,0			;Set 9th bit to zero (second stop bit)
 	sbi	ucr,2			;Set to send 9 data bits
 	out	udr,temp		;send the character
 	ldi	temp,$18		;TIME DELAY ROUTINE STARTS HERE
 	mov	dx2,temp
 ld2a:
 	clr	dx1			;   (To reduce receiver buffer overrun)
 ld1a:
 	dec	dx1
 	brne	ld1a
 	dec	dx2
 	brne	ld2a			;TIME DELAY ROUTINE ENDS HERE
 	pop	temp
 	ret
 	
 
 sendstring:				; Call with location of string in Z
 	push	temp			; Preserve temp in case its needed
 morestring:
 	lpm 				; Load byte from program memory into r0
 	tst	r0               	; Check if we've reached the end of the message
 	breq	finishsendstering     	; If so, return
 	mov	temp,r0
 	rcall	emitchar
 	adiw	ZL,1               	; Increment Z registers
 	rjmp	morestring
 finishsendstering:
 	pop	temp
 	ret
 
 
 sendnumber:	;Enter with value to be sent in temp, sends as three digit ascii 
 		;via serial port.
 	mov	U,temp			;Enter with 8 bit value in U, Exits with numerals in H,T,U
 	clr	H			;(Hundreds, Tens, and Units).
 	clr	T
 anotherh:
 	subi 	U,100			;Find out how many hundreds in U.
 	brcs	hdone
 	inc	H
 	rjmp	anotherh
 hdone:
 	subi	U,-100			;Subtracted one too many -add back.
 anothert:
 	subi	U,10
 	brcs	tdone
 	inc	T
 	rjmp	anotherT
 tdone:
 	subi	U,-10		
 	cpi	H,0		
 	breq	dontsendh
 	subi	H,-48
 	mov	temp,H
 	rcall	emitchar
 	cpi	t,0
 	brne	dontsendh
 	ldi	temp,$30		;If U=0 then don't emit this zero
 	rcall	emitchar
 dontsendh:	
 	cpi	t,0
 	breq	dontsendt
 	subi	T,-48
 	mov	temp,T
 	rcall	emitchar
 dontsendt:
 	subi	U,-48	
 	mov	temp,U
 	rcall	emitchar
 	ret
 
 
 dumparray:	;Dump contents of array: Y location, comma, data value.
 
 	ldi     ZH,high(2*idmessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*idmessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rcall	status			;Dump status
 	ldi	temp,$20		;Send a space
 	rcall	emitchar	
 	ldi	ZL,seriallowE		;Get sequence number from EEPROM int Y register
 	rcall	LoadEEByte
 	mov	YL,temp			
 	ldi	ZL,serialhighE
 	rcall	LoadEEByte
 	mov	YH,temp
 	adiw	YL,1			;Increment sequence number
 	mov	temp,YH			;Save sequence number in EEPROM
 	rcall	SaveEEByte
 	ldi	ZL,seriallowE
 	mov	temp,YL
 	rcall	SaveEEByte
 	ldi     ZH,high(2*sequencemessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*sequencemessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	mov	temp,YH			;Print new sequence number to screen
 	rcall	sendnumber
 	ldi	temp,'.'
 	rcall	emitchar
 	mov	temp,YL
 	rcall	sendnumber
 	ldi	pwmval,1		;Sample number for output.
 	ldi	YL,arraystart		;Pointer to data.
 	ldi	YH,$00
 
 dumpmore1: 
 	ldi	temp,$0D
 	rcall	emitchar
 	ldi	temp,$0A
 	sbrc	flagreg,0
 	rcall	emitchar		;Send line feed if flagreg 0 is set
 	mov	temp,pwmval		;Send sample number.
 	rcall 	sendnumber
 	
 	ldi	temp,$20
 	sbrc	flagreg,0		;If linefeed flag is clear, don't send space
 	rcall	emitchar
 	ldi	temp,$09
 	sbrs	flagreg,0		
     	rcall	emitchar		;If linefeed flag is set, don't send tab
 	ld	temp,Y+
 	rcall	sendnumber
 	inc	pwmval
 	cpi	YL,arrayend+1		;For this to work, array has to be completely within one page
 	brne	dumpmore1
 	rcall	crlf
 	ret
 	
 	
 crlf:			;Send return and linefeed 
 	ldi	temp,$0A		;Send return and line feed for each point
 	rcall	emitchar
 	ldi	temp,$0D
 	rcall	emitchar
 	ret	
 
 changeres:	;Change voltage resolution
 					;Effectively interpret pwmstartval and
 					;change based on its current content.
 					;Allowed values:64, 128, and 255
 	rcall	crlf
 	cpi	pwmstartval,64
 	brne	not64
 	ldi	pwmstartval,128
 	ldi	ZH,high(2*msg128)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*msg128)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rjmp	donechangeres
 not64:
 	cpi	pwmstartval,128
 	brne	not128
 	ldi	pwmstartval,255
 	ldi	ZH,high(2*msg255)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*msg255)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rjmp	donechangeres
 not128:
 
 	cpi	pwmstartval,255
 	brne	not255
 	ldi	pwmstartval,64
 	ldi	ZH,high(2*msg64)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*msg64)	; Load low part of byte address into ZL
 	rcall 	sendstring	
 	rjmp	donechangeres
 not255:
 	
 
 donechangeres:
 	rjmp	MainLoop
 
 msg64:
 	.db	"64 steps"
 	.db	00,00
 
 msg128:
 	.db	"128 steps "
 	.db	00,00
 
 msg255:
 	.db	"255 steps "
 	.db	00,00
 
 
 showdelaylist:	;Pointer into strings 
 	.dw	showdelay1us
 	.dw	showdelay2us
 	.dw	showdelay5us
 	.dw	showdelay10us
 	.dw	showdelay20us
 	.dw	showdelay50us
 	.dw	showdelay100us
 	.dw	showdelay200us
 	.dw	showdelay500us
 	.dw	showdelay1000us
 	.dw	showdelay2000us
 	.dw	showdelay5000us
 	.dw	showdelay10000us
 
 showdelay1us:
 	.db	"1 us,  MINIMUM"
 	.db	00,00
 
 
 showdelay2us:
 	.db	"2 us"
 	.db	00,00
 
 showdelay5us:
 	.db	"5 us"
 	.db	00,00
 
 showdelay10us:
 	.db	"10 us "
 	.db	00,00
 
 showdelay20us:
 	.db	"20 us "
 	.db	00,00
 
 showdelay50us:
 	.db	"50 us "
 	.db	00,00
 
 showdelay100us:
 	.db	"100 us"
 	.db	00,00
 
 showdelay200us:
 	.db	"200 us"
 	.db	00,00
 
 showdelay500us:
 	.db	"500 us"
 	.db	00,00
 
 showdelay1000us:
 	.db	"1 ms"
 	.db	00,00
 
 showdelay2000us:
 	.db	"2 ms"
 	.db	00,00
 
 
 showdelay5000us:
 	.db	"5 ms"
 	.db	00,00
 
 
 showdelay10000us:
 	.db	"10 ms,  MAXIMUM "
 	.db	00,00
 
 
 idmessage:
 	.db	$0A,$0D
 	.db     "Waveform capture 03.03.26   Dick Cappels, projects@cappels.org"
 	.db	$00,$00
 
 hellomessage:
 	.db	$0A,$0D
 	.db	$0A,$0D
 	.db	"C..Capture & dump "
 	.db	$0A,$0D
 	.db	"I..Increase sample time "
 	.db	$0A,$0D
 	.db	"D..Decarese sample time "
 	.db	$0A,$0D
 	.db	"T..Triggered mode "
 	.db	$0A,$0D
 	.db	"F..Free run mode"
 	.db	$0A,$0D
 	.db	"P..Trig. Polarity toggle"
 	.db	$0A,$0D
 	.db	"V..Voltage res"
 	.db	$0A,$0D
 	.db	"L..Settle time toggle " 
 	.db	$0A,$0D
 	.db	"E..Toggle line feed "
 	.db	$0A,$0D
 	.db	"M..Save settings"
 	.db	$0A,$0D
 	.db	"R..Recall saved settings"
 	.db	$0A,$0D
 	.db	"!..Default settings "
 	.db	$0A,$0D
 	.db	"S..Status "
 	.db	$0A,$0D
 	.db	"?..Menu & status"
 	.db	$0A,$0D
 	.db      00,00
 
 
 lfmessage:
 	.db     "Linefeed  = "
 	.db      00,00
 
 
 timebasemessage:
 	.db     "Timebase  = "
 	.db      00,00
 
 		
 triggermessage:
 	.db     "Trigger polarity  = "
 	.db      00,00
 
 triggeredmessage:
 	.db     "Triggerred"
 	.db      00,00
 
 freerunmessage:
 	.db     "Free Run"
 	.db      00,00
 
 
 settletimemessage:
 	.db     "Settling time = "
 	.db      00,00
 
 shsetmsg:
 	.db     "Stl="
 	.db      00,00
 
 sequencemessage:
 	.db     "  Meas ID="
 	.db      00,00
 
 sendtimebasestring:	
 	push	temp			; Preserve temp in case its needed
 
 	ldi	ZH,high(2*showdelaylist)	;Load high part of byte address into ZH
 	ldi	ZL,low(2*showdelaylist)	;Load low part of byte address into ZL
 	mov	temp,delayselect
 	lsl	temp
 	add	ZL,temp
 	brcc	nca1			;If carry results, take care of it.
 	inc	ZH		
 nca1:
 	lpm				;Get address from pointer (r0) into ZH.ZL
 	push	r0
 	adiw	ZL,1		
 	lpm	
 	mov	ZH,r0
 	pop	ZL			;Now, ZL should be pointing to the string
 	lsl	ZL			;Move left one position to point to word
 	rol	ZH
 	
 moestring:
 	lpm 				; Load byte from program memory into r0
 	tst	r0               	; Check if we've reached the end of the message
 	breq	finishsendstering1    	; If so, return
 	mov	temp,r0
 	rcall	emitchar
 	adiw	ZL,1               	; Increment Z registers
 	rjmp	moestring
 finishsendstering1:
 	pop	temp
 	ret
 
 menu:		;Show commands and status
 	ldi     ZH,high(2*idmessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*idmessage)	; Load low part of byte address into ZL	
 	rcall 	sendstring
 	ldi     ZH,high(2*hellomessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*hellomessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rcall	status
 	ret
 
 
 status:
 	rcall	crlf
 	rcall	sendtimebasestring
 	ldi	temp,$20
 	rcall	emitchar
 	cpi	pwmstartval,64
 	brne	not641
 	ldi	ZH,high(2*msg64)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*msg64)		; Load low part of byte address into ZL
 	rcall 	sendstring
 	rjmp	donechangeres1
 not641:
 	cpi	pwmstartval,128
 	brne	not1281
 	ldi	ZH,high(2*msg128)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*msg128)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	rjmp	donechangeres1
 not1281:
 	cpi	pwmstartval,255
 	brne	not2551
 	ldi	ZH,high(2*msg255)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*msg255)	; Load low part of byte address into ZL
 	rcall 	sendstring	
 	rjmp	donechangeres1
 not2551:
 donechangeres1:	
 	ldi	temp,$20
 	rcall	emitchar
 	ldi	temp,'F'
 	sbrs	flagreg,3
 	ldi	temp,'T'
 	rcall	emitchar
 	ldi	temp,$20
 	rcall	emitchar
 	ldi	temp,'+'
 	sbrs	flagreg,2
 	ldi	temp,'-'
 	rcall	emitchar
 	ldi	temp,$20
 	rcall	emitchar
 	ldi     ZH,high(2*shsetmsg)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*shsetmsg)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	ldi	temp,'5'
 	sbrs	flagreg,1
 	ldi	temp,'1'
 	rcall	emitchar
 	ldi	temp,$20
 	rcall	emitchar
 					; Status of Linefeed flag
 	ldi     ZH,high(2*lfmessage)	; Load high part of byte address into ZH
 	ldi     ZL,low(2*lfmessage)	; Load low part of byte address into ZL
 	rcall 	sendstring
 	ldi	temp,'1'
 	sbrs	flagreg,0
 	ldi	temp,'0'
 	rcall	emitchar
 	ret
 	
 	
 savesettings:
 	rcall	crlf
 	mov	temp,delayselect	;Store sampling rate
 	ldi	ZL,delayselectE
 	rcall	saveEEByte	
 	mov	temp,pwmstartval
 	ldi	ZL,pwmstartvalE
 	rcall	saveEEByte		;Store pwmstartval (voltage resolution).
 	mov	temp,flagreg		;Store flagreg.
 	ldi	ZL,flagregE
 	rcall	saveEEByte
 	mov	temp,pwmstartval	;Create checksum by subtracting flagreg then samplerate from pwmstartval without carry.
 	sub	temp,flagreg
 	sub	temp,delayselect
 	ldi	ZL,checksumE	
 	rcall	saveEEByte		;Store checksum.
 	ldi	temp, $20
 	rcall	emitchar		;Send "OK" to terminal
 	ldi	temp,'O'
 	rcall	emitchar
 	ldi	temp,'K'
 	rcall	emitchar
 	rjmp	MainLoop
 
 
 
 readsettings:	;Restore machine state if saved and contents of EEPROM are ok.
 
 	ldi	ZL,pwmstartvalE
 	rcall	loadEEByte		;Read pwmstartval (voltage resolution).
 	mov	YL,temp
 
 	ldi	ZL,flagregE
 	rcall	loadEEByte
 	mov	YH,temp			;Read flagreg.
 	
 	ldi	ZL,delayselectE
 	rcall	loadEEByte
 	mov	XH,temp			;Read delay select register ..
 	
 	mov	XL,YL
 	sub	XL,YH			;Data checksum is in XL.
 	sub	XL,XH
 	
 	ldi	ZL,checksumE	
 	rcall	loadEEByte		;Read stored checksum.
 	cp	temp,XL			;If checksum is good,then store otherwise, skip.
 	brne	dontload
 
 	mov	pwmstartval,YL
 	mov	flagreg,YH
 	mov	delayselect,XH
 	
 	ldi	temp,'*'		;Type asterisk to terminal after loading.
 	rcall	emitchar
 dontload:	
 	ret
 
 
 restoredefault:
 	ldi	temp,$02		;Default scan rate is 10 us per sample.
 	mov	delayselect,temp
 	ldi	flagreg,defaultflags	;Set flag to free run, trigger set to positive edge,
 					;and settling time of 5X (5 ms filter)
 	ldi	pwmstartval,255		;Initialize voltage resolution
 	rcall	status
 	rjmp	MainLoop
 
 SaveEEByte:	;Save byte to EEPROM
 					;Enter with data in temp and address in ZL.
 wrat1:	sbic	eecr,eewe		;Wait for EEPROM write to not be busy
 	rjmp	wrat1
 	out	eear,ZL				
 	out	eedr,temp		;Set up the  write data
 	sbi	eecr,eemwe
 	sbi	eecr,eewe		;Trigger the write
 	dec	ZL
 	ret
 
 LoadEEByte:	;Read byte from EEPROM
 					;Enter with address in ZL. Exits with data in temp
 	
 wratz1:	sbic	eecr,eewe		;Wait for EEPROM write to not be busy
 	rjmp	wratz1
 	out	eear,ZL			;move to EEPROM address register
 	sbi	eecr,eere		;Trigger the read
 	in	temp,eedr		;Get the data into temp
 	ret
 
 ; End of Minimum Mass Waveform Capture demonstration program
 ; http://www.projects.cappels.org
 
