DisplayList für Neueinsteiger -1

← Zurück zum Forum

DisplayList #1

Wenn man (so wie ich damals in den 80-igern) Programme in einer Hochsprache entwickelt - damals auf dem Atari 800 z.B. in BASIC, dann ist man nicht unbedingt darauf angewiesen ein tieferes Verständnis der grafischen Bildschirmausgabe zu besitzen. Das BASIC hat bereits entsprechende Befehle integriert und übersetzt am Ende alles in den entsprechenden Maschinencode. So ist es dann einfach z.B. eine „Hallo Welt”- Ausgabe zu programmieren:

10 Print „Hallo Welt”
20 Goto 10

Ein entsprechender Print-Befehl existiert in Assembler nicht und wenn man etwas auf den Bildschirm bekommen möchte, dann stößt man recht bald unweigerlich auf die DisplayList. So erging es auch mir! Aber was genau passiert denn da? Der Name zunächst ist völlig irreführend, denn es handelt sich dabei gar nicht um eine Bildschirm Liste, sondern um Programmanweisungen eines ganz eigenen Prozessors - dem ANTIC. Vielfach gehört, aber wie genau erzeugt dieser Chip das Bild und wie kann ich das beeinflussen und steuern?

Die Idee eines solchen Prozessors war zwar damals nicht neu, für den Heimcomputerbereich aber schon fast revolutionär. Der ANTIC liest dabei Daten aus dem Hauptspeicher, verarbeitet sie und baut dann entsprechend den Bildschirm auf. Zusätzlich sorgt der ANTIC-Prozessor auch für die Generierung der Player-/Missile Grafik. Der Antik kann nur lesen und keine Daten in den Prozessor zurückschreiben, um dieses Problem zu lösen gibt es die Schattenregister…

Die Befehle, die der Anti verarbeiten kann sind eigentlich recht simpel. Es gibt einen Vectorbefehl, wo angegeben ist, wo der Bildschirm anfängt und einen Sprungbefehl, der saget, wo die Displaylist wieder startet. Alle anderen Displaylisteinträge sind keine Befehle im eigentlichen Sinn, sondern stellen nur Werte dar, wie die einzelnen Zeilen aufgebaut werden.

Wenn man jetzt weiß, an welcher Speicherstelle der Bildschirm bei Graphics 0 (in Basic) anfängt, kann man relativ einfach durch ein Poke-Befehl im Basic oder durch LDA / STA in Assembler eine Ausgabe auf den Bildschirm machen: Zum Experimentieren empfehle ich das in Basic, das kann man dann relativ einfach in Assembler umsetzen:

Poke 40000,33 läßt links oben ein A entstehen, denn dort fängt der Bildschirm im Speicher an… und dieser Anfang wird in der DL festgelegt!

In den Speicherstellen 560 (LoBi) und 561 (HiBi) steht die Adresse des DL: Print Peek(560) 32 Print Peek(561) 156 Print 32+256*156 39968

Am Beispiel von Graphics 0 fängt die DL also an der Stelle 39968 im Hauptspeicher an. Hier geht es los mit drei Einträgen 112 ? Peek(39968), Peek(39969), Peek(39970) 112 112 112 Das sind die drei schwarzen Zeilen am oberen Rand, die einfach leer sind. Poke 39968,2 würde eine leere Textzeile am Oberrad erzeugen (die man aber nicht beschreiben kann).

