Forums

Sega Master System / Mark III / Game Gear
SG-1000 / SC-3000 / SF-7000 / OMV
Home - Forums - Games - Scans - Maps - Cheats - Credits
Music - Videos - Development - Hacks - Translations - Homebrew

View topic - A few hints on coding a medium/large sized game using WLA-DX

Reply to topic
Author Message
  • Joined: 05 Sep 2013
  • Posts: 3796
  • Location: Stockholm, Sweden
Reply with quote
A few hints on coding a medium/large sized game using WLA-DX
Post Posted: Mon Dec 14, 2015 1:47 pm
Last edited by sverx on Wed Dec 16, 2015 10:42 am; edited 1 time in total
After having completed Waimanu Scary Monsters Saga, I feel to share a few things I learnt -mainly through my own mistakes- so to help anyone willing to work with WLA-DX on a non-trivial project. I hope you'll find these hints useful.
Of course I'm not saying this is 'the only' or 'absolutely the better' way to deal with this stuff, it's just what I found that could better ease my work.
Feel free to comment and let's debate! :)

To page or not to page
The first thing to address is whether your project needs paging or not, and it's better not to postpone that decision. If you're going to use background music, SFXs, splash screens, rich backgrounds, different sprites, animations... chances are you'll hardly be able to fit everything into just 48 KB. If you're still in doubt: go for it, as it's easier to remove paging than to add it later.
So, once you decided your big project does need paging, you have to choose your strategy. The simpler approach, which means to page in ROM banks using slot 2 only, will be enough for your needs at least in 95% of cases. If for some peculiar reason you've got a single asset which is bigger than 16 KB and you really can't break it into smaller parts, you could consider paging in both slot 1 and slot 2 so that you could map a contiguous block of 32 KB. Paging into slot 0 is instead discouraged, as you'll end up paging out the code you didn't store in the first 1024 bytes... and that means you'll have to face bigger problems.
Once you've decided your paging strategy, you've got to write your own ROM map and learn how to use free and superfree sections. Paging can really become a nightmare if you don't learn to use superfree sections. Instead, using superfree sections for every single asset you can page out (so you've got to exclude what you can't page out, more on this later) will relieve you of the burden of finding the right spot to place each asset in ROM.
As for free sections, it's the way to go to place into unpaged ROM your code and the assets you don't want to page out. See next section.

examples of ROM maps
Very simple memory map with 3 16 KB slots. 3 16 KB banks defined. Can be used for paging in any slot.
.memorymap
defaultslot 0
slotsize $4000 ; ROM
slot 0 $0000
slot 1 $4000
slot 2 $8000
slotsize $2000 ; RAM
slot 3 $c000
.endme

.rombankmap
bankstotal 3
banksize $4000
banks 3
.endro


