Due to unusually high levels of website traffic you will be presenting with regular Cloudflare checks for the time being.
Changing all attributes
Changing all attributes
Another "let's get the Specchums to do my work for me disguised as a programming challenge" post!
What's the quickest way to change all attributes of a given value to another given value? So if I'm given (PAPER_RED|INK_WHITE) and I want all the character cells with that attribute value changed to (PAPER_WHITE|INK_BLUE), what's the most efficient way to do it (timewise)?
What's the quickest way to change all attributes of a given value to another given value? So if I'm given (PAPER_RED|INK_WHITE) and I want all the character cells with that attribute value changed to (PAPER_WHITE|INK_BLUE), what's the most efficient way to do it (timewise)?
Derek Fountain, author of the ZX Spectrum C Programmer's Getting Started Guide and various open source games, hardware and other projects, including an IF1 and ZX Microdrive emulator.
Re: Changing all attributes
Most likely a loop of PUSH opcodes. I'll leave the actual code to you!
Re: Changing all attributes
I’ll bite… A 256 high-byte aligned look-up table, where each low byte is an attribute value, and the value the attribute to change it to. Then run over the whole attribute buffer converting each one in turn.
Re: Changing all attributes
I'm not reallly convinced about tricks with stack and table
Here is some very straighforward code, I'm writing without testing:
And repeat it 3 times for 3 screen segments.
Now show me a faster version
Here is some very straighforward code, I'm writing without testing:
Code: Select all
LD HL,22528
LD DE, 256*checkedValue + newValue
LD B,0
Loop:
LD A,(HL)
CP D ;is checked value
JP NZ,Loop2
LD (HL),E ;load new value
Loop2:
INC L ;next byte
DJNZ Loop
Now show me a faster version
- MatGubbins
- Dynamite Dan
- Posts: 1241
- Joined: Mon Nov 13, 2017 11:45 am
- Location: Kent, UK
Re: Changing all attributes
D is the attribute that you want to change
E is the new attribute colour
E is the new attribute colour
Code: Select all
LD D,1 ; look for this value
LD E,2 ; Change to this
LD HL,22528 ; start of attribs
again
LD A,(HL) ; grab the value in HL
CP D ; is this what I want?
JR NZ,skip ; no, do JR
LD (HL),E ; yes, change it
skip
INC HL ; move along
LD A,H ; read value of H
CP 91 ; is it in the printer buffer?
JR NZ,again ; no, loop
RET ; yes, done
Re: Changing all attributes
Code: Select all
ld hl, attr_third
ld d, table
loop:
ld e, (hl)
ld a, (de)
ld (hl), a
inc l
djnz loop
Proud owner of Didaktik M
Re: Changing all attributes
Ralf wrote: ↑Sun Apr 03, 2022 10:56 pm I'm not reallly convinced about tricks with stack and table
Here is some very straighforward code, I'm writing without testing:And repeat it 3 times for 3 screen segments.Code: Select all
LD HL,22528 LD DE, 256*checkedValue + newValue LD B,0 Loop: LD A,(HL) CP D ;is checked value JP NZ,Loop2 LD (HL),E ;load new value Loop2: INC L ;next byte DJNZ Loop
Now show me a faster version
Let's go:
Code: Select all
ld hl, 22528
ld bc, 32*24
ld de, 256*checkedValue + newValue
loop:
ld a, d
cpir
ld (hl), e
ld a, b
or c
jp nz, loop
If something works, don't touch it !!!! at all !!!
Re: Changing all attributes
Nice one. I'm not very familiar with CPIR behaviour but I believe P/V flag could be used to control loop .Bubu wrote: ↑Mon Apr 04, 2022 1:57 am
Let's go:
Code: Select all
ld hl, 22528 ld bc, 32*24 ld de, 256*checkedValue + newValue loop: ld a, d cpir ld (hl), e ld a, b or c jp nz, loop
Code: Select all
loop:
cpir
ld (hl), e
jp po, loop
Proud owner of Didaktik M
Re: Changing all attributes
Thanks once again for the advice, chaps.
I don't get the PUSH or table approaches at all, but I can study and learn from the fast loops which have been presented.
I don't get the PUSH or table approaches at all, but I can study and learn from the fast loops which have been presented.
Derek Fountain, author of the ZX Spectrum C Programmer's Getting Started Guide and various open source games, hardware and other projects, including an IF1 and ZX Microdrive emulator.
Re: Changing all attributes
Bubu wrote: ↑Mon Apr 04, 2022 1:57 amCode: Select all
ld hl, 22528 ld bc, 32*24 ld de, 256*checkedValue + newValue loop: ld a, d cpir ld (hl), e ld a, b or c jp nz, loop
HOWEVER: I tried this, and it replaced every attribute one square immediately to the right of those I'd checked with what I specified to replace. Add in a DEC HL immediately after the CPIR, and... it works but is still not quite perfect, in that it stops replacing with four squares still unreplaced on row 21. (My BASIC test program only filled the PRINT rows rather than the INPUT area as well.)
I was using JR instead of JP in the last line. I changed it back to JP and it made no difference, though there was only one attribute left unreplaced.
But still, it's almost top banana! While ploughing through the Machine Code Tutor again, the chapters on block instructions are very vague about the non-LD instructions, so although I thought CPIR (and CPDR) might be useful, it was still hard to see how. This has at least given a concrete example that I can work with.
The IN and OUT block instructions are still a total mystery...
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Re: Changing all attributes
Code: Select all
ld hl, attr_third
ld d, table
loop:
ld e, (hl)
ld a, (de)
ld (hl), a
inc l
djnz loop
The lookup table approach is very counter-intuitive but yes, it actually may be the fastest.
Explanation for anybody who needs it:
We don't check if we should change a byte or not. We change every byte.
Or it would better to say we "change" every byte.
Because we have a table (actually a block of bytes) which looks like
Code: Select all
address value
32768 + 0 0
32768 + 1 1
32768 + 2 2
32768 + 3 155
32768 + 4 4
Then we use it as lower byte of table address.
Then we read a value from table address and write it to the memory
in 99% of cases we "replace" the value with the same value
In one single case we replace our value with new value (3 to 155 in my example)
And it is actually faster then checking each time if we should replace the value or not
Re: Changing all attributes
I've worked out what's going wrong with my fix to Bubu's CPIR routine. Every time I have to DEC HL so that the new attributes are dropped in the right place, it reduces the number of squares that the BC loop will check by one. A slow (for machine code!) solution is to put the loop right back to the initial line so that it starts again at the first square and CPIRs the entire attribute area until it finds nothing:
On closer inspection I found Bubu's idea was to start the CPIR process again from the point where it found the last attribute to compare with, so where HL had to be dropped by one to compensate for CPIR landing one byte further on from where it should be, so BC also needs to be corrected, nudged back up by one. After some hair-pulling, I worked out that the INC BC has to come before the CPIR, so that when the CPIR process is finished, BC=0 if there are no attributes left to check - I was putting it after the DEC HL, in which case BC always equals at least 1 and it gets stuck in a loop.
I wrote a (not entirely small!) routine to fill the entire screen (INPUT area as well) with * characters, no flash or bright, black ink, and a random paper colour. To make sure the adjustments worked perfectly, I made sure (23,31) - the last byte of the attributes - was black on black so that the CPIR instruction would pick up on it (at least, given the value I'd put in DE). So now, this version of Bubu's code will do the job, put the new attributes where they need to be, and scan and correct the entire attribute area without touching the next byte:
And to vary it from BASIC:
POKE startaddress+7,CheckedValue
POKE startaddress+8,NewValue
Now the Triumphant Baby can have his day!
Code: Select all
loop: ld hl, 22528
ld bc, 32*24
ld de, 256*checkedValue + newValue ; I used 199 here so that flash/bright/paper/ink 0 is replaced with fl1/br1/paper0/ink7
ld a, d
cpir
[b]dec hl[/b] ; this had to be inserted to get the attribute in the right place
ld (hl), e
ld a, b
or c
[b]jr nz, loop[/b] ; no idea why Bubu used JP here...!
I wrote a (not entirely small!) routine to fill the entire screen (INPUT area as well) with * characters, no flash or bright, black ink, and a random paper colour. To make sure the adjustments worked perfectly, I made sure (23,31) - the last byte of the attributes - was black on black so that the CPIR instruction would pick up on it (at least, given the value I'd put in DE). So now, this version of Bubu's code will do the job, put the new attributes where they need to be, and scan and correct the entire attribute area without touching the next byte:
Code: Select all
ld hl, 22528
ld bc, 32*24
ld de, 256*checkedValue + newValue
loop:
ld a, d
[b]inc bc[/b]
cpir
[b]dec hl[/b]
ld (hl), e
ld a, b
or c
[b]jr nz, loop[/b]
POKE startaddress+7,CheckedValue
POKE startaddress+8,NewValue
Now the Triumphant Baby can have his day!
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
- Lethargeek
- Manic Miner
- Posts: 759
- Joined: Wed Dec 11, 2019 6:47 am
Re: Changing all attributes
also it may be unrolled to make it faster BUTRalf wrote: ↑Mon Apr 04, 2022 12:48 pmI get it now.Code: Select all
ld hl, attr_third ld d, table loop: ld e, (hl) ld a, (de) ld (hl), a inc l djnz loop
The lookup table approach is very counter-intuitive but yes, it actually may be the fastest.
it's always 25 clocks per byte (without djnz)
however
Code: Select all
...
cp (hl)
jrnz _skip
ld (hl),c
_skip inc l
...
...but of course using tables you can change more than one value in one go
Re: Changing all attributes
Rather than worrying about fudging BC around the CPIR, could you not just do:
DEC HL
LD (HL), E
INC HL
DEC HL
LD (HL), E
INC HL
Re: Changing all attributes
As it turns out, that works as well. (And it'll be faster, but imperceptibly so...)
Spectribution: Dr. Jim's Sinclair computing pages.
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Features my own programs, modified type-ins, RZXs, character sets & UDGs, and QL type-ins... so far!
Re: Changing all attributes
Splitting the screen into thirds may also help, since then you might be able to get away with 8-bit adjustments, though I haven't thought through all the logic there so it might get tricky at the boundaries.
Re: Changing all attributes
I think JP y faster than JR:
JP Z : 10 clock cycles
JR Z : 12 clock cycles (only if condition fails, then 7 clock cycles)
If something works, don't touch it !!!! at all !!!
- MatGubbins
- Dynamite Dan
- Posts: 1241
- Joined: Mon Nov 13, 2017 11:45 am
- Location: Kent, UK
Re: Changing all attributes
Please correct me if I've made a huge mistake here but when the CPIR BC counter hits zero it will continue with the next instruction of placing E into (HL) and that will be address 23296, the first byte of the printer buffer.
Once the code has been run and returned to basic, do PRINT PEEK 23296 and it should be 249.
This next snip of code removes the INC BC command and adds in the INC HL after LD (HL),E
This will now place a colour in 23295 (the last byte of the attributes)
PRINT PEEK 23296 should be zero.
Are the routines flawed or is it me doing something wrong?
Code: Select all
LD A,0 ;
LD (23296),A ; set to zero for testing
LD D,64 ; look for this
LD E,249 ; set to this value
LD HL,22528
LD BC,32*24
alloop LD A,D
INC BC
CPIR
DEC HL
LD (HL),E
LD A,B
OR C
JR NZ,alloop
RET
This next snip of code removes the INC BC command and adds in the INC HL after LD (HL),E
This will now place a colour in 23295 (the last byte of the attributes)
Code: Select all
LD A,0
LD (23296),A ; set to zero for testing
LD D,64 ; look for this
LD E,249 ; set to this value
LD HL,22528
LD BC,32*24
alloop LD A,D
; INC BC
CPIR
DEC HL
LD (HL),E
INC HL
LD A,B
OR C
JR NZ,alloop
RET
Are the routines flawed or is it me doing something wrong?
- Turtle_Quality
- Manic Miner
- Posts: 508
- Joined: Fri Dec 07, 2018 10:19 pm
Re: Changing all attributes
Yes the routine is flawed
The point @catmeows made was overlooked in the latest versions, there needs to be a check of the P/V flag to see why the the CPIR finished, either because a match was found or because the BC counter reached zerocatmeows wrote: ↑Mon Apr 04, 2022 2:08 am I'm not very familiar with CPIR behaviour but I believe P/V flag could be used to control loop .
Code: Select all
loop: cpir ld (hl), e jp po, loop
Definition of loop : see loop
- MatGubbins
- Dynamite Dan
- Posts: 1241
- Joined: Mon Nov 13, 2017 11:45 am
- Location: Kent, UK
Re: Changing all attributes
I did wonder what the PO part did but couldn't get it working properly. A jiggle of the placement and by using the RET PO command and adding 1 to the BC counter then it should now work properly.
If it still fails then I'm going back to the simple routines
Code: Select all
LD A,64 ; look for this
LD E,249 ; set to this value
LD HL,22528 ; start address
LD BC,768+1 ; bytes to count <--- note the +1 here
alloop CPIR ; if (HL) = A then flag it up
; auto INC HL
; auto DEC BC
; if flag is true then exit CPIR
; if BC is 0 then exit CPIR
RET PO ; if BC is 0 then it has hit the printer buffer, RET
; JP PO,done ; or use this
; else
DEC HL ; shuffle back a byte
LD (HL),E ; put the value in
INC HL ; shuffle forward
JP alloop ; loop
done
Re: Changing all attributes
You can change CPIR to CPDR, so it will run until ROM.MatGubbins wrote: ↑Tue Apr 05, 2022 1:07 am Please correct me if I've made a huge mistake here but when the CPIR BC counter hits zero it will continue with the next instruction of placing E into (HL) and that will be address 23296, the first byte of the printer buffer.
- Turtle_Quality
- Manic Miner
- Posts: 508
- Joined: Fri Dec 07, 2018 10:19 pm
Re: Changing all attributes
Neat solution, certainly quicker than checking every time why the CPIR finished
Definition of loop : see loop