01 апреля 2021

Разработка с помощью NedoOS 
Alone Coder 

  Под  NedoOS  можно разрабатывать на ас─ 
семблере, Си,Паскале и Недоланге,но в этой 
статье я расскажу именно про ассемблер. На 
нём  примеров  больше всего, и с ним будет 
более понятно, как работает система. 
 Кроме примеров, NedoOS предоставляет ра─ 
зработчику  большой набор функций, которые 
обычно  лень  писать с нуля, а также среду 
для автоматической сборки. 

           Первое знакомство

   Первое знакомство с системой для разра─
ботчика выглядит так (если у вас Windows):

 1. скачать всё из http://nedoos.ru/svn/ 
в директорию,не содержащую пробелов в пути 
 2. войти  в  эту директорию, а точнее, в 
поддиректорию src/ 
 3. запустить mkevo.bat.

   При этом пересобирается вся система (на
момент  написания  этой  статьи - 70 паке─
тов), создаётся образ SD-карты sd_nedo.vhd
(если его не было), а на него записывается
всё, что  есть  в  release/. Образ лежит в 
us/ - директории эмулятора. 
   Как это работает внутри:

 1. mkevo.bat  записывает  файлы настроек 
для  конфигурации  ZX Evo в файл src/_sdk/ 
syssets.asm 
 2. mkevo.bat  запускает make.bat с пара─ 
метром  noneedtrd  (то есть не делаем trd- 
образ дискеты, она нам не нужна) 
 3. make.bat  при  необходимости  создаёт 
директорию release/ 
 4. make.bat собирает пакет fatfs4os (фа─ 
йловая система FAT) 
 5. make.bat  собирает пакет kernel (ядро 
ОС), создаётся nedoos.$c 
 6. make.bat  в цикле собирает все пакеты 
путём вызова build.bat у каждого из них, а 
после  сборки  пакета  копирует в release/ 
полученные  *.com, *.ext, *.txt, *.new   и 
поддиректорию, одноимённую  с  директорией 
пакета 
 7. make.bat  при  отсутствии   параметра 
noneedtrd (а он у нас присутствует) созда─ 
ёт и заполняет test.trd 
 8. mkevo.bat переименовывает nedoos.$c в 
sd_boot.$c (чтобы  можно было запускать из 
Evo Reset Service одной кнопкой 5) 
 9. mkevo.bat  запускает chkimg.bat с па─ 
раметром  sd (если бы был hdd, то работали 
бы с образом жёсткого диска hdd_nedo.vhd ) 
 10. chkimg.bat при необходимости создаёт 
образ (с помощью images.exe ) 
 11. chkimg.bat  записывает на образ всё, 
что есть в release/ (с помощью dmimg.exe ) 
 12. mkevo.bat запускает настроенный эму─ 
лятор. 

   Аналогично  работают сборщики для оста─
льных конфигураций:

mkevsd-g.bat - ZX Evo без SD-карты в NeoGS 
mkatm3.bat - ATM3 на дискете 
mkatm3hd.bat - ATM3 на HDD (Nemo IDE,можно 
исправить в батнике) 
mkatm3sd.bat - ATM3 на SD-карте 
mkpe26.bat - Pentagon 2.666LE на дискете 
mkpe26sd.bat - Pentagon 2.666LE на SD card 
mkatm2.bat - ATM2 на дискете 
mkatm2hd.bat - ATM2 на HDD (ATM IDE) 

   Они все однотипные,так что каждый может
создать свой сборщик или исправить сущест─
вующий.
  makeall.bat  вызывает  все mk*.bat. Его
надо  запускать перед коммитом в репозито─
рий.

             Батник сборки

   Из всего этого (особенно  из пункта про 
build.bat ) видно, что программу пользова─ 
теля  лучше  писать  по  образцу имеющихся
программ.  Проще  всего  взять  директорию 
emptyapp (или, если ваша программа  похожа 
на  какую-то  существующую - то директорию
той), скопировать её целиком и исправлять.
   Как выглядит простейший build.bat:

 1. Настройка путей: 
if "%settedpath%"=="" call ../_sdk/ 
                              setpath.bat
 2. Компиляция (может быть много строк, с 
вызовом нужных утилит конверсии ресурсов и 
т.п.): 
sjasmplus --nologo --msg=war emptyapp.asm 
  Вот  это  место вы можете исправить для 