Memory map with a single (almost) 32 KB bank 0, a 16 bytes bank 1 just for the SEGA ROM header and a 16 KB slot 2. Can be used for slot 2 only paging. This is from Waimanu SMS source.
.memorymap
defaultslot 0
slotsize $7ff0 ; ROM (won't page this)
slot 0 $0000
slotsize $0010 ; SEGA ROM header (won't page this too)
slot 1 $7ff0
slotsize $4000 ; ROM (... will page this!)
slot 2 $8000
slotsize $2000 ; RAM
slot 3 $c000
.endme

.rombankmap
bankstotal 8
banksize $7ff0 ; 32 KB minus 16 bytes
banks 1
banksize $0010 ; 16 bytes, for SEGA ROM header
banks 1
banksize $4000 ; 16 KB (each)
banks 6        ; 6 of them. Makes this 128 KB total
.endro


Switch to dynamic placement of code and assets
Using free and superfree sections also means you're switching to dynamic placement of code and assets into your ROM. At first, it might seem you're losing control on what's happening into your project but... no, it isn't so. Instead, you're gaining a big advantage: you don't absolutely have to worry where to place your stuff around, it's just like having a warehouse that organizes itself each time you add/remove or modify an item. So the only code that shouldn't be dynamically placed is boot code (at .org $0000), IRQ and NMI service handlers (at .org $0038 and .org $0066 respectively): these will have their own force sections. Everything else (code and data!) should be contained within a free, superfree or semisubfree (see note) section.

examples of superfree and free sections
Superfree sections:
.section "Intro (MUSIC Asset)" superfree
Intro:
.incbin "inc/intro.psg"
.ends

.section "Points (SFX)" superfree
SFXPoints:
.incbin "inc/punti.psg"
.ends


Free sections (code):
.section "waitForVBlank" free
waitForVBlank:
  xor a
  ld (VBlankFlag),a
-:ld a,(VBlankFlag)
  or a
  jr z,-
  ret
.ends

.section "Div and Mod" free
; courtesy of http://www.smspower.org/Development/DivMod
; Integer divides D by E
; Result in D, remainder in A
; Clobbers F, B
DivMod:
  xor a
  ld b,8
-:sla d
  rla
  cp e
  jr c,+
  sub e
  inc d
+:djnz -
  ret
.ends


The boot, IRQ and NMI sections
.org $0000                      ; this goes at ROM address 0 (boot) : standard startup
.section "Startup" force
  di                            ; disable interrupt
  im 1                          ; interrupt mode 1 (this won't change)
  ld sp, $dff0                  ; set stack pointer at end of RAM
  jp init_mapper                ; run init_mapper
.ends

.org $0038
.section "Interrupt handler" force
  push af
    in a,(VDPStatusPort)        ; read port to satisfy interrupt
    ld a,$01                    ; here the only interrupt enabled is VBlank so...
    ld (VBlankFlag),a           ;   ... write down that it actually happened
  pop af
  ei                            ; enable interrupt (that were disabled by the IRQ call)
  reti                          ; return from interrupt
.ends

.org $0066
.section "Pause handler" force
  push af
    ld a,$01
    ld (PauseRequested),a       ; raise flag to indicate pause was pressed
  pop af
  retn                          ; return from NMI
.ends

.org $0400
.section "Initialize mappers" semisubfree
; This maps the first 48K of ROM to $0000-$BFFF and resets RAM mapper to $00 too
; this section must exist within first 1K of ROM, thus it's semisubfree (.org $0400)
; also, this clears RAM mem setting $00 from $C000 to $dff0
init_mapper:
  ld de,$fffc
  ld hl,mapper_init_values
  ld bc,$0004
  ldir

  xor a              ; clear RAM (to value 0x00)
  ld hl,$c000        ;   by setting value 0
  ld (hl),a          ;   to $c000 and
  ld de,$c001        ;   copying (LDIR) it to next byte
  ld bc,$1ff0        ;   for 8 KB minus 16 bytes
  ldir               ;   do that

  jp      main                  ; jump to main program
mapper_init_values:
  .db $00, $00, $01, $02
.ends

Note: semisubfree tells WLA-DX allocator to place the section freely but entirely within a given region. In this case it ensures the code is placed in an available spot within ROM first 1024 bytes.

Everything you shouldn't place in superfree sections
Superfree sections, as said, should be the most common, for assets. If you're going to use PSGlib for music and SFXs, you should declare a superfree section for each tune you put into your project, and a superfree section for each SFX too, even the smaller ones, even if they're just few bytes each. WLA-DX allocator will make great use of the assets fine granularity, packing everything the perfect way you really couldn't achieve manually.
Code and some data, instead, shouldn't be ever paged out, so you shouldn't place them into slot 2. Instead, use free sections into slot 0 and slot 1 (or slot 0 only if you choose for a single almost-32 KB unpaged ROM block). LUTs, especially those you use for speeding up your math, are probably the best example of the few assets you should keep in your unpaged ROM. Also, LUTs that are 256-bytes aligned can be accessed faster, so you'll probably want to align them all. Sections can be aligned easily, just tag them as so.

examples of LUTs
.section "Divide by 5" align 256 free
divide5:
  .rept 60 index n     ; 60 are enough for my needs...
    .db n/5
  .endr
.ends

.section "Module 3" align 256 free
module3:
  .rept 43
    .db 0,1,2         ; 129 values are more than enough...
  .endr
.ends


Use dynamic placement on variables too!
Just as we did with the code, it's very handy to let WLA-DX allocate our variables in RAM, for the very same reasons given before. So you really should avoid placing your variables at fixed addresses and let ramsections work. You can define as many ramsections as you need, in fact I suggest you define one for each 'module' you're coding.

examples of ramsections
.ramsection "PSGlib variables" slot 3
  ; fundamental vars
  PSGMusicStatus             db    ; are we playing a background music?
  PSGMusicStart              dw    ; the pointer to the beginning of music
  PSGMusicPointer            dw    ; the pointer to the current
  PSGMusicLoopPoint          dw    ; the pointer to the loop begin
.ends

.ramsection "system variables" slot 3
  SpriteTableY                    dsb MAXSPRITES          ; shadow sprite table
  SpriteTableXN                   dsb MAXSPRITES*2
  VBlankFlag                      db  ; VBlank flag
  PauseRequested                  db  ; Pause button flag
  KeysDown                        db  ; controllers
  NextAvailableSprite             db  ; sprite management
  MusicROMPage                    db  ; Music/SFX banks
  SFXROMPage                      db
  RestoreROMPage                  db
  savedbank                       db  ; previous mapped bank
 .ends

.ramsection "game variables" slot 3
  CurrentLevel            db       ; 0-31
  CurrentFrame            db       ; fraction of second (0-59)
  SecondsLeft             db
  Lives                   db       ; BCD!
  AliveWekas              db       ; number of Wekas currently roaming
  WekaEggs                db       ; number of eggs still hidden in the map
  Score                   dsb   4  ; BCD  (up to 99.999.999)
  HiScore                 dsb   4  ; BCD  (up to 99.999.999)
  GameMode                db       ; EASY or HARD
.ends


Know your macros
I found very convenient to create small macros for addressing some small but very common coding needs, such as shifting or rotating the accumulator a given number of times, or slightly more complex tasks, such as adding the accumulator value to HL. Using simple macros you can imagine to somewhat expand your Z80 instruction set.

example of some of the macros I actually use
.macro ASLA args shift
  ; this is ARITHMETIC SHIFT LEFT A by 'shift' bits using ADD A,A since it's more efficient than SLA A
  ; cycles taken = 4*shift
  ; affects A, Flags
  .rept shift
    add a,a
  .endr
.endm

.macro ASLHL args shift
  ; this is ARITHMETIC SHIFT LEFT HL by 'shift' bits using ADD HL,HL
  ; cycles taken = 11*shift
  ; affects HL, Flags unaffected
  .rept shift
    add hl,hl
  .endr
.endm

.macro RRA args shift
  ; this is ROTATE RIGHT A by 'shift' bits
  ; cycles taken = 4*shift
  ; affects A, Flags
  .rept shift
    rrca
  .endr
.endm

.macro ADDHLA
  ; this is HL=HL+A, since ADD HL,A doesn't exist...
  ; cycles taken = 20
  ; affects HL and A, Flags trashed
  add a,l
  ld l,a
  adc a,h
  sub l
  ld h,a
.endm


Never skip compression
Both if you plan to use paging or if you don't, you should always consider applying compression to your assets. For instance, if you don't need to randomly access and stream tiles into VRAM as fast as possible, you shouldn't store an uncompressed tileset into ROM, it just wastes precious space. Tiles can be compressed quite well using PSGaiden compression, for instance, which is also a built-in option into Maxim's BMP2Tile. Decompression can be achieved simply using this code snippet. I rolled out a faster PSGaiden tileset decoder, if you find the previous one too slow. Also, my version uses free sections and ramsections.
Tilemaps can be compressed too and there are a few options for compressing them. I can't suggest any particular choice here, except for my own STMcomp, which is anyway only suitable for compressing and decompressing whole screen maps (or at least whole screen lines) directly into VRAM. The compressor (it's a plugin for BMP2Tile) performs especially well on title/splash screens. Decompression from this format is probably the fastest option available too.
Non-graphic assets can be compressed too, either using a simple RLE or more powerful tools. Tunes and SFXs (.psg files) can be compressed once using psgcomp tool and can be used directly in this compressed form, as the library plays compressed files too, thus I suggest to always compress these files.

(do you think I skipped something of importance? let me know!)
  View user's profile Send private message Visit poster's website
  • Joined: 01 Feb 2014
  • Posts: 875
Reply with quote
Post Posted: Mon Dec 14, 2015 4:51 pm
Last edited by Kagesan on Wed Dec 16, 2015 8:10 am; edited 1 time in total
Very good topic.

When developing Bruce Lee I actually never used any superfree sections, I just put mostly free ones manually into the banks. Since my ROM was going to be clearly bigger than 64 KB but not coming very near the 128 KB, that worked quite well. I can see how superfree sections can help when space is tight and you have to squeeze your game in, though.

One more hint from me:

Organize your assets
Try to plan beforehand how you want to use the available space the VDP offers you. Re-use sprite parts whenever possible, and try to have a good idea at which points you are going to load in new data.
  View user's profile Send private message
  • Joined: 23 Mar 2013
  • Posts: 611
  • Location: Copenhagen, Denmark
Reply with quote
Post Posted: Mon Dec 14, 2015 9:45 pm
Thanks for the writeup, sverx! I'll surely draw on this information when I go beyond 32 kb next time!
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14726
  • Location: London
Reply with quote
Post Posted: Mon Dec 14, 2015 10:27 pm
I think it would be good to have some general purpose tools for compression outside of graphics, as that's coming up a lot recently. Level data can be uncompressed in the ROM, or decompressed to RAM. The former offers you almost unlimited level sizes, but costs ROM space. The latter allows you to optimise much better for ROM size.
  View user's profile Send private message Visit poster's website
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14726
  • Location: London
Reply with quote
Post Posted: Mon Dec 14, 2015 10:31 pm
Last edited by Maxim on Tue Dec 15, 2015 11:11 am; edited 1 time in total
A tip to add to your macros: you might want to install some of them as functions - for example, the "add hl, a" one - in the interrupt vectors. This makes a call fairly cheap (rst $08 etc) and you can hide that inside the macro. However, the inlined code is faster, but takes a lot more space. Code space is highly likely not to be a problem though :) it's hard to write 32KB of code.
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3796
  • Location: Stockholm, Sweden
Reply with quote
Post Posted: Tue Dec 15, 2015 9:49 am
Last edited by sverx on Tue Dec 15, 2015 9:59 am; edited 1 time in total
Maxim wrote
I think it would be good to have some general purpose tools for compression outside of graphics


Pucrunch? aPLib? (LOL! I'm sending you a link to a page on your website! :D )

Kagesan wrote
When developing Bruce Lee I actually never used any superfree sections, I just put mostly free ones manually into the banks.


I started that way too, but soon I realized too many times I had to move stuff around to avoid adding a new 16 KB bank when there was still more than 16 KB unused in my ROM. I ended leaving some free sections in the banks and using superfree sections from that point on. We mostly learn by doing, you know... but I would use superfree sections straight from the beginning, now.

Out of curiosity: how much free space is left into Bruce Lee ROM?
  View user's profile Send private message Visit poster's website
  • Joined: 01 Feb 2014
  • Posts: 875
Reply with quote
Post Posted: Tue Dec 15, 2015 7:33 pm
sverx wrote
Out of curiosity: how much free space is left into Bruce Lee ROM?

About 12 KB.
  View user's profile Send private message
  • Joined: 05 Sep 2013
  • Posts: 3796
  • Location: Stockholm, Sweden
Reply with quote
Post Posted: Wed Dec 16, 2015 10:49 am
Kagesan wrote
sverx wrote
Out of curiosity: how much free space is left into Bruce Lee ROM?

About 12 KB.


Oh, I see why you didn't struggle too much placing assets manually. (BTW I guess you're using quite a bit of compression on your assets, at least it seems to me, as WinRAR compresses your Bruce Lee ROM less than my Waimanu ROM, which contains quite a lot of compressed stuff...)
  View user's profile Send private message Visit poster's website
  • Joined: 09 Apr 2013
  • Posts: 106
  • Location: Sydney Australia
Reply with quote
Post Posted: Fri Dec 18, 2015 12:22 pm
Can I also suggest that you might want to try my WLA-DX z80 IDE.

It's great for larger projects and comes with

syntax highlighting
syntax checking
z80 cycle / byte counting
built-in z80 instruction set docs
quick navigation to labels/macros/defines etc
collapsable sections
find references to labels
define / macros shown in tool tip while hovering over label

:)

