;====== Diagnostic display function V1.00 =============
**********************************************************************
;This program provides storage of error codes and display 
;on a 7-segment LED diagnostic digit.
**********************************************************************

;Disclaimer:
;	This is an example program supplied "as is" with no warranties
;	implied or express as to its suitability for any application.
;	You must integrate this code into your own program and thoroughly 
;	test it.

;Incorporating this code into your program (general instructions)
;	Only code between ;vvvvvvvvvvvvvv and '^^^^^^^^^^^^^^ is intended
;	to be used in your program. Everything else is documentation and
;	test code.
;	If the program contains an NVEM0 directive, the stuff below that 
;	must be appended at the end of your program, after one single NVEM0
;	directive.
;	The example code is designed to run under MultiTrack. You must 
;	make sure to include LaunchTask instructions to make it run.
;	You need to understand MultiTrack. It is quite simple to comprehend
;	and will save you a lot of effort in the long run.
;	MultiTrack is previewed in a MiniTutorial within SPLat/PC
;	and covered in detail in the SPLat Knowledge Base.

; Accessing the functionality from your program:
;
;	All external access to the function is via subroutine calls (GoSub). In keeping 
;	with the ideas of object oriented programming we refer to Properties 
;	and Methods. 
;	- Properties are data you can GET or LET. The data belongs 
;	to the function so you are only allowed to access it through subroutine 
;	calls to the applicable GET or LET routine.
; 
;	Example:	GoSub	XYZ_GET_RunStatus
;			GoSub	XYZ_LET_RequiredLevel
;
;	- Methods do something. You invoke them by subroutine calls. 
;
;	Example:	GoSub	XYZ_TurnOnPump
;
;	All property and method calls have names 
;	starting in the short name of the function (XYZ_ above). By convention the short name 
;	is in upper case, very few letters and unique to the function. It ends 
;	in an underscore. The short name is NOT included in the property and 
;	method descriptions below but must be included when you use them.
;	
;	The startup method is usually invoked by a LaunchTask rather than a GoSub.
;
;	Examples:	LaunchTask	XYZ_Start
;			or
;			LaunchTask	XYZ_Init

************* TEST CODE ********************************
;	STScale	200	;for simulation
	OBLCD_Type	5
;ruff test
	LaunchTask	Test		;Main tester
	LaunchTask	DSD_Init	;Start up the task being tested
	LaunchTask	KPD_Start	;Get the keypad scanner going (TR602 feature)
	LaunchTask	TestTime	;Time program execution
	RunTasksForever

***********************************************************************

**** Test code is for a TR0601 trainer board, which has heaps of buttons,
;keypad and other useful stuff, plus a 7 segment digit.
;
;Controls are:
;Keypad keys 1-9 enter fault codes. Holding down IN7 enters the fault as blocking.
;
;IN6 is the Prev button
;IN8 is the next button
;In5 is the reset button
;The LCD shows the fault buffer in Hex.
********* TEST ****************************************
Test:
	YieldTask
	YieldTask
	YieldTask
	YieldTask

	KBeepOff			;HVAC control has no beeper.
	LoadX		5
	GoSub		DSD_LET_Reg_NB
	LoadX		4
	GoSub		DSD_LET_Reg_NB
	LoadX		3
	GoSub		DSD_LET_Reg_NB

T1:
	YieldTask
	RecallS		sDSD_TimedUnBlock
	NOT
	Output		17
	GoSub		DispBuffer
	YieldTask
	GoSub		DoGets
	YieldTask
	GoIfInK		5,TR
	GoSub		KPD_GET_Key
	GoIfF		T1
	Push
	GoIfZ		T1		;Stop 0 key
	Push
	GoIfXGT		9,T1		;Stop anything > 9
	GoIfInOn	7,T2		;Blocking
	GoSub		DSD_LET_Reg_NB
	GoTo		T1
T2
	GoSub		DSD_LET_Reg_BB
	GoTo		T1

TR
	GoSub		DSD_Reset
	GoTo		T1 

