;    File              : $MISC.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;    $Log$
;    MISC.A86 1.29 94/11/30 14:40:17
;    fixed error return for function 6602
;    MISC.A86 1.28 94/07/13 15:31:04 
;    Pass name to share on Int21/5D02 (close file by name)
;    MISC.A86 1.27 93/11/29 14:18:16
;    Fix bug in get extended country info if not current country
;    MISC.A86 1.25 93/11/19 17:00:14
;    If 21/38 get country info fails for current codepage, try any codepage
;    MISC.A86 1.24 93/11/16 15:57:08
;    int 21/5D06 clears CY flag 
;    MISC.A86 1.23 93/10/21 19:31:16
;    Move Int 21/5D03+5D04 support (close all files by machine/psp) into share
;    MISC.A86 1.22 93/10/18 17:41:45
;    fix for >255 open files (PNW Server)
;    MISC.A86 1.21 93/09/03 20:28:57
;    Add intl/dbcs support for int 21/6523 (query yes/no char)
;    MISC.A86 1.20 93/07/26 20:42:13
;    Seperate int21/3306 
;    MISC.A86 1.12 93/03/05 18:12:33
;    Fix DS corruption for NLSFUNC calls
;    ENDLOG
;

VALID_SIG	equ	0EDC1h

	include	pcmode.equ
	include	i:msdos.equ
	include i:mserror.equ
	include i:psp.def
	include	i:driver.equ
	include	i:char.def
	include	i:country.def
	include	i:doshndl.def
	include	i:redir.equ
	include	i:fdos.equ

NLSFUNC	equ	TRUE

;
GLOBAL_DATA	DSEG	WORD
	extrn	default_country:byte
	extrn	Ucasetbl:word
	extrn	FileUcasetbl:word
	extrn	FileCharstbl:word
	extrn	Collatingtbl:word
	extrn	DBCS_tbl:word
	extrn	NoYesChars:byte
	extrn	info1_len:abs
	extrn	info2_len:abs
	extrn	info4_len:abs
	extrn	info5_len:abs
	extrn	info6_len:abs
	extrn	info7_len:abs
	extrn	dos_version:word
	extrn	country_filename:byte


PCM_CODE	CSEG	BYTE
	extrn	dbcs_lead:near
	extrn	device_write:near	; Write to a Character Device
	extrn	device_read:near	; Read from a Character Device
	extrn	dos_entry:near
	extrn	error_exit:near
	extrn	error_ret:near
	extrn	func5F_common:near
	extrn	get_dseg:near
	extrn	valid_drive:near
	extrn	ifn2dhndl:near
	extrn	int21_func:near
	extrn	invalid_function:near
	extrn	patch_version:word
	extrn	reload_registers:near
	extrn	reload_ES:near
	extrn	return_AX_CLC:near
	extrn	return_BX:near
	extrn	return_CX:near
	extrn	return_DX:near
	extrn	toupper:near
	extrn	xlat_xlat:word

;
;	*****************************
;	***    DOS Function 34    ***
;	***   Get The Indos Flag  ***
;	*****************************
	Public	func34
func34:
	mov	bx,offset indos_flag
;	jmp	return_DSBX_as_ESBX	; return ES:BX -> indos flag

	Public	return_DSBX_as_ESBX

return_DSBX_as_ESBX:
;-------------------
; On Entry:
;	DS:BX to be returned to caller in ES:BX
; On Exit:
;	ES/DI trashed
;
	les	di,ss:int21regs_ptr
	mov	es:reg_ES[di],ds
	mov	es:reg_BX[di],bx
	ret


;
;	*****************************
;	***    DOS Function 52    ***
;	***   Get Internal Data   ***
;	*****************************
;
;
	Public	func52
func52:
	mov	bx,offset func52_data
	jmps	return_DSBX_as_ESBX	; return ES:BX -> internal data

;
;	*****************************
;	***    DOS Function 5D    ***
;	***    Private DOS Func   ***
;	*****************************
;
;
	Public	func5D
func5D:
if DOS5
	cmp	al,7
	 jae	f5d_05
	mov	si,dx				; ES:SI -> callers structure
	mov	bx,es:word ptr 20[si]
	mov	ss:owning_psp,bx		; update PSP from there
	cmp	WindowsHandleCheck,26h
	 jne	f5D_05				; check if Windows is running
	mov	bx,es:word ptr 18[si]
	mov	ss:machine_id,bx		; no, update machine_id
f5D_05:
else
	push	ds
	push es ! pop ds			; restore users DS
	callf	ss:win386_remote_machine
	pop	ds
endif
	cbw					; zero AH for valid functions
	xchg	ax,bx				; sub function in BX
	
	cmp	bx,func5D_ftl			; Assume Illegal Subfunction
	 jb	f5D_10				; check that it is a valid
	mov	bx,func5D_ftl			;  subfunction
f5D_10:
	shl	bx,1
	push	func5D_ft[bx]			; save function address
	shr	bx,1
	xchg	ax,bx				; restore BX
	ret					; go to function
	
f5D_msnet:
	mov	ax,I2F_REDIR_5D			; magic number to redirect
	jmp	func5F_common			;  calls to extentions
;
;
;	The registers for some of the following sub-functions are passed
;	in the following structure.
;
RDC_AX		equ	es:word ptr 00[di]	; User AX
RDC_BX		equ	es:word ptr 02[di]	; User BX
RDC_CX		equ	es:word ptr 04[di]	; User CX
RDC_DX		equ	es:word ptr 06[di]	; User DX
RDC_SI		equ	es:word ptr 08[di]	; User SI
RDC_DI		equ	es:word ptr 10[di]	; User DI
RDC_DS		equ	es:word ptr 12[di]	; User DS
RDC_ES		equ	es:word ptr 14[di]	; User ES
RDC_RES		equ	es:word ptr 16[di]	; Remote Machine ID (High Word)
RDC_UID		equ	es:word ptr 18[di]	; Remote Machine ID (Low Word)
RDC_PID		equ	es:word ptr 20[di]	; Process ID
;
;	*****************************
;	***   DOS Function 5D00   ***
;	***   Remote DOS Call     ***
;	*****************************
;
;
f5D00:
	mov	remote_call,0ffh	; set remote op flag
	push	es
	les	di,int21regs_ptr	; stack image the copy registers
	pop	ds
	mov	cx,6
	rep	movsw			; Copy AX, BX, CX, DX, SI, DI
	inc di ! inc di			; Skip BP in the destination
	movsw ! movsw			; finally copy DS and ES
	push ss ! pop ds		; DS -> PCMDSEG
	call	reload_registers	; load up the new registers
	jmp	int21_func		;  then execute that function

;	*****************************
;	***   DOS Function 5D01   ***
;	***    Commit all files   ***
;	*****************************
;
f5D01:
;-----
; We are being asked to commit all files to disk
; By pretending to be FCB's we force use of IFN's, and we commit all possible
; files in the range 0-255, ignoring errors from unopened handles.
;
	mov	remote_call,DHM_FCB	; pretend to be FCB, forces use of IFN
	xor	bx,bx			; start with handle zero
f5D01_10:
	mov	ah,MS_X_COMMIT
	call	dos_entry		; commit this file
	inc	bx			; onto next candidate
	cmp	bx,0FFh			; runout yet ?
	 jb	f5D01_10		;
	ret


