; Disassembly of the file "Z:\home\knoppix\Public\CPM_KRYSS_Loader.bin"
; 
; CPU Type: Z80
; 
; Using the opcode map file "Z:\home\knoppix\Public\CPM_KRYSS_Loader.opmap"
; ; Created with dZ80 2.0
; 
; on Friday, 13 of January 2017 at 09:52 PM
; 
	ORG	$FC00	; code will start loaded at $0000 but will relocate at $FC00

	DEFB	$00

	DI	
	LD	HL,STACK
	LD	SP,HL	; stack at STACK
	LD	A,$41
	OUT	($FE),A	; write 41 to port C of 8255:
;			; set border to blue, set signal "SO" to 0, set signal "O5" to 0,
;			; set signal "O6" to 1 to allow access to video memory in the CP/M hw config
	LD	HL,$0000	; copy all of
	LD	DE,$FC00	; this code
	LD	BC,$0200	; from 0000
	LDIR			; to FC00 and then...
	JP	LOGO	; ...jump in copied code to logo routine

			; ########## ATTENTION !!! ############
			; In order to determine the correct values for the next 4 words, we need to save the pass 2 assembler listing
			; and see the actual addresses of CTC INT. ROUTINE #1 and CTC INT. ROUTINE #2 which must be copied here
			; after the DEFW keywords, instead of the current values.
			; #####################################
STACK	DEFW	$FC95	; (FC18) CTC0 vector / CTC INT. ROUTINE #2 does nothing

CTC_V1	DEFW	$FC95	; (FC1A) CTC1 vector / CTC INT. ROUTINE #2 does nothing

CTC_V2	DEFW	$FC88	; (FC1C) CTC2 vector / CTC INT. ROUTINE #1 [replaces CTC0 vector with CTC1 vector]

CTC_V3	DEFW	$FC95	; (FC1E) CTC3 vector / CTC INT. ROUTINE #2 [does nothing]

FLAG	DEFB	$00	; (FC20) used as flag: =00 right before & during track reading, =FF after
T1STOR	DEFB	$00	; (FC21) \ storage start address
	DEFB	$00	; (FC22) / for data read from disk (0000 initially)
T2STOR	DEFB	$00	; (FC23) \ storage start address for the second system track
	DEFB	$24	; (FC24) / (18 sectors * 512 bytes = 12h*200h = 2400h)
STOR	DEFB	$00	; (FC25) \ current storage address
	DEFB	$00	; (FC26) / for data read from disk
TCTC1	DEFB	$00	; (FC27) time constant for CTC channel 1 (256 bytes to read)
TCTC2	DEFB	$24	; (FC28) time constant for CTC channel 2 (24=2*12 -> 2*12h*256=12h*512=18 sectors=1 track both sides)
SPEC	DEFB	$EF	; (FC29) \ these 2 bytes are arguments for a Specify command
	DEFB	$FF	; (FC2A) / (SRT=E=2ms, HUT=F=240ms, HLT=7F=254ms, ND=1->Non-DMA mode)
ARGS	DEFB	$00	; (FC2B) arg#1: Side, Drive No.
ARG1	DEFB	$00	; (FC2C) arg#2: C (Track no.)
	DEFB	$00	; (FC2D) arg#3: H (Head no.)
	DEFB	$01	; (FC2E) arg#4: R (Sector no.)
	DEFB	$02	; (FC2F) arg#5: N (Bytes/sector) *** 2 means 512 bytes/sector=128*2^N ***
	DEFB	$09	; (FC30) arg#6: EOT (Last sector no. of a track) *** 9 sectors/track ***
	DEFB	$50	; (FC31) arg#7: GPL (Gap 3 Length, betw sectors)
	DEFB	$FF	; (FC32) arg#8: DTL (Data Length, no.of bytes to be rd/wr into sector) =FF if N!=0
	DEFB	$FF