;GET faults and display (testing GET)
DoGets:

	GoSub		DSD_GET_LastFault
	OBLCD_SetCur	1,0
	OBLCD_HexDispX
	OBLCD_Text	" "

	GoSub		DSD_GET_PrevFault
	OBLCD_HexDispX
	OBLCD_Text	" "

	GoSub		DSD_GET_PrevFault
	OBLCD_HexDispX
	OBLCD_Text	" "

	GoSub		DSD_GET_PrevFault
	OBLCD_HexDispX
	OBLCD_Text	" "

	GoSub		DSD_GET_PrevFault
	OBLCD_HexDispX
	OBLCD_Text	" "

	GoSub		DSD_GET_PrevFault
	OBLCD_HexDispX
	OBLCD_Text	" "

	GoSub		DSD_GET_PrevFault
	OBLCD_HexDispX
	OBLCD_Text	" "
	Return

;Display buffer on lcd
DispBuffer
 	OBLCD_SetCur  0,0
	LoadI		0
	LoadX		cDSD_BufSize
DBLoop
	iRecall		bDSD_Buffer
	IncI
	OBLCD_HexDispX
	OBLCD_Text " "
	DecX
	Push
	GoIfNZ		DBLoop
	Return
;-------------------
LoopCounter	defBYTE
LoopTimer	defTIME24

TestTime:	;Measure how fast MultiTrack cycles through the task queue
		STStart		LoopTimer
TT1:
		YieldTask
		DMGNZ		LoopCounter,TT1
		fSTTimeSince	LoopTimer
		fLoadQ		0.0256
		fSwap
		fDiv
		OBLCD_SetCur	2,0
		OBLCD_fDispW	6,0
		OBLCD_Text	228,"S/Q"
		GoTo		TestTime

;======= Key pad ===================
;Short name	KPD_

;Entry points:
;		Start		;The task to launch
;		GET_Key		;Get any result.
;				;Returns X = T if there is a key stroke
;				;	 Y=Key code
;				;A key is returned with X=T only once.

;Size: 207 bytes Flash. 3 bytes and 1 bit of RAM

;The code returned is a function of row and column, generated by a
;table in NVEM.

;A 4x4 keypad is connected to 4 outputs (rows) and 4 inputs (columns)
;Note that whether outputs are rows or columns is an arbitrary hardware design
;decision. We have made one choice in this program. "Unscrambling" is a matter
;of updating the translation table

oKPD_Rows	oEQU		0	;Base address of row outputs
iKPD_Cols	iEQU		0	;Base address of column inputs

KPD_Row:	defByte
KPD_Col:	defBYTE
KPD_Temp:	defBYTE
sKPD_Flag:	defSEM

KPD_Start:
KPD_Let0:
		LoadX		'F
		Push
		OutputM		oKPD_Rows	;Turn on all row drives
KPD_Let0a:
		MarkTime
KPD_0:	;Wait for all inputs off for at least 50mS
		YieldTask
		InputFM		iKPD_Cols
		LoadX		'F
		AndM
		GoIfNZ		KPD_Let0a
		LoopIfTiming	5,KPD_0
KPD_Let1:
KPD_1:	;Wait for any input
		YieldTask
		InputFM		iKPD_Cols
		LoadX		'F
		AndM
		Push
		GoIfZ		KPD_1
;Have at least one input. Encode 4-bit pattern into a bit number
		GoSub		KPD_Enc4
		Push
		GoIfXEQ		'FF,KPD_Let0	;Not an acceptable pattern
		Store		KPD_Col
KPD_2:	;Wait 20mS then check to see if input is the same
  		Pause		5		;Wait 20mS
		InputFM		iKPD_Cols
		LoadX		'F
		AndM
		GoSub		KPD_Enc4
		Recall		KPD_Col
		Compare
		GoIfNZ		KPD_Let0
KPD_Let3:
		SetMem		KPD_Row,%1000
		SetMem		KPD_Temp,0
KPD_3:
		Recall		KPD_Row		;Bit pattern
		LoadX		'F
		OutputM		oKPD_Rows
		YieldTask			;Allow a bit of time to settle (not critical)
		InputFM		iKPD_Cols
		LoadX		'F
		AndM
		GoSub		KPD_Enc4
		Recall		KPD_Col
		Compare
		GoIfNZ		KPD_3a		;g/no input on this row
		Recall		KPD_Temp
		Recall		KPD_Row
		ORM
		Store		KPD_Temp	;build pattern of row bit that turn on the colum
