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
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
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.
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).
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