;	SCCSID = @(#)msinit.asm 1.2 85/07/23
; TITLE MSINIT.ASM -- MS-DOS INITIALIZATION CODE
; AN000 version 4.0 Jan. 1988
; AN007 PTM 3957 - fake version for IBMCACHE.COM
; AN008 PTM 4070 - fake version for MS WINDOWS
[list -]
%include "sysvar.mac"
%include "doscntry.mac"
%include "fastopen.mac"
%include "lmacros3.mac"
%include "entrysw.mac"
%include "lstruct.mac"
%include "entryseg.nas"
[list +]

extern i20, i21, i25, i26, i27, i2F, i2D, call5, i23, i24, entry_iret, i31

	I_need	DMAAdd,DWORD		; current dma address
	I_need	DPBHead,DWORD		; long pointer to DPB chain
	I_need	SFT_Addr,DWORD		; pointer to open file list
	I_need	NumIO,BYTE		; number of physical drives
	I_need	BuffHead,DWORD		; pointer to buffer chain
	I_need	EndMem,WORD		; first unavailable address in memory
	I_need	CurrentPDB,WORD 	; current process ID
	I_need	CreatePDB,BYTE		; TRUE => create a new PDB
	I_need	Arena_Head,WORD 	; paragraph address of head of arena
	I_need	sfTabl,BYTE		; internal file table
	I_need	SysInitVar,BYTE 	; label for internal structures
	I_need	NulDev,DWORD		; long pointer to device chain
	I_need	BCon,DWORD		; pointer to console device
	I_need	BClock,DWORD		; pointer to clock device
	I_need	CallUnit,BYTE		; unit field in dd packet
	I_need	CallBPB,DWORD		; returned BPB from DD
	I_need	Maxsec,WORD
	I_need	Dskchret,BYTE
	I_need	Devcall,BYTE
	i_need	Header,BYTE
	I_need	JShare,DWORD
	I_need	COUNTRY_CDPG,BYTE	 ; country info table, DOS 3.3
	I_need	SysInitTable,BYTE	 ; sys init table for SYSINIT
	I_need	FastOpenTable,BYTE	 ; table for FASTOPEN
	I_need	FETCHI_TAG,WORD 	 ; TAG CHECK
	I_need	Special_Entries,WORD	 ; address of special entries ;AN007;
	I_need	IFS_DOS_CALL,DWORD	 ; IFS IBMDOS CALL entry      ;AN000;
	I_need	HASHINITVAR,WORD	 ; hash table variables       ;AN000;
	I_need	Packet_Temp,WORD	 ; used for initial Hash table;AN000;
	I_need	BUF_HASH_PTR,DWORD	 ; used for initial Hash table;AN000;
	I_need	SWAP_ALWAYS_AREA,DWORD	 ; swap always area addr    ;AN000;
	I_need	SWAP_ALWAYS_AREA_LEN,WORD; swap always area length  ;AN000;
	I_need	SWAP_IN_DOS,DWORD	 ; swap in dos area	    ;AN000;
	I_need	SWAP_IN_DOS_LEN,WORD	 ; swap in dos area length  ;AN000;
	I_need	SWAP_AREA_LEN,WORD	 ; swap area length	    ;AN000;
	I_need	SWAP_START,BYTE 	 ; swap start addr	    ;AN000;
	I_need	SWAP_ALWAYS,BYTE	 ; swap always addr	    ;AN000;
	I_need	Hash_Temp,WORD		 ; temporary Hash table     ;AN000;

	i_need	CallDevAd,DWORD

section DATA
	; ORG	0			; reset to beginning of data segment

%ifdef DONOBITS
 [list -]
%endif

%ifndef DONOBITS

Public MSINI001S,MSINI001E
MSINI001S label byte
MSINI001E label byte


section SYSINITSEG PUBLIC class=INIT
section SYSINITTRAIL PUBLIC class=INIT

group SYSINITGROUP SYSINITSEG SYSINITTRAIL

extern doscode_retf
extern dosdata_to_doscode

%imacro neartransfer 1.nolist
	extern %1
	call transfer_sysinitseg_to_doscode
	jmp strict short %%skip
	dw %1
%%skip:
%endmacro

 global transfer_sysinitseg_to_doscode
transfer_sysinitseg_to_doscode:		; near call ip: in_ip_out_cs
 assume ds:nothing, es:nothing, ss:nothing
	push ax				; in_ax_out_ip
	mov ax, doscode_retf
	push ax				; out_retf
	push ax				; out_destination + 2
	push ax				; out_destination + 0
	lframe 0
	lpar word, in_ip_out_cs
	lpar word, in_ax_out_ip
	lpar word, out_retf
	lpar dword, out_destination
	lenter
	push ds
	push si
	mov si, cs			; si = cs
	xchg si, word [bp + ?in_ip_out_cs]
					; set cs, get ip
	push si				; preserve ip for later
	lodsw				; skip jmp short
	cs lodsw			; get destination
	mov word [bp + ?out_destination + 0], ax
					; set destination
	call getdosdata
	mov ds, ax
 assume ds:DOSGROUP
	mov ax, word [dosdata_to_doscode]
					; ax => DOSCODE
	mov word [bp + ?out_destination + 2], ax
					; => DOSCODE
	pop ax
	xchg ax, word [bp + ?in_ax_out_ip]
					; ax = original ax, set out ip
	pop si
	pop ds
 assume ds:nothing
	lleave
	retf


	global sysinit_getdosdata
sysinit_getdosdata:
getdosdata:
 assume ds:nothing
	push ds
	mov ax, 0
	mov ds, ax			; ds => IVT
 assume ds:IVT
	mov ax, [31h * 4 + 2]		; ds => DOSDATA
	pop ds
 assume ds:nothing
	retn


section DATA

%macro replayrecorded 2-*.nolist
 %rep (%0 - 2) / 2
  %rotate 2
  %assign %%offset %1
  %assign %%value %2
  %if ($ - MSINI001S) < %%offset
	org %%offset
  %endif
  %if ($ - MSINI001S) == %%offset
	db %%value
  %endif
 %endrep
%endmacro

	replayrecorded RECORDED

	org (MSDAT001e - MSDAT001S)
%else	; DONOBITS
	org (MSDAT001e - MSDAT001S), db ?
%endif	; DONOBITS

[list +]

; (no prior section) ; DATA	ENDS

; the next segment defines a new class that MUST appear last in the link map.
; This defines several important locations for the initialization process that
; must be the first available locations of free memory.

section LAST

%ifdef DONOBITS
 [list -]
%endif

%ifndef DONOBITS

entry DOSINIT
 assume ds:nothing, es:nothing, ss:nothing
%ifndef DOPLACEHOLDER
	jmp DOSENTRY - DOSENTRYADJUSTSEGMENT:i31 + DOSENTRYADJUSTOFFSET
%else
	jmp 0:0
%endif

	align 2, nop

	PUBLIC	SYSBUF
	PUBLIC	MEMSTRT

SYSBUF	LABEL	WORD

section SYSINITTRAIL

%ifndef DOPLACEHOLDER

		; INP:	dx => DOSDATA segment source (0 if initial)
		;	ax => DOSDATA segment destination
		;	cs:si -> table, 0FFFFh terminated
		;	ds:di -> base for table entries
init_table_plus_di:
 assume ds:nothing
.loop:
	xchg bx, ax
	cs lodsw
	cmp ax, -1
	xchg bx, ax
	je .ret
	cmp word [di + bx], dx
	jne .next
	mov word [di + bx], ax
.next:
	jmp .loop

.ret:
	retn


	align 2, db 0
table_country:
	dw ccUcase_ptr + 2
	dw ccFileUcase_ptr + 2
	dw ccFileChar_ptr + 2
	dw ccCollate_ptr + 2
	dw ccDBCS_ptr + 2
	dw -1

table_sysinit:
	dw SYSI_Country_Tab + 2
	dw SYSI_InitVars + 2
	dw -1

%ifdef BUF2
%else
table_buffers:
BUFFHEAD equ BuffHead	; NASM port label
	dw BUFFHEAD + 2
	dw BUF_HASH_PTR + 2
	dw -1
%endif

		; INP:	dx => DOSDATA segment source (0 if initial)
		;	ax => DOSDATA segment destination
		;	cannot overlap
		;	data copied already
		; OUT:	es = ds = ax
		; CHG:	bx, si, di, cx
entry DOSREINIT
	xor bx, bx
	mov ds, bx
 assume ds:IVT
	mov es, ax
 assume es:nothing
	mov word [31h * 4 + 2], es	; RI31S => DOSDATA
	mov ds, ax
 assume ds:DOSGROUP, es:DOSGROUP
DSKCHRET equ Dskchret	; NASM port label
	MOV	WORD PTR [DSKCHRET+3], ax
	MOV	WORD PTR [sft_addr+2], ax
		; Must be set to print messages
	MOV	WORD PTR [SWAP_ALWAYS_AREA+2], ax
	MOV	WORD PTR [SWAP_IN_DOS+2], ax
 extern dosinfo
	mov word [cs:dosinfo + 2], ax
 extern SYSI_Country
	cmp word [cs:SYSI_Country + 2], dx
	jne @F
	mov word [cs:SYSI_Country + 2], ax
@@:

;F.C Modification start  DOS 3.3
	mov di, COUNTRY_CDPG wrt DOSGROUP	; country info table address
	mov si, table_country
	call init_table_plus_di

	mov di, SysInitTable wrt DOSGROUP
	mov si, table_sysinit
	call init_table_plus_di

%ifdef BUF2
%else
	xor di, di
	mov si, table_buffers
	call init_table_plus_di
%endif

	test dx, dx			; re-init after relocation ?
	jz .retn				; no, just return -->
	; Now the fun starts. Poke into sharer table to find the sharer.
 extern JShare, okcallentry
	push es
	push dx
	les di, [JShare + 4]		; entry for MFT_enter
 assume es:nothing
	mov dx, es
	cmp dx, DOSENTRY
	jne @F
	cmp di, okcallentry
	jne @F
	pop dx
	pop es
 assume es:DOSGROUP
.retn:
	retn

 extern init2_disp_dxax_times_cx_width_bx_size
 extern init2_disp_msg_asciz_cs_dx
 extern init2_disp_al
 extern init2_disp_dxax_hex
 extern init2_disp_ax_hex
 extern init2_disp_al_hex
 extern init2_disp_ax_dec

%include "mft.mac"

@@:
dosreinit_scan_sharer:
	push ax
	lframe 0
	lpar word, orig_es
	lpar word, orig_dx
	lpar word, orig_ax
	lenter
	xor dx, dx
	lvar word, seensftcount
	 push dx
	lvar word, editsftcount
	 push dx
	lvar word, seenlockcount
	 push dx
	lvar word, editlockcount
	 push dx
	lvar word, seenmftcount
	 push dx
	lvar word, editmftcount
	 push dx
	lvar word, sftloopdetection
	 push dx
	lvar word, lockloopdetection
	 push dx
	mov dx, initmsg.share.detected.1
	call init2_disp_msg_asciz_cs_dx
	mov ax, es
	call init2_disp_ax_hex
	mov dx, initmsg.share.detected.2
	call init2_disp_msg_asciz_cs_dx

	push ds
	 push cs
	 pop ds
 assume ds:SYSINITGROUP
	xor di, di
	mov cx, 16 * 1024
.loop_scan:			; if branching here with cx = 0, must be NZ
	mov si, sharer_magic_code	; access with ds
	mov al, [si]
	repne scasb
	jne .none		; no luck -->
	 push cx
	 push di
	dec di
	mov cx, sharer_magic_code.size
	mov ax, [es:di + sharer_magic_code.mft_offset]
		; This code must take care not to access the word
		;  at offset FFFFh. The counter in cx insures that
		;  di isn't too high.
	mov [si + sharer_magic_code.mft_offset], ax
	repe cmpsb
	 pop di
	 pop cx
	jne .loop_scan		; not yet found --> (NZ)
	pop ds
 assume ds:DOSGROUP

	mov dx, initmsg.share.magic.1
	call init2_disp_msg_asciz_cs_dx
	xchg ax, di
	call init2_disp_ax_hex
	xchg ax, di
	mov dx, initmsg.share.magic.2
	call init2_disp_msg_asciz_cs_dx
	call init2_disp_ax_hex
	mov dx, initmsg.share.magic.3
	call init2_disp_msg_asciz_cs_dx

	xchg ax, di
.loop_mft:
 assume ds:DOSGROUP, es:nothing
		; es:di -> next MFT entry
		; ds => new DOSDATA segment
	mov al, byte [es:di + mft_flag]
	cmp al, MFLG_END
	je .done
	cmp al, MFLG_FRE
	je .next_mft
	cmp al, MFLG_NAM
	jne .error
	inc word [bp + ?seenmftcount]
	push es
	push di
	mov bx, di
	mov ax, word [es:di + mft_sptr + 2]
	cmp ax, word [bp + ?orig_dx]
	jne @F
	inc word [bp + ?editmftcount]
	mov word [es:di + mft_sptr + 2], ds
@@:
	les di, [es:di + mft_sptr]
 assume es:nothing
	test di, di			; must have at least one SFT
	jz .error_pop_pop		; if invalid -->
	and word [bp + ?sftloopdetection], 0
.loop_sft:
		; dword [ss:sp] -> current MFT entry
		; bx = current MFT entry offset
		; es:di -> next SFT entry, except if di == 0
	test di, di
	jz .done_sft
	inc word [bp + ?seensftcount]
	inc word [bp + ?sftloopdetection]
	jz .error_pop_pop
	cmp word [es:di + sf_MFT], bx
	jne .error_pop_pop
	mov ax, word [es:di + sf_chain + 2]
	cmp ax, word [bp + ?orig_dx]
	jne @F
	inc word [bp + ?editsftcount]
	mov word [es:di + sf_chain + 2], ds
@@:
	les di, [es:di + sf_chain]
 assume es:nothing
	jmp .loop_sft

.done_sft:
	pop di
	pop es				; restore es:di -> MFT
 assume es:nothing
	push di
	and word [bp + ?lockloopdetection], 0
	mov di, [es:di + mft_lptr]
.loop_lock:
		; word [ss:sp] -> current MFT entry
		; es:di -> next lock record, except if di == 0
		; es => sharer segment
	test di, di
	jz .done_lock
	inc word [bp + ?seenlockcount]
	inc word [bp + ?lockloopdetection]
	jz .error_pop
	mov ax, word [es:di + rlr_sptr + 2]
	cmp ax, word [bp + ?orig_dx]
	jne @F
	inc word [bp + ?editlockcount]
	mov word [es:di + rlr_sptr + 2], ds
@@:
	push es
	push di
	les di, [es:di + rlr_sptr]
 assume es:nothing
	cmp word [es:di + sf_MFT], bx
	pop di
	pop es
 assume es:nothing
	jne .error_pop
	mov di, [es:di + rlr_next]
	jmp .loop_lock

.done_lock:
	pop di
.next_mft:
		; MFT loop doesn't need loop detection
		;  because we detect empty lengths or
		;  offset additions that carry here.
	mov cx, word [es:di + mft_len]
	jcxz .error
	add di, cx
	jnc .loop_mft
	db __TEST_IMM16
.error_pop_pop:
	pop ax
.error_pop:
	pop ax
.error:
	mov dx, initmsg.share.error
	call init2_disp_msg_asciz_cs_dx
	jmp .ret

.done:
	mov dx, initmsg.share.done.1
	call init2_disp_msg_asciz_cs_dx
	mov ax, word [bp + ?editmftcount]
	call init2_disp_ax_dec
	mov dx, initmsg.share.done.2
	call init2_disp_msg_asciz_cs_dx
	mov ax, word [bp + ?seenmftcount]
	call init2_disp_ax_dec
	mov dx, initmsg.share.done.3
	call init2_disp_msg_asciz_cs_dx
	mov ax, word [bp + ?editsftcount]
	call init2_disp_ax_dec
	mov dx, initmsg.share.done.4
	call init2_disp_msg_asciz_cs_dx
	mov ax, word [bp + ?seensftcount]
	call init2_disp_ax_dec
	mov dx, initmsg.share.done.5
	call init2_disp_msg_asciz_cs_dx
	mov ax, word [bp + ?editlockcount]
	call init2_disp_ax_dec
	mov dx, initmsg.share.done.6
	call init2_disp_msg_asciz_cs_dx
	mov ax, word [bp + ?seenlockcount]
	call init2_disp_ax_dec
	mov dx, initmsg.share.done.7
	call init2_disp_msg_asciz_cs_dx
	jmp .ret

.none:
	mov dx, initmsg.share.nomagic
	call init2_disp_msg_asciz_cs_dx
	pop ds
 assume ds:DOSGROUP
.ret:
	lleave
	pop ax
	pop dx
	pop es
 assume es:DOSGROUP
	retn


sharer_magic_code:
 assume nocheck, cs:nothing, ds:nothing, es:nothing, ss:nothing
		; need cs assumption so the following push/pop pair
		;  isn't misdetected by listvars.pl
.:
	push cs
	pop  ds
 assume nocheck, ds:nothing
	mov si, 0
.mft_offset equ ($ - 2) - .
	cmp byte [si + mft_flag],MFLG_FRE
.end:
.size equ .end - .
 assume nocheck, cs:SYSINITGROUP
		; reset cs assumption


initmsg:
.share.detected.1:	asciz "init: Sharer detected at segment="
.share.magic.3:		; asciz "h.",13,10
.share.detected.2:	asciz "h.",13,10
.share.magic.1:		asciz "init: Sharer magic code detected at offset "
.share.magic.2:		asciz "h, MFT start is at "
.share.nomagic:		asciz "init: Warning: Sharer magic code not detected!",13,10
.share.done.1:		asciz "init: Edited/total sharer MFTs "
.share.done.6:		; asciz "/"
.share.done.4:		; asciz "/"
.share.done.2:		asciz "/"
.share.done.3:		asciz ", next SFTs "
.share.done.5:		asciz ", locks "
.share.done.7:		asciz ".",13,10
.share.error:		asciz "init: Error: Sharer tables invalid.",13,10


		; INP:	es => DOS data segment
		;	bx => where to create PSP
		;	dx => behind usable memory (at EBDA or UMA)
		;	ss:sp -> init stack
		;	ds:si -> CON device
		;		(and subsequent device chain in msbio)
		;	cx => S MCB to link to (chain at top of LMA)
		;	di => space for free MCB (behind DOSENTRY S MCB)
	entry	NEARDOSINIT
 assume ds:nothing, es:DOSGROUP, ss:SYSINITGROUP
	CLI
	CLD
	MOV	[es:CurrentPDB], bx
	MOV	[es:ENDMEM],DX
NULDEV equ NulDev	; NASM port label
	MOV	WORD PTR [es:NULDEV+2],DS
	MOV	WORD PTR [es:NULDEV],SI   ; DS:SI Points to CONSOLE Device

	push di
	push cx
	push ds
	push si
			; Need Crit vector inited to use DEVIOCALL
	XOR	AX,AX
	MOV	DS,AX
 assume ds:IVT
	mov word [31h * 4 + 2], es	; RI31S => DOSDATA
	MOV	word [addr_int_IBM], entry_iret + DOSENTRYADJUSTOFFSET
	MOV	word [addr_int_IBM+2], DOSENTRY - DOSENTRYADJUSTSEGMENT

	dec dx			; => first UMCB
	mov word [es:first_umcb], dx
	 push es
	mov es, dx
 assume es:nothing
	xor di, di		; -> first UMCB
	 push cs
	 pop ds
 assume ds:SYSINITGROUP
	mov si, first_umcb_smcb	; -> template ; access with ds
	mov cx, words(MCB_size)
	rep movsw		; copy the template
	 pop es
 assume es:DOSGROUP
	mov ax, es
	xor dx, dx
	call DOSREINIT
 assume ds:DOSGROUP, es:DOSGROUP

	MOV	BP,OFFSET SYSBUF wrt DOSGROUP
	MOV	[Special_Entries],BP ;MS.;AN007 save starting address of Special entries
	MOV	SI,OFFSET Version_Fake_Table wrt DOSGROUP	;MS.;AN007
	MOV	DX,SI		     ;MS.;AN007
	XOR	AH,AH		     ;MS.;AN007
.NextEntry:			     ;MS.;AN007
	LODSB			     ;MS.;AN007  get name length
	test	AL,AL		     ;MS.;AN007  end of list
	JZ	endlist 	     ;MS.;AN007  yes
	ADD	SI,AX		     ;MS.;AN007  position to
	ADD	SI,3		     ;MS.;AN007  next entry
	JMP	.NextEntry	     ;MS.;AN007
endlist:			     ;MS.;AN007
	SUB	SI,DX		     ;MS.;AN007
	ADD	BP,SI		     ;MS.;AN007

	mov cx, si
	mov si, dx
	mov di, SYSBUF wrt DOSGROUP
	rep movsb

	ADD	BP,15		; True start of free space (round up to segment)
	RCR	BP,1
	MOV	CL,3
	SHR	BP,CL		; Number of segments for DOS resources
	MOV	DX,bp		; = size of DOSDATA segment in paragraphs
	MOV	BX,0FH
	MOV	CX,[ENDMEM]

	MOV	BP, es

; BP has segment of DOS (whether to load high or run in place)
; DX has program segment (whether after DOS or overlaying DOS)
; CX has size of memory in paragraphs (reduced by DOS size if HIGHMEM)
	MOV	[ENDMEM],CX
	MOV	ES,BP
ASSUME	ES:DOSGROUP

	pop si
	pop ds
 assume ds:nothing

	push dx
	push bp

	CALL	CHARINIT
	PUSH	SI			; Save pointer to header
		; es => DOSDATA
 ASSUME ES:DOSGROUP
	MOV	DI,OFFSET sftabl + SFTable wrt DOSGROUP   ; Point to sft 0
	MOV	AX,3
	STOSW		; Refcount
	DEC	ax
	errnz	sf_mode-(sf_ref_count+2)
	STOSW		; Access rd/wr, compatibility
	XOR	AL,AL
	errnz	sf_attr-(sf_mode+2)
	STOSB		; attribute
	MOV	AL,devid_device_EOF | devid_device | ISCIN | ISCOUT
	errnz	sf_flags-(sf_attr+1)
	STOSW		; Flags
	MOV	AX,SI
	errnz	sf_devptr-(sf_flags+2)
	STOSW			; Device pointer in devptr
	MOV	AX,DS
	STOSW
	XOR	AX,AX
	errnz	sf_firclus-(sf_devptr+4)
	STOSW			; firclus
	errnz	sf_time-(sf_firclus+2)
	STOSW			; time
	errnz	sf_date-(sf_time+2)
	STOSW			; date
	DEC	AX
	errnz	sf_size-(sf_date+2)
	STOSW			; size
	STOSW
	INC	AX
	errnz	sf_position-(sf_size+4)
	STOSW			; position
	STOSW
	ADD	DI,sf_name - sf_cluspos ;Point at name
	ADD	SI,SDEVNAME		; Point to name
	MOV	CX,4
	REP	MOVSW	; Name
	MOV	CL,3
	MOV	AL," "
	REP	STOSB	; Extension
	POP	SI	; Get back pointer to header
	OR	BYTE PTR [SI + SDEVATT],ISCIN | ISCOUT
BCON equ BCon	; NASM port label
	MOV	WORD PTR [es:BCON],SI
	MOV	WORD PTR [es:BCON+2],DS

	pop bp
	pop dx

	XOR	AX,AX
	MOV	DS,AX
	MOV	ES,AX
 assume ds:IVT, es:IVT
	MOV	DI,INTBASE+2
	MOV	AX,BP		; Final DOS segment to AX

	push ax

	mov ax, DOSENTRY - DOSENTRYADJUSTSEGMENT

	extern i00
	mov word [0], i00 + DOSENTRYADJUSTOFFSET
	mov word [2], ax	; Set default divide trap address

; Set vectors 20-28 and 2A-3F to point to IRET.
	MOV	CX,17
	REP	STOSW		; Set 9 segments
				;   Sets segs for INTs 20H-28H
	ADD	DI,6		; Skip INT 29H vector (FAST CON) as it may
				;   already be set.
	MOV	cl, 43
	REP	STOSW		; Set 22 segments
				;   Sets segs for vectors 2AH-3FH
	pop word [31h * 4 + 2]	; RI31S => DOSDATA

	MOV	DI,INTBASE
	MOV	AX, entry_iret + DOSENTRYADJUSTOFFSET
	MOV	cl, 9		; Set 9 offsets (skip 2 between each)
				;   Sets offsets for INTs 20H-28H

ISET1:
	STOSW			; offset
	scasw			; di += 2
	LOOP	ISET1

	scasw			; di += 2
	scasw			; di += 2
				; Skip vector 29H

	MOV	cl, 22		; Set 22 offsets (skip 2 between each)
				;   Sets offsets for INTs 2AH-3FH

ISET2:
	STOSW
	scasw			; di += 2
	LOOP	ISET2

	MOV	AX,BP		; Final DOS segment to AX

	mov word [2Fh * 4], i2F + DOSENTRYADJUSTOFFSET
	mov word [2Dh * 4], i2D + DOSENTRYADJUSTOFFSET

; Set up entry point call at vectors 30-31H
	MOV	BYTE PTR [ENTRYPOINT],mi_Long_JMP
	MOV	WORD PTR [ENTRYPOINT+1], call5 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [ENTRYPOINT+3], DOSENTRY - DOSENTRYADJUSTSEGMENT
	mov byte [ENTRYPOINT+5], 0
		; this also happens to init int 31h to DOSDATA:0,
		;  which is equal to our legacy DOSINIT entrypoint.
		;  this is now re-used as i31, far-jumping to DOSENTRY
		;  and relocating to DOSCODE. currently this just sets
		;  ax = 1 and CY and returns (using an iret).

	%IF	ALTVECT
	MOV	DI,ALTBASE+2
	MOV	CX,15
	REP	STOSW		; Set 8 segments (skip 2 between each)
	%ENDIF

		; all interrupt handler entrypoints
		;  => DOSENTRY - DOSENTRYADJUSTSEGMENT
	MOV	WORD PTR [addr_int_abort], i20 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [addr_int_command], i21 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [addr_int_terminate],100H
	MOV	WORD PTR [addr_int_terminate+2],DX
	MOV	WORD PTR [23h * 4], i23 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [24h * 4], i24 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [addr_int_disk_read], i25 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [addr_int_disk_write], i26 + DOSENTRYADJUSTOFFSET
	MOV	WORD PTR [addr_int_keep_process], i27 + DOSENTRYADJUSTOFFSET

	call getdosdata
	mov ds, ax
	mov es, ax
ASSUME	DS:DOSGROUP,ES:DOSGROUP

	mov bp, dx		; = size for DOSDATA

	mov dx, 60h		; new first MCB (init PSP)
arena_head equ Arena_Head	; NASM port label
	MOV	[ES:arena_head], dx

	 pop bx			; bx => S MCB to link to
	 pop ax			; ax => free MCB
	push ds
	push es
	mov es, dx		; => DOSENTRY MCB destination
 assume es:nothing
	push cs
	pop ds
 assume ds:SYSINITGROUP
	mov si, dosentry_mcb	; -> template ; access with ds
	xor di, di
	mov cx, words(16)
	rep movsw
	neg dx
	add dx, ax		; dx = ax - dx
	dec dx
	mov word [es:arena_size], dx
				; set size
	pop es
 assume es:DOSGROUP

	MOV	DS,AX
 assume ds:nothing
	MOV	byte [arena_signature], 'M'
		; The free MCB does link to the LMA top chain of S MCBs.
		;  The last LMCB does not initially link to the first
		;  UMCB, but the free MCB is not the last LMCB.
ENDMEM equ EndMem	; NASM port label
	SUB	AX, bx
	not ax			; like neg ax, dec ax
	MOV	[arena_size],AX
	xor ax, ax		; = 0
	mov word [arena_owner], ax
	mov word [mcbReserved], ax
	mov word [mcbReserved + 2], ax
	mov word [mcbName], ax
	mov word [mcbName + 2], ax
	mov word [mcbName + 4], ax
	mov word [mcbName + 6], ax
	pop ds
 assume ds:DOSGROUP

	mov dx, [CurrentPDB]
	mov es, dx
 assume es:nothing
	xor di, di		; -> PSP to create
	mov cx, words(60h)	; clear PSP needed part
	rep stosw		; clear memory
	mov ax, [ENDMEM]

	neartransfer	SETMEM	; Basic Header
				; SETMEM works with ss != DOSDATA
	mov word [ES:PDB_Parent_PID], es
				; set parent to itself (better than uninit)
ASSUME	DS:NOTHING,ES:NOTHING
	call getdosdata
	mov ds, ax
ASSUME	DS:DOSGROUP
	MOV	DI,PDB_JFN_Table
	XOR	AX,AX
	STOSW
	STOSB			; 0,1 and 2 are CON device
	MOV	AL,0FFH
	MOV	CX,FilPerProc - 3
	REP	STOSB		; Rest are unused
	PUSH	ds
	POP	ES
ASSUME	ES:DOSGROUP

; After this points the char device functions for CON will work for
; printing messages

	%IF	(! IBM) | (DEBUG)
	%IFN ALTVECT
HEADER equ Header	; NASM port label
	MOV	SI,OFFSET HEADER wrt DOSGROUP
.OUTMES:
	call getdosdata
	mov ds, ax
	lodsb
	CMP	AL,"$"
	JZ	.OUTDONE
	invalid function call
	invoke	OUT
	JMP	SHORT .OUTMES
.OUTDONE:
	PUSH	ds			; OUT stomps on segments
	POP	es
	%ENDIF
	%ENDIF

%ifdef BUF2
%else
	MOV	SI,OFFSET HASHINITVAR wrt DOSGROUP	  ;LB. points to Hashinitvar	   ;AN000;
	MOV	WORD PTR [ES:BUFFHEAD],SI	  ;LB.				   ;AN000;
	MOV	SI,OFFSET Hash_Temp wrt DOSGROUP	  ;LB.				   ;AN000;
	MOV	WORD PTR [ES:BUF_HASH_PTR],SI	  ;LB.				   ;AN000;
%endif
	MOV	word [ES:FETCHI_TAG],22642	  ; TAG for IBM,
					  ; Fetchi's serial # = 822642

	mov bx, bp			; = size for DOSDATA S MCB
	mov ah, 4Ah
	int 21h				; shrink it
	mov ax, es
	dec ax				; => D0SDATA S MCB
	mov ds, ax
 assume ds:MCB
	mov word [mcbOwner], 8		; reset owner

	MOV	DI,OFFSET SWAP_START wrt DOSGROUP	  ;IFS. 			;AN000;
	MOV	CX,OFFSET SWAP_END wrt DOSGROUP	  ;IFS. 			;AN000;
Swap_Always equ SWAP_ALWAYS	; NASM port label
	MOV	DX,OFFSET Swap_Always wrt DOSGroup	  ;IFS. 			;AN000;
	MOV	BP,CX			;IFS.					;AN000;
	SUB	BP,DI			;IFS.					;AN000;
	SHR	BP,1			;IFS. div by 2, remainder in carry	;AN000;
	ADC	BP,0			;IFS. div by 2 + round up		;AN000;
	SHL	BP,1			;IFS. round up to 2 boundary.		;AN000;
	MOV	[ES:SWAP_AREA_LEN],BP	;IFS.					;AN000;

	SUB	CX,DX			;IFS.					;AN000;
	SUB	DX,DI			;IFS.					;AN000;
	SHR	CX,1			;IFS. div by 2, remainder in carry	;AN000;
	ADC	CX,0			;IFS. div by 2 + round up		;AN000;
	SHL	CX,1			;IFS. round up to 2 boundary.		;AN000;
	MOV	[ES:SWAP_IN_DOS_LEN],CX 	    ;IFS.			;AN000;
	MOV	WORD PTR [ES:SWAP_ALWAYS_AREA],DI   ;IFS.			;AN000;
	OR	DX,8000H			    ;IFS.			;AN000;
	MOV	[ES:SWAP_ALWAYS_AREA_LEN],DX	    ;IFS.			;AN000;
	MOV	DI,OFFSET Swap_Always wrt DOSGroup	    ;IFS.			;AN000;
	MOV	WORD PTR [ES:SWAP_IN_DOS],DI	    ;IFS.			;AN000;

SySInitTable equ SysInitTable	; NASM port label
	MOV	DI,OFFSET SySInitTable wrt DOSGROUP

	%IFN Installed
	invoke	NETWINIT
;	ELSE
;	invoke	NETWINIT
;	%OUT Random NETWINIT done at install
	%ENDIF

	mov ds, word [es:CurrentPDB]	; ds => created PSP
 assume ds:nothing
DMAADD equ DMAAdd	; NASM port label
	MOV	WORD PTR [es:DMAADD+2], ds
	retn


entry INITDPB
 assume ds:nothing, es:nothing, ss:SYSINITGROUP
	call getdosdata
	mov es, ax
 assume es:DOSGROUP

	lds si, [es:NULDEV]
 assume ds:nothing
CHAR_INIT_LOOP:
	LDS	SI,[SI]		; AUX device
 assume ds:nothing
	CALL	CHARINIT
	TEST	BYTE PTR [SI + SDEVATT],ISCLOCK
	JZ	CHAR_INIT_LOOP
BCLOCK equ BClock	; NASM port label
	MOV	WORD PTR [es:BCLOCK],SI
	MOV	WORD PTR [es:BCLOCK+2],DS

	mov ah, 48h
	mov bx, paras(DPBSIZ * 2)
	int 21h
	jnc @F
mem_err_j:
	extern MEM_ERR
	jmp MEM_ERR
@@:
	lframe
	lenter
	lvar word, dosdataseg
	 push es		; => DOS data segment
	lvar dword, nextdpb
	 push ax		; => DPB allocation
	xor ax, ax
	 push ax		; -> next DPB
PERDRIVER:
	mov es, word [bp + ?dosdataseg]
 assume es:DOSGROUP
	LDS	SI,[SI]		; Next device
 assume ds:nothing
	CMP	SI,-1
	JZ	CONTINIT
	CALL	CHARINIT
	TEST	word [SI + SDEVATT],DEVTYP
	JNZ	PERDRIVER		; Skip any other character devs
CALLUNIT equ CallUnit	; NASM port label
	xor cx, cx
	MOV	CL,[es:CALLUNIT]
	MOV	[SI + SDEVNAME],CL	; Number of units in name field
	jcxz PERDRIVER
	PUSH	DS
	PUSH	SI
	MOV	DL,[es:NUMIO]
	XOR	DH,DH
	ADD	[es:NUMIO],CL
CALLBPB equ CallBPB	; NASM port label
	LDS	BX,[es:CALLBPB]
 assume ds:nothing
PERUNIT:
	MOV	SI,[BX] 		; DS:SI Points to BPB
	INC	BX
	INC	BX			; On to next BPB

	push bp
	les bp, [bp + ?nextdpb]
 assume es:nothing

	test bp, bp
	jz @F
	mov word [es:bp - DPBSIZ + dpb_next_dpb], bp
	mov word [es:bp - DPBSIZ + dpb_next_dpb + 2], es
@@:

	PUSH	BX
	PUSH	CX
	PUSH	DX

					; es => DPB allocation
	mov cl, 4
	lea bx, [bp + DPBSIZ + 15]	; -> behind this DPB, round up
	cmp bx, bp
	jb mem_err_j
	shr bx, cl			; to paragraphs
	mov ah, 4Ah
	int 21h				; resize
	jc mem_err_j

		; ! only write the next DPB after it is allocated
	pop dx				; = unit/drive numbers
%if dpb_drive + 1 != dpb_UNIT
 %error Unexpected layout
%endif
	MOV	[ES:BP + dpb_drive], dx

	mov ax, -1
	mov word [es:bp + dpb_next_dpb], ax
	mov word [es:bp + dpb_next_dpb + 2], ax
	push dx				; push unit/drive numbers again

		; es:bp -> DPB, ds:si -> BPB
%if 0
	neartransfer	D_SETDPB
				; D_SETDPB works with ss != DOSDATA
%else
	mov ah, 53h
	int 21h
%endif
	mov byte [es:bp + dpb_first_access], -1	 ; Never accessed before
	MOV	AX,[ES:BP + dpb_sector_size]
	POP	DX
	POP	CX
	POP	BX

	pop bp
	mov es, word [bp + ?dosdataseg]
 assume es:DOSGROUP

MAXSEC equ Maxsec	; NASM port label
	CMP	AX,[es:MAXSEC]
	JBE	.NOTMAX
	MOV	[es:MAXSEC],AX
.NOTMAX:


	MOV	AX,DS			; Save DS
	POP	SI
	POP	DS
 assume ds:nothing

	push bp
	les bp, [bp + ?nextdpb]
 assume es:nothing
 
	MOV	WORD PTR [ES:BP + dpb_driver_addr],SI
	MOV	WORD PTR [ES:BP + dpb_driver_addr+2],DS

	pop bp

	PUSH	DS
	PUSH	SI
	INC	DH
	INC	DL
	MOV	DS,AX
 assume ds:nothing
	ADD	word [bp + ?nextdpb],DPBSIZ
	LOOP	PERUNIT
	POP	SI
	POP	DS
 assume ds:nothing
	JMP	PERDRIVER

	align 2
first_umcb_smcb:
	istruc MCB
at mcbSignature,	db 'Z'
at mcbOwner,		dw 8
at mcbSize,		dw 0
at smcbName,		db "S", 0
at smcbType,		db S_EXCLDUMA
	iend

dosentry_mcb:
	istruc MCB
at mcbSignature,	db 'M'
at mcbOwner,		dw 8
at mcbSize,		dw 0
at smcbName,		db "S", 0
at smcbType,		db S_DOSENTRY
	iend

dpb_smcb:
	db 0,0,0
	db "S",0		; name "S"
	db 8			; type S_DPB
	times 5 db 0

CONTINIT:		; es => DOSDATA
 assume ds:nothing, es:DOSGROUP
	pop ax			; offset last DPB
	pop ax			; => DPB allocation
	mov word [es:DPBHEAD + 2], ax
	dec ax
	mov es, ax
 assume es:MCB
	mov word [es:arena_owner], 8
				; init owner
	mov di, arena_reserved
	 push cs
	 pop ds
 assume ds:SYSINITGROUP
	mov si, dpb_smcb	; access with ds
	mov cx, 3 + 8
	rep movsb		; init name and type

	pop ds			; => DOS data
 assume ds:DOSGROUP
	PUSH	ds
	POP	es
 assume es:DOSGROUP

	lleave

sft_addr equ SFT_Addr	; NASM port label
NUMIO equ NumIO	; NASM port label
DPBHEAD equ DPBHead	; NASM port label
DOSGroup equ DOSGROUP	; NASM port equate
sftabl equ sfTabl	; NASM port label

	retn


CHARINIT:
ASSUME	DS:NOTHING,ES:NOTHING
; DS:SI Points to device header
DEVCALL equ Devcall	; NASM port label
	PUSH	ES
	PUSH	BX
	PUSH	AX
	call getdosdata
	mov es, ax		; => DOSDATA
 assume es:DOSGROUP
	pop ax
	push ax
	MOV	BX,OFFSET DEVCALL wrt DOSGROUP
	MOV	byte [es:bx + REQLEN],DINITHL
	MOV	byte [es:bx + REQUNIT],0
	MOV	byte [es:bx + REQFUNC],DEVINIT
	and	word [es:bx + REQSTAT],0
%if 0
	neartransfer	DEVIOCALL2
%else
		; device critical section is not yet enabled
	MOV	AX, [SI + SDEVSTRAT]
CALLDEVAD equ CallDevAd	; NASM port label
	MOV	WORD PTR [es:CALLDEVAD],AX
	MOV	WORD PTR [es:CALLDEVAD+2],DS
	CALL	far [es:CALLDEVAD]
	MOV	AX,[SI + SDEVINT]
	MOV	WORD PTR [es:CALLDEVAD],AX
	CALL	far [es:CALLDEVAD]
%endif
	POP	AX
	POP	BX
	POP	ES
 assume es:nothing
	RET

%endif	; DOPLACEHOLDER


section LAST

Public MSINI002S,MSINI002E
MSINI002S label byte

	align 2, db ?
	DW	?
	DB	"ADD SPECIAL ENTRIES",0     ;AN007  tiltle
;The following entries don't expect version 4.0
;The entry format: name_length, name, expected version, fake count
;fake_count: ff means the version will be reset when Abort or Exec is encountered
;	     n means the version will be reset after n DOS version calls are issued
;
Version_Fake_Table:			    ;AN007  starting address for special
	DB	12,"IBMCACHE.COM",3,40,255  ;AN007  ibmcache     1
	DB	12,"IBMCACHE.SYS",3,40,255  ;AN007  ibmcache     2
	DB	12,"DXMA0MOD.SYS",3,40,255  ;AN007  lan support  3
	DB	10,"WIN200.BIN"  ,3,40,4    ;AN008  windows      4
	DB	 9,"PSCPG.COM"   ,3,40,255  ;AN008  vittoria     5
	DB	11,"DCJSS02.EXE" ,3,40,255  ;AN008  netview      6
	DB	 8,"ISAM.EXE"    ,3,40,255  ;AN008  basic        7
	DB	 9,"ISAM2.EXE"   ,3,40,255  ;AN008  basic        8
	DB	12,"DFIA0MOD.SYS",3,40,255  ;AN008  lan support  9
	DB	20  dup(0)		    ;AN007

	align 16, db 0

MEMSTRT LABEL	WORD
MSINI002E label byte
ADJFAC	EQU	MEMSTRT-SYSBUF

%endif	; DONOBITS

[list +]

; (no prior section) ; LAST	ENDS
