Log In  


Hello.

I am working on some code to use the spritesheet as screen memory as detailed in the Pico-8 help:

0X5F54 GFX:    can be 0x00 (default) or 0x60 (use the screen memory as the spritesheet)
0X5F55 SCREEN: can be 0x60 (default) or 0x00 (use the spritesheet as screen memory)
0X5F56 MAP:    can be 0x20 (default) or 0x10..0x2f, or 0x80 and above.
0X5F57 MAP SIZE: map width. 0 means 256. Defaults to 128.

Unfortunately map() does not work in ANY capacity to draw directly into the spritesheet.
@zep, Please have this fixed for next release !

Here is the code:

-- show map() does not work
-- for screen/spritesheet swap

for i=0,31 do
  for j=0,31 do
    mset(j,i,rnd(3)+1)
  end
end

cls()

-- take your pick, none work!
poke(0x5f54,0x00,0x00)
--poke(0x5f54,0x00,0x60)
--poke(0x5f54,0x60,0x00)
--poke(0x5f54,0x60,0x60)
cls(1)
map()

-- copy spritesheet to screen.

memcpy(0x6000,0,0x2000)

Here is the cart, I am using 3-sprites in it:

[24x8]

Cart #kefawadoze-0 | 2022-11-10 | Code ▽ | Embed ▽ | No License
2

... or if any of you guys can get it to work the way it is intended, I'll retract the bug ticket with my thanks to you.

2


1

@zep

Okay, so dw's code is actually too broken to demonstrate the problem, but he's right that there's a problem.

When PICO-8 remaps screen memory to 0x0000, it doesn't just redirect draw calls, it also redirects all memory access. Every @, peek(), poke(), and memcpy() in 0x6000-0x7fff range gets redirected to the 0x0000-0x1fff range.

Is this as-intended? If so, it's not very useful, because it makes it absolutely impossible to access the real 0x6000-0x7fff while screen mem is based elsewhere. Direct memory read/write shouldn't be redirected, just draw calls.

I would imagine the same problem exists with re-basing sprite/tile memory. I think you handle map mem differently but check on that as well.


@dw817

Your code appears to have two problems that would foil it even if PICO-8 weren't bugged:

1) You're doing a cls() and then a map() after pointing the screen at sprite/tile memory. The cls() immediately clobbers all of your tile definitions, and then the map() call uses those clobbered tiles to clobber them again. Bad day for tiles all around.

2) I don't know for sure if this was intentional, but after you memcpy() the results back to 0x6000, you don't actually set the screen base back to 0x6000 to match, so it would still be showing the contents of 0x0000.

That being said, the underlying functionality doesn't work right and moving the screen base around like that won't act the way it should even if you correct those issues.

I imagine you've been trying to figure it out and ended up with something non-functional by chance. Wait for zep to fix this and you'll probably be able to come up with a solution that does whatever you intended.


1

Here's a repro case that should print 1,2 but doesn't.

Cart #bobifetipu-0 | 2022-11-13 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1


Hi @Felice:

Oh gosh, you're right. Yet I did leave =ALL= possible situations to test it 0/0, 0/6, 6/0, 6/6. Hmm ... This reminds of another post I made HERE:

https://www.lexaloffle.com/bbs/?tid=50194

I already knew of this limitation to only 0x00 and 0x60 so I was gonna use the Spritesheet as a possible screen (and not a good idea to CLS() ) on it. :)

But yep, as you still discovered the command is still confusing cause it really does just -rearrange- total memory for POKE() and stuff and it's not going to the good memory I originally want.

I guess my challenge is to try and rewrite this so it does work with the information you've given me, provided I am understanding it correctly.


Honestly, @dw817, until @zep addresses this, I think it's a waste of time to try to use the feature in conjunction with direct memory access. Even with the knowledge that it works the way it currently does, I just don't think you can do anything useful with it unless you only use draw calls.


OK, thanks, @Felice.

This may be the one time where I do the impossible that - stays impossible.


These two issues are not bugs, they are just weird. They do both stem from the same (poorly documented) premise though:

The gfx & video memory are linear mappings of a separate planar video ram, rather than being plain ram the way that every other address is. So there's only 48k of regular, directly addressable ram -- and the rest is like 0xa000 under Mode 13h.

