		subtitle	"< SYS0 IPL - Initialization code for BIOS, port hardware, stack, banks, etc >"
		newpage
		scope

STARTBIOS	call	scrolloff
		call	GETDATE
		call	GETTIME

; Fill spare DCB area with 0's.
		xor	a
		LD	HL,S1DCB$
ZERDCB		LD	(HL),A				;Zero spare dcb area
		INC	L
		JR	NZ,ZERDCB
			
; Setup high memory pointers.
		LD	HL,0ffffh			; Set physical max memory to 64k.
		LD	(PHIGH$),HL

; This code is kept intact to notate need for bank initialization. 
; Initialize Bank Available Ram & Bank Used Ram. Check the BANKS.

		XOR	A
		LD	(LBANK$),A			; Set logical bank #.
		LD	A,0feh				; Init BAR$ for bank 0-2.
		LD	(BAR$),A			; Load Bank Avail RAM.
		ld	a,0F8h
		LD	(BUR$),A			; Load Bank Used RAM.

; Initialize lowcore TIME$ values.

		RTC.HOUR.get (TIME$+2)			
		RTC.MIN.get (TIME$+1)				
		RTC.SEC.get (TIME$+0)
		PUMP	DEVICE.console,CRTBGN$		; Pump out message to a fresh clear screen.
		ld	a,(initialflg)
		or	a
		jr	nz,pastshawn

		PUMP	DEVICE.console,initialmsg

pastshawn	PUMP	DEVICE.console,splashiplmode	; Report CPU registers initialized.
				
; Get SLICE 0 @ 010000h. Slice 0 points to slices 1-9. Move slicetable in RAM for 9 slices (1-9).

		LD.Lil	HL,010000h			; HL now contains 24b address pointing to file info of RAM drive.
		LD.L	DE,slicebuf 			; Convert 16 bit destination address to 24 bit.
		LD.L	BC,256				; Copy 256 bytes, HL contains 24b source & DE destination.
		LDIR.L					; Copy now.
		MVC slicebuf,slicetable,27		; Move slice table to table in memory.

; Get SLICE 1 pointed to by SLICE 0.

		ld.l	hl,(slicetable)			; HL now contains 24b address pointing to file info of RAM drive.
		LD.lil	de,slicebuf			; Convert 16 bit destination address to 24 bit.
		LD.L	BC,256				; Copy 256 Bytes, HL contains 24b source.
		LDIR.L					; Copy now.
			
; Slicebuf now contains SLICE 1.
; SLICE 1 first 3 bytes, is an offset to boot volume, VOLUME 0.

; Now we get volume 0 HEADER in volbuf.
; Using SLICE 1 offset to calculate physical address to VOLUME 0 (boot volume).

		ld	hl,(slicebuf+1)			; Get offset to VOLUME 1.
		ld	de,(slicetable+1)		; Get upper 16 bits of pointer to slice 1.
		add	hl,de				; offset = offset + base pointer of slice 1.
			
; Using pointer to RAMDRIVE, copy first sector to volbuf.
; HL now points to slice 1.

		xor	A				; A = 0 for below code.
		LD	(XXX),a
		ld	(XXX+1),hl			; Store base of slice 1 + offset to volume 0.
			
; But first store this pointer in driver for RAM drive.

		inc	hl
		ld	(ramdrv$),hl			; Store in 0 slot of RAM driver table of addresses.

; Load volume 0  header/sector 0 into volbuf.

		LD.L	HL,(XXX)			; Load HL with 24b address of RAM drive.
		xor	a
		LD	(XXX+2),a			; Zero out upper 8 bits of 24b address.
		LD	de,volbuf			; Convert 16 bit destination address to 24 bit.
		LD	(XXX),de			; Store 16 bit address of volbuf destination.
		LD.L	DE,(XXX)			; DE contains 24b destination.
		LD.L	BC,256				; Copy 256 bytes, one sector, HL contains 24b source.
		LDIR.L					; HL contains pointer to RAM drive. Copy now.

