SAS Emulator (эмулятор «ПК-01 Львов»)

...
  • Pathoswithin wrote: По поводу производительности, в простейшем случае, чтобы вызывать цикл 50 раз в секунду, можно добавить в него такой код:
    mov eax, 26
    mov ebx, 9
    int 40
    mov ebx, eax
    shr ebx, 1
    inc ebx
    shl ebx, 1
    sub ebx, eax
    mov eax, 5
    int 40
    Pathoswithin, как я этот код только не примудривал, - ошибку выдает Extr_KlbrInWin
    Image
    а QEMU, сразу же закрывает после запуска! :-(
    Пробовал вставлять над своими процедурами, и под ними... и с pusha/popa, и в отдельной процедуре.... и в отдельной процедуре с pusha/popa... и в пустом цикле без своих процедур вообще... и опять же с pusha/popa и без этого, а результат один и тот же, - ОШИБКА! :-(
    Надо еще паузу саму-по-себе будет попробовать будет
  • А, int 40h же. Короче, mcall лучше использовать...
  • ALEXS1983 wrote:Да и вообще, я ничего не понял с теми процедурами. Я думал это как-то проще. А как потом доступатся к той памяти ? Ну к нормальной понятно например mov [ebx+VideoDirtyM],al или mov bl,[VideoDirtyM+eax] А быстродеqствие такое же как и в обычной размеченной, типа VideoDirtyM rb 16384 ?
    Всё то же. Это частичные аналоги функций API, в одной нехорошей системе: HeapAlloc, HeapRealloc и т.д.

    Включение в главный файл:

    Code: Select all

    include "proc32.inc" ; макросы облегчают жизнь ассемблерщиков!
    include "memory.inc" ; макросы облегчают жизнь ассемблерщиков!
    Использование функций:

    Code: Select all

    ; например, нужен очищенный буфер, размером в 64кб (на выходе: eax=адрес буфера).
    stdcall  memory.initialize_allocate_reallocate_and_clear, 64000

    Code: Select all

    ; например, нужен доступ к 100-ому элементу в буфере (адрес буфера, в регистре eax).
    mov     [eax+100], dword 0x12345678

    Code: Select all

    ; например, нужно удалить выделенный буфер (eax=адрес буфера).
    stdcall  memory.free, eax
    Вот, закомментировал и переделал макросы по управлению блоком(ами) памяти (буфера, массива, кучи), который выкладывал ранее.
    Attachments
    memory.inc (3.7 KiB)
    Downloaded 290 times
  • Вот, небольшое сравнение. Может так, будет понятней.

    Выделение динамического массива (Delphi):

    Code: Select all

    1. m: array of integer;  // определение динамического массива.
    2. SetLength(m, 4096);   // перераспределение динамического массива.
    3. MemFree(m);           // удаление динамического массива.
    Выделение динамического массива (с использованием процедур из memory.inc)(FASM):

    Code: Select all

    1. stdcall memory.initialize_and_allocate, 1024  ; определение динамического массива.
       ; проверяем, хватает ли памяти в ОЗУ, для выделения динамического массива (0-неуспех, иначе размер созданной кучи).
       test  eax, eax
       jz    error.not_enough_memory
    2. stdcall memory.reallocate, 4096, eax          ; перераспределение динамического массива.
    3. stdcall memory.free, eax                      ; удаление динамического массива.
    Last edited by Yason on Fri Mar 25, 2016 5:04 pm, edited 2 times in total.
  • Yason,
    Ну вот смотри как было и как я заменил. А НЕ РАБОТАЕТ ВЕДЬ! :-(

    Code: Select all

    БЫЛО:
    VideoDirtyM rb 16385
    …......................
    …......................
    mov [ebx+VideoDirtyM],al
    …......................
    …......................
    mov [eax+VideoDirtyM],bl
    …......................
    …......................
    mov cl,[VideoDirtyM+ebx]
    …......................
    …......................
    
    ЗАМЕНИЛ ВЕЗДЕ ВОТ ТАК (СООТВЕТСВЕННО)
    
    СТАЛО:
    VideoDirtyMa rd 1
    …......................
    …......................
     INITuMem: ; заупскается при старте 
     stdcall  memory.initialize_allocate_reallocate_and_clear, 16385
     mov[VideoDirtyMa],eax
     ret
    …......................
    …......................
        add ebx,[VideoDirtyMa]
        mov [ebx],al 
    …......................
    …......................
    add eax,[VideoDirtyMa]
        mov [eax],bl
    ….....................
    ….....................
    add ebx,[VideoDirtyMa]
     mov cl,[ebx]
    ….....................
    ….....................
    Где "собака зарыта" ?
  • ALEXS1983 wrote:Yason,
    Ну вот смотри как было и как я заменил. А НЕ РАБОТАЕТ ВЕДЬ! :-(
    Мои процедуры, работают как часы - проверил. Просто ты, что-то не так делаешь. Адресуешься наверно, не верно. Твой код, вообще компилируется?
  • Yason wrote:Мои процедуры, работают как часы - проверил. Просто ты, что-то не так делаешь. Адресуешься наверно, не верно. Твой код, вообще компилируется?
    "Адресуешься наверно, не верно"
    Ну я ведь выложил что я заменял, ну и что там не так ?!....
    вот и я думаю, что всё так, а работать то не хочет.
    Ну да ладно. Я на этом не сильно парюсь, после того как я узнал и попробовал KPACK :-)

    "Твой код, вообще компилируется?" -
    Да, компилируется! А после запуска в KlbrInWin выдаёт табличку, вот такую, как тут viewtopic.php?f=43&t=3253&start=30#p64936 ну понятное дело, что с другими цифрами.
    Я предполагаю, что ты можешь сказать, что в KlbrInWin нефиг запускать и проверять что-то....
    Но дело в том, что эмуль до сих пор работал в КОС, QEMU и KlbrInWin, поэтому пусть он будет большим размером, но как и прежде запускается и работает везде! :-) Это очень удобно! :-) И в этом преимущество эмуля, перед теми программами которые где-то не запускаются.
    В КОС и QEMU я не запускал, так как не сразу сообразил проверить, а когда сообразил уже "откатал" назад код.
    Last edited by ALEXS1983 on Fri Mar 25, 2016 4:43 pm, edited 1 time in total.
  • Сегодня занимался изучением и наблюдением за поведением эмулятора и загрузки процессора компа, используя различные процедуры задержек в эмуляторе. Наблюдал в KOS, KlbrInWin и QEMU. Можно долго и нудно объяснять, но проще выражаясь, пришлось реализовать разные способы запуска движка эмуля, с различными процедурами задержек, для разных случаев. Забегая наперёд, пришёл к выводу, что эмуль, сам-по-себе без встроенной игры делать никогда не буду и что обязательно нужен генератор, в котором, нужно регулировать количество тактов КР580 в одном цикле (opcodes_to_run), номер движка с которым будет запущен емуль (TimerNumber), и задержка которая будет использоваться в том движке эмуля (TimerDelay), для нормальной работы конкретной игры в конкретном случае (в KOS, KlbrInWin или QEMU), да и на конкретной машине. Так же у меня возникли большие сомнения в возможности реализации режима 512х512, точнее говоря, нормальной его работы, хотя бы в одном из перечисленных случаев (в KOS, KlbrInWin или QEMU).

    Выложу часть кода с реализацией движков и задержек, может кто-то подскажет ещё какие-то варианты реализации движков и задержек.
    Spoiler:

    Code: Select all

    opcodes_to_run: dd 55000 ; 68500
    TimerNumber: dd 3 ; номер движка таймера 
    TimerDelay: dd 2 ; задержка
    ;---------------------------------------------------------------------
    
    EmulEngineTimer0: ; основной движок без таймера
           mov eax,[opcodes_to_run]
           mov [i8080_do_opcodes_nb_cycles],eax
           call i8080_do_opcodes
          call draw_screen
                ret
                
    EmulEngineTimer1:
          Call EmulEngineTimer0
          call Timer1
            ret            
               
    
    EmulEngineTimer2:
    Call Timer2
    mov eax,[Timer2_result]
    Cmp eax, [PlatoonTimer2T]
    jb EmulEngineTimer2_ret
    call PlatoonTimer2 ; взвод таймера
          Call EmulEngineTimer0
    EmulEngineTimer2_ret:   ret            
    
    EmulEngineTimer3:
          Call EmulEngineTimer0
          call Timer3
            ret 
    …..........................
    …..........................
    macro INT0x40 { int 0x40 }
    Timer1:
    mov eax, 23 ; - номер функции
    mov ebx, [TimerDelay];ebx = таймаут (в сотых долях секунды)
    INT0x40;
    ret
    ;---------------------------------------------------------------------
    Timer2_result rd 1
    Timer2:
    mov eax,26
    mov ebx,9
    int 0x40
    mov [Timer2_result],eax ;= число сотых долей секунды, прошедших с момента
    ret
    
     PlatoonTimer2T rd 1
     PlatoonTimer2: ; взвод таймера (для блока комманд)
    Call Timer2;
    mov eax,[Timer2_result]
    add eax,2
    mov [PlatoonTimer2T],eax
    ret
    ;---------------------------------------------------------------------
    Timer3:
    mov eax, 26 ; Функция 26, подфункция 9 - получить значение счётчика времени.
    mov ebx, 9 ;
    mcall ;int 40 Возвращаемое значение:  * eax = число сотых долей секунды, прошедших с момента запуска системы
    mov ebx, eax
    shr ebx,1
    inc ebx
    shl ebx,1 ; ebx = время в сотых долях секунды
    sub ebx,eax ;  eax = 5 - номер функции
    mov eax,5 ; Функция 5 - пауза
    mcall;int 40
    ret
    
    ====================================================
    Spoiler:==============================================================
    ============== Функция 2 - получить код нажатой клавиши. =============
    ======================================================================
    Забирает код нажатой клавиши из буфера.
    Параметры:
    * eax = 2 - номер функции
    Возвращаемое значение:
    * если буфер пуст, возвращается eax=1
    * если буфер непуст, то возвращается al=0, ah=код нажатой клавиши,
    биты 16-23 содержат сканкод нажатой клавиши в режиме ASCII,
    в режме сканкодов биты обнулены.
    биты 23-31 обнулены
    * если есть "горячая клавиша", то возвращается
    al=2, ah=сканкод нажатой клавиши (0 для управляющих клавиш),
    старшее слово регистра eax содержит состояние управляющих клавиш
    в момент нажатия горячей клавиши
    Замечания:
    * Существует общесистемный буфер нажатых клавиш размером 120 байт,
    организованный как очередь.
    * Существует ещё один общесистемный буфер на 120 "горячих клавиш".
    * При вызове этой функции приложением с неактивным окном
    считается, что буфер нажатых клавиш пуст.
    * По умолчанию эта функция возвращает ASCII-коды; переключиться на
    режим сканкодов (и назад) можно с использованием функции 66.
    Однако, горячие клавиши всегда возвращаются как сканкоды.
    * Узнать, какие комбинации клавиш соответствуют каким кодам, можно,
    запустив приложения keyascii и scancode.
    * Сканкоды возвращаются непосредственно клавиатурой и фиксированы;
    ASCII-коды получаются с использованием таблиц преобразования,
    которые можно установить подфункцией 2 функции 21 и прочитать
    подфункцией 2 функции 26.
    * Как следствие, ASCII-коды учитывают текущую раскладку клавиатуры
    (rus/en) в отличие от сканкодов.
    * Поступает информация только о тех горячих клавишах, которые были
    определены этим потоком подфункцией 4 функции 66.

    ---------------------- Константы для регистров: ----------------------
    eax - SF_GET_KEY (2)
    ======================================================================
    Может мне кто нибудь всё таки скажет, как после выполнения этой функции определять дополнительные клавиши (ext и ext2 или как там их правильно) ?
  • ALEXS1983 wrote:после запуска в KlbrInWin выдаёт табличку, вот такую, как тут viewtopic.php?f=43&t=3253&start=30#p64936 ну понятное дело, что с другими цифрами.
    Я предполагаю, что ты можешь сказать, что в KlbrInWin нефиг запускать и проверять что-то....
    Так и есть. KlbrInWin очень сильно устарел и многих новых функций API Колибри, в нём нет. Поэтому, нефиг его использовать. Хотя процедуры выделения и удаления массива работают - проверил (никаких ошибок).