
%if 0

lDOS DOSENTRY handling
 by C. Masloch, 2018

Usage of the works is permitted provided that this
instrument is retained with the works, so that any entity
that uses the works is notified of this instrument.

DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.

%endif

%if 0

The DOSENTRY section is always located at segment 70h. It includes some data
structures found in MS-DOS (most notably the Int19 interrupt restoration table
at 70h:100h), the device headers for all default devices (except NUL) and all
code entries into the DOS code. If the DOS code is not relocateable, DOSENTRY
and DOSCODE are merged into one section.

Relocated entries which are in DOSENTRY for relocation:
Interrupt 00h		Division error exception
Interrupt 06h		Invalid opcode exception
Interrupt 13h		BIOS disk services
Interrupt 19h		Reboot
Interrupt 1Bh		Ctrl-Break notification
Interrupt 20h		Terminate program
Interrupt 21h		DOS services
Interrupt 25h		Absolute disk read
Interrupt 26h		Absolute disk write
Interrupt 27h		Terminate and stay resident
Interrupt 28h		Idle
Interrupt 29h		Display character
Interrupt 2Fh		Multiplex, DOS services
CALL 5			CP/M compatibility services (subset of interrupt 21h)
Device strategy		All device functions
Case map function	Maps language-specific characters >=80h to uppercase
Redirector mapper	Maps unhandled redirector calls to MS-DOS interface

If DOSCODE is not the same section as DOSENTRY (if DOSCODE is relocateable),
each of the relocated entries calls relocatedentry with a parameter behind the
near call opcode. The near call opcode and the byte sized parameter for each
entry need 4 byte. relocatedentry insures that DOSCODE is available (switches
the A20 line on if DOSCODE is in the HMA) and jumps to the DOSCODE function
relocated_relocatedentry. The latter uses the in-code parameter (behind the
function call within DOSENTRY) as a displacement into doscode_entrypoint_list,
where we store the target near address in DOSCODE.

If the target DOSCODE near address is equal to DEVStrategy, then the entry in
doscode_entrypoint_list contains another word (after the DEVStrategy address)
which points at a device strategy function dispatch table, stored in DOSCODE.
Some of the device strategies also include another byte after the table offset,
which gives the device unit to use (only used for PRN/LPTx and AUX/COMx).

It is insured that an entry of doscode_entrypoint_list with displacement
equal to 0CCh is not used, so that relocated_relocatedentry can reliably
detect breakpoints set after the call jumps. Such a breakpoint hides the true
displacement value and thus must be detected and circumvented.

Entries which are in DOSENTRY but don't execute code in DOSCODE:
Interrupt 23h		Always terminates application
Interrupt 24h		Always fails call
Interrupt 2Eh		Always reports error
Device interrupt	MS-DOS device compatibility (does nothing)
Dummy file sharer	Always returns error 0001h (Invalid function)
Dummy redirector	Always returns error 0001h (Invalid function)
Other hooked interrupts	Do nothing

%endif

%include "RXDMACRO.NEW"


%assign _FATFS			0
%assign _MAPPER			0
%assign _INT28			1
%assign _CALL5			1


%define ENTRYPOINT_LIST .none,dw "",dw "",db ""

	%imacro _relocate 1-3.nolist "",""
	call relocatedentry
add_to_entrypoint_list %%label,dw %1,dw %2,db %3,ENTRYPOINT_LIST
	db %%label - doscode_entrypoint_list
	%endmacro
%if _RELOCATEDOSCODE
 %idefine relocate _relocate
%endif
%idefine devicerelocate _relocate

	%imacro add_to_entrypoint_list 4-*.nolist
%if %0 & 3
 %fatal Expected a number of arguments that is a multiple of four
%endif
%push
%define %$label	%1
%define %$two	%2
%define %$three	%3
%define %$four	%4
%assign %$found	0
%rotate 4
%rep (%0 - 4) / 4
 %ifn %$found
  %ifidn	%$two,	%2
   %ifidn	%$three,%3
    %ifidn	%$four,	%4
%$label: equ %1
     %assign %$found 1
    %endif
   %endif
  %endif
 %endif
 %rotate 4
