;##################################################################
;
REV	EQU	109		;Versionsnummer
NEIN	EQU	0
JA	EQU	.NOT.NEIN
;
; wenn Sektor 1 mit assembliert werden soll muss nachfolgendes
; Bedingungsflag auf NEIN gesetzt werden.
;
CREATE	EQU	JA		;Bedingtes assemblieren IBOOT
;
	TITLE	'ZBIOS 2.0 (C) ELEKTRONIKLADEN 82'
;
;
	NLIST
;
; COPYRIGHT VERMERK
;
;
; Dieses CBIOS ist spezifisch auf den ELZET 80 FDC-
; Kontroller angepasst. Es unterstuezt NUR 8" single density
; softsektorierte Diskettensysteme
; Wir haben diesem angepassten CBIOS den Namen ZBIOS
; gegeben.
; ZBIOS ist NUR auf einem Z80 System lauffaehig
; ZBIOS ist durch Copyright (C) 1980,1981,1982
; fuer ELEKTRONIKLADEN , Detmold geschuetzt.
; Jegliche Weitergabe, gleichgueltig in welcher Form
; und auf welchem Datentraeger, auch in abgeaenderter Form
; oder auszugsweise ist nicht gestattet.
; ZBIOS ist nur fuer den von uns lizensierten CP/M Benutzer
; zur Anpassung anderer als vorbestimmter Baugruppen
; bestimmt.
; ZBHOS darf auf kdine BACKUP Diskette die fuer Andere
; zugaenglich ist uebertragen werden.
;
; ELEKTRONIKLADEN haftet in keinem wie auch immer gearteten
; Falle fuer Folgeschaeden aus diesem oder einem abge-
; aenderten Programm
;
; Wir haben uns bemudht das Programm so kurz und schnell wie
; moeglich zu machen. Darunter musste zweifellos 
; die Uebersichtlichkeit leiden. Wir haben versucht dies
; durch entsprechende Kommentierung wettzumachen.
;
; Fuer Anregungen und Fehlermeldungen sind wir Dankbar
;
;
;			ELEKTRONIKLADEN
;
;
;
; programmiert von	R.Koerber
;			AUGUST 1981
;
;
;
;***********************************************************
;
;
;
; Aenderungen:
;
; DATUM	AENDERUNG
;
; 12.09.81	Vorbereitet auf VID5 fuer ZAP
;		Druckertreiber kommt mit Zeichen in
;		AKKU zurueck
;		Variable beginnen bei Zbios+380H
;
; 06.04.82	Video80 Treiber eingebaut mit Selbstcheck
;
; 08.05.82	check nach CPU/IEC mit eingebaut
;
;
;
;
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; VARIABLE KONSTANTE PORTADRESSEN und was sonst noch so
; 				  zum Assemblieren gehoert
;
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Equates Tafel
;
;
;----------------------------------------------------------
;
; ASC	I Kontrollzeichen und Cursorsteuerung
;
;----------------------------------------------------------
;
;
CR	EQU	0DH		;Wagenruecklauf
LF	EQU	0AH		;Wagenvorschub
FF	EQU	0CH		;Formfeed (CLRS)
HOME	EQU	1CH		;Cursor oben links
				;ohne loeschen
SUB	EQU	1AH		;FF fuer TVI
ESC	EQU	1BH		;Escape Character
HT	EQU	09H		;Horizontal TAB
VT	EQU	0BH		;Vertikal TAB (up)
BS	EQU	08H		;Backspace
BLANK	EQU	20H		;Leerzeichen
BELL	EQU	07H		; Klingel...
;
;
;---------------------------------------------------------
;
;
; PIO-Modes
;
;
;-----------------------------------------------------------
;
;
OUTPUT	EQU	00001111B	;Ausgabemodur
INPUT	EQU	01001111B	;Eingabe
BITMOD	EQU	11001111B	;BITmode
ENINT	EQU	10000111B	;mit Interupts
NOINT	EQU	00000111B	;ohn% Interrupts
;
;-----------------------------------------------------------
;
; VIDEO 80
;
;-----------------------------------------------------------
;
;
VIDRAM	EQU	0E800H			;start Videoram
VIDREG	EQU	2AH			;Kontrollreg video
VIDFIL	EQU	VIDREG+1		;File Register VID80
;
MAXLIN	EQU	25			;Maximale Zeilenanzahl
MAXCOL	EQU	80			;Zeichen pro Zeile
SCREEN	EQU	2048
LSTLIN	EQU	VIDRAM+(MAXLIN-1)*MAXCOL
LSTPOS	EQU	VIDRAM+MAXLIN*MAXCOL-1
;
;
;-----------------------------------------------------------
;
; CPU UART
;
;-----------------------------------------------------------
;
;
UARTC	EQU	06H		;Kontrollport UART
UARTD	EQU	04H		;Datenport
;
;
;-----------------------------------------------------------
;
; CPU/IEC PIO und SIO
;
;-----------------------------------------------------------
;
CPAD	EQU	01H		;PIO A Daten
CPAC	EQU	03H		;PIO A CNTRL
CSBD	EQU	05H		;SIO B Daten
CSBC	EQU	07H		;SIO B CNTRL
;
;
;-----------------------------------------------------------
;
; PIO PRINT KRTE
; Die PRINT Karte wird normalerweise durch das Bootprogramm
; initialisiert. Sicherheitshalber hier die Daten
: Bitte Beachtdn: SSB benoetigt keine Initialisierung!
;
;-----------------------------------------------------------
;
;
;
;
PBAE	EQU	24H		;Portbasisadresse
CONT	EQU	PBASE		;KONTROLL PORT
DATA	EQU	PBASE+1		;DATEN PORT
INITA	EQU	PBASE+2		;INITPORT A
INITB	EQT	PBASE+3		;INITPORT B
;
BUSY	EQU	4		;Busy BIT
;
;
;-----------------------------------------------------------
;
; FC CONTROLLER KARTE
;
;-----------------------------------------------------------
;
;
; FD 179X
;
;
FBAS	EQU	40H		;FDC Basisadresse
FCCONT	EQU	FBAS		;Kontrollwort-Reg.
FCSTAT	EQU	FBAS		;Status Register
FCTRCK	EQU	FBAS+1		;Spur Register
FCSEC	EQU	FBAS+2		;Sektor Register
FCDATA	EQU	FBAS+3		;Daten Register
;
; nachfolgende Parameter sind in den Unterlagen zum
; FD 179X beschrieben bitte dort nachlesen!
;
;
RESTOR	EQU	00001101B	;Restore
SEEK	EQU	00011101B	;Spur suchen
READS	EQU	10000000B	;Sektor lesen
WRITES	EQU	10100000B	;Sektor schreiben
FORCEI	EQU	11010000B	;Interupt ausloesen
				; wenn fertig...
