diff src/options.asm @ 631:185ba2f91f59

3.09 beta 1 release
author heinrichsweikamp
date Fri, 28 Feb 2020 15:45:07 +0100
parents cd58f7fc86db
children 4050675965ea
line wrap: on
line diff
--- a/src/options.asm	Fri Feb 21 10:51:36 2020 +0100
+++ b/src/options.asm	Fri Feb 28 15:45:07 2020 +0100
@@ -1,6 +1,6 @@
 ;=============================================================================
 ;
-;   File options.asm                          next combined generation V3.04.3
+;   File options.asm                          next combined generation V3.08.8
 ;
 ;   Manage all options data.
 ;
@@ -22,14 +22,13 @@
 
 	extern	write_eeprom
 	extern	read_eeprom
-	extern	eeprom_serial_save,eeprom_opt_backup
 	extern	option_table_begin,option_table_end
 	extern	convert_meter_to_feet
 
 options		CODE
 
 ;=============================================================================
-; Reset all options to factory defaults
+; Reset all options to factory defaults (in memory only)
 ;
 ; INPUT:  none
 ; OUTPUT: none
@@ -37,163 +36,27 @@
 ;
 	global	option_reset_all				; reset all options to factory default
 option_reset_all:
-	clrf	EEADRH
-	read_int_eeprom .2
-	tstfsz	EEDATA							; number of total dives = 0 ?
-	bra		option_reset_all2				; NO - skip resetting logbook
-	read_int_eeprom .3
-	tstfsz	EEDATA							; number of total dives = 0 ?
-	bra		option_reset_all2				; NO - skip resetting logbook
-
-	clrf	EEDATA
-	write_int_eeprom .4
-	write_int_eeprom .5
-	write_int_eeprom .6
-	write_int_eeprom .2						; delete total dive counter, too
-	write_int_eeprom .3
-	call	ext_flash_erase_logbook			; complete logbook
-
-option_reset_all2:
-	clrf	lo
-	clrf	hi
-	call	do_logoffset_common_write		; reset logbook offset
-	movlw	LOW(option_table_begin)			; point to option table begin
-	movwf	FSR0L
-	movlw	HIGH(option_table_begin)
-	movwf	FSR0H
-option_reset_all_1:
-	movlw	LOW(option_table_end)			; get low byte of end of table address
-	cpfseq	FSR0L							; does it equal the current pointer position?
-	bra		option_reset_all_2				; NO - more options to process
-	movlw	HIGH(option_table_end)			; get high byte of end of table address
-	cpfseq	FSR0H							; does it equal the current pointer position?
-	bra		option_reset_all_2				; NO - more options to process
-	return									; YES to both - end of option table reached, done
-option_reset_all_2:
-	rcall	option_reset					; reset one option...
-	bra		option_reset_all_1				; ... and loop
+	call	eeprom_total_dives_read			; read total number of dives
+	tstfsz	mpr+0							; number of total dives, low  byte = 0 ?
+	bra		option_reset_all_1				; NO - skip resetting logbook
+	tstfsz	mpr+1							; number of total dives, high byte = 0 ?
+	bra		option_reset_all_1				; NO - skip resetting logbook
 
-;=============================================================================
-; Check all option and reset option if out of min/max boundary
-;
-; INPUT:  none
-; OUTPUT: none
-; TRASH:  TBLPTR, TABLAT, WREG, FSR0, FSR1, FSR2
-;
-	global	option_check_all				; check all option and reset option if out of min/max boundary
-option_check_all:
-	bcf		option_repaired					; no option needed repair up to now
-	movlw	LOW(option_table_begin)			; point to option table begin
-	movwf	FSR0L
-	movlw	HIGH(option_table_begin)
-	movwf	FSR0H
-option_check_all_1:
-	movlw	LOW(option_table_end)			; get low byte of end of table address
-	cpfseq	FSR0L							; does it equal the current pointer position?
-	bra		option_check_all_2				; NO - more options to process
-	movlw	HIGH(option_table_end)			; get high byte of end of table address
-	cpfseq	FSR0H							; does it equal the current pointer position?
-	bra		option_check_all_2				; NO - more options to process
-	bra		option_check_all_3				; YES to both - end of option table reached
-option_check_all_2:
-	rcall	option_check					; check one option...
-	bra		option_check_all_1				; ... and loop
-option_check_all_3:
-	bsf		is_diluent_menu					; setup checking diluents
-	call	gaslist_cleanup_list			; check and correct multiple or none First diluent
-	bcf		is_diluent_menu					; setup checking gases
-	call	gaslist_cleanup_list			; check and correct multiple or none First gas
- IFNDEF _gauge_mode
-	call	option_cleanup_gauge			; check and correct Gauge mode
- ENDIF
- IFDEF _ccr_pscr
-	call	option_cleanup_oCCRMode			; check and correct CCR / pSCR mode
- ENDIF
-	call	option_cleanup_GF				; check and correct GFlow <= GFhigh
-	return									; all done
-
-
+	; reset logbook
+	call	erase_complete_logbook			; erase complete logbook
 
