
.Z80

;-----------------------------
;     Murphy 1987
;-----------------------------

;********************************************************************
;*    2 * 80 Track am CPC 6128 unter CP/M+                          *
;*   Austausch der DPBs von A: und C: in Loginroutine               *
;********************************************************************

PATCH	 Macro	  Nr

;;ein Speicherbereich ab DE aus der TPA in die
;;Systembank an die Speicherstelle (Ziel) kopiert. Die
;;Anzahl der Bytes wird durch Laenge bestimmt.
;;DE weist auf das erste Byte danach.

	 ld	  bc,ende&Nr - ptch&Nr	;;Laenge des Patch
	 ld	  hl,code&Nr		;;Zieladresse
	 call	  Patchexecution
         endm

; 4 Flagvariablen koennen den erzeugten Code veraendern:
;--------------------------------------------------------

LOGIN	 equ	  1	;0 : Festlegen des Formats
			;1 : autom. Erkennung von 4 Formaten

Drive_C	 equ	  0	;1 : Doppelfloppy

F800k	 equ	  0	;0 : 512 Byte Sektoren
			;1 : 1k Sektoren

Step_B	 equ	  4	;Steprate fuer Drive B: und C:
Step_C	 equ	  4	;in ms (geradzahlig von 2 - 30 !)

; --- durch Streichen von Sektorenpuffern
;     Arbeitsplatz in der Systembank schaffen

	 ld	  de,ptchS
	 PATCH	  S	;neue Anzahl der Sektoren
			;(nicht bei Verwendung des
			;Kassettenpuffers)
   if F800k
	 PATCH	  T	;evtl. neue Groesse
   endif

	 call	  0FC5Ah;Call SYS 019Ah
	 defw	  19Ah	;Sektorpuffer initialisieren

	 ld	  (DIRBCB_ADR),de
	 ld	  (DATBCB_ADR),hl

	 ld	  de,ptchU

	 PATCH    U

	 ld	  de,ptch1

	 PATCH	  1	;2. Kopf beim Auslesen des 
			;FDC nicht als Fehler werten
	 PATCH	  2	;individuelle Steprate
	 PATCH	  3	;Arbeitsroutinen fuer 1 & 2
			;im Sektorpuffer
	 PATCH	  4	;fiktives Drive I: & J:
			;in Drivetabelle einbinden
	 PATCH	  5	;Flag fuer 2 Drives
	 PATCH	  6	;logisches B: = physik. B:
	 PATCH	  7	;entscheidet, auf welchem
			;Drive weitere simuliert
			;werden koennen

   if Drive_C
	 PATCH	  8	;DPB - Austausch von A: & C:
   endif			;ins BIOS einbinden

   if LOGIN
	 PATCH 	  A	;autom. Formaterkennung
   else
	 PATCH	  B	;oder DPB fuer B: festlegen
   endif

   if Drive_C		;zwei externe Laufwerke

	 PATCH	  C	;DPH / CKS / Alloc / Hashtab
	 PATCH	  D	; DPH von C: in Drivetabelle
   endif

	 ld	  de,message
	 ld	  c,9	;BDOS "Stringausgabe"
	 jp	  5	;Ausgabe & Ende


Patchexecution:
	 push	  bc
	 ld	  bc,0001	;Ziel    : Bank 0
	 call	  0FC57h	;Quelle  : Bank 1
	 pop	  bc
	 call	  0FC4Bh	;Block kopieren
	 ret


   if F800k
;--------------------------------------------------------------------

ptchS:
;***    1k - Sektorpuffer !!!???

codeS	 equ	  19Ah
	 ld	  bc,400h	;4 Sektoren Dir / Bank 0
	 ld	  de,8C76h	;Adresse des 1. Puffers
	 ld	  hl,8A00h	;1. Header
	 push	  hl
	 call	  01D9h		;Initialisieren
	 dec	  hl
	 ld	  (hl),b	;letzter Eintrag
	 dec	  hl		;kein Nachfolger
	 ld	  (hl),b
	 inc	  hl
	 inc	  hl
	 push	  hl

Space	 equ	  1		;1k Bloecke (max. 4 !!!)
	 ld	  b,5-Space	;max. 5 Datenpuffer
	 call	  01D9h
	 ld	  bc,0B02h	;11k - Puffer in Bank 2
	 ld	  de,05080h
	 call	  01D9h
	 dec	  hl
	 ld	  (hl),b
	 dec	  hl
	 ld	  (hl),b

Free_Ad	 equ	  09C67h + 1024 * (5-Space)
;  1. freie Adresse im neuen Speicherbereich
;  die letzte freie Adresse ist immer B066h

endeS:


ptchT:
codeT	 equ	  01F5h

  .phase codeT

	 inc	  d		;-> neue Sektorlaenge
	 inc	  d		;   auf 1k setzen
	 djnz	  01DCh
	 ret

  .dephase