;
;
; FDC PIO
;
;
PIOAD	EQU	FBAS+4		;PIO A DATEN
PIOBD	EQU	FBAS+5		;PIO B DATEN
PIOAC	EQU	FBAS+6		;PIO A CNTRL
PIOBC	EQU	FBAS+7		;PIO B CNTRL
DRIVEA	EQU	10001110B	;Laufwerk'nummer'
DRIVEB	EQU	10001101B	;wird von SELDSK
DRIVEC	EQU	10001011B	;errechnet
DRIVED	EQU	10000111B
;
;
;-----------------------------------------------------------
;
; CP/M ADRESSEN
;
;
;-----------------------------------------------------------
;
;
MSIZE	EQU	56		;in vollen K
;
;
BIAS	EQU	.RES.((MSIZE-20)*1024)
CCP	EQU	.RES.(3400H+BIAS)
IBOOT	EQU	CCP-80H
BDOS	EQU	.RES.(CCP+806H)		;start BDOS
CBIOS	EQU	.RES.(CCP+1600H)	;start ZBIOS
NSECTS	EQU	(CBIOS-CCP)/128		;wieviel Sektoren
;
;
CDISK	EQU	0004H		;Zeiger auf Laufwerk
IOBYTE	EQU	0003H
DRIVES	EQU	2		;wieviel Laufwerke?
				; MAX 4 Laufwerke
MAXTRY	EQU	10
;
;
; Vektortabelle fuer IM 2
;
;
VECTAB	EQU	0FFE0H		;ueberlaedt BONTvectoren
INTVRE	EQU	VECTAB
INTVWR	EQU	VECTAB+2
INTRQ	EQU	VECTAB+4	;siehe hierzu spaeter!
;
	LIST
	EJECT
;**********************************************
;
; ZBIOS vers 2.0 
; Anpassungsprogramm fuer CP/M (R)
;
; mit Selbscheck fuer VIDEO80 und CPU/IEC
; (C) ELECTRONIKLADEN 1982
; geschrieben von RAOUL O. KOERBER
;
;**********************************************
;
; Der fuer ZBHOS zur Verfuegung stehende Platz auf der
; Diskette ist nur 380H Bytes lang. Wir muessen daher
; 'Einiges' ausl gern in d%n noch freien Sektor 1 auf
; Spur 0. Dabei ist zu beachten, dass nach der Initiali-
; sierung dieser Speicherplatz ueberschrieben werden kann!
;
; Die Routinen auf Sektor 1 betreffen NUR die CPU/IEC
; und eine moegliche Neuinitialisierung von VIDEO80.
; Beides kann NUR ab SSM 2.0 angesprochen werden. Aeltere
; Bootprogramme koennen NUR die Standartversion laden
; (Standard-CPU mit Terminal-Betrieb)
;
	IF	.NOT.CREATE	;nur wenn notwendig....
	ORG	IBOOT
;
;
	DEFB	0E5H		;kein BOOTER vorhanden!
;
;
	LD	A,B		;VIDEO80 FLAG
	LD	BC,ILEN1	;laenge von CONST und CONIN
	LDIR
	OR	A		;Test ob VIDEO80
	RET	Z		;wenn nicht
	LD	BC,ILEN2	;Laenge von CONOUT
	LDIR
	RET
;
	NOP			;AUSGLEICH
;
; CONST und CONIN fuer CPU/IEC
;
NCONST:
;
	IN	,(CSBC)
	RRCA
	SBC	A,A
	RET
	NOP			;Ausgleich
;
NCONIN:
;
	IN	A,(CSBC)
	RRCA
	JR	C,NCONIN
	IN	A,(CSBD)
	AND	7FH
	RET
	NOP			;Ausgleich
;
ILEN1	EQU	$-NCONST
;
NOUT:
;
; CONOUT fuer CPU/IEC ohne VIDEO80
;
;
	IN	A,(CSBC)
	BIT	2,A
	JR	Z,NOUT
	D	A,C
	OUT	(CSBD),A
	CP	20H
	RET	NC		;wenn nicht ...
	PUSH	BC		;B wird veraendert!
	LD	B,140
	CP	FF
	JR	Z,NDEL
	CP	HOME
	JR	Z,NDEL
	LD	B,7
NDEL:	PUSH	BC
	LD	B,0
	DJNZ	$		;innere Schleife
	POP	BC
	DJNZ	NDEL		;aeussere Schleife
	POP	BC		;Benutzer BC vom Stack
	RET
