;********************************************************
;*							*
;*		PIC-Based DIGITAL SCOPE			*
;*							*
;*     - Records 4 channels x 64K at 357 KHz		*
;*     - Displays recorded waveforms on oscilloscope	*
;*     - Straight or edge-triggered recording		*
;*							*
;*		Designed by Parallax, Inc.		*
;*		Last updated 11/15/91			*
;*							*
;********************************************************
;
		device	pic16c55,hs_osc,wdt_off,protect_off
		id	checksum		;device's id word = checksum
		reset	power_up		;jmp to power_up on reset
;
;
; RA is a 4-bit bidirectional data bus connecting the PIC, DRAM, and channels
; RB contains control signals - some pins are used as both inputs and outputs
; RC is an 8-bit output bus for DRAM address and DAC data
;
zip		=	rb.0			;can output a low to cancel dac
we		=	rb.1			;dram write enable bit
cas		=	rb.2			;dram column address select bit
ras		=	rb.3			;dram row address select bit
tr		=	rb.4			;can output low to stop noise
record		=	rb.5			;record button input
left		=	rb.6			;left button input
right		=	rb.7			;right button input

display_on	=	11100001b		;directions for rb
display_off	=	11110000b		;0=output, 1=input

toggle		=	pa0			;bit variables
bit		=	pa1			;(unused bits in status reg)
direction	=	pa2
;
;
; General register assignments (08h-1fh)
;
		org	08h			;set org to general registers

trace_buffer	ds	8			;trace buffer holds 40h bits
address_low	ds	1			;dram address low
address_high	ds	1			;dram address high
channel		ds	1			;channel number
trace		ds	1			;trace position (x axis)
level		ds	1			;level of line (y axis)
temp		ds	1			;temporary usage register

		org	0h			;reset origin for rom code
;
;
;************************
;*	SUBROUTINES	*
;************************
;
;
; Clear memory from address to end
;
clear_to_end	clr	ra			;make ra output lows
		mov	!ra,#0000b		;to be written into dram
		mov	w,#regular_loop		;load w with address
		clrb	bit			;clear bit
		jmp	write_entry		;write to end of memory
;
;
; Record pressed - get final record command and read 64K samples into dram
;
handle_record	call	clear_address		;reset address
		clrb	bit			;clear bit to flag left/right

:debounce	mov	temp,#100		;100 cycles of record debounce
:wait		sb	left			;if left button, direction=0
		clrb	direction
		sb	right			;if right button, direction=1
		setb	direction
		snb	left			;if either button, set bit
		sb	right
		setb	bit
		jnb	record,:debounce	;if record, reload debounce
		djnz	temp,:wait		;record not pressed, 100 yet?

		mov	w,#101000b		;get pos or neg edge for option
		snb	direction		;according to direction
		mov	w,#111000b
		mov	option,w		;load option with w

		mov	w,#regular_loop		;if left or right button is
		snb	left			;still pressed, then do per-
		sb	right			;sample triggering
		mov	w,#trigger_loop

write_entry	mov	temp,w			;save loop address in temp
		clrb	we			;clear we to enable dram writes
		jnb	bit,regular_loop	;if no button, regular

trigger_loop	clr	rtcc			;reset rtcc
:wait		jnb	record,clear_to_end	;if record button, abort
		jnb	rtcc.0,:wait		;wait for trigger edge

regular_loop	mov	rc,address_low	;2 cyc	;write row address
		clrb	ras		;1	;latch into dram
		mov	rc,address_high	;2	;write column address
		clrb	cas		;1	;latch into dram, write
		setb	cas		;1	;end write cycle
		setb	ras		;1	;return ras high, too
		mov	w,temp		;1	;get loop address in w
		inc	address_low	;1	;inc low address
		snz			;1/2s	;skip if not 0
		incsz	address_high	;1/2s	;inc high address, skip if 0
		jmp	w		;2	;jmp to loop (14 cycles/loop)

		setb	we			;done, return we high
		jmp	make_ra_input		;compensate for write_entry
