Skip to content

Commit

Permalink
Merge pull request #293 from nub1604/develop
Browse files Browse the repository at this point in the history
feat(*) mapbuffer example + extension
  • Loading branch information
alekmaul authored Sep 24, 2024
2 parents 1b2c576 + 9aed4af commit 825914f
Show file tree
Hide file tree
Showing 13 changed files with 2,606 additions and 0 deletions.
39 changes: 39 additions & 0 deletions snes-examples/maps/mapbuffer/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
ifeq ($(strip $(PVSNESLIB_HOME)),)
$(error "Please create an environment variable PVSNESLIB_HOME by following this guide: https://github.com/alekmaul/pvsneslib/wiki/Installation")
endif

include ${PVSNESLIB_HOME}/devkitsnes/snes_rules

.PHONY: bitmaps all

MAPDIR := ./maps/$(MAPDIR)
GFXDIR := ./gfx/$(GFXDIR)

CFLAGS += -I$(CURDIR)/maps $\
-I$(CURDIR)/src/gameobjects $\
-I$(CURDIR)/src/addons $\

#---------------------------------------------------------------------------------
# ROMNAME is used in snes_rules file
export ROMNAME := mapbuffer

all: bitmaps $(ROMNAME).sfc

clean: cleanBuildRes cleanRom cleanGfx
@rm -f $(GFXDIR)*.map $(GFXDIR)*.pal $(GFXDIR)*.pic
@rm -f $(MAPDIR)*.m16 $(MAPDIR)*.t16 $(MAPDIR)*.o16 $(MAPDIR)*.b16 $(MAPDIR)*.r16

#---------------------------------------------------------------------------------
tilesMario.pic: $(GFXDIR)tilesMario.png
@echo convert font with no tile reduction ... $(notdir $@)
$(GFXCONV) -s 8 -o 48 -u 16 -p -m -i $<

BG1.m16: $(MAPDIR)tiledMario.tmj tilesMario.pic
@echo convert map tiled ... $(notdir $@)
$(TMXCONV) $< $(MAPDIR)tilesMario.map

mario.pic: $(GFXDIR)mario.png
@echo convert sprite bitmap ... $(notdir $@)
$(GFXCONV) -s 16 -o 16 -u 16 -p -i $<

bitmaps : mario.pic tilesMario.pic BG1.m16
37 changes: 37 additions & 0 deletions snes-examples/maps/mapbuffer/data.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.include "hdr.asm"

.section ".rodata1" superfree

tileset:
.incbin "gfx/tilesMario.pic"
tilesetend:

tilesetpal:
.incbin "gfx/tilesMario.pal"


mapmario:
.incbin "maps/BG1.m16"
mapmario_end:

tilesetatt:
.incbin "maps/tiledMario.b16"

tilesetdef:
.incbin "maps/tiledMario.t16"

objmario:
.incbin "maps/tiledMario.o16"

.ends

.section ".rodata2" superfree

gfxsprite:
.incbin "gfx/mario.pic"
gfxsprite_end:

palsprite:
.incbin "gfx/mario.pal"

.ends
Binary file added snes-examples/maps/mapbuffer/gfx/mario.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added snes-examples/maps/mapbuffer/gfx/tilesMario.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 46 additions & 0 deletions snes-examples/maps/mapbuffer/hdr.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
;==LoRom== ; We'll get to HiRom some other time.

.MEMORYMAP ; Begin describing the system architecture.
SLOTSIZE $8000 ; The slot is $8000 bytes in size. More details on slots later.
DEFAULTSLOT 0 ; There's only 1 slot in SNES, there are more in other consoles.
SLOT 0 $8000 ; Defines Slot 0's starting address.
SLOT 1 $0 $2000
SLOT 2 $2000 $E000
SLOT 3 $0 $10000
.ENDME ; End MemoryMap definition

.ROMBANKSIZE $8000 ; Every ROM bank is 32 KBytes in size
.ROMBANKS 8 ; 2 Mbits - Tell WLA we want to use 8 ROM Banks

.SNESHEADER
ID "SNES" ; 1-4 letter string, just leave it as "SNES"

NAME "LIBSNES TIMER SAMPLE " ; Program Title - can't be over 21 bytes,
; "123456789012345678901" ; use spaces for unused bytes of the name.

SLOWROM
LOROM

CARTRIDGETYPE $00 ; $00 = ROM only $02 = ROM+SRAM, see WLA documentation for others
ROMSIZE $08 ; $08 = 2 Mbits, see WLA doc for more..
SRAMSIZE $00 ; $00 = No Sram, $01 = 16 kbits, see WLA doc for more..
COUNTRY $01 ; $01 = U.S. $00 = Japan, that's all I know
LICENSEECODE $00 ; Just use $00
VERSION $00 ; $00 = 1.00, $01 = 1.01, etc.
.ENDSNES

.SNESNATIVEVECTOR ; Define Native Mode interrupt vector table
COP EmptyHandler
BRK EmptyHandler
ABORT EmptyHandler
NMI VBlank
IRQ EmptyHandler
.ENDNATIVEVECTOR

.SNESEMUVECTOR ; Define Emulation Mode interrupt vector table
COP EmptyHandler
ABORT EmptyHandler
NMI EmptyHandler
RESET tcc__start ; where execution starts
IRQBRK EmptyHandler
.ENDEMUVECTOR
71 changes: 71 additions & 0 deletions snes-examples/maps/mapbuffer/mapbuffer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------
Simple map example with object scrolling
-- alekmaul
---------------------------------------------------------------------------------*/
#include <snes.h>

#include "mario.h"
#include "mapbufferextension.h"

extern char objmario;
extern char tileset, tilesetend, tilesetpal; // for map & tileset of map

extern char mapmario, mapmario_end, tilesetdef, tilesetatt;