;
ILEN2	EQU	$-NOUT
;
;
; Initialisierung fuer VIDEO 80
;
;
	ORG IBOOT+50H
;
;
;
	LD	HL,VTAB
	XOR	A
INV1:	OUT	(VIDREG),A	;Adressregister
	INC	HL		;Zeiger auf INITBYTE
	PUSH	AF
	LD	A,(HL)		;Initbyte
	OUT	(VIDFIL),A	;senden
	POP	AF
	INC	A		;naechstes Adr Register
	CP	TABLEN		;fertig
	JR	NZ,INV1		;wenn nicht
	RET
;
;
VTAB:
;
; INITIALISIDRUNG von VIDEO80
;
;
	ORG	IBOOT+60H
;
	DEFB	77H		;H-TOTAL
	DEFB	MAXCOL		;Zeichen pro Zeile
	DEFB	60H		;H-SYNC
	DEFB	9H		;H-SYNC BREITE
	DEFB	1BH		;V-TOT
	DEFB	0		;V-SCAN Ausgleich
	DEFB	MAXLIN		;Zeilen pro Seite
	DEFB	19H		;V-SYNB POS
	DEFB	0		;INTERLACE
	DEFB	0BH		;MAX SCAN
	DEFB	60H		;CURSOR
	DEFB	0BH		;CURSOR LEN
	DEFW	0
	DEFW	0
TABLEN:	EQU	$-VTB
;
;
	ENDIF
	EJECT			;neue Seite fuer CBIOS
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; ...und hier beginnt nun das eigentliche Zbios
;
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;
;>>>AB HIER KEINESFALLS AENDERN - CP/M greift direkt zu<<<<
;
;
	ORG	CBIOS
;
;
;-----------------------------------------------------------
;
; CP/M JUMPTABELLE
;
; ZBIOS unterstuetzt die Funktionen PUNCH und READER nicht.
; Da ZBIOS 'vollgepackt' ist, kann u.U (auch in MOVCPM)
; ein Sprung zu einer festen Adresse eingefuegt werden
;
;
;-----------------------------------------------------------
;
;
BOOT:	JP	CBOOT		;KALTSTART
WBOOTE:	JP	WBOOT		;WARMSTART
CIST:	JP	CONST		;Konsolen Status
CI:	JP	CONIN		;Tastatur Eingabe
CO:	JP	CONOUT		;Video Ausgabe
	JP	LIST		;Drucker Ausgabe
PUNCH:	LD	A,C
	RET
	NOP			;DUMMY
READER:	LD	A,1AH		;EOF-Marke
	RET
	JP	HHOME		;KOPF auf Track 00
	JP	SELDSK		;Welches Laufwerk?
	JP	SETTRK		;welche Spur
	JP	SETSEC		;welcher Sektor
	JP	SETDMA		;wo ist BUFFER?
	JP	READ		;Sektor LESEN
	JP	WRITE		;Sektor SCHREIBEN
	JP	LISTST		;STATUS des Druckers
	JP	SECTRAN
;
;
; Festgelegte Daten Tabelle fuer 'n' Laufwerke
; Standart IBM kompatible Disketten
;
; DISK 0
;
DPBASE:
;
;
	DEFW	XLT
	DEFW	0
	DEFW	0
	DEFW	0
	DEFW	DIRBUF
	DEFW	DPB
	DEFW	CSV0
	DEFW	ALV0
;
; DISK 1
;
	DEFW	XLT
	DEFW	0
	DEFW	0
	DEFW	0
	DEFW	DIRBUF
	DEFW	DPB
	DEFW	CSV1
	DEFW	ALV1
;
;
; nur wenn mehr als 2 Laufwerke vorgegeben sind wird 
; nachfolgende Tabelle mit assembliert.
;
	IF	DRIVES.GT.2
;
; DISK 2
;
	DEFW	XLT
	DEFW	0
	DEFW	0
	DEFW	0
	DEFW	DIRBUF
	DEFW	DPB
	DEFW	CSV2
	DEFW	ALV2
;
	ENDIF
;
	IF	DRIVES.GT.3
;
; DISK 3
;
	DEFW	XLT
	DEFW	0
	DEFW	0
	DEFW	0
	DEFW	DIRBUF
	DEFW	DPB
	DEFW	CSV3
	DEFW	ALV3
;
	ENDIF
;
;
XLT:
;
; Sektor 'Uebersetzungs' Tabelle fuer Skew Faktor
;
; In CP/M werden die Sektoren nicht nacheinander gelesen
; sondern (der Schnelligkeit des Zugriffes wegen) um jeweils
; 6 Sektoren versetzt.
;
;
	DEFB	1,7,13,19,25
	DEFB	5,11,17,23,3
	DEFB	9,15,21,2,8
	DEFB	14,20,26,6,12
	DEFB	18,24,4,10,16,22
;
;
DPB:
;
;
; Disketten Parameter Block
; Hier werden alle Disketten Parameter spezifiziert
;
;
	DEFW	26		;Sektoren pro Spur
	DEFB	3		;Block Shift Faktor
	DEFB	7		;Block Maske
	DEFB	0		;NULL Maske
	DEFW	242		;Disk Kapazitaet-1
	DEFW	63		;wieviel Eintraege in DIR?
	DEFB	192		;ALLOC 0
	DEFB	0		;ALLOC 1
	DEFW	16		;Pruefgroesse
	DEFW	2		;Spuroffset