To install just download Eclipse
http://www.eclipse.org/downloads/packages/eclipse-ide-java-developers/mars1

navigate to the menu
--> Help --> Install New Software... --> Add...
Enter the update site details
https://dl.bintray.com/yuv422/EclipseZ80Editor

Then create a new general project and create a file with the extension .asm
Click yes when asked about xtext and then you're ready to go.
  View user's profile Send private message
  • Joined: 17 Sep 2013
  • Posts: 128
  • Location: Gravataí, RS, Brazil
Reply with quote
Post Posted: Mon Dec 21, 2015 10:06 pm
Positioning the spriteTable on the memory:

This may read obvious, but I believe the best address to your sprite table mirror on Ram is in this format: $xx40 and with no space between the Y's array and the X:Patten's array. That way a simple shift in the least significant part of the pointer alternates between the sprite components.
Exemple:


ld b, 64
ld hl, spriteTable

-:
  ;a := calculatesY
  ld (hl), a
  rlc l
  ;de: calculateXPattern
  ld (hl), d
  inc l
  ld (hl), e
  inc l
  rrc l 
djnz -



Also, when streaming the sprite table to the Vdp, theres no need to repositioning HL, only the VDP address:


   xor a
   out (VdpControlPort), a
   ld a, VramWrite.hi | VramSpriteTable.hi
   out (VdpControlPort), a
   
   ld hl, spriteTable
   .rept 64
       outi
        .endr
   ld a, VramSpriteTableBottom.lo
   out (VdpControlPort), a
   ld a, VramWrite.hi | VramSpriteTable.hi
   out (VdpControlPort), a
   .rept 64
       outi
        .endr

  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14726
  • Location: London