An Stelle 4 des DL (also bei Speicherstelle 39971 kommt ein erster echter Befehl: 64 - dieser benennt die Adresse des Bildschirmstartes mit den zwei folgenden Stellen (Stelle 5 und 6): Peek(39972) liefert 64 und Peek(39973) liefert 156

64+256*156 ergibt 40000 - also die Startadresse des Bildschirmes. Will man also die Startadresse des Bildschirmes ändern (wird z.B. scrollen wichtig), dann ändert man einfach diese beiden Adressen entsprechend!

Danach folgen 23 x die Speichereinträge 2 - diese legen letztlich den Textmodus für Graphics 0 fest und danach kommt der Sprungbefehl (65) mit den nachfolgenden beiden Speichereinträgen, die festlegen wohin gesprungen werden soll - nämlich wieder zum Anfang der DL, die ja ständig wiederholt abgearbeitet werden muss. Warum nur 23x2 ? Wir haben ja 24 Zeilen! Das steckt witzigerweise in Stelle 4 der DL im Befehl mit drin. Der Befehl an Stelle 4 der DL ist eigentlich Dezimal 64, in der Stelle 4 steht aber 66. Hier wird einfach schon die erste zu generierende Zeile zum Befehl addiert. 64+2=66 Ich denke, die Atari-Entwickler wollten damit ein wenig Verwirrung stiften 😉

Das wars eigentlich schon, was den Aufbau der DL angeht. Im Anhang noch eine schöne Übersicht über die DL bei Graphic 0.

In Teil 2 wird man dann erfahren, was es für Auswirkungen haben kann die DL zu verändern, wie flexibel man alles mischen kann und wie man einfach eigene Grafikmodi erstellen kann.

Anhänge:

Hier noch kurz mein Listing für "Hallo Welt":

Basic:

10 Print "Hallo Welt"
20 Goto 10

Assembler:

10 ;Hallo Welt Demo einfach
20      *= $4000
30      LDA #40
40      STA $9C40
50      LDA #33
60      STA $9C41
70      LDA #44
80      STA $9C42
90      STA $9C43
100     LDA #47
110     STA $9C44
120     LDA #55
130     STA $9C46
140     LDA #37
150     STA $9C47
160     LDA #44
170     STA $9C48
180     LDA #52
190     STA $9C49
200     LDA #00
210     STA $9C45
220     STA $9C4A

Dieser Assemblercode ist hier bewusst ganz einfach gewählt! Etwas komplexer wird es dann hier:

10 ;Hallo Welt Demo Komplex
20      .OPT OBJ
30      *= $4000
40 RET = $9B
50 IOCB = $0340
60 CIOV = $E456
70 DEVICE .BYTE "E:",RET
80 TEXT .BYTE "HALLO WELT!",RET
90      LDX #$20
100     LDA #$03
110     STA $0342,X
120     LDA #DEVICE&255
130     STA $0344,X
140     LDA #DEVICE/256
150     STA $0345,X
160     LDA #$08
170     STA $034A,X
180     LDA #$00
190     STA $034B,X
200     JSR CIOV
210     LDA #$09
220     STA $0342,X
230     LDA #TEXT&255
240     STA $0344,X
250     LDA #TEXT/256
260     STA $0345,X
270     LDA #$00
280     STA $0348,X
290     LDA #$FF
300     STA $0349,X
310     LDA #$FF
320     STA $02FC
330 LP  JSR CIOV
340     LDA $02FC
350     CMP #$FF
360     BNE STOP
370     JMP LP
380 STOP    BRK
390     .END

Das Programm startet nicht bei 4000, sondern bei 400F !! Man sieht an diesem Beispiel wie komplex die Programmierung einfacher Anwendungen in Assembler werden kann. Zur Ausgabe von Hallo Welt wurde zudem in diesem Beispiel die CIOV des Ataris benutzt, man könnte den Text natürlich auch direkt auf den Bildschirm ausgeben - was allerdings in diesem Fall etwas weniger elegant ist. Dies würde dann den Text "Hallo Welt!" quasi Buchstabe für Buchstabe zerlegen und dann in den Bildschirmspeicher direkt kopieren - ähnlich wie im ersten etwas plumpen Listing.

Prodehl schrieb: Leider macht dieser Editor alle Tabs weg, die müssen natürlich im Assembler rein!

Umschliesse Quellcode mit [ code ] [ /code ] Bloecken (nur ohne die Leerzeichen hinter [ und ]), z.B.:

10 ;Hallo Welt Demo einfach
20      *= $4000
30      LDA #40
40      STA $9C40
50      LDA #33
60      STA $9C41
70      LDA #44
80      STA $9C42
90      STA $9C43
[...]

Dann sieht es besser aus ;)