; Now test if first 8 bytes of buffer contain ASCII string 'DiskDISK'. 
; If yes, this volume is in Misosys DiskDISK format.
; Else assume TRS-80 floppy disk format.

		ld	bc,8				; Test a string length of 8 characters.
		ld	hl,string1$			; Get pointer to string containing 'DiskDISK'
		ld	de,volbuf			; Address of buffer to test.
tstmore0	ld	a,(de)				; Get char pointed to by DE.
		inc	de				; Bump to next char.
		cpi					; Test and increment HL & DE while decrementing BC (char count).
		jp	nz,mounterr0			; If they dont match we can stop now.
		jp	v,tstmore0			; If BC <> 0 after CPI then continue looping until it does.

		mov.BYT volbuf,boottyp$,8		; Move boot volume name (DiskDisk) to menu.
setup_dct0	MVC	volbuf+11,DCT0$+3,7		; Move DiskDisk DCT to drive 0 DCT.
		ld	iy,DCT0$			; dct1.
		set	0,(iy+4)			; Tell world this is drive 0.
		ld	d,(iy+6)			; Get max cylinders.
		inc	d				; Adjust +1.
		ld	a,(iy+7)			; Get config, lower nibble contains sec per trk.
		and	3Fh				; Get lower nibble containing sectors per trk.
		inc	a
		bit	5,(IY+4)			; Sides = 2?
		jr	z,pastdbl			; If so then we double sectors per track.
		sla	a				; Sectors per track = sectors per track * 2.
pastdbl		ld	e,a				; Setup to multiply track * sectors per track.
		MLT	DE				; we then multiply (# CYL * SEC per CYL) 16b result in DE.
		push	de				; Save a copy of MLT results.
		inc	de				; Bump one sector higher to adjust for diskdisk header.
		ld	BC,bootsiz$			; Address to store ASCII result in string.

; Convert DE to 24 bit by  H=D, L=E, E=0    24 BIT--> E:HL.
; Store ASCII representation of 24 bit value in main memu.
; Convert to packed BCD.
; Convert packed BCD to unpacked.
; Convert unpacked to ASCII.
; Trim leading 0's away.
; Stuff all these results in main menu.

		call		cvt_24_ascii

		pop		de			; Recover original MLT results.
		ld		hl,2			; offset to VOLUME 1. VOl 0 length+diskdisk hdr sector VOLUME 0).
		add		hl,de			; Add size of VOLUME 0 and this will be where VOL 1 starts.
		ld.lil		(010104h),hl		; Offset to volume 1 image.
		ld		(slicebuf+4),hl		; Update table bc it has already been built.

; Update current cylinder to 0.

		xor		a			; Its way it is on real model 4.
		ld		(DCT0$+5),a		; Update DCT current cylinder.

; Now, get track 0, sector 0 of drive 0 image.

		LD		HL,volbuf		; HL points to buffer of READ.
		ld		d,0			; D=> Track 0.
		ld		e,0			; E=> Sector 0.
		ld		c,0			; C=> C contains drive #0.
		ld		b,9			; B=> Function in register B.
		call		FDCDVR			; Call driver.

; Recover from sector 0, directory cylinder & store in DCT0$.

		ld		a,(volbuf+2)		; Get pointer to directory.
		ld		(DCT0$+9),a		; Store directory cylinder in DCT.

