;**************************************************************
;BOOTLOAD
;PTDOS Loader
;
; Disassembled, commented, and verified by by Martin Eberhard
; 25 May 2015
;
; This program resides in the first 3-sector block (starting at
; track 0, sector 0) on a bootable PTDOS disk. Solos's boot
; loader (the "BO" command) loads it into memory at address 0
; and executes it there.
;
; This loader loads the next two 4095-byte (13-sector) disk
; blocks into memory starting at address 0A002h. Once loaded,
; it jumps into the loaded code at address BCC0h.
;
; Processor Technology seems to have based this loader on the
; disk routines in PTDOS, especially in the file called D14P#13.
; Perhaps for this reason, there is a lot of unnecessary code
; here. (There seems to have been little attempt to optimize
; what was borrowed from PTDOS.) I have attempted to use the
; same labels here as are used in the equivalent PTDOS code.
; However, the version of PTDOS source code that I have seems
; to be missing the end part of DP14P#13, so some labels here
; probably have different names than in the PTDOS source.
;
; The uninitialized variable space and unused memory at the end
; of PT's loader are all filled with 76h. It matters not what is
; in these memory locations, but here they are also filled with
; 76h, so that assembling this source code will create an
; identical file to the original PT loader code.
;
; This loader is stored on disk as a PTDOS Image file, which
; has a 4-byte header at the beginning, and a CRC byte and
; executation address at the end. The boot code in Solos, which
; loads and executes this loader, starts execution of this code
; at address 0 - executing the non-executable 4-byte header.
; Fortunately, executing these particular 4 bytes has no effect
; here.
;
; On disk, this Image file is called BOOTLOAD, with type "I00",
; and ID 0006.
;
; This code assembles with Digital Research's ASM assembler.
;**************************************************************

;Loader Constants

LODADR	equ	0A002h		;code load address
BLKSIZ	equ	0FFFh		;Load block byte count
FILEID	equ	0004h		;File ID for load
EXEADR	equ	0BCC0h		;load execution address
BTUNIT	equ	0		;boot disk unit

HDRSIZ	equ	13		;Header byte count
TRACKS	equ	77		;number of tracks per disk

;Helios Controller Ports
;(Helios II Disk Memory Systems Manual, page 7-10)

HEBASE	equ	0F0h		;Helios controller base address
STAPT	equ	HEBASE		;Status input
TRAPT	equ	HEBASE+1	;Transfer Command output
BYTLO	equ	HEBASE+3	;Transfer Length Low output
BYTHI	equ	HEBASE+4	;Transfer Length High output
ADRLO	equ	HEBASE+5	;Transfer Address High output
				;also clears STAPT
ADRHI	equ	HEBASE+6	;Transfer Address High output
COMPT	equ	HEBASE+7	;Drive Command output

;Helios Status bits
;(Helios II Disk Memory Systems Manual, page 7-4)

TRCOM	equ	01h		;High=Transfer Complete
SREDY	equ	02h		;High=SREADY	
ABERR	equ	04h		;High=Abort
CCERR	equ	08h		;High=CRC Error
CKCOM	equ	10h		;High=CRC Checked
DREDY	equ	20h		;Low=Disk Ready
				;(selected, loaded, spinning)
SKCOM	equ	40h		;Low=Seek Complete
INDEX	equ	80h		;Low=Index Hole Present

;Helios Transfer Commands, active low. Bits 4-7 are ignored.
;(Helios II Disk Memory Systems Manual, page 7-4)

RDATA	equ	3		;READ DATA COMMAND
WDATA	equ	1		;WRITE DATA COMMAND
READH	equ	7		;READ HEADER COMMAND
WRITH	equ	5		;WRITE HEADER COMMAND


;Helios Drive Command Bits, all active low
;(Helios II Disk Memory Systems Manual, page 7-5)

HDSTPN	equ	0FEh		;Low=step
HDSIN	equ	0FDh		;Low = step in, high=step out
HDDS1N	equ	0FBh		;Drive Select 1, active low
HDDS2N	equ	0F7h		;Drive Select 2, active low
HDRSTN	equ	0EFh		;Restore Disk, active low
HDHL0N	equ	0DFh		;Load Even Head, active low
HDHL1N	equ	0BFh		;Load Odd Head, active low
HDDSN	equ	07Fh		;Select Odd Disk, active low

;**************************************************************
	org	0

;--------------------------------
; Image File Header
;As it turns out, executing these
;bytes has no significant effect.
;--------------------------------
	dw	032Dh		;byte count
	dw	0004h		;load address