;
;
; Handle panning - perform pan if left and/or right buttons are pressed
;
handle_panning	mov	w,#01h			;w=01h in case of left or right
		jb	left,:not_left		;check for left button pressed
		jnb	right,:both		;check for both buttons pressed
		clrb	direction		;just left, clear direction bit
		jmp	step_left		;step left 01h

:not_left	jb	right,:neither		;check for right button pressed
		setb	direction		;just right, set direction bit
		jmp	step_right		;step right 01h

:both		mov	w,#40h			;both buttons pressed
		jnb	direction,step_left	;step either left or right 40h
		jmp	step_right		;depending on direction bit

:neither	ret				;neither button, exit
;
;
; Step left w positions - don't go below address 0
;
step_left	sub	address_low,w		;subtract w from address_low
		jc	:done			;if c=1 (no underflow), done
		dec	address_high		;decrement address_high
		movsz	w,++address_high	;skip if address_high was 0
:done		ret

clear_address	clr	address_low		;clear address word
		clr	address_high
		ret
;
;
; Step right w positions - don't go above address 0ffc0h
;
step_right	mov	temp,w			;store w in temp
:loop		mov	w,++address_high	;check if address_high = 0ffh
		jnz	:step			;if not, do step
		cje	address_low,#0c0h,:done	;if address = 0ffc0h, done
:step		inc	address_low		;inc address_low
		snz				;skip if not 0
		inc	address_high		;inc address_high
		djnz	temp,:loop		;loop if more steps
:done		ret
;
;
; Refresh - cycle dram through 256 row accesses
;
refresh		clr	rc			;clear rc for row addresses
:loop		clrb	ras			;latch row address to refresh
		setb	ras			;return ras high
		ijnz	rc,:loop		;inc row address, loop if not 0
		ret
;
;
; Get buffer pointer - fsr points to a trace_buffer register and w holds mask
;
get_buffer_ptr	mov	w,>>trace		;get trace/2 in w
		mov	fsr,w			;store in fsr
		rr	fsr			;rotate right fsr
		rr	fsr			;rotate right fsr
		clrb	fsr.4			;fsr = 08h-0fh (trace_buffer)
		mov	w,trace			;get trace in w
		and	w,#07h			;isolate lower 3 bits (0h-07h)
lookup_mask	jmp	pc+w			;lookup bit mask according to w
		retw	01h,02h,04h,08h,10h,20h,40h,80h	;w holds mask after ret
;
;
; Trigger scope - make ra and tr output lows, generate sync pulse, start rtcc
;  All I/O pins which connect to input channels or the trigger will output lows
;  in order to reduce ac noise on the dac output during trace generation
;
trigger_scope	clr	ra			;ra=gnd to reduce dac noise
		mov	rb,#00001110b		;tr=gnd to reduce dac noise
		mov	rc,#0ffh		;dac will go high when enabled
		mov	!ra,#0000b		;make ra output
		mov	!rb,#display_on		;make tr output and enable dac
		mov	!rc,#00000000b		;make rc output (already was)
		mov	option,#001000b		;rtcc counts instruction cycles
		clr	rtcc			;clear rtcc for fresh count
		clr	trace			;clear trace for caller's use
		ret
;
;
; Bit sync output - wait for rtcc's msb to go high, then output w
;
bit_sync_level	add	w,level			;on entry, w must hold height

bit_sync	jnb	rtcc.7,$		;loop until rtcc's msb high
		mov	rc,w			;output w to dac
		clrb	rtcc.7			;clear msb (low bits unchanged)
		ret
;
;
; Bit sync output - wait for rtcc's msb to go high, then output w with slewing
; this routine was added at the last minute so that traces look like functions
;
bit_sync_level2	add	w,level			;on entry, w must hold height

		mov	1eh,w
		mov	1fh,rc

		jnb	rtcc.7,$		;loop until rtcc's msb high
		clrb	rtcc.7			;clear msb (low bits unchanged)

		cje	1eh,1fh,:exit

		cjb	1eh,1fh,:a

:b		mov	1dh,#0ch
:blp		inc	rc
		djnz	1dh,:blp
		jmp	:exit

