теория спрайтовго движка

Ответить
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

теория спрайтовго движка

Сообщение lenin1st »

не буду приводить расчёты (потому что глупец). но кажется (я практически уверен что это факт) единственный способ это раскрытие циклов - абстрактно говоря. потому что мы тупо расписываем множество ветвлений, предполагая что можно заранее посчитать ветку поведения (скажем). у меня просто вопрос, так все делают: раскрывают на 16кб варианты отрисовки 8x8 спрайта со всеми смещениями. но если грубо посчитать в уме, не получается чтобы даже 8*8 вариантов помещалось в 16кб. а ведь ещё есть специфика экрана, с его тремя областями. вопрос в том: как это обычно делают?
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

[img=1323]https://www.shemale-porn-galls.com/galls/baileyjay/sex_hat_pic/3.jpg[/img]
Последний раз редактировалось lenin1st 15 сен 2022, 08:46, всего редактировалось 1 раз.
Аватара пользователя
shiny
Сообщения: 9825
Зарегистрирован: 22 дек 2016, 00:00
Откуда: в Шуе бал

Сообщение shiny »

лобовое решение: вывод на экран по линиям.
обходное решение - вывод "змейкой"
Ненависть- это подарок
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

да это понятно всё. хотя что значит змейкой я не понял. я про то что есть алгоритм вывода в любую точку экрана, так. а если раскрыть циклы и знать первую точку (соответственно), можно очень навыигрывать сократив циклы. естественно все так делают, просто интересно насколько жёстко и что именно раскрывают. потому что так-то 64 варианта только начала спрайта есть, а ещё специфика экрана.
Последний раз редактировалось lenin1st 15 сен 2022, 15:00, всего редактировалось 1 раз.
Аватара пользователя
shiny
Сообщения: 9825
Зарегистрирован: 22 дек 2016, 00:00
Откуда: в Шуе бал

Сообщение shiny »

если выводить по линиям, то выходит побайтное копирование данных на экран, например (HL)->(DE). Нарисовав линию спрайта, нужно перейти вниз, по экрану, например

Код: Выделить всё

nbde    INC D:LD A,D:AND 7:RET NZ
        LD A,E:ADD A,#20:LD E,A:RET C
        LD A,D:SUB 8:LD D,A:RET
Казалось бы, удобнее запомнить DE и сделать так

Код: Выделить всё

push de
;копирование линии
pop de
call nbde;переход на линию экрана
Трюк в том, что первая линия копируется, без сохранения регистра DE, а следующая линия копируется обратно
т.е. в первой линии будет
ld a,(hl)
ld (de),a
inc hl
inc e

а во второй

ld a,(hl)
ld (de),a
inc hl
dec e

Понятно, что нужно подготовить спрайт, чтобы шел другой порядок байтов.
Ненависть- это подарок
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

щас пока не понял (уже накидался). но самое быстрое, как я понял, это заполнять экран через стек - это не наш путь, просто хотелось упомянуть. прочитаю завтра - осмыслю.
Последний раз редактировалось lenin1st 15 сен 2022, 17:02, всего редактировалось 1 раз.
Аватара пользователя
shiny
Сообщения: 9825
Зарегистрирован: 22 дек 2016, 00:00
Откуда: в Шуе бал

Сообщение shiny »

стек можно использовать, НО:
-число размер спрайта четное
-положение на экране одно и тоже
-нужно уместиться в 1 фрейм между прерываниями, иначе данные засрутся.

пример:

Код: Выделить всё

 ld (backsp+1),sp;сохранить стек
 ld sp, sprite ; стек указывает на данные спрайта
;развернутый цикл
 pop hl
 ld ($4000),hl
 pop hl
 ld ($4002),hl
 ... продолжить копирование одной линии
;вторая линия
 pop hl
 ld ($4100),hl
 pop hl
 ld ($4102),hl

backsp: ld sp,0;восстановить значение стека

можно по-другому, но с ограничениями.Например
pop bc
ld (hl),c
inc l
ld (hl),b
inc l
Ненависть- это подарок
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