;------------------------
;Actual beginning of code
;------------------------
INIT:	di
	lxi	sp,STACK
	call	RSTOR0		;restore disk 0 head 0

;------------------------------------------------------
;Use Transfer Descriptor 1 to read the first disk block
;------------------------------------------------------
	lxi	d,TDESC1	;1st transfer descriptor
	lxi	h,TDAD		;Actual Transfer Descriptor
	call	MOVE8		;copy 1st TD to actual TD

;The above loop fails to copy the unit number in the transfer
;descriptor. Rather than fix the count in MOVE8 to be 9,
;the code below just writes 0 into TUNIT.

	xra	a		;select drive 0, disk 0
	sta	TUNIT

	call	RDSK		;read the 1st block

;-------------------------------------------------------
;Use Transfer Descriptor 2 to read the second disk block
;-------------------------------------------------------
	lxi	h,TDAD		;Actual Transfer Descriptor
	lxi	d,TDESC2	;2nd transfer descriptor
	call	MOVE8		;copy 2nd TD to actual TD
				;..not writing TUNIT

	call	RDSK		;read the 2nd block

;-------------------
;Jump to loaded code
;-------------------
	jmp	EXEADR

;***Subroutine*************
;Move 8 bytes
;On Entry:
;  de = source address
;  hl = destination address
;Trashes b,de,hl
;**************************
MOVE8:	mvi	b,8

MOVBLK:	ldax	d
	mov	m,a
	inx	h
	inx	d
	dcr	b
	jnz	MOVBLK

	ret

;*********************************
;Transfer Descriptor 1
;Source:
;  Track 0
;  Block 1
;  length: 13 sectors = 4095 bytes
;Destination: 0A002h
;*********************************
TDESC1:	db	03h		;Sector number to read
	db	00h		;Track number to read
	dw	BLKSIZ		;Byte count

	dw	FILEID		;File ID
	dw	LODADR		;Transfer address
	db	BTUNIT		;Boot disk number (not used)

;***************************************
;Transfer Descriptor 2
;Source:
;  Track 1
;  Block 0
;  length: 13 sectors = 4095 bytes
;Destination: 0B001h
;  (Immediately following the 1st block)
;***************************************
TDESC2:	db	00h		;Sector number to read
	db	01h		;Track number to read
	dw	BLKSIZ		;Byte count
	dw	FILEID		;File ID
	dw	LODADR+BLKSIZ	;Transfer address
	db	BTUNIT		;Boot disk number (not used)

;***Subroutine**********************************
;Read Disk Block
;On Entry:
; Transfer descriptor at TDAD describes the read
;trashes all registers
;************************************************
RDSK:	mvi	a,RDATA		;Read operation
	di			;(unnecessary...)
	call	OPSE1		;set operation
	call	DATNS		;clear error control flags

DOVER:	call	HDVER		;reset retry counter & Seek track

DATAOP:	call	HDATOP		;Read next sector header
	jc	DATER		;Correct track?
	jnz	DRTRY		;Process the error

;c has desired track, b has desired header

	lda	IHSEC		;Get the read-in sector
	cmp	b		;correct block number?
	jnz	NOSEC		;n: retry block read

;Test header parameters

	lxi	h,TBCNT		;Point to transfer descriptor
	lda	IHSIZ		;Get specified size
	cmp	m		;Is it the same?
	jnz	FATAL		;n: abort and hang forever

	inx	h		;note: high byte not checked

;Byte count is ok. Now check the file ID

	inx	h
	lda	IHFID		;File ID from header
	cmp	m		;correct ID?
	jnz	FATAL		;n: abort and hang forever

				;Note: high byte not checked

;We even check the sector count

	lda	OHPRO		;Get the protect word
				;no need to strip high bit here
	mov	b,a
	lda	IHPRO		;Block sector count from header
	cmp	b		;correct sector count?
	jnz	FATAL		;n: abort and hang forever



	lhld	DRLEN		;Length of transfer
	xchg			;..to de for output
	lhld	TBUF		;Transfer address

	lda	DOPER		;disk read operation
	call	DODMA		;initiate DMA command
	jmp	DMAWAT

;***Subroutine************
;Delay for 8 mS
;trashes psw,de
;*************************
DELAY:	lxi	d,02a6h

DELY1:	dcx	d		;(5)
	mov	a,d		;(5)
	ora	e		;(4)
	jnz	DELY1		;(10)

	ret