Prodehl schrieb: Wenn man dies jetzt per copy/paste in den Assembler überträgt, dann werden die Anführungszeichen nicht mitkopiert ...

In deinen Beispielen sind typografische Anfuehrungszeichen, wie sie von Textverarbeitungen erzeugt werden. Die kennt der Atari nicht, daher verschwinden die. Ggf. oben im Post durch echte Anfuehrungszeichen "" ersetzen.

https://mastodon.social/@cstrotm https://abbuc.social

Der Graphikbildschirm hat im Textmodus 24 Zeilen a 40. Jede Zeile in der Displaylist bekommt also eine 2 eingeschrieben (die ersten 24 Scanlines bleiben leer). Die erste Zeile hat dabei nicht den Wert 2 sondern 66 (64+2) - wobei die 64 einen Adressbefehl darstellt - nämlich den des Bildschirmanfanges. Es folgen dann die 23 nächsten Zeilen mit dem Wert 2 und danach kommt noch der Sprungbefehl mit Adresse des Displaylistanfanges.

Nun möchten wir folgendes machen: In jede Zeile schreibe ich statt einer 2 eine 66 (alles dezimal) rein mit der jeweiligen um eine Zeile höheren Adresse für den Bildschirm: 66 64 156 (40000) 66 104 156 (40040) 66 144 156 (40080) 66 184 156 66 224 156 .......... usw am Ende wieder der Sprungbefehl für den Start der Displaylist.

Als nächstes (mein Grobscroll) kopiere ich einfach die Displaylist um - verschiebe also alle Zeilen um eins nach oben: 66 104 156 66 144 156 66 184 156 66 224 156 66 08 157 ..... 66 64 156 Sprungbefehl Displaylistanfang

die letzte Zeile vor dem Sprungbefehl müsste dann wohl immer die vorherige erste sein. Damit bekomme ich ein fortlaufendes Grobscroll des gesamten Bildschirmes. Will ich jetzt einen Shooter damit machen, überschreibe ich einfach die komplette erste Zeile der Displaylist mit den neuen Landschaftsdaten (meine "letzte Reihe") - anschließend wird die erste Reihe durch Umsortierung der Displaylist letzte Reihe. Theoretisch könnte ich auch ganz einfach 24 verschiedene Displaylist mit entsprechend versetzten Zeilen vorbereiten und dann einfach nur jeweils von Displaylist 1 zu 2 zu 3 zu .... zu 24 umschalten. Die Displaylist wird dann noch so aufgebaut, daß der entsprechende Graphicmodus plus Feinscroll mit drin ist, der Grobscroll wird entsprechend immer einmal nach 7 Feinscrollaktionen durchgeführt.

So habe ich die Theorie verstanden.... das möchte ich demnächst mal umsetzen. Habe ich das korrekt verstanden?? Liebe Grüße Peter

In dem Beispiel von mir im "Return to Phobos"-Thread sieht das Scrolling wie folgt aus.

Scrolling.png

Nur in der letzten Zeile muss man aufpassen, da dort der Code $42 steht und nicht $62 !!! Deine Aussage trifft also zu.

Grüße Janko

Anhänge:

Meine Projekte findest Du hier...

ha... so habe ich es verstanden! Cool! Das sieht ja prinzipiell viel einfacher und vor allem viel schneller aus... mit der Methode müsste man doch auch eine hochaufgelöste Graphik ganz schnell scrollen können!!
Wenn ich also eine Graphik mit 320x192 scrollen möchte, dann erstelle ich einfach 192 verschiedene Displaylisten (dann fällt ja auch noch die Rechenzeit für die Umkopiererei weg) und schalte die einfach hintereinander um und habe so einen hochaufgelösten schnellen Scroll ....

Hallo!

Einzelne Displaylisten bei 192 Zeilen? In jeder Liste sind doch min. 200 Bytes enthalten, oder? Also 192 Schritte x 200 Bytes = 38400 Bytes = 37,5kb = Atari voll 🙂

Oder mache ich jetzt einen Denkfehler?