;	*****************************
;	***   DOS Function 5D02   ***
;	***   Close file by name  ***
;	*****************************
;
f5D02:
;-----
; Close file by name. We do a search first to find out about the file.
; As long as it's local we call share with the directory cluster/index
; to close it if it's open.
;
	mov	di,dx			; ES:DI -> register block
	mov	dx,RDC_DX
	mov	es,RDC_DS		; ES:DX -> filename to close
	push	dma_offset
	push	dma_segment
	mov	dma_offset,offset fcb_search_buf
	mov	dma_segment,ds
	mov	cx,DA_RO+DA_SYSTEM+DA_HIDDEN
	mov	ah,MS_X_FIRST		; look for this file
	call	dos_entry		;  to get dir entry info
	pop	dma_segment
	pop	dma_offset
	 jc	f5Dret
	mov	al,fcb_search_buf	; get drive #
	test	al,al			; reject networked drives
	 js	f5Dret
	dec	ax			; make drive zero based
	mov	cx,word ptr fcb_search_buf+0Dh
					; CX = directory count
	mov	dx,word ptr fcb_search_buf+0Eh
					; DX = parent directory cluster
	lea	bx,fcb_search_buf+1	; DS:BX -> name
	mov	di,S_CLOSE_IF_OPEN
;	jmps	f5D_common
f5D_common:	
	callf	lock_tables		; protect SHARE with a critical section
	callf	share_stub[di]
	callf	unlock_tables		; safe again
f5Dret:
	ret

;	*****************************
;	***   DOS Function 5D03   ***
;	*** Close files by machine***
;	*****************************
;
;	*****************************
;	***   DOS Function 5D04   ***
;	*** Close by Machine/PSP  ***
;	*****************************
f5D03:
f5D04:
;-----
; Close all files for given PSP
;
	mov	di,S_CLOSE_FILES
	jmps	f5D_common


;	*****************************
;	***   DOS Function 5D05   ***
;	*****************************
;
f5D05:
; On Entry:
;	RDC.BX = Share File # to look to
;	RDC.CX = Share Record # to look for
; On Exit:
;	AX = DOSHNDL Attribute Word
;	BX = machine ID
;	CX = locked blocks count
;	ES:DI -> buffer containing full pathname
;
;
	callf	lock_tables		; protect SHARE with a critical section
	callf	share_stub+S_GET_LIST_ENTRY
	callf	unlock_tables		; safe again
	 jnc	f5Dret			; just return if it went OK
	jmp	error_ret		;  else return error code in AX


;	*****************************
;	***   DOS Function 5D06   ***
;	***   Get Internal Data   ***
;	*****************************
;
f5D06:
	mov	cx,offset swap_indos		; Calculate Size of Swap
	sub	cx,offset internal_data		; Swap INDOS Array
	call	return_CX			; CX = Swap Indos length
	mov	dx,offset swap_always		; Calculate Size of Swap
	sub	dx,offset internal_data		; Swap ALWAYS Array
	call	return_DX			; DX = Swap Always length
	mov	si,offset internal_data
	call	return_DSSI
	mov	ax,5D06h
	jmp	return_AX_CLC


;
;	*****************************
;	***   DOS Function 5D0A   ***
;	***Set Extended Error Info***
;	*****************************
;
f5D0A:
	mov	si,dx			; ES:SI -> parameter block
	lods	es:word ptr [si]
	mov	error_code,ax		; copy the appropriate fields
	lods	es:word ptr [si]
	mov	error_class,ah
	mov	error_action,al
	lods	es:word ptr [si]
	mov	error_locus,ah
	add	si,2*WORD		; skip to device
	lods	es:word ptr [si]
	mov	error_dev,ax
	lods	es:word ptr [si]
	mov	error_dev+2,ax
	xor	ax,ax			; return AL=0
	ret
;
;	*****************************
;	*** Get switch character  ***
;	*****************************
;
;
	Public	func37
func37:
	cmp al,1 ! jb f37_getswitch	; Get the current Switch Character
		   je f37_setswitch	; Set the Switch Character
	cmp al,3 ! je f37_s03		; Sub-Func 03 Return Unchanged
	mov dl,0ffh ! jb f37_return_DX	; Sub-Func 02 Return DL == 0FFh
	mov	al,0FFh			; else invalid sub-function
f37_s03:
	ret

f37_getswitch:
	mov	dl,switch_char
f37_return_DX:
	jmp	return_DX		; return current setting in DX

f37_setswitch:
	mov	switch_char,dl
	ret

;**************************************************
;**************************************************
;***						***
;***	  Miscellaneous Isolated Functions	***
;***						***
;**************************************************
;**************************************************
;
;	*****************************
;	***    DOS Function 30    ***
;	***   Get Version Number  ***
;	*****************************

	Public	func30
func30:
if DOS5
	mov	es,current_psp		; version is kept in the PSP
	mov	ax,PSP_VERSION
else
	mov	ax,dos_version		; version returned in AX
endif
ReturnVersionNumber:
	xor	bx,bx			; zero BX and CX
	xor	cx,cx
	call	return_BX
	call	return_CX
	jmp	return_AX_CLC

;
;	*****************************
;	***    DOS Function 33    ***
;	***  Get/Set Cntl-Break   ***
;	*****************************
	Public	func33

; WARNING - called on USER stack

func33:
	cmp	al,2			; range check subfunction
	 jbe	f33_10
	mov	dl,bootDrv		; assume we want boot drive
	cmp	al,5			; did we ?
	 je	f33_30
if DOS5
	cmp	al,6			; get true version ?
	 je	f33_60
endif
	mov	reg_AL[bp],0FFh		; return AL = FF
	ret				; Illegal function request
f33_10:
	and	dl,01h			; force a valid value
	cmp	al,1			; check for get or set
	 jae	f33_20
	mov	dl,break_flag		; it's a get, so use existing setting
f33_20:
	xchg	dl,break_flag		; replace current setting
	 je	f33_40
f33_30:
	mov	reg_DL[bp],dl		; return setting in DL
f33_40:
	ret

if DOS5
f33_60:
	mov	reg_BX[bp],TRUE_VERSION	; return version number
	mov	ax,patch_version
	mov	reg_DX[bp],ax		; return revision+HMA
	ret
endif


;
;	*****************************
;	***    DOS Function 25    ***
;	***  Set Interrupt Vector ***
;	*****************************
;

; WARNING - use no stack as device drivers have called us re-entrantly

	Public	func25
func25:
	mov	bx,es			; is really dos_DS - save for later
	xor	di,di			; replace with the values in dos_DS:DX
	mov	es,di			; es -> zero segment
	xor	ah,ah			; the interrupt number
	mov	di,ax			; 0:di -> vector
	shl di,1 ! shl di,1		; 4 bytes per vector

	cli
	xchg	ax,dx			; Get New Offset
	stosw				; and Save
	xchg	ax,bx			; Get New Segment
	stosw				; and Save
	sti
	xchg	ax,dx			; recover entry AL to (preserve it)
	ret

;	*****************************
;	***    DOS Function 35    ***
;	***  Get Interrupt Vector ***
;	*****************************

; WARNING - use no stack as device drivers have called us re-entrantly

	Public	func35
