SNES Splatoon (How do I shot HiROM?)

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
Post Reply
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Drew Sebastino »

bazz wrote:just do the subtraction on the lower 16 bits and leave the bank byte unmodified.
I know how you'd do this in software, but how would you write it in the assembler (ca65)? There's no sense in doing something at runtime that doesn't have to be.
bazz wrote:most programs use % for modulus, but you don't really need to actually use it.
You're saying I could just write "%" before the value of the address? I thought this was for telling the assembler that the value is to be interpreted as binary. I'm not sure what "modulus" are.
bazz wrote:I'm all for sharing neat tips and tricks but I feel like Espozo is in over his head.
It's not like anyone is obligated to try and help me.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by tokumaru »

Espozo wrote:You're saying I could just write "%" before the value of the address?
No. He meant that in many programming languages, "%" is the symbol for the modulo operation, just like "+" is for addition and "*" is for multiplication.
I'm not sure what "modulus" are.
The modulo operation finds the remainder of a division. In binary, much like division and multiplications by powers of 2 can be quickly done through shifting, you can find the module with an AND operation. For example, to divide a number by 8, you shift it right 3 times, and to find the modulo you AND it with %00000111, and you'll get a remainder ranging from 0 to 7.

When tepples said you had to do the minus 1 part modulo 65536, he meant that you should keep the math restricted to the lower 16-bits of the address, preventing any bits above that from being affected.

As for how to do it in ca65... maybe (address & $ff0000) | ((address - 1) & $ffff)? Unless ca65 has some feature I'm not aware of specifically implemented to handle this case.
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by bazz »

I used ca65 for SNES years ago and I recall being able to separate symbols into their constituent pieces. RTFM

.BANKBYTE
.LOWORD

ie lda #.LOWORD(foo) - 1
16-bit accum implied..

The next time you dont know how to do something, I highly recommend taking a look at the manual and learning at least one new thing while trying to find your target matter. better yet, gloss over the entire manual for a few days with the direct purpose of gaining a high understanding of how everything works. that's what I did when I first learned.

you still don't need to do any actual modulus operations, but that's EXPECTED to be obvious to you. if it is not, then you don't really understand what a modulus is -- it is a cycling constrainer .. think of a byte - you know it can only go from 0-255.. if you try to go 256 it just becomes zero. that phenomenon is mathematically identical to a % 256 operation.

ie 256 % 256 = 0
257 % 256 = 1
but it is a little different since we can have great input values
513 % 256 = 1

the value to the right of the % is defining the range of the modulus. subtract 1 and you get the eligible values, which start from 0. basically, the input value is divided by the modulus value, and you are given the remainder.

ie 512 % 256
512 / 256 = 2 r 0
therefore, 512 % 256 = 0

54 % 10
54 / 10 = 5 r 4
therefore, 54 % 10 = 4


Anyway, we don't need to manually use any modulus symbols since we separated our arithmetic to the lowest 16 bits with .LOWORD, making a 16-bit modulus automatically. just load the bank byte yourself and you'll be on your merry way -

any self-respecting programmer will also ensure that their assembler outputs #$FFFF when performing the-operation at-the-top-of-this-post -- on a symbol whose bottom 16-bits is 0000. That is just to fill in the hole between desired behavior and actual behavior -- this is important to do when you try to use newly acquired knowledge -- in my experience I have developed a sense of how many assumptions I am making as I go along -- and to verify those assumptions at the most ideal times (altogether now!)
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by bazz »

also tokumaru, your reaction to the SNES memory map, although valid, was based on a terse textual description.. I am overwhelmed at the idea of impressing upon you how simple it seems to me, but I believe this page is the source of my happiness relating to understanding the SNES memory map - relating to how it maps the cartridge particularly. Bare in mind that there is a certain harmony of knowledge in software and hardware that facilitates a simple understanding of the matter. That, I earned over years and years of patience and a hardcore deliberation to master an understanding of what is required to make a game on the SNES. it was a childhood dream, for me. and it has led into many other relevant areas.