endeT:

   else
;--------------------------------------------------------------------



ptchS:
codeS	 equ	  1AFh		;Platz in Bank 0 schaffen
Space	 equ	  2		;512k - Bloecke (max. 9 !!!)
	 defb	  10 - Space

Free_Ad	 equ	  09C67h + 512 * (10-Space)
;  1. freie Adresse im neuen Speicherbereich
;  die letzte freie Adresse ist immer B066h

endeS:

   endif
;--------------------------------------------------------------------


ptchU:
codeU	 equ	  0BEFBh
DIRBCB_ADR:
         defs  2
DATBCB_ADR:
         defs  2
endeU: 

ptch1:
code1	 equ	  0D5Ah		;Patch in der Resultphasen-
				;auswertung
	 call	  TwoInOne

ende1:


ptch2:
code2	 equ	  0C07h		;Patch in der Seek Track
				;Routine
	 jp	  Steprate

ende2:


ptch3:
code3	 equ	  Free_Ad	;Arbeitsspeicher im freien
				;Sektorpuffer
  .phase code3


Steprate:
	 add	  iy,bc		;Funktion vor dem Patch muss
				;erhalten bleiben
	 push	  hl
	 srl	  c		;C / 2 : Drivenr. in C
	 ld	  hl,Steptab	;B ist 0
	 add	  hl,bc		;zugeh. Eintrag finden
	 ld	  a,(hl)
	 ld	  hl,0B0F1h	;ist die aktive Steprate
	 cp	  (hl)		;gleich der gewuenschten ?
	 jr	  z,stimmt
	 ld	  (hl),a	;neue Steprate uebernehmen
	 ld	  hl,0AD5h	;HL auf Headload; -unload
	 call	  0AF2h		;Drive Parameter an FDC

stimmt:
	 pop	  hl
	 pop	  bc		;wurde vor Einsprung gepusht
	 ret

Steptab:			;geradzahlige Werte (2-30)!
	 defb	  12		;12 ms fuer eingeb. Drive A:
	 defb	  Step_B
	 defb	  0		;Dummy !
	 defb	  Step_C

	 ;das logische Drive C:
	 ;ist physikalisch als Drive D: anzusprechen !

TwoInOne:
	 ld	  a,c		;ausblenden der Headadresse
	 res	  2,a		;um Kopf 2 im Result des FDC
	 or	  020h		;wie Kopf 1 zu behandeln
	 ret

        if LOGIN
;--------------------------------------------------------------------


DPBTab:	;		A C H T U N G !
	; kein Highbyte-Wechsel innerhalb der Tabelle!

	 defw	  02Eh		;DE - Register f. Format_00
	 defw	  Form01	;DPB   f. Format_00

	 defw	  02Ch		;Format_40
	 defw	  0C0Bh

	 defw	  02Eh		;Format_80
	 defw	  Form81

	 defw	  02Eh		;Format_C0
	 defw	  0C25h



Bereichchk:
	 ;es sind nur 4 Formate moeglich
       	 ;Ein Overflow duerfte zwar nicht auftreten,
	 ;aber man kann ja nie wissen.

	 cp	  low DPBTab	;A war kleiner als 0
	 jr	  c,error
	 cp	  low(Bereichchk-1);A war groesser als 3
	 ccf

error:				;C = Fehler
	 jp	  03FE0h	;ld   hl,(hl)


;DPB fuer Format_00    		;800k - Format !

Form01:
	 defw	  36		;Records / Track
	 defb	  5		;Blockshift
	 defb	  31		;Blockmaske
	 defb	  3		;Extent Maske
	 defw	  176		;Bloecke - 1
	 defw	  127		;Directoryeintraege - 1
	 defw	  080h		;ein Block fuer Directory
	 defw	  32		;zu pruefende Eintraege / 4
	 defw	  2		;Systemsp. * Heads
	 defb	  2		;Sektorlaenge
	 defb	  3		;Sektormaske
	 defb	  1		;2 Koepfe
	 defb	  80		;Tracks
	 defb	  9		;Sektoren / Track
	 defb	  01h		;1. Sektor
	 defw	  200h		;???
	 defb	  2Ah		;Gap3 lesen / schreiben
	 defb	  52h		;Gap3 formatieren
	 defb	  60h		;Maske fuer MFM


;DPB fuer Format_81