func35:
	xor	bx,bx
	mov	ds,bx			; DS:0 -> vector table
	mov	bl,al			; BX = the interrupt number
	shl bx,1 ! shl bx,1		; 4 bytes per vector
	lds	bx,ds:dword ptr [bx]	; DS:BX -> vector
	les	di,ss:int21regs_ptr
	mov	es:reg_BX[di],bx
	mov	es:reg_ES[di],ds
	jmp	get_dseg		; restore DS for return

;PC-DOS	Verify and Break Flags Support
;
;	*****************************
;	***    DOS Function 2E    ***
;	***    Set/Reset Verify   ***
;	*****************************
	Public	func2E
func2E:
	and	al,1			; only use bottom bit
	mov	verify_flag,al		; store for use later
	ret

;	*****************************
;	***    DOS Function 54    ***
;	***  Get Verify Setting   ***
;	*****************************
	Public	func54
func54:
	mov	al,verify_flag		; return verify flag
	ret

;	*****************************
;	***    DOS Function 63    ***
;	***  Get Lead Byte Table  ***
;	*****************************
;


	Public	func63
func63:
	cmp	al, 1			; subfunction #?
	 jb	f63_get_tbl		; subfunction 0
	 je	f63_set_flg		; subfunction 1
	cmp	al, 2			; subfunction 2?
	 je	f63_get_flg		;  yes

	mov	ax, ED_FUNCTION		; invalid subfunction number
	jmp	error_exit		; so quit


f63_get_flg:
; Get the current state of the DOS interim character console flag.
; If this flag is set int 21h functions 07h, 08h, 0Bh, 0Ch are supposed
; to return "interim character information" which I assume is incomplete
; characters. (In languages like Korean a given double byte character
; may be built by the user entering several keystrokes which form
; incomplete characters.)
	mov	es, current_psp
	mov	dl, PSP_RIC		; Return Interim Character flag
	jmp	return_DX		; flag returned in dl


f63_set_flg:
; Set the current state of the DOS interim character console flag.
; dos_DL = 0 - clear flag, dos_DL = 1 - set flag
	mov	es, current_psp
	mov	PSP_RIC, dl		; record flag
	ret


f63_get_tbl:
; Get the current DBCS table address.
	mov	si,offset DBCS_tbl+2; skip the table size entry
;	jmp	return_DSSI

return_DSSI:
;-----------
; On Entry:
;	DS:SI to be returned to caller
; On Exit:
;	AX preserved
;
	les	di,ss:int21regs_ptr
	mov	es:reg_DS[di],ds
	mov	es:reg_SI[di],si
	ret

;
;	*****************************
;	***    DOS Function 29    ***
;	***     Parse String      ***
;	*****************************
	Public	func29
func29:
;
; Entry:
;    DS:SI  ->	line to parse
;    ES:DI  ->	resulting fcb
; Exit:
;    DS:SI  ->	terminating delimeter past parsed filename
;    ES:DI  ->	filled in fcb (Affects 16 bytes:  DnnnnnnnnTTT0000)
;
	push	ds
	push	es
	call	reload_ES
	pop	ds
	call	parse
	call	return_DSSI		; return result of parse
	pop	ds
	mov	al,dh			; return result in AL
	ret

;-----
parse:	; parse DOS filename delimited by TAB,SPACE,or .,+:;=|"/\[]<> or ctrl
;-----
; Entry:
;    DS:SI  ->	line to parse
;    ES:DI  ->	fcb to parse into
;	AL  ==	bit options:
;		Bit 0 == 1:  scan off leading delimiters
;		Bit 1 == 1:  change drive only if specified
;		Bit 2 == 1:  change name   "	"     "
;		Bit 3 == 1:  change type   "	"     "
; Exit:
;    DS:SI  ->	terminating delimeter past parsed filename
;    ES:DI  ->	filled in fcb (Affects 16 bytes:  DnnnnnnnnTTT0000)
;	DH  ==	1 if wild, 0FFh if the drive is invalid, 0 otherwise
;
	push	di			; (<--keep DI last on stack)

	cld				; ChSh
	xor	dx,dx			; DH = default return value (0)
	xchg	al,dl			; put flags into DL, AL = 0

	mov	cx,1
	test	dl,0010b		; should we initialize drive?
	call	nz_store_al		; do conditional store

	mov	al,' '			; use spaces for filename & typ

	mov	cl,8
	test	dl,0100b		; should we initialize the name?
	call	nz_store_al

	mov	cl,3
	test	dl,1000b		; should we initialize the typ?
	call	nz_store_al

	xor	ax,ax			; zero-out the 4 post-typ bytes
	stos	ax
	stos	ax

	pop di ! push di		; restore DI to start of FCB

deblank_loop:
	lods	al			; grab char
	cmp	al,' '			; is it a blank?
	 je	deblank_loop		;  Y: keep looping
	cmp	al,'I'-'@'		; is it a tab?
	 je	deblank_loop		;  Y: keep looping

	test	dl,0001b		; skip-delimiter-bit set?
	 jz	parse_drive		;  N: go start parsing

skip_delim_loop:
	call	check_delimiters	; check AL for delimiterness
	 jb	parse_dec_ret		; found terminator, dec SI & leave
	mov	dl,dh			; flag no-more-delimiter-skip (DL = 0)
	 je 	deblank_loop		; found separator, go deblank after it

parse_drive:
	dec	si
	cmp	byte ptr 1[si],':'	; is the drive specified?
	 jne	parse_name
	lods	ax			; get drive, junk colon
	and	al,01011111b		; upper case it
	sub	al,'@'			; AL = 1-relative drive #
	push ax ! push ds		; Save the drive code and call
	call	get_dseg		; Restore our Data Segment
	call	valid_drive		; routine to validate drive ZR == OK
	pop ds ! pop ax			; Restore drive code and User DS
	 jz	parse_d10
	dec	dh			; flag drive error (0FFh)
parse_d10:
	mov	es:[di],al		; insert drive in fcb

parse_name:
	inc	di			; DI -> fname
	mov	cx,8
	call	parse_item		; parse an up-to 8 char filename
	cmp	dl,'.'			; was the delimeter a '.'?
	 jne	parse_dec_ret		;  N:  the parse is complete

	mov	cl,3
	call	parse_item		; parse an up-to 3 char filetype
parse_dec_ret:
	dec	si			; bump SI back to point to delimeter

parse_ret:
	pop	di
	ret


;-----------
nz_store_al:
;-----------
; Entry:
;	DI  ->	destination to conditionally initialize
;	CX  ==	length of destination
;	AL  ==	byte to initialize with
;	ZF set: do not initialize destination
; Exit:
;	DI  ->	past destination (initial DI+CX)
; Changed:
;	CX,DI
;
	 jnz	skip_store		; should we initialize?
	rep	stosb			;  Y: store them bytes
skip_store:
	add	di,cx			; bump DI to post-dest position
	ret


;==========
parse_item:		; Parses item into fcb if item is specified
;==========
;
; Entry:
;	SI  ->	item to parse (name or type)
;	DI  ->	fcb area to parse it into
;	CX  ==	length of item
;	DH  ==	parse_return
; Exit:
;	SI  ->	past ending delimeter
;	DI  ->	past fcb area (initial DI + CX)
;	CH  ==	If zero on enter zero on exit else 0-255
;	DH  ==	updated parse_return w/possible wild bit set
;	DL  ==	character delimiter @(SI-1)
; Changed:
;	AX,CX,DX,SI,DI
;

	mov	ah,FALSE		; specified item flag
