Sorry the comments are in German language ;-)

Tip: If you want to download BOOTSYS.Z80 use the intel-hex or the uuencoded part


	title	BOOTSYS	- CPM3.SYS 1.12
	subttl	Copyright (C) 2000 Andreas Gerlich
	page	70,132
;
; BOOTSYS relocates and places the resident and banked (if presend) part
; of a CPM3.SYS to their right places into memory and starts CP/M 3.1.
; You generate a cpm3.com for starting CP/M 3.1 with:
;	pip cpm3.com=bootsys.com,cpm3.sys
;
; Copyright © 2000 Andreas Gerlich (agl @ IRCNet)
;
; BOOTSYS is free software; you can redistribute it and/or modify it under
; the terms of the GNU General Public License as published by the Free
; Software Foundation; either version 2 of the License, or (at your
; option) any later version.
;
; This program is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
; General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
;

	; BOOTSYS WILL NOT WORK ON ALL CP/M 3.1 SYSTEMS. WHY ?
	; If the MOVE, XMOVE are placed in the banked part of the bios
	; then it can be that this parts will be superscribed when
	; bootsys+cpm3.sys are transfered to bank 0.
	; Try it if this utility works under your CP/M 3 system.
	; If it works you can start a new version of your
	; bios (if you develop one) direct under CP/M 3.1 .
	;
	; Bootsys works always under CP/M 2.2 to relocate a nonbanked or a
	; banked system in CPM3.SYS and to start CP/M 3.1 !!!
	;
	; You generate a cpm3.com for starting CP/M 3.1 with:
	;	pip cpm3.com=bootsys.com,cpm3.sys
	;

	if0
	.printx	'BOOTSYS - CPM3.SYS 1.12'
	endif

	.z80	; Code uses Z80 instructions

	aseg
	org	100h

;---------------------------------------------------------------------------
; 8086 Prefix by Andreas Gerlich. Necessary if bootsys.com/cpm3.com is
; running on a MS-DOS/Windows system. On a MS-DOS/Windows system this
; prefix prints a message and exits.
;
	;
	db	0ebh	  ; -*----  ex de,hl   (Z80)
			  ;   \
			  ;    +--  jmp 011ah  (8086)
			  ;   /
	db	018h	  ; ==
			  ;   \
	db	begin-$-1 ; ---+--  jr begin   (Z80)
	; please test if the relativpoint ist less then 80h far away.

	; Von Adresse 103 bis 11a ist Raum frei. Die Anzahl der nachfolgenden
	; Bytes sollte nicht erhoeht werden!
	;	|                       |
	;
	db	'8086 prefix by agl ;-) ' ; room for a little Message ;-)
	;
	;
;	org	 011ah	 ; must begin at this address

	db	0bah		; move dx,DOS_Txt (8086)
	dw	DOS_Txt
	db	0b4h,09h	; mov ah,09h	(8086, print string)
	db	0cdh,021h	; int 21	(8086, MSDOS)
	db	0b4h,01h	; mov ah,01h	(8086, input one character)
	db	0cdh,021h	; int 21	(8086, MSDOS)
	db	0cdh,020h	; int 20	(8086, Ende Programm)

begin:	jp	main

DOS_Txt:db	13,10,'Z80 or CP/M Emulator Required !',13,10,10

	db	'BOOTSYS is a new loader for a CPM3.SYS to load and run'
	db	13,10
	db	'CP/M 3.1 in an Z80-Emulator or on a real CP/M-Z80 system.'
	db	13,10,10
	db	022H,'Yet Another Z80 Emulator by AG',022h,' is a Z80 '
	db	'Emulator.',13,10
	db	'It is designed to provide an exact simulation of the',13,10
	db	'Z80 microprocessor on a UNIX / Linux system.',13,10,10
	db	'You find yaze-ag under',13,10,10
	db	9,'http://www.mathematik.uni-ulm.de/users/ag/yaze-ag/',13,10
	db	'     or',13,10
	db	9,'ftp://ag-yaze:yaze@xylopia-upload.mathematik.uni-ulm.de'
	db	13,10,10
	db	'Press any key to continue ...'
	db	'$'