Reply with quote
Post Posted: Tue Dec 22, 2015 8:31 am
That's quite cunning. You're making the address of sprite n be 64+n so a shift gets you 128+2n (without overflow) which builds the 64 byte gap into the multiplication by 2 which you need anyway...

I've always spent my frame time to collect the y, x, n from all over the place into a sprite table mirror since I'd have been using some structs per "actor" with other data too. This also dissociates them from the sprite table order, allowing you to implement some reordering to counter sprite overflow.,
  View user's profile Send private message Visit poster's website
  • Joined: 17 Sep 2013
  • Posts: 128
  • Location: Gravataí, RS, Brazil
Reply with quote
Post Posted: Tue Dec 22, 2015 5:39 pm
Now that I posted this, I came across with a better, but a bit uglier solution. Putting the Y's array in a 65 to 128 position and filling the sprote table bacwards:


Ld hl, spriteTable ;$xx80
ld b, 64
-:
  ;a:calculate Y 
  ld (hl), a
  sla l
  ;de:calculate x,pattern
  ld (hl), e
  dec l 
  ld (hl), d
  Sra l
Djnz -



This way, there is no need for the an extra dec l, with the disadvantage of the last sprite patten been at a $xx00 address, but this can be easily resolved when streeming the sprite table to the VDP.
  View user's profile Send private message
  • Joined: 23 Mar 2013
  • Posts: 611
  • Location: Copenhagen, Denmark