parse_item_loop:
	lods	al			; get char
	call	check_delimiters	; is it a delimiter?
	jbe	pi_pad_ret		;  Y:  the parse is complete
	jcxz	parse_item_loop		; if the name is full, skip the char
	mov	ah,al			; flag name as present
	cmp	al,'?'			; is it a single wild char?
	je	pi_set_wild		;  Y: set wild flag
	cmp	al,'*'			; is it a multi-char wild card?
	jne	pi_store		;  N: store it
	mov	al,'?'			;  Y: fill with '?' to end of name
	rep	stosb
pi_set_wild:
	or	dh,1			; set wild flag
	jcxz	parse_item_loop		; skip store if name is now filled
pi_store:
	dec	cx			; another char done
	call	dbcs_lead		; is it the 1st byte of kanji ?
	 jne	pi_store10
	inc	si			; skip 2nd byte
	 jcxz	parse_item_loop		; can I copy both ?
	dec	cx			; yes, do so
	stosb
	dec	si			; point at second byte
	lodsb				;  so we can copy it too..
pi_store10:
	stos	al			; put the char in the fcb
	jmps	parse_item_loop
pi_pad_ret:
	mov	dl,al			; DL = ending delimeter
	or	ah,ah			; the the item specified?
	 jz	pi_ret			;  N: skip padding
	mov	al,' '			;  Y: pad to end with spaces
	rep	stosb
pi_ret:
	add	di,cx			; bump DI out to end
	ret


;----------------
check_delimiters:
;----------------
;
; Entry:
;	AL  ==	char to check in list of delimiters
; Exit:
;	AL  ==	char changed to uppercase
;	CF set if it is one of the terminators:  |"/\[]<>  & ctrl chars != TAB
;	ZF set if it is one of the separators:   .,+:;=    SPACE & TAB
;	       OR one of the non-ctrl terminators
;

	cmp	al,'a'			; check for lower case
	jb	not_lower
	cmp	al,'z'
	ja	not_lower
	and	al,01011111b		; uppercase it, CF clear, ZF clear
	ret
not_lower:
	push	cx ! push di ! push es
	push	cs ! pop  es		; ES = Code segment
	mov	di,offset parse_separators
	mov	cx,length parse_separators
	repne	scasb			; is AL a separator?
	je	cpd_pop_ret		;  Y: return ZF set
	mov	cl,length parse_terminators
	repne	scasb			; is AL a terminator?
	 stc				;  (set CF if true)
	je	cpd_pop_ret		;  Y: return CF & ZF set
	cmp	al,' '			; (AL == ' ') ZF set, (AL < ' ') CF set
cpd_pop_ret:
	pop	es ! pop  di ! pop  cx
	ret

;
;	*****************************
;	***    DOS Function 2A    ***
;	***  Get Current Date     ***
;	*****************************
	Public	func2A
func2A:
;
;	entry:	None
;
;	exit:	cx = year (1980-2099)
;		dh = month (1-12)
;		dl = day (1-31)
;		al = DOS returns day of week here
;
	call	ReadTimeAndDate		; Get the current Time and Date
	mov	cx,1980
	add	cx,yearsSince1980
;	mov	dl,dayOfMonth
;	mov	ah,month
	mov	dx,word ptr dayOfMonth
	mov	al,dayOfWeek
	jmps	f2C_10			; exit via common routine

;	*****************************
;	***    DOS Function 2C    ***
;	***  Get Current Time     ***
;	*****************************
	Public	func2C
func2C:
;
;	entry:	None
;
;	exit:	ch = hours (0-23)
;		cl = minutes (0-59)
;		dh = seconds (0-59)
;		dl = Hunredths seconds (0-99)
;		al = 0
;
	call	ReadTimeAndDate		; Get the current Time and Date
	mov	cx,biosDate+2		; Get the Hour and Minute
	mov	dx,biosDate+4		; Get the Seconds and Hundredths
	xor	ax,ax			; return AL = 0
f2C_10:
	call	return_CX		; and return to caller
	jmp	return_DX



;	*****************************
;	***    DOS Function 2B    ***
;	***  Set Current Date     ***
;	*****************************
	Public	func2B
func2B:
;
;	entry:	cx = year (1980-2099)
;		dh = month (1-12)
;		dl = day (1-31)
;
;	exit:	al = 00H success
;		   = FFH failure
;
	call	ConvertDate		; Convert to BIOS date
	 jc	f2B_20			; Abort on Error
	push	cx			; save the converted date
	call	ReadTimeAndDate		; Get the current Time and Date
	pop	biosDate		; new date, existing time
f2B_10:					; Set the current Time and Date
	call	rw_clock_common		; make sure clock is there/setup regs
	call	device_write		; Update the Date and Time
	xor	ax,ax
	ret

f2B_20:
	mov	al,0FFh			; return FAILURE
	ret

;	*****************************
;	***    DOS Function 2D    ***
;	***  Set Current Time     ***
;	*****************************
	Public	func2D
func2D:
;
;	entry:	ch = hours (0-23)
;		cl = minutes (0-59)
;		dh = seconds (0-59)
;		dl = hundredth seconds (0-99)
;
;	exit:	al = 00H success
;		   = FFH failure
;
	cmp	ch,23			; Range check hours
	 ja	f2B_20
	cmp	cl,59			; Range check minutes
	 ja	f2B_20
	cmp	dh,59			; Range check seconds
	 ja	f2B_20
	cmp	dl,99			; Range check hundredths
	 ja	f2B_20
	push	cx			; save hours/mins
	push	dx			; save secs/hundredths
	call	ReadTimeAndDate		; Get the current Time and Date
	pop	biosDate+4		; leave the date alone
	pop	biosDate+2		;  but update the time
	jmps	f2B_10			; Update the Date and Time	


	Public	ReadTimeAndDate
	
ReadTimeAndDate:			; Get the current Time and Date
	call	rw_clock_common		; make sure clock is there/setup regs
	call	device_read		; read the Date and Time
	mov	ax,biosDate		; get the BIOS date and convert
	cmp	ax,daysSince1980	; (but only if necessary)
	 jne	NewDate
	ret

NewDate:
	mov	daysSince1980,ax	; so we won't have to convert next time
	inc	ax			; Day number starting 1 Jan 1980
	mov	cx,ax			; save day count
	inc	ax			; convert to a sunday as 1/1/80 is tues
	xor	dx,dx
	mov	bx,7
	div	bx
	mov	dayOfWeek,dl		; save day of week

	xor	dx,dx
	mov	ax,cx			; recover day count

	xor	di,di			; assume zero leap days
	sub	ax,60			; less than 60 days
	 jc	no_leap_days		; means no leap days to subtract
	mov	bx,1461			; 1461 = days in four years
	div	bx			; get number of leap years since 1980
	inc	ax			; include 1980
	sub	cx,ax			; normalize years to 365 days
	mov	di,ax			; save proper leap day count
no_leap_days:

	xor	dx,dx
	xchg	ax,cx			; DX:AX = years since 1980 * 365
	mov	bx,365
	div	bx			; get number of years since 1980
	or	dx,dx			; check for zero days left
	 jnz	days_left
	dec	ax			; dec year count
	mov	dx,365			; set day count to last day of last year