; Read directory sector GAT of drive 0, boot volume. Register A still has directory track.

		LD		HL,volbuf		; HL points to buffer of READ.
		ld		d,a			; D=> A still contains directory track from last operation.
		ld		e,0			; E=> sector desired.
		ld		c,0			; C=> C contains drive #.
		ld		b,9			; B=> Function in register B.
		call		FDCDVR			; Call driver.
			
		MVC		volbuf+0D0h,bootvol$,8		; Copy volume name.
		MVC		volbuf+0D8h,bootvol$+9,8	; Copy volume creation time.

		ld		a,(volbuf+0CDh)		; Get drive configuration.
		bit		7,a			; Test if data disk?
		call		nz,cant_ipl_data	; Report not systems disk.

		ld		a,(volbuf+0cbh)		; Get version that formatted this volume.
		and		0F0h			; Strip of right hex digit.
		cp		060h			; Make sure this volume created by a DOS 6.x version.
		jp		nz,mounterr0		; If this volume not formatted by DOS 6.x we cannot use it.

		set		4,(iy+4)		; Tell world this is an alien controller.

		ld		a,(volbuf+0cch)		; Get logical cylinders in excess of 35.
		add		35
		jp		c,mounterr0		; Cannot overflow.
		ld		(DCT0$+7),a		; Make it highest sector per track.

		ld		a,(volbuf+0CDh)		; Get drive configuration.
		bit		7,a			; Test if data disk?
		jp		nz,mounterr0		; Report not systems disk.

		ld		bc,2409h		; Setup for single density.
		ld		(iy+7),c
		ld		(iy+8),b		; Tell OS this volume is formatted in single density. 
		bit		6,a			; Test if single or double density. 1=DDEN.
		jr		z,tstsides
		ld		bc,4511h
		ld		(iy+7),c
		ld		(iy+8),b		; Tell OS this volume is formatted in single density. 

tstsides	set		5,(IY+4)		; Set sides to 2.	
		bit		5,a			; Test number sides when formatted.
		jr		nz,getauto		; If <>0 then leave sides = 2.
		res		5,(IY+4)		; Make sides = 1.

; Get if any AUTO command & status of SYSGEN flag. Information contained in sector 2 of track 0, drive 0.

getauto		ld		iy,DCT0$		; dct0.
		LD		HL,volbuf		; HL points to buffer of READ.
		ld		d,0			; D=> track desired.
		ld		e,2			; E=> sector desired.
		ld		c,0			; C=> C contains drive #.
		ld		b,9			; B=> Function in register B.
		call		FDCDVR			; Call driver.

		MVC		spaceout$,autoflg$+2,69	; Make sure string is full of spaces.
		mov.CHR		volbuf+20h,autoflg$+2,71,CR.asc	; Move auto command if any to menu.
		MVC		volbuf+20h,INBUF$,79	; Copy auto command to auto buffer.
		ld		a,CR.asc

		ld		a,(volbuf+1)		; Get sysgen flag.
		ld		(ZERO$),a		; Store it in flag.
		or		A			; Sysgen off or on?
		jr		nz,nosysgenmsg		; SYSGEN = N so do not put file name in menu.
		MVC		CFGFCB$,SYSGN$,10	; Move name of sysgen file to menu.

nosysgenmsg	CKDRIVE		0,mounterr0		; See if TRSDOS finds formatted media it can read. If not, stop IPL.

; slicebuf now contains SLICE 1. Bytes 3,4,5 is an offset to data VOLUME 1 (TRS-80 drive 1).
; Get sector 0 of volume 1 in volbuf.

; Using SLICE 1 offset to VOLUME 1, calculate physical address to ram drive.

		ld		de,(slicetable+1)	; Get upper 16 bits of pointer to slice 1.
		ld		hl,(slicebuf+4)		; Get offset to VOLUME 1.
		add		hl,de			; offset = offset + base pointer of slice 1.
		
; Using pointer to RAMDRIVE, copy first sector to volbuf.

		xor		A			; A = 0 for below code.
		LD		(XXX),a			; Zero lower 8 bits.
		ld		(XXX+1),hl		; Store upper 16 bit pointer to VOL1.
			
; But first store this pointer in driver for RAM drive.

		inc		hl			; Advance pointer one page to actual RAMDRIVE contents.
		ld		(ramdrv$+2),hl		; Store in slot 1 of RAM driver table of addresses.