Reply with quote
Post Posted: Sun Dec 27, 2015 6:54 pm
Why should the SEGA header have a separate slot and bank?
  View user's profile Send private message Visit poster's website
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post Posted: Sun Dec 27, 2015 7:12 pm
hang-on wrote
Why should the SEGA header have a separate slot and bank?

Would you suggest using one $8000 bank followed by multiple $4000 banks? With that the bank starting from $8000 will be numbered as bank 1, $c000 as bank 2 and so on. So you won't be able to use the :label directive because it would always be off by 1. Theoretically this could be solved by adding a bank of size 0 in between but WLA-DX won't allow you to do that. As you won't want to overwrite the header anyways an obvious solution is to put the header into its own bank.
  View user's profile Send private message Visit poster's website
  • Joined: 23 Mar 2013
  • Posts: 611
  • Location: Copenhagen, Denmark
Reply with quote
Post Posted: Sun Dec 27, 2015 8:23 pm
Calindro wrote
Would you suggest using one $8000 bank followed by multiple $4000 banks?

You got me :) But now I see the light!
  View user's profile Send private message Visit poster's website
  • Joined: 15 Feb 2016
  • Posts: 19
Reply with quote
Post Posted: Mon Feb 15, 2016 11:46 pm
I am not sure if I fully understand the concept of slots. Is there a diagram or illustration to help me visualize the relationship with paging and banking?
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14726
  • Location: London
Reply with quote
Post Posted: Tue Feb 16, 2016 6:10 pm
Last edited by Maxim on Tue Feb 16, 2016 11:08 pm; edited 1 time in total
Each 16KB of ROM is a bank. You can put each of them* in three 16KB slots in the CPU address space. Paging is the process by which you put a bank in a slot. At assembly time, the code has to be assembled to know what slot it will be in, so WLA DX has directives for that.

You can view the RAM as another slot, but let's not complicate matters.

