Bank switching issue

The place for codemasters or beginners to talk about programming any language for the Spectrum.
User avatar
Prototron
Drutt
Posts: 11
Joined: Thu Mar 07, 2024 2:10 pm
Location: Glasgow, Scotland

Bank switching issue

Post by Prototron »

I'm struggling a bit with bank switching on the 128K, so I'd be grateful for some clarification on a few things.

I'm using this subroutine to switch banks:

Code: Select all


BANKPORT 	= $7FFD
BANKPORTPREV 	= $5B5C

SWITCHBANK:		
	;---------------------------
	ld      a,(BANKPORTPREV)      		; Previous value of port
	and     $F8
	or      e             			; Select bank (number in e)
	ld      bc,BANKPORT
	ld      (BANKPORTPREV),a
	out     (c),a
	;---------------------------
	RET
At the moment I'm simply trying to switch out a bank and then switch back before the main loop starts by using the code below, but it doesn't seem to return to the original bank.

Code: Select all

	ld	de,$03		; Switch to Bank 3
	call	SWITCHBANK
	
	ld	de,$00		; Switch back to original Bank 0
	call	SWITCHBANK
I'm wondering, how are the banks organised when switched?

For example, if I switch in whatever is in bank 3, does that then become bank 0? Or does the computer retain the ID number of whichever bank is switched?

Code: Select all


0,1,2,3,4,5,6,7 <- Original

3,1,2,0,4,5,6,7 <- Switched

3     0
0,1,2,3,4,5,6,7 <- Switched, but just with data
Or is there some delay that I need to take into account? If so, that would bring me on to my next question:

If I have graphics data in bank 3 and a drawing screen in say bank 7, I'd need to instantly swap to the graphics bank, grab the data, then switch to the screen to place it there. If there is a delay in the swap, how is this countered, as I'd essentially be reading from and writing to the $C000 block, so it'd need to be fast - especially as my drawing routine reads and writes bytes within a line of each other.

I've looked at the documentation, but it doesn't seem clear on some of these points.

Many thanks!
"For the money, for the glory, and for the fun! Mostly for the money."
~ Bo "Bandit" Darville
User avatar
ketmar
Manic Miner
Posts: 716
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Bank switching issue

Post by ketmar »

you doesn't switch banks, you are changing address mapping. think of it this way: address $C000 can be mapped to one of several memory banks, and you are selecting to which one. there is no changes in bank order. i.e. $C000 is a "window" you are using to look at a huge RAM, and by OUTing, you simply "shift" that window to another region.

we are usually talking about "bank switching", tho, but that's just something we are used too. ;-)

as for switching itself — it is instant. but you'll have the usual instruction delay (12ts for OUT), and ULA may eat some tstates too. constantly switching banks is not the best way to transfer data: it will be slow simply due to the amount of OUTs you need to do. like: 7ts+12ts for "switch in", 7ts to load A, 4ts to save A, 7ts+12ts for "switch out", 4ts+7ts to store A, 6ts+6ts to advance pointers. 70+ tstates per byte even for the more-or-less ideal case.
User avatar
deanysoft
Dizzy
Posts: 75
Joined: Sat Jun 18, 2022 10:35 pm

Re: Bank switching issue

Post by deanysoft »

Hi Protoron,

The bank switching is an instantaneous hardware switch so there's no delay. When a bank is switched in, it appears at the area specified so you can read and write to it and the other banks are inaccessible.

There are some special banks which when you switch them in, appear elsewhere in the RAM. Bank 2 for example, will appear simultaneously at $c000 and $8000!

I can't say I've found a use for this so really only use banks 0,1,3,4,6 and 7 which are always appear at $c000 to $ffff


I'm sure you will have seen this, but if not, it might help...

https://worldofspectrum.org/faq/referen ... erence.htm


Here's the simple code I use to switch banks for the original 128K machine

Code: Select all

		ld	a,1			; bank 1 for example
		call	switchRamPage
		...
		...
			
			

switchRamPage:
		ld	(currentPage),a			; keep a note of it (in case I need to know later)
		di
		ld	bc,$7ffd
		out	(c),a				; the 16K that is bank 1 now appears starting at address $c000
		ei
		ret