-;=============================================================================
-; Read option handle
-; INPUT:  FSR0 = option handle
-; OUTPUT: FSR1 = address of variable.
-; TRASH:  TBLPTR, TABLAT, WREG, FSR0, FSR1
-;
-option_read:
-	movff	FSR0L,TBLPTRL					; set memory address of option data set, low byte
-	movlw	HIGH(option_table_begin)		; get table begin address, high byte
-	andlw	0xF0							; keep only the upper nibble
-	iorwf	FSR0H,W							; add the memory address of the option data set, high byte
-	movwf	TBLPTRH							; set the resulting memory address, high byte
-	movlw	UPPER(option_table_begin)		; get table begin address, upper byte
-	movwf	TBLPTRU							; set memory address of option data set, upper byte
-
-	; Read type, default and register from table
-	tblrd*+
-	movff	TABLAT,opt_type
-	tblrd*+
-	movff	TABLAT,opt_default
-	tblrd*+
-	movff	TABLAT,opt_inc
-	tblrd*+
-	movff	TABLAT,opt_min
-	tblrd*+
-	movff	TABLAT,opt_max
-	tblrd*+
-	movff	TABLAT,opt_eeprom
-	tblrd*+
-	movff	TABLAT,opt_unit+0
-	tblrd*+
-	movff	TABLAT,opt_unit+1
-	tblrd*+
-	movff	TABLAT,FSR1L
-	tblrd*+
-	movff	TABLAT,FSR1H
-	movff	TBLPTRL,FSR0L					; advance handle to next option data set (used for reset_all)
-	movff	TBLPTRH,FSR0H
-	return
+	; reset logbook offset
+	CLRI	mpr								; set   logbook offset to zero
+	call	eeprom_log_offset_write			; store logbook offset
 
-;=============================================================================
-; Check one option and reset if it's out of it's min/max boundaries
-; INPUT:  FSR0 = option handle
-; OUTPUT: none
-; TRASH:  TBLPTR, TABLAT, WREG, FSR1, FSR2, lo
-;
-option_check:
-	; Read type, default and register from table
-	rcall	option_read
+option_reset_all_1:
+	lfsr	FSR0,option_table_begin			; point to start of option definition table
+option_reset_all_loop:
+	rcall	option_reset					; reset option
+	incfsz	opt_end_token,F					; was this the last option (was opt_end_token = 255) ?
+	bra		option_reset_all_loop			; NO  - do next option
+	return									; YES - done
 
-	; Switch on type
-	movf	opt_type,W						; get option type
-	xorlw	.2								; type == STRING ?
-	bz		option_check_string				; YES
-	movf	opt_type,W						; get option type (again)
-	xorlw	.1								; type == ENUM8 ?
-	bz		option_check_enum8				; YES - check if lower then max. value only
-											; NO to all - must be integer then
-	tstfsz	opt_min							; opt_min = 0 ?
-	bra		option_check_both				; NO - check it
-	bra		option_check_enum8				; check max only
-
-option_check_both:
-	decf	opt_min,W						; check against minimum value
-	cpfsgt	INDF1							; bigger than opt_min - 1 ?
-	bra		option_check_reset				; NO - reset option
-option_check_enum8:							; check against maximum value
-	infsnz	opt_max,W						; max = 255?
-	return									; YES - ignore the max. test
-	cpfslt	INDF1							; NO  - smaller then opt_max + 1 ?
-	bra		option_check_reset				;       NO  - reset option
-	return									;       YES - within range, return
-
-option_check_reset:
-	movff	opt_default,INDF1				; reset option to default
-	bsf		option_repaired					; flag that an option was repaired
-	return									; done
-
-option_check_string:
-	return
 
 ;=============================================================================
 ; Reset an option to its default value
@@ -201,170 +64,316 @@
 ; OUTPUT: none
 ; TRASH:  TBLPTR, TABLAT, WREG, FSR1, FSR2
 ;
-	global	option_reset					; reset FSR0 option to factory default
+	global	option_reset					; reset option value to default
 option_reset:
-	; Read type, default and register from table
-	rcall	option_read						; read option data
+	; read type, default and register from table
+	rcall	option_read_definition			; read option definition
+
+option_reset_loaded:						; entry point with option definition already read
+	bsf		option_repaired					; flag that an option was repaired
+	bsf		options_changed					; flag that EEPROM needs to be updated
 	movf	opt_type,W						; get option type
-	xorlw	2								; Type == STRING ?
-	bz		opt_reset_string				; YES - special copy
-	movff	opt_default,INDF1				; NO  - just a 8 bit indirect copy
-	return
+	xorlw	.2								; type = STRING ?
+	bz		opt_reset_string				; YES - string copy
+	movff	opt_default,INDF1				; NO  - 1 byte copy
+	return									;     - done
+
 opt_reset_string:
 	movff	FSR1L,FSR2L						; set string destination address
 	movff	FSR1H,FSR2H						; ...
 	movff	opt_default+0,FSR1L				; get handle to multi-lingual text in FSR1
 	movff	opt_default+1,FSR1H				; ...
-	movff	TBLPTRL,opt_backup_tbl+0		; TBLPTR trashed by text routine...
-	movff	TBLPTRH,opt_backup_tbl+1		; ...
-	movff	TBLPTRU,opt_backup_tbl+2		; ...
+	movff	TBLPTRL,mpr+0					; TBLPTR will be trashed by text routine, so make a back-up
+	movff	TBLPTRH,mpr+1					; ...
+	movff	TBLPTRU,mpr+2					; ...
 	call	strcat_text						; copy translated text to FSR2
-	movff	opt_backup_tbl+0,TBLPTRL		; restore TBLPTR
-	movff	opt_backup_tbl+1,TBLPTRH		; ...
-	movff	opt_backup_tbl+2,TBLPTRU		; ...
+	movff	mpr+0,TBLPTRL					; restore TBLPTR
+	movff	mpr+1,TBLPTRH					; ...
+	movff	mpr+2,TBLPTRU					; ...
+	return									; done
+
 