extern u16 mapwidth;

//---------------------------------------------------------------------------------
unsigned short pad0;

//---------------------------------------------------------------------------------
int main(void)
{

bgInitTileSet(0, &tileset, &tilesetpal, 0, (&tilesetend - &tileset), 16 * 2, BG_16COLORS, 0x2000);
bgSetMapPtr(0, 0x6800, SC_64x32);

// Now Put in 16 color mode and disable Bgs except current
setMode(BG_MODE1, 0);
bgSetDisable(1);
bgSetDisable(2);

// Init Sprites gfx and palette with default size of 16x16
oamInitDynamicSprite(0x0000, 0x1000, 0, 0, OBJ_SIZE8_L16);
// Object engine activate
objInitEngine();
// Init function for state machine
objInitFunctions(0, &marioinit, &marioupdate, NULL);

// Load all objects into memory
objLoadObjects((char *)&objmario);

// load map into wram
mapbuffersLoad((u8 *)&mapmario, (&mapmario_end - &mapmario));
// map engine gets the buffer instead
mapLoad((u8 *)&mapbuffer, (u8 *)&tilesetdef, (u8 *)&tilesetatt);
//
dynamicTileBuffer_Init();

setScreenOn();
WaitForVBlank();
while (1)
{
// Update all the available objects
objUpdateAll();

// prepare next frame and wait vblank
mapUpdate();
DynamicTileAutoUpdate();
maptileQueueUpdate();
oamInitDynamicSpriteEndFrame();
WaitForVBlank();
mapVblank();
maptileVRAMUpdate();
oamVramQueueUpdate();
}
return 0;
}
106 changes: 106 additions & 0 deletions snes-examples/maps/mapbuffer/mapbufferextension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
## MapBuffer Extension

### Desciption:

- this addon extends the ability of PVSneslibs mapengine to change tiles during runtime.


### Installation:

- copy the following files to your source folder
> "mapbufferextension.h","mapbufferextension.c", "mapbufferextensionA.asm"
- include with mapbufferextension.h to your c files, where you need the extension
> #include "mapbufferrextension.h"
### Load maps into mapbuffer[]

- first load the map into the buffer with the mapbuffersLoad
- than call mapLoad as usual but now we point to the mapbuffer
```c
#include "mapbufferextension.h"
...
//load map into wram
mapbuffersLoad((u8 *)&mapmario, (&mapmario_end- &mapmario));
//map engine gets the buffer instead
mapLoad((u8 *)&mapbuffer, (u8 *)&tilesetdef, (u8 *)&tilesetatt);
```
congrats the easy part is done, now you can manipulate the map during runtime.
when you manipulate the maptiles in ram it doesn't mean the vram ram will be updated.
there are two ways to solve this problem
### Update vram 1 slow
one way to update the onscreen tiles is to set "mapdirty" to 1
```c
mapbuffer[123] = 0x0001;
mapdirty = 1;
```
- this updates the entire screen to the vram
- if frames doesn't matter, this the way to go

### Update vram 2 fast

the second approach uses additional buffers, the sequence is the following:

1. select and check a tile
2. prebuffer the tile
3. manipulate the tile
4. push tiles that are currently on screen to a queue before v-blank
5. update vram from queue during v-blank

##### Detailed steps:

1. the first function __mapGetMetaTilesInfo__ is similar to __mapGetMetaTilesProp__ but it prestores some variables which are preparation for __dynamictilebuffer__ -> __dtb__.

2. the next function __GetDynamicTileID__ returns will setup or update the __dtb__ and it returns the index which can be used by on of the next function.

3. Manipulating variants

1. the __mapChangeTileByID__ takes the index of the __dtb__, the new tile number and the tile attributes (tile attributes not fully integrated)

2. __ManipulateDynamicTile__ extends the __mapChangeTileByID__, this is an example
of event driven tile manipulation (hit by head, stand on it.) feel free to extend it or write you own logic

3. __DynamicTileAutoUpdate__ extends the __mapChangeTileByID__, this is an example how to manipulate a tile over time, feel free to extend it or write you own logic call it once before __maptileQueueUpdate__

4. __maptileQueueUpdate__ pushes tiles from __dtb__ into a __dynamictilequeue__ (__dtq__) when there on screen, call it once before WaitForVBlank


5. the __maptileVRAMUpdate__ function update all queued tiles in the __dtq__ into vram __queuetilebuffer__ and flush the queue, call it once after WaitForVBlank because vram access is only allowed during v-blank

### Important notes and limitations



- **!!!** only map widths based on __power of two__ are allowed
> tested map widths: 32, 62, 128
- **!!!** only testet with lowrom, the bank for wram is set to__$7F__ by default.
```assembly
;mapbufferextensionA.asm:
.DEFINE DTBUFFERBANK $7F
```
- manipulated offscreen tiles will be updated by mapUpdateCamera when the scree is scrolling

- the map size in byte must not exceed the size of the mapbuffer
> default 16.387 bytes (4003 hex), 3 bytes for the header, it is defined in asm file
```assembly
;mapbufferextensionA.asm:
.DEFINE MAPBUFFER_SIZE $4003
```
> the calculation of the map in bytes: (3+ 2*(mapheightblock * block width))
- the size of __dtb__ and the __dtq__ is set to 32 by default
- to change the size of __dynamictilebuffer__ has to be set in the header and the asm file as well


```assembly
;mapbufferextensionA.asm:
.DEFINE DTBUFFER_MAX 32
.DEFINE DTQUEUE_MAX 32
```

```c
//mapbufferextension.h:
#define DTBUFFER_MAX 32 // same value like in the asm file!
```
- to change the size of __dynamictilequeue__ has to be set asm file
Loading

0 comments on commit 825914f

Please sign in to comment.