;
;
ITAB:	DEFB	11		;Laenge
	DEFB	CPAC,OUTPUT	;CPU/IEC
	DEFB	VIDREG,OUTPUT	;VID80
	DEFB	INITB,OUTPUT	;PRINTER PORT B
	DEFB	INITB,NOINT
	DEFB	INITA,NOINT		;Kein Interupt bei Print
	DEFB	INITA,BITMOD		;PORT A BITMODUS
	DEFB	INITA,00111100B		;Maske
	DEFB	CONT,00111100B		;Setze Strobe und Prime
	DEFB	CPAC,55H		;Testbyte CPU/IEC
	DEFB	CONT,11000011B		;reset Strobe
	DEFB	VIDFIL,55H		;testByte VID80
;
VECTOR:	DEFW	ISERV1
	DEFW	ISERV2
	DEFW	ISERV3
;
;
SETPIO:
;
; diese Routine initialisiert die FDC-Pio und setzt die
; Interupt Vektoren
;
	DI
	LD	A,OUTPUT	;Port A+b auf Ausgabe
	OUT	(PIOAC),A
	OUT	(PIOBC),A
	LD	HL,VECTOR	;VECTOR ADDR
	LD	DE,INTVRE	;Zieladdr
	LD	BC,3*2
	LDIR
	LD	A,(VECTAB.SHR.8).AND.0FFH	;HIGH BYTE
	LD	I,A				;als Vector
	LD	A,INTRQ.AND.0FFH		;LOW BYTE
	OUT	(PIOAC),A
	IM	2
	LD	A,ENINT		;Interupt zulassen
	OUT	(PIOAC),A
	OUT	(PIOBC),A
	RET
;
;
HELLO:	DEFB	FF,SUB		;evtl ESC-Sequenz
	DEFM	'00k CP/M vers 2.2'
	DEFB	CR,LF,0
;
;
;>>AB HIER AENDERUNGEN ZULAESSIG * Aber bitte auf BACKUP<<
;
;
;
CBOOT:
;
; wir erwarten hier,dass die Schnittstelle auf der CPU bereits
; initialisiert ist. Um Aenderungen leichter vornehmen zu koennen
; wird die Initialisierung fuer VID80 und PRINT nocheinmal
; gemacht. Dazu ist es notwendig 'einiges' nach Sektor 1 (IBOOT)
; zu verlegen...
;
; Aus Platznot wird Speicherplatz teilweise 'doppelt" genutzt
; fuer CSV0 und CSV1 sowie gegebenenfalls fuer CURPNT,SPALTE und
; ZEILE.
;
;
; RESERVIERUNG von Speicherplatz bei Doppelbelegung
;
CSV0	EQU	CBOOT
CSV1	EQU	CSV0+16
CURPNT	EQU	CSV1+16
SPALTE	EQU	CURPNT+2
ZEILE	EQU	SPALTE+1
TRACK	EQU	ZEILE+1
SECTOR	EQU	TRACK+1
RUNDSK	EQU	SECTOR+1
REQDSK	EQU	RUNDSK+1
DMAAD	EQU	REQDSK+1
;
;
	LD	HL,ITAB		;Init-Tabelle
	LD	B,(HL)		;Laenge
CB1:	INC	HL
	LD	C,(HL)		;PORT#
	INC	HL
	LD	A,(HL)		;InitByte
	OUT	(C),A		;senden
	DJNZ	CB1		;bis alles fertig
;
; PRINT ist Initialisiert und CPU/IEC sowie VID80 kann
; abgefragt werden. B wird als Flag (jetzt NUL von DJNZ)
; benutzt fuer VID80
;
	IN	A,(VIDFIL)	;Video80 vorhanden?
	CP	55H
	JR	NZ,CB2		;wenn nicht...
;
; Wenn VIDEO80 dann Treiberroutine einlinken
;
	DEC	B		;doch zuerst V80 Flag setzten
	LD	HL,VCO		;nun CONOUT umlinken
	LD	(CO+1),HL
;
; VIDEO80 ist bereits von SSM initialisiert
; u.U. kann es notwendig sein dies (mit anderen Werten)
; von CP/M aus nocheinmal zu tun...daher hier ein
; Sprung in Sektor 1. Soll der Sprung verwendet werden
; muss der NAECHSTE RELATIVE JUMP ausge'NOP't werden
;
	JR	CB2		;jump around call
	LD	HL,IBOOT+60H	; Adressuebergabe
	CALL	IBOOT+50H
;
;
CB2:	IN	A,(CPAD)	;CPU/IEC testen
	CP	55H
;
; Parameteruebergabe um leichter aendern zu koennen
;
	LD	HL,IBOOT+10H	;Start der Routinen
	LD	DE,CONST	;dorthin wird transferiert
	CALL	Z,IBOOT+1	;Overlay....
;
;
; wir kommen nun zur 'normalen' Initialisierung
;
;
;
	CALL	SETPIO		;FDC initialisieren
;
	LD	HL,HELLO	; ...jetzt kommt CPM
SM:	LD	A,(HL)		;hole Zeichen
	OR	A		;evtl 00?
	JR	Z,NXT		;dann fertig
	LD	C,A		;sonst ueber AKKU
	CALL	CO		; senden
	INC	HL		;und naechstes...
	JR	SM		;bis fertig (00)
;
NXT:	LD	HL,0
	LD	(IOBYTE),HL	; IOBYTE und CDISK auf 0
;
;
GOCPM:
;
; Initialisierung der Einsprungadressen auf Seite 0
;
;
	LD	A,0C3H		;JUMP Befehl
	LD	(0),A		;nach Adresse 0
	LD	(5),A		;und 5
	LD	HL,WBOOTE
	LD	(1),HL		;fuer Warmstart
	LD	HL,BDOS
	LD	(6),HL		;Einsprung in BDOS
	LD	A,(CDISK)	;welches Laufwerk?
	LD	C,A
	JP	CCP		;und ab geht's.....