-	return
+;=============================================================================
+; Read option definition
+; INPUT:  FSR0 = option handle
+; OUTPUT: FSR1 = address of variable.
+; TRASH:  TBLPTR, TABLAT, WREG, FSR0, FSR1
+;
+option_read_definition:
+	movff	FSR0L,TBLPTRL					; low byte  : set memory address of option data set
+	movlw	HIGH(option_table_begin)		; high byte : get table start address
+	andlw	0xF0							;             keep only the upper nibble
+	iorwf	FSR0H,W							;             add the memory address of the option data set
+	movwf	TBLPTRH							;             set the resulting memory address
+	movlw	UPPER(option_table_begin)		; upper byte: get table start address
+	movwf	TBLPTRU							;             set memory address of option data set
+
+	lfsr	FSR1,opt_type					; load FSR1 with base address of option definition vars
+	movlw	opt_definiton_bytes				; get number of bytes to copy
+	movwf	eeprom_loop						; initialize loop counter (using an EEPROM variable here)
+option_read_definition_loop:
+	tblrd*+									; read one byte from program memory and increment address
+	movff	TABLAT,POSTINC1					; transfer byte from program memory to memory
+	decfsz	eeprom_loop,F					; all bytes done?
+	bra		option_read_definition_loop		; NO  - loop
+	tblrd*									; YES - read one byte ahead without incrementing address
+	movff	TABLAT,POSTINC1					;     - store byte
+	movff	opt_memory+0,FSR1L				;     - load FSR1 with the address of the option value variable
+	movff	opt_memory+1,FSR1H				;     - ...
+	movff	TBLPTRL,FSR0L					;     - advance handle to the next option definition data set
+	movff	TBLPTRH,FSR0H					;     - ...
+	return									;     - done
+
 
 ;=============================================================================
-; Save all options to EEPROM
+; Check one option and reset its value if it is out of min/max boundary
+; INPUT:  opt_* vars and FSR1
+; OUTPUT: option value set to default if out of min/max
+; TRASH:  WREG
 ;
-	global	option_save_all					; save options to EEPROM
-option_save_all:
-	bcf	PIR3,RC2IE
-	;---- Save option serial into EEPROM to detect reset and new version
-	movlw	LOW(eeprom_serial_save)
-	movwf	EEADR
-	movlw	HIGH(eeprom_serial_save)
-	movwf	EEADRH
-	movlw	LOW(eeprom_opt_serial)
-	movwf	EEDATA
-	call	write_eeprom
-	incf	EEADR,F
-	movlw	HIGH(eeprom_opt_serial)
-	movwf	EEDATA
-	call	write_eeprom
+option_check_loaded:
+	; switch on type
+	movf	opt_type,W						; get type
+	bz		option_check_uint8				; type 0: INT8
+	dcfsnz	WREG							; decrement
+	bra		option_check_enum8				; type 1: ENUM
+	dcfsnz	WREG							; decrement
+	bra		option_check_string				; type 2: STRING
+	;bra	option_check_uint8				; type 3: INT8
+
+option_check_uint8:
+	tstfsz	opt_min							; opt_min = 0 ?
+	bra		option_check_min				; NO  - check it
+	bra		option_check_enum8				; YES - continue with check for maximum
+
+option_check_min:
+	decf	opt_min,W						; get (minimum permissible value - 1) into WREG
+	cpfsgt	INDF1							; option value > (minimum permissible value - 1) ?
+	bra		option_reset_loaded				; NO  - reset option value
+	;bra	option_check_enum8				; YES - continue with check for maximum
+
+option_check_enum8:
+	infsnz	opt_max,W						; get (highest permissible value + 1) into WREG
+	return									; highest permissible value was 255, skip check, done
+	cpfslt	INDF1							; option value < (highest permissible value + 1) ?
+	bra		option_reset_loaded				; NO  - reset option value
+	return									; YES - within range, done
+
+option_check_string:
+	return									; nothing to check with strings
 
-	;---- Save all options
-	movlw	LOW(option_table_begin)
-	movwf	FSR0L
-	movlw	HIGH(option_table_begin)
-	movwf	FSR0H
+
+;=============================================================================
+; Check and store all option values in EEPROM
+;
+	global	option_check_and_store_all
+option_check_and_store_all:
+	bcf		PIR3,RC2IE						; disable EUSART interrupts
+
+	;---- save option version
+	MOVLI	eeprom_opt_version,mpr				; get   options version number
+	EEPROM_II_WRITE mpr,eeprom_options_version	; store options version number in EEPROM
+
+	;---- check and resolve some interdependencies among options
+	bsf		is_diluent_menu					; setup checking diluents
+	call	gaslist_cleanup_list			; check and correct multiple or none First diluents
 
