
%if 0

lDOS initialisation
 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


%include "lmacros2.mac"
%include "lstruct.mac"

defaulting

numdef DEBUG0
numdef DEBUG1
numdef DEBUG2
numdef DEBUG3
numdef DEBUG4
numdef DEBUG5
%idefine d5 _d 5,

%assign _COMBINED_DOSDATA_DOSCODE 1

                ; Write a label "%1" (only if DOSCODE isn't relocated) and
                ; "relocated%1" (always).
        %imacro relocated 1+.nolist
%ifn _RELOCATEDOSCODE
%1
%endif
relocated%1                                     ; always used for local labels
        %endmacro

	struc FSPARM
fspStart:	resd 1
fspLength:	resd 1
fspUnit:	resb 1
fspPartition:	resb 1
fspBoot:	resb 1
fspType:	resb 1
fspSectorSize:	resw 1
		resw 1
	endstruc


	[map all lDOSLOAD.MAP]

	cpu 8086
	section INIT0 start=0 vstart=0
init0_start:
	cld
	mov ax, cs
	add ax, (init0_end - init0_start) >> 4
	mov cx, ( 16 \
		+ (dosentry_end - dosentry_start) \
		+ 16 \
		+ (doscode_end - doscode_start) \
		+ 16 \
		+ (dosdata_end - dosdata_start) \
		+ 16 \
		+ (init1_end - init1_start) \
		+ 16 \
		) >> 4
	mov dx, word [ bp + ldLoadTop ]
	sub dx, cx
	call init0_movp

	mov ax, dx
	add ax, ( \
		+ 16 \
		+ (dosentry_end - dosentry_start) \
		+ 16 \
		+ (doscode_end - doscode_start) \
		+ 16 \
		+ (dosdata_end - dosdata_start) \
		+ 16 \
		) >> 4
	xor bx, bx
	push ax
	push bx
	retf


		; Move paragraphs
		;
		; INP:	ax:0-> source
		;	dx:0-> destination
		;	cx = number of paragraphs
		; CHG:	-
		; OUT:	NC
		; Note:	Doesn't work correctly on HMA; doesn't always wrap to LMA either.
		;	Do not provide a wrapped/HMA source or destination!
init0_movp:
	push cx
	push ds
	push si
	push es
	push di

	cmp ax, dx		; source below destination ?
	jb .down		; yes, move from top down -->
	je .return		; same, no need to move --> (NC)

	push ax
	push dx
.uploop:
	mov ds, ax
	mov es, dx
	xor di, di
	xor si, si		; -> start of segment
	sub cx, 1000h		; 64 KiB left ?
	jbe .uplast		; no -->
	push cx
	mov cx, 10000h /2
	rep movsw		; move 64 KiB
	pop cx
	add ax, 1000h
	add dx, 1000h		; -> next segment
	jmp short .uploop	; proceed for more -->
.uplast:
	add cx, 1000h		; restore counter
	shl cx, 1
	shl cx, 1
	shl cx, 1		; *8, paragraphs to words (NC)
	rep movsw		; move last part
	pop dx
	pop ax
	jmp short .return

.down:
	std
.dnloop:
	sub cx, 1000h		; 64 KiB left ?
	jbe .dnlast		; no -->
	push ax
	push dx
	add ax, cx
	add dx, cx
	mov ds, ax		; -> 64 KiB not yet moved
	mov es, dx
	pop dx
	pop ax
	mov di, -2
	mov si, di		; moved from last word down
	push cx
	mov cx, 10000h /2
	rep movsw		; move 64 KiB
	pop cx
	jmp short .dnloop	; proceed for more -->
.dnlast:
	add cx, 1000h		; restore counter
	shl cx, 1
	shl cx, 1
	shl cx, 1		; *8, paragraphs to words
	mov di, cx
	dec di
	shl di, 1		; words to offset, -> last word (NC)
	mov si, di
	mov ds, ax
	mov es, dx		; first segment correct
	rep movsw		; move first part
	cld
.return:
	pop di
	pop es
	pop si
	pop ds
	pop cx
	retn

	align 16
init0_end:
	istruc MCB
at mcbSignature,	db "M"
at mcbOwner,		dw 8
at mcbSize,		dw (dosentry_end - dosentry_start) >> 4
at smcbName,		dw "S"
at smcbType,		db S_DOSENTRY
	iend

	section DOSENTRY align=16 follows=INIT0 vstart=0
dosentry_start:
%include "entry.asm"

	align 16
dosentry_end:
	istruc MCB
at mcbSignature,	db "M"
at mcbOwner,		dw 8
at mcbSize,		dw (doscode_end - doscode_start) >> 4
at smcbName,		dw "S"
at smcbType,		db S_DOSCODE
	iend

	section DOSCODE align=16 follows=DOSENTRY vstart=40h
			; vstart:
			; +10h for end of ROM-BIOS image (at 0FFFFh:0)
			; +20h for VDISK header
			; +10h for first HMCB
doscode_start:
	times 32 db "C"

	align 16
doscode_end:
	istruc MCB
at mcbSignature,	db "M"
at mcbOwner,		dw 8
at mcbSize,		dw (dosdata_end - dosdata_start) >> 4
at smcbName,		dw "S"
at smcbType,		db S_DOSDATA
	iend

	section DOSDATA align=16 follows=DOSCODE vstart=0
dosdata_start:

%define _cmp32 dos_cmp32
%define _mul32 dos_mul32
%define _div32 dos_div32
%define DaysInMonthTable dos_DaysInMonthTable
%define getDaysSince1980 dos_getDaysSince1980
%include "RxDOS.ASM"

%undef _cmp32
%undef _mul32
%undef _div32
%undef DaysInMonthTable
%undef getDaysSince1980

	align 16
dosdata_end:
	istruc MCB
at mcbSignature,	db "M"
at mcbOwner,		dw 8
at mcbSize,		dw (init1_end - init1_start) >> 4
at smcbName,		dw "S"
at smcbType,		db S_INIT
	iend

	section INIT1 align=16 follows=DOSDATA vstart=0
init1_start:
		; S0 +28
	pushf	; +26
	jmp sig_valid

msg:
.error:	db "Test payload error: ", 0
.test:	db "Test payload loaded.", 13, 10, 0

	align 4
.foundname:
	times 8+1+3+1 db 0
		; buffer for base name (8) + dot (1) + ext (3) + NUL (1)
	align 2
.foundname_default:
	asciz "LDOS.COM"
	_fill (8 + 1 + 3 + 1), 0, .foundname_default
	align 2
.foundname_none:
	asciz "(None)"
.foundname_none_size: equ $ - .foundname_none
	align 2
.names:
	dw .name_first, 0
	dw .name_second, 0
	dw .name_third, 0
	dw .name_fourth, 0
	dw 0
.name_first:	asciz "1st name"
.name_second:	asciz "2nd name"
.name_third:	asciz "3rd name"
.name_fourth:	asciz "4th name"
.name_before:	asciz ": "
.name_quote:	asciz '"'
.name_after:	asciz 13,10

.bootfail:	asciz "init: Boot failure: "
.bootfail_read:	db "Reading sector failed (error "
.bootfail_read_errorcode:	asciz "__h).",13,10
.bootfail_sig:	asciz "Boot sector signature missing (is not AA55h).",13,10
.bootfail_sig_parttable:	ascii "Partition table signature missing"
				asciz " (is not AA55h).",13,10
.bootfail_secsizediffer:
		asciz "BPB BpS differs from actual sector size.",13,10
.boot_too_many_partitions_error:asciz "Too many partitions (or a loop).",13,10
.boot_partition_cycle_error:	asciz "Partition table cycle detected.",13,10
.boot_partition_not_found:	asciz "Partition not found.",13,10
.boot_access_error:	asciz "Read error.", 13,10
.boot_sector_too_large:	asciz "Sector size too small (< 32 bytes).", 13,10
.boot_sector_too_small:	asciz "Sector size too large (> 8192 bytes).", 13,10
.boot_sector_not_power:	asciz "Sector size not a power of two.", 13,10
.boot_invalid_sectors:	asciz "Invalid geometry sectors.", 13,10
.boot_invalid_heads:	asciz "Invalid geometry heads.", 13,10
.boot_badclusters:	asciz "Bad amount of clusters.",13,10
.boot_badchain:		asciz "Bad cluster chain.",13,10
.boot_internal_error:	asciz "! Internal error !",13,10

.login_partition_error_1:	asciz "init: Error during unit "
.login_partition_error_2:	asciz "h partitions login.",13,10

.excess_partitions_1:		asciz "init: Found "
.excess_partitions_2:		asciz " excess partitions, ignored.",13,10

.list_partition_1:		asciz ": "
.list_partition_4:		asciz " s="
.list_partition_5:		asciz "h ("
.list_partition_6:		asciz ") l="
.list_partition_7:		asciz "h ("
.list_partition_8:		asciz ") "
.list_partition_9:		asciz " t="
.list_partition_10:		asciz "h ("
.list_partition_11:		asciz ").",13,10

.prefixes:			asciz " kMGT"

.bootname_1:	asciz "init: Boot drive and file name: ",'"'
.bootname_2:	asciz '"',13,10


%define pt_messages ..@pt_notype,""

	%imacro pt_type 2.nolist
		dw %2, %%label
%defstr %%str	%1
%xdefine pt_messages pt_messages,%%label,%%str
	%endmacro

	%imacro pt_msg 2-*.nolist
%if %0 & 1
 %error Expected even number of parameters
%endif
%rotate 2
%rep (%0 - 2) / 2
%1:	asciz %2
%rotate 2
%endrep
	%endmacro

	align 4
pt_types:
 pt_type	Empty,		0
 pt_type	FAT12,		1
 pt_type	FAT16 16BIT CHS,4
 pt_type	ExtendedCHS,	5
 pt_type	FAT16 CHS,	6
 pt_type	FAT32 CHS,	0Bh
 pt_type	FAT32,		0Ch
 pt_type	FAT16,		0Eh
 pt_type	Extended,	0Fh
 pt_type	Linux,		83h
 pt_type	ExtendedLinux,	85h
	dw -1, -1

pt_msg pt_messages

ptmsg_unknown:	asciz "Unknown"

%undef pt_messages
%unimacro pt_type 2.nolist
%unimacro pt_msg 2-*.nolist


init1_msg:
.alloc_error:		asciz "init: Error "
.alloc_error_q:		asciz "h while allocating memory: "
.linebreak:		asciz 13,10
.internal_error_upb:	asciz "init: Internal error during UPB allocation.",13,10
.reboot_prompt:		asciz "Halted. Press Ctrl+Alt+Del to reboot.",13,10
.loading_os:		asciz "Starting ",RXDOSLONG_S,"...",13,10

	align 2
init1_msg_errors:
.:
	dw .no_error
	dw .invalid_function
	dw .file_not_found
	dw .path_not_found
	dw .no_handles_available
	dw .access_denied
	dw .invalid_handle
	dw .mcb_destroyed
	dw .insufficient_memory
	dw .mcb_invalid
	dw .environment_invalid
	dw .format_invalid
	dw .access_code_invalid
	dw .data_invalid
	dw .fixup_overflow
	dw .invalid_drive
	dw .attempted_to_remove_current_directory
	dw .not_same_device
	dw .no_more_files
.count equ ($ - .) / 2
.unknown_error:		db "Unknown error",0
.no_error:		db "No error",0
.invalid_function:	db "Invalid function",0
.file_not_found:	db "File not found",0
.path_not_found:	db "Path not found",0
.no_handles_available:	db "No handles available",0
.access_denied:		db "Access denied",0
.invalid_handle:	db "Invalid handle",0
.mcb_destroyed:		db "MCB destroyed",0
.insufficient_memory:	db "Insufficient memory",0
.mcb_invalid:		db "MCB invalid",0
.environment_invalid:	db "Environment invalid",0
.format_invalid:	db "Format invalid",0
.access_code_invalid:	db "Access code invalid",0
.data_invalid:		db "Data invalid",0
.fixup_overflow:	db "Fixup overflow",0
.invalid_drive:		db "Invalid drive",0
.attempted_to_remove_current_directory:		db "Attempted to remove current directory",0
.not_same_device:	db "Not same device",0
.no_more_files:		db "No more files",0


	align 4
%define load_sectors		(bp + bsBPB + bpbCHSSectors)
%define load_heads		(bp + bsBPB + bpbCHSHeads)
%define load_sectorsize		(bp + ldBytesPerSector)
%define load_sectorsizepara	(bp + ldParaPerSector)
%define load_sectorseg		(bp + ldSectorSeg)
load_partition_cycle:		dw 0
%define load_lba		(bp + ldHasLBA)
%define load_unit		(bp + bsBPB + ebpbNew + bpbnBootUnit)
load_current_partition:		db 0


error:
	push cs
	pop ds
	mov si, msg.error
	call disp_msg
	pop si
	call disp_msg
	xor ax, ax
	int 16h
	int 19h

init1_disp_msg_asciz:
disp_msg_asciz:
	push ds
	push si
	push ax
	 push cs
	 pop ds
	mov si, dx
	call disp_msg
	pop ax
	pop si
	pop ds
	retn

init1_disp_msg:
disp_msg:
@@:
	lodsb
	test al, al
	jz @F
	call disp_al
	jmp short @B

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

sig_valid:
		; S0 +28
;	pushf	; +26
	sti
	cld
	push ax	; +24
	push bx	; +22
	push cx	; +20
	push dx	; +18
	push si	; +16
	push di	; +14
	push bp	; +12
	mov ax, sp
	add ax, 16
	push ax	; +10 SP
	xor ax, ax
	push ax	; +8 IP
	push cs	; +6
	push ds	; +4
	push es	; +2
	push ss	; +0
	mov si, sp
	 push ss
	 pop ds
	mov di, table
	 push cs
	 pop es
loop_table:
	mov bx, [es:di + 0]
	mov al, 32
	call disp_al
	mov ax, [es:di + 2]
	call disp_al
	xchg al, ah
	call disp_al
	cmp bx, -1
	je @F
	mov al, '='
	call disp_al
	mov ax, [si + bx]
	call disp_ax_hex
@@:
	add di, 4
	cmp di, table.end
	jb loop_table


init_mcb_chain:
	push cs
	pop ds
	push cs
	pop es
	mov ax, 60h			; where
	xor cx, cx			; owner
	mov bx, cs
	sub bx, ax			; size
	dec bx				; less 1
	sub bx, ( 16 \
		+ (dosentry_end - dosentry_start) \
		+ 16 \
		+ (doscode_end - doscode_start) \
		+ 16 \
		+ (dosdata_end - dosdata_start) \
		+ 16 \
		) >> 4
					; point at DOSENTRY MCB
	mov dx, "M" | (0 << 8)
	call init_an_mcb

	mov bx, mcbtable
	mov ax, cs
	add ax, (init1_end - init1_start + 16) >> 4
	mov word [bx], ax
	mov word [bx + 2], -1

	add bx, 4
	mov ax, ss
	mov word [bx], ax
	mov word [bx + 2], S_INITSTACKBPB

	add bx, 4
	mov ax, [bp + lsvFATSeg]
	dec ax
	mov word [bx], ax
	mov word [bx + 2], S_INITFATSEG

	add bx, 4
	mov ax, [bp + ldSectorSeg]
	dec ax
	mov word [bx], ax
	mov word [bx + 2], S_INITSECTORSEG

	add bx, 4
	mov ax, [bp + ldMemoryTop]
	mov word [bx], ax
	mov word [bx + 2], -1

.next_bubble:
	mov bx, mcbtable
	xor cx, cx
.next:
	mov ax, word [bx]
	cmp ax, word [bx + 4]
	jbe @F
	push word [bx]
	push word [bx + 2]
	push word [bx + 4]
	push word [bx + 6]
	pop word [bx + 2]
	pop word [bx]
	pop word [bx + 6]
	pop word [bx + 4]
	inc cx
@@:
	add bx, 4
	cmp word [bx], -1
	jne .next
	test cx, cx
	jnz .next_bubble

init_end_mcb_chain:
	mov si, mcbtable
.next:
	mov ax, word [si]
	dec ax			; = where to place MCB
	mov dx, word [si + 2]	; dl = S MCB type, -1 if none
	mov cx, 8		; owner = 8
	cmp dl, -1		; none type ?
	jne @F			; no -->
	xor cx, cx		; yes, owner = 8
@@:
	xchg dl, dh		; dh = S MCB type
	mov dl, "M"		; dl = MCB signature
	mov di, word [si + 4]
	mov bx, di
	sub bx, ax
	dec bx
	dec bx			; size = to next
	cmp di, -1		; any next ?
	jne @F			; yes -->
	mov dl, "Z"		; else, mcbSignature = "Z"
	xor bx, bx		; size = zero
@@:
	call init_an_mcb
	add si, 4
	cmp word [si], -1
	jne .next

	mov bx, cs
	sub bx, ((dosdata_end - dosdata_start) + 16) >> 4
	mov ds, bx
	mov word [first_umcb], ax


init_dosentry:
	mov ax, cs
	sub ax, ( 16 \
		+ (dosentry_end - dosentry_start) \
		+ 16 \
		+ (doscode_end - doscode_start) \
		+ 16 \
		+ (dosdata_end - dosdata_start) \
		+ 16) >> 4
	mov dx, 6Fh
	mov cx, (16 + (dosentry_end - dosentry_start)) >> 4
	call init1_movp			; move DOSENTRY to 70h:0

	add cx, dx			; => behind DOSENTRY
	mov ax, cx
	mov bx, cs
	sub bx, ax			; size
	dec bx				; less 1
	sub bx, ( 16 \
		+ (doscode_end - doscode_start) \
		+ 16 \
		+ (dosdata_end - dosdata_start) \
		+ 16 \
		) >> 4			; ax + bx + 1 => DOSCODE MCB
	xor cx, cx			; owner
	mov dx, "M" | (0 << 8)
	call init_an_mcb

	mov ax, 60h			; where
	xor cx, cx			; owner
	mov bx, 6Fh
	sub bx, ax			; size
	dec bx				; less 1
					; ax + bx + 1 => point at DOSENTRY MCB
	mov dx, "M" | (0 << 8)
	call init_an_mcb


listnames:
	push cs
	pop es
	mov bx, msg.names

%assign _NEW_BPB_NAME_SCAN_SIZE 1
%if _NEW_BPB_NAME_SCAN_SIZE
	mov ax, ss
	dec ax
	mov ds, ax
	mov cx, word [mcbSize]	; get size of stack/BPB
	shl cx, 1
	jc @F
	shl cx, 1
	jc @F
	shl cx, 1
	jc @F
	shl cx, 1		; <<= 4, *= 16, to byte offset
	jnc @FF
@@:
	xor cx, cx		; 0 = full 64 KiB
@@:
%endif

	push ss
	pop ds
	lea si, [bp + bsBPB + ebpbNew + BPBN_size]
%if _NEW_BPB_NAME_SCAN_SIZE
	sub cx, si		; byte offset end - byte offset start = length
	dec cx
	dec cx			; don't scan into AA55h sig
%else
	mov cx, (512 - (bsBPB + ebpbNew + BPBN_size)) - 2
						; -2 = AA55h sig
	cmp word [bp + bsBPB + bpbSectorsPerFAT], 0
	je @F
	mov cx, (512 + (ebpbNew - bpbNew) - (bsBPB + ebpbNew + BPBN_size)) - 2
@@:
%endif
.nextname:
	call findname
	 lahf
	mov dx, [cs:bx]
	call disp_msg_asciz
	mov dx, msg.name_before
	call disp_msg_asciz
	 sahf
	 jc @F		; skip quote if no name -->
	 mov dx, msg.name_quote
	 call disp_msg_asciz
@@:
	mov dx, msg.foundname
	call disp_msg_asciz
	 sahf
	 jc @F		; skip quote if no name -->
	 mov dx, msg.name_quote
	 call disp_msg_asciz
@@:
	mov dx, msg.name_after
	call disp_msg_asciz
	 sahf
	 mov ax, 0
	 jc @F		; set to zero if no name -->
	 lea ax, [si - 11]	; -> name in buffer
@@:
	 mov word [cs:bx + 2], ax	; -> name in buffer, or 0
	add bx, 4
	cmp word [cs:bx], 0
	jne .nextname


set_bootname.filename:
	mov si, word [cs:msg.names + 2]
	test si, si
	jnz @F

	mov si, msg.foundname_default
	jmp @FF

@@:
	call convert_name_to_asciz

	mov si, msg.foundname
@@:
	push cs
	pop ds				; ds:si -> name

	mov cx, (8 + 1 + 3) >> 1
		; note: last byte is already initialised to zero

	mov bx, cs
	sub bx, ((dosdata_end - dosdata_start) + 16) >> 4
	mov es, bx
	mov di, bootname.filename	; es:di -> buffer in DOSDATA
	rep movsw


store_boot_partition:
	push cs
	pop ds
	mov al, [bp + bsBPB + ebpbNew + bpbnBootUnit]
	mov byte [boot_unit], al
	mov ax, [bp + bsBPB + bpbHiddenSectors]
	mov dx, [bp + bsBPB + bpbHiddenSectors + 2]
	mov word [boot_partition], ax
	mov word [boot_partition + 2], dx

scan_for_partitions:
	lea sp, [bp + LOADDATA2]	; allow space for LOADDATA2
	push cs
	pop ds
	and word [bp + bsBPB + bpbHiddenSectors], 0
	and word [bp + bsBPB + bpbHiddenSectors + 2], 0

scan_diskettes:
	mov dl, 80h
	mov ah, 08
	stc
	int 13h			; get number of fixed disks
	mov bx, 0
	jc @F			; fail ? then set bx = 0 and try diskettes
	mov bl, dl
@@:
	mov dl, 0
	mov ah, 08h
	stc
	int 13h			; get number of diskettes
	mov ch, 0
	mov cl, dl		; cx = result
	jc @F			; fail -->
	cmp bx, cx		; differs from number of fixed disks ?
	jne @FF			; yes, use -->
@@:
				; else,
	int 11h			; query int 11 "get equipment list"
	xor cx, cx		; initialise to zero
	test ax, 1		; any specified ?
	jz @F			; no -->

	mov cl, al
	rol cl, 1
	rol cl, 1		; get bits 6 & 7
	and cl, 3		; separate
	inc cx			; +1
@@:
	xor dl, dl
	call scan_given_partitions

		; manufacture units 0 and 1 if not existant
	mov si, word [fsparm_pointer]
	cmp si, fsparm + FSPARM_size*2	; got at least 2 diskettes ?
	jae @FF				; yes -->
	cmp si, fsparm + FSPARM_size	; got 1 diskette ?
	jae @F				; yes -->
	mov di, si
	push cs
	pop es
	xor ax, ax
	mov cx, FSPARM_size >> 1
	rep stosw			; initialise
	mov byte [si + fspUnit], 0
	mov word [si + fspSectorSize], 512
	mov byte [si + fspType], -1	; initialise to unit 0
	inc byte [fsparm_number]
	add si, FSPARM_size		; -> next
@@:
	mov di, si
	push cs
	pop es
	xor ax, ax
	mov cx, FSPARM_size >> 1
	rep stosw
	mov byte [si + fspUnit], 1
	mov word [si + fspSectorSize], 512
	mov byte [si + fspType], -1
	inc byte [fsparm_number]
	add si, FSPARM_size
	mov word [fsparm_pointer], si
@@:

scan_fixed_disk_partitions:
	mov dl, 80h
	mov ah, 08h
	stc
	int 13h				; get number of fixed disks
	mov ch, 0
	mov cl, dl
	jnc @F				; success -->

	mov ax, 40h
	mov es, ax
	mov cl, byte [es:bdaNumberFixedDisks]	; get from BDA instead
@@:
	mov dl, 80h
	call scan_given_partitions

display_excess_partitions:
	push cs
	pop ds
	mov ax, word [fsparm_excess]
	test ax, ax
	jz @F
	mov dx, msg.excess_partitions_1
	call disp_msg_asciz
	call disp_ax_dec
	mov dx, msg.excess_partitions_2
	call disp_msg_asciz
@@:


display_partitions:
	push cs
	pop ds
	mov byte [boot_drive], -1
	xor cx, cx
	mov cl, byte [fsparm_number]
	mov si, fsparm
	mov bx, 'A'
	test cx, cx
	jz .end
.loop:

	mov al, bl
	call disp_al

	mov dx, msg.list_partition_1
	call disp_msg_asciz
	mov al, [si + fspUnit]

	push ax
	cmp al, byte [boot_unit]
	jne @FF

	cmp byte [boot_drive], -1	; found any on this unit yet ?
	jne @F				; yes -->
	mov byte [boot_drive], bh	; default initialise to first part
@@:

	mov ax, [si + fspStart]		; does it match the partition ?
	mov dx, [si + fspStart + 2]
	cmp word [boot_partition], ax
	jne @F
	cmp word [boot_partition + 2], dx
	jne @F				; no -->

	mov byte [boot_drive], bh	; yes, store this drive
@@:
	pop ax

	mov dl, 'h'
	test al, 80h
	jnz @F
	mov dl, 'f'
@@:
	and al, ~80h
	add al, 'a'
	cmp al, 'z'
	jbe @F

	mov al, 'u'
	call disp_al
	mov al, [si + fspUnit]
	call disp_al_hex
	mov al, '_'
	call disp_al

	jmp @FF

@@:
	push ax
	mov al, dl
	call disp_al
	mov al, 'd'
	call disp_al
	pop ax
	call disp_al

@@:
	xor ax, ax
	mov al, [si + fspPartition]
	test ax, ax
	jz .disp_2_blank

	call disp_ax_dec
	cmp ax, 10
	jb .disp_1_blank
	jmp .disp_0_blank

.disp_2_blank:
	mov al, 32
	call disp_al
.disp_1_blank:
	mov al, 32
	call disp_al
.disp_0_blank:

	mov dx, msg.list_partition_4
	call disp_msg_asciz

	mov ax, [si + fspStart]
	mov dx, [si + fspStart + 2]
	call disp_dxax_hex

	push dx
	mov dx, msg.list_partition_5
	call disp_msg_asciz
	pop dx

	 push cx
	 push bx
	mov cx, [si + fspSectorSize]
	mov bx, 4+4
	call disp_dxax_times_cx_width_bx_size

	mov dx, msg.list_partition_6
	call disp_msg_asciz

	mov ax, [si + fspLength]
	mov dx, [si + fspLength + 2]
	call disp_dxax_hex

	push dx
	mov dx, msg.list_partition_7
	call disp_msg_asciz
	pop dx

	call disp_dxax_times_cx_width_bx_size
	 pop bx
	 pop cx

	mov dx, msg.list_partition_8
	call disp_msg_asciz

	mov al, [si + fspBoot]
	test al, 80h
	mov al, 32
	jz @F
	mov al, 'B'
@@:
	call disp_al

	mov dx, msg.list_partition_9
	call disp_msg_asciz

	xor ax, ax
	mov al, byte [si + fspType]
	call disp_al_hex

	mov dx, msg.list_partition_10
	call disp_msg_asciz

	push si
	mov si, pt_types
.types_loop:
	cmp word [si], -1
	je .types_unknown
	cmp word [si], ax
	je .types_known
	add si, 4
	jmp .types_loop

.types_known:
	mov dx, word [si + 2]
	jmp .types_common

.types_unknown:
	mov dx, ptmsg_unknown
.types_common:
	pop si
	call disp_msg_asciz

	mov dx, msg.list_partition_11
	call disp_msg_asciz

	add si, FSPARM_size
	add bx, 0101h
	loop .loop_jmp
	jmp .end
.loop_jmp:
	jmp .loop
.end:

	mov al, byte [boot_drive]
	cmp al, -1
	jne @F
	mov al, [boot_unit]
	cmp al, 2
	jb @F
	mov al, 2
@@:
	mov byte [boot_drive], al

	mov bx, cs
	sub bx, ((dosdata_end - dosdata_start) + 16) >> 4
	mov es, bx

	mov byte [es:_RxDOS_CurrentDrive], al
	inc ax
	mov byte [es:bootdrive], al

	mov di, bootname.drive
	add al, 'A'-1
	stosb


display_bootname:
	mov dx, msg.bootname_1
	call disp_msg_asciz
	push es
	pop ds
	mov si, bootname
	call disp_msg
	mov dx, msg.bootname_2
	call disp_msg_asciz


save_interrupts:
	mov dx, 70h
	mov ds, dx
	xor ax, ax
	mov es, ax
	mov si, InterruptRestorationTable
.loop:
	xor ax, ax
	lodsb
	cmp al, -1
	je .end
	xchg ax, bx
	add bx, bx
	add bx, bx
	push word [es:bx + 2]
	push word [es:bx]
	pop word [si]
	pop word [si + 2]
	add si, 4
	jmp .loop
.end:


		; ds = 70h
set_dos_segments:
	mov bx, cs
	sub bx, ((dosdata_end - dosdata_start) + 16) >> 4
	mov word [dosdata_segment], bx
%if _COMBINED_DOSDATA_DOSCODE
	mov word [doscode_segment], bx
%endif

	mov es, bx
	mov word [es:_RxDOS_CurrentSeg], bx
	mov word [es:_RxDOS_pStartMemBlock], 60h
	mov ax, word [bp + ldMemoryTop]
	mov word [es:_RxDOS_MaxMemory], ax


		; ds = 70h
set_interrupts:
	xor ax, ax
	mov es, ax
	mov word [es:21h * 4], i21
	mov word [es:21h * 4 + 2], ds

%imacro setint 2.nolist
	mov dx, %1
	mov ax, (25h << 8) | %2
	int 21h
%endmacro

	clc
	int3
	jc .skip_ints_00_06
	setint i00, 00h
	setint entry_iret, 01h
	setint entry_iret, 03h
	setint i06, 06h
.skip_ints_00_06:
	setint entry_iret, 02h
	setint entry_iret, 04h
	setint entry_iret, 05h
	setint entry_iret, 07h
	setint entry_iret, 33h
	setint i19, 19h
	setint i20, 20h
	setint i23, 23h
	setint i24, 24h
	setint i25, 25h
	setint i26, 26h
	setint i27, 27h
	setint i28, 28h
	setint i29, 29h
	setint i2F, 2Fh
	setint entry_iret, 2Ah

	mov byte [es:30h * 4], 0EAh
	mov word [es:30h * 4 + 1], call5
	mov word [es:30h * 4 + 3], ds
	mov byte [es:30h * 4 + 5], 0
	mov word [es:30h * 4 + 6], bx	; DR-DOS compatible DOSDATA reference


initialise_upb_chain:
	xor cx, cx
	mov cl, byte [cs:fsparm_number]
	mov ax, DISKBLOCK_size
	mul cx
	test dx, dx
	jz @F

init1_internal_error_reboot_prompt: equ $
	mov si, init1_msg.internal_error_upb
	call init1_disp_msg
	jmp init1_reboot_prompt

@@:
	 push cx
	push cx
	mov cl, S_UPB
	call init1_alloc_s_mcb
	pop cx

	mov dx, 70h
	mov ds, dx
	mov si, ptrStartBlockedDeviceTable - _dskNextPointer
	mov bx, fsparm
	xor ax, ax

	mov word [si + _dskNextPointer], -1
	mov word [si + _dskNextPointer + 2], -1
	test cx, cx
	jz .end

		; es:di -> next UPB to initialise
		; ds:si -> previous UPB (_dskNextPointer uninitialised)
.next:
	mov al, [cs:bx + fspUnit]
	mov byte [es:di + _dskPhysDriveNumber], al
	mov byte [es:di + _dskDOSLogicalDiskUnit], ah

	push ax

	mov al, byte [cs:bx + fspType]
	mov byte [es:di + _dskFATSystemType], al

	push word [cs:bx + fspStart + 2]
	push word [cs:bx + fspStart]
	pop word [es:di + _dskBPB + bpbHiddenSectors]
	pop word [es:di + _dskBPB + bpbHiddenSectors + 2]
	and word [es:di + _dskBPB + bpbTotalSectors], 0
	push word [cs:bx + fspLength + 2]
	push word [cs:bx + fspLength]
	pop word [es:di + _dskBPB + bpbTotalSectorsLarge]
	pop word [es:di + _dskBPB + bpbTotalSectorsLarge + 2]
	push word [cs:bx + fspSectorSize]
	pop word  [es:di + _dskBPB + bpbBytesPerSector]

	push word [cs:bx + fspStart + 2]
	push word [cs:bx + fspStart]
	pop word [es:di + _dskDefaultBPB + bpbHiddenSectors]
	pop word [es:di + _dskDefaultBPB + bpbHiddenSectors + 2]
	and word [es:di + _dskDefaultBPB + bpbTotalSectors], 0
	push word [cs:bx + fspLength + 2]
	push word [cs:bx + fspLength]
	pop word [es:di + _dskDefaultBPB + bpbTotalSectorsLarge]
	pop word [es:di + _dskDefaultBPB + bpbTotalSectorsLarge + 2]
	push word [cs:bx + fspSectorSize]
	pop word  [es:di + _dskDefaultBPB + bpbBytesPerSector]

	push word [cs:bx + fspStart + 2]
	push word [cs:bx + fspStart]
	pop word [es:di + _dskExtHiddenSectors]
	pop word [es:di + _dskExtHiddenSectors + 2]

	mov word [es:di + _dskPartitionType], 1
	mov byte [es:di + _dskDeviceType], -1

	cmp byte [es:di + _dskPhysDriveNumber], 80h
	jb @F
	or word [es:di + _dskStatusFlag], IsNonRemovable
@@:

	push dx
	push cx
	push bx
	mov ah, 41h
	mov dl, [es:di + _dskPhysDriveNumber]
	mov bx, 55AAh
	stc
	int 13h		; 13.41.bx=55AA extensions installation check
	mov al, 0	; zero in case of no LBA support
	jc .no_lba
	cmp bx, 0AA55h
	jne .no_lba
	test cl, 1	; support bitmap bit 0
	jz .no_lba
	inc ax		; al = 1 to indicate LBA support
.no_lba:
	pop bx
	pop cx
	pop dx
	mov byte [es:di + _dskExtReadWrite], al

	pop ax
	mov word [es:di + _dskNextPointer], -1
	mov word [es:di + _dskNextPointer + 2], -1
	mov word [si + _dskNextPointer], di
	mov word [si + _dskNextPointer + 2], es

	add bx, FSPARM_size
	inc ah
	add di, DISKBLOCK_size
	lds si, [si + _dskNextPointer]
	loop .loop_jmp_next
	jmp .end
.loop_jmp_next:
	jmp .next
.end:

	mov ds, dx
	 pop cx
	mov byte [block + devUnits], cl


relocate_int1E_table:
	xor ax, ax
	mov es, ax
	lds si, [es:1Eh * 4]
	mov cx, 10h
	mov di, 522h
	push di
	rep movsb
	mov word [es:1Eh * 4 + 2], es
	pop word [es:1Eh * 4]


finish:
	push cs
	pop ds
	mov si, msg.test
	call disp_msg
	int3
	xor ax, ax
	int 16h

	mov al, 20h
	out 20h, al					; reset interrupts

	mov ax, 0500h
	int 10h						; set page zero

	mov si, init1_msg.loading_os
	call disp_msg

	mov ds, dx

	mov bx, cs
	sub bx, ( (dosdata_end - dosdata_start) \
		+ 16) >> 4
	xor ax, ax
	push bx
	push ax

	xor cx, cx
	mov cl, byte [cs:fsparm_number]

	mov dx, cs
	mov ax, init1_relocate_end

	mov si, cs
	mov di, init1_relocate_device

		; INP:	cs = combined DOSDATA DOSCODE segment
		;	ip = 0
		;	cx = number of default block device's units (<= 32)
		;	dx:ax -> far function to relocate DOSDATA (and DOSCODE)
		;	 this is to be called after all DEVICE= processing
		;	si:di -> far function to relocate DOSCODE to HMA
		;	 this is to be called after each DEVICE= processing
	retf


		; INP:	ax = flags
		; CHG:	ds, es, si, di, ax, bx, cx, dx
init1_relocate_device:
	lframe 0
	lpar dword, returnaddress
	lenter
	lvar word, flags
	push ax

%assign _DOSCODEHMA 0
%if !_COMBINED_DOSDATA_DOSCODE && _DOSCODEHMA
	int3

	mov ax, 70h
	mov ds, ax
	mov bx, word [doscode_segment]
	cmp bx, -1		; already in HMA ?
	je .return		; yes, return -->

	test word [bp + ?flags], DOSFLAGS_RELOCATE_DOSCODE_HMA
	jz .return

	mov ax, 4300h
	int 2Fh			; XMS present ?
	cmp al, 80h
	jne .return		; no -->

	mov ax, 4310h
	int 2Fh			; get XMS entrypoint
	mov word [cs:.call_xms_address], bx
	mov word [cs:.call_xms_address + 2], es		; store

	mov ax, 0100h
	mov dx, -1
	call .call_xms		; allocate HMA
	jc .return		; failure -->

	mov ah, 03h
	call .call_xms		; enable A20
	mov dx, init1_msg.error_enabling_a20
	jc .return_free		; error -->

	call init1_test_a20	; check A20 is enabled
	mov dx, init1_msg.error_enabling_a20
	jc .return_free		; error -->

	mov dx, -1
	mov es, dx
	mov di, 10h
	mov cx, 30h >> 1
	xor ax, ax
	rep stosw
	sub di, 10h

	mov cx, (doscode_end - doscode_start)

	mov word [es:di + hmcbSignature], "MS"
	mov word [es:di + hmcbOwner], -1
	mov word [es:di + hmcbSize], cx
	mov bx, di
	add bx, HMCB_size
	add bx, cx
	mov word [es:di + hmcbNext], bx

	mov di, bx
	xor ax, ax
	mov cx, 16 >> 1
	rep stosw
	mov di, bx
	mov word [es:di + hmcbSignature], "MS"
	mov word [es:di + hmcbOwner], -1
	mov di, bx
	add di, 10h
	neg di
	add di, -1
	mov word [es:di + hmcbSize], di
	; word [es:di + hmcbNext] already == 0

	mov di, doscode_start
	mov si, di
	mov dx, 70h
	mov ds, dx
	mov bx, [doscode_segment]
	mov ds, bx
	shr cx, 1
	rep movsw

	mov ds, dx
	mov word [doscode_segment], es

	cmp word [bp + ?returnaddress + 2], bx
	jne @F
	mov word [bp + ?returnaddress + 2], es
@@:
	mov ax, ss
	cmp ax, bx
	jne @F
	push es
	pop ss
@@:

	add bx, 4
	mov es, bx			; => DOSCODE source
	mov ah, 49h
	int 21h				; free it	

	mov ds, dx
	mov ds, word [dosdata_segment]
	mov word [first_hmcb], 30h

	jmp .return


.return_free:
	call init1_disp_msg_asciz

	mov ah, 02h
	call .call_xms		; free HMA

.return:
%endif
	lleave
	retf

.call_xms:
	call 0:0
.call_xms_address: equ $-4
	cmp ax, 1
	je @F			; (NC) -->
	stc
@@:
	retn


init1_relocate_end:
	lframe 0
	lpar dword, returnaddress
	lenter
	lvar word, flags
	push ax

	mov ax, 5802h
	int 21h
	mov ah, 0
	push ax
	mov ax, 5800h
	int 21h
	push ax

	mov ax, 5803h
	xor bx, bx
	int 21h			; link out UMB chain
	mov ax, 5801h
	xor bx, bx
	int 21h			; alloc strategy: first fit, LMA only

	test word [bp + ?flags], DOSFLAGS_RELOCATE_DOSDATA_UMA
	jz .got_dosdata_strat

	mov ax, 5803h
	mov bx, 1
	int 21h			; link in UMB chain
	mov ax, 5801h
	mov bx, 80h
	int 21h			; alloc strategy: first fit, try UMA then LMA
.got_dosdata_strat:

	mov ax, (dosdata_end - dosdata_start)
	mov cl, S_DOSDATA
	call init1_alloc_s_mcb		; es:di -> DOSDATA destination

	int3

	mov ax, 70h
	mov ds, ax			; => DOSENTRY
	mov bx, word [dosdata_segment]	; get DOSDATA segment
	mov ds, bx
	xor si, si			; ds:si -> DOSDATA source
	shr cx, 1			; get number of words
	rep movsw			; move

	mov ds, ax
	mov word [dosdata_segment], es	; DOSDATA reference in DOSENTRY
%if _COMBINED_DOSDATA_DOSCODE
	mov word [doscode_segment], es
%endif
	xor ax, ax
	mov ds, ax
	mov word [30h * 4 + 6], es	; DR-DOS compatible DOSDATA reference

	cmp word [bp + ?returnaddress + 2], bx
	jne @F
	mov word [bp + ?returnaddress + 2], es
@@:
	mov ax, ss
	cmp ax, bx
	jne @F
	push es
	pop ss
@@:

	mov es, bx			; => DOSDATA source
	mov ah, 49h
	int 21h				; free it

%if !_COMBINED_DOSDATA_DOSCODE

	mov ax, 5803h
	xor bx, bx
	int 21h			; link out UMB chain
	mov ax, 5801h
	xor bx, bx
	int 21h			; alloc strategy: first fit, LMA only

	test word [bp + ?flags], DOSFLAGS_RELOCATE_DOSCODE_UMA
	jz .got_doscode_strat

	mov ax, 5803h
	mov bx, 1
	int 21h			; link in UMB chain
	mov ax, 5801h
	mov bx, 80h
	int 21h			; alloc strategy: first fit, try UMA then LMA
.got_doscode_strat:

	mov ax, 70h
	cmp word [doscode_segment], -1
	je .skip_doscode

	mov ax, (doscode_end - doscode_start)
	mov cl, S_DOSCODE
	call init1_alloc_s_mcb

	mov ax, 70h
	mov ds, ax			; => DOSENTRY
	mov bx, es
	sub bx, 4
	mov es, bx
	mov di, doscode_start		; es:di -> DOSCODE destination
	mov bx, word [doscode_segment]
	mov ds, bx
	mov si, di			; ds:si -> DOSCODE source
	shr cx, 1			; = number of words
	rep movsw			; move

	mov ds, ax
	mov word [doscode_segment], es

	cmp word [bp + ?returnaddress + 2], bx
	jne @F
	mov word [bp + ?returnaddress + 2], es
@@:
	mov ax, ss
	cmp ax, bx
	jne @F
	push es
	pop ss
@@:

	add bx, 4
	mov es, bx			; => DOSCODE source
	mov ah, 49h
	int 21h				; free it

.skip_doscode:
%endif

	pop bx
	mov ax, 5801h
	int 21h			; alloc strategy: restore
	pop bx
	mov ax, 5803h
	int 21h			; restore UMB chain linkage

	lleave
	retf


		; INP:	ax = space in bytes to allocate
		;	cl = allocation type
		; OUT:	es:di-> allocated space
		;	ax = cx = space in bytes allocated
		;	does not return after allocation failure
		; CHG:	es, bx
init1_alloc_s_mcb:
	add ax, 15
	jnc @F
	mov ax, 1_0000h >> 1
	db __TEST_IMM16
@@:
	shr ax, 1
	shr ax, 1
	shr ax, 1
	shr ax, 1			; = paragraphs

	mov bx, ax
	mov ah, 48h
	stc
	int 21h
	jc .alloc_fail

	dec ax
	mov es, ax
	mov word [es:_smcbOwner], 8	; system-owned MCB
	mov word [es:_smcbName], "S"	; S MCB
	mov ch, 0
	mov word [es:_smcbType], cx	; type = cl, reserved = 0
	mov cl, 0
	mov word [es:_smcbLink], cx	; link = 0
	mov word [es:_smcbLink + 2], cx	; reserved = 0

	inc ax
	mov es, ax
	xor di, di			; -> allocated space
	mov cx, bx
	shl cx, 1
	shl cx, 1
	shl cx, 1			; = number of words
	xor ax, ax
	push di
	push cx
	rep stosw			; initialise allocated memory to zeros
	pop cx
	pop di
	shl cx, 1
	mov ax, cx
	retn


.alloc_fail:
	push cs
	pop ds
	 push ax
	mov si, init1_msg.alloc_error
	call init1_disp_msg
	 pop ax
	call init1_disp_ax_hex
	 push ax
	mov si, init1_msg.alloc_error_q
	call init1_disp_msg
	 pop ax

	cmp ax, init1_msg_errors.count
	jb .valid
	mov si, init1_msg_errors.unknown_error
	jmp short .disp_error
.valid:
	mov bx, ax
	add bx, bx
	mov si, [ init1_msg_errors + bx ]
.disp_error:
	call init1_disp_msg
	mov si, init1_msg.linebreak
	call init1_disp_msg

init1_reboot_prompt:
	push cs
	pop ds
	mov si, init1_msg.reboot_prompt
	call init1_disp_msg
.loop:
	int3
	xor ax, ax
	int 16h
	jmp .loop


		; Move paragraphs
		;
		; INP:	ax:0-> source
		;	dx:0-> destination
		;	cx = number of paragraphs
		; CHG:	-
		; OUT:	NC
		; Note:	Doesn't work correctly on HMA; doesn't always wrap to LMA either.
		;	Do not provide a wrapped/HMA source or destination!
init1_movp:
	push cx
	push ds
	push si
	push es
	push di

	cmp ax, dx		; source below destination ?
	jb .down		; yes, move from top down -->
	je .return		; same, no need to move --> (NC)

	push ax
	push dx
.uploop:
	mov ds, ax
	mov es, dx
	xor di, di
	xor si, si		; -> start of segment
	sub cx, 1000h		; 64 KiB left ?
	jbe .uplast		; no -->
	push cx
	mov cx, 10000h /2
	rep movsw		; move 64 KiB
	pop cx
	add ax, 1000h
	add dx, 1000h		; -> next segment
	jmp short .uploop	; proceed for more -->
.uplast:
	add cx, 1000h		; restore counter
	shl cx, 1
	shl cx, 1
	shl cx, 1		; *8, paragraphs to words (NC)
	rep movsw		; move last part
	pop dx
	pop ax
	jmp short .return

.down:
	std
.dnloop:
	sub cx, 1000h		; 64 KiB left ?
	jbe .dnlast		; no -->
	push ax
	push dx
	add ax, cx
	add dx, cx
	mov ds, ax		; -> 64 KiB not yet moved
	mov es, dx
	pop dx
	pop ax
	mov di, -2
	mov si, di		; moved from last word down
	push cx
	mov cx, 10000h /2
	rep movsw		; move 64 KiB
	pop cx
	jmp short .dnloop	; proceed for more -->
.dnlast:
	add cx, 1000h		; restore counter
	shl cx, 1
	shl cx, 1
	shl cx, 1		; *8, paragraphs to words
	mov di, cx
	dec di
	shl di, 1		; words to offset, -> last word (NC)
	mov si, di
	mov ds, ax
	mov es, dx		; first segment correct
	rep movsw		; move first part
	cld
.return:
	pop di
	pop es
	pop si
	pop ds
	pop cx
	retn


		; INP:	dx:ax = numerator
		;	cx = multiplier
		;	bx = field width
		; STT:	UP
		; OUT:	displayed
disp_dxax_times_cx_width_bx_size:
	lframe near
	lvar 4 + 4 + 2, buffer
	lvar 6, dividend
	lenter
	 lvar word, width
	 push bx
	push si
	push di
	push ds
	push es
	push cx
	push ax
	push dx

	 push cs
	 pop ds
	 push ss
	 pop es

	push dx
	mul cx
	xchg ax, di
	xchg dx, si		; si:di = first mul

	pop ax
	mul cx
	add ax, si
	adc dx, 0		; dx:ax = second mul + adj, dx:ax:di = mul

	mov cx, di		; dx:ax:cx = mul

	mov word [bp + ?dividend], cx
	mov word [bp + ?dividend + 2], ax
	mov word [bp + ?dividend + 4], dx

	mov si, msg.prefixes
.loop:
	cmp word [bp + ?dividend + 4], 0
	jnz .divide
	cmp word [bp + ?dividend + 2], 0
	jnz .divide
	cmp word [bp + ?dividend], 2048
	jbe .end
.divide:
	inc si
	mov cx, 1024		; 1000 here if SI units
	xor dx, dx
	mov di, 6
.loop_divide:
	mov ax, [bp + ?dividend - 2 + di]
	div cx
	mov word [bp + ?dividend - 2 + di], ax
	dec di
	dec di
	jnz .loop_divide
				; dx = last remainder
	jmp .loop

.end:
	lea di, [bp + ?buffer + 4 + 4 + 1]
	std
	mov al, "B"
	stosb
	mov al, [si]
	cmp al, 32
	je @F

	and al, ~20h		; uppercase, don't do this if SI units
	push ax
	mov al, "i"
	stosb			; don't store this if SI units
	pop ax
	stosb
@@:
	mov al, 32
	stosb

	mov ax, word [bp + ?dividend]
	mov cx, 10
.loop_write:
	xor dx, dx
	div cx
	xchg ax, dx
				; ax = remainder (next digit)
				; dx = result of div
	add al, '0'
	stosb
	xchg ax, dx		; ax = result of div
	test ax, ax		; any more ?
	jnz .loop_write		; loop -->

	cld

	lea bx, [bp + ?buffer + 4 + 4 + 1]
	sub bx, di

	mov cx, [bp + ?width]
	sub cx, bx
	jbe .none_blank
.loop_blank:
	mov al, 32
	call disp_al
	loop .loop_blank
.none_blank:

	mov cx, bx
.loop_disp:
	inc di
	mov al, [ss:di]
	call disp_al
	loop .loop_disp

	pop dx
	pop ax
	pop cx
	pop es
	pop ds
	pop di
	pop si
	pop bx
	lleave
	lret


		; INP:	ax = number to display
		; CHG:	-
disp_ax_dec:			; ax (no leading zeros)
	; In: number in ax
	; Out: displayed
		push bx
		xor bx, bx
.pushax:
		push ax
.pushend:
		or bl, bl
		jz .nobl
		sub bl, 5
		neg bl
.nobl:
		push cx
		mov cx, 10000
		call .divide_out
		mov cx, 1000
		call .divide_out
		mov cx, 100
		call .divide_out
		mov cl, 10
		call .divide_out
							; (Divisor 1 is useless)
		add al, '0'
		call disp_al
		pop cx
		pop ax
		pop bx					; Caller's register
		retn

.divide_out:
	; In: ax = number
	;     cx = divisor
	; Out: ax = remainder of operation
	;      result displayed
		push dx
		xor dx, dx
		div cx				; 0:ax / cx
		push dx				; remainder
		dec bl
		jnz .nobl2
		or bh, 1
.nobl2:
		or bh, al
		jz .leadingzero
		add al, '0'
		call disp_al			; display result
 .leadingzero:
		pop ax				; remainder
		pop dx
		retn


		; INP:	es:si -> partition table entry,
		;	 si = parttable .. (parttable.end - 16),
		;	 es = cs
		;	ss:bx + di -> above part table metadata,
		;	 dwo [ss:bx + di - 4] = root (outermost extended position)
		;	 dwo [ss:bx + di - 8] = base (current table position)
		;	ss:bp -> BPB area, LOADSTACKVARS, LOADDATA, LOADDATA2
		; CHG:	ax, (cx), dx
login_partition:
	mov al, byte [es:si + piType]
	cmp al, ptFAT12
	je .isfat
	cmp al, ptFAT16_16BIT_CHS
	je .isfat
	cmp al, ptFAT16_CHS
	je .isfat
	cmp al, ptFAT32_CHS
	je .isfat
	cmp al, ptFAT32
	je .isfat
	cmp al, ptFAT16
	je .isfat

	retn

.isfat:
	push ds
	push cx
	push bx
	push si
	push di
	push es
	 push cs
	 pop ds
	cmp byte [fsparm_number], 32
	jne @F

	inc word [fsparm_excess]
.return:
	pop es
	pop di
	pop si
	pop bx
	pop cx
	pop ds
	retn

@@:
	mov dl, al
	inc byte [fsparm_number]
	mov di, [fsparm_pointer]
	add word [fsparm_pointer], FSPARM_size
	 push cs
	 pop es				; -> FSPARM
	xor ax, ax
	mov cx, FSPARM_size >> 1
	push di
	rep stosw			; initialise
	pop di
	mov al, [load_unit]
	mov byte [di + fspUnit], al
	push word [load_sectorsize]
	pop word [di + fspSectorSize]
	mov byte [di + fspType], dl
	mov cx, di
	pop es
	pop di
	pop si
	push si
	push di
	push es

	mov ax, word [ss:bx + di - 8]
	mov dx, word [ss:bx + di - 6]	; root
	add ax, word [es:si + piStart]
	adc dx, word [es:si + piStart + 2]	; add partition offset

	mov di, cx
	mov word [di + fspStart], ax
	mov word [di + fspStart + 2], dx
	push word [es:si + piLength + 2]
	push word [es:si + piLength]
	pop word [di + fspLength]
	pop word [di + fspLength + 2]

	mov al, byte [load_current_partition]
	mov byte [di + fspPartition], al
	mov al, byte [es:si + piBoot]
	mov byte [di + fspBoot], al

	jmp .return


		; INP:	dl = first unit to log in
		;	cx = number of units to log in
scan_given_partitions:
	jcxz .none
.loop:
	push cx
	push dx

	mov word [throw_sp], sp
	mov word [throw_ip], .fail

	mov byte [load_unit], dl
	call query_geometry

	mov cx, login_partition
	call scan_partitions
	push cs
	pop ds
	db __TEST_IMM8		; (NC)
.fail:
	stc
	 push cs
	 pop ds
	pop dx
	pop cx

	jnc @F
	push dx
	xchg ax, dx
	mov dx, msg.login_partition_error_1
	call disp_msg_asciz
	call disp_al_hex
	mov dx, msg.login_partition_error_2
	call disp_msg_asciz
	pop dx
@@:
	inc dx
	loop .loop
.none:
	retn


hexword:
	xchg al, ah
	call hexbyte
	xchg al, ah

hexbyte:
	push cx
	mov cl, 4
	rol al, cl
	call hexnyb
	rol al, cl
	pop cx

hexnyb:
	push ax
	and al, 0Fh
	add al, 90h
	daa
	adc al, 40h
	daa			; these four instructions change to ASCII hex
	stosb
	pop ax
	retn


cmd3:
	push cs
	pop ds
	push cs
	pop es
	mov sp, [throw_sp]
	jmp near [throw_ip]


bootcmd:
.fail_read:
	 push cs
	 pop es
	mov di, msg.bootfail_read_errorcode
	mov al, ah
	call hexbyte
	mov dx, msg.bootfail_read

.fail:
	push dx
	mov dx, msg.bootfail
	call disp_msg_asciz
	pop dx
	call disp_msg_asciz
	jmp cmd3

scan_partitions:
	and word [load_partition_cycle], 0
d4	call init1_d4message
d4	asciz "In scan_partitions",13,10

	push bp
	mov bx, sp
	xor di, di
	push di		; [ss:bx+di-2]
	push di		; [ss:bx+di-4]
	push di		; [ss:bx+di-6]
	push di		; [ss:bx+di-8]

	cmp byte [load_unit], 80h
	jae @F

	push di
	push cx
	 push cs
	 pop es
	mov di, parttable
	xor ax, ax
	mov cx, (4 * 16) >> 1
	rep stosw			; initialise fake partition table
	pop cx
	pop di
	mov si, parttable		; -> fake PARTINFO
	mov byte [es:si + piBoot], 80h	; fake primary active
	mov byte [es:si + piType], 0FFh	; fake a type
	mov byte [load_current_partition], 0	; special: 0 = diskette
					; piLength is 0, too
	call cx

	jmp scan_logical.end


@@:
	xor ax, ax
	xor dx, dx
d4	call init1_d4message
d4	asciz "In scan_partitions (before first call to read_partition_table)",13,10
	call read_partition_table
d4	call init1_d4message
d4	asciz "In scan_partitions (after first call to read_partition_table)",13,10
	mov si, parttable
	mov byte [load_current_partition], dl	; = 0
.loop_primary_parts:
	inc byte [load_current_partition]
	cmp byte [es:si + piType], 0
	je .loop_primary_skip
	call cx			; es:si -> partition table entry
				; byte [load_current_partition] = which
.loop_primary_skip:
	add si, 16
	cmp si, parttable.end
	jb .loop_primary_parts

scan_logical:
.:
d4	call init1_d4message
d4	asciz "In scan_logical.",13,10
	mov si, parttable
.loop:
	inc word [load_partition_cycle]
	jz .got_partition_cycle

	mov al, [es:si + piType]

	test al, al
	jz .next
	cmp al, 0Fh		; extended partition (LBA aware) ?
	je .push		; yes -->
	and al, ~80h		; extended partition Linux (85h) ?
	cmp al, 05h		;  or extended partition DOS (05h) ?
	je .push		; yes -->

	cmp word [ss:bx+di-2], 0
	jne .logical
	cmp word [ss:bx+di-4], 0
	je .next
.logical:
	inc byte [load_current_partition]
	jz .error_too_many_partitions
	call cx			; CHG: ax, (cx), dx
				; preserve: (cx), si, di, bx, ds, es
.next:
	add si, 16		; -> next partition table entry
	cmp si, parttable.end	; was last?
	jb .loop		; no, loop -->
	test di, di		; still some on stack?
	jnz .pop		; yes, pop
.end:
	mov sp, bx		; restore sp
	pop ax			; (discard dummy bp value)
	retn			; and bye

.push:
d4	call init1_d4message
d4	asciz "In scan_logical.push",13,10

	push si
.push_check_empty_next:
	add si, 16		; -> next
	cmp si, parttable.end	; at end?
	jae .replace		; yes, no other partitions found, replace -->
	cmp byte [es:si + piType], 0	; is this a partition?
	je .push_check_empty_next	; no, check next -->
				; found a partition after this, do push
				; (possibly logical or another extended)
.push_check_is_not_empty:
d4	call init1_d4message
d4	asciz "In scan_logical.push_check_is_not_empty",13,10
	pop si			; restore -> partition table entry
	push si			; stored at word [ss:bx+di-10]
	sub di, 10
	push word [ss:bx+di+10-2]
	push word [ss:bx+di+10-4]	; copy root

	mov ax, word [es:si + piStart]
	mov dx, word [es:si + piStart + 2]	; get extended partition offset
	add ax, word [ss:bx+di-4]
	adc dx, word [ss:bx+di-2]	; add in root to get absolute sector number

	push dx
	push ax			; new base

.replace_common:
	cmp word [ss:bx+di-2], 0	; have a (nonzero) root?
	jne .have_root
	cmp word [ss:bx+di-4], 0
	jne .have_root		; yes -->

	mov word [ss:bx+di-2], dx
	mov word [ss:bx+di-4], ax	; set root
.have_root:

	call read_partition_table
	jmp .

.pop:
d4	call init1_d4message
d4	asciz "In scan_logical.pop",13,10

	add di, 10
	add sp, 8
	pop si

	mov ax, word [ss:bx+di-8]
	mov dx, word [ss:bx+di-6]
	call read_partition_table
	jmp .next

.replace:
d4	call init1_d4message
d4	asciz "In scan_logical.replace",13,10

	pop si				; (discard)
	mov ax, word [es:si + piStart]
	mov dx, word [es:si + piStart + 2]	; get extended partition offset
	add ax, word [ss:bx+di - 4]
	adc dx, word [ss:bx+di - 2]	; add in root
	mov word [ss:bx+di - 8], ax
	mov word [ss:bx+di - 6], dx	; set base

	jmp .replace_common

.got_partition_cycle:
	mov dx, msg.boot_partition_cycle_error
	jmp bootcmd.fail

.error_too_many_partitions:
	mov dx, msg.boot_too_many_partitions_error
	jmp bootcmd.fail


read_partition_table:
	push bx
	mov bx, word [bp + ldSectorSeg]
	call read_ae_512_bytes
	pop bx
	cmp word [es:510], 0AA55h
	jne @F
	 push ds
	 push di
	 push si
	 push cx
	push es
	pop ds
	mov si, 510 - 4*16		; ds:si -> partition table in sectorseg
	push cs
	pop es
	mov di, parttable		; es:di -> cs:parttable
	mov cx, 4*16 / 2
	rep movsw
	 pop cx
	 pop si
	 pop di
	 pop ds
	retn

@@:
	mov dx, msg.bootfail_sig_parttable
	jmp bootcmd.fail


query_geometry:
	mov dl, [load_unit]
;	test dl, dl		; floppy?
;	jns @F			; don't attempt query, might fail -->
	; Note that while the original PC BIOS doesn't support this function
	;  (for its diskettes), it does properly return the error code 01h.
	; https://sites.google.com/site/pcdosretro/ibmpcbios (IBM PC version 1)
	mov ah, 08h
	xor cx, cx		; initialise cl to 0
	mov [load_heads], cx
	mov [load_sectors], cx
	stc			; initialise to CY
	call .int13_retry	; query drive geometry
	jc .try_bootsector	; apparently failed -->
	mov dl, dh
	mov dh, 0		; dx = maximum head number
	inc dx			; dx = number of heads (H is 0-based)
	mov ax, cx		; ax & 3Fh = maximum sector number
	and ax, 3Fh		; get sectors (number of sectors, S is 1-based)
	jnz .got_sectors_heads	; valid (S is 1-based), use these -->
				; zero = invalid
.try_bootsector:
	mov es, word [bp + ldSectorSeg]
	xor bx, bx
	mov ax, 0201h			; read sector, 1 sector
	mov cx, 1			; sector 1 (1-based!), cylinder 0 (0-based)
	mov dh, 0			; head 0 (0-based)
	mov dl, [load_unit]
	stc
	call .int13_retry
	jc .access_error

		; note: the smallest supported sector size, 32 bytes,
		;  does contain these entries (offset 18h and 1Ah in sector)
		;  within the first BPB sector.
	mov ax, word [es:bx + bsBPB + bpbCHSSectors]
	mov dx, word [es:bx + bsBPB + bpbCHSHeads]

.got_sectors_heads:
	mov word [load_sectors], ax
	mov word [load_heads], dx

	test ax, ax
	jz .invalid_sectors
	cmp ax, 63
	ja .invalid_sectors
	test dx, dx
	jz .invalid_heads
	cmp dx, 100h
	ja .invalid_heads

	mov bx, 10h
	mov di, bx
	mov cx, (8192 + 2) >> 1
	mov ax, word [bp + ldSectorSeg]
	dec ax
	mov es, ax			; es:bx -> buffer, es:di = same
	xor ax, ax
	rep stosw			; fill buffer, di -> behind (0:7C00h+8192+2)
	mov ax, 0201h			; read sector, 1 sector
	inc cx				; sector 1 (1-based!), cylinder 0 (0-based)
	mov dh, 0			; head 0 (0-based)
	mov dl, [load_unit]
	stc
	call .int13_retry
	jc .access_error

	std
	mov word [es:bx - 2], 5E5Eh
	scasw				; -> 0:7C00h+8192 (at last word to sca)
	mov cx, (8192 + 2) >> 1
	xor ax, ax
	repe scasw
	add di, 4			; di -> first differing byte (from top)
	cld
	push di

	mov di, bx
	mov cx, (8192 + 2) >> 1
	dec ax				; = FFFFh
	rep stosw

	mov ax, 0201h
	inc cx
	mov dh, 0
	mov dl, [load_unit]
	stc
	call .int13_retry
	jc .access_error

	std
	scasw				; di -> 0:7C00h+8192 (last word to sca)
	pop dx
	mov ax, -1
	mov cx, (8192 + 2) >> 1
	repe scasw
%if 0
AAAB
   ^
	sca B, match
  ^
	sca B, mismatch
 ^
	stop
%endif
	add di, 4			; di -> first differing byte (from top)
	cld

%if 0
0000000000000
AAAAAAAA00000
	^
FFFFFFFFFFFFF
AAAAAAAA00FFF
	  ^
%endif
	cmp dx, di			; choose the higher one
	jae @F
	mov dx, di
@@:
	sub dx, bx			; dx = sector size

	cmp dx, 8192 + 2
	jae .sector_too_large
	mov ax, 32
	cmp dx, ax
	jb .sector_too_small
@@:
	cmp dx, ax
	je .got_match
	cmp ax, 8192
	jae .sector_not_power
	shl ax, 1
	jmp @B

.got_match:
	mov word [load_sectorsize], ax
	mov cl, 4
	shr ax, cl
	mov word [load_sectorsizepara], ax

	mov byte [load_lba], 0
	mov ah, 41h
	mov dl, [load_unit]
	mov bx, 55AAh
	stc
	int 13h		; 13.41.bx=55AA extensions installation check
	jc .no_lba
	cmp bx, 0AA55h
	jne .no_lba
	test cl, 1	; support bitmap bit 0
	jz .no_lba

	inc byte [load_lba]
.no_lba:
	retn


.int13_retry:
	pushf
	push ax
	int 13h		; first try
	jnc @F		; NC, success on first attempt -->

; reset drive
	xor ax, ax
	int 13h
	jc @F		; CY, reset failed, error in ah -->

; try read again
	pop ax		; restore function number
	popf
	int 13h		; retry, CF error status, ah error number
	retn

@@:			; NC or CY, stack has function number
	inc sp
	inc sp
	inc sp
	inc sp		; discard two words on stack, preserve CF
	retn


.access_error:
	mov dx, msg.boot_access_error
	jmp .error_common_j
.sector_too_large:
	mov dx, msg.boot_sector_too_large
	jmp .error_common_j
.sector_too_small:
	mov dx, msg.boot_sector_too_small
	jmp .error_common_j
.sector_not_power:
	mov dx, msg.boot_sector_not_power
	jmp .error_common_j
.invalid_sectors:
	mov dx, msg.boot_invalid_sectors
	jmp .error_common_j
.invalid_heads:
	mov dx, msg.boot_invalid_heads
.error_common_j:
	jmp .error_common

.error_common: equ bootcmd.fail


		; INP:	dx:ax = first sector
		;	bx:0 -> buffer
		; OUT:	dx:ax = sector number after last read
		;	es = input bx
		;	bx:0 -> buffer after last written
		; CHG:	-
		; STT:	ds = ss
read_ae_1536_bytes:
	push cx
	push bx
	mov cx, 1536
.loop:
	call read_sector
	sub cx, word [bp + bsBPB + bpbBytesPerSector]
	ja .loop
	pop es
	pop cx
	retn

		; INP:	dx:ax = first sector
		;	bx:0 -> buffer
		; OUT:	dx:ax = sector number after last read
		;	es = input bx
		;	bx:0 -> buffer after last written
		; CHG:	-
		; STT:	ds = ss
read_ae_512_bytes:
	push cx
	push bx
	mov cx, 512
.loop:
	call read_sector
	sub cx, word [bp + bsBPB + bpbBytesPerSector]
	ja .loop
	pop es
	pop cx
	retn


		; Read a sector using Int13.02 or Int13.42
		;
		; INP:	dx:ax = sector number (absolute in unit)
		;	bx:0-> buffer
		; OUT:	If unable to read,
		;	 ! jumps to error instead of returning
		;	If sector has been read,
		;	 dx:ax = next sector number (has been incremented)
		;	 bx:0-> next buffer (bx = es+word[load_sectorsizepara])
		;	 es = input bx
		; CHG:	-
		;
		; Note:	If error 09h (data boundary error) is returned,
		;	 the read is done into the load_sectorseg buffer,
		;	 then copied into the user buffer.
read_sector:
.err: equ bootcmd.fail_read
d5	call init1_d5dumpregs
d5	call init1_d5message
d5	asciz 13,10,"In read_sector",13,10

	push ds
	 push ss
	 pop ds
	push dx
	push cx
	push ax
	push si

	push bx

; DX:AX==LBA sector number
; add partition start (= number of hidden sectors)
		add ax,[bp + bsBPB + bpbHiddenSectors + 0]
		adc dx,[bp + bsBPB + bpbHiddenSectors + 2]

	xor cx, cx
	push cx
	push cx
	push dx
	push ax		; qword sector number (lpSector)
	push bx
	push cx		; bx:0 -> buffer (lpBuffer)
	inc cx
	push cx		; word number of sectors to read (lpCount)
	mov cl, 10h
	push cx		; word size of disk address packet (lpSize)
	mov si, sp	; ds:si -> disk address packet (on stack)

	test byte [bp + ldHasLBA], 1
	jz .no_lba

d5	call init1_d5message
d5	asciz "In read_sector.lba",13,10

	mov dl, byte [load_unit]
	mov ah, 42h
	int 13h		; 13.42 extensions read
	jnc .lba_done

	xor ax, ax
	int 13h
	jc .lba_error

		; have to reset the LBAPACKET's lpCount, as the handler may
		;  set it to "the number of blocks successfully transferred".
		; (in any case, the high byte is still zero.)
	mov byte [si + lpCount], 1

	mov ah, 42h
	int 13h
	jnc .lba_done

	cmp ah, 9	; data boundary error?
	jne .lba_error

d4	call init1_d4dumpregs
d4	call init1_d4message
d4	asciz 13,10,"In read_sector.lba_sectorseg",13,10

		; the offset part of the pointer is already zero!
	; push word [si + lpBuffer + 0]
	push word [si + lpBuffer + 2]	; user buffer
	 push word [load_sectorseg]
	 pop word [si + lpBuffer + 2]
	; and word [si + lpBuffer + 0], byte 0

	mov byte [si + lpCount], 1
	mov ah, 42h
	int 13h
	jnc .lba_sectorseg_done

	xor ax, ax
	int 13h
	jc .lba_error

	mov byte [si + lpCount], 1
	mov ah, 42h
	int 13h
	jc .lba_error
.lba_sectorseg_done:

	xor si, si
	mov ds, word [load_sectorseg]
	pop es
	; pop cx
	 push di
	; mov di, cx
	xor di, di
	mov cx, word [load_sectorsize]
	rep movsb
	 pop di

	push ss
	pop ds
.lba_done:
	add sp, 10h
	pop bx
	jmp .chs_done

.lba_error: equ .err

.no_lba:
	add sp, 8
	pop ax
	pop dx
	pop cx
	pop cx

; DX:AX=LBA sector number
; divide by number of sectors per track to get sector number
; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability
; Use two-step 32:16 divide to avoid overflow
			mov cx,ax
			mov ax,dx
			xor dx,dx
			div word [load_sectors]
			xchg cx,ax
			div word [load_sectors]
			xchg cx,dx

; DX:AX=quotient, CX=remainder=sector (S) - 1
; divide quotient by number of heads
			mov bx, ax
			xchg ax, dx
			xor dx, dx
			div word [load_heads]
			xchg bx, ax
			div word [load_heads]

; bx:ax=quotient=cylinder (C), dx=remainder=head (H)
; move variables into registers for INT 13h AH=02h
			mov dh, dl	; dh = head
			inc cx		; cl5:0 = sector
			xchg ch, al	; ch = cylinder 7:0, al = 0
			shr ax, 1
			shr ax, 1	; al7:6 = cylinder 9:8
	; bx has bits set iff it's > 0, indicating a cylinder >= 65536.
			 or bl, bh	; collect set bits from bh
			or cl, al	; cl7:6 = cylinder 9:8
	; ah has bits set iff it was >= 4, indicating a cylinder >= 1024.
			 or bl, ah	; collect set bits from ah
			mov dl, [load_unit]
					; dl = drive
			mov ah, 04h	; error number: sector not found
			 jnz .err	; error if cylinder >= 1024 -->
					; ! bx = 0 (for 13.02 call)

; we call INT 13h AH=02h once for each sector. Multi-sector reads
; may fail if we cross a track or 64K boundary
			pop es

			mov ax, 0201h
			int 13h		; read one sector
			jnc .done
; reset drive
			xor ax, ax
			int 13h
			jc .err

; try read again
			mov ax, 0201h
			int 13h
	jnc .done
	cmp ah, 9	; data boundary error?
	jne .err

d4	call init1_d4dumpregs
d4	call init1_d4message
d4	asciz 13,10,"In read_sector.chs_sectorseg",13,10

	push es		; user buffer
	 mov es, word [load_sectorseg]

	mov ax, 0201h
	int 13h
	jnc .chs_sectorseg_done

	xor ax, ax
	int 13h
	jc .err

	mov ax, 0201h
	int 13h
	jc .err
.chs_sectorseg_done:

	xor si, si
	mov ds, word [load_sectorseg]
	pop es
	 push di
	xor di, di
	mov cx, word [load_sectorsize]
	rep movsb
	 pop di

	push ss
	pop ds
.done:
; increment segment
	mov bx, es

.chs_done:
	mov es, bx
	add bx, word [load_sectorsizepara]

	pop si
	pop ax
	pop cx
	pop dx
	pop ds
; increment LBA sector number
	inc ax
	jne @F
	inc dx
@@:
	retn


		; INP:	ax => MCB to initialise
		;	cx = owner to set (8 if S MCB)
		;	bx = size to set
		;	dl = MCB signature, "M" or "Z"
		;	dh = S MCB type
		; OUT:	-
		; CHG:	-
init_an_mcb:
	push ax
	push ds
	push es
	push di
	mov ds, ax
	mov es, ax
	xor di, di
	xor ax, ax
	push cx
	mov cx, MCB_size >> 1
	rep stosw
	pop cx
	mov word [di - MCB_size + mcbOwner], cx
	mov word [di - MCB_size + mcbSize], bx
	mov byte [di - MCB_size + mcbSignature], dl
	cmp cx, 8
	jne @F
	mov byte [di - MCB_size + smcbName], "S"
	mov byte [di - MCB_size + smcbType], dh
@@:
	pop di
	pop es
	pop ds
	pop ax
	retn


disp_dxax_hex:			; dx:ax
		xchg ax, dx
		call disp_ax_hex
		xchg ax, dx
init1_disp_ax_hex:
disp_ax_hex:			; ax
		xchg al,ah
		call disp_al_hex		; display former ah
		xchg al,ah			;  and fall trough for al
disp_al_hex:			; al
		push cx
		mov cl,4
		ror al,cl
		call disp_al_lownibble_hex	; display former high-nibble
		rol al,cl
		pop cx
						;  and fall trough for low-nibble
disp_al_lownibble_hex:
		push ax			 ; save ax for call return
		and al,00001111b		; high nibble must be zero
		add al,'0'			; if number is 0-9, now it's the correct character
		cmp al,'9'
		jna .decimalnum		 ; if we get decimal number with this, ok -->
		add al,7			;  otherwise, add 7 and we are inside our alphabet
 .decimalnum:
		call disp_al
		pop ax
		retn


		; INP:	ds:si -> first byte to check for name
		;	cx = number of bytes left
		; OUT:	(8+1+3+1)bytes[es:msg.foundname] = found name,
		;	 converted to 8.3 ASCIZ format,
		;	 "(None)" if none
		;	CY if no filename found,
		;	 si = INP:si + INP:cx
		;	 cx = 0
		;	NC if filename found,
		;	 ds:si -> byte behind the name, thus ds:(si-11)-> name
		;	 cx = number of bytes left
		; CHG:	di, ax
findname:
.:
	cmp cx, 11		; enough for another name ?
	jb .none		; no -->
				; (cx == 0 jumps here too)
.check:
	push cx
	push si
	mov cx, 11
	lodsb
	mov ah, al		; check for same char in all 11 places
	cmp al, 32		; first character must not be blank
	je .check_fail		; if it is -->
;	cmp al, 5		; first character may be 05h to indicate 0E5h
;	je .check_pass
	db __TEST_IMM8		; (skip lodsb)
.check_loop_same:
	lodsb
	cmp ah, al
	jne .check_loop_differs
	call .check_character
	jc .check_fail
	loop .check_loop_same
		; if we arrive here, all characters (while valid) are the
		;  same character repeated 11 times. we disallow this in case
		;  that the padding character is an allowed one (eg '&' 26h).
.check_fail:
	pop si
	pop cx
	dec cx			; lessen the counter
	inc si			; -> next position to check
	jmp .

.check_character:
	cmp al, 32
	jb .check_character_fail
	cmp al, 127
;	je .check_character_fail
	jae .check_character_fail
		; note: with all characters >= 128 allowed,
		;  we get false positives in our sectors.
	cmp al, '.'
	je .check_character_fail
	cmp al, '/'
	je .check_character_fail
	cmp al, '\'
	je .check_character_fail
	cmp al, 'a'
	jb .check_character_pass
	cmp al, 'z'
	ja .check_character_pass
.check_character_fail:
	stc
	retn

.check_character_pass:
	clc
	retn

.check_loop:
	lodsb
.check_loop_differs:
	call .check_character
	jc .check_fail
.check_pass:
	loop .check_loop

	pop ax			; (discard si)
	sub si, 11		; -> at name

	call convert_name_to_asciz
				; si -> behind name
	pop cx
	sub cx, 11		; lessen the counter
	clc
	retn

.none:
	 add si, cx
	mov di, msg.foundname
	 push si
	 push ds
	push cs
	pop ds
	mov si, msg.foundname_none
	mov cx, (msg.foundname_none_size + 1) >> 1
	rep movsw
	 pop ds
	 pop si
	 xor cx, cx
	stc
	retn


		; INP:	ds:si -> 11-byte blank-padded name
		;	es:msg.foundname -> (8+1+3+1)-byte buffer
		; OUT:	ds:si -> behind 11-byte blank-padded name
		;	es:msg.foundname filled
		; CHG:	cx, di, ax
convert_name_to_asciz:
	mov di, msg.foundname
	mov cx, 8
	rep movsb		; copy over base name, si -> extension
	cmp byte [es:di - 8], 05h	; is it 05h ?
	jne @F			; no -->
	mov byte [es:di - 8], 0E5h	; yes, convert to 0E5h
@@:

	db __TEST_IMM8		; (skip dec)
@@:
	dec di			; decrement -> at previous trailing blank
	cmp byte [es:di - 1], 32	; trailing blank ?
	je @B			; yes -->

	mov al, '.'
	stosb			; store dot (if needed)
	mov cl, 3
	rep movsb		; copy over extension, si -> behind name

	db __TEST_IMM8		; (skip dec)
@@:
	dec di			; decrement -> at previous trailing blank
	cmp byte [es:di - 1], 32	; trailing blank ?
	je @B			; yes -->

	cmp byte [es:di - 1], '.'	; trailing dot ? (only occurs if all-blank ext)
	jne @F			; no -->
	dec di			; -> at the dot
@@:
	mov al, 0
	stosb			; store filename terminator
	retn


%if _DEBUG5 || _DEBUG4
init1_d5dumpregs:
init1_d4dumpregs:
	pushf
	push ax
	push bx
	push cx
	push dx
	push si
	push di
	push bp
	mov ax, sp
	add ax, byte 18
	push ax
	push cs
	push ss
	push ds
	push es

	mov bx, sp
	mov ax, 1<<8|7
	push ax
	mov ax, 14
	push cs
	pop ds
	mov dx, .regnames

	mov bp, sp
%assign _columns	0
%assign _blanks		1

	xor cx, cx

.looprows:
	push ax
	mov al, 13
	call init1_disp_al
	mov al, 10
	call init1_disp_al
%if 0
	push cx
	push dx
	xor cx, cx
	mov cl, byte [ _blanks + bp ]
	jcxz .doneblanks
	mov al, 32
.loopblanks:
	call init1_disp_al
	loop .loopblanks
.doneblanks:
	pop dx
	pop cx
%endif
	pop ax

	mov cl, byte [ _columns + bp ]
.loopcolumns:
	call init1_disp_reg
	add dx, byte 2
	add bx, byte 2
	dec ax
	jz .done
	loop .loopcolumns
	jmp short .looprows

.done:
	pop ax

	pop es
	pop ds
	pop ax
	pop ax
	pop ax
	pop bp
	pop di
	pop si
	pop dx
	pop cx
	pop bx
	pop ax
	popf
	retn

.regnames:
	db "esdssscsspbpdisidxcxbxaxflip"


	; ss:bx-> word value
	; ds:dx-> 2-byte message
init1_disp_reg:
	push ax
	xchg bx, dx
	mov ax, [ bx ]				; get 2 bytes at [ ds:dx ]
	xchg bx, dx
	call init1_disp_al
	xchg al, ah
	call init1_disp_al

	mov al, '='
	call init1_disp_al

	mov ax, [ ss:bx ]
	call disp_ax_hex

	mov al, 32
	call init1_disp_al
	pop ax
	retn

init1_disp_stack_hex:
	push ax
	push bp
	mov bp, sp
	mov ax, [bp+6]
	pop bp
	call init1_disp_ax_hex
	pop ax
	retn 2

init1_d5message:
init1_d4message:
	push ax
	push bx
	push si
	push ds
	push bp
	mov bp, sp
	mov si, [bp+10]
	pushf
	 push cs
	 pop ds
	call init1_disp_msg
	popf
	mov word [bp+10], si
	pop bp
	pop ds
	pop si
	pop bx
	pop ax
	retn

init1_d4uppercase:
	pushf
	cmp al, 'a'
	jb .return
	cmp al, 'z'
	ja .return
	and al, ~ 20h
.return:
	popf
	retn
%endif


	align 4
table:
	dw  +0, "SS"
	dw +12, "BP"
	dw +10, "SP"
	dw  +6, "CS"
	dw  +8, "IP"
	dw +26, "FL"
	db -1, -1, 13,10
	dw  +4, "DS"
	dw +16, "SI"
	dw  +2, "ES"
	dw +14, "DI"
	db -1, -1, 13,10
	dw +24, "AX"
	dw +22, "BX"
	dw +20, "CX"
	dw +18, "DX"
	db -1, -1, 13,10
	dw +28, "S0"
	dw +30, "S1"
	dw +32, "S2"
	dw +34, "S3"
	dw +36, "S4"
	dw +38, "S5"
	dw +40, "S6"
	dw +42, "S7"
	db -1, -1, 13,10
	dw +44, "S8"
	dw +46, "S9"
	dw +48, "SA"
	dw +50, "SB"
	dw +52, "SC"
	dw +54, "SD"
	dw +56, "SE"
	dw +58, "SF"
	db -1, -1, 13,10
.end:

	align 4
mcbtable:
	times 5 dd 0
	dd -1

boot_partition:
	dd 0
boot_unit:
	db 0
boot_drive:
	db 0


	align 4
throw_sp:	dw 0
throw_ip:	dw 0

fsparm_pointer:	dw fsparm
fsparm_excess:	dw 0
fsparm_number:	db 0

	align 16
parttable:
	times 4 * 16 db 0
.end:

	align 16
fsparm:
times FSPARM_size * 32 db 0


	align 16
init1_end:
	istruc MCB
at mcbSignature,	db "Z"
at mcbOwner,		dw 0
at mcbSize,		dw 0
	iend