я немного ушёл головой и телом в плотнейшую лень. но держал в голове эту тему. вчера-позавчера начал ковырять. вот кстати даже ни разу не писал спрайты (не в частностях каких-то там), интересно поковыряться.

Шыншыл
в nbde наверно не ret c и sub 8, а ret nc и add 8. и типа в среднем тогда (168*(4+4+7+11) + 21*(4+4+7+11+4+7+4+11) + 3*(4+4+7+11+4+7+4+11+4+7+4+10)) / 192 = 29.64~ ну да, я же что-то такое и писал уже. а почему-то в этот раз мне в голову залетела только таблица адресов.

ну а стек это понятно, чтобы экран двигать. то наверно не об этом.

ну а что поделать, вкину что высрал и пусть будет уже.
Последний раз редактировалось lenin1st 13 окт 2022, 15:43, всего редактировалось 1 раз.
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

СПРАЙТ КСОРАМИ

оговорюсь об ограничениях (помимо того что это тупой ксор): немодифицирующийся код (код не меняется во время исполнения или непосредственно до); не выравненный по прерыванию и с правильным стеком - код всюду готов к прерыванию; код reentrant (при сохранении контекста на прерывании, можно вызвать этот же код пока он в процессе); формат хранения спрайта по линиям; отрисовка прямо на экран без теневого; без обработки ситуаций половинчатой отрисовки на краях экрана. по сути, всё для упрощения.

лобовая реализацию:

Код: Выделить всё

  #define SCREENPTR(HIGHBYTE, X, Y)                             \
    ((char*)(((HIGHBYTE) | ((Y) & 0xC0) >> 3 | (Y) & 0x07) << 8 \
             | (((Y) & 0x38) << 2 | (X) >> 3)))

  #define SPRITE(XBYTE, Y) (gl_ptr+(Y)*2+(XBYTE))

  void draw_sprite(int vpos_x, int vpos_y, int vsizex, int vsizey) {

    int x, y; char *ptr;

    for (y = 0; y != vsizey; y++) {

      for (x = 0; x != vsizex + (vpos_x&7?8:0); x += 8) {

        ptr = SCREENPTR(0x40, vpos_x+x, vpos_y+y);

        if (x < vsizex) {

          *ptr ^= *SPRITE(x/8, y) >> (vpos_x&7);
        }
        if (x >= 8 && (vpos_x&7)) {

          *ptr ^= *SPRITE((x-8)/8, y) << (8-(vpos_x&7));
        }
      }
    }

    return;
  }
первое что нужно раскрывать, это построчную отрисовку. потому что по горизонтали это тупо инкримент и потому что опционального сдвига нет (а мне почему-то кажется что это горячее место). получается 8 блоков отрисовки линий с разным сдвигом. ориентировка на форматы спрайтов 8x8, 16x16, 24x24, покрывает практически все случаи (из адекватных), так что я нацелюсь на них. но начнум с 16x16 и дальше будет видно (примерно умножив на 3) до какой глубины это можно довести.

отрисовка 16x16 спрайта, линии:

Код: Выделить всё

    .globl _draw_sprite16_line1
  _draw_sprite16_line1:
    pop bc
    pop hl
    pop de
    push de
    push hl
    push bc

    ;; 0 head
    ld  a,(de)
    srl a       ;; rrca; rrca; and a,#0x3f ...
    ld  b,(hl)
    xor a,b
    ld  (hl), a

    ;; 1 body
    ld  a,(de)
    rrca        ;; rrca; rrca; a,#0xc0 ...
    and a,#0x80 ;;
    ld  b,a

    inc l
    inc de

    ld  a,(de)
    srl a       ;; rrca; rrca; and a,#0x3f ...
    or  a,b
    ld  b,(hl)
    xor a,b
    ld  (hl), a

    ;; 2 tail
    ld  a,(de)
    rrca
    and a,#0x80 ;; rrca; rrca; and a,#0xc0 ...

    inc l

    ld  b,(hl)
    xor a,b
    ld  (hl), a

    ret
отрисовка 16x16 спрайта, верхний уровень:

Код: Выделить всё

  void draw_sprite2(int vpos_x, int vpos_y, int vsizex, int vsizey) {

  int y; char *ptr;

    if (vsizex != 16) {

      draw_sprite(vpos_x, vpos_y, vsizex, vsizey);
    }

    for (y = 0; y != vsizey; y++) {

      ptr = SCREENPTR(0x40, vpos_x, vpos_y+y);

      switch (vpos_x & 7) {
      case 0: draw_sprite16_line0(ptr, SPRITE(0, y)); break;
      case 1: draw_sprite16_line1(ptr, SPRITE(0, y)); break;
      case 2: draw_sprite16_line2(ptr, SPRITE(0, y)); break;
      case 3: draw_sprite16_line3(ptr, SPRITE(0, y)); break;
      case 4: draw_sprite16_line4(ptr, SPRITE(0, y)); break;
      case 5: draw_sprite16_line5(ptr, SPRITE(0, y)); break;
      case 6: draw_sprite16_line6(ptr, SPRITE(0, y)); break;
      case 7: draw_sprite16_line7(ptr, SPRITE(0, y)); break;
      }
    }

    return;
  }
(без пролога pop/push но с эпилогом ret, потому что пролог временный)
70+149+185+201+217+201+185+149 = 1357 t-states
11+28+38+42+46+42+38+28 = 273 bytes

сравнение с лобовой реализацией (прерываний на спрайт - отрисовка и очистка):
94/176 = 0.535~
377/176 = 2.142~
(1-2714/70908/(94/176) = 0.92~ процент времени в вышестоящем коде (прологе, в
C и библиотечном коде на прерываниях).
https://disk.yandex.com/d/p9yWzCoo2UNZtA
(тапка теста + частичный код (без библиотеки и сборщика))
Аватара пользователя
shiny
Сообщения: 9825
Зарегистрирован: 22 дек 2016, 00:00
Откуда: в Шуе бал

Сообщение shiny »

lenin1st 88509 писал(а): в nbde наверно не ret c и sub 8, а ret nc и add 8. и типа в среднем тогда (168*(4+4+7+11) + 21*(4+4+7+11+4+7+4+11) + 3*(4+4+7+11+4+7+4+11+4+7+4+10)) / 192 = 29.64~ ну да, я же что-то такое и писал уже. а почему-то в этот раз мне в голову залетела только таблица адресов.
то, что есть. Для процедуры были другие изябретения.
Ненависть- это подарок
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

СПРАЙТ КСОРАМИ

дальше нужно раскрыть цикл for (y = 0; y != vsizey; y++), что даст 8 блоков по 16 вызовов конкретного сдвига линии (call draw_sprite16_line<сдвиг>).

как считать адрес на экране по Y (накидал что смог придумать):
0) всегда считать заново
1) пересчитывать инкрементально (в смысле не с нуля)
2) заранее определить специфику адреса и чётко располагать нужные операции
3) определить таблицу адресов каждого Y на экране: 192*2 = 384.
x) может что-то смеженное, где как-то замостить вызовы в цикле, может до кратности 8 линий, потом считать адрес заново...

2) кажется совсем нереальном, потому что специфика экрана слишком сложная
0) на вскидку, если требуется случайный адрес получить, даже быстрее его посчитать. но последовательно, исходя из того что у меня получилось, лучше таблица.
1) если бы было последовательное заполнение по знакоместам, там что-то можно было бы придумать. x + 32 давал бы переполнение через 7 в старшем байте... (вот тут я похоже и погнал, не знаю что меня дёрнуло)
3) беру

раскрыл я одну функцию и понял, что это уже никуда не годится. нет смысла
вызывать отрисовку линий не в цикле, ведь (в моей реализации, конечно).

средняя сложность отрисовка линии спрайта 16x16 в t-states
l = (70+149+185+201+217+201+185+149) / 8
общий размер отрисовки линий спрайта 16x16
b = 11+28+38+42+46+42+38+28

вызов отрисовки линии в цикле по Y:
142 + 10 + (91+l) * 16 = 4322 t-states (в среднем на спрайт)
(20 + 1 + 19) * 8 + b = 593 bytes (всего)

раскрытие по Y с вызовом отрисовки линии:
135 + (77+l)*16-6 = 4075
(18 + 15*16 - 1) * 8 + b = 2329