%endrep
%ifn %$found
 %xdefine ENTRYPOINT_LIST ENTRYPOINT_LIST,%$label,%$two,%$three,%$four
%endif
%pop
	%endmacro

	%imacro write_entrypoint_list 0-*.nolist
	align 2
doscode_entrypoint_list:
%if %0 & 3
 %fatal Expected a number of arguments that is a multiple of four
%endif
%rep %0 / 4
	align 2
%if ($ - doscode_entrypoint_list) == 0CCh
 %warning added word to doscode_entrypoint_list to find bp after call jump
	dw 9090h
%endif
%1:		; label (used as displacement from doscode_entrypoint_list)
	%2	; entrypoint (word)
	%3	; device function table, if any (word)
	%4	; device unit number, if any (byte)
%rotate 4
%endrep
doscode_entrypoint_list.end:
	%endmacro


	align 2
%if _RELOCATEDOSCODE
 %if _DOSCODEHMA
	nop
	nop
 %endif
i00:	relocate relocatedi00
i06:	relocate relocatedi06
; i13:	relocate relocatedi13
%ifn _INT19_IN_DOSENTRY
i19:	relocate relocatedi19
%endif
; i1B:	relocate relocatedi1B
i20:	xor ax, ax
i21:	relocate relocatedi21
i25:	relocate relocatedi25
i26:	relocate relocatedi26
i27:	relocate relocatedi27
 %if _INT28
i28:	relocate relocatedi28
 %endif
i29:	relocate relocatedi29
i2F:	relocate relocatedi2F
i31:	relocate relocatedi31
 %if _CALL5
call5:	relocate relocatedcall5
 %endif
 %if _FATFS
fatfs:	jmp short .jump
.next:	jmp FIXDOSENTRY:mapper
.jump:	relocate relocatedfatfs
fatfs2:	jmp short .jump
.next:	jmp FIXDOSENTRY:NotHooked
.jump:	relocate relocatedfatfs2
 %endif
 %if _MAPPER
mapper:	relocate relocatedmapper
 %endif
casemap:relocate relocatedcasemap
%endif


i23:
	stc
	retf

i24:
	mov al, 03h
entry_iret:
	iret


	%imacro irtentry 1.nolist
db %1
dd 0
	%endmacro


%assign num 256-($-$$)
%warning num bytes in front of IRT

	_fill 256, 0, dosentry_start
InterruptRestorationTable:
	irtentry 10h
	irtentry 13h
	irtentry 15h
	irtentry 19h
	irtentry 1Bh
		; Above interrupts are in the order MS-DOS 6.x saves them
		;  (FDEMM386?/Jemm expects Int19 within the first five entries)

	irtentry 00h
	irtentry 01h
	irtentry 03h
	irtentry 04h
	irtentry 06h
	irtentry 08h
	irtentry 16h
	irtentry 1Ch
	irtentry 1Eh
	irtentry 21h
	irtentry 23h
	irtentry 24h
	irtentry 29h
	irtentry 2Fh
	irtentry 33h
	db -1


		; INP:	byte [cs:ip] = displacement in doscode_entrypoint_list
		;	word [DOSCODE:(displaced)]
		;	 = relocated entry address
		;	word [DOSCODE:(displaced + 2)]
		;	 = optional pointer to device function table
		;	byte [DOSCODE:(displaced + 4)]
		;	 = optional device unit
		; OUT:	Jumps to DOSCODE:(relocated entry address)
		;	If the relocated entry address is DEVStrategy,
		;	 ss:sp-> device unit or garbage (word),
		;		 pointer to device function table (word)
		; CHG:	- (not even flags!)
		; STT:	-
relocatedentry:
	push cs
	jmp 70h:.fixsegment				; insure cs = 70h
.fixsegment:

	pushf
	push ax
