Programming - working with memory banks in IAR.

Banking in IAR and ZX Spectrum 128K
 by DimkaM

                            ---00---

At the request of TV viewers, as well as for myself as a cheat sheet,
I decided to describe working with banking in IAR.
I want to disappoint you right away - banking does not apply to variables
and constants (with some reservations), only for executable
code.
This article assumes that you have already read my
article in Info Guide #11 and you have an application to IG #11, so
as I will refer to the previous examples.

First I want to complement the previous article, since many do not
read the documentation and ask questions:

Q:Why is everything wrapped in modules? 
R: At a minimum, so that there is no confusion of labels, and when linking 
(libraries) only used modules are linked. 

Q:How to pass/receive parameters of C functions in assembler? 
R: Up to two parameters are passed in registers, the rest via 
stack 
If the number of parameters is indefinite (printf(...) etc.), 
then all parameters are passed through the stack. 
The first parameter is passed to E, DE, CDE or BCDE, depending 
depending on the bit depth of the value. 
If the first and second parameters are not wider than 16 bits each, then the second 
the parameter is passed in registers B or BC,otherwise via the stack. 
The return value from the function is passed in registers A (L when 
banking), HL, CHL or BCHL, depending on the bit depth 
meanings. 

                            ---01---

First we need procedures to call (and return)
procedures from switched pages.
Or rather, they are already in the libraryz8Oincclz80b.r01, but they
designed for a port with an eight-bit address, and even with reading from
from the current page.
Therefore, we will replace (so as not to constantly link) in this library
the procedures we need.
Let's create a project directoryfix_clz80b.
Create a file in this directoryzx128k.s01 with the following
content:

;since there is no hardware way to find out the current page in 
;window OxcOO0-Oxffff, 
;then each page (participating in banking) will store its 
;number at 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 с 
заменёнными процедурами банкинга.
ПроектWe won't need fix_clz80b anymore.

                            ---02---

Now we will rework the printing procedures from the previous one for banking
articles.
Let's create a directorymylibb, with a subdirectory list in it.
Copy the filesmylib.c to the current project (renaming them to
putchar.c ) andgraf.s01 from the mylib directory (see article in Info 
Guide #11 ). 

graf.s01 will be edited as follows:
We will put the FastPixel and the table in non-banked memory, i.e. in
RCODE segment. Accordingly, the way out of it is the usual ret.
 MODULE fast_set_pix
 PUBLIC fast_set_pix,fast_set_pix_table
 RSEG RCODE ;NON-BANKED segment
fast_set_pix ;http://zxdn.narod.ru/coding/zg1etud2.txt
... 
procedure body 
... 
 ret ;normal output
 RSEG ALIGN8
fast_set_pix_table
 DEFS 1024
 ENDMOD

We will define the initialization of tables and the slow pixel in a bankable
CODE segment, in this case it is necessary to use the library
exit procedure.
 MODULE fast_set_pix_init
 PUBLIC fast_set_pix_init
 EXTERN fast_set_pix_table,?BANK_FAST_LEAVE_L08
 RSEG CODE ;banked segment
fast_set_pix_init
... 
procedure body 
... 
 ;ret will be replaced with exit from the page:
 JP LWRD ?BANK_FAST_LEAVE_L08
 ENDMOD

 MODULE little_set_pix
 PUBLIC little_set_pix
 EXTERN ?BANK_FAST_LEAVE_L08
 RSEG CODE ;banked segment
little_set_pix
... 
procedure body 
... 
 ;ret will be replaced with exit from the page:
 JP LWRD ?BANK_FAST_LEAVE_L08
 END

Let's createbuild.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

The -mb option indicates the need to compile all functions under
banking calls, because library puts and printf expect
putchar bankable function.

Let's add the library layout there:
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"

We collect the library with this batch file.

In the new file'mylibb.h' we will describe the variable MY_CURRENT_PAGE, ituseful if you need to find out the current page:
extern unsigned char MY_CURRENT_PAGE;

And other functions, not forgetting to specify banking modifiers:
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

The banked modifier need not be specified, because by default we have
everything is banked, but it’s better to indicate - it may come in handy later.

                            ---03---

So, finally, the preparations for HelloWorld are over.Let's check that
we did it.
Let's create a project'HelloWorld', in it there will be subdirectories list and bin

FileLnk.xcl:
-cZ80 //processor
//-P(CODE)CODE=[C000-FFFF]*2+10000, 
  //[3C000-ЗFFFF]*2+10000,[6C000-бFFFF]*2+10000 //select banks
  //0,1,3,4,6,7 for banking (we don’t use 5 and 2, because they are nailed down)
//but we will also take into account the bit (in port 7ffd) for selecting 48K ROM. If necessary 
//ROM 128K, then use the previous line: 
-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 //segments
 //memories with constants and code
-Z(DATA)DATAO,IDATAO,UDATAO,ECSTR,ALIGN8|8,CSTACK+200=8000-BFFF
 //segments of the stack, variables, etc.
-e_medium_write=_formatted_write //printf/sprintf configuration
-e_medium_read=_formatted_read //scanf/sscanf configuration
../mylibb/mylibb //link your library
-C ../z80/lib/clzx128k //connect the IAR library
-FMOTOROLA
-o list/aout.a01
-l list/cout.html
-xehinms //include everything for completeness
-R
-w47 //suppress warnings that I haven't understood yet
 //how to get rid of

Build.bat file:
..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

Let's copycstartup from the previous article and add numbering
pages by adding the following code immediately after theinit_C label:
#ifdef banking
#if NOTprocбЧ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 
#include 
#include 
//#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 !?
Этотfile inmotorola s-records format.
To convert the fileaout.a01 we will use a third-party
SRecord utility - you can find it in the appendix to this
newspaper.

Add tobuild.bat (attention! designed for the starting address
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

A binary code file will be created in thebin directory, which
you need to download and run from address 0x6000, as well as the files
with the number (taking into account the ROM selection bit) of the page in the name - theirs,
Naturally, you need to download it page by page.

You may have your own methods for creating the bootloader and image
disk. If not, you can add the following line to
build.bat:
..z8Obinbuild128k.bat buildscl

And you will receive an scl file with all the files and the loader as output.
Let's launch and enjoy the pure sine.

                             * * *

In the next episode we'll look at dirty hacks. For example, how 
put local variables and constants in the banked segment. 

Share your thoughts about the article