Názory k článku Kopie datových bloků na ZX Spectru: přenášení bajt po bajtu

  • Článek je starý, nové názory již nelze přidávat.
  • 2. 5. 2023 7:55

    _dw

    Diky za clanek.
    Pouzit u skoku PE je svatecni pocit. .)

    V kapitole 3. mas napsano:

            ld   bc, SCREEN_ADR
            ld   de, SECOND_SCREEN_BLOCK
            ld   hl, SCREEN_BLOCK_SIZE
            call mem_copy
    Poznámka: volba registrových párů není náhodná, jak uvidíme v navazujících kapitolách.

    To muze nekoho zmast, protoze uz v kapitole 6 je:

    ld   hl, SCREEN_ADR           ; adresa zdrojového bloku
    ld   de, SECOND_SCREEN_BLOCK  ; adresa cílového bloku
    ld   bc, SCREEN_BLOCK_SIZE    ; velikost přenášených dat
    ldir

    Te mem_copy rutine z kapitoly 16 se neda vubec nic vytknout, imho je i lepsi nez hrani se zasobnikem. Tam je spousta problemu. Vypinat interupty. Pokud uz si hrajeme o nejvyssi rychlost, tak se tam jeste pridavaji IX a IY registrove pary. Takze pocet prenaseneho bloku neni nasobek 2. Pak se jeste musi resit, jestli to chces fakt rozbalit pres celou kopirovanou oblast a mit kod tim padem o podobne velikosti, nebo nad tim udelat nejakou smycku a pak resit, ze jednou jde ukazatel zasobniku nahoru a podruhe dolu. Takze muzes mit i zdroj prevraceny v bufferu. Same komplikace.

    U toho elegantniho rozbaleneho LDI bych jen podotknul, ze nemusis mit

            ld   a, b                ; kontrola BC na nulu
            or   c
            ret  z                   ; při prázdném bloku podprogram ukončíme
    
            ld   a, 16               ; na základě hodnoty v C vypočteme, kolik
            sub  c                   ; insturkcí LDI se má na začátku přenosu přeskočit
            and  15                  ; maximálně se přeskočí 15 instrukcí ze 16
    
            add  a, a                ; vynásobit dvěma protože LDI je dvoubajtová instrukce
            ld   (jump_address), a   ; uložíme offset do tohoto paměťového místa
            jr   $                   ; relativní skok přečte offset z ^
    jump_address equ $-1             ; trik jak zasáhnout do operandu instrukce JR

    dokud je ta velikost prenasenych dat konstanta.
    Pak to jen volas misto:
    call mem_copy
    napriklad takto:
    call repeat+2*((16-SCREEN_BLOCK_SIZE)& 15)
    a nechas si spocitat ten skok prekladacem.

  • 2. 5. 2023 12:08

    Pavel Tišnovský
    Zlatý podporovatel

    dik za doplneni.

    jinak na okraj: zrovna ted v sobotu jsem zpovidal dva sinclairisty (jeden dela pekna dema) a ti rikali, ze kdyz jde fakt o kopie cele VRAM, tak si nechaji ty rozbalene smycky s push/pull vygenerovat za behu. Chce to ale zhruba 2x tolik pameti, nez kopirovana data :) Ale ze vetsinou to neni tak kriticke a staci rozbaleni LDI.

  • 2. 5. 2023 17:55

    _dw

    Jo pro demo je to pouzitelne, ale kdyz si predstavim ze mam 48kb volne ram. Z toho si odectu nejake misto na zasobnik a buffer obrazovky tak jsem nekde kolem 40kb. Pak misto na tu rutinu a kdyby to bylo ldi tak jsem na 26.5 kb a to je sakra malo, pokud delam hru a spoustu mista mi zabira grafika. To fakt neni 64kb.

    Schvalne jsem se snazil prepsat tu samomodifikujici rutinu na variantu, kterou muze pouzit i ROM rutina a vzhledem k tomu, ze pri volani je obsazene i HL a nejde to diky LDI zmenit tak je to zajimavy problem, protoze "jp HL" (oficialne to je "jp (HL)") nejde pouzit. Ale pomohl jsem si pres RET.

    Prepsal jsem i co se kopiruje na atributy, protoze u textu, ktery ani nedosahuje do konce tretiny obrazovky to neni prukazne. A aby to bylo citelnejsi tak je lepsi si pomoct i v basicu tiskem na obrazovku predtim.

    10 FOR a=0 TO 16
    20 PRINT AT a,0;a
    30 NEXT a
    40 RANDOMIZE USR 32768
    50 PAUSE 0
    ; Example #88x:
    ;    Print color on screen + copy it to second part of screen using unrolled loop.
    
    ATTR_ADR            equ $5800
    SCREEN_BLOCK_SIZE   equ 130
    SECOND_SCREEN_BLOCK equ $5901
    
    ENTRY_POINT         equ $8000
    
            org ENTRY_POINT
    
            ; Vstupní bod celého programu
    start:
            call draw_attr                ; vyplnění prvni tretiny barvou
            ld   hl, ATTR_ADR             ; adresa zdrojového bloku
            ld   de, SECOND_SCREEN_BLOCK  ; adresa cílového bloku
            ld   bc, SCREEN_BLOCK_SIZE    ; velikost přenášených dat
            call mem_copy
            ret
    
    repeat:
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            ldi                      ; provést přenos jednoho bajtu
            jp   pe, repeat          ; ukončit blokový přenos při BC==0
            ret                      ; návrat z podprogramu
    mem_copy:
            ld   a, b                ; kontrola BC na nulu
            or   c
            ret  z                   ; při prázdném bloku podprogram ukončíme
    
            ld   a, 16               ; na základě hodnoty v C vypočteme, kolik
            sub  c                   ; insturkcí LDI se má na začátku přenosu přeskočit
            and  15                  ; maximálně se přeskočí 15 instrukcí ze 16
            add  a, a                ; vynásobit dvěma protože LDI je dvoubajtová instrukce
            exx
            add  a, low repeat
            ld   c, a
      if ((0xFF & repeat) >= (256-2*16))
            adc  a, high repeat
            sub  c
            ld   b, a
      else
            ld   b, high repeat
      endif
            push bc                  ; menit stinove BC je bezpecne pro navrat do basicu
            exx
            ret
    
    
    draw_attr:
            ld   a,1*8+2
            ld  de,ATTR_ADR
            ld   b,0
    draw_loop:
            ld   (de),a              ; zápis hodnoty na adresu (DE)
            inc  de
            djnz draw_loop           ;
            ret                      ; návrat z podprogramu
    
    
    end ENTRY_POINT
  • 3. 5. 2023 2:38

    _dw

    Podporuje to pasmo, kterym se prekladaji ty priklady zrovna. Ale umi to i sjasmplus.

    pasmo ma vse 16 bitove a unsigned
    sjasmplus je myslim 32 bit signed a umi vic operatoru, ale na rozdil od pasma neumi ?

    pasmo ma chybu, a nebo featuru v tom ze uvodni - vyhodnocuje skoro nakonec takze:

    -5+3 =-8

    https://pasmo.speccy.org/pasmodoc.html

    ## (see note)
    $, NUL, DEFINED
    *, /, MOD, %, SHL, SHR, <<, >>
    +, - (binary)
    EQ, NE, LT, LE, GT, GE, =, !=, <, >, <=, >=
    NOT, ~, !, +, - (unary)
    AND, &
    OR, |, XOR
    &&
    ||
    HIGH, LOW
    ?

    Pokud chces v pasmu nasobit ve vyrazu nejake dve 16 bitova cisla, ale signed a vysledek ma byt 32 bitovy signed, tak je to docela orisek jak to udelat... :) kdyz pasmo umi jen 16 bit unsigned.

    dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(abc,def) MMUL'
                            ;[8:42]     abc def m*   ( -- +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8-((abc)>>15)*(def)-((def)>>15)*(abc) +(abc)*(def) )
        push DE             ; 1:11      abc def m*
        push HL             ; 1:11      abc def m*
        ld   DE, +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8-((abc)>>15)*(def)-((def)>>15)*(abc); 3:10      abc def m*
        ld   HL, +(abc)*(def); 3:10      abc def m*
    ; seconds: 0           ;[ 8:42]

    O neco kratsi by to bylo kdyby jsi chtel u16*u16=u32

    dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(abc,def) UMMUL'
                            ;[8:42]     abc def um*   ( -- +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8 +(abc)*(def) )
        push DE             ; 1:11      abc def um*
        push HL             ; 1:11      abc def um*
        ld   DE, +((abc)>>8)*((def)>>8)+(low(abc))*((def)>>8)>>8+((abc)>>8)*(low(def))>>8+((low((low(abc))*((def)>>8)))+(low(((abc)>>8)*(low(def))))+(low(abc))*(low(def))>>8)>>8; 3:10      abc def um*
        ld   HL, +(abc)*(def); 3:10      abc def um*
    ; seconds: 0           ;[ 8:42]

    Pro signed deleni 16 bitove konstanty 16 bitovou konstantou a jeste ziskanim zbytku by vypadalo takto:

    dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(abc,def) DIVMOD'
                            ;[8:42]     abc def /mod   ( -- +((((abc)|(def))>>15) xor 1)*(abc)mod(def)+(((~(abc))&(def))>>15)*(abc)mod(-(def))-(((abc)&(~(def)))>>15)*(-(abc))mod(def)-(((abc)&(def))>>15)*(-(abc))mod(-(def)) +((((abc)|(def))>>15) xor 1)*(abc)/(def)-(((~(abc))&(def))>>15)*(abc)/(-(def))-(((abc)&(~(def)))>>15)*(-(abc))/(def)+(((abc)&(def))>>15)*(-(abc))/(-(def)) )
        push DE             ; 1:11      abc def /mod
        push HL             ; 1:11      abc def /mod
        ld   DE, +((((abc)|(def))>>15) xor 1)*(abc)mod(def)+(((~(abc))&(def))>>15)*(abc)mod(-(def))-(((abc)&(~(def)))>>15)*(-(abc))mod(def)-(((abc)&(def))>>15)*(-(abc))mod(-(def)); 3:10      abc def /mod
        ld   HL, +((((abc)|(def))>>15) xor 1)*(abc)/(def)-(((~(abc))&(def))>>15)*(abc)/(-(def))-(((abc)&(~(def)))>>15)*(-(abc))/(def)+(((abc)&(def))>>15)*(-(abc))/(-(def)); 3:10      abc def /mod
    ; seconds: 0           ;[ 8:42]

    No a pak jsou jeste vetsi hruzy :D
    Nekdy to 32 bitove cislo musi mit nejaky pocet nejvyssich bitu nulovy a je potreba prave ten ? operator

    dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(hi,lo,abc) UMDIVMOD'
    
      .warning Pasmo does not support 32 bit numbers and M4 does not know all values(They can only emulate 28-bit/12-bit or 16-bit/16-bit without checking the range): "hi<<16+lo abc um/mod"
                            ;[8:42]     hi lo abc um/mod   ( -- +((((((hi) mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)+(lo) mod (abc))mod (abc) +((hi) mod (abc)<<4+(lo)>>12)/(abc)<<12+(((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)/(abc)<<8+((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)/(abc)<<4+(((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)mod (abc)<<4+(15& (lo)))/(abc) )
        push DE             ; 1:11      hi lo abc um/mod
        push HL             ; 1:11      hi lo abc um/mod
        ld   DE, +((((((hi) mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)<<4)mod (abc)+(lo) mod (abc))mod (abc); 3:10      hi lo abc um/mod
        ld   HL, +((hi) mod (abc)<<4+(lo)>>12)/(abc)<<12+(((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)/(abc)<<8+((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)/(abc)<<4+(((((hi) mod (abc)<<4+(lo)>>12)mod (abc)<<4+(lo)<<4>>12)mod (abc)<<4+(lo)<<8>>12)mod (abc)<<4+(15& (lo)))/(abc); 3:10      hi lo abc um/mod
    ; seconds: 0           ;[ 8:42]
    dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(hi,lo,abc) FMDIVMOD'
    
      .warning Pasmo does not support 32 bit numbers and M4 does not know all values(They can only emulate 16-bit/16-bit without checking the range): "hi<<16+lo abc fm/mod"
                            ;[8:42]     hi lo abc fm/mod   ( -- +((((hi)|(abc))>>15) xor 1)*(lo)mod(abc)+(((~(hi))&(abc))>>15)*(((lo)mod(-(abc))!=0)&((abc)+(lo)mod(-(abc))))+(((hi)&(~(abc)))>>15)*((((-(lo))mod(abc))!=0)&((abc)-(-(lo))mod(abc)))-(((hi)&(abc))>>15)*(-(lo))mod(-(abc)) +((((hi)|(abc))>>15) xor 1)*(lo)/(abc)-(((~(hi))&(abc))>>15)*((lo)/(-(abc))-(((lo)mod(abc))!=0))-(((hi)&(~(abc)))>>15)*((-(lo))/(abc)-(((lo)mod(abc))!=0))+(((hi)&(abc))>>15)*(-(lo))/(-(abc)) )
        push DE             ; 1:11      hi lo abc fm/mod
        push HL             ; 1:11      hi lo abc fm/mod
        ld   DE, +((((hi)|(abc))>>15) xor 1)*(lo)mod(abc)+(((~(hi))&(abc))>>15)*(((lo)mod(-(abc))!=0)&((abc)+(lo)mod(-(abc))))+(((hi)&(~(abc)))>>15)*((((-(lo))mod(abc))!=0)&((abc)-(-(lo))mod(abc)))-(((hi)&(abc))>>15)*(-(lo))mod(-(abc)); 3:10      hi lo abc fm/mod
        ld   HL, +((((hi)|(abc))>>15) xor 1)*(lo)/(abc)-(((~(hi))&(abc))>>15)*((lo)/(-(abc))-(((lo)mod(abc))!=0))-(((hi)&(~(abc)))>>15)*((-(lo))/(abc)-(((lo)mod(abc))!=0))+(((hi)&(abc))>>15)*(-(lo))/(-(abc)); 3:10      hi lo abc fm/mod
    ; seconds: 0           ;[ 8:42]
    dworkin@dw-A15:~/Programovani/ZX/Forth/Pasmo_test$ ../check_word.sh 'PUSH(hi,lo,abc) SMDIVREM'
    
      .warning Pasmo does not support 32 bit numbers and M4 does not know all values(They can only emulate 28-bit/12-bit or 16-bit/16-bit without checking the range): "hi<<16+lo abc sm/rem"
                            ;[8:42]     hi lo abc sm/rem   ( -- +(((((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)+((hi)>>15?-(lo):lo) mod (((abc)>>15)?-(abc):abc))mod (((abc)>>15)?-(abc):abc))xor((hi)>>15*(-1)))+((hi)>>15) +(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)/(((abc)>>15)?-(abc):abc)<<12+((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)/(((abc)>>15)?-(abc):abc)<<8+(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)/(((abc)>>15)?-(abc):abc)<<4+((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)mod (((abc)>>15)?-(abc):abc)<<4+(15& ((hi)>>15?-(lo):lo)))/(((abc)>>15)?-(abc):abc))xor(((hi) xor (abc))>>15*(-1)))+(((hi) xor (abc))>>15) )
        push DE             ; 1:11      hi lo abc sm/rem
        push HL             ; 1:11      hi lo abc sm/rem
        ld   DE, +(((((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)<<4)mod (((abc)>>15)?-(abc):abc)+((hi)>>15?-(lo):lo) mod (((abc)>>15)?-(abc):abc))mod (((abc)>>15)?-(abc):abc))xor((hi)>>15*(-1)))+((hi)>>15); 3:10      hi lo abc sm/rem
        ld   HL, +(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)/(((abc)>>15)?-(abc):abc)<<12+((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)/(((abc)>>15)?-(abc):abc)<<8+(((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)/(((abc)>>15)?-(abc):abc)<<4+((((((hi)>>15?~((hi)+((lo)=0)):hi) mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<4>>12)mod (((abc)>>15)?-(abc):abc)<<4+((hi)>>15?-(lo):lo)<<8>>12)mod (((abc)>>15)?-(abc):abc)<<4+(15& ((hi)>>15?-(lo):lo)))/(((abc)>>15)?-(abc):abc))xor(((hi) xor (abc))>>15*(-1)))+(((hi) xor (abc))>>15); 3:10      hi lo abc sm/rem
    ; seconds: 0           ;[ 8:42]

    Popravde to byla docela dobra vyzva co vsechno tim jde udelat.

    3. 5. 2023, 02:40 editováno autorem komentáře

  • 3. 5. 2023 2:13

    _dw

    Na jp (ix) a jp (iy) jsem uplne zapomnel!
    Zda se ze automaticky preferuji jednobajtove instrukce, protoze bez IX se jde vetsinou obejit.
    Ten kod jsem psal narychlo nez jsem bezel do prace a cestou me napadlo, ze tam vubec nemusim pouzivat stinove registry, zvlast v ROMce, kde to spis prepinam zase zpet a ze bych mel uchovavat ten pouzity registr a s ex (sp),hl to dokonce bude i kratsi!

    original

    ld   (jump_address), a   ; 3:13   uložíme offset do tohoto paměťového místa
    jr   $                   ; 2:12   relativní skok přečte offset z ^
                             ;[5:25]

    stinovy a niceny BC

          exx                      ; 1:4
          add  a, low repeat       ; 2:7
          ld   c, a                ; 1:4
    if ((0xFF & repeat) >= (256-2*16))
          adc  a, high repeat      ; 2:7
          sub  c                   ; 1:4
          ld   b, a                ; 1:4
    else
          ld   b, high repeat      ; 2:7
    endif
          push bc                  ; 1:11    menit stinove BC je bezpecne pro navrat do basicu
          exx                      ; 1:4
          ret                      ; 1:10
                                   ;[9:47]
                                  ;[11:55]

    niceny IX (pouzivam instrukce co umi originalni ZX, ale ne vsechny Z80 klony, slo by to resit i jinak mozna i rychleji, ale nechce se me v tehle variante moc vrtat)

          add  a, low repeat       ; 2:7
          ld   ixl, a              ; 2:8
    if ((0xFF & repeat) >= (256-2*16))
          adc  a, high repeat      ; 2:7
          sub  ixl                 ; 2:8
          ld   ixh, a              ; 2:8
    else
          ld   ixh, high repeat    ; 3:11
    endif
          jp  (ix)                 ; 2:8    menit stinove BC je bezpecne pro navrat do basicu
                                   ;[9:34]
                                  ;[12:46]

    Moje volba by byla pouzit zasobnik a ex (sp),hl. Kratke ale pomalejsi.

          push hl                  ; 1:11
          add  a, low repeat       ; 2:7
          ld   l, a                ; 1:4
    if ((0xFF & repeat) >= (256-2*16))
          adc  a, high repeat      ; 2:7
          sub  l                   ; 1:4
          ld   h, a                ; 1:4
    else
          ld   h, high repeat      ; 2:7
    endif
          ex (sp),hl               ; 1:19
          ret                      ; 1:10
                                   ;[8:58]
                                  ;[10:66]

    A jeste jedna varianta do poctu. Jen kdyz je rozbalene LDI mimo predel "segmentu". Navic tahle by vyzadovala inicializaci (zapsani 2 bajtu), takze dalsi takty a bajty.

            add  a, low repeat       ; 2:7
            ld   (in_ram+1), a       ; 3:13
      if ((0xFF & repeat) >= (256-2*16))
            .error ldi deli 256 segment!
      endif
            jp   in_ram              ; 3:10
                                    ;[11:40]
    ...
    in_ram:
            jp repeat                ; 3:10
  • 2. 5. 2023 9:40

    atarist

    Na tom je asi nejzajimavejsi to, ze LDIR mel byt tim spravnym resenim, ale asi bylo nejjednodussi ho implementovat tak, ze se vlastne porad opakuje jeho nacitani z pameti, Jako asi to chapu - resi se tak problem preruseni apod. - ale pres zasobnik to tusim jde napsat skoro az 2x rychleji.

    (na 6502 to je uplne jina debata, tam se bez automodifukujiciho kodu asi neda rozumne obejit - zadny LDIR nemame :/).

  • 2. 5. 2023 11:26

    Korporátní lopata

    No, ono to kopírovaní přes zásobník není až tak velká výhra. Ten statickej příklad v článku je opravdu rychlej, ale v praxi chce člověk mít to kopírování přece jen ve smyčce, obzvlášt když přenáší několik kilobajtů dat. A tam narazí na to, že pro každou iteraci potřebujete počítat adresy zdroje a cíle a znovu je vložit do registru SP. Ve výsledku je kopírování přes zásobník přinejlepším jen o trochu rychlejší než rozvinutá smyčka LDI.
    Když se na to člověk podívá v kontextu toho, že limitem reálného 8 bitového počítače je rychlost paměti, tak na 6502 je obrovský problém s tím, že na kopírování jednoho bajtu (jeden read, jeden write) je potřeba hodně čtení instrukcí (opcode fetch). Na stejně rychlé paměti nejde prostě kopírovat tak rychle jako pomocí LDI.
    6809 má podobný problém, ale má dvě výhody: dva zásobníky a zvláštní implementaci push a pull ze zásobníku. Jde o to, že PSH,PUL instrukce jsou implementované jako jeden opcode pro PUL a jeden PSH, a za nimi následuje parametrický bajt. Každý bit parametrického bajtu určuje, který registr se vloží/načte ze zásobníku. Takže PSH/PUL má na 6809 nejlepší poměr přenesených dat a načtených instrukcí. I tak je rychlá kopírovací rutina přes dva zásobníky jen o malinko rychlejší než LDI a má samozřejmě problém s přerušením.
    Nástupci 6502 a 6809, tedy 65816 a 6309 už mají dedikované instrukce pro blokové kopírování.

  • 2. 5. 2023 11:50

    atarist

    jj ja vim, kde se pouzival, ale bylo to takove slabsi (trosku jako 6809, ale tu aspon pouzivali na videoautomatech). Nakonec i vsechny tri firmy, co zacinali na osmibitech s 6502 (Apple, Atari, CBM), presly na Motorolu...

  • 2. 5. 2023 16:28

    xbastaj

    Děkuju za další pěkný článek.
    Když jsem se po zkušenostech s 8080, 8086, 8051 (no a trochu 6502) dostal k 8-bitovému atmelu tak moje první reakce na instrukční sadu byla že autoři si vzali to nejlepší z Intelu, Motoroly a možná i jiných.
    Hlavně se mi líbily instrukce pro čtení/zapis z/do paměti, které automaticky inkremetují/de­krementují ukazatele.

            ldi r26,low(SCREEN_ADR)             ;nastaveni ukazatele X
            ldi r27,high(SCREEN_ADR)
    
            ldi r28,low(SECOND_SCREEN_BLOCK)    ;nastaveni ukazatele Y
            ldi r29,high(SECOND_SCREEN_BLOCK)
    
            ldi r18,low(SCREEN_BLOCK_SIZE)     ;nastaveni pocitadla opakovani
            ldi r19,high(SCREEN_BLOCK_SIZE)
    
    cyklus: ld r16,X+       ;nacteni hodnoty z pameti             2
            st  Y+,r16      ;zapis do pameti                      2
            dec r18         ;snizeni pocitadla                    1
            brne cyklus     ;opakovani dokud R18<>0               2
            dec r19         ;snizeni pocitadla
            brne cyklus     ;opakovani dokud R19<>0

    Vnitřní smyčka trvá tedy pouze 7 taktů (a vzhledem k běžné frekvenci 16MHz je tedy asi 6x rychlejší). Je vidět že zkušenosti více než 20 let s prvními mikroporcesory se při návrhu zúročily. Předpokládám že svoji roli tady hraje také dvojúrovňový pipelining.

    2. 5. 2023, 16:30 editováno autorem komentáře

  • 2. 5. 2023 20:52

    Pavel Tišnovský
    Zlatý podporovatel

    to se vám možná bude líbit Uzebox: https://www.root.cz/clanky/herni-konzole-uzebox-s-osmibitovym-mikroradicem-atmega644/

    (ten video výstup by bylo lepší upravit na něco modernějšího, ale kompozitní vstup se dá ještě najít, nebo nějakej konvertor pro něj).

  • 2. 5. 2023 22:35

    xbastaj

    Uzebox je pěkná hračka. (Četl jsem všechny články o hardware....)
    Učím na střední škole a asi před 5 roky jsem měl žáka který se zajímal o herní konzole a v rámci praktické práce si chtěl pořídit Uzebox a zkusit si do něj napsat nějakou hru. Bohužel už se stavebnice nedala koupit. Jako náhradní program nakonec vznikla vektorová "herní konzole" (2x 8bitD/A - R-2R sít přímo na dvou portech a výstupy operačních zesilovačů připojené na X a Y vstupy CRT osciloskopu).
    Pěkná hračka je taky emulátor čipu AY-3-8500 pro TV hry založený na arduinu http://searle.x10host.com/AVRPong/index.html (použitý byl upravený návrh od Nostalcompa). Po připojení na dataprojektor málokdo odolal....

  • 2. 5. 2023 22:47

    xbastaj

    S kompozitním výstupem jsem zatím neměl problém, pořád je dost televizí co mají scart nebo cinch, no a zatím co jsem viděl tak každý dataprojektor má kompozitní vstup.

  • 3. 5. 2023 16:34

    Pavel Tišnovský
    Zlatý podporovatel

    tak to musim gratulovat k dobremu zaku! ze mel zajem, ktery presahoval hrani Minecraftu :-)

    K te vektorove konzoli: takze ty operacni zesilovace byly pouzity jako linearni interpolatory? Tj. "semtam" se zapsaly nove souradnice [x,y] a pockalo se, az to paprsek dokresli? To by bylo pekne reseni, jeste bych asi pridal Z vystup s intenzitou paprsku (pokud to osciloskop umi).

  • 3. 5. 2023 18:35

    dw

    takze ty operacni zesilovace byly pouzity jako linearni interpolatory? tie tam boli imho koli prisposobeniu, vystup z r2r je prudovy, bez toho operaku by bol vystu nelinearny (pri covoxe je to jedno)

  • 3. 5. 2023 19:36

    Pavel Tišnovský
    Zlatý podporovatel

    Mě šlo o to, že třeba na Vectrexu to je takto řešeno. Program musí generovat jen koncové body vektorů a zbytek si dopočítají integrátory (na vstupu asi bude výstup z toho r2r žebříku). Takže ten program nakonec může být docela pomalý a určitě nemusí počítat nějakého Bresenhama atd.

    Je to vidět tady
    https://www.root.cz/clanky/historie-vyvoje-pocitacovych-her-16-cast-herni-konzole-vectrex/#k07

    hlavně na schématu https://i.iinfo.cz/images/391/7720.png

  • 3. 5. 2023 20:47

    xbastaj

    Takhle dokonalý jako Vectrex to není. Vykreslování je realizováno softwarově jeden 8.bitový port X a druhý Y. Aby se vykreslila čára musí se kreslit "pomalu" 1b za 5us (0,2b/us) což prři opakovací frekvenci 150Hz vychází na celkovou délku čáry 1300 bodů. Na herní ploše 160x160 jsou vykreslovány objekty o velikosti 20x20 (délka čáry 80-120) no a víc jak 10 se jich na obrazovku nevejde. Žádné složité výpočty program nedělá, objekty se skládají z vodorovné, svislé a šikmé (45°) čáry takže na všechno stačí jednoduchá smyčka s DEC a INC
    ukázka vykreslovaní:

    .macro CaraL                ;Cara vlevo
            ldi r21,@0
    cal:    out PORTB,r8        ;vystup X
            out PORTD,r9        ;vystup Y
            dec r8              ;zmena souradnice
            call delay_cara     ;zpozdeni
            dec r21             ;snizeni pocitadla
            brne cal            ;cyklus
    .endmacro
    ;..............................................................
    .macro CaraUR               ;Sikma cara v pravo nahoru
            ldi r21,@0
    caur:   out PORTB,r8        ;vystup X
            out PORTD,r9        ;vystup Y
            inc r8              ;zmena souradnice do prava
            inc r9              ;zmena souradnice nahoru
            call delay_cara     ;zpozdeni
            dec r21             ;snizeni pocitadla
            brne caur           ;cyklus
    .endmacro
    
    ;-------Vykresleni hrace---------------------------------------
    
    Hrac:   mov r8, r5
            ldi r20, YHrac
            mov r9, r20
            CaraDR VelObj/2
            CaraD  VelObj/4
            CaraDL VelObj/4
            CaraL  VelObj/2
            CaraUL VelObj/4
            CaraU  VelObj/4
            CaraUR VelObj/2
            ret
    
    ;-------Objekt04 Ctverec X-------------------------------------
    .org AdrObj04*0x100
            ldi r20, VelObj/2
            sub r8, r20
            CaraR VelObj
            CaraU VelObj
            CaraL VelObj
            CaraD VelObj
            CaraUR VelObj
            ldi r20, VelObj
            sub r8, r20
            CaraDR VelObj
            ret

    3. 5. 2023, 20:51 editováno autorem komentáře

  • 4. 5. 2023 0:51

    dw

    Na to by ale dva operaky skutocne nestacili. Podla toho co vidim v tej scheme k vectrexu, tak sa to chova podobne ako turtle grafika s tym ze jediny sposob ako ovladat zelvu (elektronovy luc, kresliaci na luninofor) je nastavovat napatie ktore udava cielove suradnice do ktorych sa ma zelva dostat. Zelva ma 2 rychlosti - vykreslovanie a "teleport" (analogovy spinac v spatnej vazbe integratoru, tvoreny 4066). Naviac procesor nema spatnu vezbu kde sa v skutocnosti zelva nachadza, vie len to kde by sa nachadzat mala. Pride mi ze kod procesoru bude za tychto podmienok o dost zlozitejsi ako jednoduche priame hybanie zelvou. Ale umozni to vykreslit komplikovanejsiu grafiku.

    Ten operak tvoriaci prudovo napatovy konvertor mate vo vectrexu pripojeny na vystup MC1408. V spominanom skolskom projekte budu predpokladam tie r2r prevodniky dva a tym padom aj dva operaky ktore prevadzaju prudovy vystup (vysoka impedancia) na napatovy vystup (nizka impedancia).