Index: trunk/Assembly_Library/Src/AssemblyLibrary.asm
===================================================================
--- trunk/Assembly_Library/Src/AssemblyLibrary.asm	(revision 592)
+++ trunk/Assembly_Library/Src/AssemblyLibrary.asm	(revision 593)
@@ -5,5 +5,5 @@
 ;
 ; XTIDE Universal BIOS and Associated Tools
-; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2013 by XTIDE Universal BIOS Team.
+; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2018 by XTIDE Universal BIOS Team.
 ;
 ; This program is free software; you can redistribute it and/or modify
@@ -45,4 +45,5 @@
 	%include "Drive.asm"
 	%include "FileIO.asm"
+	%include "InterruptHandlers.asm"
 %endif
 
@@ -134,4 +135,8 @@
 		%include "Sort.asm"
 	%endif
+
+%ifdef INCLUDE_CMOS_LIBRARY
+	%include "CMOS.asm"
+%endif
 %endif
 
Index: trunk/Assembly_Library/Src/Display/CgaSnow.asm
===================================================================
--- trunk/Assembly_Library/Src/Display/CgaSnow.asm	(revision 592)
+++ trunk/Assembly_Library/Src/Display/CgaSnow.asm	(revision 593)
@@ -37,5 +37,5 @@
 
 	; All standard CGA modes use 25 rows but only EGA and later store it to BDA.
-	cmp		BYTE [BDA.bVidRows], 25		; *FIXME* Shouldn't this be 24 (rows - 1)?
+	cmp		BYTE [BDA.bVidRows], 24		; BDA contains rows - 1
 	jae		SHORT .CgaNotFound
 	ret
Index: trunk/Assembly_Library/Src/File/DosCritical.asm
===================================================================
--- trunk/Assembly_Library/Src/File/DosCritical.asm	(revision 592)
+++ trunk/Assembly_Library/Src/File/DosCritical.asm	(revision 593)
@@ -45,8 +45,6 @@
 	push	ds
 
-	push	cs
-	pop		ds
-	mov		ax, (SET_INTERRUPT_VECTOR<<8) | DOS_CRITICAL_ERROR_HANDLER_24h
-	int		DOS_INTERRUPT_21h
+	mov		al, DOS_CRITICAL_ERROR_HANDLER_24h
+	call	HookInterruptVectorInALwithHandlerInCSDX
 
 	pop		ds
Index: trunk/Assembly_Library/Src/File/Drive.asm
===================================================================
--- trunk/Assembly_Library/Src/File/Drive.asm	(revision 592)
+++ trunk/Assembly_Library/Src/File/Drive.asm	(revision 593)
@@ -131,6 +131,12 @@
 .IsValidDriveNumberInDL:
 	push	ds
+	push	ax
+	cmp		dl, 1
+	jbe		SHORT .FloppyDrive
+
+.MessageSuppressedByInt2FhHandler:
+.MoreThanOneFloppyDrive:
+.NoFloppyDrive:
 	push	bx
-	push	ax
 
 	inc		dx			; Default drive is 00h and first drive is 01h
@@ -152,8 +158,27 @@
 	test	al, al
 
+	pop		bx
+.ReturnFromFloppyDriveFiltering:
 	pop		ax
-	pop		bx
 	pop		ds
 	ret