I would be pain-staken to try to deliver that amount of knowledge to you over a brief moment, but I can only say that I looked up the Sega megadrive memory map, and the fact it alots a completely linear chunk of space to the cartridge looks very comfortable. Of course, I do not have as vast an understanding of architecture design to be able to consider potential pros/cons of a linear model and non-linear model.. I could only see that the architecture was undoubtedly designed around the CPU features, undoubtedly on the SNES.. Whether the SNES CPU was well designed, I cannot say.. I am not sure exactly why certain hardware is mapped where - except probably to accommodate the Direct Page of the CPU...

I had to stop learning 65816 in favor of z80 when I was young so that I could digest easier. z80 on ticalc and Gameboy helped act as a stepping stone to SNES understanding. I would not be surprised if anyone else besides yourself needs a stepping stone. I seem to have not appealed to your interest in SNES. I have no more energy to properly tie the conclusion to this post. good night.

I'm getting in way over my head so I'm just going to sit back now...
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Sik »

That table is still a horrible mess.

The biggest problem seems to be $00~$3F and $80~$BF (and when you think about it, that looks a lot like the NES, with RAM in the bottom 8KB range and cartridge in the top 32KB range). Running off the other banks seems much easier, but I wonder how that affects port accesses.
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by bazz »

"problem" is a funny thing to call it -- I mean I believe it's setup this way to simplify the programmer's job -- word access to RAM and system registers in the execution bank - saves opcodes and thought.

If anything I figure the banks that the cart has complete access to, if even used (Super Mario World only uses banks 00-0F), would probably just store static assets to the game, whether that is LUT, gfx, sfx/music, you get the idea.

Code can go in there, it's just non-ideal since it requires extra manipulation of Direct Page (DP) and Data Bank Register (DBR) to access the world outside of that bank. Therefore, simple library routines that make little to no use of external RAM/PPU/CPU registers are best.

I definitely want to see myself using the DP more ingeniously... I nearly forgot its role
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Drew Sebastino »

I'm an idiot, but RAM really shouldn't change position when using HiROM, should it? I'm just perplexed because RAM for me is full of #$55, even when I made the first instruction be "stp", incase my code no longer worked correctly.

