| |
Flashback
Registered: Dec 2009 Posts: 4 |
Want to understand this way of looping through a table
Hi,
I would like some explanation about this way of looping a colour table:
colourcycle.asm
Basically it's this section I can't wrap my head around:
...
txa ; select next colour index
adc #01
and #07 ; loop colour index
tax
...
colours .byte 09, 08, 05, 13, 01, 13, 05, 08 ; colour table
As far as I understand, the start color is saved in a ZP address (startcol) which will is changed each frame. This is done by txa/ adc #$01 I guess. (BTW, why is there no clc before adc here?) But why do we need the and #$07?
Regards |
|
| |
chatGPZ
Registered: Dec 2001 Posts: 11147 |
Quote:BTW, why is there no clc before adc here?
To answer this first: it's a bug. It should be there :)
As for the AND... ANDing with any value that is (2^n)-1 will limit the value to 0...(2^n)-1. You basically throw away the upper bits. |
| |
Flashback
Registered: Dec 2009 Posts: 4 |
Ok, so by ANDing with #$07 (%00000111) I delete bits 3-7, correct? So my result can't be > 7 as well.
Is this because I only have 8 entries in my colour table? So if I wanted to have more colours, I also would have to change this part? |
| |
rosettif
Registered: Apr 2016 Posts: 10 |
Obviously, the author assumes C=0 for sure when entering the code, so it is just not needed (the CLC). Nevertheless, it would be better in this way (one byte shorter):
inx
txa
and #$07
tax
As long as you are thinking in power of two, it can be easily modified without the need of comparing (e.g. when having 16 colours instead of 8, the #$07 is replaced with #$0f). |
| |
Copyfault
Registered: Dec 2001 Posts: 467 |
I'd also assume carry is taken care of somewhere in the code before that extracted section - but tbh, I did not check that asm yet.
So this is some index handling to get x = 0..7. In case one can live with an offset of 1, i.e. having the table starting at 1 instead of 0 of a page, the unintended sbx-opcode helps getting it even smaller:
lda #$07
sbx #$ff
This effetively slides through the values x = 1..8, with a footprint of 4 bytes and 4 cycles. |
| |
Flashback
Registered: Dec 2009 Posts: 4 |
Thank you guys. Never used this approach, I would rather do something like
ldx tabindex
lda coltab,x
...
inx
cpx maxlength
bne skip
ldx #0
skip:
stx tabindex
Better understandable but of course longer ;) |
| |
Oswald
Registered: Apr 2002 Posts: 5028 |
its a rule of thumb that when you get better in coding you write shorter more elegant and faster code, you need to move away from human thinking and think like the machine. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11147 |
Quote:Obviously, the author assumes C=0 for sure when entering the code, so it is just not needed (the CLC).
Nothing in the source implies that :) It can only work correctly, if the main loop never sets C=1 (which is kinda unlikely :)) |
| |
Bitbreaker
Registered: Oct 2002 Posts: 501 |
i'd favour using sbx aswell when it is about incrementing x by any number and pplying an and operation.
Another option is to move the end condition of the loop into the table, and wrap the index depending on that. Color-register writes usually only need the lownibble.
...
lda tab,x
bpl +
ldx #$ff
+
inx
...
tab !byte 9,8,5,13,1,13,5,$f8
|
| |
chatGPZ
Registered: Dec 2001 Posts: 11147 |
Or just count the index backwards :) |
| |
Gordian
Registered: May 2022 Posts: 36 |
Another approach:
ldx index
...
lda #7
inx
sax index |
| |
Copyfault
Registered: Dec 2001 Posts: 467 |
I'd say it won't get any shorter than doing it via sbx...
Hmm, maybe your table values allow for getting rid of that "lda #$07", but this only works for specific values, just as Bitbreaker mentioned in his post above. |
| |
Gordian
Registered: May 2022 Posts: 36 |
Yes, not shorter, just another solution.
If accumulator is utilized before LDA or it's just not needed anymore, there are no cons.
Or LDY is used for getting values. |
| |
Frostbyte
Registered: Aug 2003 Posts: 174 |
If you need to persist the value, is Gordian's solution then the shortest?
ldx zp_val // 3
lda #$07 // 2
sbx #$ff // 2
stx zp_val // 3
// ... // X = 1 to 8 here
// 8b, 10c
vs.
ldx zp_val // 3
// ... // X = 0 to 7 here
lda #$07 // 2
inx // 2
sax zp_val // 3
// ... // X = 1 to 8 here
// 7b, 10c |
| |
Copyfault
Registered: Dec 2001 Posts: 467 |
Quoting FrostbyteIf you need to persist the value, is Gordian's solution then the shortest?
ldx zp_val // 3
lda #$07 // 2
sbx #$ff // 2
stx zp_val // 3
// ... // X = 1 to 8 here
// 8b, 10c
vs.
ldx zp_val // 3
// ... // X = 0 to 7 here
lda #$07 // 2
inx // 2
sax zp_val // 3
// ... // X = 1 to 8 here
// 7b, 10c Yes, it's one byte shorter when you take the store commands to some temporary register into account. My thinking was about a loop that does not change X in its inner part, thus without any need for storing it inbetween. |
| |
Gordian
Registered: May 2022 Posts: 36 |
Hi!
As I wrote above, lda #$07 can be moved outside inner loop:
lda #$07
ldx zp_val
;ldy table_value,x
;sty somewhere
inx
sax zp_val
So we have 2b and 2c shorter version. |
| |
Copyfault
Registered: Dec 2001 Posts: 467 |
Quoting GordianHi!
As I wrote above, lda #$07 can be moved outside inner loop:
lda #$07
ldx zp_val
;ldy table_value,x
;sty somewhere
inx
sax zp_val
So we have 2b and 2c shorter version. In some rare cases, this might be possible, but usually accu is needed in an inner loop. I still think it'd be more feasible to get rid of the ldx/stx-opcodes (not saying that this is easier than to avoid usage of the accu in the inner loop). In the end, it all depends on what your loop is actually needed for. |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
A bit similar to what Bitbreaker and chatGPZ said, but X is not cluttered and it works for values other than colors too:
dec get_number:+1 ;6
bpl get_number: ;2/3
lda #max_index ;2
sta get_number:+1 ;4
get_number:
lda table
;table at page boundary
table
BYTE number1, number2, …
- Works for any table size < 130;
- Uses ((tabel size-1)*9 + 14)/(table size) cycles on average, which is smaller than 10 cycles for tables with more than 5 elements;
- X (and Y) is not cluttered. |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
About 1 cycle faster for sufficiently long tables is
dec get_number:+1 ;6
bmi set_maxsize: ;2/3
get_number:
lda table
...
jmp $ea31
set_maxsize:
lda #max_size ;2
sta get_number:+1 ;4
jmp get_number: ;3
;table at page boundary
table
BYTE number1, number2, …
|
| |
Oswald
Registered: Apr 2002 Posts: 5028 |
Quote: A bit similar to what Bitbreaker and chatGPZ said, but X is not cluttered and it works for values other than colors too:
dec get_number:+1 ;6
bpl get_number: ;2/3
lda #max_index ;2
sta get_number:+1 ;4
get_number:
lda table
;table at page boundary
table
BYTE number1, number2, …
- Works for any table size < 130;
- Uses ((tabel size-1)*9 + 14)/(table size) cycles on average, which is smaller than 10 cycles for tables with more than 5 elements;
- X (and Y) is not cluttered.
this label: is horrible :P |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
Sorry! Especially in the code, right? Like DEC get_number:+1
I use CBM Program Studio and the label in the code has to be exactly equal to the defined one. |
| |
chatGPZ
Registered: Dec 2001 Posts: 11147 |
Seriously? Wow |
| |
Frostbyte
Registered: Aug 2003 Posts: 174 |
I guess C64 studio syntax doesn't require to end the label definition with a colon like e.g. KickAssembler does, thus if you have one it just becomes part of the label string? So instead of "get_number" your label is actually "get_number:". :)
(sorry, drifting a bit off-topic here) |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
Yes, it becomes part of the label string. But there must be better alternatives, a different character than a colon, or perhaps put the colon before the label name. |
| |
Frostbyte
Registered: Aug 2003 Posts: 174 |
Why do you put the colon or other special char there in the first place? Wouldn't this work? dec get_number+1
; ...
get_number
lda table |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
Yes, that would work, but now you don't see the difference between a label and a memory address. I could, for example, have given some zeropage address the name "get_number". In general, I like to see the difference between them. |
| |
ws
Registered: Apr 2012 Posts: 229 |
Sorry for jumping in here, but i also use CBMprg Studio and not loosing track of what is what is actually a thing that bothered me too, so it boils down to a personal naming convention..
any functions/subroutines get for example named
does_thisthing
branches get named
loop1 or repeat1 ... or whatever, just using a number to identify them
and memory adresses get a descriptive functional name like
framekeep or tablpointr or something like that.
for me personally it worked so far. - other naming conventions might be much more practical, it is just an example.
but then again, for the fun of it, i normally lose any discipline in naming and formatting my code after 75% is done and the rest is then the most terrible spaghetti code ever with unreadable and confusing inserts here and there, add a little overdocumentation and the mess is complete
once i had a terrible mix up bug that i almost didn't find, because my comments stated like ";load tablepointer in x", while it actually contained a LDA |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
Everytime I start a new project I become more disciplined. When you just keep going till the project is finished, it often goes well, but if you stop for a while and want to finish it some time later, then it can take a long time to unravel the mess. |
| |
TheRyk
Registered: Mar 2009 Posts: 2085 |
@Rastah Bar yeah... and more often than not - after spending hours or evenings of cleaning up the mess that once went through as good code for your past self, you just regret not having started from scratch again (which might have even saved time and a bunch of bugs resulting only from messing around with old messy code) :) |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
Recoding part of it can definitely help. Instead of looking at the screen and trying to figure it out, recoding may tell you quicker what you were doing and perhaps show some bugs, too. |
| |
Krill
Registered: Apr 2002 Posts: 2854 |
Quoting Oswaldthis label: is horrible :P Indeed.
The point of a colon after a label definition is to tell it apart from references to that label.
So you can easily search for that label while conveniently not having to skip over all the references to that label.
Having to add the colon to all references as well is pointless, could just as well have everything without colons, like the OG Turbo Assembler did. =) |
| |
Oswald
Registered: Apr 2002 Posts: 5028 |
Quote: Quoting Oswaldthis label: is horrible :P Indeed.
The point of a colon after a label definition is to tell it apart from references to that label.
So you can easily search for that label while conveniently not having to skip over all the references to that label.
Having to add the colon to all references as well is pointless, could just as well have everything without colons, like the OG Turbo Assembler did. =)
ah I didnt know this! finally colon makes sense, but one clever text editor could just show the label without any preceeding characters, I prefer turbo asm way till death :) |
| |
Krill
Registered: Apr 2002 Posts: 2854 |
Quoting Oswaldah I didnt know this! finally colon makes sense, but one clever text editor could just show the label without any preceeding characters, I prefer turbo asm way till death :) You'll quickly end up with an IDE again, and i love me my not-too-smart text editors (like KATE aka KEdit). :) |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
Quote: Quoting Oswaldthis label: is horrible :P Indeed.
The point of a colon after a label definition is to tell it apart from references to that label.
So you can easily search for that label while conveniently not having to skip over all the references to that label.
Having to add the colon to all references as well is pointless, could just as well have everything without colons, like the OG Turbo Assembler did. =)
If you want to make clear the difference between labels and memory addresses, how do yo do that? For example, labels all in lowercase letters and memory addresses start with a capital? |
| |
chatGPZ
Registered: Dec 2001 Posts: 11147 |
but labels are memory addresses?
*runs* |
| |
Krill
Registered: Apr 2002 Posts: 2854 |
Quoting Rastah BarIf you want to make clear the difference between labels and memory addresses, how do yo do that? For example, labels all in lowercase letters and memory addresses start with a capital? Yes, by convention, if need be. It's nothing that needs to be enforced by the toolchain.
My personal convention is all-lower-case for regular labels and allcaps for labels with literally assigned addresses (either by asm source or via linker script). The latter are constants, after all. |
| |
Rastah Bar
Registered: Oct 2012 Posts: 336 |
@chatGPZ: yes, that's true, but I find code a bit easier to read when I see immediately what everything is. But maybe it does not matter in this case.
@Krill: thanks, I will try out some naming conventions. |
| |
Frostbyte
Registered: Aug 2003 Posts: 174 |
This discussion should really be under its own topic (maybe moderators can move it?), but anyway, my 2 eurocents. I've tried a few naming conventions in KickAssembler, and pretty much settled with the following.
Address pointers (i.e. labels), variables and constants are all lowercase, words separated by underscore. E.g.
jsr reticulate_splines
......
reticulate_splines:
lda #next_spline_to_reticulate
Zero page labels are wrapped under "zp" namespace:
.namespace zp {
.label current_player_state = $02
.label spline_tbl_ptr = $03
}
.......
lda #0
sta zp.current_player_state
I use namespaces (even recursive ones) for other special addresses too, e.g. sid and vic and other fixed addresses. This allows me to use dot notation, which imho is very readable, e.g.
lda freq_table_lo, y
sta sid.ch1.freqlo
For macros I use PascalCasing:
MoveCursorTo(15, 13)
...and that's about it. Very simple, really. Namespaces are a godsend, I can easily assign the same context to a bunch of labels or values, and while using them, dot notation separates them clearly from the usual labels or variable names. I don't know how many other dialects have namespaces or a similar concept, unfortunately. Some dialects probably allow adding dots to label names, making naming nearly as neat as using namespaces.
Earlier I tried using all uppercase for ZP variables, constants etc., but ditched it later as it made the code messier to read. |