|
ForumsSega Master System / Mark III / Game GearSG-1000 / SC-3000 / SF-7000 / OMV |
Home - Forums - Games - Scans - Maps - Cheats - Credits Music - Videos - Development - Hacks - Translations - Homebrew |
Goto page 1, 2 Next |
Author | Message |
---|---|
|
Bankswitching: Making a 64k game
Posted: Mon Apr 09, 2018 4:56 am
|
So I want to make a game larger than 32k. Reading some old posts I made, it seems as though what I want (a 64k or larger) game would be easy. Only trouble is, I don't know how to bankswitch. Here's my header as it currently stands:
.memorymap defaultslot 0 slotsize $c000 ; 48KB (ROM) slot 0 $0000 slotsize $2000 ; 8KB (RAM) slot 1 $c000 slotsize $4000 ; ROM slot 2 $8000 slotsize $100 ; RAM slot 3 $dd00 .endme .rombankmap bankstotal 1 banksize $8000 ; 32KB banks 1 .endro Do I need to change anything besides just changing the bankstotal and banks #s from 1 to 2? Or what do I need to do here to get a 64k game? I am under the impression that I must have 4 16k banks for a 64k game. Or is that not true? I need some help here. |
|
|
Posted: Mon Apr 09, 2018 6:02 am |
So I went ahead and changed the bank numbers to 2 and it gave me a 64k file that works perfectly on an Everdrive. So now I have a question: How does saving includes files work? At the end of my code I have all the .include statements. Would all these go in the first bank or can I direct them to the second bank if I wanted to? | |
|
Posted: Mon Apr 09, 2018 9:15 am |
Put the memory map at the start. You need to change it to the 16KB map that is part of the WLA DX examples. | |
|
Posted: Mon Apr 09, 2018 11:48 am |
if 48 KB is enough for you, you really don't need bankswitching. | |
|
Posted: Mon Apr 09, 2018 1:25 pm |
I went ahead and did that. The game still works fine. The memory map is at the start. But how does saving .include files work? Do they all go in one bank or can you make it so they can go in different ones? .memorymap defaultslot 0 slotsize $4000 ; 48KB (ROM) slot 0 $0000 slotsize $2000 ; 8KB (RAM) slot 1 $c000 slotsize $4000 ; ROM slot 2 $8000 slotsize $100 ; RAM slot 3 $dd00 .endme .rombankmap bankstotal 4 banksize $4000 ; 16KB banks 4 .endro |
|
|
Posted: Mon Apr 09, 2018 3:14 pm |
Well you save them with a Ctrl+S or Cmd+S like any other file ;)
They are included in the ROM wherever you tell WLA-DX to include them. Which will depend upon which Section you include them in. Or if your includes define the section they are in then they will go into the section mentioned in the included file. All included does is say "copy the contents of file X to here" So it will get everything in the included file and then write it out into one big file in memory and then parse it. There is nothing special about them. so if you have A ; This is line A B ; This is line B and C is .include "B" .include "A" WLA-DX will then "see" ; This is line B ; This is line A |
|
|
Posted: Mon Apr 09, 2018 3:44 pm |
I've been trying to get my program to jump to different banks without any success. Look at my second bank:
.bank 1 get_level_1 ld hl,$0000 ; first tile @ index 0. call vrampr ; prepare vram. ld hl,level1tile ; title screen tile data. ld bc,143*32 ; 128 tiles, each tiles is 32 bytes. call vramwr ; write title screen tiles to vram. ld hl,$3800 ; point to name table. call vrampr ; prepare vram. ld hl,level1map ; title screen tile map data. ld bc,32*24*2 ; 32 x 24 tiles, each is 2 bytes. call vramwr ; write name table to vram. ld hl,$c000 ; color bank 1, color 0. (bg) call vrampr ; prepare vram. ld hl,level1palette ; background palette. ld bc,32 ; 4 colors. call vramwr ; set background palette. ret level1tile .include "level1a (tiles).inc" level1map .include "level1a (tilemap).inc" level1palette .include "level1a (palette).inc" Now how would I get the program (which would currently be in bank 0) to tell it to call this code in another bank (bank 1)? Looking at the examples in wla.txt, one would surmise to use this: LD a, 2 CALL get_level_1 but this does not work. Help! |
|
|
Posted: Mon Apr 09, 2018 9:52 pm |
There's loads of examples of this.
ld a, :label
ld ($ffff), a call label ... .section "my paged function" superfree label: // Code here ret .ends ..is how I'd do it, using sections. It's more common to put the data in higher banks rather than code, though. |
|
|
Posted: Tue Apr 10, 2018 2:11 am |
So I guess "superfree" isn't going to work since I would like the include files for level 1 in bank 1 and using "superfree" puts them in bank 0. The whole point of me using banks was to put .includes in them so I can put more of them in. So here's what I have now for bank 1:
.bank 1 .org 0 .section "initialize level 1" free INIT_LEVEL1: ld hl,$0000 ; first tile @ index 0. call vrampr2 ; prepare vram. ld hl,level1tile ; title screen tile data. ld bc,143*32 ; 128 tiles, each tiles is 32 bytes. call vramwr2 ; write title screen tiles to vram. ld hl,$3800 ; point to name table. call vrampr2 ; prepare vram. ld hl,level1map ; title screen tile map data. ld bc,32*24*2 ; 32 x 24 tiles, each is 2 bytes. call vramwr2 ; write name table to vram. ld hl,$c000 ; color bank 1, color 0. (bg) call vrampr2 ; prepare vram. ld hl,level1palette ; background palette. ld bc,32 ; 4 colors. call vramwr2 ; set background palette. ret vrampr2 push af ld a,l out ($bf),a ld a,h or $40 out ($bf),a pop af ret vramwr2 ld a,(hl) out ($be),a inc hl dec bc ld a,c or b jp nz,vramwr2 ret level1tile .include "level1a (tiles).inc" level1map .include "level1a (tilemap).inc" level1palette .include "level1a (palette).inc" .ends But when I try to call to this bank using the following code, it doesn't work. ld a, :INIT_LEVEL1 ld ($ffff), a call INIT_LEVEL1 Why won't it work and how do I make it work? |
|
|
Posted: Tue Apr 10, 2018 4:36 am |
Superfree will put the section in any place where there is space, it means you don't have to manage the location.
Make sure you put your banks in slot 2. You should also consider using compression for graphics data, else you may find you have a lot of unused space as the chunks get large. |
|
|
Posted: Tue Apr 10, 2018 6:05 am |
I figured it out.
I had to change my memory map to this: .memorymap defaultslot 0 slotsize $4000 ; 16KB (ROM) slot 0 $0000 slot 1 $4000 slot 2 $8000 slot 3 $c000 .endme .rombankmap bankstotal 4 banksize $4000 ; 16KB banks 4 .endro Then I put the code in slot 1 and used this to get there: ld a, :INIT_LEVEL1 ld ($fffe), a call INIT_LEVEL1 But then PSGLib broke and the title screen song wouldn't play any more. That was not fun trying to fix that. I eventually got it back working again. I changed its slot 3 address to $dd00, which it apparently liked. How do I compress graphic data? |
|
|
Posted: Tue Apr 10, 2018 7:37 am |
That's the memory map I referred to earlier. You should avoid using slot 1, slot 2 only is the most compatible way.
You can compress data using BMP2Tile. It includes decompressors. These can often reduce the data size by 60-70% which helps pack data into the 16KB slots. But I suggest you tackle the paging first. |
|
|
Posted: Tue Apr 10, 2018 7:47 am |
Did you read this BTW?
There are useful hints I wrote after I had done Waimanu SMS and also some code you may grab :) |
|
|
Posted: Tue Apr 10, 2018 11:11 am |
Thanks for the tips. This game I'm working on is actually a game for the Game Gear, but I know it and the SMS are very similar. I chose Game Gear because it doesn't have very many games for it, and even fewer homebrews for it. And I love the Game Gear, hence my username.
As for paging, I have selected for my game to be 64k, but from my understanding it's a very odd size? |
|
|
Posted: Tue Apr 10, 2018 11:58 am Last edited by sverx on Tue Apr 10, 2018 3:32 pm; edited 1 time in total |
actually not. 32 KB, 64 KB, and 128 KB are very common.
edit: rephrasing - 64 KB is a common size for ROM chips. ;) |
|
|
Posted: Tue Apr 10, 2018 12:50 pm |
There are very few 64KB games, but not for any great technical reason. | |
|
Posted: Thu Apr 12, 2018 7:22 am |
I need help again. This time it's displaying sprites that are 16 pixels wide. The closest I ever got was it displaying the first half of the sprite. This is the code I used.
spritey1 ld c,a ; save hpos in C .rept 2 ; wladx: Repeat code four times. ld a,c ; load hpos into A ld b,2 ; loop: Repeat four times. - ld (hl),a ; write value to buffer at address. inc hl ; skip over the char code byte. inc hl ; point to next hpos byte in buffer. add a,8 ; add 8 (a tile's width in pixels). djnz - ; jump back .endr ; end of wladx repeat directive. ret spritex1 ld c,a ; save hpos in C .rept 2 ; wladx: Repeat code four times. ld a,c ; load hpos into A ld b,2 ; loop: Repeat four times. - ld (hl),a ; write value to buffer at address. inc hl ; skip over the char code byte. inc hl ; point to next hpos byte in buffer. add a,4 ; add 8 (a tile's width in pixels). djnz - ; jump back .endr ; end of wladx repeat directive. ret layout .db 16 17 .db 18 19 But no matter what I do, I can't get the second half to display. I am going mad. Can someone please help? |
|
|
Posted: Thu Apr 12, 2018 8:18 am |
you don't need to skip the 'char code byte' when writing the Y table, as the Y table is just an array of 64 Y positions, whereas XN table is a table of 64 X and 'char code byte' pairs. | |
|
Posted: Thu Apr 12, 2018 12:20 pm |
I commented that part out, but it still won't work. | |
|
Posted: Thu Apr 12, 2018 1:50 pm |
Are you sure the sprite pattern numbers are set correct? You are not writing them in this code. Only the x and y positions.
Also, you add 4 pixels to the x offset. not 8: add a,4 ; add 8 (a tile's width in pixels).
Could you also provide the buffer update routine to VRAM? Possibly there could be an issue too. |
|
|
Posted: Thu Apr 12, 2018 3:25 pm |
Here is the buffer updating code:
upbuf ld a,(spritey) ld hl,spritevp call spritey1 ld a,(spritex) ld hl,spritehp call spritex1 ld hl,score ld de,scorecc ld b,5 - ld a,(hl) add a,64 ld (de),a inc hl inc de inc de djnz - ret Here is the subroutines the above calls. spritey1 ld c,a .rept 2 ld a,c ld b,2 - ld (hl),a inc hl add a,8 djnz - .endr ret spritex1 ld c,a .rept 4 ld a,c ld b,4 - ld (hl),a inc hl inc hl add a,8 djnz - .endr ret And here is the map for it: spritelayout .db 16 17 .db 18 19 I'm using Emulicious's sprite and tile viewer and the tiles are loaded in there, but they're just not displaying correctly. This sprite I'm trying to make correct is 16x16 pixels. There is another sprite I want to do that is 16x32 pixels, and a third that's 16x8 pixels, but I haven't tried that yet because I haven't gotten this first one done. |
|
|
Posted: Fri Apr 13, 2018 12:04 am |
You need to debug by inspecting your data in RAM and the sprite table in video RAM. You can't fix it very easily just from the code. | |
|
Posted: Fri Apr 13, 2018 8:09 am |
I think I fixed it, although I can't seem to optimize at the moment because still some parts of the code must be screwed up. For example, the 16x32 sprite only displays correctly if I have two blank spaces after the sprte on each row. The first discovery came when I changed the first sprite's y getting code to this:
spritey1 ld b,2 - push af push af add a,8 djnz - ld de,3 add hl,de ld b,4 - pop af ld (hl),a dec hl djnz - ret But I couldn't just double this for the sprite that is double its length. And finally for the third sprite that is 16x8, I did this: thirdsprite1y ld b,2 - push af push af add a,0 djnz - ld de,3 add hl,de ld b,4 - pop af ld (hl),a dec hl djnz - ret So I'm thinking that this sprite is actually two overlapping each other, but it looks like one, and it works. |
|
|
Posted: Fri Apr 13, 2018 8:21 am |
Emulicious' "sprite viewer" gives you h/v position of each sprite, so that you can check if they're as you expect them | |
|
Posted: Fri Apr 13, 2018 12:02 pm |
When these routines (in their original form) work to put cars on the screen in Racer (http://www.smspower.org/Articles/CreateARacingGame), they are coded specifically to expect 32x32 pixel sprites (4x4 tiles). As far as I can see, you have halfway altered them (which is totally great), but these routines were tailored to a very specific layout of the sprite table, and only updating y and x position as far as I remember. When you have duplicated these routines (firstsprite, secondsprite, thridsprite) with different sprite layouts, that might cause trouble.
I suspect that the sprite routines may partly overwrite the same areas of the sprite attribute table, and this causes upredictable output. I second Maxim's suggestion about debugging: Maybe put some breakpoints in after each sprite update routine, and see if the SAT buffer looks like it should. |
|
|
Posted: Fri Apr 13, 2018 12:38 pm |
First of all: By all means, stop (ab)using hang-on‘s Racer code as a base for new games already! It‘s really taylor-made to do very soecific things that only work within the context of the original Racer game and cannot easily be adapted for other purposes. You will inevitably run into trouble if you‘re trying to get results this way, as you obviously experience right now, and not for the first time, I might add.
Sorry if I sound harsh. I find your persistance admirable, but if you had taken all the time you sunk into poking around in the Racer code in hope of random achievments, and put it to good use by actually trying to understand what‘s going on there, you‘d be pretty much a pro by now and could create games from scratch as you desire. |
|
|
Posted: Fri Apr 13, 2018 1:20 pm |
I'm sorry for abusing it. I was only using it because I was too lazy to type out all that stuff anyway, and I learn by examples, anyway. That's how i learned C, from seeing examples and then copying what they did and seeing the results I wanted to see. I don't have to copy anything any more when I want to program a new Virtual Boy game. In fact, I'm programming one right now, and out of the almost 2,000 lines of code in it, 2 are copied/pasted. So, I'm sorry for learning to program this way, I am learning how stuff works this way for the Game Gear too, though. | |
|
Posted: Fri Apr 13, 2018 1:39 pm |
I understand, and that‘s generally a good way to learn. The problem in this case is that Racer does many things a lot differently than one would usually approach them, thus being not a very good example to learn from. | |
|
Posted: Fri Apr 13, 2018 3:14 pm |
Sorry for the double post. Have you, by any chance, looked into using devkitSMS? If you‘re familiar with C you should get good results in no time, much faster than by trying to figure out another person‘s asm code. | |
|
Posted: Tue Apr 17, 2018 4:16 pm |
It's great that the 2000 lines of code are yours,can you do the same for your projects on game gear? | |
|
Posted: Wed May 02, 2018 3:17 am |
OK, I read this page on bankswitching:
http://www.smspower.org/Development/Mappers It did not, however, explain how to switch to slot 3. I was thinking putting each slot in each bank: 4 slots in 4 banks, making one 64k game. Or am I missing something? |
|
|
Posted: Wed May 02, 2018 5:56 am |
If you were able to bank in slot 3, you'd be shutting out the console RAM.
I think that would make it very hard to have a game without that. |
|
|
Posted: Wed May 02, 2018 7:05 am |
A quote from said page:
So if slot 3 can't be written to, is this an okay memory map? .memorymap defaultslot 0 slotsize $4000 ; 16KB (ROM) slot 0 $0000 slot 1 $4000 slotsize $8000 slot 2 $8000 .endme .rombankmap bankstotal 4 banksize $4000 ; 16KB banks 4 .endro |
|
|
Posted: Wed May 02, 2018 8:17 am |
No - use the ones we already recommended. I would suggest slot 2 only is the best option. | |
|
Posted: Wed May 02, 2018 12:38 pm |
The matter is that your game will be made up of ROM banks, 16 KB each, and you can map 3 of them at a given time, using Master System's 3 slots. Thus, if you have a 48 KB ROM, you map each of the bank in each of the slots and you don't need any bank-switching at run time. If you do make a ROM bigger than that, for instance a 64 KB ROM, you'll have to page-in/page-out chunks of your ROM, that's what bank-switching is all about. I again suggest you simply map ROM bank0 to slot0, ROM bank1 to slot1 and you map either bank2 and bank3 to slot 2, according to your needs. If both bank2 and bank3 simply contains assets (graphic, music...) bank-switching will be pretty straightforward (using WLA-BX :label directive, which will give you the number of the bank containing the asset you want to access) |
|
|
Posted: Wed May 02, 2018 8:30 pm |
So if I'm making a 64k game (which I am), and I want to put everything in one slot, I'd have to make that slot big enough for everything, right? Which would mean I would have to change slotsize in the memory map accordingly, right? But if I do that, and you're suggesting I put everything in slot 2, what size and where should the other slots go to? | |
|
Posted: Wed May 02, 2018 10:07 pm |
There are two things in play here. One is the way stuff is put into the ROM. You want 64KB, fine. The Master System can only look at 48KB at once, though. This is what bank switching is for.
You can think of the 48KB as three 16KB slots. You can choose which of the ROM's four 16KB banks appears in each slot. Most commonly, we only swap out one of them, because it's kind of complicated otherwise. You don't get to choose the slot size, or the bank size, or how many slots there are. You just get to choose what goes in which slot. There is a WLA DX ROM bank map which pretends the slot sizes are variable. This is a hack to allow some stuff to work while not enforcing a split at the 16KB mark. It's really not that important though. It only works if you don't actually try to use the funny sized slots for other banks. Stick to the 16KB version. |
|
|
Posted: Wed May 02, 2018 10:20 pm |
So if I think of the 48KB as 3 slots, what is the other 16KB then? Aren't there supposed to be 4 slots, numbered 0-3?
And so if each slot and each bank are 16k, this is what my memory map now looks like: .memorymap defaultslot 0 slotsize $4000 ; 16KB slot 0 $0000 slot 1 $4000 slot 2 $8000 slot 3 $c000 .endme .rombankmap bankstotal 4 banksize $4000 ; 16KB banks 4 .endro |
|
|
Posted: Thu May 03, 2018 12:23 am |
No, there is no fourth slot, since the top 16KB are reserved for internal stuff (mainly RAM); this means that if you want to use more than 48KB of ROM, you will have to use bankswitching. | |
|
Posted: Thu May 03, 2018 6:04 am |
You can consider the RAM to be in a third 8KB slot, and you need to do that to use ramsections. But they aren't interchangeable. | |
|
Posted: Thu May 03, 2018 8:01 am |
@Gamegearguy: I'm curious to know how you know you're making a 64 KB game. I mean, how can you be so sure that your game won't fit in 48 KB? How can you be sure that it will surely fit in 64 KB?
You already know the size of your assets so you're doing an educated guess? |
|
|
Posted: Thu May 03, 2018 10:01 am |
This is an experiment to help me through the process of bankswitching. But seeing as though one bank is 1% empty and another is about 20% empty, I doubt I could fit it in 48k. | |
|
Posted: Thu May 03, 2018 3:31 pm |
I see. So, if you're using superfree sections, it won't be too hard to add bankswitching.
See this example: ; ********** SUPERFREE ASSETS (in slot 2) ********** .slot 2 .section "DESERT level assets" superfree BGdesert_palette: .incbin "inc/BGdesert_palette.bin" BGdesert_tiles: .incbin "inc/BGdesert_tiles.psgcompr" .ends .section "ROCK level assets" superfree BGrock_palette: .incbin "inc/BGrock_palette.bin" BGrock_tiles: .incbin "inc/BGrock_tiles.psgcompr" .ends here I declared two superfree sections, each containing two assets (one palette and one set of tiles in psgcompr format). Then, when you need to access the assets in the first section, you do: ld a,:BGdesert_palette
ld ($ffff),a this tells the SEGA mapper to map the block that contains the BGdesert_palette label (the first in our case) into slot 2. If you need to map the other asset instead, you do ld a,:BGrock_palette
ld ($ffff),a quite easy, after all. |
|
|
Posted: Mon May 07, 2018 6:32 am |
So what if I wanted a 1MBit game? Would I have 8 16k banks like so:
.memorymap defaultslot 0 slotsize $4000 ; 16KB slot 0 $0000 slot 1 $4000 slot 2 $8000 slot 3 $c000 .endme .rombankmap bankstotal 8 banksize $4000 ; 16KB banks 8 .endro |
|
|
Posted: Mon May 07, 2018 6:48 am |
Yes.
The memory map defines what the Z80 can see (splits the 64KB address space into slots). The ROM bank map defines what's in the ROM (almost unlimited). You should remove the slot at $c000, or make it smaller if you intend to use RAM sections. |
|
|
Posted: Mon May 07, 2018 10:51 am |
In Astroswab I used the following memorymap with a constant to define the size of the rom: ; -----------------------------------------------------------------------------
.memorymap ; ----------------------------------------------------------------------------- defaultslot 0 slotsize $4000 slot 0 $0000 slot 1 $4000 slot 2 $8000 slotsize $2000 slot 3 RAM_START .endme .if ROMSIZE == 128 .rombankmap ; 128K rom bankstotal 8 banksize $4000 banks 8 .endro .endif .if ROMSIZE == 256 .rombankmap ; 256K rom bankstotal 16 banksize $4000 banks 16 .endro .endif ... and assigned every ramsection to slot 3 like this: ; -----------------------------------------------------------------------------
.ramsection "bluelib global variables" slot 3 ; ----------------------------------------------------------------------------- VDPStatus db pause_flag db input_ports dw tv_type db ; .ends |
|
|
Posted: Tue May 08, 2018 7:43 am |
This is what my ROM header looks like now:
.memorymap defaultslot 0 slotsize $4000 ; 16KB slot 0 $0000 slot 1 $4000 slot 2 $8000 .endme .rombankmap bankstotal 8 banksize $4000 ; 16KB banks 8 .endro I tried putting some code into bank 2 but it wouldn't play it, it looped back to the title screen. I'm assuming it's because my slotsize is only 16KB like it says, but I don't know how to make them any bigger. I'm assuming the only valid numbers are $0000-$ffff, but when I look at the compiled message, it mentions $1ffff. Can I make my slots like be 64k wide and use $10000-$1ffff, or what do I do? |
|
|
Posted: Tue May 08, 2018 7:51 am |
maps seems right.
as for putting CODE in bank2, I suggest you don't do that. it's way easier if you put ASSETS in banks 2 & 3, and keep your code in bank 0 and 1, which you won't page out. |
|
|
Posted: Tue May 08, 2018 8:39 am |
How can games be 1MBit yet only use one 16k slot? | |
|
Posted: Tue May 08, 2018 8:47 am |
you're still missing the point of bank switching
the point is: imagine your stereo has 3 audio cassette decks - you can have 100 audio cassettes in your collection but you can put only up to 3 of them at a given time in your stereo. ROM slots are your decks, ROM banks are your audio cassettes. |
|
Goto page 1, 2 Next |