Form81:
         defw	  36		;Records / Track
	 defb	  5		;Blockshift
	 defb	  31		;Blockmaske
	 defb	  3		;Extent Maske
	 defw	  179		;Bloecke - 1
	 defw	  127		;Directoryeintraege - 1
	 defw	  080h		;ein Block fuer Directory
	 defw	  32		;zu pruefende Eintraege / 4
	 defw	  0		;Systemsp. * Heads
	 defb	  2		;Sektorlaenge
	 defb	  3		;Sektormaske
	 defb	  1		;2 Koepfe
	 defb	  80		;Tracks
	 defb	  9		;Sektoren / Track
	 defb	  081h          ;1. Sektor
	 defw	  200h		;???
	 defb	  2Ah		;Gap3 lesen / schreiben
	 defb	  52h		;Gap3 formatieren
	 defb	  60h		;Maske fuer MFM

        endif
;--------------------------------------------------------------------

last_3:

  .dephase

ende3:


ptch4:
code4	 equ	  0FE2Fh + 2 * ("I"-"A")

;Drive I bzw. J sind wie der Name schon sagt eine
;imaginaere Einheit ! 
;  (finde ich immer wieder toll, diese Eselsbruecke)

	 defw	  3F94h		;I : DPH - Adr. fuer Drive A:
	 defw	  3FB7h		;J : DPH - Adr. fuer Drive B:
ende4:


ptch5:
code5	 equ	  0BE40h
	 defb	  255		;Flag fuer 2 Drives

ende5:


ptch6:

code6	 equ	  03FB5h

;wenn beim Booten keine Diskette in B: war, steht
;hier sonst die Physikalische Adresse von Drive A:

	 defb	  1		;Drive B:
ende6:


ptch7:

code7	 equ	  03F55h

; nur ein Byte (!) entscheidet ueber die
; Simulationsmoeglichkeit von Drives

	 dec	  a	;Physik. Drive B: ist auch durch J:
			;anzusprechen, es erscheint beim
			;Wechsel die bekannte Meldung und
			;es wird eine Taste erwartet.

;****    or       a     ;Drive A: = I:

;****	 xor	  a	;beide Drives mit dem Nachteil,
			;jedesmal eine Taste druecken zu
			;muessen, auch wenn nur zwischen A:
			;und B: gewechselt wird.

ende7:


        if Drive_C
;--------------------------------------------------------------------

ptch8:
code8	 equ	  3E57h
	 call	  Sel_Login	;DPB A: und C: tauschen

ende8:

;--------------------------------------------------------------------
        endif


   if LOGIN
;--------------------------------------------------------------------

ptchA:
codeA	 equ	  0C4Eh

;  Erweiterung der Login - Routine im BIOS um zwei
;  weiter Formate; dabei werden diese beiden zusaetz-
;  lichen Formate abgeblockt, wenn Drive A: gemeint
;  ist  (kann man auch weglassen)

  .phase codeA			;Login bei Drive select

	 rlca			;Bit 6,7 --> Bit 1,0
	 rlca	
	 jr	  c,cpcform	;Format auch fuer A: erlaubt
	 inc	  c		;Drive A: setzt Z-Flag
	 dec	  c		;Fehler
	 jr	  z,0C7Fh	;A: mit 80 Track Format

cpcform:
	 nop			;alten Code loeschen
	 nop
	 nop
	 nop
	 add	  a,a		;Select Format (A)
	 add	  a,a		;4*A -> A
	 add	  a,Low DPBTab	;Lowbyte der Parametertab.
	 ld	  l,a		;daher kein Highbyte -
	 ld	  h,High DPBTab	;Wechsel moeglich !!!
	 push	  hl
	 call	  03FE0h	;ld  HL,(HL)
	 ex	  (sp),hl	;1. Wort auf Stack
	 inc	  hl
	 inc	  hl
	 call	  Bereichchk	;2. Wort in HL
	 pop	  de		;Formatnr. ausserhalb von 
	 jr	  c,0C7Fh	;0 bis 3: Error

  .dephase

endeA:

        else
;--------------------------------------------------------------------

ptchB:
codeB	 equ	  0FF7Fh

;DPB fuer Format_81 auf B: - nach eigenen Wuenschen 
;               anpassen
         
         defw	  36		;Records / Track
	 defb	  5		;Blockshift
	 defb	  31		;Blockmaske
	 defb	  3		;Extent Maske
	 defw	  179		;Bloecke - 1
	 defw	  127		;Directoryeintraege - 1
	 defw	  080h		;ein Block fuer Directory
	 defw	  32		;zu pruefende Eintraege / 4
	 defw	  0		;Systemsp. * Heads
	 defb	  2		;Sektorlaenge
	 defb	  3		;Sektormaske
	 defb	  1		;2 Koepfe
	 defb	  80		;Tracks
	 defb	  9		;Sektoren / Track
	 defb	  081h          ;1. Sektor
	 defw	  200h		;???
	 defb	  2Ah		;Gap3 lesen / schreiben
	 defb	  52h		;Gap3 formatieren
	 defb	  60h		;Maske fuer MFM
      
	 defb	  0FFh		;kein Login moeglich

endeB:

;--------------------------------------------------------------------
        endif


   if Drive_C
;--------------------------------------------------------------------

ptchC:
codeC	 equ	  last_3

  .phase codeC

;* der folgende Code ist nur dann interessant, wenn
;* Sie als Drive C: ein drittes Laufwerk anschliessen
;* wollen.

	 defw	  03F1Ch	;Write Sektor
	 defw	  03F17h	;Read Sektor
	 defw	  03ED6h	;Select Drive C:
	 defw	  03ECBh	;Init (-> nur Return)
	 defb	  3		;Physikalisches Drive D:
	 defb	  0		;Userbyte; nicht benutzt

DPH_C
	 defw	  0		;Skewing not used
	 defb	  0,0,0		;Scratch - Bereich
	 defb	  0,0,0
	 defb	  0,0,0

Mediaflag:
	 defb	  0
	 defw	  0FF64h	;Diskparameter Block von A:
				;mitverwenden, das ist auch
				;der Grund dafuer, dass es ohne
				;Login unsinnig wird:
				;Es waere fuer A: und C: nur
				;dasselbe Format verwendbar
	 defw	  C_CHKSUM	;Pruefsummen
	 defw	  C_Alloc	;Blockbelegungstabelle
	 defw	  0BEFBh	;Directory - Contr. Block
	 defw	  0BEFDh	;Datenpuffer Contr. Block
	 defw	  Hash_C	;Hashtab von A: verwendet
	 defb	  0		;Bank der Tabelle


C_CHKSUM:
	 defs	  33		;reicht fuer 128 Dir-Eintraege

C_Alloc:
	 defs	  66		;max. 256 Bloecke

Sel_Login:
	 ld	  (0BEF0h),a	;alte Funktion !
	 ld	  hl,aktiv_Drive;welcher DPB ab FF64 ?
	 cp	  (hl)
	 ret	  z		;schon richtig
	 cp	  0		;nur bei A:
	 jr	  z,Login1
	 cp	  2		;bzw. C: in Aktion treten
	 ret	  nz

Login1:
	 push	  af
	 push	  bc
	 push	  de
	 ld	  (hl),a	;neues aktives Drive
	 ld	  hl,0FF64h	;DPB_A
	 ld	  de,DPB_C
	 ld	  b,1Bh		;Anzahl incl. Loginflag

login2:
	 ld	  a,(de)	;tauschen der DPBs fuer
	 ld	  c,(hl)	;A: und C:
	 ex	  de,hl
	 ld	  (hl),c
	 ld	  (de),a
	 ex	  de,hl
	 inc	  de
	 inc	  hl
	 djnz	  login2
	 pop	  de
	 pop 	  bc
	 pop	  af
	 ret

aktiv_Drive:
	 defb	  0		;Default erstmal A:

DPB_C:
	 defw	  36		;Records / Track
	 defb	  5		;Blockshift	
	 defb	  31		;Blockmaske
	 defb	  3		;Extent Maske
	 defw	  179		;Bloecke - 1
	 defw	  127		;Directoryeintraege -1
	 defw	  080h		;1 Block fuer Directory
	 defw	  32		;zu pruefende Eintraege / 4
	 defw	  0		;Systemsp. * Heads
	 defb	  2		;Sektorlaenge		
	 defb	  3		;Sektormaske
	 
	 defb	  1		;2 Koepfe
	 defb	  80		;Tracks
	 defb	  9		;Sektoten / Track
	 defb	  081h		;1. Sektor
	 defw	  200h		;???
	 defb	  2Ah		;Gap3 lesen / schreiben
	 defb	  52h		;Gap3 formatieren
	 defb	  60h		;Maske fuer MFM


   if LOGIN
	 defb	  0		;Loginflag von A: und C: wird mit
   else				;ausgetauscht, so dass beide
	 defb	  0FFh		;unabhaengig voneinander sind
   endif

Hash_C:
	 defs	  512		;Hash - Tabelle fuer C:

Hash_Ende:

  .dephase

endeC:


ptchD:
codeD	 equ	  0FE2Fh + 2 * ("C" - "A")

	 ;C: in Drivetable eintragen
	 defw	  DPH_C

endeD:

;--------------------------------------------------------------------
        endif


message:
	 defb	  0Dh,0Ah,0Ah
	 defb	  "P A T C H"
	 defb	  0Dh,0Ah,0Ah

   if LOGIN
	 defb	  "Login von 4 Formaten auf Drive B:"

        if Drive_C
	 defb	  " und C:"
        endif

   else
	 defb	  "auf Drive B: "

        if Drive_C
	 defb	  " und C:"
        endif

	 defb	  "kein Login mehr moeglich"
   endif

	 defb	  0Dh,0Ah,0Ah
	 defb	  "$"		;Stringende

end

	
        endif

	 