Исследование интро Motion(Atari 65XE)


Эта работа(http://a8.fandal.cz/detail.php?files_id=4900) попалась мне на глаза совершенно случайно. Поскольку в демо используется походая техника, я решил посмотреть внутрь.

Какой инструмент понадобится? С одной стороны Dis6502(http://www.atarimax.com/dis6502/) удобнее, потому что в нем включена поддержка символьных мнемоник и можно помечать часть фрагментов в слова или в байты, тогда нечитаемая каша превратится в осмысленный код. С другой стороны, самомодифицирущийся код в дизассемблере останется непонятным. Тогда IDA вам в помощь.


Хорошо, скормил программу дизассемблеру, и после нескольких просмотров получилось вот что(комментарии мои)
               
    *= $B000
;
LB000       ldx #$1B
            lda #$02 LB004       sta LB0EF,X dex             bpl LB004
немного неясный участок, разберемся попозже
                lda    #$EC
                sta    LB10C
                sta    SDLSTL
             lda #$B0             sta LB10D             sta SDLSTH


             lda #$80              sta CHBAS          lsr A              sta GPRIOR              lda #$41              sta LB10B
Часть становится яснее, например в CHBAS вносится значение $80.Загляну в руководство Mapping the Atari: CHBAS(756) это адрес данных знакогенератора, умноженный на 256, получается данные шрифта хранятся в $8000. в GPRIOR заносится значение $40(LSR A - деление на 2) получается GRAPHICS 9 (GTIA mode)
Осталось разобраться с непонятным кодом. SDLSTL/SDLSTH(560 и 561) это адрес инструкции Display List.

Запущу интро в Atari800Plus, жму F8 и ввожу следующее:

TIP: Type '?' for help, 'CONT' to exit                                         
312   0 B0A7 INC $B098             (003B) A=0f S=fb X=1c Y=0c P=--*B---C       
> m 230                                                                        
0230 : EC B0 03 00 00 00 92 C0 6E C9 00 00 00 00 00 00  ........n.......       
0240 : 00 00 00 07 00 00 01 00 00 00 00 00 00 00 00 00  ................       
0250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................       
0260 : 00 00 00 00 00 00 00 00 00 00 00 CC 00 00 00 40  ...............@       
0270 : E4 E4 E4 E4 E4 E4 E4 E4 0F 0F 0F 0F 01 01 01 01  ................       
0280 : 01 01 01 01 01 01 01 01 00 00 00 00 00 00 00 00  ................       
0290 : 00 02 00 00 60 BF 00 00 00 00 00 00 00 00 01 00  ................       
02A0 : FF 00 00 01 01 01 01 01 01 01 01 01 01 01 01 01  ................       
02B0 : 01 01 FF FF FF FF 00 00 00 00 00 00 00 00 40 18  ..............@.       
02C0 : 00 00 00 00 28 CA 94 46 00 00 00 00 00 00 00 00  ....(..F........       
02D0 : 00 00 00 00 00 80 00 00 00 28 05 00 00 00 00 00  .........(......       
02E0 : 00 B0 00 D7 C0 1F BC 00 07 00 18 FF 01 00 CC 05  ................       
02F0 : 00 00 00 02 80 00 00 00 00 00 80 00 FF 00 00 00  ................       
0300 : 31 01 52 01 00 04 07 00 80 00 01 00 00 00 00 00  1.R.............       
0310 : 00 00 00 00 1E 00 00 00 00 00 50 30 E4 43 40 E4  ..........P0.C@.       
0320 : 45 00 E4 53 10 E4 4B 20 E4 00 00 00 00 00 00 00  E..S..K ........       
> dlist b0ec                                                                   
B0EC: LMS 9000 MODE 2                                                          
B0EF: MODE 2                                                                   
B0F0: MODE 2                                                                   
B0F1: MODE 2                                                                   
B0F2: MODE 2                                                                   
B0F3: MODE 2                                                                   
B0F4: MODE 2                                                                   
B0F5: MODE 2                                                                   
B0F6: MODE 2                                                                   
B0F7: MODE 2                                                                   
B0F8: MODE 2                                                                   
B0F9: MODE 2                                                                   
B0FA: MODE 2                                                                   
B0FB: MODE 2                                                                   
B0FC: MODE 2                                                                   
Press return to continue ('q' to quit):                                        
B0FD: MODE 2                                                                   
B0FE: MODE 2                                                                   
B0FF: MODE 2                                                                   
B100: MODE 2                                                                   
B101: MODE 2                                                                   
B102: MODE 2                                                                   
B103: MODE 2                                                                   
B104: MODE 2                                                                   
B105: MODE 2                                                                   
B106: MODE 2                                                                   
B107: MODE 2                                                                   
B108: MODE 2                                                                   
B109: MODE 2                                                                   
B10A: MODE 2                                                                   
B10B: JVB b0ec                                                                 
                                                         


первая инструкция B0EC: LMS 9000 MODE 2  - текстовый режим(аналог GR.0 в Бейсике), адрес берется с $9000(бит LMS), следующие 28 строк - тот же текстовый режим, а последняя JVB b0ec  - дождаться прерывания и перейти по адресу,стандартное завершение Display List, как раз переход по начальному адресу инструкций.

Теперь о текстовом режиме. Существует 256 символов - 128 стандартные и 128 инверсные.Символы задаются 8 байтами, например А задается так:

Подробнее можно узнать о знакогенераторе, прочитав статью: http://www.atariarchives.org/creativeatari/The_Beginners_Guide_to_Character_Sets.php ,но я ее пересказывать не буду.

Еще одно интересное значение в GPRIOR изменяет режим отображения символов. Каждый байт разбивается на 2 половинки АААА.ВВВВ. Ясно, что половинка может состоять из значений 0-15, а это и есть цвет или оттенок цвета.
           
             ldy    #$1F
  inx
 LB02B       lda    LB0CC,X
             sta    L8600,X
             sta    L8640,X
             sta    L8680,X
             sta    L86C0,X
             sta    L8700,X
             sta    L8620,Y
             sta    L8660,Y
             sta    L86A0,Y
            sta L86E0,Y
            sta L8720,Y
 dey             inx             cpx #$20             bne LB02B

Не совсем ясно, что творится здесь, разбор оставлю на потом(это оказалась генерация таблицы синуса).
 
 iny             lda #$FF LB055    ldx #$07 LB057    sta L8000,Y iny dex                                bpl LB057 sec                                sbc #$11                           cpy #$80                           bne LB055
Данные знакогенератора готовятся очень интересно - 8 байт $FF, затем $EE и до $11, таким образом получаются 15 символов, каждый из которых отображается как квадратик разного цвета, по сути почти атрибуты на ZX Spectrum.

Теперь, после подготовки данных, интро начинает работать. данные для экрана хранятся в $9000, таблица синуса в $8600
 


 LB065       lda #$00             sta LB0A5             lda #$90             sta LB0A6

Непонятны эти инструкции, результат работы дизассемблера. Нахожу похожий адрес LB0A4       sta L9000,Y  инструкция представляет собой $99,$00,$90, значит LB0A5 это LB0A4+1, LB0A6 это LB0A4+2, получается, что в инструкцию вносятся байты $00 и $90,
  
Lb06F         lda #$00             sta LB09C; LB09B+1
Еще интереснее, B09C это адрес adc L8614,Y. Дальше я плюнул на копание адресов и дописал правильные значения в комментариях.
              eor    #$FF
              sta    LB07F;LB07E+1

              inc    LB070 ; Lb06F+1

              ldx    #$1C
LB07E         lda    L8600
              lsr    A                 
              sta    LB08C;LB08B+1
              inc    LB07F;LB07E+1
              inc    LB07F;LB07E+1

LB08B         lda #$14             sta LB098;LB097+1             lda #$86                           sta LB099;LB097+2

             ldy #$27 LB097       lda L860A,Y             asl A

LB09B             adc L8614,Y             cmp #$10             bcc LB0A4             lda #$0F LB0A4       sta L9000,Y             inc LB098;LB097+1             inc LB098;LB097+1 dey                                bpl LB097

             inc LB09C;LB09B+1             inc LB09C;LB09B+1             inc LB09C;LB09B


дальше идет переход на 40 байт,столько необходимо для текстового режима GR.0
               clc                   
              lda    #$28              
              adc    LB0A5;LB0A4+1
              sta    LB0A5;LB0A4+1
              bcc    LB0C7
              inc    LB0A6;LB0A4+2
LB0C7         dex
              bpl    LB07E

Весьма логичное завершение цикла по строкам - bpl сработает тогда, когда результат операции положительной, регистр X работает от $1C до $FF,29 линий

            
 bmi    LB065

Это элегантно и красиво: результат вышел отрицательный и переход bmi выполнится - 2 байта вместо jmp адрес(3 байта)

 LB0CC       .byte $00,$00,$00,$01,$01,$01,$02,$02
            .byte $03,$03,$04,$04,$05,$05,$06,$07
            .byte $08,$09,$0A,$0A,$0B,$0B,$0C,$0C
            .byte $0D,$0D,$0E,$0E,$0E,$0F,$0F,$0F

Это и есть начало инструкции Display List
           
 .byte    $42,$00,$90
"Хвостик" .com-файла: адрес запуска.
         
 *= $02E0
    .word LB000
Наверное, читатели запутаются в исходном тексте и в моем описании. Для простоты понимания я написал программу на PureBasic:
Dim s.a(256)

Restore L8600

For i=0 To 255
  Read.A s(i)
Next i

LB06F.a=0
LB07F.a=0

If InitSprite() And OpenWindow(0,0,0,640,480,"Motion",#PB_Window_SystemMenu) And OpenWindowedScreen(WindowID(0),0,0,640,480,0,0,0) 
  Repeat 
    StartDrawing(ScreenOutput()) 
    Box(0,0,640,480,0)
    LB09C.a=LB06F
    LB07F=LB06F|255
    LB06F+1
    For y=0 To $1C; To -1 Step -1
      a.a=s(LB07F)
      LB08C.a=a>>1
      LB07F+2
      
      For x.a=$27 To 0 Step -1
        t.a=x+LB08C
        u.a=x+LB09C
        a.a=s(t)>>1+s(u)
        If a>16:a=15:EndIf
        
        ;a=(a|255)&15
        Box(x*8,y*8,8,8,RGB(a*8,a*8,a*8))
        LB08C+2
      Next x
      LB09C+3
    Next y
        StopDrawing() 
      FlipBuffers() 
  Until WindowEvent()=#PB_Event_CloseWindow 
EndIf


DataSection
  ;generated file:D:\00\bluegriffon\.nntro\model\l8600.bin
L8600:
 Data.b $00,$00,$00,$01,$01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$07
 Data.b $08,$09,$0A,$0A,$0B,$0B,$0C,$0C,$0D,$0D,$0E,$0E,$0E,$0F,$0F,$0F
 Data.b $0F,$0F,$0F,$0E,$0E,$0E,$0D,$0D,$0C,$0C,$0B,$0B,$0A,$0A,$09,$08
 Data.b $07,$06,$05,$05,$04,$04,$03,$03,$02,$02,$01,$01,$01,$00,$00,$00
 Data.b $00,$00,$00,$01,$01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$07
 Data.b $08,$09,$0A,$0A,$0B,$0B,$0C,$0C,$0D,$0D,$0E,$0E,$0E,$0F,$0F,$0F
 Data.b $0F,$0F,$0F,$0E,$0E,$0E,$0D,$0D,$0C,$0C,$0B,$0B,$0A,$0A,$09,$08
 Data.b $07,$06,$05,$05,$04,$04,$03,$03,$02,$02,$01,$01,$01,$00,$00,$00
 Data.b $00,$00,$00,$01,$01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$07
 Data.b $08,$09,$0A,$0A,$0B,$0B,$0C,$0C,$0D,$0D,$0E,$0E,$0E,$0F,$0F,$0F
 Data.b $0F,$0F,$0F,$0E,$0E,$0E,$0D,$0D,$0C,$0C,$0B,$0B,$0A,$0A,$09,$08
 Data.b $07,$06,$05,$05,$04,$04,$03,$03,$02,$02,$01,$01,$01,$00,$00,$00
 Data.b $00,$00,$00,$01,$01,$01,$02,$02,$03,$03,$04,$04,$05,$05,$06,$07
 Data.b $08,$09,$0A,$0A,$0B,$0B,$0C,$0C,$0D,$0D,$0E,$0E,$0E,$0F,$0F,$0F
 Data.b $0F,$0F,$0F,$0E,$0E,$0E,$0D,$0D,$0C,$0C,$0B,$0B,$0A,$0A,$09,$08
 Data.b $07,$06,$05,$05,$04,$04,$03,$03,$02,$02,$01,$01,$01,$00,$00,$00 
EndDataSection
И на основе алгоритма я написал маленькое интро http://www.pouet.net/prod.php?which=63739
В интро создана другая генерация чанок 8х8 и добавлен звук. Да, воровать код нехорошо, но мне изучение этой работы помогло многим вопросам(не второму месту в компо)

Интро Атари, которые используют похожую описанную технику:

Swamp/g0blinish
http://www.pouet.net/prod.php?which=63639

Dragons:
http://a8.fandal.cz/detail.php?files_id=244

Point 256b:
http://256bytes.untergrund.net/demo/1279

Ye Olde Plasma/g0blinish
http://256bytes.untergrund.net/demo/1135

И да пребудет с вами легкий код!