-option_save_all_1:
-	movlw	LOW(option_table_end)
-	cpfseq	FSR0L
-	bra		option_save_all_2				; not yet done...
-	movlw	HIGH(option_table_end)
-	cpfseq	FSR0H
-	bra		option_save_all_2				; not yet done...
-	bsf	PIR3,RC2IE
-	return									; all done
-option_save_all_2:
-	rcall	option_save						; save one option...
-	bra		option_save_all_1				; ...and loop
+	bcf		is_diluent_menu					; setup checking gases
+	call	gaslist_cleanup_list			; check and correct multiple or none First gases
+
+ IFNDEF _gauge_mode
+	call	option_cleanup_gauge			; check and correct gauge mode
+ ENDIF
+
+ IFDEF _ccr_pscr
+	call	option_cleanup_oCCRMode			; check and correct CCR / pSCR mode
+ ENDIF
+
+	call	option_cleanup_GF				; check and correct GFlow <= GFhigh
+
+	;---- check and save all option values
+	lfsr	FSR0,option_table_begin			; point to start of option definition table
+option_save_all_loop:
+	rcall	option_check_and_store			; check and save option value
+	incfsz	opt_end_token,F					; was this the last option (was opt_end_token = 255) ?
+	bra		option_save_all_loop			; NO  - do next option
+	return									; YES - done
 
 
-	global	option_save
-option_save:
-	rcall	option_read
-	incf	opt_eeprom,W					; should we save it ?
-	btfsc	STATUS,Z						; EEPROM address is FFh ?
-	return									; YES - nothing to do
-	movf	opt_eeprom,W					; compute backup address in EEPROM
-	addlw	LOW(eeprom_opt_backup)			; add offset
-	movwf	EEADR
-	movlw	HIGH(eeprom_opt_backup)
-	btfsc	STATUS,C						; > 256 ?
-	addlw	.1								; YES - +1
-	movwf	EEADRH
+;=============================================================================
+; Check and store an option value in EEPROM
+;
+	global	option_check_and_store
+option_check_and_store:
+	rcall	option_read_definition			; read the option definition
+	rcall	option_check_loaded				; check if option value is within min/max, set to default if not
+
+option_save_loaded_checked:
+	movf	opt_eeprom_bank,W				; get bank
+	andlw	b'11111110'						; keep only bits 7-1
+	tstfsz	WREG							; bank < 0x02 ?
+	return									; NO  - volatile option or illegal address, abort
+	tstfsz	opt_eeprom_bank					; YES - bank = 0 ?
+	bra		option_save_execute				;       NO  - address is valid
+	movlw	low(eeprom_options_storage-1)	;       YES - get start address of options storage minus 1
+	cpfsgt	opt_eeprom_index				;           - index >= start address ?
+	return									;             NO  - illegal address, abort
+	;bra	option_save_execute				;             YES - address is valid
+
+option_save_execute:
+	movff	opt_eeprom_index,EEADR			; set EEPROM index (address low  byte)
+	movff	opt_eeprom_bank, EEADRH			; set EEPROM page  (address high byte)
+
 	movf	opt_type,W						; get option type
-	xorlw	2								; option type is string ?
-	bz		option_save_string				; YES
-	movff	INDF1,EEDATA					; NO  - one byte to be saved to EEPROM
-	btfss	EEADRH,1						;     - EEADR:EEADRH < 512 ?
-	call	write_eeprom					;       YES  - write
-	return									;       (NO) - done
+	xorlw	.2								; option type = string ?
+	bz		option_save_string				; YES - special handling
+	movff	INDF1,EEDATA					; NO  - copy option value to EEPROM write register
+	call	write_eeprom					;     - execute write
+	return									;     - done
+
 option_save_string:
-	movff	POSTINC1,EEDATA					; write one byte
-	btfss	EEADRH,1						; EEADR:EEADRH < 512 ?
-	call	write_eeprom					; YES  - write
-	infsnz	EEADR,F							; (NO) - increment EEPROM address
-	incf	EEADRH,F						;      - ...
-	decfsz	opt_max							;      - decrement string length, done?
-	bra		option_save_string				;        NO  - loop
-	return									;        YES
+	movff	POSTINC1,EEDATA					; copy a character from the option value to the EEPROM write register
+	btfss	EEADRH,1						; current EEPROM address < 512 ?
+	call	write_eeprom					; YES  - execute write
+	infsnz	EEADR,F							; increment EEPROM address, low  byte
+	incf	EEADRH,F						; increment EEPROM address, high byte
+	decfsz	opt_max							; decrement string length, done?
+	bra		option_save_string				; NO  - loop
+	return									; YES - done
+
+
+;=============================================================================
+; Restore and check all option values from EEPROM
+;
+	global	option_restore_and_check_all	; restore options from EEPROM
+option_restore_and_check_all:
+	;---- Read option version from EEPROM
+	EEPROM_II_READ	eeprom_options_version,mpr
+
+	movlw	LOW(eeprom_opt_version)			; get options version from current firmware, low  byte
+	xorwf	mpr+0,W							; compare with EEPROM version, do they match?
+	bnz		option_restore_reset			; NO - reset to defaults of current firmware
+
+	movlw	HIGH(eeprom_opt_version)		; get options version from current firmware, high byte
+	xorwf	mpr+1,W							; compare with EEPROM version, do they match?
+	bnz		option_restore_reset			; NO  - reset to defaults of current firmware
+
+	;---- restore all option values
+	lfsr	FSR0,option_table_begin			; point to start of option definition table
+option_restore_all_loop:
+	rcall	option_restore_and_check		; restore and check the option
+	incfsz	opt_end_token,F					; was this the last option (was opt_end_token = 255) ?
+	bra		option_restore_all_loop			; NO  - do next option
+	return									; YES - done
+
+option_restore_reset:
+	call	option_reset_all				; reset all option values to their default
+	goto	option_check_and_store_all		; write back all option values to EEPROM (and return)
+
 
 ;=============================================================================
+; Restore an option value from EEPROM and check it
+;
+	global	option_restore_and_check
+option_restore_and_check:
+	rcall	option_read_definition			; read the option definition
 