* Not entirely true, slot 0 is weird.
  View user's profile Send private message Visit poster's website
  • Joined: 07 Aug 2007
  • Posts: 220
  • Location: Yach, Germany
Reply with quote
Post Posted: Tue Feb 16, 2016 6:44 pm
Maxim wrote
I think it would be good to have some general purpose tools for compression outside of graphics, as that's coming up a lot recently. Level data can be uncompressed in the ROM, or decompressed to RAM. The former offers you almost unlimited level sizes, but costs ROM space. The latter allows you to optimise much better for ROM size.


I wrote http://colecovision.eu/ColecoVision/development/compression.shtml for my ColecoVision games. While I mostly use it for graphics, in some games level data is compressed that way, too.

Philipp
  View user's profile Send private message Visit poster's website
  • Joined: 15 Feb 2016
  • Posts: 19
Reply with quote
Post Posted: Tue Feb 16, 2016 8:59 pm
Maxim wrote
Each 16KB of ROM is a bank. You can put each of them* in three 16KB slots in the CPU address space. Paging is the process by which you put a bank in a slot. At assembly time, the code has to be assembled to know what slot it will be in, so WLA DX has directives for that.

You cam view the RAM as another slot, but let's not complicate matters.

* Not entirely true, slot 0 is weird.


Ah! Ok, thank you. I was thinking backwards with each bank being split up into slots. This makes much more sense.
  View user's profile Send private message
  • Joined: 15 Feb 2016
  • Posts: 19
Reply with quote
Post Posted: Sun Feb 12, 2017 9:42 pm
I'm having more difficulty with paging. Everything works fine if I put my graphical data in free sections in bank 2 slot 2, but everything gets glitchy if I use superfree or bank 3.

For reference, my curret setup has a memory map that looks like this:

.memorymap
defaultslot 0

slotsize $7ff0 ;unpaged
slot 0 $0000

slotsize $0010 ;header
slot 1 $7ff0

slotsize $4000 ;paged rom
slot 2 $8000

slotsize $2000 ;ram
slot 3 $c000
.endme

.rombankmap
bankstotal 8

banksize $7ff0
banks 1

banksize $0010
banks 1

bamksize $4000
bamk 6 ;mostly an arbitrary number; just growing room.
.endro

And my data area looks something like this:

.bank 2 slot 2
.org $8000

And then everything is currently in superfree sections.

Now, my game logic is basically just loading the appropriate tileset tilemap and palette data into pointers that the VRAM writing routines use every frame. I know this works perfectly well without banking. And then it just goes into a loop waiting for VBlank and checking the directions which will send the program to the other routine which loads different data locations and then loops and does the same thing again...

The thing is, even with everything in superfree sections and no matter how I arrange them in the editor, the palette always loads perfectly, but then tilemaps or sets won't load properly. A set will load but map is garbage or the other way around or neither will load and I can't seem to detect the pattern of errors.

To be fair, I don't have anything compressed yet and all the data is just pasted in my main file for the moment. I was waiting to make sure banking would work in the first place before adding any more potential complications.

So what is it that I'm missing here?
  View user's profile Send private message
  • Site Admin
  • Joined: 19 Oct 1999
  • Posts: 14726
  • Location: London
Reply with quote
Post Posted: Sun Feb 12, 2017 10:06 pm
I'd need the code to diagnose it. Check the symbol file for the page and address data ends up with, or step through the code to see what the data looks like when it gets there at runtime.

I seem to remember you need a .slot directive for superfree to work, could that be it?
  View user's profile Send private message Visit poster's website
  • Joined: 05 Sep 2013
  • Posts: 3796
  • Location: Stockholm, Sweden
Reply with quote
Post Posted: Mon Feb 13, 2017 9:42 am
yes, it's probably just a missing
.slot 2

before the superfree sections. I wonder if I have to fix the first post to be clearer...
  View user's profile Send private message Visit poster's website
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post Posted: Mon Feb 13, 2017 10:01 am
Phano wrote
And my data area looks something like this:

.bank 2 slot 2
.org $8000

And then everything is currently in superfree sections.

sverx wrote
yes, it's probably just a missing
.slot 2

before the superfree sections. I wonder if I have to fix the first post to be clearer...

I guess specifying bank 2 and slot 2 once before the superfree sections should be enough. :P