;***Error Routine*********
;Wait for DMA to complete
;Retry on error
;*************************
DMAWAT:	call	DLOPS		;Wait for transfer to complete
	jnz	DRTRY		;Retry on error

RCCHB:	in	STAPT		;Get Helios status
	ani	CKCOM		;done checking CRC?
	rnz			;y: done

	in	STAPT		;Get Helios status
	ani	ABERR+CCERR	;Controller abort or CRC error?
	jz	RCCHB		;n: keep waiting

DRTRY:	call	ERRPR		;y: bump the data error count
	jmp	DOVER		;go retry the block

;***Error Routine**************
; Wrong track: Retry block read
;******************************
DATER:	call	TERRP		;Process the track error
	jmp	DOVER		;go re-seek & re-read block

;***Subroutine******************
;Data error found
;Cancel the command and wait for
;the controller to be ready
;On Exit:
;  c = target track
;  DTRIES has been bumped
;trashes psw
;*******************************
ERRPR:	mvi	a,0FFh		;cancel any previous command
	out	TRAPT

	call	WAITS		;wait for Helios to be ready
	lda	TTRK		;target track
	mov	c,a		;keep in c

	lda	DTRIES		;data retry count
	cpi	1eh		;too many?
	jnc	FATAL		;n: abort and hang forever

	inr	a		;bump data error count
	sta	DTRIES
	ret

;***Subroutine*******************
;Seek 2 tracks inward, to try and
;recover from a track error
;On Entry:
;   c = target track+2
;   CTRCK = current track
;   CUNIT = drive command
;trashes psw
;********************************
FIDWN:	mov	a,c
	sui	2		;seek 2 tracks outward
	jp	SEEKA		;unless at outside diameter

	xra	a		;seek track 0

SEEKA:	mov	c,a
	jmp	SEEKC		;seek track c


;***Subroutine********************
;Seek 2 tracks outward, to try and
;recover from a track error
;On Entry:
;   c = target track-2
;   CTRCK = current track
;   CUNIT = drive command
;trashes psw
;*********************************
FIUP:	mov	a,c
	adi	2
	cpi	TRACKS		;at inside diameter?
	jc	SEEKA		;n: go again

	mvi	a,TRACKS-1	;y: go no further
	jmp	SEEKA

;***Error Routine*******************************
;Read valid header, but still not desired sector
;Retry block read, unless too many retries
;***********************************************
NOSEC:	lda	DSECN		;bump block retries
	inr	a
	sta	DSECN

	cpi	19h		;Too many?
	jnc	FATAL		;y: abort and hang forever

	jmp	DATAOP		;n: try again

;***Subroutine***********
;Error flag setup
;On Exit:
;   DTRIES = 0
;   DNERR = 0FFh
;   DTERR = 0FFh
;trashes psw
;************************
DATNS:	xra	a		;reset data error counter
	sta	DTRIES

	cma			;0FFh
	sta	DNERR		;Set in/out seek switch
				;(this flag is never used.)

	sta	DTERR		;minus track errors
	ret

;***Subroutine**************************
;Seek new track
;On Entry:
;   CTRCK = current track
;   TTRK = target track
;   RUNIT = drive command
;On Exit:
;  disk is at index hole on target track
;  a = STAPT value shifted left once
;  UNTAB(unit) = target track
;trashes de,hl
;***************************************
HDVER:	xra	a
	sta	DSECN		;reset block read retries
	jmp	SECHK		;and go seek

;***Subroutine*******************
;Read block header
;On Entry:
;  TSEC = target sector
;  TTRK = target track
;
;On Exit:
;  block header is in IHEAD
;  b = sector number
;  c = current track
;  Z cleared if any errors
;  carry set if wrong track
;trashes a,de,hl
;********************************
HDATOP:	call	WAITS		;Be sure controller is ready

;Read block header

	lxi	h,IHEAD		;DMA in header buffer
	lxi	d,HDRSIZ	;Length of header

	mvi	a,READH		;Read head command
	call	DODMA		;initiate DMA transfer
	call	DLOPS		;Wait for transfer to complete
				;and get error reports
	rnz			;Error return with Z cleared

	lxi	h,TDAD
	mov	b,m		;b=target sector number
	inx	h
	mov	c,m		;c=target track

;Wait for controller to complete CRC calculation

WATCRC:	in	STAPT		;Check Helios status
	ani	ABERR+CCERR+SREDY
	rnz			;Another error exit

	in	STAPT		;Get Helios status
	ani	CKCOM		;Done checking CRC?
	jz	WATCRC		;n: keep waiting