This is the reason that gfx and non-gfx ram can't be mapped to each other (internally there are performance reasons for this too), and the reason that gfx/video ram can not be accessed when there is no memory address range mapped to it.

The main reason for the second choice is aesthetic, and I agree that it's a pain sometimes. But there is at least one benefit to it that I'm quite fond of: you can get zero-based indexing to video ram for doing peek/poke-based effects without having to add 0x6000 to every address.


OK, if I'm understanding this correctly, ZEP, that ... there are some technical reasons it's behaving the way it is - and that it can't get changed.

I'll go to close this ticket out then and - reading what you said, I'll see if I can get the effect I want by reversing my idea of output.


Side note: up until now, I haven't need to mention this, or include any mention of the planar video memory in the documentation, because it didn't make any difference at all how you would understand or interact with the machine! I probably won't commit to a explanation like this in the manual to keep it simple, but it does certainly need to be made clear that peek/poke for video/gfx is also remapped.


@dw817 yeah, remapping map <--> spritesheet is the one that has more sensible technical reasons for being how it is, and is unlikely to change. I hope you can find a good way around it!


Oh wait.. I think I misunderstood what you're trying to do. Let me test a couple of things before you go looking for an alternative solution..


ok, so you can indeed use map() to draw to the spritesheet, but it is confusing for the reason @Felice describes: the tiles being drawn to the spritesheet are being overwritten during the act of drawing them.

Here's a working program that draws the tiles at 0,8 to avoid clobbering:

cls()

for i=0,31 do
  for j=0,31 do
    mset(j,i,rnd(3)+1)
  end
end

-- draw to the spritesheet
poke(0x5f55, 0x0)

palt(0,false)
map(0,0,0,8)
--circfill(64,64,32,12)

-- back to normal
poke(0x5f55,0x60)

function _draw()
-- show to state of the
-- the spritesheet
cls()
spr(0,0,0,16,16)
end

It's actually a nice little programming puzzle! For anyone reading, try to guess what happens when map(0,0,0,8) is changed to map().


output for map(0,0,0,8) and then map()


Not sure if I'm reading this right. The pokes act like a bank swap?

You can have spritesheet and map at the same location though? Or your just switching in addressable space.

Its a shame that spritesheet isn't mappable to arbitrary memory. I could see tline used for generating sfx.


Hi @zep. I see what your program is doing.

No, what I wanted to do ultimately if possible is draw map() to a high memory location like 0x8000 or 0xA000 or 0xC000 or 0xE000. Also the regular commands, pset(), pget(), rect, etc would be affected by that new memory location to both plot and read from.

So I can change either the main screen or spritesheet to any of these high memory locations or change one or both to 0x0000 or 0x6000.


@zep

Ah okay, I think I see now. Sounds like these conceptually move dedicated on-die ram for the GPU around in the system memory map, rather than pointing the GPU at system ram.

I think typically on hardware that did it your way, you wouldn't be able to put both the 8k screen ram and the 8k sprite tile ram in the same place. Typically you'd have a range of banks where each could go and they wouldn't likely overlap, because otherwise you'd get undefined behavior—if they're both mapped to 0x0000, which one am I writing to when I poke 0x0000? I guess one would take priority by convention, but kind it's confusing for the dev.

A box like the C64 almost entirely used system ram chips, even for video hardware, and then switched in rom banks or mapped certain 256-byte pages to registers on one of the various support chips. This is what I thought you were doing and it would definitely be the most flexible option for us on the dev side.

I don't know what your architecture looks like, so I can I only ask: Would it be really difficult to change the behavior so the API simply swaps base pointers for certain segments of the API when those registers change?

Am I right in guessing that the memory handler for these two banks are entirely incompatible with the memory handler for the boring user mem at 0x4300? Maybe the internal data format is different or expanded to support their expanded feature set, something like that?

If so, how much impact on perf would it have if all memory had one common internal format that was capable of all the features but didn't necessarily perform them for some banks?

Sorry if I opened a pandora's box here, btw. I thought it was just an accident. ;)



[Please log in to post a comment]