RSLT	DEFB	$00	; (FC34) result byte#1: ST0
	DEFB	$00	; (FC35) result byte#2: ST1 / PCN
	DEFB	$00	; (FC36) result byte#3: ST2
	DEFB	$00	; (FC37) result byte#4: C
	DEFB	$00	; (FC38) result byte#5: H
	DEFB	$00	; (FC39) result byte#6: R
	DEFB	$00	; (FC3A) result byte#7: N
	DEFB	$00
	DEFB	$00

START	LD	HL,STACK
	IM	2
	LD	A,H	; upper half of CTC interrupt table start address (FC)...
	LD	I,A	; ...is stored in register I (see Z80 docs)
	LD	A,L	; lower half of CTC interrupt table start address (18)...
	OUT	($E3),A	; ... is sent to CTC channel 0
			; MEANING: Interrupt Vector being used by all 4 channels!!
			; Interrupt Vector = 00011cc0 where cc is the CTC channel.
			; requesting the interrupt. So vector for CTC0 = 18, CTC1 = 1A, CTC2 = 1C, CTC3 = 1E
			; The upper half of addr table pointer is FC thus we have the
			; int. vectors at (FC18-FC19) for CTC0, (FC1A-FC1B) for CTC1
			;		  (FC1C-FC1D) for CTC2, (FC1E-FC1F) for CTC3
	LD	A,$FF	; data for CTC0 on next line: set CTC0 to counter mode and enable CTC0 interrupts
	OUT	($E3),A	; write FF to CTC channel 0 (Enable Interrupt, Counter Mode,
			; Rising Edge, Time Const. Follows, Reset, Control).
			; MEANING: Reset, Enable Interrupts for Channel 0, a Time Constant follows.
	LD	A,$01	; time constant for CTC0 on next line: generate INT for each byte transferred from 8272 to µP
	OUT	($E3),A	; write 01 to CTC channel 0
			; MEANING: Time Constant byte (=01, for 1 byte read).
	LD	A,$7B	; data for CTC3 on next line: disable int, Counter Mode, rising edge,
			; No Time Const. Follows, Software Reset
	OUT	($FB),A	; write 7B to CTC channel 3 (disable interrupt)
	LD	HL,CTC2 ; CTC0 interrupt vector (for CTC INT. ROUTINE #2) to be set by CTC_1+2_INIT next
	CALL	CTCINI	; call CTC_1+2_INIT
	CALL	RD	; call 8272_RD (make sure 8272 is finished with any possible previous command)
	LD	HL,SPEC	; start addr for arg bytes of a Specify command (0027 within this code)
	LD	BC,$0303 ; B=03 for 3 command bytes, C=03 for 8272 Specify opcode
	CALL	WR	; call 8272_WR (send a Specify command to 8272)
L0066	EXX		; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <---|
	LD	BC,$0204 ; B=02 for 2 command bytes, C=04 for 8272 Sense Drive Status opcode		      |
	CALL	WRHL	; call 8272_WR_HL (send a Sense Drive Status command to 8272, arg bytes from FC13)    |
	CALL	RD	; call 8272_RD (reads command result, returns ST3 in register A)		      |
	BIT	5,A	; test bit 5 of ST3 (RDY), should be 1(?) if READY				      |
	EXX		;										      |
	JR	NZ,L007A ; if crt drive# is READY -->|							      |
	INC	(HL)	; HL=FC13 points to first command arg (drive# and head#), this increments drive#      |
	RES	2,(HL)	; set head# to 0	     |							      |
	JR	L0066	; ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> --->|
L007A	CALL	TRKRD	; call TRACK_READ <--- <-----|	reads track 00 both sides
	CALL	TRKRD	; call TRACK_READ		reads track 01 both sides
; ############################ GET CPU IN SAME STATE AS LK.SYS BEFORE LEAVING ###############################
;	LD	A,$90
;	OUT	($E3),A
;	EXX
;	LD	BC,$0102 ; standard CP/M Loader leaves BC'=$0102, and looks like standard CP/M needs it
;	LD	DE,$0002 ; standard CP/M Loader seems to leave DE=$??02 ...
;	LD	HL,$FCB2 ; standard CP/M Loader leaves HL'=$FCB2, and looks like standard CP/M needs it
;	EXX
;	DI
; ###########################################################################################################
	JP	$0000	; runs the code at the beginning of sector 1 track 00 side 0 (Boot Sector)

CTC3	INI		; ######## CTC INT. ROUTINE #3 (FC8E) #######
	EI		; this routine reads bytes from FDD
	RETI
			; ######## CTC INT. ROUTINE #1 (FC93) #######
			; this is executed when CTC2 generates an interrupt, which
			; happens after one full track has been read (both sides,
			; 9 sectors each side, 18 sectors in total)
CTC1	PUSH	HL
	LD	HL,(CTC_V1) ; \ overwrite current CTC0 int. vector in (FC00-FC01)
	LD	(STACK),HL  ; / with CTC1 int. vector from (FC02-FC03)
	LD	HL,FLAG	; location FC08 used as a flag:
	LD	(HL),$FF ; (FC08)=FF right after a track has been completely read
	POP	HL
CTC2	EI		; ######## CTC INT. ROUTINE #2 (FCA0) #######
	RETI		; this int. routine does nothing
			; ######## CTC_1+2_INIT (FC82) #########
			; inputs: HL=CTC0 interrupt vector
CTCINI	DI	
	LD	(STACK),HL ; set CTC0 interrupt vector to contents of HL
	LD	A,$7F	; data for CTC1 on next line: disable int, Counter Mode, rising edge,
			; Time Const. Follows, Software Reset
	OUT	($EB),A	; write 7F to CTC channel 1
	LD	A,(TCTC1) ; time constant for CTC channel 1
	OUT	($EB),A	; write time constant to CTC channel 1
	LD	A,$FF	; data for CTC2 on next line: enable int, Counter Mode, rising edge,
			; Time Const. Follows, Software Reset
	OUT	($F3),A	; write 7F to CTC channel 2
	LD	A,(TCTC2) ; time constant for CTC channel 2
	OUT	($F3),A	; write time constant to CTC channel 2
	RET
			; ######## 8272_POLL (FC99) #########
POLL	IN	A,($F5)	; read 8272 Status Register			| this routine polls the 8272 Status Register
	BIT	7,A	; test bit 7 (RQM)				| until 8272 is ready to send or receive
	JR	Z,POLL ; if bit 7 = 0 read 8272 Status Register again	| data to or from the CPU. When ready (RQM=1)
	BIT	6,A	; test bit 6					| bit 6 is tested (DIO, 0=receive/1=send)
	RET
			; ######## 8272_WR_HL (FCA2) #########
WRHL	LD	HL,ARGS ; routine below with arg bytes taken from FC13
			; ######## 8272_WR (FCA5) #########
			; send a command to 8272
			; inputs: HL=start addr of bytes to be sent (command args)
			;	  C=first byte to be sent (command opcode)
			;	  B=nr of command bytes (opcode+args)
WR	IN	A,($F5)	; <--- <--- <--- <--- <-----| read 8272 Status Register
	BIT	4,A	; test bit 4 (FDC Busy)	    |
	JR	NZ,WR	; ---> ---> ---> ---> ----->| repeat if FDC busy
	JR	L00C5	; ---> ---> --->|
L00C3	LD	C,(HL)	; <--- <--- <---- <--- <--- <---|
	INC	HL	;		|		|
L00C5	DI		; <--- <--- <---|		|
	CALL	POLL	; call 8272_POLL		|
	CALL	NZ,CPM2CO ; call CPM_TO_COBRA if 8272 to send data
	LD	E,C	;				|
	LD	C,$FD	;				|
	OUT	(C),E	; send byte to 8272 Data Reg	|
	DJNZ	L00C3	; ---> ---> ---> ---> ---> ---->|
	EI
	RET
			; ######## 8272_SIS (FCBF) ########
SIS	LD	BC,$0108 ; B=01 for 1 command bytes, C=08 opcode for Sense Interrupt Status
	CALL	WR	; call 8272_WR (send Sense Interrupt Status command to 8272)
			; ######## 8272_RD (FCC5) #########
			; reads last command result bytes from 8272
			; outputs: A=first result byte
			;	   (FC1C-FC22)=the max. 7 result bytes in order
RD	LD	HL,RSLT	; start address to store 8272 command result bytes
	LD	B,$08	; counter (8 bytes to read from 8272)
L00E0	CALL	POLL	; call 8272_POLL <--- <--- <----|
	JR	Z,L00F8	; ---> ---> --->|if all result bytes are read, exit (normal exit)
	LD	(HL),$FF ;		|		|
	IN	A,($FD)	; read 8272 Data| Register	|
	LD	(HL),A	; store result	|		|
	INC	HL	; increment storage pointer	|
	DJNZ	L00E0	; ---> ---> --------> ---> ---->|
CPM2CO	DI		; ######## CPM_TO_COBRA (FCD7) ######## if 8 result bytes read and still reading, exit (error)
	XOR	A	;		| A=00
	LD	L,A	;		|
	LD	H,A	;		| HL=0000 (jump address)
	OR	$C1	;		| A=C1
	OUT	($FE),A	;		| set SO=1, O6=1, border=blue
	LD	R,A	;		| set bit 7 of R to 1 (change to startup config)
	JP	(HL)	;		| jump to 0000 in startup config
L00F8	LD	HL,RSLT	; <--- <--- <---|
	LD	A,(HL)	; return with first result byte in A
	RET
			; ######## TRACK_READ (FCE7) #########
			; reads one track both sides (9 sectors/side, 18 sectors in total)
TRKRD	LD	BC,$030F ; B=03 for 3 command bytes, C=0F opcode for Seek command
	CALL	WRHL	; call 8272_WR_HL (Seek command with args from FC13)
L0103	CALL	SIS	; call 8272_SIS (returns first result byte (ST0) in A) <--- <--- <--- <--|
	BIT	5,A	; test bit 5 of first result byte ST0 (Seek End bit, if 1 Seek ended ok) |
	JR	Z,L0103	; if Seek not ended, repeat SIS   ---> ---> ---> ---> ---> ---> ---> --->|
	AND	$50	; test bits 6 and 4 (Abnorm. Termination/Equipment Check/Track00 not reached)
	CALL	NZ,CPM2CO ; if any of above errors, CPM_TO_COBRA
L010F	CALL	SIS	; call 8272_SIS (returns first result byte (ST0) in A)
	CP	$80	; check if invalid command (SIS after SIS should give ST0=80, invalid command)
	JR	NZ,L010F ; if ST0 not 80, go back 2 lines (repeat SIS)
L0116	IN	A,($F5)	; read 8272 Status Register <---|
	AND	$0F	; test bits 0-3 (FDD 0-3 Busy)	|
	JR	NZ,L0116 ; if any drive busy -> ---> -->|
	LD	B,$03	; counter for max. 3 loops to be executed next
L011E	EXX		; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <----|
	LD	BC,$024A ;B=02 for 2 command bytes, C=4A opcode for Read ID command in MFM mode	|
	CALL	WRHL	; call 8272_WR_HL (send Read ID command with arg from (FC13))		|
	CALL	RD	; call 8272_RD (returns ST0 in A)					|
	AND	$C0	; test bits 6, 7 (normal termination if both =0)			|
	EXX		;									|
	JR	Z,L0132	; --->| if normal termination skip next 2 lines				|
	DJNZ	L011E	; ------> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ---> ->|
	CALL	CPM2CO	;     | CPM_TO_COBRA
L0132	LD	B,$0A	; <---| B as counter for max 10 loops next
L0134	PUSH	BC	; <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <--- <-|
	XOR	A	; location FC08 used as a flag:								 |
	LD	(FLAG),A ; =00 right before and during track reading						 |
	LD	HL,CTC3 ; CTC0 interrupt vector to be used next (for CTC INT. ROUTINE #3 - saves FDD bytes)	 |
	CALL	CTCINI	; call CTC_1+2_INIT									 |
	LD	BC,$09C6 ; B=09 for 9 command bytes, C=C6 opcode for Read Data (MT, MFM) on next line		 |
	CALL	WRHL	; call 8272_WR_HL (send a Read Data (MT, MFM) to 8272) which leaves C=FD		 |
	LD	HL,(T1STOR) ; HL=storage start addr for data read from disk (0000 to begin, 2400 after 1 track)	 |
L0148	HALT		; wait for first interrupt from CTC <--- <--- <-| ## one interrupt per byte read,	 |
	IN	A,($F5)	; read 8272 Main Status Register		| ## bytes read by INI from (C)=(FD)	 |
	AND	$20	; test bit 5 (Non-DMA, 1 during exec in Non-DMA,| 0 when exec finished)			 |
	JR	NZ,L0148 ; if exec not finished yet ---> ---> ---> ---->|					 |
	LD	(STOR),HL ; save current storage address for data read from disk				 |
	LD	HL,CTC2 ; CTC0 interrupt vector to be used next (for CTC INT. ROUTINE #2 - does nothing)	 |
	CALL	CTCINI	; call CTC_1+2_INIT									 |
	CALL	RD	; call 8272_RD (after this HL=FC1C)							 |
	LD	A,(FLAG) ; location FC08 used as a flag for track reading					 |
	OR	A	;											 |
	POP	BC	;											 |
	JR	Z,L0167	; if track reading in progress skip 3 lines and restart loop --> ---> ---> ---> ---> --->|
	LD	A,(HL)	; (HL)=(FC1C)=first result byte (ST0) of the 8272 Read Data command			 |
	AND	$D8	; test bits 7 6 4 3 (Interrupt Code, Equipment Check, Not Ready)			 |
	JR	Z,L016C	; ---> ---> ---> ---> ---> ---> ---> ---> --->| if none of above errors			 |
L0167	DJNZ	L0134	;   ----> ---> ---> ---> ---> ---> ---> --->---> ---> ---> ---> ---> ---> ---> ---> ---->|
	CALL	CPM2CO	; call CPM_TO_COBRA			      |
L016C	LD	HL,ARG1	; storage address for current track no. <-----|
	INC	(HL)	; increment current track number
	LD	HL,(T2STOR) ; copy the value of storage addr for second system track...
	LD	(T1STOR),HL ; ...over the value of current storage addr
	RET

LOGO	LD	HL,LOGODT
	LD	DE,$5A00 ; video mem attrs address in CP/M
	LD	B,$20
LOOP0	PUSH	BC	; BC=$20xx <-- <--- <--- <--- <--- <--- <--- <--|
	LD	C,(HL)	; C=($003B)=$00					|
	LD	B,$08	; 8 loops next					|
LOOP1	LD	A,$00	; <--- <--- <--- <--- <--- <--- <--- <--|	|
	RLC	C	; Carry Flag is 0			|	|
	JR	NC,LOOP2 ; jump if bit=0 --->|			|	|
	LD	A,$1B	; color attr	     |			|	| <<< color attr for INK=magenta, PAPER=magenta
LOOP2	LD	(DE),A	; <--- <--- <--- <---|	($5A00)=$00	|	|
	INC	DE	; DE=$5A01				|	|
	DJNZ	LOOP1	; ---> ---> ---> ---> ---> ---> ---> -->|	|
	POP	BC	; BC=$20xx					|
	INC	HL	; HL=$003C					|
	DJNZ	LOOP0	; ---> ---> ---> ---> ---> ---> ---> ---> ----->| 32 loops ($20)
	LD	A,$01
	OUT	($FE),A	; write 01 to port C of 8255
			; (set border to blue, set signal "SO" to 0, set signal "O5" to 0,
			;  set signal "O6" to 0 to allow access to DRAM#1 in the CP/M hw config)
	JP	START

			; ############## LOGO DATA START ####################
			; this block is used to generate the logo while loading CP/M
LOGODT	DEFB	$00	; 5A00: 00 00 00 00 00 00 00 00		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
	DEFB	$00	; 5A08: 00 00 00 00 00 00 00 00
	DEFB	$00	; 5A10: 00 00 00 00 00 00 00 00
	DEFB	$00	; 5A18: 00 00 00 00 00 00 00 00
	DEFB	$33	; 5A20: 00 00 12 12 00 00 12 12		00 00 12 12 00 00 12 12 12 00 00 00 12 00 12 00 00 12 00 00 12 12 12 00 00 00 12 00 00 00 00 00
	DEFB	$8A	; 5A28: 12 00 00 00 12 00 12 00
	DEFB	$4E	; 5A30: 00 12 00 00 12 12 12 00
	DEFB	$20	; 5A38: 00 00 12 00 00 00 00 00
	DEFB	$4A	; 5A40: 00 12 00 00 12 00 12 00		00 12 00 00 12 00 12 00 00 12 00 00 12 00 12 12 12 12 00 12 00 00 00 00 00 12 12 00 00 00 00 00
	DEFB	$4B	; 5A48: 00 12 00 00 12 00 12 12
	DEFB	$D0	; 5A50: 12 12 00 12 00 00 00 00
	DEFB	$60	; 5A58: 00 12 12 00 00 00 00 00
	DEFB	$42	; 5A60: 00 12 00 00 00 00 12 00		00 12 00 00 00 00 12 00 00 12 00 12 00 00 12 12 00 12 00 12 12 12 00 00 12 00 12 00 00 12 12 00
	DEFB	$53	; 5A68: 00 12 00 12 00 00 12 12
	DEFB	$5C	; 5A70: 00 12 00 12 12 12 00 00
	DEFB	$A6	; 5A78: 12 00 12 00 00 12 12 00
	DEFB	$43	; 5A80: 00 12 00 00 00 00 12 12		00 12 00 00 00 00 12 12 12 00 00 12 00 00 12 00 00 12 00 12 00 00 12 00 12 12 12 00 12 00 00 00
	DEFB	$92	; 5A88: 12 00 00 12 00 00 12 00
	DEFB	$52	; 5A90: 00 12 00 12 00 00 12 00
	DEFB	$E8	; 5A98: 12 12 12 00 12 00 00 00
	DEFB	$4A	; 5AA0: 00 12 00 00 12 00 12 00		00 12 00 00 12 00 12 00 00 00 12 00 00 00 12 00 00 12 00 12 00 00 12 00 00 00 12 00 12 00 00 00
	DEFB	$22	; 5AA8: 00 00 12 00 00 00 12 00
	DEFB	$52	; 5AB0: 00 12 00 12 00 00 12 00
	DEFB	$28	; 5AB8: 00 00 12 00 12 00 00 00
	DEFB	$32	; 5AC0: 00 00 12 12 00 00 12 00		00 00 12 12 00 00 12 00 00 00 12 00 00 00 12 00 00 12 00 00 12 12 00 00 00 00 12 00 00 12 12 00
	DEFB	$22	; 5AC8: 00 00 12 00 00 00 12 00
	DEFB	$4C	; 5AD0: 00 12 00 00 12 12 00 00
	DEFB	$26	; 5AD8: 00 00 12 00 00 12 12 00
	DEFB	$00	; 5AE0: 00 00 00 00 00 00 00 00		00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
	DEFB	$00	; 5AE8: 00 00 00 00 00 00 00 00
	DEFB	$00	; 5AF0: 00 00 00 00 00 00 00 00
	DEFB	$00	; 5AF8: 00 00 00 00 00 00 00 00
			; ############## LOGO DATA END ######################