вашей программы, но если её главный модуль 
называется emptyapp.asm - не исправляйте:) 
  А остальное исправлять, скорее всего, и 
так не придётся. 

 3. Если build.bat вызван отдельно (у ме─ 
ня это кнопка F9 в Notepad++ ), то копиро─ 
вать в release/ и на образ SD-карты: 
if "%currentdir%"=="" ( 
FOR %%j IN (*.com) DO (
copy /Y %%j "../../release/bin/" > nul
"../../tools/dmimg.exe" ../../us/
             sd_nedo.vhd put %%j /bin/%%j
)
if "%makeall%"=="" ....usemul.exe 
) 
  Вариант  для игр с кучей файлов в одно─ 
имённой поддиректории: 
SET releasedir2=../../../release/ 
if "%currentdir%"=="" ( 
 FOR %%j IN (*.com) DO (
 "../../../tools/dmimg.exe" ../../../us/
        sd_nedo.vhd put %%j /nedogame/%%j
 move "*.com" "%releasedir2%nedogame">nul
 IF EXIST %%~nj xcopy /Y "%%~nj"
       "%releasedir2%nedogame%%~nj">nul
 )
cd ../../../src/
call ..toolschkimg.bat sd
if "%makeall%"=="" ..usemul.exe 
) 
   Может быть, кто-то сможет сделать везде
одинаково? Но пока живём так.
   Есть  ещё  вариант батника runcurl.bat,
который после сборки пытается передать со─
бранный  *.com  по HTTP  на уже запущенный
эмулятор, а потом  его удалённо запустить.
Для этого в эмуляторе должна быть запущена
NedoOS, а в ней веб-сервер 3ws. 

        Главный модуль программы

   Предположим,что вы пишете игру.Её исхо─
дники лежат в src/games/somegame/. Её гла─
вный  модуль  называется main.asm. Её com-
файл называется somegame.com, а дополните─
льные   файлы  генерируются  в  src/games/ 
somegame/somegame/  (при  сборке попадут в 
release/nedogame/somegame/somegame/ ). Как 
выглядит main.asm:

       DEVICE ZXSPECTRUM128
       include "../../_sdk/sys_h.asm"
   ;там все системные константы и макросы

STACK=0x4000 ;так удобнее всего для игр 

       org PROGSTART ;все программы начи─
                   ;наются с этого адреса 
begin 
       ld sp,STACK ;иначе получится sp=0 
;Наша программа работает не через stdio, а 
;сама ковыряется в экране, поэтому: 
       OS_HIDEFROMPARENT ;разбудить роди─
       ;теля программы (обычно навигатор)
       ld e,0+128 ;e=0: EGA, e=2:MC,
                   ;e=3: 6912, e=6: text
             ;+8=noturbo ;+128=keep screen
                   ;e=-1: disable gfx
       OS_SETGFX ;включаем граф.режим и
      ;получаем фокус (out:e=old gfxmode)

       ld de,path
       OS_CHDIR ;входим в поддиректорию 
;...грузим файлы... 

mainloop 
;...играем... 
       jr mainloop

       ld hl,0 ;результат
       QUIT ;выход 
path 
       db "somegame",0

;...всякие процедурки, таблицы, инклюды... 

end 
savebin "somegame.com",begin,end-begin
LABELSLIST "../../../us/user.l" ;сохраним 
 ;метки,смотреть их дебагере Unreal Speccy 

   Если  код вашей программы компилируется
в несколько страниц, то используйте дирек─
тиву  PAGE и несколько savebin (можно под─
смотреть в проекте br, как это делается).

                Отладка

   Итак, если  вы написали вышеприведённый
пример main.asm  и запустили build.bat (не
забудьте указать там main.asm ),то как раз
попадёте в Unreal Speccy.
   По  кнопке  "5"  вы окажетесь в NedoOS,
оттуда  запустите  свою  игру из nedogame/
(можно   это  автоматизировать  с  помощью 
autoexec.bat ).Нажмите Shift+F1, чтобы по─ 
пасть  в  отладчик. Нажмите  Ctrl+L, чтобы
увидеть метки (а чтобы развидеть - ещё раз
Ctrl+L ). 
   Убедитесь, что  текущая задача - именно