KPD_3a
		Recall		KPD_Row		;Bit pattern
		Push
		GoIfXEQ		1,KPD_Let4	;Done all rows. Lets see if just one responded
		RORM
		Store		KPD_Row
		GoTo		KPD_3

;Have identified a key. Temp is the row bit pattern. Col is the bit number
KPD_Let4:
		Recall		KPD_Temp	;Should have just one bit set
		GoSub		KPD_Enc4	;Convert to a bit number
		Push
		GoIfXEQ		255,KPD_Let0	;255=more than 1 bit set
		ROLM
		ROLM
		Recall		KPD_Col
		ORM				;Now have 4-bit row-column index
		NVSetPtr	KPD_KeyTable
		GoSub		KPD_TableLook
		Store		KPD_Row
		SetS		sKPD_Flag
KPD_4:
		WaitForSF	sKPD_Flag
		KBeep
		GoTo		KPD_Let0

;Encode 4-bit pattern into a 2-bit number. Return 'FF if  >1 bit
KPD_Enc4:
		NVSetPtr	KPD_Enc4_table
KPD_TableLook:
		LoadX		'F
		AndM				;Defensive!!!
		NVSetPage	0
		NVPopRecNum
		NVSetRecLen	1
		NVPushByte	0
		Return

KPD_GET_Key:
		Recall		KPD_Row
		RecallS		sKPD_Flag
		ClrS		sKPD_Flag
		Return




;vvvvvvvvvv Start of the actual example code vvvvvvvvvvv

;=========== Diagnostic Storage and Display =====================
;Short name:	DSD_

;What it does:

;	DSD_ provides complete management of the diagnostic readout digit and two
;	control push buttons. It can simply be fed error codes from your program,
;	and will then store them in non-volatile memory for later readout.
;
;	A circular buffer is used to save the last 'n' faults. Old faults
;	are lost when new ones are written.
;
;	A constant determines how many sequential faults can be stored. We suggest 6
;	is a suitable maximum number. Beyond that nobody would be able to make much
;	sense of more data.
;
;	Timed blocking
;	--------------
;	Once a fault is written, further faults are blocked for a certain amount of time
;	(set by a constant, cDSD_BlockTime). Hence, the logic is mainly to capture the first-up fault,
;	which will normally give the best indication of what went wrong.

;	Startup blocking
;	----------------
;	A constant, cDSD_StartBlockTime, controls the unconditional blocking of all faults 
;	immediately after initialization.
;
;	Repeat fault blocking
;	---------------------
;	Faults can be registered in two ways:
;	Blocking	A subsequent attempt to register the same fault code will
;			be blocked.
;	Non-blocking	A subsequent attempt to register the same fault code will
;			be honored.

;
;	Examples:
;		Say A is always registered as non-blocking and B is always registered
;		as blocking (this would controlled by you, using different calls).
;		AABA  would be stored as AABA
;		AABBA would be stored as AABA	The 2nd B is blocked
;		AABAB would be stored as AABAB	The second B is not blocked because the 3rd A 
;						clears the block
;		ABBBBBBBA would be stored as ABA
;
;	If C is also registered as blocking, then
;		ABCBC would be stored as ABCBC	B and C cannot block each other, only themselves.

;	Fault codes
;	-----------
;	Valid fault codes are 1 to 127. 0 is reserved for 'empty'. Bit 7 is used for blocking. 
;	In practice you should use a small number of contiguous codes, starting from 1. The 
;	7-segment display patterns for each code are determined by an NVEM0 table.
;	We have provided some example digit patterns. You can make
;	your own if you want something different, or more. Code 0 is reserved for 
;	"no fault" and also has a display pattern.
;	Note that the actual meaning of fault codes is up to you. This function simply stores
;	and retrieves them.

;	Button behaviour:
;	-----------------
;	A user can read out faults without any intervention from your program. The buttons are
;	designated as Prev and Next.
;
;	At initialization the newest fault code, if any will be
;	displayed for 30 seconds. Then the display is blanked (off).
;	If there is a fault registered, the decimal point flashes.
;	Pressing either button initially lights up the display and shows the newest fault.
;	Pressing Prev shows the previous fault, or zero if there is none.
;	Pressing Next shows the next (later) fault, or zero if there is none.
;	The display blanks again 30s after the last button activity.
;
;	A separate task, DSD_RstMon, if you launch it, will monitor the two buttons continuously 
;	for a reset sequence. If Prev is held on while Next is pressed 3 times, DSD_RstMon will
;	call DSD_Reset and clear the fault memory. If you don't want that function don't launch it.
;	You can delete the code if you are not using it.