days_left:

	mov	yearsSince1980,ax	; save the year
	xchg	ax,si			; save in SI

	xor	bx,bx
	mov	cx,12
get_month:				; find the appropriate month
	cmp	dx,totaldays[bx]
	 jbe	got_month
	inc bx ! inc bx
	loop	get_month
got_month:
	shr	bx,1			; BX = month
	mov	month,bl
	
	dec	bx
	shl	bx,1			; BX = index to previous months
	sub	dx,totaldays[bx]	; get days into this month

	cmp	bx,2			; if it's FEB 29th we've lost a day
	 jne	not_leap_yr		; check it's FEB
	test	si,3
	 jnz	not_leap_yr		; but is it a leap year ?
	shr si,1 ! shr si,1		; divide years by 4
	inc	si			; include this year
	cmp	si,di			; compare against leap day adjustment
	 jne	not_leap_yr
	inc	dx			; put 29th feb back again
not_leap_yr:
	mov	dayOfMonth,dl		; save the day of the month
	ret


ConvertDate:
    sub cx,1980         ; Base year is 1980
	jc	xset_date_error
	cmp	cx,2100-1980		; Year in valid range ?
	jnc	xset_date_error
	mov	bl,dh			; Month to BL
	xor	bh,bh
	dec	bx			; Adjust month to 0-11
	cmp	bl,12			; Month in valid range ?
	jnc	xset_date_error
	mov	al,dl			; Day of month
	cbw
	test	cl,3			; Leap year ?
	jnz	not_leap_year		; Jump if not
	cmp	dh,3			; After February ?
	cmc
	adc	al,ah			; Increment days in current year if so
	cmp	dl,29			; Day of month 29 ?
	jz	day_valid		; Valid if so

not_leap_year:
	dec	dx
	cmp	dl,monthdays[bx]	; Day of month within range for non-leap
					; year ?
	jnc	xset_date_error

day_valid:
	shl	bx,1
	add	ax,totaldays[bx]	; Get total days in current year

	push	ax

	mov	ax,365
	mul	cx			; Convert year to days since 1-1-1980
	mov	cx,ax

;	if total (ax) >= 60 (Feb 29 1980) then
;	  leap$days = (total - 60) / (365 * 4) + 1

	sub	ax,60			; Before first leap year date
	jc	noleap			; Jump if so
	mov	bx,365*4		; 4 years worth of days (365 * 4)
	sub	dx,dx
	div	bx			; Get number of leap years - 1
	inc	ax
	add	cx,ax			; CX now has total days including leap
					; days

noleap:
	dec	cx

	pop	ax

	add	cx,ax			; Get total days since 1-1-1980
	clc
	ret

xset_date_error:
	stc
	ret

rw_clock_common:
	mov	cx,6			; read/write 6 characters
	mov	dx,offset biosDate	; DX -> 6 byte buffer
	les	si,clk_device		; Get the address of the Clock Device
	cmp	si,-1			; Has a valid device been selected
	 jne	rw_clock_common10
	add	sp,WORD			; discard near return address
rw_clock_common10:
	ret


;	*****************************
;	***    DOS Function 38    ***
;	*** Get/Set Country Data  ***
;	*****************************
	Public	func38
func38:
	xor	ah,ah			; Generate 16 Bit country
	cmp	al,0FFh			; FF means country code in BX
	 jne	f38_10
	xchg	ax,bx			; AX = real country code
f38_10:
	xchg	ax,dx			; DX = country
	test	dx,dx			; dos_AL = 0 get the current country
     jz f38_get         
	inc	ax			; now check for dos_DX = FFFF
	 jz	f38_set			;  which means set country code
	dec	ax			; no, return buffer to normal
f38_get:
	test	dx,dx			; Get current?
	 jnz	f38_g10			; Yes
	mov	dx,cur_country		; use current country
f38_g10:
; look for (and if neccessary load) type 1 info into buffer
	xchg	ax,di			; ES:DI -> buffer
	mov	bx,cur_cp		; bx=codepage
	call	f38_get_info		; get info in current codepage
	 jnc	f38_g20
	push ss ! pop ds
	xor	bx,bx			; now try any codepage
	call	f38_get_info		; if none for current codepage
	 jc	f38_error		; No Match Found
f38_g20:
	lea	si,EXCI_CI_DATAOFF[si]	; point at CI_, not EXCI_ data
	mov	bx,CI_CODE[si]		; Return the selected country code
	mov	cx,CI_STATICLEN/2
	rep	movsw
	
if NLSFUNC
	push	ss
	pop	ds
;	call	get_dseg		; DS back to PCMODE
endif
	call	return_BX		; return country code in BX
	xchg	ax,bx
	jmp	return_AX_CLC		; and in AX

f38_get_info:
	push es ! push di ! push dx	; save pointer to buffer
	mov	al,1			; Get data list seperators etc...
	call	f65_get_info		; DS:SI -> extended country info buffer
	pop dx ! pop di ! pop es	; ES:DI -> users buffer
	ret

f38_set:
	mov	bx,cur_cp		; bx=codepage
	and	bx,f38_flag		; force CP to zero if 1st time here
	call	f38_set_country		; Update the Internal Data Structures
	 jc	f38_error
f38_s20:
	mov	f38_flag,0FFFFh		; Country Code Set Successfully
	mov	ax,cur_country		; and return the current country
	jmp	return_AX_CLC		;  to the user


f38_error:

if NLSFUNC
	push	ss
	pop	ds
;	call	get_dseg		; DS back to PCMODE
endif
	mov	ax,ED_FILE		; This is the Error to return 
    jmp error_exit      


f38_set_country:
; On Entry:
;	BX = codepage
;	DX = country
; On Exit:
;	AX = error code
; preserve codepage/country info if there is an error (ie do type 1 last!)
;
	mov	al,2			; Get uppercase & filename table
	mov	di,offset Ucasetbl
	mov	cx,info2_len
	call	f38_update
	 jc	f38_seterr
	mov	al,4			; Get uppercase & filename table
	mov	di,offset FileUcasetbl
	mov	cx,info4_len
	call	f38_update
	 jc	f38_seterr
	mov	al,5			; Get Legal file characters
	mov	di,offset FileCharstbl
	mov	cx,info5_len
	call	f38_update
	 jc	f38_seterr
	mov	al,6			; Get Collating table
	mov	di,offset Collatingtbl
	mov	cx,info6_len
	call	f38_update
	 jc	f38_seterr
	mov	al,7			; Get double byte character set table
	mov	di,offset DBCS_tbl
	mov	cx,info7_len
	call	f38_update
	 jc	f38_seterr
	mov	al,1			; Get data list seperators etc...
	mov	di,offset country_data	; do last since this updates
	mov	cx,info1_len		; cur_country/cur_cp
	call	f38_update
;	 jc	f38_seterr
;	clc
	ret
f38_seterr:
	mov	ax,ED_FILE		; return file not found error
	ret

f38_update:
	push	ds			; save important registers
	push	bx			; codepage
	push	cx			; count for move
	push	dx			; country
	push	di			; destination offset for move
	push	ds			; destination segment
	call	f65_get_info		; DS:SI -> buffer with country info
	pop	es			; destination seg in ES
	pop	di			; ES:DI -> destination of move
	pop	dx			; country
	pop	cx			; bytes to move
	pop	bx			; codepage back again
	 jc	f38_update10		; any problems ?
	rep	movsb			; no, copy the data