Grüße Janko

Meine Projekte findest Du hier...

ähhhh .. ja das stimmt ... das geht nicht. Da müsste man umkopieren... 😀

Hallo Peter!

Irgendwie hast Du mich neugierig gemacht ein Scrolling für 320x192 Punkte hinzu bekommen 🙂 7,5k zu scrollen, kann man nur über Displaylist-Manipulation hinbekommen, aber wie? 192 Displayliste? Nein zu wenig Speicher 1 Liste Rotieren lassen, wie in meinem Beispiel für den Textmodus Gr. 0 ? Das wären fast 600 Bytes, die ständig 1/50s rotieren...

In Endeffekt habe ich nun zwei Displaylisten und lasse darin den MemScan wandern. Also $40 in der Displaylist mit der Angabe des Speicherbereiches. Es müssen nun 15 Bytes ständig neu gesetzt werden. Die Logik ist aufwendig, wird wegen unterschiedlichen Zuständen nur tlw. immer abgearbeitet. Insgesamt scheint das Scrolling gut zu funktionieren.

Hier mein Beispiel (Quick & Dirty 😀 ) :

; ############################################################################
; # Programm um den Bildschirm im Grafikmodus 8 (320x192) zu scrollen,
; # ohne den Speicherbereich zu verschieben
; #
; # ZeroPage A0 bis A5 werden für notwendige Zeiger genutzt
; # Der Bildschirmspeicher liegt ab Adresse $2150 bis $3F4F
; # Die Displaylist wird in den Bereich $2000 geschrieben
; #
; # Das Programm selbst beginnt ab Adresse $4000, direkt hinterm 
; # Bildschirmspeicher
; #
        org $4000

        icl 'Systemkonstanten.asm'

; ############################################################################
; ### Variabeln definieren

ScrMemZero  equ $a0
DListZero   equ $a2
LastLine    equ $a4

ScrMemZ     .word $2150
DListZ      .word $2000

VScrollPos  .byte 99
VScrollCnt  .byte 00

; ############################################################################
; ### Daten definieren

; # DisplayList
; # Da der ANTIC nur 4K-Daten verarbeiten kann, muss man den Bildschirm in zwei
; # Bereiche aufteilen, die jeweils < 4096 Bytes haben.
; # Beim scrollen muss man dies berücksichtigen, weshalb ich in zwei Abschnitten
; # scrolle und deshalb 2 Displaylisten benötige
; # Jeweils mit vertauschten 4K Block
; # Um einen zusammenhängenden Bild-Bereich zu haben, beginnt man bei
; # $2150 und endet bei $3F4F

DList1      .byte $70,$70,$70
        .byte $4F,$50,$21               ; Der MEMSCAN wird hier beim scrollen
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F   ; regelmäßig der neuen Situation angepasst
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F       
        .byte $4F,$00,$30               ; Dieser MEMSCAN wird "nach oben" wandern,
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F   ; und immer auf die selbe Adresse zeigen
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F
        .byte $4F,$28,$3F               ; Dieser MEMSCAN wird einmalig korigiert und 
                                ; anschließend wander er ebenfalls "hoch"
        .byte $41,$00,$20
        .byte $ff

DList2      .byte $70,$70,$70
        .byte $4F,$00,$30               ; Der MEMSCAN wird hier beim scrollen
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F   ; regelmäßig der neuen Situation angepasst
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $4F,$50,$21               ; Dieser MEMSCAN wird "nach oben" wandern,
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F   ; und immer auf die selbe Adresse zeigen 
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
        .byte $0F,$0F
        .byte $4F,$D8,$2F               ; Dieser MEMSCAN wird einmalig korigiert und
                                ; anschließend wander er ebenfalls "hoch"
        .byte $41,$00,$20
        .byte $ff

; ############################################################################
; # VSync
; # 
        .proc WaitVSync         ; VBI abwarten

        lda RTCLOK+2
Loop        cmp RTCLOK+2  
            beq Loop

        rts

        .end

