This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.
Messages - Runer112
Pages: 1 ... 98 99 [100] 101 102 ... 153
1486
« on: February 15, 2011, 12:30:36 am »
shmibs, are you thinking of a different compression algorithm maybe? Because I'm pretty sure what you just described isn't Huffman. Huffman compression doesn't employ any tactic involving null-terminated strings of 1s. It relies on an algorithm that simply encodes more common values with smaller strings of bits and less common values with larger strings of bits, with 1s and 0s throughout. It does so by counting the number of occurrences of each value present in a set of data to give each value a "weight." Then, the two units with the smallest total "weight" are paired together, becoming a new unit with the combined weight. The two units with the smallest "weights" are continually paired together until all units have been joined into a tree with one root element. This will result in values with a higher weight being further up the tree and having been assigned a shorter bit string. Here's is an example of a finished Huffman tree that I shamelessly stole from Wikipedia, based on the string: j'aime aller sur le bord de l'eau les jeudis ou les jours impairsAnyways, back to ralphdspam's question. Can someone explain how to save a tree for Huffman compression? That would be helpful.
Seeing as you're asking how to save a tree, I'll go ahead assuming you already know how to make a tree like above. If not, feel free to ask how that's done, or you can read the Wikipedia article which is how I learned how Huffman coding works. There may be better ways than the method I'm about to describe, but I'm not very familiar with binary heap/tree storage so I'll just explain the simplest way I could imagine! I would code the tree starting from the top down as an array consisting of nodes and leaves. Just to clarify some terms before we continue: a node is a point on the tree that branches into two other elements, and a leaf is a terminal element that doesn't branch further. So nodes would be the circles in the above diagram, and leaves would be the ends that represent pieces of data. I would build the tree into the array by following node branches down to leaves, and after reaching each leaf, backtracking until you reach an unvisited branch and exploring that branch. And every time you visit a node or leaf that hasn't yet been reached, assign it the next element in the array. The above tree could be navigated like so by always navigating 0-branches with priority: Man that took forever to format right... Anyway. Once you have finished that process, the final table that you have will tell you the placement in which to fill in the real data in the real table! Yay, we're almost done! Now go through the list you came up with from the above process element by element, looking at what the bit string corresponds to in the Huffman tree. If the element is a node, encode that element in the array with the first bit being a 0 and the following bits representing the distance minus one between this element and the element containing the current bit string with an additional 1 at the end (I'll explain this later). If the element is a leaf, encode the data with the first bit being a 1 and the following bits being the uncompressed data represented by that bit string. The example I've been using throughout this post would generate a table like so: Data is represented as 8 bits in the form of [Type bit]:[Data], so the actual data would be 7 bits. A number for [Data] means these 7 bits should be the binary representation of the number, which will be added to the table pointer if the current table entry is of this type and the bit read is a 1. This, along with the mandated pointer+1 every iteration, will simulate a jump to the 1-branch of the node. A symbol for [Data] means the 7 bits should be the ASCII value of the symbol (disregarding the high bit, because none of our ASCII characters have this bit set anyway)Offset in table: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Data at location: | 0:19 | 0:9 | 0:7 | 0:5 | 0:3 | 0:1 | 1:b | 1:p | 1:j | 1:u | 1:space | 0:5 | 0:3 | 0:1 | 1:' | 1:m | 1:r | 0:1 | 1:a | 1:i | 0:5 | 0:3 | 0:1 | 1:o | 1:d | 1:e | 0:1 | 1:l | 1:s |
The decompressor can then decompress the data stream by repeating the following steps for each compressed value: - Set a variable indicating the length, in number of bits, of the data stream
- Set a variable to point to the start of the table
- Halt decompression if bits left to read equals zero
- Check the table entry currently pointed to
- If the first bit is a 1, pull out and handle uncompressed data from current table entry, and then jump back to the second step
- (The first bit is a 0) Read a bit from the data stream and decrease the variable containing the number of bits left to read
- If the bit read is a 1, add the value of the current table entry to the table pointer
- Increase the table pointer by 1 entry
- Jump back to the third step
And that's it! Wasn't that simple? But I'm sure that was confusing as hell, so please ask me what I mean by something if you get confused by any of this.
1487
« on: February 14, 2011, 01:40:27 am »
I was thinking about that, but for some reason I thought it was a scrolling tilemapper and that that wouldn't work. But you're right, that's smaller and faster, so I've adjusted my code for that.
1488
« on: February 14, 2011, 01:35:30 am »
By the way, loops like these: :18While -1->X :12While -1->Y Will break as soon as the variable becomes 0, so a row and column of sprites would be missing.
Also, you using Pt-Off() to draw a 5*5 tilemap could (in this case will) overwrite already drawn tiles as it draws other tiles, so it's best (and faster anyways) to use Pt-On().
Here's the code I would use. It's size-optimized, but is still also quite speed-optimized, too.
Lbl MAP ClrDraw 12*18+L₁→Z 12*256 Repeat 0 -256→Y 18 Repeat 0 -1→X Pt-On(*5,{°Y+1}ʳ*5,{Z-1→Z}*16+Pic1) End!If X End!If Y StorePic Return
EDIT: 432 cycles saved with some pretty sweet high/low byte abuse of Y.
1489
« on: February 13, 2011, 11:59:10 pm »
If you are pixel testing a constant pixel, like pxl-Test(20,20), you can more than halve the speed of this command with the following optimization: {20*12+L6+2}e4 Remember, storing or recalling two-byte values at a constant address is always smaller and faster than storing or recalling one-byte values! {20*12+L₆+2}ʳe4 Is using Rect( to draw straight horizontal/vertical lines faster than Line( ?
Definitely. Here are the results I got for vertical lines. Also note the 3 bytes saved in the Rect() arguments with some nice hl "abuses." - Line(A,0,A,63): ~6132 cycles
- Rect(A,0,+1,-2): ~3354 cycles
The results are even more profound for horizontal lines. Also, 1 byte saved in the Rect() arguments. - Line(0,A,95,A): 9809 cycles
- Rect(0,A,-1,+2): 2579 cycles
But if you really want a blazingly fast horizontal line drawer in pure Axe, use the following. It has a check to make sure that the y-value is valid, and if so, the whole process of calling the routine, checking the y-value, and drawing the line takes only 497 cycles. You would call it with something like Ysub(HL). Also, note the trick employed to check that the y-value is less than 64. That saves a few bytes and cycles over a normal comparison by utilizing the fact that we want a value precisely in the 6-bit range. By simply resetting these 6 bits and leaving any other bits unchanged, any 6-bit values will become zero and any out of range values will become nonzero, resulting in easy checking. Lbl HL →r₆ ReturnIf and b11000000 ᴇFFFF→{r₆*12+L₆}ʳ Fill(,10) Return
1490
« on: February 13, 2011, 05:33:38 pm »
Attached an updated file for version 0.5.0 to the original post.
1491
« on: February 12, 2011, 11:56:36 pm »
Yes, that would work. Although it would be more optimized as follows, because multiplying by 3 is an optimized calculation:
B>C*3+A→A
EDIT: And yes, multiplying by negative 1 negates the number as you would expect. Remember that the result is only treated as negative if you use a signed comparison or something else that uses signed numbers. Otherwise as far as the processor knows it's just a really big positive number. -6 is just 65530 interpreted differently.
1492
« on: February 06, 2011, 08:45:09 pm »
It looks like yet another error has been discovered with my attempts to optimize things. The nibble retrieval routines and the nibble storage routine that I posted treat low and high nibbles in opposite ways. I'm pretty sure that the nibble retrieval routines are backwards and that the conditional jr c jumps should be changed to jr nc.
1493
« on: February 05, 2011, 11:53:46 pm »
Epic. I love return'd value hacks
My favorite Axe optimization trick! That's amazing, Runer. Do you always come up with code like this?
I am so familiar with using optimized loops that I pretty much automatically come up with maximally optimized loops. The rest of the stuff, though, just takes a bit of thinking and comparing potential command sizes/speeds. This file I assembled a while ago actually helps a good deal when assessing options.
1494
« on: February 05, 2011, 11:46:14 pm »
Here's the fastest code I could come up with. It uses optimized loop structures and even more pre-calculation. Also, did you mean to use B as the y value in your code squidgetx? Because looping from 0-11 wouldn't make much sense for y, so I changed it. Y and ᴇF8*2+(X/2/2/2)+L₁→D ⁻(X^8)→I ⁻(Y^8)→J 12→B Lbl LB 64 Lbl LA →A If {*2+B+D} →C Pt-On(B*8+I,A+J,C*8+Pic0) End If A -8 Goto LA End If {°B-1}ʳ -256→{°B-1}ʳ Goto LB End
| | Tilemap offset; optimized version of Y/8*16+(X/8)+L₁→D X offset; Y offset For(B,12,0,⁻1)
For(A,64,0,⁻8)
If tile != 0 Store tile value Display tile
Second part of For(A) loop
Second part of For(B) loop Utilizes the fact that the second byte of A is 0 and -256 is faster than -1
|
1495
« on: February 03, 2011, 03:34:39 pm »
Hopefully somebody notices it here...I'm pretty sure it's an axe bug b/c pretty much the exact same thing worked before (in TWHG). Ever since I've updated to Axe 0.4.8, I've been having one problem after another of this nature, also with some problems with RotC( and RotCC( which didn't work when I tried to use them. So, I was wondering...what changed? Because data storage to lists and external appvars wasn't failing like this before, and now it fails more often than it works.
After close inspection, I noticed a few things that could be causing problems in the code you posted. Firstly, the End:If {L1+D}=2 in subroutine D should just be replaced with an Else. Otherwise, the first if statement may activate and change {L1+D} to 2, thus also activating the following if statement, effectively negating the change you want. The other problem I noticed is that the data isn't being generated correctly for how you step through it later. To generate the data, you loop through Y values inside of a loop through X values, thus making X the "major" direction. However, in all other places in your program, you assign Y as the "major" direction. You should change the data generation code so the X loop is inside of the Y loop. Hopefully these two things will solve your problems! I also can explain the bugs in Axe 0.4.8 with rotC() and rotCC(), as those are my fault. I supplied Quigibo with optimized routines for them, but neither of us noticed until it was too late that the code had a slight fault. Instead of returning the pointer to the rotated sprite, they return the pointer plus eight. For a temporary fix you can add -8 after any rotate commands in Axe 0.4.8. Hopefully this problem will be fixed in the next version though, so if it is, make sure to get rid of any -8's that you may have added. Quigibo: If you're reading this and haven't already fixed the rotate problem, I posted the corrected code a while back here.
1496
« on: February 02, 2011, 11:23:19 pm »
Want these? They were for the InsertMem/DelMem axiom I made a short while ago, so if you're making VAT commands, they could be useful to you. I didn't heavily test them, but they looked pretty good to me.
;---------> InsertMem <---------; ;B_CALL(_InsertMem) with checks to make sure there is enough RAM and the insertion address is okay ; ;INPUTS: Arg1: Pointer to start of variable according to GetCalc() ; Arg2: Offset in variable to insert RAM ; Arg3: Number of bytes to attempt to insert ; ;OUTPUTS: 0 if not enough RAM or Arg2 is out of range ; Nonzero if successful
p_InsertMem: ;REGISTERS: hl=Arg3 ;STACK: | Ret | Arg2 | Arg1 | ... B_CALL(_EnoughMem) ;Check if enough RAM ;REGISTERS: de=Arg3 ;STACK: | Ret | Arg2 | Arg1 | ... pop hl ;Rearrange arguments and stack pop bc ex (sp),hl ;REGISTERS: bc=Arg2 de=Arg3 hl=Arg1 ;STACK: | Ret | ... jr c,__InsertMemFail0Args ;Return failure if not enough RAM dec hl ;Get current variable size ld a,(hl) dec hl push hl ld l,(hl) ld h,a ;REGISTERS: bc=Arg2 de=Arg3 hl=current variable size ;STACK: | Arg1-2 | Ret | ... sbc hl,bc ;Check if offset is outside of variable jr c,__InsertMemFail1Arg ;Return failure if offset is outside of variable add hl,bc ;REGISTERS: bc=Arg2 de=Arg3 hl=current variable size ;STACK: | Arg1-2 | Ret | ... add hl,de ;Calculate final variable size ex de,hl ex (sp),hl ;REGISTERS: bc=Arg2 de=final variable size hl=Arg1-2 ;STACK: | Arg3 | Ret | ... ld (hl),e ;Store final variable size inc hl ld (hl),d inc hl ;REGISTERS: bc=Arg2 de=final variable size hl=Arg1 ;STACK: | Arg3 | Ret | ... add hl,bc ;Calculate RAM insertion address ex de,hl ;Prepare arguments for _InsertMem pop hl ;REGISTERS: bc=Arg2 de=Arg1+Arg2 (RAM insertion address) hl=Arg3 (bytes to insert) ;STACK: | Ret | ... B_CALL(_InsertMem) ;Insert memory ;REGISTERS: de=RAM insertion address hl=a lot (negative size of a VAT entry?) ;STACK: | Ret | ... ret ;Result is always nonzero __InsertMemFail1Arg: pop hl __InsertMemFail0Args: ld hl,0 ret __InsertMemEnd:
;---------> DelMem <---------; ;B_CALL(_DelMem) with checks to make sure that the deletion address and size are okay ; ;INPUTS: Arg1: Pointer to start of variable according to GetCalc() ; Arg2: Offset in variable to delete RAM ; Arg3: Number of bytes to attempt to delete ; ;OUTPUTS: 0 if Arg2+Arg3 is out of range ; Nonzero if successful
p_DelMem: ;REGISTERS: hl=Arg3 ;STACK: | Ret | Arg2 | Arg1 | ... pop af ;Rearrange arguments and stack pop bc pop de push af push hl ;REGISTERS: bc=Arg2 de=Arg1 hl=Arg3 ;STACK: | Arg3 | Ret | ... add hl,bc ;Calculate offset of end of deletion jr c,__DelMemFail ;Return failure if offset+size was so big that it overflowed ex de,hl ;REGISTERS: bc=Arg2 de=Arg2+Arg3 hl=Arg1 ;STACK: | Arg3 | Ret | ... dec hl ;Get current variable size ld a,(hl) dec hl push hl ld l,(hl) ld h,a ;REGISTERS: bc=Arg2 de=Arg2+Arg3 hl=current variable size ;STACK: | Arg1-2 | Arg3 | Ret | ... sbc hl,de ;Check if end is outside of variable pop de ;Early pop to avoid having fail condition requiring 2 pops ;REGISTERS: bc=Arg2 de=Arg1-2 hl=current variable size - (Arg2+Arg3) ;STACK: | Arg3 | Ret | ... jr c,__DelMemFail ;Return failure if end is outside of variable add hl,bc ;Add back offset to get final variable size ;REGISTERS: bc=Arg2 de=Arg1-2 hl=final variable size ;STACK: | Arg3 | Ret | ... ex de,hl ;Store final variable size ld (hl),e inc hl ld (hl),d inc hl ;REGISTERS: bc=Arg2 de=final variable size hl=Arg1 ;STACK: | Arg3 | Ret | ... add hl,bc ;Calculate RAM deletion address pop de ;Prepare size argument for _DelMem ;REGISTERS: bc=Arg2 de=Arg3 (bytes to delete) hl=Arg1+Arg2 (RAM deletion address) ;STACK: | Ret | ... B_CALL(_DelMem) ;Delete memory ;REGISTERS: de=Arg3 (bytes to delete) hl=a lot (negative size of a VAT entry?) ;STACK: | Ret | ... ret ;Result is always nonzero __DelMemFail: pop hl ld hl,0 ret __DelMemEnd:
1497
« on: February 02, 2011, 04:51:18 pm »
If you look at the CLASS column in TASM80.TAB, you will see that IN0 and OUT0 are class 2 instructions. Class 2 instructions aren't available on the z80 processors found in TI calculators, so adding them would probably just cause confusion with people wondering why they don't work. Although it would be nice if they were and we could use such class 2 instructions as TST and MLT, unfortunately we can't.
1498
« on: January 28, 2011, 01:36:24 am »
Just wanted to let everyone know how mine works, since it's not really explained anywhere. It has become quite a massive project. To use it, you can either enter your queries into OmnomIRC right on the forums or through a normal IRC client. If you use the latter, you can also /msg me with your queries. Anyways, to use this, start a message with one of the following: .z80, !z80, or @z80. After this, you can then enter one of four things for different effects: - Enter the hex for an instruction to see detailed information about the instruction (mnemonic, hex, bytes, cycles, flag effects, and description)
- Enter the mnemonic for an instruction to see detailed information about the instruction
- Enter the hex for multiple instructions to receive disassembled mnemonics
- Enter the mnemonics for multiple instructions separated by backslashes to receive compiled hex (Note that backslashes work oddly in OmnomIRC and require about five of them in a row to be entered)
I believe this should support every instruction on the z80, including all undocumented instructions and bcalls. I'm particularly proud of my recent number handling system for assembling code, as it supports all equates defined in ti83plus.inc and math functions like a real assembler! If you notice any bugs or have any other feedback, please tell me! And if you use it heavily in a short period of time and it stops responding to you, that's not a bug, that's flood control. If you're using an IRC client, you can avoid flood control by /msg'ing me with as many queries as you want. EDIT: Examples of the four main ways to use this. If you're using OmnomIRC, the bot will always respond publicly like in the first example. If you're an IRC user, you have a few more options. You enter: @z80 CB30 I respond: <Runer112> [Z80] Instruction \ Main: sll reg8 \ Instance: sll b [CB30] \ Bytes: 2 \ Cycles: 8/15 \ Flags: SZ-0-P0C \ Undocumented \ Description: The contents of reg8 are shifted left one bit position. The contents of bit 7 are put into the carry flag and a one is put into bit 0. You enter: .z80 jr nz,$-14 I respond: -Runer112- [Z80] Instruction \ Main: jr cc,ofs8 \ Instance: jr nz,$-14 [20F0] \ Bytes: 2 \ Cycles: 12/7 \ Flags: -------- \ Description: If condition cc is true, the signed value imm8 is added to pc. The jump is measured from the start of the instruction opcode. You enter: !Z80 E3EF9247218384D1010900EDB0 I respond: -Runer112- [Z80] Disassembly \ http://z80.pastebin.com/D0SRU5X5 \ ex ( sp), hl \ B_CALL( _SetXXXXOP2) \ ld hl, $8483 \ pop de \ ld bc, $0009 \ ldirYou enter: /msg Runer112 .Z80 B_CALL(_RunIndicOn) \ set donePrgm,(iy+doneFlags) \ ret \ .db 19,$37,%10101010,plotSScreen % 256 >> (7/2) I respond in a private message: <Runer112> [Z80] Assembly \ http://z80.pastebin.com/KDbM1xHq \ EF6D45FDCB00EEC91337AA08
1499
« on: January 24, 2011, 09:15:05 pm »
If you only need to initialize five or fewer 2-byte values like variables, it is best to initialize them without any special commands. With this method, you can also avoid changing the values of any memory locations that you don't want to initialize between values you do want to initialize. 0→A→B→X→Y→{L₁}ʳ
However, if you need to initialize more than five values, it is best to use the Fill() method, like you suggested for zeroing out every variable. However, a few slight changes: - Storing 2 bytes to a constant address (e.g. °A) is more optimized than storing 1 byte.
- Because of the above modification and the first two bytes now being equal to 0, the start address for Fill() can be increased by 1 (e.g. °A+1) to save some clock cycles by reducing the total number of bytes needed to be filled by 1.
- There are only 27 variables, for a total of 54 bytes. Subtracting 1 for the above reason and 1 more due to the fact that the size argument of Fill() doesn't count the starting byte gives a grand total of 52 bytes to fill.
0→{°A}ʳ Fill(°A+1,52)
The above method can also be used to initialize 1-byte values by adjusting the size parameter of Fill() accordingly. Note, though, that it will only work for initializing 2-byte values whose low and high bytes are equal, like 0. To initialize six or fewer 2-byte values that do not adhere to this, use the first method mentioned in this post. To initialize more than six 2-byte values that do not adhere to this, use something like the following method. This example will initialize all 27 variables to the hex value 1337: ᴇ1337→{°A}ʳ Copy(°A,+2,52)
1500
« on: January 24, 2011, 06:09:17 pm »
All of the main variables (A-θ) were moved. Because of this, L1 also no longer points to the same location. As for the other variables, I'm not sure, but my guess is that none of them were moved.
Pages: 1 ... 98 99 [100] 101 102 ... 153
|