%if _RELOCATEDOSCODE
	push ds

	mov ds, word [cs:dosentry_to_dosdata_segment]	; => DOSDATA
	mov ax, word [dosdata_to_doscode_segment]	; => DOSCODE
 %if _DOSCODEHMA
	cmp ax, -1
	jne .not_in_hma

		; DOSCODE is in HMA: check A20 and try to enable if disabled

		; INP:	ax = 0FFFFh
		; OUT:	ax = 0FFFFh
		; CHG:	fl, ds

	push si
		; INP:	ax = 0FFFFh
		; OUT:	ax = 0FFFFh
		;	A20 enabled
		; CHG:	si, ax, fl, ds
	call dosentry_check_a20
	jne .doscodeavailable	; not equal, A20 line is switched on -->

	mov ah, 03h
	push bx
	call far [ cs:dosentry_xmsentry ]
				; request global enable A20
	pop bx			; may return an error code in bl
	dec ax			; 0001h if successful
	mov ax, dosentry_msg.a20_error_xms
	jnz .error		; was not 0001h -->

	call dosentry_check_a20	; check again
	jne .doscodeavailable	; not equal, A20 line is now switched on -->

	mov ax, dosentry_msg.a20_error_actual
.error:
	mov si, dosentry_msg.a20_error_common
	call dosentry_disp_msg_cs
	xchg ax, si
	call dosentry_disp_msg_cs
	mov si, dosentry_msg.a20_error_after
	call dosentry_disp_msg_cs
.loop:
	int3
	xor ax, ax
	int 16h
	jmp .loop
	

.doscodeavailable:
	pop si
	mov ax, -1

.not_in_hma:
 %endif
		; ax = DOSCODE segment
	pop ds
		; INP:	ss:(sp + 8) -> arbitrary stack of entrypoint
		;	word [ss:sp + 6] = entrypoint's ip,
		;	word [ss:sp + 4] = entrypoint's cs,
		;	word [ss:sp + 2] = original flags value,
		;	word [ss:sp + 0] = original ax value,
	push ax
	mov ax, relocated_relocatedentry
	push ax
	retf
%else
		; This stub is used by devicerelocate!
	jmp relocated_relocatedentry
%endif

%if _INT19_IN_DOSENTRY
 %if _RELOCATEDOSCODE
i19:
 %endif
%include "int19.asm"
%endif

%if _RELOCATEDOSCODE && _DOSCODEHMA
		; CHG:	ds, si, ax
		; OUT:	NZ if A20 line is switched on
		;	ZR if A20 line is switched off
		;	may return with IF=0
		;	UP
dosentry_check_a20:
	cld
	push es
	push di
	push cx
	xor si, si
	mov ds, si		; ds = 0000h
	dec si
	mov es, si		; es = FFFFh
	inc si			; ds:si = 0000h:0000h =  00000h
	mov di, 0010h		; es:di = FFFFh:0010h = 100000h
				;  (same address if bit 20 off)
	mov cx, di		; 32 byte (16 = 10h word)
	repe cmpsw		; compare, A20 line switched on if differing
	jne .ret		; differing -->

	cli			; try not to run interrupt handlers during this
	mov di, 10h		; -> FFFFh:0010h = 10_0000h
				;  (in the HMA, part of the VDISK header)
	lea si, [di - 10h]	; -> 0000h:0000h = 00_0000h
				;  (in the LMA, offset word of int 00h handler)
	push word [es:di]	; save value
	dec word [es:di]	; change value (in HMA, or wrapped around LMA)
	cmpsw			; compare values, NZ if A20 is switched on
	pop word [es:di - 2]	; restore value
		; This can still report a false negative (A20 detected off
		;  when actually it is on), but we don't care about that.
.ret:
	pop cx
	pop di
	pop es
	retn


dosentry_disp_msg_cs:
	push ax
@@:
	cs lodsb
	test al, al
	jz @F
	call dosentry_disp_al
	jmp short @B


dosentry_disp_al:
	push ax
	push bx
	push bp
	mov ah, 0Eh
	mov bx, 7
	int 10h
	pop bp
	pop bx
@@:
	pop ax
	retn


dosentry_msg:
.a20_error_common:	asciz "DOSENTRY A20 error: "
.a20_error_xms:		asciz "XMS call failed."
.a20_error_actual:	asciz "XMS call reported success but A20 is still off."
.a20_error_after:	asciz 13,10,"System halted. "

	align 4
dosentry_xmsentry:	dd 0
%endif


%include "bioentry.asm"