;	Manual editing
;	--------------
;	Lines/areas requiring manual editing are flagged with  ;<<<<<
;	*	You must manually edit the argument of the defBYTE at bDSD_Buffer 
;		so it matches the value of cDSD_BufSize.
;	*	You must manually edit the I/O assignments to match your controller hardware.
;	*	You must manually edit cDSD_BlockTime. See Timed Blocking above.
;		

;---- Properties and methods ---------
;Methods:
;	Init		;Initialises the background task. Invoke with 'LaunchTask  DSD_Init'
;			;The Init method reads back previously stored data from shadow memory
;	Reset		;clears the fault memory

;Property LETs:
;	LET_Reg_NB	;Register the fault code in X, non-blocking
;	LET_Reg_BB	;Register the fault code in X, blocking

;Property GETs: (You would need to use these only if you need to access faults for
;		use elsewhere in your program, for example to make them available via ModBus)
;	GET_LastFault	;Return the last recorded (newest) fault, in X. 
;			;Leaves the auto readout pointer on that fault.
;	GET_PrevFault	;Retard the auto readout pointer, if not at the first (oldest)
;			;fault and return the fault in X. 
;	Both of the above return 0 if no fault meets the criterion.
	***NB!! Be sure to do a GET_LastFault before attempting a GET_PrevFault!
	***NB!! Your program should read out the whole fault buffer in one go, without an intervening 
;		Yield, to avoid the hazard of new data being written and changing the buffer contents.
;	These functions use their own pointer, independent of the button controlled readout above.


;--------- Revision history (newest on top) -------------

;V1.01	070419	Changed terminology from SET to LET
;V1.00	060828	Initial release




	
cDSD_BufSize	EQU		6 ;<<<<<<<	;You will get one fewer faults saved (one byte is used as a sentinal)
bDSD_Buffer	defBYTE		6 ;<<<<<<< *** IMPORTANT *** Manually edit to equal the value of cDSD_BufSize

cDSD_BlockTime	EQU		200 ;<<<<<<< x10mS	Can go up to 46 hours
cDSD_StartBlockTime EQU		100 ;<<<<<<< x10mS	Can go up to 46 hours


bDSD_ManPntr	defBYTE				;Pointer for manual readout
sDSD_HaveData	defSEM
sDSD_NewData	defSEM
sDSD_ManAtLast	defSEM
sDSD_ManAtFirst	defSEM
bDSD_AutoPntr	defBYTE				;Pointer for programatic readout
sDSD_AutoAtLast	defSEM
sDSD_AutoAtFirst defSEM
sDSD_HadAuto	defSEM				;Have had a GET_LastFault call

sDSD_TimedUnBlock defSEM				; CLEARED to indicated faults are blocked through timing.
						;Inverted logic is used to avoid a startup hazard. The power up default
						;is the registration is blocked (semaphore false).

bDSD_Temp	defBYTE				;Scratchpad

;I/O assignments must match your hardware!!!!!!!!!!!
oDSD_DP		oEQU		26 ;<<<<<<<	Decimal point
oDSD_7Seg	oEQU		19 ;<<<<<<<	7 segment display base address
iDSD_Next	iEQU		8  ;<<<<<<<	Next button
iDSD_Prev	iEQU		6  ;<<<<<<<	Prev button

DSD_Init:
		LaunchTask	DSD_StartBlockTimer		;Block writing of faults for a time after startup

;At init-time determine if there is anything in the buffer. A non-zero datum indicates something
		ShadowRead	bDSD_Buffer,cDSD_BufSize	;Read back shadow data
		LoadI		0				;Prepare to scan buffer
		SetMem		bDSD_ManPntr,cDSD_BufSize	;Use pointer reg for loop counter
