+atari2600land Posted March 9, 2018 Share Posted March 9, 2018 So I'm making a game for the Channel F and I'm having a bit of trouble. Surely there is a better way of doing what I want to do here. But darned if I can figure out what to do. Here is the code I want to shorten, but I can't figure out how: LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A inc lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A inc inc lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A inc inc inc lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A ai 0 lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A ai 255 lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A ai 254 lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A ai 253 lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A ai 252 lr S, A LISU 3 LISL 4 XS S ; You now get EXOR between A (from 'O'63) and 'O'62 bz subLives See what I mean? Any help is appreciated. Quote Link to comment Share on other sites More sharing options...
carlsson Posted March 12, 2018 Share Posted March 12, 2018 Do LISU 3 and LISL 6 fill a purpose, or will LR S,A simply overwrite ISAR with the value of the accumulator anyway? Also will AI 0 make a difference? I understand that AI 255, AI 254, AI 253 are there due to lack of DEC operands. Does it matter in which order you are testing? I mean now it is +1, +2, +3, 0, -1, -2, -3, -4. If it was possible to set up a loop that goes from -4 to +3 or perhaps 7 to 0 with some clever offsetting, you might be able to save a little code, but I don't understand enough to suggest how it is done. Quote Link to comment Share on other sites More sharing options...
carlsson Posted March 12, 2018 Share Posted March 12, 2018 Entirely dry coded based on assumptions. I may have completely misunderstood your code. It might work just as expected, almost as expected or not at all. LIS 8 LR r0,A ; load scratchpad register 0 with 8, which is used as a counter and offset loop: LI (6*16)+2 ; equals LISU 3; LISL 2 if I understand correctly but goes directly to the accumulator AS r0 ; add register value AI 251 ; subtract to get the desired offset LR IS,A LI (6*16)+4 XS S BZ subLives DS r0 ; decrease the scratchpad register, should affect flags BNZ loop Quote Link to comment Share on other sites More sharing options...
+atari2600land Posted March 12, 2018 Author Share Posted March 12, 2018 LISU 3 LISL 6 is the place of the variable where I store the changed lettuce Y's direction for testing collisions so it doesn't screw up the actual Y position of the lettuce to appear on screen. Quote Link to comment Share on other sites More sharing options...
carlsson Posted March 12, 2018 Share Posted March 12, 2018 Ok, so LR S,A doesn't load the value of the accumulator into ISAR as I imagined it would? I understand the regular registers go 0-11, 0-B or A-L depending on the assembler syntax, so I didn't know what S otherwise would be. It kind of seemed odd that you would be using immediate values all the time, but if those form addresses where values are read from, it makes more sense. But even so, I would think my routine can be modified to work? LIS 8 LR r0,A loop: LISU 3 LISL 2 lr A, S LISU 3 LISL 6 lr S, A AS r0 AI 251 LR S,A LISU 3 LISL 4 XS S BZ subLives DS r0 BNZ loop Quote Link to comment Share on other sites More sharing options...
+atari2600land Posted March 12, 2018 Author Share Posted March 12, 2018 Yes, there are those types of registers, but for values since I'm trying to learn this I put them into LISU/L positions. This is all still very new to me. Quote Link to comment Share on other sites More sharing options...
Kurt_Woloch Posted April 23, 2018 Share Posted April 23, 2018 (edited) OK, now... seems like this thread got abandoned without finding a proper solution for the problem. I'll try to find one here, but let's go step by step so that you can follow me. First, let's see what this code actually does. I'll just pick the first sequence here.. but first, let's get some terminology straight... The ISAR is actually a pointer to one of the 64 registers in the register file. Its upper half (3 bits) gets loaded with LISU, and its lower half (3 bits) with LISL. So LISU 3, LISL 2 selects register 2 in bank 3, if we say we have 8 banks with 8 registers each. The ISAR retains its value until it gets overwritten, so if you access multiple registers in the same bank, you only have to update the lower half of the register by doing LISL, but not LISU. Then, LR S, A does not overwrite the ISAR (which would be the pointer), but the register which the ISAR currently points to, with the contents of A. Therefore, LI (6*16) + 2 does not set the ISAR, but only the A register. To write that value to the ISAR, you have to do LR IS, A. However, this takes more bytes than doing LISU and LISL. These two only take one byte each while you need 2 bytes for the LI (6*16) + 2 instruction and one more for LR IS; A. In Carlsson's first proposal, before this is done, the loop value is being added (minus a constant) in order to loop through the registers. But that's not what the original code does, instead it always works on the same three registers. So, with that out of the way, let's see what the code actually does... LISU 3 ;point the ISAR at bank 3 LISL 2 ;register 2 of bank 3 lr A, S ;load the value from that register into A LISU 3 ;point the ISAR at bank 3 (this is useless, it already points at bank 3!) LISL 6 ;register 6 of bank 3 lr S, A ;load the value from A into that register (which is useless because it's not accessed after that) inc ;increment A lr S, A ;and load the value from A into the same register again (without the first value having been used) LISU 3 ;point the ISAR at bank 3 (this is useless, it still points at bank 3!) LISL 4 ;register 4 of bank 3 XS S ;EXOR the value of A (which is also in Register 6 of bank 3) with the value of register 4 bank 3 bz subLives ;and branch to subLives if the value in A is now 0, that is, the EXORed values were the same Now maybe there's one more useless line up there, but only if the value of register 6 of bank 3 isn't being used after branching to subLives. In that case we don't need to write to that register at all because the changed value is in A anyway, and we don't need it after the comparison. Now, Carlsson's 2nd attempt has put this into a loop, but still retains the useless statements shown above. With those gone, Carlsson's code would look like this... LIS 8 ;start loop at 8 LR r0,A ;into r0 LISU 3 ;select bank 3 for whole loop loop: LISL 2 ;read value from register 2 in bank 3 lr A, S ;lr S, A obviously is useless in that case as well since the value is still there AS r0 ;add loop variable, then subtract 5 (?) AI 251 LISL 4 ;point to register 4 to compare to XS S ;eor BZ subLives ;and branch if equal DS r0 ;otherwise decrease the loop value and loop back if > 0 BNZ loop Now this is still not the optimum solution because while the code is shorter, it's sill being executed in a loop, so it actually takes longer to execute than the unrolled code. However, the actual purpose of the code seems to be to check if the contents of register 4 in bank 3 fall within a range of -4 to 3 plus the contents of register 2 of bank 3. This doesn't have to be compared in a loop, rather you only need two comparisons against an upper and a lower boundary, which again depends on one of the two registers So, similar to the code posted above, I'd do this as follows: LISU 3 ;point the ISAR at bank 3 LISL 2 ;register 2 of bank 3 LR A, S ;load the value from that register into A COM ;complement A (this means A = 255 - A) LISL 4 ;point to register 4 of bank 3 AS S ;add that register to A, so now A contains the value of (Register 2 - Register 6 of bank 3 - 1) CI 2 ;Set carry flag if A > 2 BNC subLives ;branch to subLives if carry was clear (Reg. 2 - Reg. 6 <= 3) CI 250 ;else compare against 250 (set carry flag if A > 250) BC subLives ;branch to subLives if carry was set (Reg. 2 - Reg. 6 > -5) Let's see how this one works... we'll do some examples. Example 1: Reg. 2 = 100, Reg. 4 = 96. In this case, the collision should just fire... 100 gets loaded from Reg. 2 and complemented to 155. We add 96 from Reg. 4 and get 155 + 96 = 251. CI 2 here sets the carry flag because 251 is greater than 2, so the BNC command doesn't get executed. CI 250 here sets the carry flag because 251 is greater than 250, so the BC command fires, and a collision is registered. Example 2: Reg. 2 = 100, Reg. 4 = 95. In this case, the collision should not fire... 100 gets loaded from Reg. 2 and complemented to 155. We add 95 from Reg. 4 and get 155 + 95 = 250. CI 2 here sets the carry flag because 250 is greater than 2, so the BNC command doesn't get executed. CI 250 here clears the carry flag because 250 is not greater than 250, so the BC command doesn't fire as well, and no collision is registered. Example 3: Reg. 2 = 100, Reg. 4 = 103. In this case, the collision should just fire... 100 gets loaded from Reg. 2 and complemented to 155. We add 103 from Reg. 4 and get 155 + 103 = 258 - 256 = 2. CI 2 here clears the carry flag because 2 is not greater than 2, so the BNC command gets executed and registers the collision. Example 4: Reg. 2 = 100, Reg. 4 = 104. In this case, the collision should not fire... 100 gets loaded from Reg. 2 and complemented to 155. We add 104 from Reg. 4 and get 155 + 104 = 259 - 256 = 3. CI 2 here sets the carry flag because 3 is greater than 2, so the BNC command doesn't fire as well. CI 250 here clears the carry flag because 3 is not greater than 250, so the BC command doesn't fire as well, and no collision is registered. I think this is a pretty effective solution which doesn't need any additional registers or loop variables except for A. Edited April 23, 2018 by Kurt_Woloch 1 Quote Link to comment Share on other sites More sharing options...
carlsson Posted April 24, 2018 Share Posted April 24, 2018 Thanks for the explanations! I'm sure they're useful for those getting deep into the F8. I understood that atari2600land feared he was running out of ROM space, and thus wanted to optimize routines for size rather than speed. However as I found, several other Channel F programs are far bigger than his, so unless there is a limitation on the cartridge PCB to be used to make physical copies, I don't think restricting for size at the cost of content would be desired. Quote Link to comment Share on other sites More sharing options...
Kurt_Woloch Posted April 24, 2018 Share Posted April 24, 2018 Well, in this case I think that my version was improved both speed- and size-wise since like Atari2600land's original version, it runs down in a straight line, only that it's much shorter and thus also should execute much faster. But I agree that there's often a trade-off between speed and size, for instance, you can probably draw objects the fastest if they're stored with a pixel per byte in the ROM, while compressing them saves space, but takes additional time for un-compressing. And sometimes you may also have to optimize for memory footprint, which is evident in the Channel F where there's effectively only 64 bytes of readable and writable RAM (in addition to the video RAM you can't read from). Thus you may be forced to compress the game data you store somewhat to get it all into memory, which slows down execution, or draw as much data as possible off pre-calculated tables in the ROM instead of generating it on the fly. Quote Link to comment Share on other sites More sharing options...
+atari2600land Posted April 25, 2018 Author Share Posted April 25, 2018 OK, I put Kurt's code in and it works great! I was able to put in some more stuff since the code was trimmed a lot. 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.