; #############################################################################
; ### Speicherbereiche initialisieren

        .proc MemInI                    

        lda ScrMemZ         ; Speicherbereich des Bildbildschirm
        sta ScrMemZero          ; in die ZeroPage kopieren
        lda ScrMemZ+1
        sta ScrMemZero+1        

        lda DListZ          ; Speicherbereich der Displaylist
        sta DListZero           ; in die ZeroPage kopieren
        lda DListZ+1
        sta DListZero+1     

        jsr ScreenMem           ; Bildschirm löschen (leeren)
        jsr DListLoad.First     ; Erste Displaylist einladen

        jsr WaitVSync           ; VBI abwarten

        lda DlistZ          ; Displaylist laden
            sta SDLSTL
        lda DlistZ+1
        sta SDLSTL+1

        jsr WaitVSync           ; Erneut VBI abwarten

        rts

        .end

; ############################################################################
; # Clear Screen
; # 
; # Sicherstellen, dass der Bildschirm sauber ist
; #
        .proc ScreenMem

        ldx #$00
        ldy #$00

        lda #$00            ; Routine um den Bildschirm
@       sta (ScrMemZero),y      ; zu löschen
        iny             ; Da insgesamt 7,5k gelöscht
        bne @-              ; werden müssen,
        inx                 ; wird der Vorgang
        cpx #$1E            ; in mehrere 256er Läufe
        beq @+
        inc ScrMemZero+1
        jmp @-

@       inc ScrMemZero+1
        ldy #$B0            ; und einem Restlauf
@       dey             ; aufgeteilt
        cpy #$00
        beq @+
        lda #$00
        sta (ScrMemZero),y
        jmp @-

@       lda ScrMemZ         ; ZeroPage-Variable
        sta ScrMemZero          ; wieder herstellen
        lda ScrMemZ+1
        sta ScrMemZero+1

        rts

        .end

; ############################################################################
; # Displaylist einrichten
; # 
; # Es gibt zwei Displaylisten, die je nach Scrollposition eingeblendet werden müssen
; #
        .proc DListLoad

First       ldy #$00            ; Die Displaylist

Loop        lda DList1,y            ; muss in den Bereich ab $2000
        cmp #$ff            ; eingelesen werden
        beq @+              ; $ff ist dabei das Signal
        sta (DlistZero),y       ; das alles gelesen wurde
        iny
        jmp Loop

@       rts

Second      ldy #$00

Loop2       lda DList2,y            ; Ein zweiter Loop für die
        cmp #$ff            ; zweite Displaylist
        beq @+
        sta (DlistZero),y
        iny
        jmp Loop2

@       rts

        .end

; ############################################################################
; # VScroll - Routine um Grafikmodus 320 x 192 zu scollen 
; # 
        .proc VScroll
        lda move            ; In Move wird festgehalten
        cmp #$02            ; Ob man im ersten Durchlauf = 0 ist
        bcc @+              ; Mehr als Durchlauf der 1. Häfte hat = 1
        jmp SecondDLI           ; Oder gar in der 2. Hälfte sich befindet >2

@       lda VScrollPos          ; VScrollPos wieder verringert
        cmp #$06            ; bis zur 6, um nicht wichtige Einträge zu 
        beq Restore1            ; überschreiben
        dec VScrollPos          ; Bei 6 wird die 2. Displaylist aktiviert

        ldy #$03            ; y wird auf die 1 Memory-Scanzeile der Displaylist
        jsr DListSetMem         ; ausgerichtet und dieser dann hochgezählt
        ldy VScrollPos          ; y nun auf den ersten Wander-Memscan setzen
        jsr DListSet1UPF        ; und wandern lassen

        lda Move            
        cmp #$01
        beq @+
        ldy #199
        jmp FirstMove1          ; Im ersten Durchgang wird der 3. Memscan nur 
@       tya             ; verändert auf $2150
        clc
        adc #97             
        tay
        jsr DListSet1UPF        ; In weiteren Durchgängen wird dieser wandern

@       jmp Count

