Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Assembly question
#16
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 :?: :?:
Reply
#17
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.
------------------------------------------------------------
http://lcd-one.da.ru redirector is dead
Visit my http://members.inode.at/838331/index.html home page!
Reply
#18
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?
Reply
#19
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" Big Grin

Update: No joke. Many people here has read your tutorial. Me included Tongue
Reply
#20
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 Sad 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
Reply
#21
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.
  1. Poke 8 vertical lines with 255 (a black 8x8 square)
  2. Like above but at any coord at (Y = 0, X = 0 .. 31)
  3. Like the above but at any coord at (Y = 0..7, X = 0..31) < = This is first 3rd screen
  4. Like the above but in any coord at (y = 8..15, x = 0, 31) <= This is the second screen "third"
  5. Like the above but in any coord. You got it.
  6. Add Attrs Smile
Reply
#22
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
Reply
#23
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 Wink
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. Wink
Experiment with this now, and try again. If not, we'll keep working on it.
Reply
#24
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
Reply
#25
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
Reply
#26
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 Wink 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.
Reply
#27
yes that is what I need for sprites,

but I am trying to do some scrolling

got it to work Big Grin
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
Reply
#28
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?!?!
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)