; Now copy sector 0 of a drive image or diskdisk header to volbuf (either way, first record of volume).

		LD.l		HL,(XXX)		; Load HL with 24b address of RAM drive.
		LD		de,volbuf		; Convert 16 bit destination address to 24 bit.
		LD		(XXX+2),a		; Zero out upper 8 bits of 24b address.
		LD		(XXX),de		; Store address lower 16bits.
		LD.L		DE,(XXX)		; DE=24b destination address.
		LD.L		BC,256			; Copy 256 Bytes, HL contains 24b source.
		LDIR.L					; Copy now.

		ld		iy,DCT1$
		set		1,(iy+4)		; Set 2nd physical drive (drive 1).

; We test string1$ which contains DiskDISK string against volbuff contents.

		ld		bc,8			; Test a string length of 8 characters.
		ld		hl,string1$		; Get pointer to string containing 'DiskDISK'
		ld		de,volbuf		; Address of buffer to test.
tstmore1	ld		a,(de)			; Get char pointed to by DE.
		inc		de			; Bump to next char.
		cpi					; Test and increment HL & DE while decrementing BC (char count).
		jr		nz,notdskdsk		; If they dont match we can stop now.
		jp		v,tstmore1		; If BC <> 0 after CPI then continue looping until it does.
		jr		dskdsk			; Finished testing, all match. Pass control to match code.

; Arriving here we are not mounting a DiskDisk volume.
; Change datatyp$ to JV1
; Recover pointer to RAM drive image and change from DD to JV1 by changing pointer to beginning. Begin=begin-1 sector.
; JV1 image starts at xxxxxxh DiskDisk actual drive image starts at xxxxxxh+0100h.

; Adjust pointer for RAMDRIVE since this is not DiskDISK in Volume 1.

notdskdsk	MVC		jv1$,datatyp$,8		; Change data type to JV1.

		LD		A,0			; Get Z80 NOP opcode.
		ld		(not_DD),a		; Change inc de to nop.

		ld		hl,(ramdrv$+2)		; Slot for this Volume of RAM driver table of addresses.
		dec		hl			; Pointer = pointer - one sector.
		ld		(ramdrv$+2),hl		; Store adjusted pointer back.

		xor		a
		ld		(DCT1$+5),a		; Set track position of head.

; Now read track=0 sector=0 of drive=1 & recover pointer to directory cylinder.

		LD		HL,volbuf		; HL points to buffer of READ.
		ld		d,0			; D=> Track 0.
		ld		e,0			; E=> Sector 0.
		ld		c,1			; C=> C contains drive #1.
		ld		b,9			; B=> Function in register B.
		call		FDCDVR			; Call driver.

		ld		a,(volbuf+2)		; Sector 0 of a drive image is on volbuf.
		ld		(DCT1$+9),a		; Point DCT to true directory cylinder.

		set		1,(iy+4)		; Tell world this is drive 1.
		set		4,(iy+4)		; Tell world this is an alien controller.

; Set density for single and make first attempt @finding directory.

		ld		bc,2409h		; Setup for single density.
		ld		(iy+7),c
		ld		(iy+8),b		; Tell OS this volume is formatted in single density. 

		jr		get1dir

; Arriving here it verified we are mounting a DiskDISK for volume 1.
; Move DCT from DD header to drive 1 DCT.
; Move file type from volbuf to datatyp$ in menu.

; Pointers built in storage driver already point to correct place for diskdisk.
; DiskDISK header still resides in volbuf.

; Mounted drive has been determined to be DISKDisk.
; Sector 0 of DISKDisk file is still in volbuf. 
; Recover DCT for this drive and copy to system DCT1$.
; Also set main menu to show DISKDisk type for this volume.

dskdsk		MVC	volbuf,datatyp$,8		; Copy name of DiskDISK volume to menu.
		MVC	volbuf+11,DCT1$+3,7		; Recover DCT settings for this DiskDISK volume to DCT 1.
	
		ld	a,(DCT1$+4)			; Drive 1.
		and	0F0h				; Strip out drive select bits.
		set	0,a				; Make this drive #1.
		set	4,a				; Tell world this is an alien controller.
		set	6,a				; Capable of double density.
		ld	(DCT1$+4),a

		xor	a				; Update DCT, its way it is on real model 4.
		ld	(DCT1$+5),a			; Cylinder=0.