@Phano: Did you insert the necessary paging instructions?
The assembler won't insert paging instructions for automatically. You have to manually do it. So for example if you want to use the data labeled with myTiles you need to do something like:
ld a, :myTiles
ld ($ffff), a
ld hl, myTiles
call loadTiles

The colon (:) in front of the label tells the assembler to put the bank number of the label there.

Another idea: If I remember correctly
.org $8000
means $8000 bytes relative to the current slot. But then it should complain that $8000 exceeds the slot size. So instead it should either be
.orga $8000
or
.org 0
. But you said without superfree sections it worked correctly so I guess this isn't really the issue.
  View user's profile Send private message Visit poster's website
  • Joined: 15 Feb 2016
  • Posts: 19
Reply with quote
Post Posted: Mon Feb 13, 2017 6:36 pm
Aha! As a matter of fact I was just playing around with loading :LABEL into ($FFFF). I wasn't getting it to work for every situation but I suspect there is just a syntax/logical error somewhere I need to work out.

Also, I've since studied the listing file to see where my data was going and if I read it correctly, it was indeed everything that was getting put into bank 3. I suspect bank 2 works because it is still part of the z80 address space, correct? It's switch into the slot by default and I just haven't been switching the other banks in. I'm not sure why I assumed the assembler would take care of this!
  View user's profile Send private message
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post Posted: Mon Feb 13, 2017 6:55 pm
Phano wrote
Aha! As a matter of fact I was just playing around with loading :LABEL into ($FFFF). I wasn't getting it to work for every situation but I suspect there is just a syntax/logical error somewhere I need to work out.

The paging register must be set correctly for every access to slot 2. Otherwise, at some point it might be set to bank 3 while you want to access data in bank 2 or other way round.

Phano wrote
I suspect bank 2 works because it is still part of the z80 address space, correct?

No. It only works because at the point your program starts the mapper already maps bank 2 into slot 2. If all 3 slots mapped to bank 0 you wouldn't even be able to access the data starting from address $4000 in your ROM. It has nothing to do with z80 address space.
  View user's profile Send private message Visit poster's website
  • Joined: 15 Feb 2016
  • Posts: 19
Reply with quote
Post Posted: Mon Feb 13, 2017 10:12 pm
Calindro wrote

No. It only works because at the point your program starts the mapper already maps bank 2 into slot 2. If all 3 slots mapped to bank 0 you wouldn't even be able to access the data starting from address $4000 in your ROM. It has nothing to do with z80 address space.


I'm going to say that's what I meant.

In any case, is it at all possible to load the bank number into a variable to use later, or pull one of these (:Label)? I can't seem to get either to work but I assume I'm missing or don't understand something before anything else.

I'll take a break and come back to it later and stop botherig everyone now!
  View user's profile Send private message
  • Joined: 14 Apr 2013
  • Posts: 624
Reply with quote
Post Posted: Mon Feb 13, 2017 10:31 pm
Phano wrote
Calindro wrote

No. It only works because at the point your program starts the mapper already maps bank 2 into slot 2. If all 3 slots mapped to bank 0 you wouldn't even be able to access the data starting from address $4000 in your ROM. It has nothing to do with z80 address space.


I'm going to say that's what I meant.

In any case, is it at all possible to load the bank number into a variable to use later, or pull one of these (:Label)? I can't seem to get either to work but I assume I'm missing or don't understand something before anything else.

I'll take a break and come back to it later and stop botherig everyone now!

When the assembler encounters a :label it replaces it with the bank number of the label. For example if label is in bank 3 and you have
ld a, :label

the assembler will replace the :label with 3 and the instruction becomes
ld a, 3

So it's just a constant and that means yes, you can save it in a variable:
ld a, :label
ld (bankVariable), a

You can also have it in a table for example holding label bank pairs such as:
.dw label1
.db :label1
.dw label2
.db :label2
...


I hope this is clear enough. Unfortunately I don't know what you mean by
Phano wrote
or pull one of these (:Label)?
  View user's profile Send private message Visit poster's website
  • Joined: 03 Jul 2005
  • Posts: 45
  • Location: Toronto, Canada
Reply with quote
Post Posted: Mon Feb 20, 2017 4:03 pm
First of all, thank you for the effort to figure it out and then to organize and write down your thoughts in here.

Top post should really be expanded, polished and posted along with Maxim's tutorials
  View user's profile Send private message Visit poster's website
Reply to topic



Back to the top of this page

Back to SMS Power!