Posts: 282
Threads: 48
Joined: Feb 2011
Reputation:
0
thanks
Ive read a few tutorials on spectrum screen memory but still dont understand it
the first 3 bits are always 010 so a lot of screen manipulation starts off with binary rotations,
aside from that I dont get it :?: :?:
Posts: 615
Threads: 49
Joined: Feb 2009
Reputation:
2
Yes, rotations are important. I'm using rotation to calculate screen address from line. You just need to change bits 0-2 with 3-5 to get the address that can be multipied with 32 (bitshift 5 times to left) and added 16384 or 49152 to get the address in screen memory. This all can be done much easier in binary.
Posts: 806
Threads: 135
Joined: Apr 2009
Reputation:
5
boriel Wrote:britlion Wrote:You could read my tutorial for Berksman? It discusses things like where to place data for the map in an array within ZX Basic as one of the many topics I rambled on about randomly in passing while making a game....
I was about to comment on it, but the link is not in the Wiki tutorials page. Can you add it, please?
Um. It's the first one listed?
Posts: 1,791
Threads: 56
Joined: Aug 2019
Reputation:
25
britlion Wrote:boriel Wrote:britlion Wrote:You could read my tutorial for Berksman? It discusses things like where to place data for the map in an array within ZX Basic as one of the many topics I rambled on about randomly in passing while making a game....
I was about to comment on it, but the link is not in the Wiki tutorials page. Can you add it, please?
Um. It's the first one listed? Yes, I realised later but didn't commented it here. Sorry.
I overlooked it at first, because you say the "ASM" tutorial, and for me this is the "Awesome PAC-MAN Tutorial"
Update: No joke. Many people here has read your tutorial. Me included
Posts: 806
Threads: 135
Joined: Apr 2009
Reputation:
5
slenkar Wrote:thanks
Ive read a few tutorials on spectrum screen memory but still dont understand it
the first 3 bits are always 010 so a lot of screen manipulation starts off with binary rotations,
aside from that I dont get it :?: :?:
It's a bit hard to get your head around - though if you call it a binary number it becomes clearer.
There are two ways of thinking of this.
If T = third of screen, R = Character row inside a third (row 0 to row 7), and L = line of that character row, and X = X value along the screen, then the 16 bit value you want in binary is:
010TTLLLRRRXXXXX
So you can think of it as chaining together numbers T (a two bit number 0-2 ["3" here is in the attributes - there isn't a "fourth third"]), followed by L (a three bit number 0-7), R (a three bit number 0-7), and the X value (a 5 bit number 0-31)
As you can imagine, this requires some bit shuffling to glue the parts together. The L before R seems strange - but inside a character, you can actually go down one line with an 8 bit command INC H, which is very fast. (It sets bit 8 of the above, or the last bit of the high byte).
Alternatively, you can think of it in pixels alone - and you have an X value along the screen, and a Y value down the screen. In this case you have:
0 1 0 Y7 Y6 Y2 Y1 Y0 Y5 Y4 Y3 XXXXX
So it's 010 YYYYYYYY XXXXX Yay! Except the 8 Y value bits are in the wrong order Bits 3,4,5 belong in the top of the low byte, and the rest get into the high byte in order. This is one reason why rotates happen in screen routines to shuffle bits into the right place.
e.g. if we have a Y value with bits 76543210 - we can rotate it left twice to get 54321076, then use AND to map the first three bits off into another register. (e.g. AND 11100000) then AND the X value with the result to make the lower byte.
So yes. It's complex. But read this post over and over while looking at some screen routines ( <!-- m --><a class="postlink" href="http://www.boriel.com/wiki/en/index.php/ZX_BASIC:Library#Graphics_Library">http://www.boriel.com/wiki/en/index.php ... cs_Library</a><!-- m --> ) and hopefully you'll work out how it was all done.
Here's an example from the high res print routine:
Code: ;HRPAT is a subroutine TO convert pixel values into an absolute screen address
;On Entry - B = Y Value C = X Value On EXIT - DE = Screen Address
HRPat:
ld a,b
srl a
srl a
srl a
ld e,a
AND 24
OR 64
ld d,a
ld a,b
AND 7
add a,d
ld d,a
ld a,e
AND 7
rrca
rrca
rrca
ld e,a
ld a,c
srl a
srl a
srl a
add a,e
ld e,a
ret
Posts: 1,791
Threads: 56
Joined: Aug 2019
Reputation:
25
The best way, IMHO, is to try to write your own routine in ZX Basic (in BASIC, yes).
UIsing POKE 16384 + Yourfunction(x, y) to calculate the screen address.
I did it in Sinclair BASIC years ago, and you will learn a lot doing it. :roll:
Update: This is what I did, If I recall correctly.
- Poke 8 vertical lines with 255 (a black 8x8 square)
- Like above but at any coord at (Y = 0, X = 0 .. 31)
- Like the above but at any coord at (Y = 0..7, X = 0..31) < = This is first 3rd screen
- Like the above but in any coord at (y = 8..15, x = 0, 31) <= This is the second screen "third"
- Like the above but in any coord. You got it.
- Add Attrs
Posts: 282
Threads: 48
Joined: Feb 2011
Reputation:
0
thanks,
I made this little program to draw on each line going down the screen but it didnt look like I expected
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,16384
LD BC,20
drawblokes:
INC H
LD (HL),254
DEC BC
LD A, B
OR C
JP NZ,drawblokes
end asm
end function
drawthis()
Print "done"
it just draws a couple of blobs to the screen
I am using INC H to go down the screen
using BC as a 16 bit counter
Loading 254 into screen memory each time
Posts: 1,791
Threads: 56
Joined: Aug 2019
Reputation:
25
slenkar Wrote:thanks,
I made this little program to draw on each line going down the screen but it didnt look like I expected
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,16384
LD BC,20
...
drawthis()
Print "done"
it just draws a couple of blobs to the screen
I am using INC H to go down the screen
using BC as a 16 bit counter
Loading 254 into screen memory each time Yes, you are on STEP 3) of the list
Britlion has written the generic solution. But I suggest you to proceed as you are doing now (you are doing great, BTW!).
Basically, each time you use INC H, you are adding 256 to HL. Lets see it in binary form:
- Initially HL = 16348 = 0x4000 = BIN 0100 0000 0000 0000 <= LINE 0
Now the next line is 256 bytes ahead (INC H)
- HL 16640 = 0x4100 = BIN 0100 0001 0000 0000 <= LINE 1
This works for lines 0..7. At that moment, HL will point to scanline 7, and its value will be:
HL = 16384 + 256 * 7 = 18176 = 0x4700 = BIN 0100 0111 0000 0000
As you can see, the bit pattern seems to be:
BIN 0100 0xxx 0000 0000 where 'xxx' ranges from 000 to 111 (0..7). These are the caracter scalines.
But LINE 8 is just 32 bytes ahead of LINE 0! This means, we have to "undo" 7 times INC H, and ADD 32 to L.
A simple way to do this could be, DEC H, DEC H, DEC H (7 times) and then ADD L, 32. :roll: But this is slow and takes 9 bytes. Using bit operations leads to an optimized method.
Experiment with this now, and try again. If not, we'll keep working on it.
Posts: 282
Threads: 48
Joined: Feb 2011
Reputation:
0
thanks both
for number 1 did you mean horizontal lines?
if so here is the solution:
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,16384
LD BC,7
drawblokes:
INC H
LD (HL),254
DEC BC
LD A, B
OR C
JP NZ,drawblokes
end asm
end function
drawthis()
Print "done"
doing it with vertical lines would be doing the above, but drawing lines in like this
100000000
replaced with..
11000000
replaced with..
11100000
replaced with..
11110000
replaced with..
11111000
replaced with..
11111100
replaced with..
11111110
replaced with..
11111111
Posts: 282
Threads: 48
Joined: Feb 2011
Reputation:
0
this is fun
here is how I printed a block to co-ords 7,3
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,16384
LD BC,7
LD A,3
RRC A
RRC A
RRC A
OR 7
LD L,A
drawblokes:
INC H
LD A,254
LD (HL),A
DEC BC
LD A, B
OR C
JP NZ,drawblokes
end asm
end function
drawthis()
Print "done"
but if I move INC H to just before the jump command the screen goes a bit weird
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,16384
LD BC,7
LD A,3
RRC A
RRC A
RRC A
OR 7
LD L,A
drawblokes:
LD A,254
LD (HL),A
DEC BC
LD A, B
OR C
INC H
JP NZ,drawblokes
end asm
end function
drawthis()
Print "done"
EDIT
ohhh
INC seems to set the zero flag
Posts: 1,791
Threads: 56
Joined: Aug 2019
Reputation:
25
Hmmm.
This wasn't what I was thinking.
The ZX Spectrum screen has 24 * 8 = 192 scan lines (0..to 191 couting from the top of the screen). My idea was you created an *universal* function to return the memory address of a scan line. This way:
Code: function ScrAdd(scanline as uByte) as Uinteger
...
End Function
REM DRAW A Vertical Line
FOR i = 0 to 191
POKE ScrAdd(i), 255
NEXT
The trick is: the screen is divided in 3 thirds. The first one PRINTs AT 0..7, X so scan lines 0 to 63. This means, that al least for lines 0 to 63, we are in the 1rst third. And each character scan top-line starts at 32 * row, where row is 0..7. Remember, as britlion stated, the trick is to take the 'scanline' parameter as binary:
Code: Lets try for line 120 (BIN 0111 1101)
s s n n n p p p
0 1 1 1 1 1 0 1 => so [s s][n n n][p p p] = [0 1][1 1 1][1 0 1]
So the line address is 16384 + [s s] * 32 * 64 + [n n n] * 32 + [p p p] * 256
The number 32 * 64 above is 32 bytes * 64 lines = Number of bytes on each 1/3 of the screen (each 3rd has 64 lines, of a total of 64 * 3 = 192 lines).
Here is the function (in ZX BASIC):
Code: FUNCTION ScrAddr(scanline as Ubyte) AS UInteger
DIM result As UInteger
LET result = (32 * 64) * (scanline >> 6) ' Only 2 higher bits [s s].
LET result = result + 32 * ((scanline bAnd 63) >> 3) [n n n]
LET result = result + 256 * (scanline bAnd 7) [p p p]
RETURN 16384 + result
END FUNCTION
DIM i as Ubyte
FOR i = 0 TO 191:
POKE ScrAddr(i), 255
NEXT
PAUSE 0
Translate the above to assembler an you are done But as Britlion pointed, in Assembler there are more optimized ways to do the above operations 8)
E.g. - (scanline >> 6) is equivalent to (scanline / 64)
- (scanline bAnd 63) >> 8 is equivalent to (scanline % 63) >> 3
- (scanline bAnd 7) is equivalnet to (scanline % 8 )
But in assembler bit manipulation is much faster.
Posts: 282
Threads: 48
Joined: Feb 2011
Reputation:
0
yes that is what I need for sprites,
but I am trying to do some scrolling
got it to work
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,16384
LD BC,8
;LD A,3
;RRC A
;RRC A
;RRC A
OR 7
LD L,A
drawblokes:
LD A,255
LD (HL),A
INC H
DEC BC
LD (blockiter),BC
LD BC,(delayiter)
wait1:
dec BC
ld a,B
or C
jr nz, wait1
LD BC,(blockiter)
LD A, B
OR C
JP NZ,drawblokes
LD BC,8
LD (blockiter),BC
LD A,31
LD (screenwidthiter),A
LD A,7
LD (screenheightiter),A
LD HL,6000
LD (scrolliter),HL
LD DE,(scrolliter)
LD HL,16384
scroll:
DEC DE
LD BC,(delayiter)
wait2:
dec BC
ld a,B
or C
jr nz, wait2
LD B,(screenwidthiter)
;LD C,(screenheightiter)
screenhori:
LD L,B
;shift graphic
LD A,(HL)
RLA
LD (HL),A
DEC B
LD A,B
jr nz,screenhori
LD DE,(scrolliter)
LD A, D
OR E
jp nz,scroll
end asm
end function
drawthis()
Print "done"
STOP
ASM
blockiter:
defw 8
delayiter:
defw 60000
screenwidthiter:
defb 31
screenheightiter:
defb 23
scrolliter:
defw 10000
end asm
I slowed it down intentionally so you can see whats going on
Posts: 282
Threads: 48
Joined: Feb 2011
Reputation:
0
scrolling 8 lines:
Code: function FASTCALL drawthis() as ubyte
asm
LD HL,2000
LD (delayiter),HL
LD HL,16384
drawvert:
LD A,31
LD L,A
LD (screenwidthiter),A
drawblokes:
LD A,(graphic)
DEC A
LD (graphic),A
LD (HL),A
LD BC,(delayiter)
wait1:
dec BC
ld a,B
or C
jr nz, wait1
LD A,(screenwidthiter)
LD L,A
DEC A
LD (screenwidthiter),A
LD B,A
INC B
INC B
INC B
djnz drawblokes
LD A,(screenheightiter)
LD L,A
INC H
DEC A
LD (screenheightiter),A
LD B,A
INC B
djnz drawvert
LD A,31
LD (screenwidthiter),A
LD A,7
LD (screenheightiter),A
LD HL,60000
LD (delayiter),HL
LD HL,16384
LD (screenaddress),HL
screenvert:
LD A,31
LD L,A
LD (screenwidthiter),A
screenhori:
;shift graphic
LD A,(HL)
RLA
LD (HL),A
LD A,(screenwidthiter)
LD L,A
DEC A
LD (screenwidthiter),A
LD B,A
INC B
INC B
INC B
djnz screenhori
LD A,(screenheightiter)
INC H
DEC A
LD (screenheightiter),A
LD B,A
INC B
INC B
djnz screenvert
DEC H
DEC H
DEC H
DEC H
DEC H
DEC H
DEC H
DEC H
LD A,7
LD (screenheightiter),A
jp screenvert
end asm
end function
drawthis()
Print "done"
STOP
ASM
blockiter:
defw 8
delayiter:
defw 60000
screenwidthiter:
defb 31
screenheightiter:
defb 8
scrolliter:
defw 10000
graphic:
defb 255
screenaddress:
defw 0
end asm
why does it wrap around? thats like a bit shift affecting the byte next to it?!?!
|