DSD_Init1:
		IasJ:GoIfMNZ	bDSD_Buffer,DSD_Init2		;G/found something
		IncI						;select next buffer byte
		DMGNZ		bDSD_ManPntr,DSD_Init1	
		GoTo		DSD_Let2
DSD_Init2: ;Found something
		SetS		sDSD_HaveData
		GoTo		DSD_Let1

;Idle state when we have data. Look for buttons and blink DP
DSD_Let0:
		LoadX		'0			;Blank digit
		LoadX		'7F
		OutputM		oDSD_7Seg
		Blink		oDSD_DP			;Toggle DP
DSD_0:
		YieldTask
		GoIfST		sDSD_NewData,DSD_Let1	;Data changed?
		GoIfSF		sDSD_HaveData,DSD_Let2	;Still got data? (could get reset by others)
		InputK		iDSD_Next
		InputK		iDSD_Prev
		OR
		GoIfT		DSD_Let1
		GoTo		DSD_0
;Displaying data
DSD_Let1:	
		On		oDSD_DP
		GoSub		DSD_FindLastPntr	;Get pointer to last entry
		Store		bDSD_ManPntr
		ClrS		sDSD_NewData
DSD_1_Show:
		GoSub		DSD_ShowPtr		;Show the entry
		MarkTime
DSD_1:
		YieldTask
		GoIfST		sDSD_NewData,DSD_Let1	;Data changed?
		GoIfSF		sDSD_HaveData,DSD_Let2	;G/ buffer just got reset
		GoIfInK		iDSD_Next,DSD_1_Next
		GoIfInK		iDSD_Prev,DSD_1_Prev
		LoopIfTiming	3000,DSD_1
		GoTo		DSD_Let0
;Show next entry
DSD_1_Next:
		GoIfST		sDSD_ManAtLast,DSD_1	;Check if already at last entry
		Recall		bDSD_ManPntr
		GoSub		DSD_MakeNext		;Advance pointer in X, current (old) value remains in Y
		Push
		XtoI
		IasJ:GoIfMNZ	bDSD_Buffer,DSD_1_GoNext	;G/ next entry is NZ, meaning we are not at the end.
		SetS		sDSD_ManAtLast			;Signal we are at the last entry
DSD_1_GoNext:
		Store		bDSD_ManPntr
		ClrS		sDSD_ManAtFirst				
		GoTo		DSD_1_Show

;Show previous entry
DSD_1_Prev:
		GoIfST		sDSD_ManAtFirst,DSD_1		;Check if already at first entry
		Recall		bDSD_ManPntr
		GoSub		DSD_MakePrev			;Advance pointer in X, current (old) value remains in Y
		Push
		XtoI
		IasJ:GoIfMNZ	bDSD_Buffer,DSD_1_GoPrev	;G/ previous entry is NZ, meaning we are not at the start.
		SetS		sDSD_ManAtFirst			;Signal we are at the first entry
DSD_1_GoPrev:
		Store		bDSD_ManPntr				
		ClrS		sDSD_ManAtLast
		GoTo		DSD_1_Show

DSD_Let2:
		LoadX		0		;Output byte
		LoadX		'FF		;Mask for OutputM
		OutputM		oDSD_7Seg	;Blank display and dp
DSD_2:
		YieldTask
		GoIfST		sDSD_NewData,DSD_Let1	;Got some data??
		InputK		iDSD_Next
		InputK		iDSD_Prev	
		OR
		GoIfF		DSD_2
DSD_Let3:
		LoadX		0
		GoSub		DSD_ShowX
		MarkTime
DSD_3:	
		YieldTask
		GoIfST		sDSD_NewData,DSD_Let1	;Got some data??
		LoopIfTiming	3000,DSD_3
		GoTo		DSD_Let2

;======== Sub tasks =======
DSD_BlockTimer: ;Simply generate a blocking semaphore for a given time.
		ClrS		sDSD_TimedUnBlock
		Pause		cDSD_BlockTime
		SetS		sDSD_TimedUnBlock
		KillTask

DSD_StartBlockTimer:
		ClrS		sDSD_TimedUnBlock
		Pause		cDSD_StartBlockTime
		SetS		sDSD_TimedUnBlock
		KillTask


;===== LETs and GETs ===========
DSD_LET_Reg_BB:	;register fault in X as blocking
		LoadX		'80
		OrM