This is what I did, and it looks good to me. (it's for identifying objects and jumping to their code.) Of course, like I said, it appears to crash before it even gets here.

Setting up the object:

Code: Select all

  sep #$20		;A=8
  lda #.BANKBYTE(object1_code)
  sta ObjectTableSlot::Identity
  rep #$30		;A=16, X/Y=16
  lda #.LOWORD(object1_code-1)
  sta ObjectTableSlot::Identity+1
Jumping to the object's code:

Code: Select all

  sep #$20	;A=8
  lda ObjectTableSlot::Identity
  pha
  rep #$30		;A=16, X/Y=16
  lda ObjectTableSlot::Identity+1
  pha
  rts
  rep #$30		;A=16, X/Y=16
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by tepples »

Espozo wrote:I'm an idiot, but RAM really shouldn't change position when using HiROM, should it?
Save RAM moves, but internal work RAM ($7E0000-$7FFFFF) doesn't, nor does shadow RAM.
I'm just perplexed because RAM for me is full of #$55, even when I made the first instruction be "stp", incase my code no longer worked correctly.
Do you know the value of the B register at any given moment? A general strategy of B=K is more viable in LoROM than in HiROM. And you can prove out "appears to crash before it even gets here" by using debug breakpoints and the emulator's memory viewer/hex editor.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Drew Sebastino »

tepples wrote:Do you know the value of the B register at any given moment
I imagine the initialization routine sets it to 0, but how do you interact with the B register again? is it "tab", kind of like "tcd" with the d register? (there's only 24 bits of addressable space, so B would only be 8 bits)
tepples wrote:A general strategy of B=K is more viable in LoROM than in HiROM.
What's "K"?
tepples wrote:you can prove out "appears to crash before it even gets here" by using debug breakpoints and the emulator's memory viewer/hex editor.
Do you know how to place a breakpoint in the bsnes debugger? I don't know what all these fields mean. The options other than "Exec" are Read, and Write.
Breakpoint Editor.png
Breakpoint Editor.png (4.22 KiB) Viewed 2433 times
Really though, I don't know how it's doing this, and I even put an stp before and then moved it to after the initialization routine and it's till messed up. I think my configuration file must be messed up or something, considering I made it. :lol:

This is the HiROM configuration file I made using your advice. There's probably an error somewhere.

Code: Select all

# ca65 linker config for 256 KiB (2 Mbit) sfc file

# Physical areas of memory
MEMORY {
  ZEROPAGE:   start =  $000000, size =  $0100;   # $0000-00ff -- zero page
                                                 # $0100-01ff -- stack
  BSS:        start =  $000200, size =  $1e00;   # $0200-1fff -- RAM
  BSS7E:      start =  $7e2000, size =  $e000;   # SNES work RAM, $7e2000-7effff
  BSS7F:      start =  $7f0000, size =  $10000;  # SNES work RAM, $7f0000-$7ffff
  ROM0:       start =  $C00000, size =  $10000, fill = yes;
  ROM1:       start =  $C00000, size =  $10000, fill = yes;
  ROM2:       start =  $C00000, size =  $10000, fill = yes;
  ROM3:       start =  $C00000, size =  $10000, fill = yes;
  ROM4:       start =  $C00000, size =  $10000, fill = yes;
  ROM5:       start =  $C00000, size =  $10000, fill = yes;
  ROM6:       start =  $C00000, size =  $10000, fill = yes;
  ROM7:       start =  $C00000, size =  $10000, fill = yes;
}

# Logical areas code/data can be put into.
SEGMENTS {
  CODE:       load = ROM0, align =  $100;
  RODATA:     load = ROM0, align =  $100;
  SNESHEADER: load = ROM0, start =  $C0FFC0;
  CODE1:      load = ROM1, align =  $100, optional = yes;
  RODATA1:    load = ROM1, align =  $100, optional = yes;
  CODE2:      load = ROM2, align =  $100, optional = yes;
  RODATA2:    load = ROM2, align =  $100, optional = yes;
  CODE3:      load = ROM3, align =  $100, optional = yes;
  RODATA3:    load = ROM3, align =  $100, optional = yes;
  CODE4:      load = ROM4, align =  $100, optional = yes;
  RODATA4:    load = ROM4, align =  $100, optional = yes;
  CODE5:      load = ROM5, align =  $100, optional = yes;
  RODATA5:    load = ROM5, align =  $100, optional = yes;
  CODE6:      load = ROM6, align =  $100, optional = yes;
  RODATA6:    load = ROM6, align =  $100, optional = yes;
  CODE7:      load = ROM7, align =  $100, optional = yes;
  RODATA7:    load = ROM7, align =  $100, optional = yes;

  ZEROPAGE:   load = ZEROPAGE, type = zp;
  BSS:        load = BSS,   type = bss, align = $100, optional = yes;
  BSS7E:      load = BSS7E, type = bss, align = $100, optional = yes;
  BSS7F:      load = BSS7F, type = bss, align = $100, optional = yes;
  }
And also, here's the header. I edited it slightly too.

Code: Select all

MAPPER_LOROM   = $20
MAPPER_HIROM   = $21
ROMSPEED_200NS = $00
ROMSPEED_120NS = $10

;ROM and backup RAM sizes are expressed as log2(size in bytes) - 10
MEMSIZE_NONE   = $00
MEMSIZE_2KB    = $01
MEMSIZE_4KB    = $02
MEMSIZE_8KB    = $03
MEMSIZE_16KB   = $04
MEMSIZE_32KB   = $05
MEMSIZE_64KB   = $06
MEMSIZE_128KB  = $07
MEMSIZE_256KB  = $08
MEMSIZE_512KB  = $09
MEMSIZE_1MB    = $0A
MEMSIZE_2MB    = $0B
MEMSIZE_4MB    = $0C

REGION_JAPAN   = $00
REGION_AMERICA = $01
REGION_PAL     = $02

;======================================================================
.segment "SNESHEADER"
;======================================================================

romname:
  .byte "SNES Splatoon"
  .assert * - romname <= 21, error, "ROM name too long"
  .if * - romname < 21
    .res romname + 21 - *, $20  ; space padding
  .endif
  .byte MAPPER_HIROM|ROMSPEED_120NS
  .byte $00              ; 00: no extra RAM; 02: RAM with battery
  .byte MEMSIZE_256KB    ; ROM size (08-0C typical)
  .byte MEMSIZE_NONE     ; backup RAM size (01,03,05 typical; Dezaemon has 07)
  .byte REGION_AMERICA   ; region code
  .byte $33              ; publisher id, or $33 for "see 16 bytes before header"
  .byte $00              ; ROM revision number
  .word $0000            ; Checksum of all bytes will be poked here after linking
  .word $0000            ; $FFFF minus above sum will also be poked here
  .res 4                 ; Unused vector space
  ;Native 65816 vectors
  ;NOTE: Reset vector set to $FFFF because it doesn't serve a purpose here;
  ;the 65816 starts up in emulation mode (even on soft reset).
  .addr EmptyHandler     ; 65816 COP vector
  .addr EmptyHandler     ; 65816 BRK vector
  .addr EmptyHandler     ; 65816 ABORT vector
  .addr VBlank           ; 65816 NMI vector
  .addr $0000            ; 65816 RESET vector -- see above
  .addr EmptyHandler     ; 65816 IRQ vector
  .res 4                 ; Unused vector space
  ;6502/65c02 vectors
  ;NOTE: BRK vector set to $FFFF because 6502/65c02 doesn't have an actual
  ;BRK vector, it only has an IRQ vector.
  .addr EmptyHandler     ; 6502/65c02 COP vector
  .addr $0000            ; 6502/65c02 BRK vector -- see above
  .addr EmptyHandler     ; 6502/65c02 ABORT vector
  .addr EmptyHandler     ; 6502/65c02 NMI vector
  .addr Main             ; 6502/65c02 RESET vector
  .addr EmptyHandler     ; 6502/65c02 IRQ vector

;These are vectors which are essentially unused on the SNES, i.e.
;defined to meet ca65 design requirements.
;
;The only ones which could be used are cop_handler, brk_handler,
;abort_handler, and irq_handler, but at present those do not serve
;a purpose for this game.

;======================================================================
.segment "CODE"
;======================================================================

.proc EmptyHandler
  rti
.endproc

.proc EmptyVBlank
  rep #30
  pha
  php

  sep #$20
  lda $4210		;clear NMI Flag

  plp
  pla
  rti
.endproc
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by tepples »

The way to set B is PLB.

K is the bank bits (23-16) of the program counter, as pushed with PHK.
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Drew Sebastino »

Wait, I thought B was for selecting the bank (the top 8 bits)... :oops: What does it do if K does that?
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by lidnariq »

B (DBR) is for data fetches, and is used whenever something has a 16-bit address.
K (PBR) is for code fetches.
Sik
Posts: 1589
Joined: Thu Aug 12, 2010 3:43 am

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Sik »

In other words: K is used for the program, B is for everything else (right?) Makes sense, since in many cases you run the code from a different bank than the data it's using.
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by lidnariq »

(Stack and direct page are always from bank=0)
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: SNES Splatoon (Not Even A Demo Yet Though...)

Post by Drew Sebastino »

Wouldn't you not really need to touch K? I mean, whenever you go further down the program and the program counter gets incremented automatically, it should also automatically increment K, shouldn't it? Even in the case of the rts that serves as a way to jump to the object code, it shouldn't even be messed with there. Where does the SNES start running code at anyway, as in what's the value in the program counter (including K)? In fact, just out of curiosity, what is the register for the lower 16 bits of the program counter?
Post Reply