;---------------------------------------------------------------------------
; TODO:
;
; 0. ERLEDIGT und getestet
;    Wenn CP/M 3 laeuft!
;    Bootsys+cpm3.sys mit Interbankmove alles nach Bank 0 transferieren
;    (source und dest beides 0100H) dort ausfuehren (durch wechsel nach
;    bank 0).  (BIOS Funktion xmove ueberpruefen ob vorhanden)
;	Die funktion conout und conin sollten alle direkt ueber das
;	Bios gehen und die Adresse von wboot sollte am Anfang abgesichert
;	werden und nur diese fuer alle spaeteren Operationen benutzt werden.
;	Warum? Nach dem Wechsel von bank 1 nach 0 werden noch Ausgaben
;	und Eingaben getaetigt (diese duerfen (nach page 67/68 System Guide)
;	direkt im bios aufgerufen werden), die CP/M Vektoren sind aber nicht
;	mehr vorhanden.
;
;
; 1. ERLEDIGT und getestet
;    Nachsehen ob die Vektoren bei 0 und 5 da sind (C3H nachsehen und ob
;    die zwei Adressen plausibel sind (wboot > bdos)).
;	Nein --> Auf jedenfall Booten und fuer die Ein-/Ausgaben direkt YAZE
;		 benutzen (Ein anderes System wird bei Halt entsprechen
;						nicht mehr reagieren ;-))
;	JA   --> Fuer die Ausgaben CP/M benutzen (conout/-in ueber bios).
;		 Parameter nachpruefen (wenn gegeben nur Copyright ausgeben).
;
; 2. ERLEDIGT und getestet bzw. siehe 0.
;    CP/M Version ueberpruefen (nur wenn Vektoren da sind).
;	Wenn Version 3 dann Meldung ausgeben (Bitte unter 2.2 ausfuehren).
;
;	ODER siehe 0 (Diese Funktion sollte nach der ueberpruefung der
;	Copyright Message erfolgen.)
;
; 3. ERLEDIGT und getestet
;    Ueberpruefen ob eine CPM3.SYS angehaengt ist. (ueber Copyright Message)
;
; 4. VERWORFEN
;    Zwischenpuffer auf printrecord (wieder verworfen und absolute auf 0080H
;    gesetzt. Diese File wird immer auf 0100H geladen)
;
; 5. ERLEDIGT und getestet
;    Nonbanked Sytem booten
;---------------------------------------------------------------------------
;
Copyright_message::
	db	0DH,0AH
	db	' BOOTSYS - CPM3.SYS, V '
	;
	db	'1.12 29.09.2002'	;<--- VERSION
	;
	db	' Copyright (c) 2000,2002 by'
	db	' A.Gerlich',0DH,0AH
Dollar::db	'$'

; Message die ausgegeben wird wenn bootsys ohne vorhandenem CP/M booten will
; (hierzu wird das Dollar 3 Zeilen weiter oben ersetzt)

	db	 'No CP/M vektors found, try to boot CP/M 3.1'
	db	' in cooperation with yaze-ag ...',0DH,0AH,'$'
;
;---------------------------------------------------------------------------

wboot::		equ	0
wbootvek::	equ	wboot+1
bdos::		equ	5
bdosvek::	equ	bdos+1
parameter::	equ	wboot+80h


	; MAIN
	;