;Verify that we landed on the correct track

	lda	IHTRK		;track number from header
	cmp	c		;correct track number?
	stc			;in case of return
	rnz			;n: return with carry set

;Success exit

	xra	a		;clear the flags
	ret			;return with carry cleared

;***Subroutine*******************************
;Wait for transfer to complete,
;then get & test Helios status
;On Exit:
;  a=0 and Z set if no errors
;  Z cleared if controller abort or CRC error
;All other registers preserved
;********************************************
DLOPS:	in	STAPT		;Get Helios status
	ani	CCERR+ABERR+SREDY+TRCOM ;done for any reason?
	jz	DLOPS		;n: keep waiting

	ani	CCERR+ABERR	;Controller abort or CRC error?
	ret

;***Subroutine******************
;Initiate DMA transfer 
; On Entry:
;   a = transfer command
;  de = transfer byte count
;  hl = DMA address for transfer
;All registers preserved
;*******************************
DODMA:	push	psw

	mov	a,l		;set up DMA address
	out	ADRLO
	mov	a,h
	out	ADRHI

	mov	a,e		;set up transfer length
	out	BYTLO
	mov	a,d
	out	BYTHI

	pop	psw		;recover transfer command

	out	TRAPT		;..and initiate transfer
	ret

;***Subroutine*****************************************
;Process track error
; The first time, restore the disk and try again
; The second time, if the found track is a valid track
;    then just re-seek the target track and try again
;    else restore and try again
; For the next 9 times
;    for odd retries, stepin 2 tracks, and try again
;    for even retries, step out 2 tracks, and try again
; After11 retries, hang
;On Entry:
;  TTRK = target track
;trashes psw,bc,hl
;******************************************************
TERRP:	call	WAITS		;wait for controller to be ready
	lda	TTRK		;get target track
	mov	c,a		;keep desired track in c

;Bump track retry counter

	lda	DTERR
	inr	a
	sta	DTERR

;if this is the second retry, re-seek TTRK

	dcr	a
	jz	RESEEK

;if this is the first retry, restore and then reseek TTRK

	jm	SEEK0		;restore to track 0

;Error - hang if more than 10 retries

	cpi	0bh
	jnc	FATAL		;abort and hang forever

;On odd retries, step in 2 tracks and try again
;On even retries, step out 2 tracks and try again

	rrc			;odd or even retry?
	jc	FIDWN		;odd: seek 2 tracks inward
	jmp	FIUP		;even:seek 2 tracks outward

;Re-seek the target track, unless invalid track number in header

RESEEK:	lda	IHTRK		;track number from header
	cpi	TRACKS		;Valid track number?
	sta	CTRCK		;current track
	jc	SECHK		;y: seek target track again

;Invalid header: Fall into RSTOR0 to restore disk 0 head 0

;***Subroutine**********************************************
;Select disk 0, head 0 and restore it
;On Exit:
;  disk is at index hole
;  a = STAPT value shifted left once
;  RUNIT = drive command register value for drive 0, head 0
;trashes bc,hl
;***********************************************************
RSTOR0:	xra	a		;select unit 0
	sta	CUNIT

;Fall into SEEK0

;***Subroutine**************************
;On Entry:
;  CUNIT = unit number
;On Exit:
;  disk is at index hole
;  a = STAPT value shifted left once
;  RUNIT = drive command register value
;trashes bc,hl
;***************************************
SEEK0:	lda	CUNIT		;Compute DC Register value
	call	USET		;returns Drive Control Reg value
	out	COMPT		;select drive

;Restore to track 0

	ani	HDRSTN		;restore command bit low
	out	COMPT

	xra	a		;track 0
	call	SETU1		;look up track number

	xra	a		;current track is now 0
	mov	m,a		;for this drive

	jmp	SKDON		;wait for seek to complete

;***Subroutine**************************
;Seek track
;On Entry:
;   CTRCK = current track
;   TTRK = target track
;   RUNIT = drive command
;On Exit:
;  disk is at index hole on target track
;  a = STAPT value shifted left once
;  UNTAB(unit) = target track
;trashes bc,hl
;***************************************
SECHK:	call	DRLOP		;wait for disk to be ready
	lda	TTRK		;target track
	mov	c,a

;Fall into SEEKC