f38_update10:
	pop	ds			; DS back to PCMDSEG
	ret

;	*****************************
;	***    DOS Function 65    ***
;	*** Extended Country Data ***
;	*****************************
;
;CODEPAGE	equ	437			; Return Standard Code Page
;
;	Get Extended Country Code Sub-Functions
;
func65_dt	dw	0FFFFh			; 00 Illegal Sub-Function
		dw	offset country_data	; 01 Extended Country Info
		dw	offset Ucasetbl		; 02 UpperCase Table
		dw	0FFFFh			; 03 Invalid Subfunction
		dw	offset FileUcasetbl	; 04 FileName Upper Case Table
		dw	offset FileCharstbl	; 05 Valid Filename Characters
		dw	offset Collatingtbl	; 06 Collating Sequence
		dw	offset DBCS_tbl		; 07 DBCS Environment Vector 
func65_dtl	equ	(offset $ - offset func65_dt)/2

	Public	func65
func65:
	cmp	al,func65_dtl		; is sub-function 0-7 ?
	 jb	func65_read_table
	sub	al,20h			; now check for 20-22
	 jb	f65_invalid
	 je	func6520		; it's upper case character
	sub	al,2
	 je	func6522
	 jb	func6521
	sub	al,1			; how about 6523 ?
	 jnz	f65_invalid
;	jmp	func6523

func6523:
;--------
; On Entry:
;	DX = character to check
; On Exit:
;	AX = 0, No
;	AX = 1, Yes
;	AX = 2, neither
;
	push	ds
	pop	es
	mov	di,offset NoYesChars	; 'NnYy'
	cbw				; assume No (AX=0)	
	xchg	ax,dx			; AX = char, DX = answer
	call	dbcs_lead		; is it 1st of a DBCS pair
	 jne	func6523_10
	scasw				; check 'N'
	 je	func6523_30
	inc	dx			; assume Yes (DX=1)
	scasw				; check 'Y'
	jmps	func6523_20
func6523_10:
	scasb				; check 'N'
	 je	func6523_30
	scasb				; check 'n'
	 je	func6523_30
	inc	dx			; assume Yes (DX=1)
	scasb				; check 'Y'
	 je	func6523_30
	scasb				; check 'y'
func6523_20:
	 je	func6523_30
	inc	dx			; it's neither (DX=2)
func6523_30:
	xchg	ax,dx			; return result in AX
	jmp	return_AX_CLC		; Return the Code Page

func6522:
;--------
; Upper case ASCIIZ string at ES:DX
	mov	cx,0FFFFh		; calculate the length
	mov	di,dx			;  of the string
;	mov	al,0
	repne	scasb
	not	cx			; CX = length, including 0
;	jmp	func6521		; now use upper case CX bytes

func6521:
;--------
; Upper case string of CX bytes at ES:DX
	mov	si,dx
	mov	di,dx			; point SI & DI at string
f6521_10:
	lods	es:al			; read a character
	call	dbcs_lead		; is it 1st of a DBCS pair
	 jne	f6521_20
	stosb				; store 1st byte of this pair
	movs	es:byte ptr [di],es:byte ptr [si]
					; copy 2nd byte
	dec	cx			; 1st byte of pair
	loopnz	f6521_10		; go around for another one
	ret				; time to go...
f6521_20:
	call	toupper			; upper case the character
	stosb				; 
	loop	f6521_10		; go and do another one
	ret

func6520:
;--------
; Upper case character DL
	xchg	ax,dx			; character in AX
	call	toupper			; upper case it
	mov	dl,al			; return in AL and DL
	jmp	return_DX		; set return code

f65_invalid:
;-----------
; short jump to invalid function
	jmp	invalid_function


func65_read_table:
;-----------------
	cmp	cx,5			; Check for valid buffer size
	 jb	f65_invalid
	cbw				; Get the request sub-function
	mov	si,ax			; into SI
	shl	si,1
	mov	si,func65_dt[si]
	inc	si			; is SI = 0FFFFh
	 jz	f65_invalid		; if so it's an invalid function
	dec	si

	cmp	dx,0ffffh
	 jne	f65_21			; FFFF means
	mov	dx,cur_country		;  use default country
f65_21:
	cmp	bx,0ffffh
	 jne	f65_22			; FFFF means
	mov	bx,cur_cp		;  use default codepage
f65_22:
	call	f65_get_info		; DS:SI -> extended info for this pair
	mov	ax,ED_FILE		; On Error return File Not Found
	 jnc	f65_23			; for any error
	push ss ! pop ds
	jmp	error_exit		; so Quit
f65_23:
	les	bx,ss:int21regs_ptr	; point to callers registers
	mov	ax,es:reg_AX[bx]	; get the subfunction number
	mov	cx,es:reg_CX[bx]	; this much data is requested
	mov	di,es:reg_DI[bx]	; Get the Parameter Block Offset
	mov	es,es:reg_ES[bx]	; and Segment
	stosb				; fill in Info ID
	cmp	al,1			; 1 is special - the rest
	 jne	f65_30			;  want a DWORD ptr
	cmp	cx,EXCI_MAXLEN		; Check CX against the sub-function 1
	 jbe	f65_25			; maximum and force CX to this value
	mov	cx,EXCI_MAXLEN		; if it is greater
f65_25:
	call	return_CX		; Return the number of bytes transfered
	sub	cx,EXI_DATA_LEN		; Adjust count for 3 byte header 
	mov ax,cx ! stosw		; fill in EXCI_LENGTH
	push	cx			; Save the count and copy as much
	cmp	cx,EXCI_STATLEN		; valid data a possible. IE at most
	 jbe f65_27			; EXCI_STATLEN bytes	
	mov	cx,EXCI_STATLEN
f65_27:
	rep	movsb			; just copy the data
	pop	cx			; Zero the rest of the data
	sub	cx,EXCI_STATLEN		; Skip if no space left in users
	 jbe	f65_40			; buffer otherwise STOSB
	xor al,al ! rep stosb
	jmps	f65_40

;
;	All function 65 sub-functions apart from 01 (Extended Country Info.)
;	pass use this code to update the users parameter block.
;
f65_30:
    mov cx,5            
	call	return_CX
	mov ax,si ! stosw		; fill in the DWORD ptr to the data
	mov ax,ds ! stosw
f65_40:
	push	ss
	pop	ds
;	call	get_dseg		; back to PCMDSEG
	mov	ax,cur_cp		; ##jc## Is the Requested or Current
	jmp	return_AX_CLC		; Return the Code Page

;	*****************************
;	***    DOS Function 66    ***
;	***Get/Set Global CodePage***
;	*****************************
;
	Public	func66
func66:
	cbw
	dec ax ! jz f66_10		; AL = 1, Get the Current CodePage
	dec ax ! jz f66_20		; AL = 2, Set the Current CodePage
	jmp	invalid_function	; Illegal Sub-Function return an Error

f66_10:					; Get the Current Code Page Info
	mov	bx,cur_cp		; Current CodePage
	call	return_BX
	mov	dx,SYS_CP		; System CodePage
	jmp	return_DX

