I'm having issues with Stefan Bauwens' game, CYANOID, and I think I may not be alone in this. This happens on both my 83+BE and my 84+SE, both in wabbitemu and on real hardware.
This is what I see when I try to play it:
EDIT: And yes, it does also appear to corrupt RAM and crash on quit.
I believe aeTIos is entirely correct. To quote the wikiti page about hooks for the 83+: Hooks are a hidden feature of the TI OS that were originally included for the official TI flash apps to use.
Since flash applications did not exist on the 83, neither do hooks.
Whatever the cost of your purchase is and whatever coins you have, you can ensure you leave with the minimum amount of change possible by giving the vendor all of your change. Whatever coins properly contribute to the payment, the vendor will keep; the coins that do not will be returned to you. Since the cents amount of the purchase is an equally-distributed random number, subtracting your constant amount of change from this will result in another equally-distributed random number (mod 100). Whatever amount of change you contribute, the vendor will always have to return to you coins that sum to an equally-distributed random amount of cents from 0-99.
You cannot limit the amount of coins you end with, so the optimal solution is simply to limit the amount of coins you begin with.
Maybe add that programs compiled to Axe Fusion won't crash when axe isn't installed. E. g. let it return to homescreen when the axe app can't be found.
This is semi-supported. If Axe is not found, the program should display a message that Axe was not found and exit cleanly. However, programs compiled as Axe Fusion currently don't check that the version of Axe found actually supports Axe Fusion, which will result in a crash when the program tries to reference nonexistent command vectors in Axe.
If the program crashes and Axe 1.1.2 exists on the calculator, then you should mosey on over to the Bug Reports thread.
I can't see why that would cause an issue, MGOS. Since I can't seem to replicate the issue in a small test program, perhaps the issue is a context/size issue. If you could post either the source program or the compiled executable, perhaps someone could discover the problem through debugging. If it's for a contest, be careful not to post anything that the contest rules forbid posting.
And you should be able to use {L1}++ just fine, I see no sign that this would trigger any bugs.
This is a pretty awesome list. Already bookmarked your post.
I have two questions:
What does the *breaks mirage* note mean for cmdShadow, and are we sure there's no way to fix this?
You mention that the "collateral damage number is only correct if you follow my recommendations for cleanup." Does this mean that if I use baseAppBrTab, even with your recommended cleanup, I'll break multi-page apps on 83+es? That doesn't sound good.
Then, suggestions:
Do you want to add specific information about what Axe commands destroy areas of RAM or will be broken if the user destroys them?
MD5Buffer could be in the list, as at least parts of it are non-essential. Here's how Axe uses it in order from the end of MD5Buffer to the start (because the order of use is mostly most common up front, least common at the back):
83D9 12 bytes Axiom variables
83D1 8 bytes Sprite rotation/flipping and hex printing
83CF 2 bytes Random seed
83B1 30 bytes Archived variable pointers (files)
83A5 12 bytes Subroutine arguments
Should OP1 and possibly OP2 have slightly lower persistence ratings? A fairly decent number of Axe commands go through these, especially OP1.
I think I could supply decently small substitutes in native Axe for a few of the cleanup B_CALLs, if you wanted that. I know some people like their Axe programs to be pure Axe.
Doing a bit of disassembly of my own, I discovered how your program was corrupting RAM! And it's not a fatal Axe bug, so it's easily fixable.
When you initially create the output program, you store its size to C. You then use this as the loop variable for converting the input program to hexadecimal, but this is a problem because you convert one byte of binary data into two bytes of hexadecimal data per iteration. This results in the conversion loop converting about twice the amount of data that it should, corrupting an amount of RAM approximately equal to twice the size of the input program!
This is easy to fix though, and it even optimizes the program in the process! Just change the line that looks like this: !If GetCalc(Str0P,{P-2}r*2-2→C)→H To this: !If GetCalc(Str0P,{P-2}r-1→C*2)→H And then delete the C++ before the conversion loop and it should work as intended.
This isn't the fault of Axe, this is a hardware limitation. To make them simpler/cheaper to manufacture, many keypads/keyboards detect key presses using a grid layout, which can result in phantom key presses if certain keys are pressed together. As an interesting coincidence, this is very much related to my recent How cheap is your keyboard? topic.
Long explanation:
Spoiler For Technical explanation:
In the keypad hardware, the keys are laid out in a grid of horizontal and vertical "wires" in which a keypress connects the wires intersecting at the key's location:
Key Code
FE
FD
FB
F7
EF
DF
BF
7F
BF
GRAPH
TRACE
ZOOM
WINDOW
Y=
2nd
MODE
DEL
G
DF
STO
LN
LOG
x²
x?¹
MATH
ALPHA
r
EF
0
1
4
7
,
SIN
APPS
X,T,?,n
o
F7
.
2
5
8
)
COS
PRGM
STAT
u
FB
(-)
3
6
9
(
TAN
VARS
p
FD
ENTER
+
-
*
/
^
CLEAR
FE
down
left
right
up
To detect a key, the user first tells the keypad driver that we're looking to retrieve the state of keys in a certain key group. A read from the keypad driver then returns an 8-bit number, with 1s representing locations where the key group wire is not connected to the key code wire (key not pressed), and 0s representing locations where they are connected (key is pressed). For instance, if you pressed TRACE, that would connect the BF key group with the FD key code wires, resulting in the FD key code bit reading as 0 when the BF group is polled:
Key Code
1
0
1
1
1
1
1
1
BF
GRAPH
TRACE
ZOOM
WINDOW
Y=
2nd
MODE
DEL
G
STO
LN
LOG
x²
x?¹
MATH
ALPHA
r
0
1
4
7
,
SIN
APPS
X,T,?,n
o
.
2
5
8
)
COS
PRGM
STAT
u
(-)
3
6
9
(
TAN
VARS
p
ENTER
+
-
*
/
^
CLEAR
down
left
right
up
Now if you pressed a second key, let's say the down arrow key, this wouldn't affect reading the BF key group. But press the down arrow key and the left arrow key, and this happens:
Key Code
0
0
1
1
1
1
1
1
BF
GRAPH
TRACE
ZOOM
WINDOW
Y=
2nd
MODE
DEL
G
STO
LN
LOG
x²
x?¹
MATH
ALPHA
r
0
1
4
7
,
SIN
APPS
X,T,?,n
o
.
2
5
8
)
COS
PRGM
STAT
u
(-)
3
6
9
(
TAN
VARS
p
ENTER
+
-
*
/
^
CLEAR
down
left
right
up
I have italicized the GRAPH key because it is now being detected as a phantom key press. Before pressing the left arrow key, pressing TRACE connects the BF key group to the FD key code. Pressing the down arrow key has no effect on polling the BF key group because it connects the FE key group to the FE key code, neither of which connect to the BF key group. But pressing the left arrow key acts as a link, connecting the key code of the TRACE key to the key group of the down arrow key, resulting in the down arrow key's FE key code reading as pressed when polling the BF key group.
Is axe 1.1.1 known to be buggy with arbitrary buffers?
If you mean drawing sprites to arbitrary buffers, this should work fine in Axe 1.1.1. There is currently a bug in drawing sprites to arbitrary buffers in Axe 1.1.2, but it shouldn't have existed before this. However, you should be able to get around this bug in Axe 1.1.2 by adding 11 to the buffer argument in sprite drawing commands.
You can recycle more than sprite : benchwork menus, rnd map, .... it was a great work.
Will do
You may want to consult with jacobly before you use the random map generator. He worked pretty hard on that and I think he may actually still be working on the original TinyCraft, although very slowly.
Other than that, keep up the good work, I'd like to see one of these projects get (at least close to) finished.
I see two issues with this. First, because of how you add bullets, the first bullet is located at L1+2, whereas this loop structure treats L1 as the location of the first bullet. Secondly, this loop will run 0 times if C equals 0, but it will run C+1 times if C doesn't equal 0. For instance, if C equals 1, the loop will iterate for L equals 0 and 1, for two iterations total. This extra iteration in combination with reading data at L1 that's not a bullet is what's causing the issues you're seeing.
Thankfully, the fix for this is really easy. It even allows you to completely remove the If C block surrounding the for loop! Just change the lower bound of the for loop to 1.
.BULLET 0→C→D 15→X→Y [18247EBDFFBDFFA5]→Pic1 [183C3C3C3C240000]→Pic2 While 1 Pt-On(getKey(3)-getKey(2)+X→X,getKey(1)-getKey(4)+Y→Y,Pic1) !If D If getKey(54) X→{Y→{C++*2-2+L1}+1} 4 End +1 End -1→D C While -1→L {*2+L1→P}-- Pt-On({}ʳʳ,/256,Pic2) !If -L6 Copy(C--*2+L,P,2) End L End DispGraphClrDraw EndIf getKey(15)
If the code box is tiny, that's because Chrome fails. Click "Select All" and then press Ctrl+C to copy it, then just paste it into something like notepad to view it.
Hayleia, I believe the point thepenguin77 is trying to make is that, unless your save file mostly fills a whole 64KB sector, manually rewriting the save file back over it's original location in flash will produce more flash wear and actually be slower in the long run than archiving it normally with the occasional garbage collect.
Ok. I think I understood the main point but what exactly does "flash wear" mean ? -.-°
Every time a 64KB sector of flash ROM is erased (which you need to do to modify its contents), that sector degrades a tiny bit. After being erased enough times, it can actually begin failing to retain data, at which point any of your archived data is at risk of being corrupted.
If you used your method of rewriting the save file back over its original location, every save would erase and rewrite that single sector and the swap sector. If you saved 100 times, the sector containing the save file and the swap sector would each be cleared 100 times.
In contract, the OS's archive function rewrites the save file back to an unused location in flash and marks the old save file for deletion on the next garbage collect. When there is no room left to write the save file back to flash, a garbage collection is performed to remove the old files marked for deletion by erasing and rewriting many sectors. But if you've saved 100 times, you may have something like 10 old save files in 10 different sectors. Each of these 10 sectors would only need to be erased once, and the swap sector would need to be erased 10 times. So the flash wear is spread over many sectors and the swap sector is used less, greatly reducing the chance of a sector wearing out to the point that it starts failing.
Theoretically, a sector isn't supposed to wear out for something like 10,000-100,000 erases, numbers which nobody would probably ever reach with either of the above methods of saving. But the first method is still a bad idea in principle. For all I know, the flash sector in which somebody's save file exists could have been manufactured poorly and will start failing after only a few hundred or thousand sector erases. I would not want to be the one to hear about someone discovering this the hard way because of my program causing an unnecessarily high amount of flash wear in one sector.