; Now read track=0 sector=0 of drive=1 & recover pointer to directory cylinder.

		ld	iy,DCT1$			; dct1.
		LD	HL,volbuf			; HL points to buffer of READ.
		ld	d,0				; D=> Track 0.
		ld	e,0				; E=> Sector 0.
		ld	c,1				; C=> C contains drive #1.
		ld	b,9				; B=> Function in register B.
		call	FDCDVR				; Call driver.

; Get pointer to directory and place in DCT1 directory pointer.

		ld	a,(volbuf+2)			; Sector 0 of a drive image is on volbuf.
		ld	(DCT1$+9),a			; Point DCT to true directory cylinder.

; Now read directory as pointed to by recovered directory cylinder pointer.

; Now read 1st sector of directory.

get1dir		ld	iy,DCT1$			; dct1.
		ld	a,(DCT1$+9)
		LD	HL,volbuf			; HL points to buffer of READ.
		ld	d,a				; D=> Track, A contains directory cylinder.
		ld	e,0				; E=> Sector 0.
		ld	c,1				; C=> C contains drive #1.
		ld	b,9				; B=> Function in register B.
		call	FDCDVR				; Call driver.

; Make sure driver reported error #6. 
; We are reading from directory thus driver should have generated error #6.
; Error #6 is normal, no error is abnormal.

		jr	z,$				; No error 6 means it was not a system sector.
		cp	6				; Error on reading system sector.
		call	nz,nodirectort1

; volbuf now contains GAT,--- first sector of directory.

		ld	a,(volbuf+0d0h)			; Get 1st char of volume name.
		or	a				; If filename starts with 00h then its bum steer.
		jp	z,switch_den			; Switch density then try getting again..
		ld	a,(volbuf+0cbh)			; Get version that formatted this volume.
		and	0F0h				; Strip of right hex digit.
		cp	060h				; Make sure this volume created by a DOS 6.x version.
		jr	z,setup_dct1			; If this volume formatted by DOS 6.x skip to setting up DCT
		cp	050h				; If not formatted by LSDOS 6.x was it 5.x?
		call	nz,not56fmt			; Report no system directory.
		jr	setup_dct1

switch_den	ld	bc,4511h			; Values needed to set double density.
		ld	(iy+7),c
		ld	(iy+8),b			; Tell OS this volume is formatted in double density. 

; Try getting first sector of directory again using double density.

		ld	iy,DCT1$			; dct1.
		ld	a,(DCT1$+9)
		LD	HL,volbuf			; HL points to buffer of READ.
		ld	d,a				; D=> Track, A contains directory cylinder.
		ld	e,0				; E=> Sector 0.
		ld	c,1				; C=> C contains drive #1.
		ld	b,9				; B=> Function in register B.
		call	FDCDVR				; Call driver.

; Make sure driver reported error #6. 
; We are reading from directory thus driver should have generated error #6.
; Error #6 is normal, no error is abnormal.

		jr	z,$				; No error 6 means it was not a system sector.
		cp	6				; Error on reading system sector.
		call	nz,nodirectort1

; volbuf now contains GAT,--- first sector of directory using double density settings.
; Test and see if we found true directory.

		ld	a,(volbuf+0d0h)			; Get 1st char of volume name.
		or	a				; If filename starts with 00h then its bum steer.
		call	z,nodirectort1			; Switch density then try getting again.

		ld	a,(volbuf+0cbh)			; Get version that formatted this volume.
		and	0F0h				; Strip of right hex digit.
		cp	060h				; Make sure this volume created by a DOS 6.x version.
		jr	z,setup_dct1			; This was formatted by LSDOS 6.x
		cp	050h				; If not formatted by LSDOS 6.x was it 5.x?
		call	nz,not56fmt			; Report if not formatted by DOS 5 or 6.