тело отрисовки линии в цикле по Y:
142 + 10 + (91-17-10+l) * 16 = 3890
(20 + 1 + 19-3) * 8 + b - 1*8 = 561

раскрытие по Y с встраиванием тела отрисовки линии
...

ну тут понятно, что даже второй вариант уже избыточен. в третьем убирается вызов (call/ret), со встраиванием тела отрисовки линии, и накладные на loop уже компенсируются.

(для наглядности вариант "вызов отрисовки линии в цикле по Y"):

Код: Выделить всё

    .globl _draw_sprite16_lines0
  _draw_sprite16_lines0:

    exx
    pop hl
    pop de
    pop bc
    push bc ; pspr
    push de ; x, y
    push hl ; addr

    push bc
    ld  b,#0x00
    ld  c,e
    ld  hl,#_gl_scryaddr
    add hl,bc
    add hl,bc
    exx
    pop bc
    ld d,#0x10

    ;; hl` = pscryaddr
    ;; bc` = tmp
    ;; de` = x, y
    ;; hl  = *pscryaddr + x
    ;; bc  = pspr
    ;; de  = counter, tmp
  l_16_0: ;;  l_16_1 ...
    exx
    ld  a,d
    or  a,(hl)
    inc hl
    ex  af,af
    ld  a,(hl)
    inc hl
    exx
    ld  h,a
    ex  af,af
    ld  l,a
    call _draw_sprite16_line0 ;; _draw_sprite16_line1 ...

    inc bc
    dec d
    jp  nz,l_16_0 ;;  jp  nz,l_16_1 ...
    ret

Код: Выделить всё

  void draw_sprite2(int vpos_x, int vpos_y, int vsizex, int vsizey) {

    if (vsizex != 16 && vsizey != 16) {

      draw_sprite(vpos_x, vpos_y, vsizex, vsizey);
    }

    switch (vpos_x & 7) {
    case 0: draw_sprite16_lines0(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 1: draw_sprite16_lines1(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 2: draw_sprite16_lines2(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 3: draw_sprite16_lines3(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 4: draw_sprite16_lines4(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 5: draw_sprite16_lines5(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 6: draw_sprite16_lines6(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    case 7: draw_sprite16_lines7(vpos_x>>3<<8 | vpos_y, SPRITE(0, 0)); break;
    }

    return;
  }
сравнение c лобовой реализацией (прерываний на спрайт - отрисовка и очистка):
26/176 = 0.1477~
377/176 = 2.142~
1-4075/70908/(26/176) = 0.61~ процент времени в вышестоящем коде.
https://disk.yandex.com/d/3PmmuihT1kJRkg
(тапка теста + частичный код (без библиотеки и сборщика))
Последний раз редактировалось lenin1st 13 окт 2022, 15:51, всего редактировалось 1 раз.
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

короче буду исправляться. переделаю, посчитаю. доделаю верхний уровень на асме. и придумаю как обрабатывать частичную отрисовку (20е слово в теме) на краях. вроде понятно более-менее.
Последний раз редактировалось lenin1st 13 окт 2022, 16:03, всего редактировалось 1 раз.
Аватара пользователя
shiny
Сообщения: 9825
Зарегистрирован: 22 дек 2016, 00:00
Откуда: в Шуе бал

Сообщение shiny »

чото как-то некошерно рисуется

Таки написал бестолковую тулзу конверсии спрайта в "змейку". Пример там же.
[file=https://zxdemos.ru/uploads/files/93/bdf3700964cc3cc84f9ae19784d6923d.zip]snakes.zip[/file]
Ненависть- это подарок
lenin1st
Сообщения: 84
Зарегистрирован: 02 фев 2022, 10:15

Сообщение lenin1st »

извиняюсь что не отписываюсь по теме. с широкой на широкой: я бухаю и сплю, и мне похуй - почти всё время, и курю ганжубас (я же русский). иногда я просыпаюсь по среди ночи от того что блюю под себя, и тогда я думаю не о самоидентификации, по каким-то там признакам, а о физической смерти, и потом плачу.
Ответить