-	global	option_restore_all				; restore options from EEPROM
-option_restore_all:
-	;---- Read option serial from EEPROM
-	movlw	LOW(eeprom_serial_save)
-	movwf	EEADR
-	movlw	HIGH(eeprom_serial_save)
-	movf	EEADRH
-	call	read_eeprom
-	movlw	LOW(eeprom_opt_serial)
-	xorwf	EEDATA,W
-	bnz		option_restore_bad				; auto reset if changed
-	incf	EEADR,F
-	call	read_eeprom
-	movlw	HIGH(eeprom_opt_serial)
-	xorwf	EEDATA,W
-	bz		option_restore_ok				; auto reset if changed
+	movf	opt_eeprom_bank,W				; get bank
+	andlw	b'11111110'						; keep only bits 7-1
+	tstfsz	WREG							; bank < 0x02 ?
+	bra		option_reset_loaded				; NO  - volatile option or illegal address, restore to default
+	tstfsz	opt_eeprom_bank					; YES - bank = 0 ?
+	bra		option_restore_execute			;       NO  - address is valid
+	movlw	low(eeprom_options_storage-1)	;       YES - get start address of options storage minus 1
+	cpfsgt	opt_eeprom_index				;           - index >= start address ?
+	bra		option_reset_loaded				;             NO  - illegal address, restore to default
+	;bra	option_restore_execute			;             YES - address is valid
 
-option_restore_bad:
-	call	option_reset_all				; reset RAM contains
-	goto	option_save_all					; then save to EEPROM
-
-	;---- Proper restore
-option_restore_ok:
-	movlw	LOW(option_table_begin)
-	movwf	FSR0L
-	movlw	HIGH(option_table_begin)
-	movwf	FSR0H
+option_restore_execute:
+	movff	opt_eeprom_index,EEADR			; set EEPROM index (address low  byte)
+	movff	opt_eeprom_bank, EEADRH			; set EEPROM page  (address high byte)
 
-option_restore_all_1:
-	movlw	LOW(option_table_end)
-	cpfseq	FSR0L
-	bra		option_restore_all_2			; not yet done...
-	movlw	HIGH(option_table_end)
-	cpfseq	FSR0H
-	bra		option_restore_all_2			; not yet done...
-	return									; all done
-option_restore_all_2:
-	rcall	option_restore					; Restore one option
-	bra		option_restore_all_1			; and loop
+	movf	opt_type,W						; get option type
+	xorlw	.2								; option type = string ?
+	bz		option_restore_string			; YES - special handling
+	call	read_eeprom						; NO  - execute read
+	movff	EEDATA,INDF1					;     - read option value from EEPROM read register
+	bcf		option_repaired					;     - clear option repaired flag
+	bra		option_check_loaded				;     - check if option value is within min/max, reset to default if not
+	btfsc	option_repaired					;     - was the option repaired?
+	bra		option_save_loaded_checked		;       YES - save repaired value to EEPROM
+	return									;       NO  - done
 
-option_restore:
-	rcall	option_read
-	incf	opt_eeprom,W					; shall we save it ?
-	btfsc	STATUS,Z						; EEPROM address is FFh ?
-	return									; YES - nothing to do.
-	movf	opt_eeprom,W					; compute backup address in EEPROM
-	addlw	LOW(eeprom_opt_backup)			; add offset
-	movwf	EEADR
-	movlw	HIGH(eeprom_opt_backup)
-	btfsc	STATUS,C						; > 256 ?
-	addlw	.1								; YES - +1
-	movwf	EEADRH
-	movf	opt_type,W						; get option type
-	xorlw	2								; Option type is string?
-	bz		option_restore_string			; YES
-	call	read_eeprom						; read one byte from EEPROM
-	movff	EEDATA, INDF1					; restore option register
-	return
 option_restore_string:
-	call	read_eeprom						; read one byte, and...
-	movff	EEDATA,POSTINC1					; ... restore it
-	infsnz	EEADR,F
-	incf	EEADRH,F
-	decfsz	opt_max							; decrement string length
-	bra		option_restore_string			; loop while not finished
-	return
+	call	read_eeprom						; read one character from the EEPROM
+	movff	EEDATA,POSTINC1					; copy it to the option value
+	infsnz	EEADR,F							; increment EEPROM address, low  byte
+	incf	EEADRH,F						; increment EEPROM address, high byte
+	decfsz	opt_max							; decrement string length, done?
+	bra		option_restore_string			; NO  - loop
+	return									; YES - done (nothing to check with strings)
+
+
 
 ;=============================================================================