ваша игра: Tab (в окно дампа), Ctrl+G,0080
- вы должны увидеть somegame.com в дампе.
   Если  это  не  так, то вернитесь в окно
дизассемблера  (Shift+Tab),  Ctrl+G,  0057
(конец  обработчика  прерываний), а дальше
жмите  F4 (выполнение до курсора), пока не
увидите somegame.com в дампе. Справа внизу
видны  текущие включенные страницы во всех
четырёх четвертинках.
   Шагать по программе - F7. Шагать с про─
пуском  вызовов - F8. Шагать  до выхода по
стеку - F11.  Можно  переставлять  текущий
адрес исполнения - Z.
   В ходе исполнения программы можно вруч─
ную  исправлять  регистры (переход к ним -
Shift+Tab, мышка тоже работает).Можно даже 
исправлять программу.
   Если другие задачи (особенно term.com )
мешают трассировать вашу программу,их мож─
но закрыть: попав в эту задачу,переставьте
адрес исполнения на 0.
   Посмотреть экран - F9.
   Сохранить  кусок памяти - Alt+W (сохра─
няется в src/ ). Загрузить - Alt+R.
   Выход  из отладчика - Shift+F1. В теку─
щей  версии эмулятора это почему-то приво─
дит к залипанию Shift, поэтому надо ткнуть
ещё раз Shift. При выходе из отладчика та─
кже  отключается  Kempston mouse, его надо
захватить кликом по окну эмулятора.

Загрузка и сохранение файлов из программы

   Если  вам нужно прочитать только какую-
нибудь мелочь, типа отгрузки, можно просто
набросать:
       ld de,filename ;"name.ext",0
       OS_OPENHANDLE ;открыли файл
       or a
       jr nz,ошибка ;не смогли открыть
       push bc ;b=хэндл открытого файла
       ld de,SAVEDATA ;адрес,куда грузить
       ld hl,SAVEDATAsz ;сколько байт
       OS_READHANDLE ;загрузили
       pop bc ;b=хэндл открытого файла
       OS_CLOSEHANDLE ;закрыли

   Сохранение - аналогично, только  вместо 
OPENHANDLE  пишем  CREATEHANDLE, а  вместо 
READHANDLE - WRITEHANDLE.  Образец   можно 
посмотреть в untangle.
   Но обычно данные надо грузить в страни─
цы, а эти страницы надо выделить!
   Изначально ОС выделяет каждой программе
4 страницы,номера которых можно узнать че─
рез  OS_GETMAINPAGES  (получаем dehl=pages 
in  0000, 4000, 8000, c000 ). Лучше  всего 
узнать  их в самом начале и запомнить, на─
пример, так:
       OS_GETMAINPAGES ;dehl=pages in
                      ;0000,4000,8000,c000
       ld a,e
       ld (pgmain4000),a
       ld a,h
       ld (pgmain8000),a
       ld a,l
       ld (pgmainc000),a 
;а дальше уже будет загрузка 

   А  в конце  программы  написать удобные
процедуры включения этих страниц:

setpgmain4000 
pgmain4000=$+1 
       ld a,0
       SETPG4000
       ret 
setpgmain8000 
pgmain8000=$+1 
       ld a,0
       SETPG8000
       ret 
setpgmainc000 
pgmainc000=$+1 
       ld a,0
       SETPGC000
       ret

   Если  ваша  программа  всегда  включает
экран  в  4000 и 8000, то имеет смысл объ─
единить  setpgmain4000  и  setpgmain8000 в
одну процедуру setpgsmain40008000. А теку─
щий   экран   включать   через   процедуру 
setpgsscr40008000. Можно  не  писать это с 
нуля, а заимствовать  целиком модуль  src/ 
games/sprexamp/mem.asm. 
   Итак, нам надо загрузить сколько-то фа─
йлов в страницы.Возьмём универсальную про─
цедуру  загрузки одной страницы из того же 
sprexamp: 