f66_20:					; Set the Current CodePage
	mov	dx,cur_country		; The Codepage has changed, so update
	call	f38_set_country		; Country Info and tables
	 jnc	f66_30			; Reset the Current CodePage if
	mov	ax,ED_FILE		; and return the error
	jmp	error_exit
f66_30:
	mov	bx,cur_cp		; select the new codepage
	call	f66_select_cp		; Prepare CodePage Devices
	 jnc	f66_40			; No Errors Skip Error Handler
	mov	ax,-65			; Update Error Status do not generate
	call	error_ret		;  a critical error but return
					;  "access denied" to the application
f66_40:
	ret


f65_get_info:
; On Entry:
;	AL = info type
;	BX = codepage (zero means any)
;	DX = country
; On Exit:
;	CY set if error
;	DS:SI -> buffer with info in it
;
; NB. Remember to to Xlat fixups !
;
	cmp	dx,cur_country		; is it default country ?
	 jne	f65_p30			; have we already got correct country ?
	test	bx,bx			; CP zero special case and we will
	 jz	f65_p20			;  accept anything for this country
	cmp	bx,cur_cp		; otherwise is the codepage
	 jne	f65_p30			;  in the default system ?
f65_p20:
	cbw				; make info type a word
	mov	si,ax			;  into index register
	shl	si,1			; now a word offset
	mov	si,func65_dt[si]	; pick up offset of correct table
	jmps	f65_p90
f65_p30:
	push	ax
	call	f65_locate_and_read	; get info into a buffer at DS:SI
	pop	ax
	 jc	f65_p_exit
f65_p90:
	cmp	al,1			; was it country info ?
	 jne	f65_p95			; no, skip the fixup
	mov	CI_CASEOFF+EXCI_CI_DATAOFF[si],offset xlat_xlat
	mov	CI_CASESEG+EXCI_CI_DATAOFF[si],ss
f65_p95:
	clc
f65_p_exit:
	ret


;
;	**********************************************************************
;	***  Function 65 support - routines for seeking a country/codepage ***
;	***  and loading the required information into the temp data area  ***
;	**********************************************************************
;
;	**************************************************
;	***   Open country.sys and search for the      ***
;	***   table of offsets for the given country/  ***
;	***   codepage, read it in and exit.           ***
;	**************************************************

f65_locate_and_read:
;	Locate and Read info AL for Country DX Codepage BX
if NLSFUNC
	mov	di,offset country_filename
					; point at pathname to country.sys 
	xchg	ax,cx			; get info into CL
	mov	ax,14feh		; then call magic backdoor
nlsfunc_int2f:
	stc				; assume an error
	int	2fh			; to do the hard work
	ret
else
	push	ax
	call	f65x_find_info		; Will need to load up the info 
	pop	ax
	 jc	f65_lr_exit		; so do it if we can.

	mov	dx,offset f65xx_temp_area
	mov	cx,256			; read 256 bytes into local buffer
	push	ax
	call	f65x_load_info		; Load required info
	pop	ax
	 jc	f65_lr_exit
	mov	ah,MS_X_CLOSE		; All done so 
	mov	bx,c_handle 		; Close the file first
	call	dos_entry		; before leaving
;	 jc	f65_lr_exit
	mov	si,offset f65xx_temp_area ; Tell subroutines where info is
f65_lr_exit:
	ret
;
; Entry:  dx=country code, bx=codepage
; Exit :  carry set, and country.sys closed if failure
;         country.sys open ready for more reads if success
;
f65x_find_info:
	push	es			; Save es
	push	ds
	pop	es			; Make es=ds
	mov	f65xx_country,dx
	mov	f65xx_codepage,bx
	mov	dx,offset country_filename
	mov	ax,(MS_X_OPEN*256)+0	; Attempt to open country.sys 
	test	dx,dx
	stc
	 jz	f65x_40 
	call	dos_entry		; Handle should come back in ax
	 jc	f65x_40
f65x_10:
	mov	c_handle,ax		; Save handle
	mov	dx,f65xx_country
	cmp	f65xx_code,dx		; do we already have the information?
	 jne	f65x_30			; No - get it from country.sys
f65x_20:
	cmp	f65xx_cp,bx		; Does codepage agree too?
	 je	f65x_35			; Yes so exit with no more ado
f65x_30:
	mov	dx,007Eh	
	xor	cx,cx			; Seek within country.sys
	mov	bx,c_handle
	mov	ax,(MS_X_LSEEK*256)+0	; seek from begining
	call	dos_entry
	 jc	f65x_err
	mov	ah,MS_X_READ		; Now read the signature bytes and
	mov	bx,c_handle		; check them
	mov 	cx,2
	mov 	dx,offset f65xx_sig
	call	dos_entry
	 jc	f65x_err
	cmp	f65xx_sig,VALID_SIG
	 jne	f65x_err		; If signature bad exit
f65x_32:
	mov	ah,MS_X_READ		; Read from country.sys header until
	mov	bx,c_handle		; Country/codepage found or NULL
	mov	cx,f65xx_ptable_len
	mov	dx,offset f65xx_code
	call	dos_entry
	 jc	f65x_err	
	cmp	f65xx_code,0		; Found NULL so reqd combination
	 je	f65x_err		; was not found
	mov	dx,f65xx_code		; Get the country/codepage values
	mov	bx,f65xx_cp		; read from Country.SYS
	cmp	dx,f65xx_country	; Check against the requested
	 jne	f65x_32			; Country. 
	cmp	f65xx_codepage,0	; If a codepage match is not
	 jz	f65x_35			; then return success
	cmp	bx,f65xx_codepage	; Check against the requested
	 jne	f65x_32			; Codepage
f65x_35:
	mov	f65xx_country,dx	; Force the Search Country and
	mov	f65xx_codepage,bx	; CodePage to be Updated
f65x_40:
	pop	es			; combination found so exit
	ret
	
f65x_err:
	pop	es
	mov	ah,MS_X_CLOSE		; On error close country.sys
	mov	bx,c_handle 		; and set the carry flag before
	call	dos_entry		; leaving
	stc
	ret
;
;	**************************************************
;	***   Load the type of information requested   ***
;	***   For the country currently active in the  ***
;	***   offset table			       ***
;	**************************************************
;
; Entry:  al=type of info, dx=offset of buffer to read info into cx=no of bytes
; Exit :  carry set, and country.sys closed if failure
;
f65x_load_info:
	push	es
	push	cx
	push	dx
	push	ds			; Make es=ds
	pop	es
	dec	al			; 1=Data , 2=uppercase, 4=fuppercase
	sub	bh,bh			; 5=filechars, 6=Collating table
	mov	bl,al			; 7=DBCS table
	shl	bx,1			; Retrieve relevant offset
	mov	dx,f65xx_data[bx]	
	xor	cx,cx			; Seek within country.sys
	mov	bx,c_handle
	mov	ax,(MS_X_LSEEK*256)+0	; seek from begining
	call	dos_entry
	pop	dx			; Get buffer address back
	pop	cx			; and number of bytes to read
	 jc	f65x_err
	test	ax,ax			; zero offset is a problem
	 jz	f65x_err		; (probably DBCS with old COUNTRY.SYS)
	mov	ah,MS_X_READ		; Now read that info into our data area
	mov	bx,c_handle
	call	dos_entry		; Return when read is done
	 jc	f65x_err
	pop	es
	ret
endif

;
;	This function scans the complete device list and prepares
;	all devices which support codepage.
;
; On Entry:
;	BX = codepage
; On Exit:
;	AX = error code

DA_CODEPAGE	equ	DA_CHARDEV+DA_IOCTL+DA_GETSET

f66_select_cp:
if NLSFUNC
	mov	ax,14ffh		; then call magic backdoor
	jmps	nlsfunc_int2f		;  to do the hard work
else	
	push	ds
	mov	f66_cp,bx		; Save requested CodePage
	mov	preperr,0000		; Initialize Prepare Error
	push ds ! pop es
	mov	bx,offset dev_root	; Get the Root of the Device List
f66_p10:				; Skip the NUL Device and check
	lds	bx,ds:DH_NEXT[bx]	; each character device for CodePage
	cmp bx,0FFFFh ! jz f66_p50	; Support.
	mov	ax,ds:DH_ATTRIB[bx]
	and	ax,DA_CODEPAGE		; Check for a Character Device which
	cmp	ax,DA_CODEPAGE		; supports IOCTL strings and GETSET
	 jne	f66_p10			; otherwise skip the device

	push	bx
	lea	si,DH_NAME[bx]		; Found a matching device so
	mov	di,offset prepname	; open the device and select the 
	mov	cx,8			; requested codepage

f66_p20:
	lodsb
	cmp al,' ' ! jz f66_p30
	stosb
	loop	f66_p20

f66_p30:
	xor al,al ! stosb	
	mov	ax,(MS_X_OPEN*256)+1	; Open the device name for
	mov	dx,offset prepname	; Write Access
	call	dos_entry
	 jc	f66_perr
	mov	bx,ax			; Save Device Handle in BX

	mov	si,es:f66_cp		; Get Requested CodePage in SI
	mov	dx,offset cp_packet	; Offset of CodePage Struct
	mov	cx,006Ah		; Get Unknown CodePage
	mov	ax,(MS_X_IOCTL*256)+0Ch	; Generic IOCTL function
	call	dos_entry		; Make function Call
	 jc	f66_p32			; Error so Select requested Code Page

	cmp si,es:cp_cpid ! jz f66_p35	; If this the currently selected
f66_p32:				; skip the select CodePage
	mov	es:cp_cpid,si
	mov	dx,offset cp_packet	; Offset of CodePage Struct
	mov	cx,004Ah		; Select Unkown CodePage
	mov	ax,(MS_X_IOCTL*256)+0Ch	; Generic IOCTL function
	call	dos_entry		; Make function Call
	 jnc	f66_p35			; No Error so skip the error
f66_p33:
 	mov	es:preperr,ax		; save

f66_p35:	
	mov	ah,MS_X_CLOSE		; Close the device and check
	call	dos_entry		; for more devices to be prepared
	jmps	f66_p40	

f66_perr:
	mov	es:preperr,ax		; Save the error code and try the
f66_p40:				; next device in the chain
	pop	bx			; Restore the Device offset
	jmps	f66_p10			; and continue

f66_p50:				; All device have been prepared
	pop	ds			; now return the last error code
	mov	ax,preperr		; in AX
	or	ax,ax
	ret
endif

PCMODE_DSIZE	DSEG	PARA
	extrn	swap_indos:word

PCM_RODATA	CSEG	WORD
;
;	Get Internal Data DOS function 5Dh
;
func5D_ft   dw  f5D00       
		dw	f5D01		; Commit All
		dw	f5D02		; Close File By Name
		dw	f5D03		; Close All Host Files
		dw	f5D04		; Close Process Host Files
		dw	f5D05 		; Get Open File List
		dw	f5D06		; Get DOS Data Area
		dw	f5D_msnet	; f5D07 ; Get Truncate Flag used
						; with Redirected Dev I/O
		dw	f5D_msnet	; f5D08 ; Set Truncate Flag with
						; with Redirected Dev I/O
		dw	f5D_msnet	; f5D09 ; Close All Spool Streams
		dw	f5D0A		; Set Extended Error Info
func5D_ftl	equ	(offset $ - offset func5D_ft)/2
		dw	invalid_function

;
;	Data used by the Binary format Time and Date routines
;
totaldays	dw	0,31,59,90,120,151,181,212,243,273,304,334,0ffffh
monthdays	db	31,28,31,30,31,30,31,31,30,31,30,31

parse_separators	db	TAB,'.,+:;='
parse_terminators	db	'|"/\[]<>'


PCMODE_DATA	DSEG	WORD
	extrn	internal_data:word
	extrn	error_code:word
	extrn	error_class:byte
	extrn	error_action:byte
	extrn	error_locus:byte
	extrn	error_dev:word

	extrn	indos_flag:word
	extrn	bootDrv:byte
	extrn	current_psp:word
	extrn	break_flag:byte
	extrn	dma_offset:word
	extrn	dma_segment:word
	extrn	fcb_search_buf:byte
	extrn	func52_data:byte	; Internal Data Table Area
	extrn	int21regs_ptr:dword
	extrn	lock_tables:dword
	extrn	unlock_tables:dword
	extrn	share_stub:dword
	extrn	remote_call:word	; set to FF if remote machine operation
	extrn	swap_always:word
	extrn	switch_char:byte
	extrn	owning_psp:word
	extrn	machine_id:word
	extrn	country_data:byte
	extrn	cur_country:word
	extrn	cur_cp:word
	extrn	verify_flag:byte

	extrn	clk_device:dword	; Clock Device Driver Address
	extrn	biosDate:word
	extrn	daysSince1980:word
	extrn	yearsSince1980:word
	extrn	month:byte
	extrn	dayOfWeek:byte
	extrn	dayOfMonth:byte
	extrn	hour:byte
	extrn	minute:byte
	extrn	second:byte
	extrn	hundredth:byte
if DOS5
	extrn	WindowsHandleCheck:byte
else
	extrn	win386_remote_machine:dword
endif

SYS_CP		equ	437		; System CodePage


GLOBAL_DATA	dseg	word

f38_flag	dw	0		; Country Code Selected Successfully

if not NLSFUNC
	extrn	dev_root:dword

f66_cp		rw	1		; INT21/66 Local Variable
cp_packet	dw	2		; Packet Size
cp_cpid		dw	0		; Request CodePage
		db	0,0		; Packet Terminators

preperr		rw	1		; Prepare function Error Code
prepname	rb	9		; Reserved for ASCIIZ Device Name

;
; Area for country.sys current pointer table 
; (these are all offsets into country.sys)
;
f65xx_code	rw	1	; Country code
f65xx_cp	rw	1	; Code page
		rw	1	; +1 reserved
f65xx_data	rw	1	; Data area
		rw	1	; Upper case table
		rw	1	; +1 reserved		
		rw	1	; Filename upper case table
		rw	1	; Legal file characters
		rw	1	; Collating table
		rw	1	; Double byte character set lead byte table
f65xx_ptable_len	equ	offset $ - offset f65xx_code

f65xx_temp_area	rb	256	; Data area for extended country info
f65xx_codepage	rw	1
f65xx_country	rw	1
f65xx_sig	rw	1	; Signature
c_handle	rw	1

endif	;not NLSFUNC

	end
