title Geisterfahrer name ('GEISTF') ; Die disassemblierte Version des Spieles Geisterfahrer. ; Urspruenglich geschrieben fuer den KC 85 entry $memry FALSE equ 0 TRUE equ NOT FALSE PCW equ TRUE ; Auf FALSE bei anderer Maschine OS equ 0000h BDOS equ 0005h TPATOP equ BDOS+1 _CIN equ 0fdh _CST equ 0feh .condir equ 6 .tod equ 105 null equ 00h bell equ 07h esc equ 1bh Pfosten equ '.' ; Darstellung des Begrenzungspfostens CUP equ 'E' ; Cursor hoch CDN equ 'X' ; Cursor runter _TEASK equ 00bfh ; PCW Xtended BIOS Funktion _USER equ 30 ; BIOS Xtended BIOS Funktion ; ; ############################################ ; ### Das Spiel Geisterfahrer started hier ### ; ############################################ ; ld sp,(TPATOP) ; Stapelspeicher laden call Randinit ; Zufallszahl initiieren call CurAus ; Cursor ausschalten ld a,bell call Konsole ; Klingeln ld a,bell call Konsole call Schirmfeld ; Darstellungsfeld bestimmen ; ; Hier beginnt eine Runde ; SpielRunde: call Clscreen ; Bildschirm loeschen ld hl,24 ld (Laufzaehler),hl; Lauf voreinstellen ld hl,100 ld (WagenVerz),hl ; Startverzoegerung voreinstellen ld a,'0' ld (km$stand),a ; Kilometerstand auf Null ld (km$stand+2),a ld a,8 ld (EigenY),a ; Startzeile fuer eigene Wagen ld a,5 ; Startzeile fuer linken Pfosten ld (PfostenStand),a ld a,35 ; Startzeile fuer rechten Pfosten ld (PfostenStand+1),a ld ix,WDaten+WagLn xor a ld b,WagMax ld de,WagLn WagenDeaktivieren: ld (ix+_wN),a ; Wagenauswahl auf Null setzen ld (ix+_wN+1),a ; (Entgegenkommende Wagen loeschen) add ix,de djnz WagenDeaktivieren ld hl,0 ld (LaufAktuell),hl; Aktuellen Laufzaehler voreinstellen ld de,8 ld hl,1 call FeldXY ; Cursor setzen ld hl,$Titel call PrStrg ; Ueberschrift anzeigen ld hl,4 call Begrenzung ; Begrenzung auf obere Reihe ld hl,13 call Begrenzung ; Begrenzung auf untere Reihe call SetzeWagen ; Wagen positionieren SpielLauf: ld a,(LaufAktuell) rra ; Test ob Lauf ungerade call c,SetzePfosten ; Bewegung erzeugen falls ja ld hl,(LaufAktuell) ld a,l or h ; Test ob Laufzaehler Null jr nz,NichtNeu WagenFalsch: ld c,15 call Random ; Zufallswert 0..15 holen ld (FremdWert),a ; Zufallszahl fuer fremden Wagen erzeugen ld hl,FremdNummer ld bc,FrmLen cpir ; Bereich testen jr nz,WagenFalsch ; Nuer Versuch ld b,00000001b ld c,5 call PosWagen ; Wagen positionieren ld b,00000010b ld c,7 call PosWagen ld b,00000100b ld c,9 call PosWagen ld b,00001000b ld c,11 call PosWagen NichtNeu: call WagenAusgeben ; Entgegenkommende Wagen ausgeben call Konstat ; Test ob Taste betaetigt ld (Zeichen),a ; Anzeigen, dass keine Taste betaetigt war call nz,MichBewegen ; Eigenen Wagen positionieren wenn eine betaetigt call Zusammenstoss ; Test ob Zusammenstoss ld hl,(LaufAktuell) inc hl ld (LaufAktuell),hl ; Laufzaehler erhoehen ld de,(Laufzaehler) or a sbc hl,de ; Test ob Zielzaehler erreicht jp nz,KMnicht.voll ld (LaufAktuell),hl ; Laufzaehler ruecksetzen falls ja ld hl,(WagenVerz) ld de,5 or a sbc hl,de ; Verzoegerung runterzaehlen jr nc,VerzOk ld hl,0 ; Wert auf Null falls Unterlauf VerzOk: ld (WagenVerz),hl call inc.km ; Kilometer hochzaehlen ld de,26 ld hl,15 call FeldXY ; Cursor fuer km-Ausgabe setzen ld a,bell call Konsole ld a,(km$stand) ; Letzten Kilometerstand eintragen ld (km$crash),a ld a,(km$stand+2) ld (km$crash+2),a ld hl,km$stand call PrStrg ; Kilometerstand ausgeben ld a,(km$stand+2) ; Kilometerstand laden cp '0' ; Test ob auf vollen Bereich jr nz,KMnicht.voll ; Nein ld a,(km$stand) cp '2' ; Test ob im Bereich 2 bis 5 km jr c,KMnicht.voll cp '5'+1 jr nc,KMnicht.voll ld hl,(Laufzaehler) dec hl ld (Laufzaehler),hl ; Laufzaehler runter KMnicht.voll: ld de,1 ld hl,15 call FeldXY ; Cursor setzen ld hl,(WagenVerz) call Delay ; Verzoegerung ld a,(Zeichen) ; Letztes Steuerzeichen laden cp 'C'-'@' ; Test ob Abbruch jr z,Spielende cp 'N' ; Test ob Ende jr z,Spielende cp 'J' ; Test ob nochmal jp nz,SpielLauf ; Dann weiter jp SpielRunde ; Sonst nochmal ; ; !!!!!!!!!!!!!!!!! ; !!! Spielende !!! ; !!!!!!!!!!!!!!!!! ; Spielende: ld de,60 ld hl,15 call FeldXY ; Cursor setzen call CurEin ; Cursor einschalten jp OS ; Ende ; ; Cursor auf Bildschirmbereich positionieren ; Relativ zur Bildschirmgroesse ; FeldXY: ld a,'Y' call esc.out ; Kontrollzeichen fier Sequenz ld ix,OffsetXY ; Auf Bereich zeigen ld a,l add a,(ix+1) ; Offset fuer Hoehe addieren call put.XY ; Ausgabe ld a,e add a,(ix+0) ; Offset fuer Breite addieren put.XY: add a,' '-1 ; Offset fuer PCW Sequenz call Konsole ret ; ; Bewegung erzeugen - zwei Pfosten bewegen ; SetzePfosten: ld hl,PfostenStand ; Ersten Pfosten setzen call EinPfosten ld hl,PfostenStand+1; Zweiten Pfosten setzen EinPfosten: dec (hl) ; Pfostenposition herunterzaehlen jr nz,PfostenNeu ; Noch nicht null push hl ld (hl),60 ; Wert neu falls null ld hl,3 call LoeschePfosten ; Oberen Pfosten loeschen ld hl,14 call LoeschePfosten ; Unteren Pfosten loeschen pop hl PfostenNeu: ld e,(hl) ; Spalte fuer Pfosten holen push de ld hl,3 call NeuerPfosten ; Neuen oberen Pfosten anzeigen, alten Pfosten loeschen pop de ld hl,14 ; Jetzt unteren Pfosten NeuerPfosten: call FeldXY ; Cursor fuer jeweiligen Pfosten setzen ld hl,$Pfosten call PrStrg ; Neuen Pfosten anzeigen, alten Pfosten loeschen ret ; ; Pfosten loeschen ; LoeschePfosten: ld de,1 call FeldXY ; Cursor fuer jeweiligen Pfosten setzen ld a,' ' call Konsole ; Pfosten loeschen ret ; ; Entgegenkommende Wagen ausgeben ; WagenAusgeben: ld hl,WDaten+WagLn ld (WagenZgr),hl ; Startwagen setzen ld b,WagMax ; Anzahl moeglicher Wagen WAloop: push bc call WAanzeige ; Einen Wagen ausgeben ld hl,(WagenZgr) ld de,WagLn add hl,de ; Zeiger auf naechsten Wagen ld (WagenZgr),hl pop bc djnz WAloop ret ; ; Einen Wagen ausgeben ; ; Die Position eines Wagens beginnt bei Spalte 60 und verlaeuft nach links ; bis Position -9: bei einer Wagenlaenge von 10 ist der Wagen dann aus ; dem Sichtbereich. ; ; Damit ergibt sich fuer die Darstellung der Wagen: ; ; Feld- Wagen- ; Position Position Startposition Laenge ; ix+_w AktX WagenPos WagenLen ; -------- -------- ------------- -------- ; 60..52 60..52 1 1..9 ; 51.. 1 51.. 1 1 10 ; 0..-8 1 2..10 10 ; -9 1 11 10 ; WAanzeige: ld ix,(WagenZgr) ld a,(ix+_wN) or (ix+_wN+1) ; Test ob Wagen aktiv ret z ; Ende falls nicht ld l,(ix+_wX) ; Spalte fuer Wagen laden ld h,(ix+_wX+1) dec hl bit 7,h ; Test 0..-9 inc hl jr z,WAX.ok ; Ist 60...1 ld hl,1 ; Sonst auf linke Position WAX.ok: ld (AktX),hl ; Startposition setzen ld e,(ix+_wX) ; Spalte fuer Wagen laden ld d,(ix+_wX+1) dec de dec de bit 7,d ; Test 60..1 ld hl,1 jr z,WAP.ok ; Ist 60...1 or a sbc hl,de ; 0..-9 -> 2..11 WAP.ok: ld (WagenPos),hl ; Startposition fuer Wagenbild ld iy,AktX ld a,(iy+_wX) ; Spalte fuer Wagen laden ld b,10 cp 52 ; Test ganze Laenge ld a,b jr c,WAL.max ; Ja ld a,61 sub (iy+_wX) WAL.max: ld (WagenLen),a ; Laenge setzen ld bc,0 call FremdWagen ; Oberen Teil positionieren ld ix,(WagenZgr) ld e,(ix+_wN) ; Wagenindex laden ld d,(ix+_wN+1) ld hl,WagenTabelle-2 add hl,de add hl,de ; Wagen in Tabelle positionieren ld e,(hl) ; Adresse auf oberen Teil laden inc hl ld d,(hl) ex de,hl push hl ld bc,0 call Wagenbild ; Oberen Teil des Wagens ausgeben ld bc,1 call FremdWagen ; Unteren Teil positionieren pop hl ld bc,FrmdWgL call Wagenbild ; Unteren Teil des Wagens ausgeben ld ix,(WagenZgr) ld l,(ix+_wX) ld h,(ix+_wX+1) dec hl ; Wagen auf vorige Spalte setzen - Bewegung nach links ld (ix+_wX),l ld (ix+_wX+1),h ld de,10 add hl,de ; Test ob Wagen aus dem Bild ld a,l or h ret nz ; Noch nicht xor a ld (ix+_wN),a ; Wagen ausser Sichtweite ld (ix+_wN+1),a ; Deshalb deaktiviern ret ; ; Fremden Wagen ausgeben ; Wagenbild: add hl,bc ; Eventuell auf unteren Teil ld de,($memry) push de ld bc,FrmdWgL ldir ; Kompletten Wagen kopieren ld b,FrmdWgL ld a,' ' WagenLeer: ld (de),a ; Leerzeichen anfuegen inc de djnz WagenLeer pop hl ld de,(WagenPos) add hl,de ; Auf Teilstueck setzen dec hl ld a,(WagenLen) ld (hl),a ; Laenge setzen call PrLStrg ; Einen Teil ausgeben ret ; ; Fremden Wagen positionieren ; FremdWagen: ld de,(AktX) ; Wagenposition laden ld ix,(WagenZgr) ld l,(ix+_wY) ; Zeile laden ld h,(ix+_wY+1) add hl,bc ; Eventuell auf unteren Teil call FeldXY ; Einen Teil positionieren ret ; ; Eigenen Wagen positionieren ; MichBewegen: call ZeichenLesen ; Zeichen lesen ld hl,EigenY cp CUP ; Test Cursor hoch jr nz,Runter? ld a,(hl) dec (hl) ; Cursor setzen inc a ; Cursor auf unteren Teil jr TeilLoeschen ; Unteren Teil loeschen Runter?: cp CDN ; Test cursor runter jr nz,KeineTaste ld a,(hl) inc (hl) ; Cursor setzen TeilLoeschen: ld de,(EigenX) ; Spalte des eigenen Wagens ld l,a ; Und Zeile call FeldXY ; Cursor positionieren ld hl,$Leeren call PrStrg ; Wagenteil loeschen KeineTaste: call SetzeWagen ; Wagen neu setzen ret ; ; Wage positionieren wenn uebernommen ; PosWagen: ld a,(FremdWert) and b ; Zahl maskieren ret z ; Bit nicht enthalten ld ix,WDaten+WagMax*WagLn ld b,WagMax+1 ; Fremden Wagen eintragen ld de,-WagLn SucheWagen: ld a,(ix+_wN) ; Test ob Wagen frei or (ix+_wN+1) jr z,WagenFrei ; Dann initialisieren add ix,de ; Sonst auf vorigen Eintrag djnz SucheWagen ret WagenFrei: ld (ix+_wX),60 ; Spalte setzen ld (ix+_wX+1),a ; Akku ist Null! ld (ix+_wY),c ; Reihe setzen ld (ix+_wY+1),a push ix ld c,6 call Random ; Zufallswert 0..5 holen inc a ; Auf 1..6 aendern pop ix ld (ix+_wN),a ; Wagen auswaehlen ld (ix+_wN+1),0 ret ; ; Test ob Zusammenstoss ; Zusammenstoss: ld hl,WDaten+WagLn ld (WagenZgr),hl ; Startwagen setzen ld b,WagMax ; Anzahl moeglicher Wagen ZSloop: push bc call Gekracht ; Einen Wagen testen push af ld hl,(WagenZgr) ld de,2*3 add hl,de ; Zeiger auf naechsten Wagen ld (WagenZgr),hl pop af pop bc ret c ; Zusammenstoss djnz ZSloop ret ; ; Einen Wagen auf Zusammenstoss testen ; Carry wird gesetzt bei einem Zusammenstoss ; Gekracht: ld a,(EigenY) ; Zeile des eigenen Wagens laden cp 4 ; Test ob an oberer Begrenzung jr z,WagenGetroffen cp 12 ; Test ob an unterer Begrenzung jr z,WagenGetroffen; Dann Zusammenstoss ld ix,(WagenZgr) ld a,(ix+_wN) ; Test ob Wagen aktiv or (ix+_wN+1) ret z ; Kein Zusammenstoss falls nicht ld l,(ix+_wX) ld h,(ix+_wX+1) ld a,(EigenX) sub 9 ld e,a sbc a,a ld d,a ld bc,17 or a sbc hl,de ; Test ob Zusammenstoss moeglich or a sbc hl,bc ret nc ; Nein ld l,(ix+_wY) ld h,(ix+_wY+1) ld de,(EigenY) ld d,0 dec de ld bc,3 or a sbc hl,de ; Test ob Zusammenstoss erfolgt or a sbc hl,bc ret nc ; Nein WagenGetroffen: ld de,12 ld hl,15 call FeldXY ; Meldung platzieren ld hl,$Crash call PrStrg ; Kilometerstand ausgeben call CurEin ; Cursor einschalten ld b,10 Alarm: push bc ld a,bell call Konsole ; Klingeln ld hl,50 call Delay ; Verzoegerung pop bc djnz Alarm ; Schleife Quittung: call ZeichenLesen ; Auswahl einlesen cp 'J' jr z,Ende.nachKrach; Test ob ok cp 'N' jr nz,Quittung Ende.nachKrach: call Konsole ; Zeichen ausgeben call CurAus ; Cursor ausschalten scf ; Zusammenstoss anzeigen ret ; ; Begrenzung auf Zeile in HL ausgeben ; Begrenzung: ld de,1 call FeldXY ; Cursor auf untere oder obere Reihe ld b,60 GrenzLinie: push bc ld a,'=' call Konsole ; Begrenzung ausgeben pop bc djnz GrenzLinie ret ; ; Eigenen Wagen positionieren ; SetzeWagen: ld de,(EigenX) ; Spalte des eigenen Wagens ld hl,(EigenY) call FeldXY ; Cursor auf eigenen Wagen setzen ld hl,$MeinWagen call PrStrg ; Oberen Teil ausgeben ld de,(EigenX) ; Spalte des eigenen Wagens ld hl,(EigenY) inc l call FeldXY ld hl,$MeinWagen+MeinWgL call PrStrg ; Unteren Teil ausgeben ret ; ; Test ob Taste betaetigt - NZ bedeutet Taste betaetigt ; Konstat: ld a,_CST call Konsole ; Status holen or a ; Z bedeutet keine Taste ret ; ; Zeichen als Grossbuchstaben lesen ; ZeichenLesen: ld a,_CIN call Konsole ; Zeichen holen res 5,a ; Als Grossbuchstaben ld (Zeichen),a ret ; ; Bildschirm loeschen ; Clscreen: ld a,'H' call esc.out ; PCW Sequenzen ausgeben ld a,'E' jr esc.out ; ; Cursor einschalten ; CurEin: ld a,'e' jr esc.out ; PCW Sequenzen ausgeben ; ; Cursor ausschalten ; CurAus: ld a,'f' ; PCW Sequenzen ausgeben esc.out: push af ld a,esc call Konsole ; ESCape ausgeben pop af ; Dann das Kontrollzeichen ; ; Konsol-Ein/Ausgabe - Akku steuert die Funktion ; Konsole: push bc push de push hl ld e,a ; Zeichen oder Code laden ld c,.condir call BDOS ; Zeichen ausgeben oder holen pop hl pop de pop bc ret ; ; Kilometer hochzaehlen ; inc.km: ld hl,km$stand+2 call incasc ; Nachkommastelle erhoehen ret nz ; Noch kein Ueberlauf dec hl dec hl incasc: inc (hl) ; Ziffer erhoehen ld a,(hl) cp '9'+1 ; Test ob Ueberlauf ret nz ; Nein ld (hl),'0' ; Sonst ruecksetzen ret ; $Pfosten: db Pfosten,' ',null $Leeren: db ' ',null $Crash: db 'CRASH bei ' km$crash: db '0.0 km. Nochmal? (J/N) ',null $Titel: db 'GEISTERFAHRER - bis 1.5 km ist Dummheit !!! ',null km$stand: db '0.0 km',null EigenX: db 20 ; Spalte des eigenen Wagens EigenY: ds 1 ; Zeile des eigenen Wagens ; CPU.MHz: db 3 ; CPU Geschwindigkeit ; $MeinWagen: db '_/~~\___.',null db '',null MeinWg equ $-$MeinWagen MeinWgL equ MeinWg / 2 ; $Wagen1: db 10,' __/&&\_ ' db 10,'"-o---o- ' FrmdWg equ $-$Wagen1 FrmdWgL equ FrmdWg / 2 ; $Wagen2: db 10,' /&|||||| ' db 10,'<-o----o> ' $Wagen3: db 10,' _/&|ooo_ ' db 10,'<=o===o=+ ' $Wagen4: db 10,' _i_/&\ ' db 10,' |o===O+` ' $Wagen5: db 10,' __/&&&\ ' db 10,'<-o----o` ' $Wagen6: db 10,' ___/&\__ ' db 10,'<-o---o-+ ' WagenTabelle: dw $Wagen1,$Wagen2,$Wagen3,$Wagen4,$Wagen5,$Wagen6 ; FremdNummer: db 3,5,6,7,9,10,11,12,13,14 FrmLen equ $-FremdNummer ; OffsetXY: ds 2 ; Aktive Spielfeldgroesse FremdWert: ds 1 ; Zeilenposition eines fremden Wagens WagenLen: ds 2 ; Laenge des fremden Wagens WagenPos: ds 2 ; Startposition fuer fremden Wagen AktX: ds 2 ; Spalte fuer fremden Wagen LaufAktuell: ds 2 ; Aktueller Laufzaehler WagenVerz: ds 2 ; Verzoegerung fuer Wagenausgabe Laufzaehler: ds 2 ; Laufzaehler Zeichen: ds 1 ; Aktuelles Zeichen PfostenStand: ds 1+1 ; Stand der Pfosten ; ; Daten des Gegenverkehrs ; WagMax equ 9 _wX equ 0 _wY equ 2 _wN equ 4 WDaten: ds 2*(WagMax+2) ; Aktuelle Spalte ds 2*(WagMax+2) ; Aktuelle Zeile ds 2*(WagMax+2) ; Wagennummer - 0 falls nicht aktiv WagData equ $-WDaten WagLn equ WagData / (WagMax+2) $memry: ds 2 WagenZgr: ds 2 ; Zeigen auf aktuellen Wagen ; ; Verzoegerung mit Wert in Regster HL ; Delay: ld a,l or h ; Test ob ein Wert ungleich Null ret z ; Nein, Ende ld a,(CPU.MHz) ; CPU Geschwindigkeit laden add a,a add a,a add a,a ; Verzoegerungswert ermitteln DelLoop: ex (sp),hl ; 5 Zyklen ex (sp),hl ; 10 Zyklen ex (sp),hl ; 15 Zyklen ex (sp),hl ; 20 Zyklen push bc ; 23 Zyklen ld bc,1234 ; 26 Zyklen pop bc ; 29 Zyklen dec a ; 30 Zyklen jr nz,DelLoop dec hl jr Delay ; ; Zufallszahl initiieren - hier ueber die Zeitfunktion von CP/M 3 ; Randinit: ld de,Zufallszahl-2; Zeitblock laden ld c,.tod call BDOS ; Zeitstempel holen ld (Zufallszahl+1),a; Wert ablegen ld c,255 call Random ; Erste Zufallszahl ld (Zufallszahl),a ; Ergebnis speichern ret ; ; Block fuer Zufallszahl ; ds 2 ; Jahr Zufallszahl: ds 1 ; Std ds 1 ; Min ; ; Zufallswert 0..C erzeugen ; Random: ld hl,Zufallszahl ld a,(hl) ; Zahl erzeugen rlca xor (hl) rrca inc hl inc (hl) add a,(hl) jp pe,Zufall1 inc (hl) Zufall1: dec hl ld (hl),a and 00001111b ; Maximal 15 in diesem Spiel cp c ; Test, ob im Bereich jr nc,Random ret ; ; Meldung mit Null am Ende ausgeben ; PrStrg: ld a,(hl) or a ; Test ob Ende ret z ; Ja call Konsole ; Zeichen ausgeben inc hl jr PrStrg ; ; Meldung mit Laenge vorweg ausgeben ausgeben ; PrLStrg: ld b,(hl) ; Laenge der Stringvariablen inc hl inc b PrLoop: dec b ret z ld a,(hl) push bc push hl call Konsole ; Type digits pop hl pop bc inc hl jr PrLoop ; ; Darstellungsfeld fuer PCW bestimmen ; ; Im Modus "24x80 ON" sind das 24 und 80 ; Im Modus "24x80 OFF" sind das 31 und 90 ; Schirmfeld: IF PCW ; ; PCW bestimmt Werte dynamisch ; call XBIOS ; Ueber Xtended BIOS Werte holen dw _TEASK ld a,d sub 14-1 srl a ; Nutzbare Bildschirmhoehe berechnen ld (OffsetXY+1),a ld a,e sub 62-1 srl a ; Nutzbare Bildschirmbreite berechnen ld (OffsetXY),a ret ; ; Aufruf auf XBIOS-Adresse ; XBIOS: ld hl,(OS+1) ; BIOS-Tabellenadress laden ld de,3*(_USER-1) add hl,de ; XBIOS-Adresse berechnen jp (hl) ; Ausfuehren ELSE ; ; Bei anderen Maschinen ist 24x80 voreingestellt ; ld a,($$Y$$) sub 14 srl a ; Nutzbare Bildschirmhoehe berechnen ld (OffsetXY+1),a ld a,($$X$$) sub 62 srl a ; Nutzbare Bildschirmbreite berechnen ld (OffsetXY),a ret ; $$X$$: db 80 ; Bildschirmweite $$Y$$: db 24 ; Bildschirmhoehe ENDIF end