loadpage 
;заказывает страничку и грузит туда файл 
;(имя файла в hl) 
;out: hl=после имени файла, a=pg 
       push hl
       OS_NEWPAGE
       pop hl
       ld a,e
       push af ;pg
       SETPGC000
       push hl
       ex de,hl
       OS_OPENHANDLE
       push bc
       ld de,0xc000 ;addr
       ld hl,0x4000 ;size
       OS_READHANDLE
       pop bc
       OS_CLOSEHANDLE
       pop hl
       ld b,1
       xor a
       cpir ;after 0
       pop af ;pg
       ret

   И будем её вызывать так:

       ld hl,texfilename
       call loadpage
       ld (pg0),a
       call loadpage
       ld (pg1),a
       call loadpage
       ld (pgsfx),a
       call loadpage
       ld (pgmusic),a
и т.п.
   В br это вообще делается в цикле, а но─
мера страниц кладутся в таблицу.

texfilename 
       db "pg0.bin",0
       db "pg1.bin",0
       db "sfx.bin",0
       db "music.bin",0

   Легко переделать loadpage на загрузку с
произвольного  адреса, который  тоже можно
взять из texfilename (допустим, перед име─
нем файла).

          Рисование на экране

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

       ld a,(timer)
       ld (oldtimer),a 
;главный цикл 
mainloop 
;вывод фона/восстановить его под спрайтами 
;... рекомендую взять из sprexamp! 

;вывод спрайтов 
       call setpgsscr40008000 ;включили
                          ;страницы экрана
       ld iy,spaceship0
       ld e,50 ;e=x = -(sprmaxwid-1)..159
         ;(кодируется как x+(sprmaxwid-1))
       ld c,50 ;c=y = -(sprmaxhgt-1)..199
                   ;(кодируется как есть)
       call prspr

       call setpgsmain40008000 ;включили
;страницы программы в 4000,8000, как было

;закончили рисовать 
     ld a,(timer)
     push af
       call changescrpg ;с этого момента
  ;(точнее,с INT'а) увидим,что нарисовали

;логика 
;вызывается столько раз,сколько прошло 
;прерываний! 
mainloop_uvwaittimer0 
       ld a,(timer) 
oldtimer=$+1 
       ld b,0
       ld (oldtimer),a
       sub b
       ld b,a
       jr z,mainloop_uvwaittimer0 ;если
     ;прерываний ещё не было,крутимся тут 
;b=сколько прошло прерываний 
mainloop_uvlogic0 
       push bc
       call logic ;свою логику пишем сюда
       pop bc
       djnz mainloop_uvlogic0

;ждём физического переключения экрана! 
;можем начать новую отрисовку, только если 
;с момента changescrpg прошло хотя бы одно 
;прерывание (возможно, внутри logic) 
     pop bc ;b=timer во время changescrpg 
waitchangescr0 
       ld a,(timer)
       cp b
       jr z,waitchangescr0

       ld a,(curkey)
       cp key_esc
       jp nz,mainloop ;выход по esc/break

   Чтобы  работал timer, подключите модуль 
sprexamp/int.asm  и  используйте  swapimer 
(включение / выключение  пользовательского
обработчика  прерываний) перед и после ва─
шего  главного  цикла. Обратите  внимание,
музыка не играется в этом обработчике, для
этого  есть  системный  вызов OS_SETMUSIC,
который  гарантирует  ровное проигрывание,
даже  если  ваша  задача вытеснена другими
задачами!
   Процедуру  prspr  возьмите из sprexamp/ 
prspr.asm. Если  у вас спрайты скомпилиро─ 
ваны  не  в страницы, а в отдельные файлы,
то можно в них определить prsprqwid=0x100,
а  перед  первым  вызовом prspr записать в 
0x100  команду  jp prsprqwid. Вообще можно 
начинать написание игры с исправления про─
екта  sprexamp. Там  есть даже два готовых
движка - со  скроллом  фона  по  двум осям
(без скролла фон не перепечатывается) и со
скроллом  по  вертикали (фон перепечатыва─
ется  всегда). Причём к первому движку уже
прикручено  управление  и  логика  в стиле
платформера.

  Стоит прочитать nedoos.txt,api_base.txt 
и sys_h.asm, чтобы  получить представление 
о других функциях системы. 



Other articles:


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Similar articles:
Scribble brain - Beth tea.
Debut - Game "The Sentinel" ("GUARD"). Fans of three-dimensional games will appreciate the idea of this game.

В этот день...   21 November