SecondDLI   lda VScrollPos          ; für die zweite Displaylist prüfen, ob das 
        cmp #$06            ; Ende der Durchläufe erreicht ist      
        beq Restore2            ; Dann wieder auf Displaylist 1 zurück ändern
        dec VScrollPos

        ldy #$03            ; y wieder auf den 1 Memory-Scann der Displaylist
        jsr DListSetMem         ; setzen und hochzählen
        ldy VScrollPos          
        jsr DListSet1UPF        ; Hier wieder den 2. Scan wandern lassen

        lda Move            ; Erster Durchlauf der 2. Displaylist = 5
        cmp #$06
        beq @+
        ldy #199
        jmp FirstMove2          ; Im ersten Durchlauf nur die Adresse korrigieren
@       tya
        clc
        adc #93
        tay
        jsr DListSet1UPF        ; Im weiteren Durchlauf den Memscan wandern lassen

@       jmp Count           ; Zähler für "LastLine" wo neuer Inhalt reingeschrieben 
                        ; werden kann
; -- Unterprogramme
FirstMove1  lda #$01            ; Move auf 1 setzen
        sta Move
        lda #$50            ; Speicherbereich auf $2150 setzen
        sta (DlistZero),y
        sta LastLine
        iny
        lda #$21
        sta (DlistZero),y
        sta LastLine+1
        rts 

Restore1    jsr DListLoad.Second        ; Zweite Displaylist aktivieren
        lda #103            ; Und Variabeln neu setzen
        sta VScrollPos          ; fürs wandern
        lda #5              ; Move auf 5
        sta Move
        lda #$d8            ; Speicher auf $2fd8 setzen
        sta LastLine
        lda #$2f
        sta LastLine+1
        rts     

FirstMove2  lda #$06            ; Move auf 6 setzen
        sta Move
        lda #$00            ; und Speicher auf $3000
        sta (DlistZero),y       ; ändern
        sta LastLine
        iny
        lda #$30
        sta (DlistZero),y
        sta LastLine+1
        rts     

Restore2    jsr DListLoad.First     ; Displaylist 1 wieder aktivieren
        lda #99             ; Variabeln neu setzen
        sta VScrollPos
        lda #0              ; Move wieder auf 0
        sta Move
        lda #$28            ; Speicherbereich auf $3f28 setzen
        sta LastLine
        lda #$3f
        sta LastLine+1
        inc VScrollCnt
        rts     

DListSet1UPF    lda #$4F            ; Routine, um die Memscan-Zeile
        sta (DlistZero),y       ; wandern zu lassen
        iny
        iny     
        lda (DlistZero),y
        dey
        sta (DlistZero),y
        iny
        iny     
        lda (DlistZero),y
        dey
        sta (DlistZero),y
        iny     
        lda #$0f
        sta (DlistZero),y       
        rts

DListSetMem clc             ; Nur den Speicher hochzählen
        iny             ; der im Memscan steht
        lda (DlistZero),y
        adc #$28
        sta (DlistZero),y
        iny
        lda (DlistZero),y
        adc #$00
@       sta (DlistZero),y   
        rts

Count       clc             ; LastLine berechnen
        lda LastLine
        adc #$28
        sta LastLine
        lda LastLine+1
        adc #$00
        sta LastLine+1
        rts

Move        .byte $00

        .end

; ############################################################################
; ### Spielfeld generieren 
; ###
; ### Irgendetwas darstellen
; ###

        .proc Spielfeld

HoleRichtung    lda RANDOM
        cmp #$55
        bcc GeheLinks
        cmp #$aa
        bcs GeheRechts

GeheGeradeaus   lda #$00
        sta Richtung        
        rts

GeheLinks   lda Richtung
        cmp #$02
        beq @+
        lda Links
        cmp LinksMin
        bcc @+
        lda #$01
        sta Richtung
        rts

@       lda #$00
        sta Richtung
        rts

GeheRechts  lda Richtung
        cmp #$01
        beq @-
        lda Rechts
        cmp RechtsMax
        bcs @-
        lda #$02
        sta Richtung
        rts             

Start       jsr HoleRichtung

        lda Richtung
        cmp #$01
        beq LLinks
        bcc GGeradeaus
        bcs RRechts
        rts