;
;
WBOOT:
;
; Warmstart von CP/M
; Beim Kaltstart wurde CP/M vom Boot (oder BBC oder wie auch
; immer) geladen. Jetzt muss das CP/M selber machen (wenn
; es sich z.B. mit DDT oder ZSID sein CCP ueberschrieben hat.
; dabei darf es aber einmal ge'ladene' Variable nicht aendern
;
;
	LD	SP,80H		;wir brauchen Stack
	CALL	SETPIO		;init FDC PIO
	XOR	A		;Akku=0
	LD	C,A		;C=Laufwerknummer (A)
	DEC	A		;A=0FFH
	LD	(RUNDSK),A	;Zeiger zurueckzetzen
	CALL	SELDSK		;und Kontroller 'mitteilen'
	CALL	HHOME		;Kopf aof Spur 0 setzen
	LD	B,NSECTS	;Sektorzaehler
				;C enthaelt immer noch 00
				; ist nun Zeiger auf SPUR 0
	LD	D,2		;wir laden ab CCP....
	LD	HL,CCP		;Adresse wo's hin soll
;
; beim Warmstart werden die Daten von Sektor 1 nicht mehr
; benoetigt..die I/O Vektoren sind initialisiert.
;
LOAD1:	PUSH	BC
	PUSH	DE
	LD	C,D		;Sektorzaehler nach C
	CALL	SETSEC		; und setzen
	LD	(DMAAD),HL	;DMA Adresse laden
;
;
; Laufwerk gesetzt, Spur gesetzt, Sektor gesetzt und 
; DMA (wohin es soll) bekannt... wir koennen lesen!
;
;
	CALL	READ		;jetzt Sektor lesen
	JR	NZ,WBOOT	;bei Lesefehler...
	POP	DE		;den 'Rest' auch wiederholen
	POP	BC
	DEC	B		;Spurzaehler-1
	JR	Z,GOCPM		;wenn fertig mit laden
;
; es sind noch nicht alle Sektoren gelesen also....
;
	INC	D		;Sektorzaehler+1
	LD	A,D
	CP	27		;evtl am Ende der Spur?
	JR	C,LOAD1		;wenn noch nicht..
	INC	C		;auf naechster Spur (1)
	LD	D,C		;  auch Sektor=1
	PUSH	HL		;und Register retten
	CALL	SETTRK		;neue Spur anwaehlen..
	POP	HL
	JR	LOAD1		;und einlesen.
	EJECT
; ..nun haben wir endlich alles was mit CP/M laden
; zu tun hat erledigt. Es folgen nun die Anpass-
; routinen die mehr oder weniger voellig HARDWARE-
; abhaengig sind.
; Dies ist DER Teil in dem der Benutzer wohl am
; meisten Aenderungen auf seine Hardware bezogen
; ausfuehren wird....
;
; BITTE BEACHTEN:
; ZBIOS darf nicht laenger als 380H BYTES sein sonst passt
; es nicht mehr auf die von CP/M zugewiesenen Sektoren...
;
;
;----------------------------------------------------------
;
; Unterprogramme fuer DISK I/O
;
; wird teilweise von CP/M (R) direkt aufgerufen
;
; Bitte hierzu ALTERTION GUIDE befragen.......
;
;-----------------------------------------------------------
;
;
SETDMA:
;
;
; Eingangsbuffer fuer einen Sektor festlegen
;
;
;
	LD	(DMAAD),BC	;ablegen DMA Adresse
	RET
;
;
SELDSK:
;
; es wird geprueft ob die gewaehlte Laufwerksnummer
; zulaessig ist und gegebenenfalls abgelegt..
;
;
	LD	HL,0		;Fehler Code laden
	LD	A,C		;welcher Drive
	CP	DRIVES		;moeglich?
	RET	NC		;wenn nicht
	LD	L,C		;LW# IN L
				;H=00 von Errorcode..
	LD	A,01111111B	;berechne Ausgabedaten
	INC	C
SELD1:	RLCA			;0 durchschieben
	DEC	C
	JR	NZ,SELD1	;entsprechend LWerk
	AND	10001111B
	LD	(REQDSK),A	;ablegen
	XOR	A
	LD	(TRACK),A	;Start bei Spur 0
;
;
; aus der 'Laufwerksnummer' in HL wird nun der Offset
; in die Laufwerksdatentabelle (DPBASE) errechnet.
;
;
	ADD	HL,HL		;*2
	ADD	HL,HL		;*4
	ADD	HL,HL		;*8
	ADD	HL,HL		;*16
	LD	DE,DPBASE	;dort steht alles
	ADD	HL,DE		;fuer 'wirkliche' Adresse
	RET
;
;
TSTSEL:
;
;
; hier pruefen wir nach ob wir den Umschaltbefehl
; ausfuehren muessen (dann 'klappert' es) oder ob
; das Laufwerk bereits selektiert ist
; Muss umgeschaltet werden fahren wir den Kopf gleich
; auf Spur 0
;
;
;
	LD	HL,RUNDSK	;ist selektiertes Laufwerk
	LD	A,(REQDSK)	;gleich angesprochenem?
	CP	(HL)
	RET	Z		;dann keine Aktion
	OUT	(PIOBD),A	;sonst selektieren
	LD	(HL),A		;und merken..
TST1:	IN	A,(FCSTAT)	;bereits erledigt?
	RLCA
	JR	C,TST1
;
;
; und wir fallen jetzt nach HHOME durch um den
; Kopf auf Spur 0 zu setzen...
;
;
HHOME:
;
;
; setzt den Kopf auf Spur 0
;
;
	PUSH	HL
	CALL	TSTSEL		;Laufwerk selektiert?
	LD	HL,TRACK
	RES	7,(HL)		;Merker ruecksetzen
	POP	HL
	LD	A,RESTOR	;nun synchronisieren
	OUT	(FCCONT),A
	EI
	JR	$		;warten bis fertig
	RET
;
;
;
;
PREDSK:
;
; vor Schreiben oder Lesen
;
;
	PUSH	HL
	CALL	TSTSEL		;Laufwerk selektiert?
	LD	HL,TRACK	;welcher Track 
	BIT	7,(HL)		;Merker gesetzt?
	LD	A,(HL)		;sonst muessen wir..
	SET	7,(HL)		;Merker setzen...
	POP	HL
	RET	NZ		;war gesetzt
	OUT	(FCDATA),A	;ausgeben
	LD	A,SEEK		;und warten bis angelegt
	OUT	(FCCONT),A
	EI
	JR	$
	RET
;
;
SETTRK:
;
;
; setzt Spurnummer wenn nicht bereits gesetzt
;
;
	PUSH	HL
	LD	HL,TRACK	;Nummer holen
	LD	A,(HL)
	RES	7,A		;Merker ruecksetzen
	CP	C		;Spurnummern gleich?
	JR	Z,SETT1		;dann fertig
	LD	(HL),C		;sonst neue Nummer
SETT1:	POP	HL
	RET
;
;
SETSEC:
;
; Sektor anwaehlen
;
;
	LD	A,C		;Sektornummer steht in C
	LD	(SECTOR),A	;ablegen fuer spaeter
	OUT	(FCSEC),A	;und an Kontroller geben..
	RET
;
;
SECTRA:
;
;
; hier wird der SKEW-Faktor eingerechnet (d.H. die Sektor
; ablage)
;
;
	EX	DE,HL
	ADD	HL,BC
	LD	L,(HL)
	LD	H,0
	RET
;
;
READ:
;
; Lesen eines Sektors
; Laufwerk,Spur,Sektor und DMA muessen bekannt sein!
; Nach fehlerfreiem Lesen ist A=0
;
;
	CALL	PREDSK		;Home notwendig
	LD	B,MAXTRY	;Anzahl der Versuche
	LD	A,INTVRE.AND.00FFH	;unterer Vektor
	OUT	(PIOBC),A	;in Pio fuer IM2
READ1:	LD	HL,(DMAAD)	;hole DMA Adresse
	LD	A,READS		;und Lesebefehl
	OUT	(FCCONT),A	;an Kontroller
READ2:	EI			;Interupts freigeben
	HALT			;Warte auf IRQ oder DRQ
	JR	READ2
	AND	00011100B	;Fehler CRC,LostDATA,RNF?
	RET	Z		;alles klar auf der .....
	DJNZ	READ1		;noch ein Versuch
	RET
;
;
WRITE:
;
;
; Schreiben eines Sektors
; Laufwerk,Spur,Sektor und DMA muessen bekannt sein
; Nach fehlerfreiem Schreiben ist A=0
;
;
	CALL	PREDSK
	LD	B,MAXTRY	;maxtry mal...
	LD	A,INTVWR.AND.00FFH	;unterer Vektor
	OUT	(PIOBC),A
WRITE1:	LD	HL,(DMAAD)
	LD	A,WRITES
	OUT	(FCCONT),A
WRITE2:	EI
	HALT
	JR	WRITE2
	AND	01110000B	;Schreibgeschuetzt,Fehler?
	RET	Z		;GUT
	DJNZ	WRITE1
	RET
;
;
; Nun folgen die INTERRUPT Service Routinen
; Die Vektoren (ZEIGER) auf diese Routinen stehen
; auf Seite 0FFH im RAM.
;
;
;
ISERV1:	IN	A,(FCDATA)	;Daten einlesen
	LD	(HL),A		;und abspeichern
	INC	HL		;naechster Speicherplatz
	RETI
;
;
ISERV2:	LD	A,(HL)		;Daten aus Buffer holen
	OUT	(FCDATA),A	;und ausgeben
	INC	HL
	RETI
;
;
ISERV3:	POP	DE		;Rueckkehradresse vom Stack
	INC	DE		;ueber JR xx 'lenken'
	INC	DE
	PUSH	DE		;und zurueck auf Stack
	IN	A,(FCCONT)	;FDC Status holen
	RETI
;
;
; >>> EINGABE UND AUSGABE <<<
;
; Es folgen nun die Ein/Ausgaberoutinen
; Zbios prueft beim Initialisieren nach ob Video80 im System
; ist...ist dies der Fall wird initialisiert und CO umgelinkt
;
;
;
LISTST:
;
; Es wird der Status des Druckers abgefragt (aehnlich CONST)
;
;
; Standardmaessig wird PRINT unterstuetzt
;
	IN	A,(CONT)	;Status
	BIT	4,A		;Busy ?
	LD	A,0
	RET	NZ		;wenn noch nicht fertig
	DEC	A		;fertig => Akuu=FF
	RET
;
;
LIST:
;
;
; Drucker Ausgaberoutine (PRINT)
; Zeichenausgabe muss ueber Register C erfolgen
; aehnlich CONOUT
;
;
	IN	A,(CONT)	;Kontrollport
	BIT	4,A
	JR	NZ,LIST		;wenn noch nicht fertig
	LD	A,C		;Uebernahme des Zeichens
	AND	7FH		;MASKIERE GRAFIK BIT....
	OUT	(DATA),A	;senden
	LD	A,11111110B	;dann Strobe
	OUT	(CONT),A
	RRCA			;Strobe zuruecksetzen
	OUT	(CONT),A
	LD	A,C		;=> mit Zeichen in Akku
	RET
;
;
CONST:
;
; Hier wird das Keyboard abgefragt ob eine Eingabe vorliegt
; Liegt ein Zeichen an kehrt die Routine mit A=0FFH zurueck
; sonst mit 00H
;
;
	IN	A,(UARTC)	;Hole UART Status
	RRCA
	RRCA			;Statusbit in Carry schieben
	SBC	A,A		;X-X=0...minus Carry =FF
	RET			;so einfach ist das
;
;
CONIN:
;
; Standard Tastatureingabe ueber CPU UART
;
;
	IN	A,(UARTC)	;Teste UART Status
	RRCA
	RRCA
	JR	NC,CONIN	;wenn nicht fertig
	IN	A,(UARTD)	;hole Daten
	AND	01111111B	;maskiere Paritybit
	RET
;
;
;
; Standard Ausgabetreiber ebenfalls fuer CPU UART
;
;
; Die Ausgabe erfolgt ueber Register C kommt jedoch
; mit dem ausgegebenen Zeichen im AKKU zurueck
;
;
CONOUT:	IN	A,(UARTC)	;hole USART Status
	RRCA
	JR	NC,CONOUT	;warte bis fertig
	LD	A,C		;Ausgabe ueber AKKU
	OUT	(UARTD),A
;
; fuer VIDEO64 und VIDEO2 ist eine Warteschleife fuer die
; CURSOR Controllzeichen notwendig. Wenn diese OPTION nicht
; gewuenscht wird, kann direkt mit RET (0C9H) abgeschlossen
; werden
;
	CP	20H
	RET	NC		;wenn nicht ...
	PUSH	BC		;B wird veraendert!
	LD	B,140
	CP	FF
	JR	Z,DEL
	CP	HOME
	JR	Z,DEL
	LD	B,7
DEL:	PUSH	BC
	LD	B,0
	DJNZ	$		;innere Schleife
	POP	BC
	DJNZ	DEL		;aeussere Schleife
	POP	BC		;Benutzer BC vom Stack
	RET
	NOP			;AUSGLEICH...CPU/IEC
;
;
; VIDEO 80 TREIBER
;
;
CLRS:
;
;Bildschirm loeschen
;
	LD	BC,2*2048-1		;'Laenge" des VIDEO80 Speichers
	LD	DE,VIDRAM+7FFH-1	;vorletztes Byte
	LD	HL,VIDRAM+7FFH
	OUT	(28H),A			;TOGGLE
	LD	(HL),0			;loeschen
	LDDR				;von 'Unten' an
	OUT	(28H),A			;TOGGLE
;
;Nun muessen wir noch den Cursor setzen (nach OBEN LINKS)
;dazu 'fallen' wir einfach 'durch...
;
HOMEC:
;
; Cursor nach oben links OHNE loeschen des Bildschirmes
;
	LD	HL,VIDRAM		;Anfangsadresse
	LD	(CURPNT),HL		;in Adresszeiger laden
	LD	H,L			;L ist bei VIDRAM=00
	LD	(SPALTE),HL		;jetzt wird Spalte und
	RET				;Zeile auf 0 gesetzt
;
VCO:
;
; dies ist der Start der eigentlichen Treiberroutine
;
	PUSH	HL
	PUSH	DE
	PUSH	BC
;
; ...und nun Trick 17 um alles kurz zu machen...
;
	LD	HL,SETCUR	;RETurn-Adresse
	PUSH	HL		;   auf den Stack..
;
; Jedes RET faellt nun nach SETCUR zurueck
;
	LD	A,C		;Datenuebergabe von CP/M in C-Reg
	CP	BLANK		;testen ob CNTRL
	JR	NC,PRINT	;..auch wenn's Grafic ist..
	CP	CR
	JP	Z,VCR
	CP	LF
	JP	Z,VLF
	CP	SUB
	JR	Z,CLRS
	CP	HT
	JR	Z,VFWD
	CP	VT
	JP	Z,VUP
	CP	HOME
	JR	Z,HOMEC
	CP	BS
	JP	Z,VBACK
;
; mehr Steuerzeichen nicht implementiert.....(380H Bytes!)
;
;also zurueck zum 'CALL'er
;
	POP	AF		;Stack 'saeubern'....
;
SETCUR:
;
; es wird nun die Cursoraddresse dem Kontroller uebergeben..
;
	LD	HL,(CURPNT)	;Adresse holen
SETCU1:	LD	A,0FH		;Cursorregister adresssieren
	OUT	(VIDREG),A
	LD	A,L		; zuerst LOW Byte laden
	OUT	(VIDREG+1),A
	LD	A,0EH		;   dann High Byte
	OUT	(VIDREG),A
	LD	A,H
	AND	00000111B	;maskieren
	OUT	(VIDREG+1),A	;  und ausgeben
;
; nun alles vorm Stack zurueck....
;
	POP	BC
	POP	DE
	POP	HL
	RET
;
PRINT:
;
;..hier wird jedes darstellbare Byte im Videoram abgelegt. Die
; Adresse wo's hinsoll steht immer in CURPNT.
;
	LD	HL,(CURPNT)	;..wo's hin soll
	OUT	(28H),A			;TOGGLE
	LD	(HL),A		;ablegen
	OUT	(28H),A			;TOGGLE
;
; nun muss die Cursoradresse incrementiert werden..dazu fallen
; wir einfach nach VFWD durch..
;
VFWD:
;
; ..schiebt den Cursor um eine Stelle weiter
;
	LD	HL,(CURPNT)	;wo ist Cursor?
VFWD0:	INC	HL		; Adresse +1
	LD	(CURPNT),HL	;wieder ablegen...
	LD	HL,SPALTE	;..in welcher Spalte sind wir
	INC	(HL)		;nach Incrementierung
	LD	A,MAXCOL	;..am Zeilenende?
	CP	(HL)
	RET	NZ		;..wenn nicht Zeilenende
	LD	(HL),0		;sonst Zaehler loeschen
	INC	HL		;womoeglich in letzter Zeile
	LD	A,MAXLIN-1
	CP	(HL)
	JR	Z,SCROLL	;dann scrollen!
	INC	(HL)		;sonst nur Zeilenzaehler incrementieren
	RET
;
SCROLL:
;
; Bildschirminhalt um eine Zeile nach oben schieben.
;
	LD	HL,VIDRAM+MAXCO;	;das erste Zeichen von Zeile 2
	LD	DE,VIDRAM		;soll an erste Stelle
	LD	BC,MAXCOL*(MAXLIN-1) 	;Laenge des Textes
	OUT	(28H),A			;TOGGLE
	LDIR
	OUT	(28H),A			;TOGGLE
;
; Nnach LDIR steht das BC-Register auf 0000, das nutzen wir
;
	LD	HL,LSTLIN	;Anfang letzte Zeile
	LD	(CURPNT),HL	;ab dort gehts weiter aber erst
	LD	B,MAXCOL	;eine Zeilenlaenge
	OUT	(28H),A			;TOGGLE
SCR1:	LD	(HL),0		;loeschen...
	INC	HL
	DJNZ	SCR1		;bis fertig (MAXCOL mal)
	OUT	(28H),A			;TOGGLE
	RET
;
VCR:
;
; Wagenruecklauf (CR,RETURN). Der CURSOR springt an den Anfang
; der Zeile zurueck. Es wird KEIN ZEILENVORSCHUB gemacht (LF)
;
	LD	HL,(CURPNT)	;wo sind wir
	LD	A,(SPALTE)	;Spaltenzaehler
	LD	E,A		;in E-Register
	XOR	A		lAKKU und CARRY loeschen
	LD	D,A		;und D-Reg auch
	CALL	VUP1		;errechnen wohin..
	LD	(SPALTE),A	;Spalte NULLsetzen
	RET
;
VLF:
;
; Wagenvorschub (LF) sind wir am Bildschirmende scrollen...
;
	LD	HL,ZEILE	;welche Zeile?
	LD	A,MAXLIN-1	;am Ende?
	CP	(HL)
	JR	Z,SCROLL	;dann scrollen...
	INC	(HL)		;sonst Zeilenzaehler+1
	LD	HL,(CURPNT)	; Adresse?
	LD	DE,MAXCOL	; Zeilenlaenge?
	ADD	HL,DE		;dazuzaehlen!
	JR	STOCUR		;  und CURSORAdresse ablegen
;
VUP:
;
; CURSOR eine Zeile hoch 
;
	LD	HL,ZEILE
	XOR	A		;AKKU=0 und CARRY=0
	CP	(HL)		;Zeile 1 (0)?
	RET	Z		;dann tun wir garnichts ....
	DEC	(HL)		;sonst Zeilenzaehler-1
	LD	DE,MAXCOL-1
	LD	HL,(CURPNT)	;..und Zeiger korrigieren
VUP1:	SBC	HL,DE
	JR	STOCUR
;
VBACK:
;
; Ein Zeichen Zurueck (BACKSPACE)
;
	LD	HL,ZEILE		;Zeilenzaehler
	XOR	A
	CP	(HL)			; 0?
	DEC	HL			;Spaltenzaehler laden
	JR	NZ,VBACK1		;wenn Zeilenzaehler nicht 0
	CP	(HL)			;SPALTENZAEHLER=0?
	JR	NZ,VBACK2		;...dann alles klar
	LD	(HL),MAXCOL-1		;sonst letzte Spaltenadresse
	INC	HL			;  und Zeilenadresse
	LD	(HL),MAXLIN-1		;  ablegen
	LD	HL,VIDRAM+((MAXLIN*MAXCOL)-1)
	JR	STOCUR
;
VBACK1:	CP	(HL)			;Spalte 0?
	JR	NZ,VBACK2		;dann herunterzaehlen
	LD	(HL),MAXCOL-1		;sonst letzte Spalte
	INC	HL			;und...
VBACK2:	DEC	(HL)			;Zeilenzaehler  runterzaehlen
	LD	HL,(CURPNT)		;CURSORadresse
	DEC	HL
STOCUR:	LD	(CURPNT),HL
	RET
;
;
	ORG	CBIOS+380H
;++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; Variable
;
;++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;
BEGDAT	EQU	$
;
;
DIRBUF:	DEFS	128
ALV0:	DEFS	31
ALV1:	DEFS	31
;
	IF	DRIVES.GT.2
ALV2:	DEFS	31
	ENDIF
	IF	DRIVES.GT.3
ALV3:	DEFS	31
	ENDIF
;
	IF	DRIVES.GT.2
CSV2:	DEFS	16
	ENDIF
	IF	DRIVES.GT.3
CSV3:	DEFS	16
	ENDIF
;
;
ENDDAT	EQU	$
DATSIZ	EQU	$-BEGDAT
;
;
	END

D
D
D

EN