;***Subroutine**************************
;Seek track
;On Entry:
;   c = target track
;   CTRCK = current track
;   RUNIT = drive command
;On Exit:
;  disk is at index hole on target track
;  a = STAPT value shifted left once
;  UNTAB(unit) = target track
;trashes b,hl
;***************************************
SEEKC:	lda	CTRCK		;get current track
	sub	c		;Which way to go?
	rz			;Already there?

	di			;no interrupts while seeking

	jp	SECH1		;positive: step out

;Go up in track numbers (step in)

	cma			;invert step count
	inr	a

	mov	b,a
	lda	RUNIT
	ani	HDSIN		;negative: step out

	jmp	SECH2

;Go down in track numbers (step out)

SECH1:	mov	b,a
	lda	RUNIT		;step in or step out

;Step in or out

SECH2:	out	COMPT
	ani	HDSTPN		;step command

;Step b tracks

SECH3:	out	COMPT
	dcr	b		;More tracks to step?
	jnz	SECH3

	mov	a,c		;target track
	call	SETU1
	mov	m,c		;put in table

;Fall into SKDON

;***Subroutine***********************
;Wait for seek to complete
;On Exit:
;  disk is at index hole
;  a = STAPT value shifted left once
;All other registers preserved
;************************************
SKDON:	in	STAPT		;Get Helios status
	ani	SKCOM		;Seek complete?
	jnz	SKDON

;Wait for next index hole

IFIND:	in	STAPT		;Get Helios status
	rlc			;test INDEX
	jc	IFIND

	ret

;***Subroutine**************************************
;Set up to perform disk operation
;On Entry:
;  a = disk operation (RDATA always in this program)
;(This whole subroutine is unnecessary, because this
;loader expects only 0FFFh-byte blocks, and only
;on unit 0.)
;Trashes all registers
;***************************************************
OPSE1:	sta	DOPER		;remember disk operation

	xra	a
	sta	NOERR		;clear error verify flag
				;(This flag is never used)

	call	CMPSCS		;compute required sectors
				;..based on BLKCNT from header

	lda	TUNIT		;current unit
	call	USET		;convert unit number to DC value
	sta	RUNIT		;save Drive Control Reg value
	out	COMPT		;and issue command

	out	ADRLO		;clear status byte
	call	DRLOP		;wait for disk to be ready

	lda	TUNIT		;target drive=current drive?
	ani	7
	lxi	h,CUNIT
	cmp	m
	mov	m,a		;target=current
	jz	SETCT		;y: set current track

;New drive. Wait for 8 mS past the next index pulse

	call	IFIND		;wait for index
	mvi	c,11h		;Remnant: unused delay value
	call	DELAY		;stall for 8 mS

SETCT:	call	SETU1		;get this drive's current track
	sta	CTRCK		;it is our current track now
	ret

;***Subroutine********************************
;Look up the current track for this unit
;
;hang forever if CUNIT>7
;On Entry:
;  a = target track
;  CUNIT = unit number (always 0 here) 
;On Exit:
;  a = current track number for this unit
; hl = address of UNTAB(Unit)
; CRTRK = target track
;(This whole subroutine is unnecessary because
;this loader only loads from unit 0.)
;trashes psw,hl
;*********************************************
SETU1:	sta	CTRCK
	lxi	h,UNTAB		;Track(drive) table

	lda	CUNIT
	cpi	8		;valid unit number?

OOPS:	jnc	OOPS		;n: Hang here forever

	ora	a		;clear carry
	rar			;divide by 2 (2 heads/drive)
	add	l		;offset into table
	mov	l,a
	mov	a,m		;get drive's current track
	ret

;***Subroutine********************************
;COmpute Drive Command Register value
;On Entry:
;  a = unit number
;On Exit:
;  a = Drive Command Register value
;      with correct select & head load bits
;(This whole subroutine is unnecessary because
;this loader only loads from unit 0.)
;trashes bc
;*********************************************
USET:	ani	7
	lxi	b,0D030h	;b=even head loaded,
				;..even unit selected
				;c=odd head loaded,
				;..odd unit selected
	rrc			;test lsb 
	rlc			;Odd or even head?
	jnc	LUNIT

	mov	b,c		;Select odd unit, load its head
LUNIT:
	ani	6		;which unit
	rlc			;into bits 1 & 2
	cma			;make them active low
	ani	0Ch		;drive select bits in place
	ora	b		;combine w/ disk sel & head load
	ori	3		;no stepping
	ret