currentPage:	db	0

Now here, I'm only bothered about changing bits 0-2 so all other bits are 0. So I'm always using the normal screen (bit 3=0) and ROM 0
can stay on too as I'm not using it anyway (bit 4=0). I don't set bit 5 which disables further paging!

The code snippet in the WoS link above keeps the complete state of the port as it's using normal interrupt code. I use interrupt mode 2
so everything is under my control so I don't need to tell the system what I've done (i.e. no saving the port state to $5b5c)

Maybe it's the rest of the 128K ROM system that is giving you the odd results?

If you can use IM2 and keep your main code and stack below $c000 then maybe it'll be easier.
AndyC
Dynamite Dan
Posts: 1410
Joined: Mon Nov 13, 2017 5:12 am

Re: Bank switching issue

Post by AndyC »

I bet you still have interrupts enabled?

If you do, you need to update the relevant system variables before you switch banks. If you don't do that, there is every chance the system will sweep in when you're not looking, do some stuff and then "restore" the correct banking as per the variables. By changing those variables first, the correct banks will remain even if the system kicks in.
DoctorRad
Drutt
Posts: 27
Joined: Mon Mar 18, 2024 4:40 pm

Re: Bank switching issue

Post by DoctorRad »

Prototron wrote: Tue Apr 02, 2024 11:10 amIf I have graphics data in bank 3 and a drawing screen in say bank 7, I'd need to instantly swap to the graphics bank, grab the data, then switch to the screen to place it there.
This is another good page covering the memory map.

Image

Banks 2 and 5 always appear as 'conventional' RAM, between &8000-&BFFF and &4000-&7FFF respectively. You can page them in to also appear at &C000-&FFFF as for any other pages, but it's not really clear why you'd want to.

The alternate screen is in bank 7. If you want to draw directly to the alternate screen, you need to page in bank 7, and then use addresses &C000-&D7FF as if they were screen memory (usually &4000-&57FF) and &D800-&DAFF as attribute memory (usually &5800-&5AFF). You can write to the second screen while it's displayed (by setting bit 4 of port &7FFD to 1) but if you're using the alternate screen, it's usual to write to whichever screen is not currently displayed and then swap them over for an instant change of graphics.

As far as I know / remember, if you're not using the alternate screen, you can use bank 7 as 'normal' paged memory, and even if you are using the alternate screen, &DB00 and above are available.

DO NOT change pages if your stack or executing code is in &C000-&FFFF unless you know exactly what you're doing or want funny things to happen. I assume that if the exact same executing code is at the same locations in multiple pages, you can swap between those pages, but I've never tried it. Probably best to have your stack going down from &C000 or a little lower so it's in (relatively) 'conventional' memory.

Generally, copying between banks is going to be slow, as you'll need to copy into 'conventional' memory (somewhere below &C000), swap pages, and then copy back to another page.

Contended memory pages are different for models +2A, +3, +2B, and +3B, where pages 4, 5, 6, and 7 are contended, rather than 1, 3, 5 and 7 on the toastrack 128 and +2. Only banks 0 & 2 are never contended on any 128 model, and bank 2 always appears as 'conventional' RAM between &8000-&BFFF.
User avatar
1024MAK
Bugaboo
Posts: 3123
Joined: Wed Nov 15, 2017 2:52 pm
Location: Sunny Somerset in the U.K. in Europe

Re: Bank switching issue

Post by 1024MAK »

It's called bank switching or paging, but at the hardware level, the value on the data bus from the OUT is stored in a latch (register). Then some of the bits are used as extra address lines to the memory (ROM and RAM).

The effect of this is that as described above, the Z80 can only see a "window" of ⅛ of the 128K bytes of RAM in the 0xC000 to 0xFFFF area of the Z80 memory map.

As said above, some parts of the 128K are available elsewhere in the memory map.

Mark
:!: Standby alert :!:
“There are four lights!”
Step up to red alert. Sir, are you absolutely sure? It does mean changing the bulb :dance
Looking forward to summer later in the year.
User avatar
Prototron
Drutt
Posts: 11
Joined: Thu Mar 07, 2024 2:10 pm
Location: Glasgow, Scotland

Re: Bank switching issue

