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?

P#40476 2017-05-11 18:43 ( Edited 2017-05-16 22:16)

:: Felice

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 ```
P#40591 2017-05-14 22:05 ( Edited 2017-05-15 04:08)

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 ```
P#40607 2017-05-15 05:16 ( Edited 2017-05-15 09:16)
:: Offler

"..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."

I must have missed something. What are you referring to?

P#40608 2017-05-15 08:46 ( Edited 2017-05-15 12:47)

@Offler He is referring to dget and dset which gets and sets values in the Pico 8's "memory card".

Search for "Cartridge Data" here.

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).

P#40627 2017-05-15 14:33 ( Edited 2017-05-15 18:33)

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)

P#40633 2017-05-15 15:31 ( Edited 2017-05-15 19:31)

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.

P#40635 2017-05-15 17:10 ( Edited 2017-05-15 21:10)
:: Felice

@WolfWings

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.

P#40646 2017-05-16 03:45 ( Edited 2017-05-16 08:18)

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.

P#40661 2017-05-16 14:54 ( Edited 2017-05-16 18:56)
:: Felice

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.

P#40669 2017-05-16 17:09 ( Edited 2017-05-16 22:15)

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)

P#40675 2017-05-16 18:09 ( Edited 2017-05-16 22:09)
:: Felice

Yeah, ultrabrite, I totally agree. I don't like that the only shift is arithmetic, though at least you can work around it by BAND()ing the result appropriately.

That and no CEI() function, mutter.

P#40676 2017-05-16 18:16 ( Edited 2017-05-16 22:37)