:a		mov	1dh,#0ch
:alp		dec	rc
		djnz	1dh,:alp
		jmp	:exit

:exit		ret
;
;
; Show levels
;
show_levels	call	:lookup
		mov	level,w
		clr	w
		snb	toggle
		mov	w,#0ch
		jmp	bit_sync_level

:lookup		add	pc,channel		;jump to pc+channel
		retw	0b8h,98h,78h,58h	;levels for 4 channels
;
;
; Trace done - disable dac output, make ra and tr inputs
;
trace_done	clr	w			;ready to output low on dac
		call	bit_sync		;wait for current bit done
		clrb	zip			;ready to disable dac
		mov	!rb,#display_off	;disable dac and make tr input
make_ra_input	mov	!ra,#1111b		;make ra input
		ret
;
;
;************************
;*	MAIN		*
;************************
;
;
; Power up
;
power_up	call	trigger_scope		;call trigger_scope to init i/o

		call	clear_address		;clear memory
		call	clear_to_end
;
;
; Draw 4 channel traces
;
display		clr	channel			;reset channel counter

:trace		call	refresh			;refresh dram

		clr	trace			;get trace_buffer from dram
:z0		mov	rc,address_low		;write low address byte to rc
		clrb	ras			;latch row address (refreshes)
		mov	rc,address_high		;write high address byte to rc
		clrb	cas			;latch column address (rd/wr)
		mov	w,channel		;get channel in w
		call	lookup_mask		;translate w to bit mask
		and	w,ra			;test bit in ra with mask
		movb	bit,/z			;move not z into bit
		setb	cas			;return cas high
		setb	ras			;return ras high
		inc	address_low		;inc low address
		snz				;skip if not 0
		inc	address_high		;inc high address
		call	get_buffer_ptr		;get fsr and mask in w
		not	w			;not w mask
		and	indirect,w		;clear bit in trace_buffer
		not	w			;return mask to normal
		snb	bit			;skip if bit not set
		or	indirect,w		;set bit in trace_buffer
		inc	trace			;inc trace
		jnb	trace.6,:z0		;if trace not 40h yet, loop
		sub	address_low,#40h	;restore address by subtracting
		subb	address_high,c		;40h from address word

		call	trigger_scope		;do sync pulse
		call	show_levels		;show levels

:sample		call	get_buffer_ptr
		and	w,indirect
		mov	w,#0
		sz
		mov	w,#0ch
		test	trace
		jnz	:slow
		call	bit_sync_level
		jmp	:cont
:slow		call	bit_sync_level2
:cont
		inc	trace			;inc trace position
		jnb	trace.6,:sample		;if not 40h, show next sample

		call	show_levels
		call	trace_done
		inc	channel			;inc channel
		jnb	channel.2,:trace	;if not 4h, show next trace
;
;
; Show guide trace
;
guide_trace	call	refresh			;refresh dram
		call	refresh			;do it a couple of times to
		call	refresh			;avoid triggering the scope
		call	refresh			;prematurely on the guide trace

		call	trigger_scope		;trigger scope
		mov	level,#30h		;draw to the guide level
		call	bit_sync		;w holds 30h

:loop		mov	w,>>address_high	;check for global indicator
		mov	temp,w			;get address_high/4 into w
		mov	w,>>temp
		and	w,#3fh
		xor	w,trace			;xor w with trace, z=1 if same
		mov	w,#10h			;(doesn't affect z)
		jz	:show			;show global indicator if z=1
		mov	w,address_low		;check for guide indicator
		add	w,trace
		and	w,#3fh
		mov	w,#08h
		sz
		clr	w			;nothing, just line
:show		call	bit_sync_level		;draw bit
		inc	trace			;increment trace
		jnb	trace.6,:loop		;if trace not 40h yet, loop

		call	bit_sync_level		;draw last segment (w=0)
		call	trace_done		;end trace
;
;
; Handle any button inputs
;
		sb	record
		call	handle_record
		call	handle_panning
		xor	status,#20h		;toggle 'toggle' bit in status
		jmp	display			;loop to display
