Важная информация

User Tag List

Страница 1 из 7 12345 ... ПоследняяПоследняя
Показано с 1 по 10 из 70

Тема: LASER BASIC 2

  1. #1
    Veteran Аватар для Oleg N. Cher
    Регистрация
    24.08.2007
    Адрес
    Днепропетровская обл.
    Сообщений
    1,599
    Спасибо Благодарностей отдано 
    2,172
    Спасибо Благодарностей получено 
    133
    Поблагодарили
    99 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию LASER BASIC 2

    Всем привет. Есть мысль разработать оптимизированный набор процедур - аналогов команд LASER BASIC.

    Я пока набросал универсальную подпрограмму вывода познакоместного спрайта с атрибутами. Требуется ваша помощь (особенно обращаюсь к Destr'у, AzAtom'у и Reobne). Код тестировал на спрайтах разного размера. По-моему, получилось довольно хорошо.

    • Конструктивная критика (баги? ускорить без серьёзного увеличения размера?)

    • Нужно модифицировать, чтобы можно было выводить спрайты с выходом за пределы экрана (как по X, так и по Y).

    • Вообще философский момент. Я кинулся кодить, но потом понял, что реализовал формат, не совместимый с оригинальным LASER BASIC'ом. Там сперва спрайт выводится весь по ширине, потом следующая пиксельная линия и т.д. А у меня получилось так: выводится первое знакоместо целиком, потом второе, и так на ширину линии, потом преход к следующей знакоместной строке. Чтобы я не занимался мазохизмом, скажите, пожалуйста, есть ли выверенные методики вывода подобных спрайтов, которые диктуют наиболее удачный формат для быстрого вывода? Берём в расчёт то, что самый внутренний цикл в моей реализации крутится 8 раз (выводим знакоместо), а в лазерной - количество, равное ширине спрайта (выводим строку). То есть мой вариант формата во внутреннем цикле оптимальнее лазерного для спрайтов, ширина которых меньше восьми знакомест.


    Так что есть альтернатива: или слегка модифицировать формат и написать перекодировщик. Или остаться на старом лазерном формате. Это вопрос, на который я ещё себе не ответил.

    Атрибуты бы целенаправленно не хотелось вставлять вперемешку с блоками пикселей, хотя такой вариант я тестировал. Но решено, что у нас останется возможность выводить спрайты без атрибутов, и тогда, если они в конце, их вывод просто легче отрезать, чем обходить атрибутные данные доп. кодом.

    Критикуем, господа.

    Код:
    /*********************/
    /*   LASER BASIC 2   */
    /*  by Oleg N. Cher  */
    /*   zx.oberon2.ru   */
    /*********************/
    
    extern unsigned int Laser2_SPRT_ADR;  // Sprite file start address
    
    /* Спрайты хранятся в памяти в следующем формате:
    Байт 1 - номер спрайта.
    Байт 2 - младший байт размера спрайта (9*HGT*LEN+3).
    Байт 3 - старший байт размера спрайта.
    Байт 4 - длина спрайта.
    Байт 5 - высота спрайта.
    8*HGT*LEN - данные о состоянии
    пикселей.
    HGT*LEN - атрибуты.
      ИТОГО: 9*HGT*LEN+5 байтов.
    */
    
    /*--------------------------------- Cut here ---------------------------------*/
    void Laser2_ATOF (void)
    {
    __asm
                 LD    A, #0xC9    ; "RET"
                 LD    (Laser2_ATOF_IN), A
    __endasm;
    } //Laser2_ATOF
    
    /*--------------------------------- Cut here ---------------------------------*/
    void Laser2_ATON (void)
    {
    __asm
                 LD    A, #0xED    ; "LD DE,(ADDR) ED5BXXXX"
                 LD    (Laser2_ATOF_IN), A
    __endasm;
    } //Laser2_ATON
    
    /*--------------------------------- Cut here ---------------------------------*/
    
    void Laser2_PTBL (signed char col, signed char row, unsigned char spn) __naked {
    __asm
                 POP   HL
                 POP   BC        ; C = col; B = row
                 POP   DE        ; E = spn
                 PUSH  DE
                 PUSH  BC
                 PUSH  HL
                 XOR   A         ; NOP
                 JP    _Laser2_PUT_SPRITE
    __endasm;
    } //Laser2_PTBL
    
    /*--------------------------------- Cut here ---------------------------------*/
    void Laser2_PTOR (signed char col, signed char row, unsigned char spn) __naked {
    __asm
                 POP   HL
                 POP   BC        ; C = col; B = row
                 POP   DE        ; E = spn
                 PUSH  DE
                 PUSH  BC
                 PUSH  HL
                 LD    A, #0xB6  ; OR (HL)
                 JP    _Laser2_PUT_SPRITE
    __endasm;
    } //Laser2_PTOR
    
    /*--------------------------------- Cut here ---------------------------------*/
    void Laser2_PTXR (signed char col, signed char row, unsigned char spn) __naked {
    __asm
                 POP   HL
                 POP   BC        ; C = col; B = row
                 POP   DE        ; E = spn
                 PUSH  DE
                 PUSH  BC
                 PUSH  HL
                 LD    A, #0xAE  ; XOR (HL)
                 JP    _Laser2_PUT_SPRITE
    __endasm;
    } //Laser2_PTXR
    
    /*--------------------------------- Cut here ---------------------------------*/
    void Laser2_PTND (signed char col, signed char row, unsigned char spn) __naked {
    __asm
                 POP   HL
                 POP   BC        ; C = col; B = row
                 POP   DE        ; E = spn
                 PUSH  DE
                 PUSH  BC
                 PUSH  HL
                 LD    A, #0xA6  ; AND (HL)
                 JP    _Laser2_PUT_SPRITE
    __endasm;
    } //Laser2_PTND
    
    /*--------------------------------- Cut here ---------------------------------*/
    void Laser2_PUT_SPRITE (void) __naked {
    //  A: mode; C: col; B: row; E: spn
    __asm
    .globl Laser2_ATOF_IN
    
                      LD    (SPRT_MODE_IN$), A ; Set draw mode
    
    ; Процедура расчёта адреса экрана из координат
    ; Вход:  C=x, B=y
    ; Выход: HL = адрес видеопамяти
    
                      LD    A, B
                      AND   #7
                      RRCA
                      RRCA
                      RRCA
                      OR    C
                      LD    L, A
                      LD    A, B
                      AND   #24
                      OR    #0x40
                      LD    H, A            ; 14 байт, 53 такта
    
                      LD    (SCR_ADR_IN$+1), HL
    
                      LD    HL, (_Laser2_SPRT_ADR)
    FIND_BY_N_IN$:    LD    A, (HL)         ; N of a sprite
                      OR    A
                      RET   Z
                      INC   HL
                      CP    E               ; spn
                      JR    Z, SPRT_FOUND_IN$
                      LD    C, (HL)
                      INC   HL
                      LD    B, (HL)
                      ADD   HL, BC          ; + offset to next sprite
                      JR    FIND_BY_N_IN$
    SPRT_FOUND_IN$:   INC   HL
                      INC   HL
                      LD    C, (HL)         ; length of sprite
                      INC   HL
                      LD    B, (HL)         ; height of sprite
                      LD    (SPRT_HGT_LEN_IN$+1), BC
                      INC   HL
    SCR_ADR_IN$:      LD    DE, #0          ; adr of sprite (up left corner)
    SPRT_HLINE_IN$:   LD    A, E            ; Begin of loop on charlines
                      LD    (SCR_LOBYTE_IN$+1), A
                      PUSH  BC
                      LD    B, #8
    SPRT_CHAR_IN$:    LD    A, (HL)
    SPRT_MODE_IN$:    NOP                   ; NOP | AND (HL) | OR (HL) | XOR (HL)
                      LD    (DE), A
                      INC   HL
                      INC   D
                      DJNZ  SPRT_CHAR_IN$
                      LD    B, #8           ; Draw 8 bytes (one charline)
                      LD    A, D
                      SUB   A, B
                      LD    D, A
                      INC   E               ; Next screen line
                      DEC   C
                      JR    NZ, SPRT_CHAR_IN$
    SCR_LOBYTE_IN$:   LD    A, #0
                      ADD   #0x20           ; Next charline
                      LD    E, A            ; If carry then jump to next third of screen
                      JR    NC, CONTIN_1_3_IN$
                      LD    A, D            ; Next third of screen
                      ADD   B
                      LD    D, A            ; DE := DE + 0x0800
    CONTIN_1_3_IN$:   POP   BC
                      DJNZ  SPRT_HLINE_IN$  ; End of loop on charlines (the same third)
    Laser2_ATOF_IN:                         ; RET
                      LD    DE, (SCR_ADR_IN$+1)
                      LD    A, D            ; Calculate attribute address
                      RRCA
                      RRCA
                      RRCA
                      AND   #3
                      OR    #0x58
                      LD    D, A
    
    SPRT_HGT_LEN_IN$: LD    BC, #0
                      LD    A, #32
                      SUB   A, C              ; 32 - len
                      LD    (SPRT_HGT_DIS_IN$+1), A
    DRAW_ATRLINE_IN$: PUSH  BC                ; Begin of loop on charlines
                      LD    B, #0
                      LDIR
                      LD    A, E
    SPRT_HGT_DIS_IN$: ADD   #0
                      LD    E, A
                      LD    A, C
                      ADC   D
                      LD    D, A
                      POP   BC
                      DJNZ  DRAW_ATRLINE_IN$
                      RET
    __endasm;
    } //Laser2_PUT_SPRITE
    Последний раз редактировалось Oleg N. Cher; 12.09.2016 в 03:29.

  2. #1
    С любовью к вам, Yandex.Direct
    Размещение рекламы на форуме способствует его дальнейшему развитию

  3. #2
    Veteran Аватар для Destr
    Регистрация
    26.03.2008
    Адрес
    Питкяранта
    Сообщений
    1,802
    Спасибо Благодарностей отдано 
    249
    Спасибо Благодарностей получено 
    113
    Поблагодарили
    87 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Oleg N. Cher Посмотреть сообщение
    модифицировать формат и написать перекодировщик
    Поддерживай тот формат который уже есть.
    Выводить по линиям - это как-бы уже по умолчанию стандарт.
    По знакоместам - это для других типов спрайтов (типа как в Popeye или TrapDoor). Но там можно обойтись банальным PRINT.

  4. #3
    Guru Аватар для null_device
    Регистрация
    26.09.2009
    Адрес
    г. Красноярск
    Сообщений
    3,090
    Спасибо Благодарностей отдано 
    20
    Спасибо Благодарностей получено 
    83
    Поблагодарили
    67 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Основной "спотыкач" лазер-бейсика, невозможность работы в нем, на 128К машине (без принудительного перехода в 48К режим). Он, вроде как использует для своих нужд буфер принтера, чем портит системные переменные.
    Когда есть, но не знаешь где - это все равно, что нету.

  5. #4
    Veteran Аватар для Oleg N. Cher
    Регистрация
    24.08.2007
    Адрес
    Днепропетровская обл.
    Сообщений
    1,599
    Спасибо Благодарностей отдано 
    2,172
    Спасибо Благодарностей получено 
    133
    Поблагодарили
    99 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Destr, а если по знакоместам быстрее? Ведь на практике большинство спрайтов имеют ширину меньше 8 символов, а имеет смысл в первую очередь оптимизировать внутренний цикл.

    null_device, оригинальный LASER BASIC я вообще не рассматриваю как серьёзный инструмент для разработки - на сегодняшний день есть более совершенные средства, генерирующие настоящий машинный код (в отличие от интерпретатора или компилятора LASER BASIC в пи-код). Поэтому я беру только принцип.

    Библиотека Raydac & Michailov [HVG] (есть реализация для SDCC и ZXDev) не использует буфер принтера и даже умеет выводить графику на второй экран (#C000).

  6. #5
    Veteran
    Регистрация
    01.03.2005
    Адрес
    Новосибирск
    Сообщений
    1,979
    Спасибо Благодарностей отдано 
    69
    Спасибо Благодарностей получено 
    261
    Поблагодарили
    99 сообщений
    Mentioned
    6 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Когда-то делал процедуру универсальную для спрайтов, она здесь http://zx-pk.ru/threads/20554-vyvodi...ts-ekrana.html
    Код, и пример работы во вложении к теме.

    Формат спрайта 8 строк спрайта, строка атрибутов, ... , ...
    Последний раз редактировалось drbars; 09.09.2016 в 09:22.

  7. #6
    Veteran Аватар для Oleg N. Cher
    Регистрация
    24.08.2007
    Адрес
    Днепропетровская обл.
    Сообщений
    1,599
    Спасибо Благодарностей отдано 
    2,172
    Спасибо Благодарностей получено 
    133
    Поблагодарили
    99 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    drbars, благодарю. Посмотрел код, правда, не знаю, как он может быть мне полезен. Разве что присмотрюсь к деталям реализации отсечения невидимой части спрайта.

    Основное преимущество при построчном выводе спрайта - возможность пользоваться командами типа LDI или разворачивать цикл вывода как-то иначе. Когда же в универсальной процедуре вывода спрайта используется логическая операция (AND/OR/XOR), смысла её разворачивать как бы и нет, я же не демки собираюсь писать. LDI в этом случае тоже не шибко поможет. Правильно я рассуждаю?

    Что касается размещения данных атрибута среди блоков пикселей. Да, это эффективнее, чем выводить их потом, но только если всегда используется вывод с атрибутами. В случае же LASER BASIC'овского PTBL/PTXR/PTOR/PTND это не всегда так.

  8. #7
    Member
    Регистрация
    21.05.2006
    Адрес
    Canada
    Сообщений
    78
    Спасибо Благодарностей отдано 
    3
    Спасибо Благодарностей получено 
    2
    Поблагодарили
    2 сообщений
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Hi Oleg,

    Код:
     void Laser2_PTBL (signed char col, signed char row, unsigned char spn) __naked {
     __asm
                  POP HL
                  POP BC;  C = col;  B = row
                  POP DE;  E = spn
                  PUSH DE
                  PUSH BC
                  PUSH HL
                  XOR A;  NOP
                  JP _Laser2_PUT_SPRITE
     __endasm;
     } // Laser2_PTBL
    Does oberon have function pointers? If not you can use callee convention to save some bytes in your binaries:

    Код:
     void Laser2_PTBL (signed char col, signed char row, unsigned char spn) __naked __z88dk_callee {
     __asm
                  POP HL
                  POP BC;  C = col;  B = row
                dec sp
                  POP DE;  D = spn
                push hl
                  XOR A;  NOP
                ld e,d  ; E = spn
                  JP _Laser2_PUT_SPRITE
     __endasm;
     } // Laser2_PTBL
    I had to do something weird in there because of the three bytes but with this in place calls to your subroutine will no longer have pops afterward.

    If you do need function pointers, you have to resort to some trickery as sdcc is not fully capable of function calls through pointers with fastcall or callee linkage.

  9. #8
    Veteran
    Регистрация
    01.03.2005
    Адрес
    Новосибирск
    Сообщений
    1,979
    Спасибо Благодарностей отдано 
    69
    Спасибо Благодарностей получено 
    261
    Поблагодарили
    99 сообщений
    Mentioned
    6 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Цитата Сообщение от Oleg N. Cher Посмотреть сообщение
    drbars, благодарю. Посмотрел код, правда, не знаю, как он может быть мне полезен. Разве что присмотрюсь к деталям реализации отсечения невидимой части спрайта.

    Основное преимущество при построчном выводе спрайта - возможность пользоваться командами типа LDI или разворачивать цикл вывода как-то иначе. Когда же в универсальной процедуре вывода спрайта используется логическая операция (AND/OR/XOR), смысла её разворачивать как бы и нет, я же не демки собираюсь писать. LDI в этом случае тоже не шибко поможет. Правильно я рассуждаю?

    Что касается размещения данных атрибута среди блоков пикселей. Да, это эффективнее, чем выводить их потом, но только если всегда используется вывод с атрибутами. В случае же LASER BASIC'овского PTBL/PTXR/PTOR/PTND это не всегда так.
    Полезность в общем принципе работы, блок расчета отсечения частей. Думаю цикл можно свернуть и сделать с любой логикой, что самой собой замедлит вывод.
    Атрибут помещен строками в тело спрайта затем, чтобы не делать повторного расчета отсечения для атрибута и не городить лишний цикл. Если у тебя спрайт цветной, а нужно вывести ч/б, то проще не выводить эту строку.

  10. #9
    Veteran Аватар для Oleg N. Cher
    Регистрация
    24.08.2007
    Адрес
    Днепропетровская обл.
    Сообщений
    1,599
    Спасибо Благодарностей отдано 
    2,172
    Спасибо Благодарностей получено 
    133
    Поблагодарили
    99 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Для перехода к следующей строке атрибутов спрайта за пределами экрана можно вычислить смещение с отсечением, и прибавлять это число. И его же, умноженное на 8, мы будем использовать, чтобы отсечь невидимые блоки пикселей, там в целом всё довольно эффективно можно сделать. Я закодирую и покажу.

    Кстати, оптимизировал чуть-чуть подпрограмму в первом посте темы. И набросал конвертер спрайтов из лазерного формата.

    Alcoholics Anonymous, yes, Oberon has function pointers (procedure variables):

    Код:
    VAR p: PROCEDURE (i: INTEGER): REAL;
    Последний раз редактировалось Oleg N. Cher; 11.09.2016 в 20:24.

  11. #10
    Veteran Аватар для Oleg N. Cher
    Регистрация
    24.08.2007
    Адрес
    Днепропетровская обл.
    Сообщений
    1,599
    Спасибо Благодарностей отдано 
    2,172
    Спасибо Благодарностей получено 
    133
    Поблагодарили
    99 сообщений
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    По умолчанию

    Laser2Demo.zip с прыгающим инопланетным чудиком. Вывод за пределами экрана пока не реализован. Бинарь занимает 986 байт, из них:

    661 байт - массив спрайтов (4 фазы чудика, 5x4 знакомест каждая, без атрибутов).
    123 байта - основная подпрограмма вывода спрайта Laser2_PUT_SPRITE_INSCR.
    202 байта - остальной код (в т.ч. процедуры установки цветов, задержки, инициализации и т.д.)

    Код:
    MODULE Laser2Demo; (*$MAIN*)
    
    (*   LASER BASIC 2 Demo for Sinclair ZX Spectrum 48 Kb   *)
    (* Copyright (C) 2016 Oleg N. Cher, VEDAsoft Oberon Club *)
    
    IMPORT gr := Laser2, b := Basic;
    
    (* Пример взят из книги "Как написать игру для ZX Spectrum" *)
    
    CONST
      FrameSizeBytes = 5(*hdr*) + 8*20(*data*) ;
    
    TYPE
      Sprites = ARRAY FrameSizeBytes * 4 (*frames*) + 1 OF CHAR;
    
    CONST
      Chudik = Sprites ( ... ); (* Данные спрайтов поскипаны *)
    
    VAR
      x, phase: SHORTINT;
    
    BEGIN
      b.Init;
    
      b.COLOR(b.Bright + b.Black*b.Paper + b.Yellow); b.BORDER(b.Black); b.CLS;
      gr.InitSpritesAr(Chudik); gr.ATOF;
    
      FOR x := -2+2 TO 30-2 BY 2 DO
        FOR phase := 1 TO 4 DO gr.PTBL(x, 5, phase); b.PAUSE(5) END;
      END;
    
      b.Quit
    END Laser2Demo.
    Старый вариант этой же демки (на библиотеке Raydac/Michailov, со сделанной мною смартлинковкой) занимает 2686 байт.

    Думаю, это хороший результат. Для дерзновенных предлагаю оптимизировать процедуру вывода ещё сильнее. ;-)

    В планах реализовать вывод спрайтов за пределами экрана. Потом ещё несколько подпрограмм: скроллинг окон, атрибутов и т.д. Все процедуры из набора LASER BASIC реализовывать не планирую, только нужные (мне).

    - - - Добавлено - - -

    Сделал процедуру для выбора экрана (по адресу), чтобы иметь возможность выводить на второй экран, да и вообще в виртуальный экран в памяти. Сначала хотел назвать SCRN, сообразно стилю LASER BASIC, но отказался от этого, назвал SetScreen. Так новая, не входящая в набор ранее, процедура будет заметнее, ибо коррелирует с InitSprites, InitSpritesEx и InitSpritesAr.

    Конвертер спрайтов LASER BASIC в LASER BASIC 2 будет включен в дистрибутив ZXDev (с исходниками). Конвертирует на уровне исходного текста, но не бинаря. Пока понимает только Оберон, планирую добавить Си. Утилита очень сырая, нет никакой обработки ошибок, но всё, что нужно, она делает, а больше пока не требуется.




    Типичный модуль ресурсов игры на Обероне может выглядеть так (сам инициализируя LASER BASIC 2):

    Код:
    MODULE Rsrc; (** Resource *)
    IMPORT Laser2;
    
    TYPE
      SpritesT = ARRAY SpriteSize OF CHAR;
    
    CONST
      Spr1* = 1; (* Константа - номер спрайта, экспортируется, т.е. её видно извне *)
      Spr2* = 2;
      (* ... *)
    
      Sprites = SpritesT ( ... );
    
    BEGIN
      Laser2.InitSpritesAr(Sprites)
    END Rsrc.
    Код:
    MODULE MyGame; (*$MAIN*)
    IMPORT Laser2, Rsrc;
      ...
    BEGIN
      ...
      Laser2.PTBL(x, y, Rsrc.Spr2);
      ...
    END MyGame.
    Последний раз редактировалось Oleg N. Cher; 11.09.2016 в 20:29.

Страница 1 из 7 12345 ... ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Похожие темы

  1. LASER BASIC
    от VETER в разделе Программирование
    Ответов: 8
    Последнее: 04.01.2015, 04:48
  2. laser basic
    от johnny в разделе Программирование
    Ответов: 32
    Последнее: 17.02.2014, 22:57
  3. LASER BASIC decompiled by HVG
    от Raydac в разделе Программирование
    Ответов: 0
    Последнее: 21.12.2009, 23:04
  4. Возрождение Laser Squad
    от Odrick в разделе Игры
    Ответов: 17
    Последнее: 08.11.2005, 02:18
  5. Re: Laser Genius
    от Oleg Golenkoff (2:451/19) в разделе Софт
    Ответов: 3
    Последнее: 01.10.2005, 06:06

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •