There's a lot of situations where being able to save a full 16:16-precision value out or load it back in later is helpful, and the 'hardware' already has the routine in the form of the dget/dset functions that just happen to be restricted to the 0x5e?? range. Maybe dpeek/dpoke?
I think that would kinda run against the theme of being an 8-bit fantasy machine. At the very least, if they existed, they'd need to respect the number of cycles the machine would need to do the work. No free 4x poking.
Edit: Judging by your other posts, you already know quite well how to do what I say below. Left for others' possible use though.
You could just have your own dpoke()/dpeek() routines:
function dpoke(a,v) poke(a, shl(v,16)) poke(a+1,shl(v,8)) poke(a+2,v) poke(a+3,shr(v,8)) end function dpeek(a) return shr(peek(a),16) + shr(peek(a+1),8) + peek(a+2) + shl(peek(a+3),8) end
Not asking for a free speedup, but the platform already has a 32-bit peek and poke currently, just one that's restricted to a 4-byte alignment and only 64 positions it can be used on currently.
Whatever the speed of the dset/dget are, let an equivalent be used elsewhere in ram, was just suggesting using the dpeek and dpoke functions.
And amusingly I found the opposite ordering easier to handle myself when I wrote my functions. XD And for the peek and poke both you can save quite a few tokens at no cost in cycles by skipping the shl/shr whenever possible:
function dpeek(a) local v = peek(a) * 0x100 v += peek(a + 1) v += peek(a + 2) * 0x0.01 v += peek(a + 3) * 0x0.0001 return v end function dpoke(a, v) poke(a, v * 0x0.01) poke(a + 1, v) poke(a + 2, v * 256) poke(a + 3, shl(v, 16)) end
@Offler He is referring to dget and dset which gets and sets values in the Pico 8's "memory card".
dset/dget stores/reads a 32 bit fixed point number (4 bytes), which is what he meant. Comparatively, poke/peek sets/gets a 8 bits (1 byte).
also I remember reading something like
function dpeek(adr) memcpy(0x5e00,adr,4) return dget(0) end -- (and dset memcpy for dpoke)
which could be interesting if you're really short on tokens. not sure about speed on heavy use since dset/poke/memset will most likely access a file, which may or may not be cached/noticeable depending on your os? (I haven't tried this)
Yeah, the memcpy + dget/dset is one solution I've considered using, but as it triggers disk I/O I felt it was perhaps better to simply file a request-for-enhancement for a proper dpeek/dpoke to read full-width 16:16 variables instead of only 8-bit bytes.
I actually did have my own that used multiplies and different ordering. I was keeping it simple and straightforward for the reader. I don't like to encourage people to use divides and multiplies when they mean to do shifts, because on real platforms that can cause problems with signed values, which in turn causes compilers to spit out annoyingly complex code for things like divides, even when the divisor is a constant power of 2, which you'd expect to become a shift:
extern int x; int y = x / 2; // People expect this to be compiled to: y = x >> 1; // However, with *signed* ints, things don't work that way: // -1 / 2 == 0, but -1 >> 1 == -1 // -3 / 2 == -1, but -3 >> 1 == -2 // So the compiled code either has to use an actual divide or it has to be conditional: if (x < 0) y = (x + 1) >> 1; else y = x >> 1; // And, obviously, if you're expecting a pure shift, you're NOT going to get the result you expect.
It's just a bad habit I don't like to encourage. But yeah, on PICO-8, you can do stuff like that reliably because it's an interpreted language and only charges you one cycle regardless of the internal trickery the math token produces. Also, you can do the safer multiply-by-reciprocal because the values are fixed point rather than integer, and you'll get the correct value out the back end.
Yeah, but here's the thing.
This is a forum specifically for PICO-8, and the coding complications that arise here.
This is not StackOverflow where you go for general programming questions.
And compilers never did the code you showed either if they're trying to do signed right-shifts that maintain the sign bit; CPU's have had a shift-arithmetic-right pretty much since the dawn of x86. Seriously, it was added between the 8080 and 8086, and is where the x86 'platform' gets it's name from back in the 70's.
If someone is programming for one of the niche, unique platforms where the average rules of x86 don't apply? That's different.
But teach the common reality first if teaching programming.
And teach the platform-specific reality first and foremost, if on a platform-specific forum.
You've misunderstood the problem. Or, perhaps more likely, I've explained it poorly. :P
This isn't an attempt to maintain the top bit on negative numbers. This is a universal truth about 2's-complement math.
Let me illustrate with some signed bytes:
-1 == 11111111
-2 == 11111110
-3 == 11111101
-3>>1 == 11111110 == -2
-3/2 == 11111111 == -1
This is true on all processors. It's just how 2's-complement math works. See here for a demonstration.
So you can't assume that x>>1 == x/2 with signed integers. Compilers don't; they work around it by checking for negative numerators and doing slightly different shift math, as I showed in my previous post. Or they just straight up do a divide, if divides aren't stupid expensive on the target CPU.
(Multiplying is safe, but encouraging people to shift when they mean to shift means they don't forget that it's multiply that's okay and divide that isn't. It's just a bad habit to develop. You should really always write the code you intend to run on the processor, not something you merely hope will turn into it.)
If you have a real-world compiler you like, try compiling some code that has a signed integer getting divided by 2, and look at the asm it outputs. It won't be a simple shift.
Also, a lot of people who are playing with PICO-8 are doing so as novices, and it is entirely reasonable to encourage practices that are more globally safe or efficient, because they aren't going to spend their lives writing PICO-8 programs. At some point, they'll move on to real platforms where this stuff matters.
also true the other way around, don't shift when you mean to divide.
in that regard I wish shr() was logical instead of arithmetic.
bit shifting should be for... shifting bits.
(moreover it's a misnomer, "shr" is known as logical in x86, "sar" is arithmetic)
[Please log in to post a comment]