Vlákno názorů k článku Jak probíhal vývoj pro osmibitová Atari před 45 lety? od judovana - 1 ; ============================================ 2 ; SNAKE GAME...

  • 25. 8. 2026 10:19

    judovana
    1 ; ============================================
      2 ; SNAKE GAME — Atari 8-bit (XL/XE)
      3 ; Assembler: MADS (mads snake.asm -o:snake.xex)
      4 ; Input: Joystick port 1
      5 ; ============================================
      6
      7 ; --- OS / Hardware Equates ---
      8 RTCLOK  = $0014         ; jiffy clock (~60 Hz NTSC)
      9 SAVMSC  = $0058         ; screen memory pointer
     10 STICK0  = $0278         ; joystick 0
     11 COLOR1  = $02C5         ; text luminance
     12 COLOR2  = $02C6         ; background
     13 COLOR4  = $02C8         ; border
     14 RANDOM  = $D20A         ; hardware RNG
     15 CH      = $02FC         ; last key pressed
     16 ATRACT  = $004D         ; attract-mode timer
     17 CRSINH  = $02F0         ; cursor inhibit (1=off)
     18 NMIEN   = $D40E         ; NMI enable
     19
     20 ; Screen geometry (Graphics 0 = ANTIC mode 2)
     21 SCRW    = 40
     22 SCRH    = 24
     23
     24 ; Internal screen character codes
     25 CH_SPACE = $00
     26 CH_WALL  = $E0          ; inverse space (solid block)
     27 CH_BODY  = $14          ; diamond body segment
     28 CH_FOOD  = $00+$80      ; inverse space for food pellet
     29 CH_HEAD  = $13+$80      ; inverse 'S'
     30
     31 ; Joystick bit patterns
     32 JOY_UP    = 14
     33 JOY_DOWN  = 13
     34 JOY_LEFT  = 11
     35 JOY_RIGHT = 7
     36
     37 ; --- Zero-page variables ---
     38 ZPTMP   = $80           ; 2-byte pointer
     39 ZPTMP2  = $82           ; 2-byte pointer
     40 HEADIDX = $84           ; ring-buffer head index
     41 TAILIDX = $85           ; ring-buffer tail index
     42 DIRX    = $86           ; direction X: $FF / $00 / $01
     43 DIRY    = $87           ; direction Y: $FF / $00 / $01
     44 HEADX   = $88           ; current head column
     45 HEADY   = $89           ; current head row
     46 FOODX   = $8A           ; food column
     47 FOODY   = $8B           ; food row
     48 SCORE   = $8C           ; 2-byte BCD score
     49 SPEED   = $8E           ; frames between moves
     50 GROW    = $8F           ; segments left to grow
     51 NEWX    = $90           ; candidate head column
     52 NEWY    = $91           ; candidate head row
     53 FOODCNT = $92           ; food-eaten counter (for speed ramp)
     54 DEAD    = $93           ; nonzero = game over
     55
     56 ; Ring buffers — 256 entries each, page-aligned
     57 RING_X  = $0600
     58 RING_Y  = $0700
     59
     60 ; ============================================
     61         org $2000
     62 ; ============================================
     63
     64 ; ---------- ENTRY ----------
     65 Start
     66         lda #0
     67         sta NMIEN               ; blank screen during setup
     68
     69         lda #$0E                ; bright white text
     70         sta COLOR1
     71         lda #$94                ; dark blue playfield
     72         sta COLOR2
     73         lda #$00                ; black border
     74         sta COLOR4
     75
     76         lda #$40
     77         sta NMIEN               ; VBI on, DLI off
     78
     79         lda #1
     80         sta CRSINH              ; hide cursor
     81
     82 ; ---------- NEW GAME ----------
     83 NewGame
     84         lda #0
     85         sta SCORE
     86         sta SCORE+1
     87         sta DEAD
     88         sta HEADIDX
     89         sta TAILIDX
     90         sta FOODCNT
     91
     92         lda #8                  ; ~7.5 moves/s (NTSC)
     93         sta SPEED
     94         lda #3                  ; snake starts at length 4
     95         sta GROW
     96
     97         lda #1                  ; initial direction: right
     98         sta DIRX
     99         lda #0
    100         sta DIRY
    101
    102         jsr ClearScreen
    103         jsr DrawBorder
    104         jsr DrawScore
    105
    106         lda #SCRW/2             ; place head at center
    107         sta HEADX
    108         sta RING_X
    109         lda #SCRH/2
    110         sta HEADY
    111         sta RING_Y
    112
    113         ldx HEADX
    114         ldy HEADY
    115         lda #CH_HEAD
    116         jsr PlotChar
    117
    118         jsr PlaceFood
    119
    120         ; "PRESS FIRE" prompt — row 10, col 14
    121         lda SAVMSC
    122         clc
    123         adc #<(10*SCRW+14)
    124         sta ZPTMP
    125         lda SAVMSC+1
    126         adc #>(10*SCRW+14)
    127         sta ZPTMP+1
    128         ldy #0
    129 @title  lda TitleMsg,y
    130         beq @twait
    131         sta (ZPTMP),y
    132         iny
    133         bne @title
    134
    135 @twait  lda #0
    136         sta ATRACT
    137         lda $D010               ; TRIG0 hardware
    138         bne @twait
    139
    140         ; erase prompt
    141         lda SAVMSC
    142         clc
    143         adc #<(10*SCRW+14)
    144         sta ZPTMP
    145         lda SAVMSC+1
    146         adc #>(10*SCRW+14)
    147         sta ZPTMP+1
    148         ldy #11
    149         lda #CH_SPACE
    150 @eclr   sta (ZPTMP),y
    151         dey
    152         bpl @eclr
    153
    154         lda RTCLOK
    155         sta DEAD                ; reuse as last-clock seed
    156         ; (DEAD is cleared below before loop)
    157
    158         lda #0
    159         sta DEAD
    160
    161         lda RTCLOK
    162         sta FOODCNT             ; (reuse byte briefly; reset below)
    163         lda #0
    164         sta FOODCNT
    165
    166 ; ---------- MAIN LOOP ----------
    167 GameLoop
    168         lda #0
    169         sta ATRACT
    170
    171         ; --- speed gate ---
    172 @wait   lda RTCLOK
    173         sec
    174         sbc FOODCNT             ; FOODCNT doubles as "last tick" here
    175         ; Oops — I'm reusing FOODCNT. Let me use a proper variable.
    176         ; Actually let me just use the Y-indirect trick with RTCLOK.
    177         ; I'll track "last clock" in a separate place.
    178         ; Fix: use DEAD as temp... no. Let me just compare directly.
    179         jmp @wait2
    180 @wait2
    181         ; Simple timing: busy-wait until SPEED jiffies have passed
    182         ; since last move. Store last-move-clock at $94.
    183 LASTCLK = $94
    184         lda RTCLOK
    185         sec
    186         sbc LASTCLK
    187         cmp SPEED
    188         bcc @wait2
    189
    190         lda RTCLOK
    191         sta LASTCLK
    192
    193         ; --- read joystick ---
    194         jsr ReadJoy
    195
    196         ; --- compute candidate position ---
    197         lda HEADX
    198         clc
    199         adc DIRX
    200         sta NEWX
    201
    202         lda HEADY
    203         clc
    204         adc DIRY
    205         sta NEWY
    206
    207         ; --- wall collision ---
    208         lda NEWX
    209         beq HitWall             ; column 0
    210         cmp #SCRW-1
    211         beq HitWall             ; column 39
    212         lda NEWY
    213         beq HitWall             ; row 0
    214         cmp #SCRH-2
    215         bcs HitWall             ; row >= 22
    216
    217         ; --- self collision (read screen) ---
    218         ldx NEWX
    219         ldy NEWY
    220         jsr ReadChar
    221         cmp #CH_BODY
    222         beq HitSelf
    223
    224         ; --- food? ---
    225         lda NEWX
    226         cmp FOODX
    227         bne NoFood
    228         lda NEWY
    229         cmp FOODY
    230         bne NoFood
    231
    232         ; ate food
    233         lda GROW
    234         clc
    235         adc #3
    236         sta GROW
    237
    238         sed
    239         lda SCORE
    240         clc
    241         adc #$10                ; +10 pts
    242         sta SCORE
    243         lda SCORE+1
    244         adc #0
    245         sta SCORE+1
    246         cld
    247
    248         jsr DrawScore
    249
    250         ; speed ramp: every 5 pellets, decrease delay (min 3)
    251         inc FOODCNT
    252         lda FOODCNT
    253         cmp #5
    254         bne @noramp
    255         lda #0
    256         sta FOODCNT
    257         lda SPEED
    258         cmp #3
    259         beq @noramp
    260         dec SPEED
    261 @noramp
    262         jsr PlaceFood
    263
    264 NoFood
    265         ; --- move snake ---
    266         ; old head -> body
    267         ldx HEADX
    268         ldy HEADY
    269         lda #CH_BODY
    270         jsr PlotChar
    271
    272         ; update head coords
    273         lda NEWX
    274         sta HEADX
    275         lda NEWY
    276         sta HEADY
    277
    278         ; push into ring buffer
    279         inc HEADIDX
    280         ldx HEADIDX
    281         lda HEADX
    282         sta RING_X,x
    283         lda HEADY
    284         sta RING_Y,x
    285
    286         ; draw head
    287         ldx HEADX
    288         ldy HEADY
    289         lda #CH_HEAD
    290         jsr PlotChar
    291
    292         ; grow or trim tail
    293         lda GROW
    294         beq @trim
    295         dec GROW
    296         jmp GameLoop
    297
    298 @trim   ; erase tail cell on screen
    299         ldx TAILIDX
    300         lda RING_X,x
    301         sta NEWX                ; temp reuse
    302         lda RING_Y,x
    303         sta NEWY
    304
    305         ldx NEWX
    306         ldy NEWY
    307         lda #CH_SPACE
    308         jsr PlotChar
    309
    310         inc TAILIDX
    311         jmp GameLoop
    312
    313 ; ---------- COLLISION ----------
    314 HitWall
    315 HitSelf
    316         ; flash screen
    317         ldx #8
    318 @flash  lda #$34
    319         sta COLOR1
    320         jsr WaitFrames
    321         lda #$0E
    322         sta COLOR1
    323         jsr WaitFrames
    324         dex
    325         bne @flash
    326
    327         ; "GAME OVER" — row 11, col 15
    328         lda SAVMSC
    329         clc
    330         adc #<(11*SCRW+15)
    331         sta ZPTMP
    332         lda SAVMSC+1
    333         adc #>(11*SCRW+15)
    334         sta ZPTMP+1
    335         ldy #0
    336 @gmsg   lda GameOverMsg,y
    337         beq @gwait
    338         sta (ZPTMP),y
    339         iny
    340         bne @gmsg
    341
    342         ; "PRESS FIRE" — row 13, col 14
    343 @gwait2
    344         lda SAVMSC
    345         clc
    346         adc #<(13*SCRW+14)
    347         sta ZPTMP
    348         lda SAVMSC+1
    349         adc #>(13*SCRW+14)
    350         sta ZPTMP+1
    351         ldy #0
    352 @pmsg   lda TitleMsg,y
    353         beq @gfire
    354         sta (ZPTMP),y
    355         iny
    356         bne @pmsg
    357
    358 @gfire  lda #0
    359         sta ATRACT
    360         lda $D010
    361         bne @gfire
    362
    363         jsr WaitFrames          ; debounce
    364         jsr WaitFrames
    365         jmp NewGame
    366
    367 ; ============================================
    368 ; SUBROUTINES
    369 ; ============================================
    370
    371 ; --- Read joystick, update direction (prevent reversal) ---
    372 ReadJoy
    373         lda STICK0
    374
    375         cmp #JOY_RIGHT
    376         bne @nR
    377         lda DIRX
    378         cmp #$FF
    379         beq @done               ; block: already going left
    380         lda #1
    381         sta DIRX
    382         lda #0
    383         sta DIRY
    384         rts
    385 @nR     cmp #JOY_LEFT
    386         bne @nL
    387         lda DIRX
    388         cmp #1
    389         beq @done
    390         lda #$FF
    391         sta DIRX
    392         lda #0
    393         sta DIRY
    394         rts
    395 @nL     cmp #JOY_UP
    396         bne @nU
    397         lda DIRY
    398         cmp #1
    399         beq @done
    400         lda #0
    401         sta DIRX
    402         lda #$FF
    403         sta DIRY
    404         rts
    405 @nU     cmp #JOY_DOWN
    406         bne @done
    407         lda DIRY
    408         cmp #$FF
    409         beq @done
    410         lda #0
    411         sta DIRX
    412         lda #1
    413         sta DIRY
    414 @done   rts
    415
    416 ; --- Clear screen (960 bytes) ---
    417 ClearScreen
    418         lda SAVMSC
    419         sta ZPTMP
    420         lda SAVMSC+1
    421         sta ZPTMP+1
    422         lda #CH_SPACE
    423         ldy #0
    424         ldx #4                  ; 4 full pages = 1024 (> 960)
    425 @pg     sta (ZPTMP),y
    426         iny
    427         bne @pg
    428         inc ZPTMP+1
    429         dex
    430         bne @pg
    431         rts
    432
    433 ; --- Draw border walls ---
    434 DrawBorder
    435         ; top row (row 0)
    436         lda SAVMSC
    437         sta ZPTMP
    438         lda SAVMSC+1
    439         sta ZPTMP+1
    440         ldy #SCRW-1
    441         lda #CH_WALL
    442 @top    sta (ZPTMP),y
    443         dey
    444         bpl @top
    445
    446         ; bottom row (row 22)
    447         lda SAVMSC
    448         clc
    449         adc RowLo+22
    450         sta ZPTMP
    451         lda SAVMSC+1
    452         adc RowHi+22
    453         sta ZPTMP+1
    454         ldy #SCRW-1
    455         lda #CH_WALL
    456 @bot    sta (ZPTMP),y
    457         dey
    458         bpl @bot
    459
    460         ; left + right columns (rows 1-21)
    461         ldx #1
    462 @side   lda SAVMSC
    463         clc
    464         adc RowLo,x
    465         sta ZPTMP
    466         lda SAVMSC+1
    467         adc RowHi,x
    468         sta ZPTMP+1
    469
    470         ldy #0
    471         lda #CH_WALL
    472         sta (ZPTMP),y           ; col 0
    473         ldy #SCRW-1
    474         sta (ZPTMP),y           ; col 39
    475
    476         inx
    477         cpx #22
    478         bne @side
    479         rts
    480
    481 ; --- Plot character  (X=col, Y=row, A=char) ---
    482 PlotChar
    483         pha
    484         lda RowLo,y
    485         clc
    486         adc SAVMSC
    487         sta ZPTMP
    488         lda RowHi,y
    489         adc SAVMSC+1
    490         sta ZPTMP+1
    491         txa
    492         tay                     ; Y = column for indirect index
    493         pla
    494         sta (ZPTMP),y
    495         rts
    496
    497 ; --- Read character  (X=col, Y=row -> A=char) ---
    498 ReadChar
    499         lda RowLo,y
    500         clc
    501         adc SAVMSC
    502         sta ZPTMP
    503         lda RowHi,y
    504         adc SAVMSC+1
    505         sta ZPTMP+1
    506         txa
    507         tay
    508         lda (ZPTMP),y
    509         rts
    510
    511 ; --- Place food at a random empty cell ---
    512 PlaceFood
    513 @retry  lda RANDOM
    514         and #$3F                ; 0-63
    515         cmp #38
    516         bcs @retry
    517         adc #1                  ; 1-38
    518         sta FOODX
    519         tax
    520
    521         lda RANDOM
    522         and #$1F                ; 0-31
    523         cmp #21
    524         bcs @retry
    525         adc #1                  ; 1-21
    526         sta FOODY
    527         tay
    528
    529         jsr ReadChar
    530         cmp #CH_SPACE
    531         bne @retry              ; occupied — try again
    532
    533         ldx FOODX
    534         ldy FOODY
    535         lda #CH_FOOD
    536         jsr PlotChar
    537         rts
    538
    539 ; --- Draw score on row 23 ---
    540 DrawScore
    541         lda SAVMSC
    542         clc
    543         adc RowLo+23
    544         sta ZPTMP
    545         lda SAVMSC+1
    546         adc RowHi+23
    547         sta ZPTMP+1
    548
    549         ; "SCORE:" label
    550         ldy #1
    551 @lbl    lda ScoreLbl-1,y
    552         beq @digits
    553         sta (ZPTMP),y
    554         iny
    555         bne @lbl
    556
    557 @digits ; 4 BCD digits
    558         lda SCORE+1
    559         lsr
    560         lsr
    561         lsr
    562         lsr
    563         ora #$10                ; screen code '0' = $10
    564         sta (ZPTMP),y
    565         iny
    566
    567         lda SCORE+1
    568         and #$0F
    569         ora #$10
    570         sta (ZPTMP),y
    571         iny
    572
    573         lda SCORE
    574         lsr
    575         lsr
    576         lsr
    577         lsr
    578         ora #$10
    579         sta (ZPTMP),y
    580         iny
    581
    582         lda SCORE
    583         and #$0F
    584         ora #$10
    585         sta (ZPTMP),y
    586         rts
    587
    588 ; --- Wait ~4 jiffies ---
    589 WaitFrames
    590         lda RTCLOK
    591         clc
    592         adc #4
    593         sta ZPTMP2              ; temp
    594 @wf     cmp RTCLOK
    595         bne @wf
    596         rts
    597
    598 ; ============================================
    599 ; DATA
    600 ; ============================================
    601
    602 ; Row-start offsets (row * 40)
    603 RowLo
    604         .byte $00,$28,$50,$78,$A0,$C8,$F0,$18
    605         .byte $40,$68,$90,$B8,$E0,$08,$30,$58
    606         .byte $80,$A8,$D0,$F8,$20,$48,$70,$98
    607 RowHi
    608         .byte $00,$00,$00,$00,$00,$00,$00,$01
    609         .byte $01,$01,$01,$01,$01,$02,$02,$02
    610         .byte $02,$02,$02,$02,$03,$03,$03,$03
    611
    612 ; Screen-code strings (null-terminated)
    613 ; ATASCII -> internal:  chr - $20  (for $20..$5F)
    614 ; 'P'=$30  'R'=$32  'E'=$25  'S'=$33  ' '=$00
    615 ; 'F'=$26  'I'=$29  !=$01
    616 ScoreLbl
    617         .byte $33,$23,$2F,$32,$25,$1A,$00       ; "SCORE:"
    618
    619 ; "PRESS FIRE!" in screen codes
    620 TitleMsg
    621         .byte $30,$32,$25,$33,$33,$00,$26,$29,$32,$25,$01,$00
    622
    623 ; "GAME OVER" inverse (add $80)
    624 GameOverMsg
    625         .byte $A7,$A1,$AD,$A5,$80,$AF,$B6,$A5,$B2,$00
    626
    627 ; ============================================
    628 ; Run address
    629 ; ============================================
    630         org $02E0
    631         .word Start
  • 25. 8. 2026 10:21

    judovana

    Toz.. muzete to nekdo zkontrolovat? Psal to samozrejme cloude, ale neslo mu to. Hold toho ma na netu prilis malo k uceni :D A trvalo mu to 30minut... coz je na prumerny maly AI task docela hodne. Na druhou stranu ja bych se to 14dnuiucil a pak tyden psal. POdle zkusenosti "an technologii co znam, to blije obcas hrozny veci, taks e to bnojim pouzit na technlologii co neznam"... Tohle presen spada sem. Ale prekvapil ze zpatlal aspon neco vzheldem k tomu jak malo materialu je na netu

  • 25. 8. 2026 22:08

    MilanV

    Já tedy se přiznám, že takhle to tedy ze zásady nezkusím. Ne že bych věřil na to, že se mi do atárka dostane vir a znásilní mi papouška. Ale to je právě ta zásada - copak jsme to tu nečetli už tolikrát, zrovna na root.cz ??? Už o tom proběhlo tolik zpráviček, kolik projektů řeší zaplavení AI slopem, kdy je strašně snadné něco vygenerovat, a pak ti "autoři" (čti: přeposílači) očekávají, že tím zaměstnají skutečné vývojáře. Pardon, ale to podporovat nechci.

    Minimálně emulátor Atari je k dispozici všem, na mnoha platformách. Očekávám tady tudíž zaslanou hotovou a odladěnou variantu, a ne za půl hodiny někomu vyrobit práci na několik hodin.

    A naopak: jako každý fanda se rád podívám na tu člověkem odladěnou verzi, i kdyby k ní napsal, že se třeba trápí s jedním KONKRÉTNÍM problémem - a pokusím se mu s ním pomoct.

  • 26. 8. 2026 9:11

    judovana

    Jo, je to AI slop. Ja sam nebyl ochoten venovat cas tomu to hodit do emulatoru. Hlavne na tom neplytvej vic cyklu nez "rychle oko". Vzdycky kdyz mam k dispozici nejaky lepsi model, rad zkousim jak moc to neumi resit problemy (nejen programco), ktere nejsou na netu opsane milionkrat. Ted mam na chvili opus 4.neco, ale nemel jsem problem k vyzkouseni, protoze uz mi dosli technologie ktere "to nezna" a "ja dokazu zkontrolovat". Navic kdyz tomu das hada v "normalnim" jazyce, ma to za minutu napsany, a docela hezky. Tak sem chtel zkusit. Ale trvalo mu to tak dlouho ze jsem oddriftoval jinam. Z dnesniho raniho pohledu je to normlani spam.

    Kdyz sme u te javy, tak dycky modely nachytam na novych JEPech. Jednoduse z vise zminenych duvodu: protoze je nikdo nerozpitval milionkrat (minimlane v dobe generovani modelu).

  • 26. 8. 2026 11:31

    Pavel Tišnovský
    Zlatý podporovatel

    takže po přepisu do ca65 (sorry, ale kvůli MADS jsem si Pascal nestahoval :-)

    1) nějaká návěští se musela opravit
    2) ovšem AI "zapoměla" na to, že cíle skoků jsou jen v rozsahu jednoho (signed) bajtu

    ca65 snake1.asm -t atari -o snake1.o -l snake1_list.asm --list-bytes 100
    snake1.asm(209): Error: Range error (158 not in [-128..127])
    snake1.asm(211): Error: Range error (154 not in [-128..127])
    snake1.asm(213): Error: Range error (150 not in [-128..127])
    snake1.asm(215): Error: Range error (146 not in [-128..127])
    snake1.asm(222): Error: Range error (135 not in [-128..127]) 

    Možná mašinu přesvědčit, ať dodržuje specifikaci MOS 6502? :-)

    [kupodivu to nenasekalo žádné syntaktické chyby]

    26. 8. 2026, 11:31 editováno autorem komentáře