DSD_LET_Reg_NB:	;register fault in X as non-blocking
		RetIfSF		sDSD_TimedUnBlock
		Store		bDSD_Temp
		GoSub		DSD_CheckBlocked	;Is this code blocked?
		RetIfT					;r/ blocked
		GoSub		DSD_FindLastPntr	;Pointer to latest entry
		SetS		sDSD_NewData
		SetS		sDSD_HaveData		*** VITAL before the next thing ..
		GoSub		DSD_MakeNext		;Next vacant slot 
							;(would stay at start of buffer if HaveData 
							;semaphore was not set!)
		Push					;Keep a copy of the next pntr
		XtoI
		Recall		bDSD_Temp
		iStore		bDSD_Buffer		;Write data, expose pointer
		GoSub		DSD_MakeNext		;Next pointer after where we just wrote
		XtoI
		iSetMem		bDSD_Buffer,0		;Sentinal byte
		LaunchTask	DSD_BlockTimer
		Return


DSD_GET_LastFault:	;Return the newest fault code, if any (programatic readout)
		GoSub		DSD_FindLastPntr	;Get pointer to the newest entry
		SetS		sDSD_HadAuto
		ClrS		sDSD_AutoAtFirst
		Push
		XtoI
		GoTo		DSD_G2

DSD_GET_PrevFault:	;Return the previous fault code, if any (programatic readout)
		GoIfSF		sDSD_HadAuto,DSD_GET_LastFault	
		GoIfST		sDSD_AutoAtFirst,DSD_G1	;Check if already at first entry
		Recall		bDSD_AutoPntr
		GoSub		DSD_MakePrev		;Retard pointer in X, current (old) value remains in Y
		Push
		XtoI
		IasJ:GoIfMNZ	bDSD_Buffer,DSD_G2	;G/ previous entry is NZ, meaning we are not at the start.
		SetS		sDSD_AutoAtFirst	;Signal we are at the first entry
DSD_G2:
		Store		bDSD_AutoPntr				
		iRecall		bDSD_Buffer
		LoadX		'7F
		AndM					;Remove the blocking bit
		Return

DSD_G1:
		LoadX		0
		Return



;======== Reset method ===============
DSD_Reset:
		ClrS		sDSD_HaveData
		ClrS		sDSD_NewData
		LoadX		cDSD_BufSize
		LoadI		0
DSD_R1:
		iSetMem		bDSD_Buffer,0
		IncI
		DecX
		Push
		GoIfNZ		DSD_R1
		ShadowWrite	bDSD_Buffer,cDSD_BufSize
		Return

;------------
;Check if the code in X is blocked. That means the code
;matches the last written and the last written has bit 7 set.
;Result is left in X as T/F. The original code is in Y
DSD_CheckBlocked:
		GoSub		DSD_FindLastPntr	;Pointer to latest entry
		XtoI
		iRecall		bDSD_Buffer		;Last entry
		LoadX		'80
		AndM					;Is it blocking?
		Push
		RetIfF					;R/ not blocking
		Recall		bDSD_Temp
		iRecall		bDSD_Buffer		;Get last entry back again
		XorM					;Compare, get 0 if they are the same
		LoadX		'7F
		AndM					;Ignore blocking bits when comparing codes
		NOT
		Return
				


;------------
;Find the newest entry. 
;This subroutine returns, in X, a pointer to the newest entry, 
;relative to the start of the buffer.
;If the buffer is empty, based on the HaveData semaphore, returns the first location (i.e. 0)

;		0123456   Bufsize=7
;		XXX0XXX	2
;		0XXXXXX	6
;		XXXXXX0	5
;		000X000	3
;		X000000	0
;		000000X	6
;Method: Test each location until one is found which is nonzero and has zero in the 
;	 next location (with wraparound)

DSD_FindLastPntr:
		GoIfSF		sDSD_HaveData,DSD_NullPntr	;Guard against empty buffer
		LoadX		0
DSD_FL0:
		ClrInstCount		;Prevent a runtime error due to too many consecutive 
					;instructions in a MultiTrack task.
		Push
		XtoI
		IasJ:GoIfMNZ	bDSD_Buffer,DSD_FL1	;test current location
		IncX
		GoTo		DSD_FL0