main::	ld	sp,lstack	; Stack setzten

	; CP/M Vektoren ueberpruefen
	;

	ld	a,0c3H		; OP-Code fuer JP
	ld	ix,0
	cp	a,(ix+wboot)	; Ist an der Stelle von WBOOT ein JP-Opcode
	jp	nz,nocpmvektoren; nein --> direkt CPM booten mit YAZE

	cp	a,(ix+bdos)	; Ist an der Stelle von BDOS ein JP-Opcode
	jp	nz,nocpmvektoren; nein --> direkt CPM booten mit YAZE

	; JP-opcodes vorhanden
	; ueberpruefen ob BDOS-Adresse kleiner als die WBOOT-Adresse ist
	;
	ld	hl,(wboot + 1)
	ld	de,(bdos + 1)
	or	a,a
	sbc	hl,de		; HL = wboot - bdos
	jp	c,nocpmvektoren	; Carry flag: bdos-addresse ist groesser als
				;	      die warmboot-adresse
				;     (bdos muss immer kleiner als wboot sein)
	jp	z,nocpmvektoren	; Zero Flag: beide Adressen sind gleich
				; (Duerfte praktisch nie vorkommen, aber
				;  man weis ja nie (Murphy laesst gruessen :-))

	; CP/M Vektoren sind vorhanden
	;
	ld	(cpmvek),a	; Boolsche Variable "CP/M Vektoren" setzen.
				; (Ab jetzt werden Ein-/Ausgaben ueber das
				; BIOS getaetigt.)
	ld	hl,(wboot + 1)	; Warmbootvektor absichern (wird ab jetzt
	ld	(wbvektor),hl	;	   bei Character in/out verwendet !)

	; CP/M Vektoren sind vorhanden
	; `--> ueberpruefen ob ein Parameter gegeben wurde

	ld	hl,Copyright_message	; Copyright message ausgeben
	call	PRSTRfunc

	ld	a,(parameter)
	or	a,a		; Ist irgend ein Parameter vorhanden?
	jr	z,testcopyright	; nein --> go on

	jp	wboot			; und beenden


	; Copyright Message von Digital Research in CPM3.SYS ueberpruefen
	;
testcopyright::
	;
	; Testet ob eine Copyright message vorliegt. Im anderen Fall
	; ist sehr wahrscheinlich noch kein CPM3.SYS angehaengt worden.
	; CPM3.COM wird mit PIP cpm3.com=bootsys.com,cpm3.sys erzeugt.
	;
	ld	hl,CPM3SYS+copyrmsg
	ld	de,CopyDRI
	ld	bc,CDRIlen - CopyDRI
	;
cloop::	ld	a,(de)
	cpi			; vergleich A mit (HL) (A-[HL], HL++, BC--)
	jr	nz,nocopyright	; ungleich (nicht null) --> nocopyright
	jp	po,testecpmversion; p=odd (gesetzt)? --> BC=0 --> Alle
	;			; character sind identisch --> go on
	inc	de
	jr	cloop


nocopyright::
	;
	ld	hl,m_nocopyright
	call	PRSTRfunc

exit::
	ld	a,(cpmvek)
	or	a,a		; sind CP/M Vektoren vorhanden ?
	jp	nz,wboot	; ja --> Warm boot
	;
	halt
	db 0ffH			; exit yaze

nocpmvektoren::
	; Sind keine CP/M Vektoeren vorhanden wird davon ausgegangen
	; Yaze laeuft.
	;
	ld	a,095h		; I/O-Byte setzen fuer yaze. Ist notwendig wenn
	ld	(3),a		; bootsys+cpm3.sys beim Start von yaze von der
				; Unix-file yaze-cpm3.boot gebootet wird.

	ld	hl,Dollar	; Dollarzeichen ersetzen
	ld	(hl),' '	; new line
	ld	hl,Copyright_message
	call	PRSTRfunc
	jp	testcopyright

testecpmversion::
	;
	ld	a,(cpmvek)	; Sind CP/M Vektoren vorhanden?
	or	a,a
	jr	z,loadcpm3	; NEIN --> gleich CP/M 3 laden

	; CP/M Version testen
	;
	ld	c,12		; Versionsnummer von CP/M holen
	call	bdos
	;
	ld	a,l
	and	a,030H		; nur oberer Teil beruecksichtigen
	cp	a,030H
	jr	nz,loadcpm3	; ungleich Version 3x --> CPM3.SYS sofort laden

	; wenn CP/M 3.1 bereits laeuft
	; XMOVE ueberpruefen
	;
	ld	de,(29-1)*3	; -1 wegen Warmboot, 29 ist Position von XMOVE
	ld	hl,(1)		; hole Adresse von Warmboot
	add	hl,de		; hl points to the xmove-jmp in bios-table
	inc	hl		; points to jmp addresse
	ld	a,(hl)
	inc	hl
	ld	h,(hl)
	ld	l,a		; hl points to the xmove routine itself
	;
	; now test if RET instruction is available for xmove-routine
	;
	ld	a,0C9H		; RET-Opcode
	cp	(hl)
	jp	nz,MoveToBank0	; NO RET --> xmove vorhanden --> ales nach bnk0
	;
	ld	hl,no_xmove_message
	call	PRSTRfunc
	jp	exit

	; XMOVE seems to be implement
	; --> try to move all to bank 0
	;
MoveToBank0:
	ld	hl,bank1_message
	call	PRSTRfunc
	;
	; Die folgenden Returnadressen muessen vor dem interbank Move auf
	; den (lokalen) Stack liegen, damit sie auch in bank 0 existieren !!!
	ld	hl,RET3
	push	hl		; Returnadresse RET3 on stack
	ld	hl,RET2
	push	hl		; Returnadresse RET2 on stack
	ld	hl,RET1
	push	hl		; Returnaddress RET1 on stack

	; I don't use the bdos func 50. I test it and it does not work.
	; I think that the banked part of the bdos are used when bdos func 50
	; is called. But when bootsys.com+cpm3.sys are moved from bank 1 to
	; bank 0 the banked part will be overwrite. So I use the bios function
	; XMOVE, MOVE and SELMEM direkt over the bios to transfere
	; bootsys+cpm3.sys to bank 0 and to switch to bank 0!
	; In the yaze bios the interbank transfer is realized in C outside the
	; Z80 memory and the short routines (HALT, funcno in yaze bios and ret)
	; are in the resident part of the bios, so all does work in yaze.
	;
	ld	de,(29-1)*3	; -1 wegen Warmboot, 29 ist Position von XMOVE
	ld	hl,(1)		; hole Adresse von Warmboot
	add	hl,de		; hl points to the xmove-routine in bios

	ld	bc,0001H	; B=bank 0 (dest),  C=bank 1 (source)
	jp	(hl)		; call XMOVE, return address (RET1) is on stack


RET1::	ld	de,(25-1)*3	; 25 is the position of MOVE
	ld	hl,(1)
	add	hl,de		; hl points to the move routine in bios
	push	hl		; --> to stack (will be called with ret)

	ld	ix,CPM3SYS	; dort beginnt CPM3.SYS
	ld	a,(ix+reslen)
	add	a,(ix+bnklen)	; Gesamtlaenge <reslen> + <bnklen> berechnen

	ld	bc,CPM3SYS	; dort beginnt CPM3.SYS
	add	a,b		; += Offset vom CPM3.SYS Binary
	ld	b,a		; BC = <reslen> + <bnklen> + Offset CPM3.SYS

	ld	hl,0100H	; destination (bank 0) fuer move
	ld	de,0100H	; source (bank 1)
	ret			; call MOVE in bios (address on stack)

RET2::	; now all is transfered to bank 0; now switch to bank 0
	; this must be do directly because the bdos func 50 does not allow
	; to use bios func 27. See page 3-72 BDOS function "direct bios calls"

	; ld	hl,RET3 	; siehe oben
	; push	hl

	ld	de,(27-1)*3	; offset fuer SELMEM
	ld	hl,(1)
	add	hl,de		; hl points to SELMEM

	xor	a,a		; a <- 0 (bank 0)
	jp	(hl)		; call SELMEM

RET3::	nop	; now this instruction will be executed in bank 0

	ld	hl,bank0_message
	call	PRSTRfunc

;---------------------------------------------------------------------------
; Hier beginnt der eigentliche Lader fuer CPM3.SYS.
;---------------------------------------------------------------------------
;
; The following information are from page 115 Appendix D of the System Guide
;

;
; CPM3.SYS File Format:
;
;	0	Header Record (128 bytes)
;	1	Print Record (128 bytes)
;	2-n	CP/M 3 operating System in reserved order, top down.
;

;
; Header Record Definitions:
;
; CPM3SYS (see at the end) ; Begin of CPM3.SYS
;
restop	equ	0	; Top page plus one, at which the resident partion
;			; of CP/M 3 is to be loaded top down.
;
reslen	equ	1	; Length in pages (256 bytes) of the resident
;			; portion of CP/M 3.
;
bnktop	equ	2	; Top page plus one, at wich the banked portion
;			; of CP/M 3 is to be loaded top down.
;
bnklen	equ	3	; Length in pages (256 bytes) of the banked
;			; portion of CP/M 3.
;
coldboot equ	4	; Address of CP/M 3 Cold Boot entry point
;
copyrmsg equ	16	; Copyright Message
;

;
; Print Record:
;
; The Print Rekord in the CP/M 3 Load Table is ASCII,
; terminated by a dollar sign ($).
;
prrecord equ	128	; position of the Print Record
;
;---------------------------------------------------------------------------
;
zwsp::		equ	80H
zwsptop::	equ	zwsp+127; letztes Byte in Zwischenspeicher
;
; Kleiner Hinweis:	Wenn ich von "oberen Bereich" spreche, so ist damit
;			der Teil gemeint der adressmaessig weiter oben liegt
;			Dasselbe analog bei der Bezeichnung "unterer Teil"
;
;
loadcpm3::
	ld	hl,loadmsg	; Text "Loading ..." ausgeben
	call	PRSTRfunc
	;
	ld	hl,CPM3SYS+prrecord	; Printrekord ausgeben
	call	PRSTRfunc

	; Zuerst den residenten Teil kopieren.
	; Im SPR-Format sind alle Sektoren Seiten-Buendig in 128 Byte
	; Bloecken (log Sektor) abgelegt. Die Ablage erfolgte jedoch
	; upside-down, d.H. der letzte Sektor wird zuerst gelesen.
	;

	ld	ix,CPM3SYS	; dort beginnt CPM3.SYS
	ld	d,(ix+restop)
	ld	e,0		; DE <- top of resident part (<restop>)
	ld	a,(ix+reslen)	; LEN of resident part (256 Byte Pages !!!)
	add	a,a		; *2 = Anzahl der log Sektoren (128 Byte)
				; Muss mal 2 genommen werden da in <reslen>
				; die Anzahl von 256 Byte Pages abgelegt ist.
	ld	hl,CPM3SYS+0100H ; Zeiger auf 2. Record (1. Binary of CP/M 3)

	call	transfer	; A=Bloecke von HL-->DE (upside down)


	; HL zeigt nun auf den Begin des banked Teils und somit
	; auf den erster log. Sektor des unteren Bereichs


	push	hl		; Fuer Berechnung auf Stack

	exx			; 2 Registersatz fuer oberen Bereich

	pop	hl		; HL <- zeiger auf banked part

	ld	a,(ix+bnklen)	; LEN of banked part (<bnklen>)
	or	a,a		; Ist die Laenge fuer banked part 0
	jr	z,bootcpm3	; ja --> nonbanked System sofort booten

	; Wenn nein, banked part laden
	;
	add	a,h		; oberste page berechnen
	ld	h,a		; HL zeigt genau hinter den obersten
	;			; Sektor (banked part)
	dec	hl		; hl zeigt auf das letzte Byte des
				; des obersten log. Sektors

	ld	a,(ix+bnklen)	; LEN of banked part (<bnklen> 256 Byte pages)
	;			; (zum zaehlen)
	;
	; Dieser Zaehler darf NICHT *2 genommen werden so wie oben.
	; Folgende Grafik verdeutlicht den Vorgang:
	;
	;
	;				+----------+<----<<--+
	;				| zwsp	   |-->>+    |
	;				+----------+	|    |
	;					     (3)|    |
	;						|    |
	;						|    |
	; Begin banked Bereich -------> +----------+<-<<+    |
	;  (unterer Bereich)	  ^	| page 0   |->>-+    |
	;			 256	+----------+	|    |(1)
	;			  |	| page 1   |	|    |
	;			-----	+----------+	|    |
	;				    ... 	|    |
	;				    ...      (2)|    |
	;				    ... 	|    |
	;				    ... 	|    |
	;				+----------+	|    |
	;				| page n-1 |	|    |
	;				+----------+<-<<+    |
	;  (oberer Bereich)		| page n   |----->>--+
	; End banked Bereich ---------> +----------+
	;
	;
	; Ein Austauschzyklus besteht aus den Schritten:
	;
	; (1) 128 Bytes vom oberen Bereich in den Zwischenspeicher
	; (2) 128 Bytes vom unteren Bereich in den oberen Bereich
	; (3) 128 Bytes vom Zwischenspeicher in den unteren Bereich
	; (4) Pointer unterer Bereich um +128 erhoehen
	;     Pointer oberer Bereich um -128 erniedrigen
	;     (Der Vorgang des Pointerverschiebens erfolgt automatisch
	;      ueber die Pointer der Befehle lddr und ldir.)
	;
	; Es werden also n/2 Vertauschungen gemacht und die Pointer treffen
	; sich genau in der Mitte (oberer Bereich wird mit dem unteren
	; Bereich spiegelverkehrt ausgetauscht). Wobei gilt <bnklen> = n/2.
	; Der Wert aus <bnklen> darf fuer den Austauschvorgang also
	; unveraendert benutzt werden, da hier genau die obere Haelfte mit
	; der unteren Haelfte spiegelverkehrt ausgetauscht wird.

;--------------

loop::
	; HL zeigt auf das letzte Byte des oberen Teils (updown)
	; oberen Teil in den scratch Bereich
	;
	ld	de,zwsptop	; letztes Byte des Zwischenspeichers
	ld	bc,128		; 128 Bytes
	lddr			; transferiere nach zwsp 128 Bytes rueckwaerts

	; hl zeigt auf letztes Byte des vorherigen Sektors

	push	hl		; fuer den naechsten move als zieladresse
				; absichern
	exx

	; HL zeigt auf die source vom unteren Teil (lowup)
	; unteren Bereich in den oberen Bereich verschieben
	;
	push	hl		; wird gleich nochmal benoetigt
	pop	iy		; ins IY
	;
	pop	de
	inc	de		; de zeigt auf ziel
	;
	ld	bc,128		; 128 Bytes
	ldir			; vorwaerts transferieren


	; vom scratchbereich transferieren

	push	iy		; hole pointer auf unteren Bereich (lowup)
	pop	de		; als destination

	ld	hl,zwsp		; scratch Bereich
	ld	bc,128		; 128 Bytes
	ldir

	; DE steht auf naechstem Sektor vom unteren Teil (lowup)

	ex	de,hl		; HL steht auf dem naechsten Sektor.
				; Wird fuer den naechsten move in HL
				; erwartet
	exx

	; Nun sind beide Sektoren vertauscht.
	; HL und HL' stehen jeweils auf den naechsten log Sektor:
	; HL' (um 128 Bytes hoeher)
	; HL  (um 128 Bytes niedriger)

	dec	a		; a--, ist letzter Sektor vertauscht?
	jr	nz,loop		; nein --> loop

;
; Damit liegt der code in der richtigen Reihenfolge vor. Jetzt muss er
; nur noch an die richtige Stelle transferiert werden und ins Bios
; eingesprungen werden.
;

	ld	hl,CPM3SYS+0100h ; Beginn vom CPM3 binary

	ld	a,(ix+reslen)
	add	a,(ix+bnklen)	; Gesammtlaenge <reslen> + <bnklen> berechnen

	add	a,h		; += Offset vom CPM3 Binary
	ld	h,a
	dec	hl		; HL steht auf dem letzten Byte des
				; banked Parts
	ld	d,(ix+bnktop)
	ld	e,0
	dec	de		; DE zeigt auf (<bnktop>*256)-1, also ein
				; Byte vor COMMON Memory

	ld	b,(ix+bnklen)
	ld	c,0		; BC beinhaltet die Laenge vom banked Bereich

	lddr			; und transferieren von oben her


; Damit sollte der Code in der richtigen Reihenfolge an der richtigen
; Position stehen.
; Jetzt CP/M 3 booten (BIOS einspringen):

bootcpm3::
	ld	l,(ix+coldboot)
	ld	h,(ix+coldboot+1)

	jp	(hl)		; und CP/M 3.1 starten


;--------------------------------------------------------------------------
; subroutines
;
; transfere: transferiert up side down den residenten Bereich
transfer::

	;
	; Uebertragen von <A> log. Sektoren (128 Byte Bloecken) von
	; HL-->DE. (upside down)
	;

	ex	de,hl		; Ziel nach HL
	ld	bc,-80H
	add	hl,bc		;
	ex	de,hl		; erster Block
trans1::ld	bc,80H		;  Laenge
	ldir			; uebertragen von HL-->DE
	dec	d		;  Ziel - 100H
	dec	a		; Zaehler
	jr	nz,trans1
	ret

.comment #
callbios::
	ld	(func),a	; bios function number
	;
	ex	af,af'
	ld	(Areg),a
	ld	(BCref),bc
	ld	(DEreg),de
	ld	(HLreg),hl
	;
	ld	de,biospb
	ld	c,50		; Bios func
	call	bdos		; return Address on Stack
	ret

biospb::
func::	db	0
Areg::	db	0
BCref::	dw	0
DEreg::	dw	0
HLreg::	dw	0

;#

PRSTRfunc::
	ld	a,(HL)
	cp	a,'$'
	ret	z		; if ch='$' --> return
	;
	push	hl		; HL wird von conout veraendert
	ld	c,a
	call	conout		; call conout in CP/M-bios or yaze-bios direkt
	pop	hl
	;
	inc	hl		; pointer to the next char
	jr	PRSTRfunc

conout::
	ld	a,(cpmvek)
	or	a,a		; existieren CP/M Vektoren
	jr	z,yaze_conout	; NEIN --> direkt Yaze-conout benutzen

	; conout im Bios aufrufen
	;
	ld	hl,(wbvektor)
	ld	de,3*3		; ab wboot der 3. Vektor
	add	hl,de
	jp	(hl)		; Returnadresse ist auf dem Stack

yaze_conout::
	halt
	db 04			; call conout im yaze-bios
	ret

;--------------------------------------------------------------------------

	; Die folgende Zeile darf nicht veraendert werden, denn
	; dieser wird zum Ueberpruefen der Copyright Message in CPM3.SYS
	; verwendet
CopyDRI::db	'Copyright (C) 1982, Digital Research'
CDRIlen	equ	$

no_xmove_message::
	db	0DH,0AH,' Your CP/M 3.1 bios does not support XMOVE'
	db	' (xmove is not implement) !'
	db	0DH,0AH,0AH
	db	' CP/M 3.1 is running.',0dh,0ah
	db	' To load and start CP/M 3.1 from this file'
	db	' you must run CP/M 2.2 before.',0dh,0ah,0ah
	db	'$'

loadmsg::db	0DH,0AH,' Loading CP/M 3.1 ...',0DH,0AH,'$'

bank1_message::
	db	0dh,0ah
	db	' CP/M 3.1 is already running and bank 1 is selected!'
	db	' Now move all to bank 0 ...',0dh,0ah
	db	'$'

bank0_message::
	db	0dh,0ah
	db	' Now all is transfered and bank 0 is selected'
	db	' -> CPM3.SYS will be loaded. :-)'
	db	0dh,0ah
	db	' (this message comes after a switch from bank 1 to bank 0)'
	db	0dh,0ah
	db	'$'

m_nocopyright::
	db	0DH,0AH
	db	' NO signature found. It seems there is no CPM3.SYS attached.'
	db	13,10
	db	' Use  PIP cpm3.com=bootsys.com,CPM3.SYS  to attach a'
	db	' CPM3.SYS.',0dh,0ah,0ah
	db	'$'

cpmvek::	db	0	; Muss 0 sein, wird so erwartet !!!
wbvektor::	dw	0	; Adresse des Warmboots

	ds	10,0AAH		; initialized is better
lstack:: equ	$		; for the next instruction


CPM3SYS::	equ	($ AND 0FF80H) + 0080H
	; damit faengt CPM3.SYS auf einer 80H Pagegrenze an


	end