setup_dct1	ld	iy,DCT1$			; dct1.
		res	0,(iy+4)			; Not drive 0.
		set	1,(iy+4)			; Tell world this is drive 1.
		set	4,(iy+4)			; Tell world this is an alien controller.

		ld	a,(volbuf+0cch)			; Get logical cylinders in excess of 35.
		add	35				; Add 35 to get true cylinder count.
		push	af
		call	c,mounterr1			; Cannot overflow.
		pop	af
		dec	a				; max_cyl=max_cyl - 1.
		push	af
		call	c,mounterr1			; Cannot overflow.
		pop	af
		ld	(iy+6),a			; Make it highest sector per track.

		ld	a,(volbuf+0cdh)			; Get configuration byte.
		res	5,(IY+4)			; Set sides to 1.	
		bit	5,a				; Test number sides when formatted.
		jr	z,v0_mounted			; If <>0 then leave sides = 2.
		set	5,(IY+4)			; Make sides = 2.

v0_mounted	ld	d,(iy+6)			; Get max cylinders.
		inc	d				; Adjust +1.
		ld	a,(iy+7)			; Get config, lower nibble contains sec per trk.
		and	3Fh				; Get lower nibble containing sectors per trk.
		inc	a
		bit	5,(IY+4)			; Sides = 2?
		jr	z,notdbl			; If so then we double sectors per track.
		sla	a				; Sectors per track = sectors per track * 2.
notdbl		ld	e,a				; Setup to multiply track * sectors per track.
		MLT	DE				; we then multiply (# CYL * SEC per CYL) 16b result in DE.

; Following instruction changes to NOP if this is not a diskDISK image.

not_DD		inc	de				; Bump one sector higher to adjust for diskdisk header.



; Stuff Volume 1 size in main menu.
; Convert DE to 24 bit by  H=D, L=E, E=0    24 BIT--> E:HL.
; Store ASCII representation of 24 bit value in main memu.
; Convert to packed BCD.
; Convert packed BCD to unpacked.
; Convert unpacked to ASCII.
; Trim leading 0's away.
; Stuff all these results in main menu.

		push	de				; Save a copy of MLT results.
		ld	BC,datasiz$			; Address to store ASCII result in string.

		call	cvt_24_ascii			; Convert 24 bit value to ASCII & stuff in main menu.

; Update slice 1 offset for volume 2.

		ld.lil	hl,(010104h)			; Recover offset to vol1.
		pop	de				; Recover volume 1 length.
		inc	de				; Bump one page forward to account for DiskDISK header.
		add	hl,de				; New sum. offset to vol1+vol1 length+256 byte DD header.
		ld.lil	(010108h),hl			; Offset to volume 2 image stored in slice table 1.
		
		MVC	volbuf+0D0h,datavol$,8		; Copy volume name.
		MVC	volbuf+0D8h,datavol$+9,8	; Copy volume creation date.

		ld	iy,DCT0$
;		set	7,(iy+3)		; WP vol 0. Force system/jcl to drive 1.
	
; Last call for alcohol. Either we have 2 ready volumes or we dont.
	
		CKDRIVE	0,mounterr0			; Volume 0, u ready Fredie? This party not starting if you not here.....
		CKDRIVE 1,mounterr1			; Last test, drive 1 <> avail? Tell operator.....we can have a minimum party :-(

; Continue IPL all drives mounted and accounted for.

flushkbd	LD	DE,KIDCB$			;  Flush keyboard, type,init ptrs.
		LD	A,3
		CALL	zCTL

; Test if server for TRSNET connected.

		GOSUB ping_server+3			; Enter ping routine ahead of scroll off call instruction.

; Delay a bit and allow copyright splash to be read.

		call		cpu_dly			; Delay a bit.
		ld		hl,reboot		; Change RST00 vector to high memory routine.
		ld		(zRST00+1),hl		; Stuff that baby in there.

		GOTO		DOSINIT			; Off to see the wizard the wonderful wizard of TRSDOS!