Post by Prototron »

Thanks for the info everyone. That makes things much clearer.

I was aware that the Z80 can only see 64K of RAM at any one time, but wasn't sure just how it all worked.

All interrupts are disabled, but I found that the OUT was affecting the stack's RET address in the subroutine, but it's fine if I back it up or just use the whole code where it's needed.

I'd like to start using the screen pages for proper double buffering instead of just a screen copy, because even with a stack copy it's more cycles that I don't need with a 128K.

At the moment I have my "Drawing" screen at $B000 and my "Visible" screen at $4800, with graphics data at $C000 (and code at $8000). I'm only using/copying 4096 bytes of screen space as it's just the bottom two-thirds of the screen where the action occurs. The top third is for text and health-bars, so I'm just writing that directly to the first 2048 bytes of $4000 as it only happens occasionally.

I assume I'll need to alter this to account for the paging of the whole block?
"For the money, for the glory, and for the fun! Mostly for the money."
~ Bo "Bandit" Darville
User avatar
ketmar
Manic Miner
Posts: 716
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Bank switching issue

Post by ketmar »

Prototron wrote: Wed Apr 03, 2024 9:41 am All interrupts are disabled, but I found that the OUT was affecting the stack's RET address in the subroutine
you prolly have the stack somewhere around $FF00, or something. so when you switched the banks, the window [$C000..$FFFF] moved to another memory region… and your stack stays in the old one. ;-)

just remember that memory area [$C000..$FFFF] is affected by switch, and design your code around that.
User avatar
Prototron
Drutt
Posts: 11
Joined: Thu Mar 07, 2024 2:10 pm
Location: Glasgow, Scotland

Re: Bank switching issue

Post by Prototron »

ketmar wrote: Wed Apr 03, 2024 2:28 pm you prolly have the stack somewhere around $FF00, or something. so when you switched the banks, the window [$C000..$FFFF] moved to another memory region… and your stack stays in the old one. ;-)

just remember that memory area [$C000..$FFFF] is affected by switch, and design your code around that.
Ahh that makes sense! Thanks for the info.
"For the money, for the glory, and for the fun! Mostly for the money."
~ Bo "Bandit" Darville
AndyC
Dynamite Dan
Posts: 1410
Joined: Mon Nov 13, 2017 5:12 am

Re: Bank switching issue

Post by AndyC »

ketmar wrote: Wed Apr 03, 2024 2:28 pm you prolly have the stack somewhere around $FF00, or something. so when you switched the banks, the window [$C000..$FFFF] moved to another memory region… and your stack stays in the old one. ;-)

just remember that memory area [$C000..$FFFF] is affected by switch, and design your code around that.
I think it's probably also important to add that when you say "your stack stays in the old one" what you really mean is "all the data that was on the stack stays in the old bank but the Z80 thinks whatever random data is in the same addresses in the new bank is what was on the stack".

When starting out with banking the thing that almost always catches people out is you have to think about all the memory accesses that happen, even normally transparent ones like PUSHing and POPing return addresses etc. It's very easy to pull thr rug out from under yourself and the Z80 will just blindly assume you know what you're doing.
DoctorRad
Drutt
Posts: 27
Joined: Mon Mar 18, 2024 4:40 pm

Re: Bank switching issue

Post by DoctorRad »

Prototron wrote: Wed Apr 03, 2024 9:41 am At the moment I have my "Drawing" screen at $B000 and my "Visible" screen at $4800, with graphics data at $C000 (and code at $8000). I'm only using/copying 4096 bytes of screen space as it's just the bottom two-thirds of the screen where the action occurs. The top third is for text and health-bars, so I'm just writing that directly to the first 2048 bytes of $4000 as it only happens occasionally.
Anything you write to the top third of the screen will have to be mirrored at $C000 in bank 7, otherwise it won't appear when you swap in the alternate screen. Same for any relevant attribute changes. Hopefully most of the top third is static so you won't have to do too much double writing.
User avatar
Prototron
Drutt
Posts: 11
Joined: Thu Mar 07, 2024 2:10 pm
Location: Glasgow, Scotland

Re: Bank switching issue

Post by Prototron »

I changed the stack to another place and the calls are working fine now. :) Screen is now at $C000 and graphics data is at $B000
DoctorRad wrote: Thu Apr 04, 2024 9:40 am Anything you write to the top third of the screen will have to be mirrored at $C000 in bank 7, otherwise it won't appear when you swap in the alternate screen. Same for any relevant attribute changes. Hopefully most of the top third is static so you won't have to do too much double writing.
Yes, the top third is just text for lives and stuff, so nothing that gets updated every frame.

I don't even really have a game as such at the moment. Just a sandbox with a flip scrolling background and a sprite with 4 frames that constitutes a walk cycle and idle pose. Trying to get the technical stuff out the way before I do the creative side.
"For the money, for the glory, and for the fun! Mostly for the money."
~ Bo "Bandit" Darville
User avatar
Seven.FFF
Manic Miner
Posts: 744
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Bank switching issue

Post by Seven.FFF »

deanysoft wrote: Tue Apr 02, 2024 2:58 pm I can't say I've found a use for this so really only use banks 0,1,3,4,6 and 7 which are always appear at $c000 to $ffff
One good use is where you have a double buffer using the shadow screen. You display one screen, and draw your updates to the other. When you finish updating, you display the other screen and start drawing updates to the first screen. And carry on alternating between the two.

You could keep bank 7 paged into the upper 16K and keep switching your updating code to draw at $4000..5AFF or $C000..DAFF. Or you can keep your updating code always drawing to $C000..DAFF, and keep switching the upper 16K paging between bank 5 and 7. Since paging is just a single out, that's more efficient than having to keep updating your updating code.

Of course such a game would be limited to 128K only, as you wouldn't be using shadow screen or paging at all in a 48K game that could also run on 128K. These are some of the reasons why 48K stayed the lowest common denominator. But I've written some games that were too large to be done in 48K at all, so I was able to really make good use use of paging in those.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
C.Born
Manic Miner
Posts: 239
Joined: Sat Dec 09, 2017 4:09 pm

Re: Bank switching issue

Post by C.Born »

Hi
wanted to mention "Logic RAM Banks" although you probably solved to problem

there are TWO ways off counting the memory banks while they are the same chip
https://github.com/ZXSpectrumVault/rom- ... 8_ROM0.asm

it mentions the difference in a table

Code: Select all

; -----------------
; Logical RAM Banks
; -----------------
; Throughout ROM 0, memory banks are accessed using a logical numbering scheme, which
; maps to physical RAM banks as follows:
;
; Logical Bank   Physical Bank
; ------------   -------------
;     $00             $01
;     $01             $03
;     $02             $04
;     $03             $06
;     $04             $07
;     $05             $00
;
; This scheme makes the RAM disk code simpler than having to deal directly with physical RAM bank numbers.
and about the ramdrive

Code: Select all

; The actual files are stored in physical RAM banks 1, 3, 4 and 6 (logical banks 0, 1, 2, 3),
; starting from $C000 in physical RAM bank 1 and growing upwards.
and these are the ROM routines calls with both the same result

Code: Select all

logical switch
        LD   A,$04        ;
        CALL L1C64        ; Page in logical RAM bank 4 (physical RAM bank 7).

physical switch
        LD   A,$07        ; RAM bank 7.
        CALL L1F3A        ; Page in RAM bank 7.

this means that if you read someones routine you have to check if its using "physical" or "logical" counting
the 128k rom are not fully develloped around this difference and that makes it 1 step more complex



to make it more complex, Toni Baker rewrote the routine with "interrupt state preservation" and i rewrote THAT into a "steady timed" version
this uses the "physical" counting, you set the values in HL and make a jump to 'setpage'
i asume you dont need it like this but maybe you like to read the flow

Code: Select all

;-+-=-+-=_=-+-=-+-_-+
setpage0        ld hl,0xF000     ;10t h=11111000 l=00000000
                jr setpage       ;12t

setpage7        ld hl,0xF007     ;10t h=11111000 l=00000111
                jr setpage       ;12t delay



setpage         ld bc,ramPage    ;10t bc = 32765, port 0x7FFD
                ld de,mbank      ;10t de = 23388, sysvar

                LD   A,R         ; 9t P/V flag=Interrupt status  (Toni Baker et all)
                PUSH AF          ;11t Stack interrupt status.

                di               ; 4t
                ld a,(de)        ; 7t
                and h            ; 4t set rambank 0  SCREEN 0
                or  l            ; 4t     set rambank 7
                ld (de),a        ; 7t store value before using it !!
                out (c),a        ;12t     port 0x7FFD

                POP  de          ; 10t P/V flag=Former interrupt status.  (Toni Baker et all)
                bit 2,e          ;  8t FLAGregister = %SZ-H-PNC  po/pe =bit2
                                 ;    https://learn.cemetech.net/index.php/Z80:Flags_and_Bit-Level_Instructions#F_Register
                                 ;    https://clrhome.org/table/#jp
;                JP   PO,NONEI    ;10t Jump if interrupts were previously disabled.
                jr nz,NONEI      ;  7t
                ret nz           ;  5t delay
 
                EI               ;} 4t Re-enable interrupts.
                jr donei          ;} 12t

                                 ;] +5 jump
NONEI:          nop              ;] 4t    
                jr donei          ;]12t

donei            ret              ; 10t

tpage   equ 10+10 +9+11 +4 +7+4+4+7 +12+10+8 +(7+5) +(4+12) +10  ;=134

tsetp   equ 10+12+tpage          ;=152
User avatar
ketmar
Manic Miner
Posts: 716
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Bank switching issue

Post by ketmar »

AndyC wrote: Thu Apr 04, 2024 9:24 am I think it's probably also important to add that when you say "your stack stays in the old one" what you really mean is "all the data that was on the stack stays in the old bank but the Z80 thinks whatever random data is in the same addresses in the new bank is what was on the stack".
yeah, you right. my bad. that's why i prefer 48K machines: my brain hurts from paging! ;-)
User avatar
deanysoft
Dizzy
Posts: 75
Joined: Sat Jun 18, 2022 10:35 pm

Re: Bank switching issue

Post by deanysoft »

Seven.FFF wrote: Thu Apr 04, 2024 2:02 pm You could keep bank 7 paged into the upper 16K and keep switching your updating code to draw at $4000..5AFF or $C000..DAFF. Or you can keep your updating code always drawing to $C000..DAFF, and keep switching the upper 16K paging between bank 5 and 7
Ah, yeah - now that's a cunning idea.

I usually end up coding for the 128K as I like the freedom the extra space brings. Yes, you've got to be more organised with where stuff gets put but that should be the norm with assembly. Keeping the banks organised is easy with Zeus as it has a Zeuspage function and you can easily view all the RAM.
User avatar
Seven.FFF
Manic Miner
Posts: 744
Joined: Sat Nov 25, 2017 10:50 pm
Location: USA

Re: Bank switching issue

Post by Seven.FFF »

deanysoft wrote: Thu Apr 04, 2024 7:59 pm Keeping the banks organised is easy with Zeus as it has a Zeuspage function and you can easily view all the RAM.
Yea, Zeus is great for 128K coding.
Robin Verhagen-Guest
SevenFFF / Threetwosevensixseven / colonel32
NXtel NXTP ESP Update ESP Reset CSpect Plugins
User avatar
Bedazzle
Manic Miner
Posts: 305
Joined: Sun Mar 24, 2019 9:03 am

Re: Bank switching issue

Post by Bedazzle »

DoctorRad wrote: Tue Apr 02, 2024 4:36 pm Banks 2 and 5 always appear as 'conventional' RAM, between &8000-&BFFF and &4000-&7FFF respectively. You can page them in to also appear at &C000-&FFFF as for any other pages, but it's not really clear why you'd want to.
For example, you are drawing on two screens. It is easier to use procedure that works with screen always in $C000, than two screen at different addresses.
User avatar
deanysoft
Dizzy
Posts: 75
Joined: Sat Jun 18, 2022 10:35 pm

Re: Bank switching issue

Post by deanysoft »

Seven.FFF wrote: Thu Apr 04, 2024 10:18 pm Yea, Zeus is great for 128K coding.
It is, and for this sort of thing...

Image
Image

Clearly I'm not. Should I be?