DSD_FL1:   ;Entry(I) is NZ. Check next one
		ItoX
		GoSub		DSD_MakeNext	;Current index is preserved in Y
		XtoI
		IasJ:GoIfMZ	bDSD_Buffer,DSD_FL2	;G/ found!
		ItoX				;"Next"
		GoTo		DSD_FL0

DSD_FL2:
		Return
;--------
;Generate "next" pointer value from the one in X. Implies it may wrap.
;Returns new value in X, old value in Y
;If the buffer is empty, based on the HaveData semaphore, returns the first location (i.e. 0)

DSD_MakeNext:
		GoIfSF		sDSD_HaveData,DSD_NullPntr	;Guard against empty buffer
		Push			;Save old value
		IncX
		Push
		GoIfXGE		cDSD_BufSize,DSD_MN1
		Return
DSD_MN1:
		Pop
DSD_NullPntr:
		LoadX		0
		Return
;--------
;Generate "previous" pointer value from the one in X. Implies it may wrap.
;Returns new value in X, old value in Y
;If the buffer is empty, based on the HaveData semaphore, returns the first location (i.e. 0)

DSD_MakePrev:
		GoIfSF		sDSD_HaveData,DSD_NullPntr	;Guard against empty buffer
		Push			;Save old value
		DecX
		Push
		GoIfXEQ		255,DSD_MP1	;g/underflow
		Return
DSD_MP1:
		Pop
		LoadX		cDSD_BufSize
		DecX
		Return
;----------
;Display the fault code pointed to by bDSD_ManPntr
;Secondary entry point to show the code number in X
DSD_ShowPtr:
		Recall		bDSD_ManPntr
		XtoI
		iRecall		bDSD_Buffer
		LoadX		'7F
DSD_ShowX:
		AndM				;Strip bit 7 (blocking flag)
		NVSetPtr	SevenSegTable
		NVSetPage	0
		NVPopRecNum
		NVSetRecLen	1
		NVPushByte	0
		LoadX		'7F
		OutputM		oDSD_7Seg	;The start of the 7-seg outputs
		Return

*******************************************************************************************
**** NVEM0 segment. YOU MUST MANUALLY CUT AND PASTE THIS ON THE *END* OF YOUR PROGRAM *****
**** As you edit make sure your program has only one NVEM0 directive, and that all    *****
**** NVEM page zero stuff is after that directive				      *****
*******************************************************************************************

		NVEM0   ******* Only one NVEM0 per program! *******


;Seven segment display table. Digit is assumed to be wired to bits 0-8 in order
; abcdefg.  They are active ON
SevenSegTable:
;this version of the table shows 0 as a numeral zero
;			0	1	2	3		4	5	6	7		8	9	
;		 .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  
;;;;	NV0Byte	%00111111,%00000110,%01011011,%01001111,%01100110,%01101101,%01111101,%00000111,%01111111,%01100111


;this version of the table shows 0 as a dash
;			0	1	2	3		4	5	6	7		8	9	
;		 .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  .gfedcba  
	NV0Byte	%01000000,%00000110,%01011011,%01001111,%01100110,%01101101,%01111101,%00000111,%01111111,%01100111

;^^^^^^^^^^ End of the actual example code ^^^^^^^^^^^^^

********************************************************************************************************
*********** Testing only using TR0601 board **************************
********************************************************************************************************
;Table to translate from Row/Col to key code
KPD_KeyTable:
;                RC  RC  RC  RC  RC  RC  RC  RC  RC  RC  RC  RC  RC  RC  RC  RC
;		 00  01  02  03  10  11  12  13  20  21  22  23  30  31  32  33  
	NV0Byte	'01,'02,'03,'80,'04,'05,'06,'81,'07,'08,'09,'82,'83,'00,'84,'85
;TR0601           1   2   3   /   4   5   6   X   7   8   9   -   C   0   .   +

;Table to translate from 4-bit pattern to bit number.
;Only patterns with 1 bit set get encoded. Anything else returns FF
KPD_Enc4_table:
;                 0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
	NV0Byte	'FF,'00,'01,'FF,'02,'FF,'FF,'FF,'03,'FF,'FF,'FF,'FF,'FF,'FF,'FF
