Banking in IAR and ZX Spectrum 128K by DimkaM ---00--- По просьбе телезрителей, а также для себя в качестве шпаргалки, решил описать работу с банкингом в IAR'е. Сразу хочу огорчить - банкинг не распространяется на переменные и константы (с некоторыми оговорками), только на исполняемый код. Данная статья предполагает, что вы уже ознакомились с моей статьёй в Info Guide #11 и у вас есть приложение к IG #11, так как я буду ссылаться на предыдущие примеры. Сначала хочу дополнить предыдущую статью, так как многие не читают документацию и задают вопросы: Q:Почему всё обёрнуто в модули? R: Как минимум, чтобы не было мешанины из меток, и при линковке (библиотек) линкуются только используемые модули. Q:Как передавать/принимать параметры С-функций в ассемблере? R: До двух параметров передаётся в регистрах, остальные через стек. Если количество параметров неопределённо (printf(...) и т.п.), то все параметры передаются через стек. Первый параметр передаётся в E, DE, CDE или BCDE, в зависимости от разрядности значения. Если первый и второй параметры не шире 16 бит каждый, то второй параметр передаётся в регистрах B или BC, иначе через стек. Возвращаемое значение из функции передаётся в регистрах A (L при банкинге), HL, CHL или BCHL, в зависимости от разрядности значения. ---01--- Для начала нам необходимы процедуры для вызова (и возврата) процедур из переключаемых страниц. Вернее, они уже есть в библиотекеz8Oincclz80b.r01, но они расчитаны на порт с восьмибитным адресом, да ещё и с чтением из него текущей страницы. Поэтому заменим (чтобы постоянно не линковать) в этой библиотеки нужные нам процедуры. Создадим директорию проектаfix_clz80b. Создадим в этой директории файлzx128k.s01 со следующим содержимым: ;так как нет аппаратной возможности узнать текущую страницу в ;окне OxcOO0-Oxffff, ;то каждая страница (участвующая в банкинге) будет хранить свой ;номер по адресу OxFFFF. MODULE MY_CURRENT_PAGE PUBLIC MY_CURRENT_PAGE MY_CURRENT_PAGE=Oxffff ENDMOD MODULE ?BANK_CALL_DIRECT_L08 PUBLIC ?BANK_CALL_DIRECT_L08 EXTERN MY_CURRENT_PAGE RSEG RCODE ?BANK_CALL_DIRECT_L08: push hl ld hl,(MY_CURRENT_PAGE-1) push bc ld bc,0x7ffd out (c),a pop bc ex (sp),hl jp (hl) ENDMOD MODULE ?BANK_LEAVE_32_L08 PUBLIC ?BANK_LEAVE_32_L08 EXTERN MY_CURRENT_PAGE RSEG RCODE ?BANK_LEAVE_32_L08: ld sp,ix pop ix pop de inc sp inc sp pop af push bc ld bc,0x7ffd out (c),a pop bc ret ENDMOD MODULE ?BANK_LEAVE_DIRECT_L08 PUBLIC ?BANK_LEAVE_DIRECT_L08 PUBLIC ?BANK_FAST_LEAVE_L08 EXTERN MY_CURRENT_PAGE RSEG RCODE ?BANK_LEAVE_DIRECT_L08: ld sp,ix pop ix pop de pop bc ?BANK_FAST_LEAVE_L08: pop af push bc ld bc,0x7ffd out (c),a pop bc ret ENDMOD MODULE ?BANK_CALL_DIRECT_EXAF_L08 PUBLIC ?BANK_CALL_DIRECT_EXAF_L08 EXTERN MY_CURRENT_PAGE RSEG RCODE ?BANK_CALL_DIRECT_EXAF_L08: ex af,af' ld a,(MY_CURRENT_PAGE) push af ex af,af' push bc ld bc,0x7ffd out (c),a pop bc jp (hl) END Далее нам понадобится файлzx128k.bat с содержимым: ..z8Obinaz80 -uu -b -v0 zx128k.s01 copy ..z8Olibclz80b.r01 ..z8Olibclzx128k.r01 /Y ..z8Obinxlib -c "fetch-modules zx128k.r01 ..z8Olibclzx128k.r01 MY_CURRENT_PAGE MY_CURRENT_PAGE" ..z8Obinxlib -c "replace-modules zx128k.r01 ..z8Olibclzx128k.r01" Запускаем его, и у нас появляется библиотека ..z8Oincclzx128k.r01 - это копия библиотекиclz80b.r01 с заменёнными процедурами банкинга. Проектfix_clz80b нам больше не понадобится. ---02--- Сейчас мы переделаем под банкинг процедуры печати из предыдущей статьи. Создадим директориюmylibb, в ней поддиректорию list. Скопируем в текущий проект файлыmylib.c (переименовав в putchar.c ) иgraf.s01 из директории mylib (см. статью в Info Guide #11 ). graf.s01 отредактируем следующим образом: БыстрыйПиксель и таблицу положим в небанкируемую память, т.е. в сегмент RCODE. Соответственно, выход из неё - обычный ret. MODULE fast_set_pix PUBLIC fast_set_pix,fast_set_pix_table RSEG RCODE ;НЕбанкируемый сегмент fast_set_pix ;http://zxdn.narod.ru/coding/zg1etud2.txt ... тело процедуры ... ret ;обычный выход RSEG ALIGN8 fast_set_pix_table DEFS 1024 ENDMOD Инициализацию таблиц и медленныйпиксель определим в банкируемый сегмент CODE, в этом случае необходимо использовать библиотечную процедуру для выхода. MODULE fast_set_pix_init PUBLIC fast_set_pix_init EXTERN fast_set_pix_table,?BANK_FAST_LEAVE_L08 RSEG CODE ;банкируемый сегмент fast_set_pix_init ... тело процедуры ... ;ret заменим на выход из страницы: JP LWRD ?BANK_FAST_LEAVE_L08 ENDMOD MODULE little_set_pix PUBLIC little_set_pix EXTERN ?BANK_FAST_LEAVE_L08 RSEG CODE ;банкируемый сегмент little_set_pix ... тело процедуры ... ;ret заменим на выход из страницы: JP LWRD ?BANK_FAST_LEAVE_L08 END Создадимbuild.bat: ..z8Obiniccz80 -v0 -mb -uua -b -q -x -K -gA -z9 -t4 -T -Olist -Llist -Alist -I"../z80/inc/" putchar.c ..z8Obinaz80 -uu -b -v0 -Olist graf.s01 Опция -mb указывает на необходимость компиляции всех функций под банкинговые вызовы, т.к. библиотечные puts и printf ожидают банкируемую функцию putchar. Туда же добавим компоновку библиотеки: del /Q mylibb.r01 ..z8Obinxlib -c "fetch-mod listputchar.r01 mylibb.r01" "fetch-mod listgraf.r01 mylibb.r01" "LIST-ALL-SYMBOLS mylibb.r01 listmods.txt" Собираем библиотеку этим батником. В новом файле'mylibb.h' опишем переменную MY_CURRENT_PAGE, она пригодится, если понадобится узнать текущую страницу: extern unsigned char MY_CURRENT_PAGE; И остальные функции, не забывая указывать модификаторы банкинга: banked void scr_init(char atribute); #ifdef FASTPIXEL banked void fast_set_pix_init(void); non_banked void fast_set_pix(unsigned char x,unsigned char y); #define set_pix fast_set_pix #define set_pix_init fast_set_pix_init #else #define set_pix_init() banked void little_set_pix(unsigned char x,unsigned char y); #define set_pix little_set_pix #endif Модификатор banked можно не указывать, т.к. по умолчанию у нас всё банкируется, но лучше указать - потом может пригодиться. ---03--- Итак, наконец-то, подготовка к HelloWorld прошла. Проверим, что у нас получилось. Создадим проект'HelloWorld', в нем поддиректории list и bin ФайлLnk.xcl: -cZ80 //процессор //-P(CODE)CODE=[C000-FFFF]*2+10000, //[3C000-ЗFFFF]*2+10000,[6C000-бFFFF]*2+10000 //выделяем банки //0,1,3,4,6,7 под банкинг (5 и 2 не юзаем, т.к. прибиты) //но мы ещё учтём бит (в порту 7ffd) выбора ПЗУ 48К. Если нужно //ПЗУ 128К, то используйте предыдущую строчку: -P(CODE)CODE=[10C000-1OFFFE]*2+10000, [13C000-1ЗFFFE]*2+10000,[16C000-1бFFFE]*2+10000 -Z(CODE)RCODE,CDATAO,CONST,CSTR,CCSTR=6000-7FFF //сегменты //памяти с константами и кодом -Z(DATA)DATAO,IDATAO,UDATAO,ECSTR,ALIGN8|8,CSTACK+200=8000-BFFF //сегменты стека, переменных и т.п. -e_medium_write=_formatted_write //конфигурация printf/sprintf -e_medium_read=_formatted_read //конфигурация scanf/sscanf ../mylibb/mylibb //линкуем свою библиотеку -C ../z80/lib/clzx128k //подключаем библиотеку ИАРа -FMOTOROLA -o list/aout.a01 -l list/cout.html -xehinms //включим всё для полноты картины -R -w47 //подавляем предупреждения, от которых я пока не понял, //как избавиться Файл build.bat: ..z8Obiniccz80 -v0 -mb -uua -q -e -K -gA -s9 -t4 -T -Llist -Olist -Alist -I"../z80/inc/" main.c ..z8Obinaz80 -Olist -Dbanking Cstartup.s01 ..z8Obinxlink -Ilist main cstartup -f Lnk.xcl Скопируемcstartup из предыдущей статьи, добавим нумерацию страниц, добавив следующий код сразу после меткиinit_C: #ifdef banking #if NOT procбЧ180 EXTERN MY_CURRENT_PAGE ld hl,MY_CURRENT_PAGE ld bc,0x7ffd ld a,0x17 bankIniLoop: REPT 2 out (c),a ld (hl),a dec a ENDR dec a cp OxOe jr nz,bankIniLoop #endif #endif Создадимmain.c: #include <stdio.h> #include <intrz80.h> #include <math.h> //#define FASTPIXEL #include "../mylibb/mylibb.h" void KbdScan(void){ printf("Keyboard scan: 0x%02Xr",input(OxOOfe)); } C_task void main(void){ unsigned char x=0; scr_init(0x07<<3); set_pix_init(); puts("Hello World!"); do{ set_pix(x,(sin((double)x/20)*20+95)); }while(++x); while(1) KbdScan(); } Компилируем батником. ---04--- Опять засада: что делать с файломlistaout.a01 !? Этот файл в форматеmotorola s-records. Для преобразования файлаaout.a01 воспользуемся сторонней утилитой SRecord - её вы сможете найти в приложении к этой газете. Добавим вbuild.bat (внимание! рассчитано на стартовый адрес 0x6000 !): @echo off ..z8Osrecordsrec_cat listaout.a01 -offset -0x6000 -crop 0 0x6000 -o bincode.c -Binary FOR %%i IN (00,01,03,04,06,07,10,11,13,14,16,17) do ..z8Osrecordsrec_cat listaout.a01 -offset -0x%%~iCOO0 -crop 0 0x4000 -o bin%%~i.c -Binary for /R bin %%i in (*.c) do (if %%~zi==0 (del /Q %%i) ELSE (echo crop file:%%~nxi size:%%~zi out^(7FFD^),0x%%~ni)) echo on В директорииbin создастся бинарный файл code, который необходимо загрузить и запустить с адреса 0x6000, а также файлы с номером (с учётом бита выбора ПЗУ) страницы в имени - их, естественно, надо загрузить по страницам. Возможно, у вас есть свои методы создания загрузчика и образа диска. Если нет, то можете добавить следующую строчку в build.bat: ..z8Obinbuild128k.bat buildscl И получите на выходе scl-файл со всеми файлами и загрузчиком. Запускаем и наслаждаемся чистым синусом. * * * В следующей серии мы рассмотрим грязные хаки. К примеру, как положить локальные переменные и константы в банкируемый сегмент.