;------------------------------------------------------------------------
;
; Relocatable loader for loading CUTER from cassette (CUTS) at the
;   address specified by the front panel sense switches (MSB of the 
;   load address). This program is loaded by the bootstrap loader
;   shown on page 24 of the SOLOS/CUTER manual.
;
;   The relocation table has one bit per byte of the module to be
;   loaded. If a bit is one, the value on the sense switches is
;   added to the byte to relocate it. This dictates that the module
;   on tape is org'd at zero.
;
;    Ver     Date	  Description
;    ---   ---------	---------------------
;    1.0   09/22/24	Original, Mike Douglas
;
;    1.1   09/28/24     Include relocation table in checksum
;
;    1.2   10/04/24	Use one byte checksum every 256 bytes to be
;			more like the original tape.
;
;------------------------------------------------------------------------
MODLEN	equ	800h		;length of module to load (800h max)
TBLLEN	equ	MODLEN/8 and 0FFh   ;length of relocation table

TAPECTL	set	0FAh		;tape control/status port
TAPEDAT	set	0FBh		;tape data register
TDR	equ	40h		;tape data ready bit mask

; A leader of over 64 bytes that are not 01h must precede this
;   code on the tape

	org	40h-1		;bootstrap loader jumps to 40h
	db	01h		;start of loader flag

; Load the relocation table from the tape

	xra	a		;init checksum
	sta	chkSum
	lxi	h,reloTbl	;HL->relocation table
	mvi	b,TBLLEN	;B=length of relocation table

loadTbl	call	getByte		;read a byte
	mov	m,a		;save it
	inx	h		;point to next byte
	dcr	b		;decrement byte count
	jnz	loadTbl

	call	getByte		;add in checksum
	lxi	h,chkSum	;should now be 0FFh
	inr	m
	jnz	csumErr		;checksum error

; Initialize for loading and validate the load address specified
;   on the front panel switches

	lxi	d,reloTbl	;DE->relocation table
	mvi	b,TBLLEN	;B=length of relocation table
	
	in	0FFh		;MSB of load addr from sense switches
	mov	h,a		;HL->where to put module
	mvi	l,0
	push	h		;save for later

	sbi	02h		;minimum address is 200h
	cpi	0F3h		;max valid address is F400h
	jnc	addrErr	

; Loop reading the module from tape into specified destination address
;   and relocate address MSBs as indicated by the relocation map
	
loop1	mvi	c,8		;C=relocation bits per byte count

loop2	call	getByte		;get module byte
	mov	m,a		;save to RAM
	
	ldax	d		;A=current relocation bits
	rlc			;test the next bit
	stax	d		;and save it back out
	jnc	next		;no carry means relocation not needed

	in	0FFh		;update MSB with relocation offset
	add	m
	mov	m,a

next	inx	h		;next destination address
	dcr	c		;bits per byte counter
	jnz	loop2		;loop if more bits left in current byte
	
	inx	d		;point to next byte of relo table 	
	dcr	b		;count table bytes
	mov	a,b		;256 bytes per checksum (32 counts of B)
	ani	1Fh
	jnz	loop1		;not checksum time

	call	getByte		;add in checksum byte
	lda	chkSum		;validate checksum, should be 0FFh here
	inr	a
	sta	chkSum		;back to zero if correct
	jnz	csumErr		;checksum error

	ora	b		;B reach zero? (A=0 here)
	jnz	loop1		;no, keep going
 
; Module loaded, set jump-to address at address zero and then halt

	mvi	a,JMP		;form jump instruction at zero
	sta	00h
	pop	h		;HL=load (and execute) address
	shld	01h		;store address portion of jump
	
	hlt			;halt

; addrErr - Address error abort
; csumErr - Checksum error abort

addrErr	lxi	h,HLT		;H=error code 00, L=HLT instruction
	shld	0
	hlt

csumErr	lxi	h,0100h+HLT	;H=error code 01, L=HLT instruction
	shld	0
	hlt

; getByte - read byte from cassette, add it to chkSum and
;   return it in A

getByte	in	TAPECTL		;wait for cassette byte
	ani	TDR
	jz	getByte

inByte	in	TAPEDAT		;A=byte from tape

	push	psw		;update checksum without clobbering
	push	h		;  any registers
	lxi	h,chkSum
	add	m
	mov	m,a
	pop	h		;restore HL
	pop	psw		;A=character read
	ret

; A byte of 0DDh marks the end of this loader

	db	0DDh		;end of loader flag
chkSum	ds	2		;16 bit checksum
reloTbl	equ	$		;relocation table put here

	end