GGeradeaus  ldy #$00
        lda #$ff
@       sta (LastLine),y
        iny
        cpy Links
        bcc @-
        dey
        lda LinksFein
        sta (LastLine),y
        iny

        lda #$00
@       sta (LastLine),y
        iny
        cpy Rechts
        bcc @-

        lda RechtsFein
        ldy Rechts
@       sta (LastLine),y
        lda #$ff
        iny
        cpy #$28
        bcc @-
        rts

LLinks      lda LinksFein
        cmp #$00
        beq @+
        clc
        asl
        sta LinksFein
        eor #$ff
        sta RechtsFein
        jmp GGeradeaus
@       lda #$ff
        sta LinksFein
        lda #$00    
        sta RechtsFein      
        dec Links
        dec Rechts
        jmp GGeradeaus

RRechts     lda RechtsFein
        cmp #$00
        beq @+
        clc
        lsr
        sta RechtsFein
        eor #$ff
        sta LinksFein
        jmp GGeradeaus

@       lda #$ff
        sta RechtsFein      
        lda #$00    
        sta LinksFein
        inc Links
        inc Rechts
        jmp GGeradeaus

LinksFein   .byte $ff
Links       .byte $14
RechtsFein  .byte $ff
Rechts      .byte $1E
Richtung    .byte $00       ; 0 = Geradeaus   1 = Links   2 = Rechts
LinksMin    .byte $08
RechtsMax   .byte $20

        .end        

; #############################################################################
; ### Hauptprogramm

        .proc main

        jsr MemInI

        ;lda #$0A
        ;sta color2

Loop        jsr WaitVSync
        jsr VScroll
        jsr Spielfeld.Start


        jmp Loop        

        .end

; #############################################################################
        run main        

Gruß Janko

Meine Projekte findest Du hier...

Guten Morgen Janko, ich habe mir das Listing gleich mal rübergezogen und ausprobiert... beeindruckend! Tatsächlich hatte ich mal ganz am Anfang mit Scrolling experimentiert unter Basic und das ging dort auch nur relativ zügig und schnell mit 2 verschiedenen Displays und dann umschalten... so hätte ich das jetzt nach meinem Projekt Phobos versucht - du hast das jetzt umgesetzt!! Das läuft echt gut. Respekt. Was ich noch besser finde ist, daß du auch noch einen Zufallsgenerator für eine einfache Landschaft eingebaut hast. Klasse!! Der Code scheint aufwendig, aber das Ergebnis ist ja entscheidend und der eigentliche Programmablauf um 8k zu scrollen läuft fast ohne CPU-Belastung. Im Grunde müsste man das jetzt noch für verschiedene interessante Grafikmodi umsetzen und veröffentlichen, dann muss das nicht jeder selber neu "erfinden". Klasse gemacht, Janko!!

Ich habe gestern ein wenig Zeit gehabt und neben der ganzen schlimmen Berichterstattung aus der Welt und vor allem aus der Ukraine mein Projekt weitergemacht. Neue Items sind drin - die Schranken! Dabei wollte ich die Schranken so gestalten, daß sie ständig animiert sind und dann als weiteres Item relativ einfach in die Landschaft reingeprintet werden können über meine Routine "letzte Reihe". Die Schranke muss in vernünftiger Zeit ein- bzw. ausgeschaltet werden, dieses Timing hat mich etwas Mühe und Zeit gekostet. Am Ende habe ich es ans "blinken" gekoppelt und mit ein paar Anpassungen läuft das jetzt ganz gut. Jetzt kommt noch eine weitere "Laufschranke" und noch weitere Landschaft und noch ein paar Sonderitems.

Für das Endlevel muss ich mir noch was schönes ausdenken...

Und danach gehe ich das nächste Projekt an... es wird wieder ein Shooter, der aber ganz anders aufgebaut sein soll...
Liebe Grüße Peter

Danke! Freut mich, dass es Dir gefällt.

Grüße Janko

Meine Projekte findest Du hier...