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