+
+.FloppyDrive:
+; On single-floppy-drive systems, both A: and B: will point to the same physical drive. The problem is that DOS will print a message telling the user
+; to "insert a disk and press any key to continue" when swapping from one logical drive to the other. To avoid this mess we hook interrupt 2Fh/AX=4A00h
+; to signal to DOS that we will handle this ourselves. However, this only works on DOS 5+ so on older DOS versions we instead try to filter out
+; the "other" logical drive (the one that isn't the current drive) during drive enumeration so the user can't select the "phantom" drive to begin with.
+; This will have the somewhat strange effect of having a drive B: but no drive A: if B: happens to be the current logical floppy drive.
+
+	cmp		BYTE [bDosVersionMajor], 5		; bDosVersionMajor must be provided by the application as it's not part of the library
+	jae		SHORT .MessageSuppressedByInt2FhHandler
+	LOAD_BDA_SEGMENT_TO ds, ax
+	mov		al, [BDA.wEquipment]
+	test	al, 0C0h
+	jnz		SHORT .MoreThanOneFloppyDrive	; No phantom drive so no need for any filtering
+	test	al, 1							; Any floppy drive at all?
+	jz		SHORT .NoFloppyDrive			; A pre-DOS 5 machine with no FDD is indeed a strange beast. However, don't trust the BIOS - let DOS decide
+	cmp		dl, [504h]						; MS-DOS - LOGICAL DRIVE FOR SINGLE-FLOPPY SYSTEM (A: / B:)
+	jmp		SHORT .ReturnFromFloppyDriveFiltering
 
 ;--------------------------------------------------------------------
Index: trunk/Assembly_Library/Src/File/InterruptHandlers.asm
===================================================================
--- trunk/Assembly_Library/Src/File/InterruptHandlers.asm	(revision 593)
+++ trunk/Assembly_Library/Src/File/InterruptHandlers.asm	(revision 593)
@@ -0,0 +1,162 @@
+; Project name	:	Assembly Library
+; Description	:	Interrupt handlers and related functions.
+
+;
+; XTIDE Universal BIOS and Associated Tools
+; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2013 by XTIDE Universal BIOS Team.
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+; GNU General Public License for more details.
+; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+;
+
+; Section containing code
+SECTION .text
+
+;--------------------------------------------------------------------
+; Int2Fh_Handler
+;	Parameters:
+;		Nothing
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+ALIGN JUMP_ALIGN
+Int2Fh_Handler:
+	cmp		ax, 4A00h			; DOS 5+ - FLOPPY-DISK LOGICAL DRIVE CHANGE NOTIFICATION
+	je		SHORT .CallMightBeForMe
+.CallIsNotForMe:
+	db		0EAh				; Far jump opcode
+.fpPreviousInt2FhHandler:
+	dd		0					; Pointer is filled in when handler is installed
+
+.CallMightBeForMe:
+	inc		cx
+	loop	.CallIsNotForMe
+	dec		cx
+ReturnFromNestedCall:
+	iret
+
+
+;--------------------------------------------------------------------
+; Int23h_Handler
+;	Parameters:
+;		Nothing
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+ALIGN JUMP_ALIGN
+Int23h_Handler:
+	cmp		BYTE [cs:Interrupt23hInProgress], 0
+	jne		SHORT ReturnFromNestedCall
+	inc		BYTE [cs:Interrupt23hInProgress]
+	push	ds
+	push	dx
+	push	ax
+	call	UnhookInterrupt2Fh
+	pop		ax
+	pop		dx
+	pop		ds
+	dec		BYTE [cs:Interrupt23hInProgress]	; Not really needed since we should not be called again
+	; Special return to terminate program - might not work under DR-DOS (see RBIL)
+	stc
+	retf
+
+Interrupt23hInProgress:		db		0
+
+
+;--------------------------------------------------------------------
+; HookInterrupt23h
+;	Parameters:
+;		Nothing
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AX, DX, DS
+;--------------------------------------------------------------------
+ALIGN JUMP_ALIGN
+HookInterrupt23h:
+	mov		al, DOS_CTRL_C_CTRL_BREAK_HANDLER_23h
+	mov		dx, Int23h_Handler
+	jmp		SHORT HookInterruptVectorInALwithHandlerInCSDX
+
+
+;--------------------------------------------------------------------
+; HookInterrupt2Fh
+;	Parameters:
+;		Nothing
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AX, BX, DX, DS, ES
+;--------------------------------------------------------------------
+ALIGN JUMP_ALIGN
+HookInterrupt2Fh:
+	mov		ax, GET_INTERRUPT_VECTOR << 8 | DOS_TSR_MULTIPLEX_INTERRUPT_2Fh
+	int		DOS_INTERRUPT_21h
+	mov		[cs:Int2Fh_Handler.fpPreviousInt2FhHandler], bx
+	mov		[cs:Int2Fh_Handler.fpPreviousInt2FhHandler+2], es
+	mov		dx, Int2Fh_Handler
+	jmp		SHORT HookInterruptVectorInALwithHandlerInCSDX
+
+
+;--------------------------------------------------------------------
+; UnhookInterrupt2Fh
+;	Parameters:
+;		Nothing
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AX, DX, DS
+;--------------------------------------------------------------------
+ALIGN JUMP_ALIGN
+UnhookInterrupt2Fh:
+	lds		dx, [cs:Int2Fh_Handler.fpPreviousInt2FhHandler]
+	mov		ax, ds
+	or		ax, dx
+	jz		SHORT NotHooked		; Only hooked on DOS 5+
+	mov		al, DOS_TSR_MULTIPLEX_INTERRUPT_2Fh
+	SKIP2B	f
+
+;--------------------------------------------------------------------
+; HookInterruptVectorInALwithHandlerInCSDX
+;	Parameters:
+;		AL:		Interrupt vector to hook
+;		DX:		Offset to handler
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AH, DS
+;--------------------------------------------------------------------
+HookInterruptVectorInALwithHandlerInCSDX:
+	push	cs
+	pop		ds
+	; Fall to HookInterruptVectorInALwithHandlerInDSDX
+
+;--------------------------------------------------------------------
+; HookInterruptVectorInALwithHandlerInDSDX
+;	Parameters:
+;		AL:		Interrupt vector to hook
+;		DX:		Offset to handler
+;		DS:		Segment of handler
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AH
+;--------------------------------------------------------------------
+HookInterruptVectorInALwithHandlerInDSDX:
+	mov		ah, SET_INTERRUPT_VECTOR
+	int		DOS_INTERRUPT_21h
+NotHooked:
+	ret
+
Index: trunk/Assembly_Library/Src/Menu/Dialog/Dialog.asm
===================================================================
--- trunk/Assembly_Library/Src/Menu/Dialog/Dialog.asm	(revision 592)
+++ trunk/Assembly_Library/Src/Menu/Dialog/Dialog.asm	(revision 593)
@@ -32,5 +32,5 @@
 ;		AX:		Selected item
 ;	Corrupts registers:
-;		BX, CX, DX, SI, DI
+;		BX, CX, DX, DI
 ;--------------------------------------------------------------------
 ALIGN JUMP_ALIGN
@@ -38,5 +38,5 @@
 	push	es
 	push	ds
-	mov		di, bp								; Backup parent MENU
+	mov		di, bp							; Backup parent MENU
 	mov		cx, DIALOG_size
 	eENTER_STRUCT cx
@@ -52,4 +52,5 @@
 
 	mov		ax, [bp+MENUINIT.wHighlightedItem]
+	mov		si, [bp+DIALOG.fpDialogIO]		; Restore SI
 	eLEAVE_STRUCT DIALOG_size
 	pop		ds
Index: trunk/Assembly_Library/Src/Menu/Dialog/DialogFile.asm
===================================================================
--- trunk/Assembly_Library/Src/Menu/Dialog/DialogFile.asm	(revision 592)
+++ trunk/Assembly_Library/Src/Menu/Dialog/DialogFile.asm	(revision 593)
@@ -32,11 +32,23 @@
 ;		Nothing
 ;	Corrupts registers:
-;		AX, BX, CX, DX, SI, DI
+;		AX, BX, CX, DX, DI
 ;--------------------------------------------------------------------
 ALIGN JUMP_ALIGN
 DialogFile_GetFileNameWithIoInDSSI:
+	; We need to store default drive because user might change drive but
+	; then cancel the file selection. In that case the original default directory
+	; must be restored.
+	call	Drive_GetDefaultToAL
+	push	ax
+
 	mov		bx, FileEventHandler
 	mov		BYTE [si+FILE_DIALOG_IO.bUserCancellation], TRUE
-	jmp		Dialog_DisplayWithDialogInputInDSSIandHandlerInBX
+	call	Dialog_DisplayWithDialogInputInDSSIandHandlerInBX
+
+	; Now restore the default drive if user cancellation
+	pop		dx
+	cmp		BYTE [si+FILE_DIALOG_IO.bUserCancellation], TRUE
+	je		Drive_SetDefaultFromDL
+	ret
 
 
Index: trunk/Assembly_Library/Src/Util/Bit.asm
===================================================================
--- trunk/Assembly_Library/Src/Util/Bit.asm	(revision 592)
+++ trunk/Assembly_Library/Src/Util/Bit.asm	(revision 593)
@@ -86,4 +86,7 @@
 	jb		SHORT Bit_SetToAXfromIndexInCL
 
+%ifdef USE_NEC_V
+	eSET1	dx, cl				; SET1 ignores bits 7...4 in CL
+%else
 	sub		cl, 16
 	xchg	ax, dx
@@ -91,4 +94,5 @@
 	xchg	dx, ax
 	add		cl, 16
+%endif
 	ret
 
@@ -106,4 +110,7 @@
 ALIGN JUMP_ALIGN
 Bit_SetToAXfromIndexInCL:
+%ifdef USE_NEC_V
+	eSET1	ax, cl
+%else
 	push	dx
 
@@ -113,4 +120,5 @@
 
 	pop		dx
+%endif
 	ret
 
Index: trunk/Assembly_Library/Src/Util/CMOS.asm
===================================================================
--- trunk/Assembly_Library/Src/Util/CMOS.asm	(revision 593)
+++ trunk/Assembly_Library/Src/Util/CMOS.asm	(revision 593)
@@ -0,0 +1,142 @@
+; Project name	:	Assembly Library
+; Description	:	Functions for accessing CMOS.
+
+;
+; XTIDE Universal BIOS and Associated Tools
+; Copyright (C) 2009-2010 by Tomi Tilli, 2011-2018 by XTIDE Universal BIOS Team.
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation; either version 2 of the License, or
+; (at your option) any later version.
+;
+; This program is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+; GNU General Public License for more details.
+; Visit http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+;
+
+
+;--------------------------------------------------------------------
+; CMOS_WriteALtoIndexInDL
+;	Parameters:
+;		AL:		Byte to write
+;		DL:		CMOS address to write to
+;	Returns:
+;		Interrupts enabled and NMI disabled
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+CMOS_WriteALtoIndexInDL:
+	push	ax
+	call	SetDLtoIndexRegister
+	pop		ax
+	out		CMOS_DATA_REGISTER, al
+	sti
+	ret
+
+
+;--------------------------------------------------------------------
+; CMOS_ReadFromIndexInDLtoAL
+;	Parameters:
+;		DL:		CMOS address to read from
+;	Returns:
+;		AL:		Byte read from CMOS
+;		Interrupts enabled and NMI disabled
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+CMOS_ReadFromIndexInDLtoAL:
+	call	SetDLtoIndexRegister
+	in		al, CMOS_DATA_REGISTER
+	sti
+	ret
+
+
+;--------------------------------------------------------------------
+; SetDLtoIndexRegister
+;	Parameters:
+;		DL:		CMOS address to select
+;	Returns:
+;		Interrupts disabled and NMI disabled
+;	Corrupts registers:
+;		AL
+;--------------------------------------------------------------------
+SetDLtoIndexRegister:
+	mov		al, dl
+	or		al, FLG_CMOS_INDEX_NMI_DISABLE	; Disable NMI
+	cli
+	out		CMOS_INDEX_REGISTER, al
+	ret		; Return works as I/O delay
+
+
+;--------------------------------------------------------------------
+; CMOS_Verify10hTo2Dh
+;	Parameters:
+;		Nothing
+;	Returns:
+;		ZF:		Set if valid checksum
+;		Interrupts disabled and NMI disabled
+;	Corrupts registers:
+;		AX, CX, DX
+;--------------------------------------------------------------------
+CMOS_Verify10hTo2Dh:
+	; Get checksum WORD from CMOS
+	mov		dl, CHECKSUM_OF_BYTES_10hTo2Dh_HIGH
+	call	CMOS_ReadFromIndexInDLtoAL
+	xchg	ah, al
+	inc		dx			; CHECKSUM_OF_BYTES_10hTo2Dh_LOW
+	call	CMOS_ReadFromIndexInDLtoAL
+	push	ax			; Store checksum word
+
+	; Verify checksum
+	call	GetSumOfBytes10hto2DhtoCX
+	pop		ax
+	cmp		ax, cx		; ZF set if checksum verified
+	ret
+
+
+;--------------------------------------------------------------------
+; CMOS_StoreNewChecksumFor10hto2Dh
+;	Parameters:
+;		Nothing
+;	Returns:
+;		Interrupts disabled and NMI disabled
+;	Corrupts registers:
+;		AX, CX, DX
+;--------------------------------------------------------------------
+CMOS_StoreNewChecksumFor10hto2Dh:
+	call	GetSumOfBytes10hto2DhtoCX
+
+	; Write it to CMOS
+	xchg	ax, cx
+	mov		dl, CHECKSUM_OF_BYTES_10hTo2Dh_LOW
+	call	CMOS_WriteALtoIndexInDL
+	xchg	al, ah
+	dec		dx			; CHECKSUM_OF_BYTES_10hTo2Dh_HIGH
+	jmp		SHORT CMOS_WriteALtoIndexInDL
+
+
+;--------------------------------------------------------------------
+; GetSumOfBytes10hto2DhtoCX
+;	Parameters:
+;		Nothing
+;	Returns:
+;		CX:		Sum of CMOS bytes 10h to 2Dh
+;		Interrupts disabled and NMI disabled
+;	Corrupts registers:
+;		AX, DX
+;--------------------------------------------------------------------
+GetSumOfBytes10hto2DhtoCX:
+	xor		cx, cx			; Sum
+	mov		dx, 10h			; First index
+
+.AddNextByte:
+	call	CMOS_ReadFromIndexInDLtoAL
+	xor		ah, ah
+	add		cx, ax
+	inc		dx
+	cmp		dl, 2Dh			; Last index
+	jbe		.AddNextByte
+	ret