-; Increment an option, based on type, and boundary
+; Read an option value via RS232
+; INPUT:  lo   = serial index
+; OUTPUT: hi   = option value
+;         WREG =0: option found and value valid, =1: option not found
+; TRASH:  TBLPTR, TABLAT, WREG, FSR0, FSR1
+;
+	global	option_read_serial
+option_read_serial:
+	lfsr	FSR0,option_table_begin			; point to start of option definition table
+option_read_serial_loop:
+	rcall	option_read_definition			; read option definition
+	movf	opt_serial,W					; get serial index of the option into WREG
+	xorwf	lo,W							; received index = index of this option ?
+	bz		option_read_serial_execute		; YES - read value
+	incfsz	opt_end_token,F					; NO  - was this the last option (was opt_end_token = 255) ?
+	bra		option_read_serial_loop			;       NO  - try next option
+	retlw	.1								;       YES - done, option not found
+
+option_read_serial_execute:
+	movff	INDF1,hi						; read option value into hi
+	retlw	.0								; done, option found
+
+
+;=============================================================================
+; Write an option value via RS232
+; INPUT:  lo   = serial index
+;         hi   = option value
+; OUTPUT: WREG =0: option found and value valid, =1: option not found, =2: value not valid
+; TRASH:  TBLPTR, TABLAT, WREG, FSR0, FSR1, up
+;
+	global	option_write_serial
+option_write_serial:
+	lfsr	FSR0,option_table_begin			; point to start of option definition table
+option_write_serial_loop:
+	rcall	option_read_definition			; read option definition
+	movf	opt_serial,W					; get serial index of the option into WREG
+	xorwf	lo,W							; received index = index of this option ?
+	bz		option_write_serial_execute		; YES - check and update value
+	incfsz	opt_end_token,F					; NO  - was this the last option (was opt_end_token = 255) ?
+	bra		option_write_serial_loop		;       NO  - try next option
+	retlw	.1								;       YES - done, option not found
+
+option_write_serial_execute:
+	movff	INDF1,up						; backup old value
+	movff	hi,INDF1						; write  new value
+	bcf		option_repaired					; clear option repaired flag
+	rcall	option_check_loaded				; check the new value
+	btfsc	option_repaired					; was   the new value valid?
+	bra		option_write_serial_execute_fail; NO  - restore old value
+	bsf		options_changed					; YES - flag that EEPROM needs to be updated
+	retlw	.0								;     - done, success
+option_write_serial_execute_fail:
+	movff	up,INDF1						; restore old value
+	retlw	.2								; done, value not valid
+
+
+;=============================================================================
+; Increment an option value based on type and min/max boundary
 ; INPUT:  FSR0 = option handle
 ; OUTPUT: none
 ; TRASH:  TBLPTR, TABLAT, WREG, FSR0, FSR1
@@ -372,28 +381,31 @@
 	global	option_inc						; increment FSR0 option
 option_inc:
 	; read type, default and register from table
-	rcall	option_read
+	rcall	option_read_definition
+
+	bsf		options_changed					; flag that EEPROM needs to be updated
 
 	; switch on type
-	movf	opt_type,W
-	bz		option_inc_uint8
-	dcfsnz	WREG
-	bra		option_inc_enum8
-	dcfsnz	WREG
-	bra		option_inc_string
+	movf	opt_type,W						; get option type
+	bz		option_inc_uint8				; type 0: INT8
+	dcfsnz	WREG							; decrement
+	bra		option_inc_enum8				; type 1: ENUM
+	dcfsnz	WREG							; decrement
+	bra		option_inc_string				; type 2: STRING
+	;bra	option_inc_uint8				; type 3: INT8
 
-option_inc_uint8:							; default type too...
-	movf	INDF1,W
-	addwf	opt_inc,W
-	cpfslt	opt_max
-	bra		option_inc_uint8_0
-	movf	opt_min,W
+option_inc_uint8:
+	movf	INDF1,W							; get option value
+	addwf	opt_inc,W						; add increment
+	cpfslt	opt_max							; max < option value ?
+	bra		option_inc_uint8_0				; NO  - new option value ok
+	movf	opt_min,W						; YES - reset to min value
 option_inc_uint8_0:
-	movwf	INDF1
+	movwf	INDF1							; store new value
 option_inc_uint8_1:
-	; Now some rather crude hack into this routine to make CCR Calibration more convenient:
-	movlw	.149							; EEPROM address of option CalGasO2
-	cpfseq	opt_eeprom						; editing CalGasO2 right now?
+	; now some rather crude hack into this routine to make CCR calibration more convenient:
+	movlw	0x37							; serial ID of option CalGasO2
+	cpfseq	opt_serial						; editing CalGasO2 right now?
 	bra		option_inc_uint8_2				; NO - check next option
 	movff	opt_dive_mode,WREG				; YES - get dive mode: 0=OC, 1=CC, 2=Gauge, 3=Apnea, 4=pSCR
 	decfsz	WREG,W							;     - in CCR mode?
@@ -405,8 +417,8 @@
 	movwf	INDF1							;                 - store it
 	return
 option_inc_uint8_2:
-	movlw	.12								; EEPROM address of option opt_GF_low
-	cpfseq	opt_eeprom						; editing opt_GF_low right now?
+	movlw	0x25							; serial ID of option opt_GF_low
+	cpfseq	opt_serial						; editing opt_GF_low right now?
 	bra		option_inc_uint8_3				; NO - check next option
 	movff	opt_GF_high,WREG				; get value of associated GF high into WREG
 	cpfsgt	INDF1							; GF low > GF high?
@@ -414,8 +426,8 @@
 	movff	opt_min,INDF1					; YES - wrap around to minimum value
 	return									;     - done
 option_inc_uint8_3:
-	movlw	.13								; EEPROM address of option opt_GF_high
-	cpfseq	opt_eeprom						; editing opt_GF_high right now?
+	movlw	0x26							; serial ID of option opt_GF_high
+	cpfseq	opt_serial						; editing opt_GF_high right now?
 	bra		option_inc_uint8_4				; NO - check next option
 	movff	opt_GF_low,WREG					; get value of associated GF low into WREG
 	cpfslt	INDF1							; GF high < GF low?
@@ -423,8 +435,8 @@
 	movwf	INDF1							; YES - rise GF high to GF low
 	return									;     - done
 option_inc_uint8_4:
-	movlw	.17								; EEPROM address of option opt_aGF_low
-	cpfseq	opt_eeprom						; editing opt_aGF_low right now?
+	movlw	0x27							; serial ID of option opt_aGF_low
+	cpfseq	opt_serial						; editing opt_aGF_low right now?
 	bra		option_inc_uint8_5				; NO - check next option
 	movff	opt_aGF_high,WREG				; get value of associated GF high into WREG
 	cpfsgt	INDF1							; GF low > GF high?
@@ -432,8 +444,8 @@
 	movff	opt_min,INDF1					; YES - wrap around to minimum value
 	return									;     - done
 option_inc_uint8_5:
-	movlw	.18								; EEPROM address of option opt_aGF_high
-	cpfseq	opt_eeprom						; editing opt_aGF_high right now?
+	movlw	0x28							; serial ID of option opt_aGF_high
+	cpfseq	opt_serial						; editing opt_aGF_high right now?
 	bra		option_inc_uint8_6				; NO - check next option
 	movff	opt_aGF_low,WREG				; get value of associated GF low into WREG
 	cpfslt	INDF1							; GF high < GF low?
@@ -444,16 +456,20 @@
 	return									; all done
 
 
-option_inc_enum8:							; always +1
-	incf	INDF1,W
-	cpfsgt	opt_max
-	clrf	WREG
-	movwf	INDF1
+option_inc_enum8:
+	movf	opt_max,W						; copy maximum permissible value to WREG
+	cpfslt	INDF1							; option value < maximum permissible value ?
+	bra		option_inc_enum8_reset			; NO  - reset     option value
+	incf	INDF1,F							; YES - increment option value
+	bra		option_inc_enum8_1				;     - continue
+option_inc_enum8_reset:
+	clrf	INDF1							; reset option value to zero
+
 option_inc_enum8_1:
  IFDEF _ccr_pscr
-	; Now some rather crude hack into this routine to unify CCR & pSCR mode setting
-	movlw	.25								; EEPROM address of option oCCRMode
-	cpfseq	opt_eeprom						; editing oCCRMode right now?
+	; now some rather crude hack into this routine to unify CCR & pSCR mode setting
+	movlw	0x1F							; serial ID of option oCCRMode
+	cpfseq	opt_serial						; editing oCCRMode right now?
 	bra		option_inc_enum8_2				; NO  - check next option
  IFDEF _external_sensor
 	btfsc	analog_o2_input					; YES - does hosting OSTC have an analog interface?
@@ -473,12 +489,26 @@
 option_inc_enum8_1_exit:
 	return									; done
  ENDIF	; _ccr_pscr
+
 option_inc_enum8_2:
-											; (unused)
+ IFDEF _gas_contingency
+	; now some rather crude hack to switch off contingency mode if gas needs calculation is switched off
+	movlw	0x5A							; serial ID of option opt_calc_gasvolume
+	cpfseq	opt_serial						; editing opt_calc_gasvolume right now?
+	bra		option_inc_enum8_3				; NO  - check next option
+	movf	INDF1,W							; YES - get option value
+;	xorlw	.0								;     - option value = off ?
+	bnz		option_inc_enum8_2_exit			;       NO  - done
+	clrf	WREG							;       YES - force contingency to be off, too
+	movff	WREG,opt_gas_contingency_dive	;           - ...
+option_inc_enum8_2_exit:
+	return
+ ENDIF	; _gas_contingency
+
 option_inc_enum8_3:
-	; Now some rather crude hack to correct opt_TR_mode in dependency of opt_dive_mode
-	movlw	.8								; EEPROM address of option opt_dive_mode
-	cpfseq	opt_eeprom						; editing opt_dive_mode right now?
+	; now some rather crude hack to correct opt_TR_mode in dependency of opt_dive_mode
+	movlw	0x20							; serial ID of option opt_dive_mode
+	cpfseq	opt_serial						; editing opt_dive_mode right now?
 	bra		option_inc_enum8_4				; NO  - check next option
 	movf	INDF1,W							; YES - get option value: 0=OC, 1=CCR, 2=Gauge, 3=Apnea, 4=pSCR
 	xorlw	.1								;       in CCR mode?
@@ -525,9 +555,9 @@
 
 option_inc_enum8_4:
  IFDEF _rx_functions
-	; Now some rather crude hack to advance opt_TR_mode in dependency of opt_dive_mode
-	movlw	.222							; EEPROM address of option opt_TR_mode
-	cpfseq	opt_eeprom						; editing opt_TR_mode right now?
+	; now some rather crude hack to advance opt_TR_mode in dependency of opt_dive_mode
+	movlw	0x7E							; serial ID of option opt_TR_mode
+	cpfseq	opt_serial						; editing opt_TR_mode right now?
 	bra		option_inc_enum8_5				; NO  - check next option
 	movff	opt_dive_mode,WREG				; YES - get dive mode: 0=OC, 1=CCR, 2=Gauge, 3=Apnea, 4=pSCR
 	decfsz	WREG,W							;       dive mode = 1 CCR?
@@ -545,7 +575,21 @@
 option_inc_enum8_4_exit:
 	return									; done
  ENDIF	; _rx_functions
+
 option_inc_enum8_5:
+ IFDEF _gas_contingency
+	; now some rather crude hack to keep contingency mode switched off if gas needs calculation is switched off
+	movlw	0x91							; serial ID of option opt_gas_contingency_dive
+	cpfseq	opt_serial						; editing opt_gas_contingency_dive right now?
+	bra		option_inc_enum8_6				; NO  - check next option
+	movff	opt_calc_gasvolume,WREG			; YES - get current setting of gas needs calculation
+	tstfsz	WREG							;     - gas needs calculation switched off?
+	return									;       NO  - done, opt_gas_contingency_dive may be switched on
+	clrf	INDF1							;       YES - force opt_gas_contingency_dive to off
+	return									;           - done
+ ENDIF	; _gas_contingency
+
+option_inc_enum8_6:
 	return
 
 
@@ -558,9 +602,8 @@
 	movff	opt_dive_mode,WREG				; get dive mode into WREG (0=OC, 1=CCR, 2=Gauge, 3=Apnea, 4=pSCR)
 	xorlw	.2								; in Gauge mode?
 	bnz		option_cleanup_gauge_1			; NO  - done
-	banksel	opt_dive_mode					; YES - setting not allowed, select options bank
-	clrf	opt_dive_mode					;     - reset to OC mode
-	banksel	common							;     - back to bank common
+	movff	WREG,opt_dive_mode				; YES - setting not allowed, WREG is zero -> reset to OC mode
+	bsf		options_changed					;     - flag that EEPROM needs to be updated
 option_cleanup_gauge_1:
 	return									; done
  ENDIF
@@ -578,7 +621,7 @@
 	banksel	opt_ccr_mode					; YES - select options bank
 	bcf		opt_ccr_mode,1					;     - clear bit 1 because opt_ccr_mode may only be 0 or 1 (reverts AutoSP to calculated SP, keeps sensor)
 	banksel	common							;     - back to bank common
-	bsf		option_repaired					;     - flag that an option was repaired
+	bsf		options_changed					;     - flag that EEPROM needs to be updated
 option_cleanup_oCCRMode_CCR:				; continue from above & jump-in from start.asm if known to be in CCR mode
  IFDEF _external_sensor
 	btfsc	analog_o2_input					; analog interface available?
@@ -593,7 +636,7 @@
 	banksel	opt_ccr_mode					;              YES - setting not allowed, select options bank
 	clrf	opt_ccr_mode					;                  - revert setting to 0 (fixed or calculated SP)
 	banksel	common							;                  - back to bank common
-	bsf		option_repaired					;                  - flag that an option was repaired
+	bsf		options_changed					;                  - flag that EEPROM needs to be updated
 	return									;                  - done
  ENDIF	; _ccr_pscr
 
@@ -611,7 +654,7 @@
 	movwf	mpr								;       YES - correct GF low to 100%
 option_cleanup_GF_1:
 	movff	mpr,opt_GF_low					; store corrected GF low
-	bsf		option_repaired					; flag that an option was repaired
+	bsf		options_changed					; flag that EEPROM needs to be updated
 option_cleanup_GF_2:
 	; cleanup alternative GF
 	movff	opt_aGF_high,WREG				; copy alternative GF high to WREG
@@ -625,7 +668,7 @@
 	movwf	mpr								;       YES - correct GF low to 100%
 option_cleanup_GF_3:
 	movff	mpr,opt_aGF_low					; store corrected GF low
-	bsf		option_repaired					; flag that an option was repaired
+	bsf		options_changed					; flag that EEPROM needs to be updated
 option_cleanup_GF_4:
 	return									; done
 
@@ -635,11 +678,11 @@
 ;
 	global	option_draw						; STRCAT FRS0 option
 option_draw:
-	; Read type, default and register from table
-	rcall	option_read
+	; read type, default and register from table
+	rcall	option_read_definition
 
-	; Switch on type
-	movf	opt_type,W
+	; switch on type
+	movf	opt_type,W						; get option type
 	bz		option_draw_uint8				; type0 = INT8
 	dcfsnz	WREG
 	bra		option_draw_enum8				; type1 = ENUM
@@ -647,7 +690,7 @@
 	bra		option_draw_string				; type2 = string
 	dcfsnz	WREG
 	bra		option_draw_uint8_depth			; type3 = INT8 with automatic display in meters or feet
-	return									; unknown, return
+	return									; unknown, do nothing
 
 option_draw_string:
 	movff	POSTINC1,POSTINC2
@@ -692,13 +735,15 @@
 
 ;---- Draw an enumerated value (set of translated strings)
 option_draw_enum8:
-	movff	INDF1,lo						; memorize current value
-	movf	INDF1,W							; copy current value to WREG
-	cpfsgt	opt_max							; max value (= highest usable value + 1) > current value?
-	clrf	WREG							; NO  - to avoid printing rubbish, reset to first value
+	movf	INDF1,W							; copy option value to WREG
+	movwf	lo								; memorize option value, too
+	cpfslt	opt_max							; option value > maximum permissible value ?
+	bra		option_draw_enum8_0				; NO  - option value allowed
+	clrf	WREG							; YES - to avoid printing rubbish, use first ENUM item instead
+option_draw_enum8_0:
 	addwf	WREG							; current value *= 2
 	addwf	opt_inc,W						; base text + 2 * current value
-	movwf	FSR1L							; load FSR0
+	movwf	FSR1L							; load FSR1
 	movlw	.0								; propagate carry...
 	addwfc	opt_min,W						; ...
 	movwf	FSR1H							; ...into FSR1
@@ -711,7 +756,6 @@
 	PUTC	"*"								; print "*"
 	return									; done
 
-
 ;-----------------------------------------------------------------------------
 
 	END