"Why stop now just when I'm enjoying it"
User avatar
clebin
Manic Miner
Posts: 979
Joined: Thu Jun 25, 2020 1:06 pm
Location: Vale of Glamorgan
Contact:

Re: Bank switching issue

Post by clebin »

deanysoft wrote: Tue Apr 02, 2024 2:58 pmThe bank switching is an instantaneous hardware switch so there's no delay.
So, just the few instructions needed to switch banks then I guess? I was expecting a bigger speed penalty to bank switching.

The game I'm working on (with z88dk) has music stored in one of the upper memory banks to be played by the Akg player. I thought the bank switching would make in-game music a no-go so I restricted it to the title screen, even avoiding calling sp1 routines to keep the music fluid.

One day fiddling around, I forgot to stop the music in-game and was really surprised. The game ran just like before. Music and animation both perfectly smooth running from different banks, in a game loop that was already running "flat out". Maybe if I added an on/off toggle or saw them side-by-side I could tell the difference. I'm impressed by that.
User avatar
1024MAK
Bugaboo
Posts: 3123
Joined: Wed Nov 15, 2017 2:52 pm
Location: Sunny Somerset in the U.K. in Europe

Re: Bank switching issue

Post by 1024MAK »

clebin wrote: Mon Apr 08, 2024 9:21 am So, just the few instructions needed to switch banks then I guess? I was expecting a bigger speed penalty to bank switching.
The latch chip that stores the extra address bits for the memory is just as quick as the Z80. The limiting factors are what you have to do in your software routine and the OUT instruction.

Once the OUT instruction has beed executed, the RAM (or ROM) will be ready no matter which area/bank has just been selected.

Mark
:!: Standby alert :!:
“There are four lights!”
Step up to red alert. Sir, are you absolutely sure? It does mean changing the bulb :dance
Looking forward to summer later in the year.
User avatar
Prototron
Drutt
Posts: 11
Joined: Thu Mar 07, 2024 2:10 pm
Location: Glasgow, Scotland

Re: Bank switching issue

Post by Prototron »

Incredibly detailed advice. Thanks everyone!

So I have it sort of working now. I have code at $8000, Graphics Data at $C000 Bank 0, and Draw screen at $C000 Bank 1.

The paging in and out of different banks within my draw routine seems to work, and sprites are appearing on the screen as they should. I'll need to do some re-writing for the tiles, and maybe sit down with pen and paper to properly figure out what goes where in all the available banks.

I'm just keeping the stack screen copy for now, but will probably try the bank swap later on once I get more comfy.
"For the money, for the glory, and for the fun! Mostly for the money."
~ Bo "Bandit" Darville
User avatar
deanysoft
Dizzy
Posts: 75
Joined: Sat Jun 18, 2022 10:35 pm

Re: Bank switching issue

Post by deanysoft »

Prototron wrote: Tue Apr 09, 2024 4:40 pm
So I have it sort of working now. I have code at $8000, Graphics Data at $C000 Bank 0, and Draw screen at $C000 Bank 1.
OK, I can't quite visualise how that works but you say it does so that's good. I would've thought if graphics data is banked in at $c000 then your screen, in another bank at $c000, is not available. So copying from one to another means you're having to copy bytes to a temp buffer first or hold your bytes in registers, swap bank then copy to screen. Both don't seem very fast methods.

I'm probably just not quite understanding your process here. So long as it works, that's the main thing.
User avatar
ketmar
Manic Miner
Posts: 716
Joined: Tue Jun 16, 2020 5:25 pm
Location: Ukraine

Re: Bank switching issue

Post by ketmar »

just to add more confusion to the thread: with +3 one can page in 16K RAM at $0000 (instead of ROM). yay. all go +3! ;-)
dvduk
Drutt
Posts: 35
Joined: Sat Apr 08, 2023 6:29 pm

Re: Bank switching issue

Post by dvduk »

ketmar wrote: Tue Apr 09, 2024 8:55 pm just to add more confusion to the thread: with +3 one can page in 16K RAM at $0000 (instead of ROM). yay. all go +3! ;-)
And with the +2 (B - if I recall!) that was based on the +3.

Nice addition for CPM support at least.
Can also be found at SamstersVideos - which does occasionally update when I have time...
Post Reply