;***Subroutine****************************************
;Compute the number of sectors required for this block
;On Entry:
;   TBCNT = bytes count from block header
;On Exit:
;   c = number of sectors required for this many bytes
;   OHPRO = computed sector count, preserving bit 7
;(This whole subroutine is unnecessary, because this
;loader expects only 0FFFh-byte blocks, which require
;13 sectors always.)
;trashes psw,de,hl
;*****************************************************
CMPSCS:	lhld	TBCNT
	mvi	c,0		;0 so far
	lxi	d,-100h		;the first sector is 256 bytes

BSLOOP:	dad	d		;16-bit subtract
	inr	c

	mov	a,h
	ora	l		;exact fit?
	jz	X024c

	mov	a,h
	ora	a
	jm	X024c		;close fit?

	lxi	d,-140h		;subsequent sec's are 296 bytes
	jmp	BSLOOP

;--------------------------------
;  c = number of sectors required
;--------------------------------
X024c:	lhld	TBCNT
	shld	OHSIZE		;put in outgoing descriptor
	shld	DRLEN		;Length of transfer

;Save computed sector count, leaving bit 7 (protection bit)

	lda	OHPRO
	ani	80h
	ora	c
	sta	OHPRO

	ret

;***Subroutine*************************
;Wait for Helios controller to be ready
;trashes psw
;**************************************
WAITS:	in	STAPT		;Get Helios status
	ani	SREDY		;Wait for ready
	jz	WAITS

	ret

;***Subroutine**********************
;Wait for disk drive to be ready
;(disk selected, inserted, spinning)
;trashes psw
;***********************************
DRLOP:	in	STAPT		;Get Helios status
	ani	DREDY		;Disk drive ready?
	rz

	out	ADRLO		;Clear STAPT
	jmp	DRLOP

;****ERROR ROUTINE********
;Fatal error: Hang forever
;*************************
FATAL:	jmp	FATAL		;hang forever

;----------------------------
;Left-over execution address?
;----------------------------
	dw	0004h

;-----------
;Stack space
;-----------
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h
STACK	equ	$

;-------------
;RAM Variables
;-------------
RUNIT:	db	76h	;Command Register value for current unit
CUNIT:	db	76h	;Current unit
CTRCK:	db	76h	;current track

; Disk Driver Temporaries (from D14P#3)

DTRIES:	db	76h	;data error retry count
DSECN:	db	76h	;block read retry count
DNERR:	db	76h	;in/out seek switch (not used...)
DTERR:	db	76h	;track retry counter
NOERR:	db	76h	;Verify flag for errors (not used...)
DRLEN:	dw	7676h	;Length of transfer
DOPER:	db	76h	;Disk operation

;***********************************************
;Track position table
;Each entry is for one drive. Note that each
;drive has 2 heads, which are on the same track.
;(This whole table is unnecessary, since this
;loader only loads from unit 0.)
;***********************************************
UNTAB:	db 76h
	db 76h
	db 76h
	db 76h

;Transfer Descriptor
;Gets initialized during INIT

TDAD:
TSEC:	db	76h	;Target sector number
TTRK:	db	76h	;Target track
TBCNT:	dw	7676h	;byte count
TFID:	dw	7676h	;File ID (not used)
TBUF:	dw	7676h	;Transfer address
TUNIT:	db	76h	;Target unit number

; Incoming Header

IHEAD:
IHSEC:	db	76h	;sector number
IHTRK:	db	76h	;track number
IHFOR:	dw	7676h	;forward pointer (not used)
IHBAK:	dw	7676h	;backward pointer (not used)
IHFID:	dw	7676h	;file ID
IHPRO:	db	76h	;block sector count & protect bit
IHSIZ:	dw	7676h	;Block byte count
IHSPR:	db	76h	;Room for expansion (not used)

; Outgoing Header

OHEAD:
OHSEC:	db	76h	;sector number (Not used)
OHTRK:	db	76h	;track number (Not used)
OHFOR:	dw	7676h	;forward pointer (not used)
OHBAK:	dw	7676h	;backward pointer (not used)
OHFID:	dw	7676h	;file ID (not used)
OHPRO:	db	76h	;block sector count & protect bit
OHSIZE:	dw	7676h	;Block byte count
OHSPR:	db	76h	;Room for expansion (not used)

;------------
;Unused space
;------------
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h
	db	76h,76h,76h,76h,76h,76h,76h,76h,76h,76h

;------------------------
;Image File Data CRC
;------------------------
	db	3Bh

;----------------------------
;Image File Execution Address
;----------------------------
	dw	0004h

	end


