Author Topic: Newbie screen address question  (Read 4989 times)

0 Members and 1 Guest are viewing this topic.

Offline scheeba

  • CPC464
  • **
  • Posts: 26
  • hapless codephobe
  • Liked: 0
  • Likes Given: 0
Newbie screen address question
« on: 16:38, 28 January 10 »
Hi all, was wondering if anyone here could help me out.

I'm using z88dk with cpcrslib, and can get sprites loaded and displaying at 0xc000 just fine. However I'm trying to get them to display at other x/y co-ordinates and I'm having trouble figuring out how the screen address is calculated on the cpc. Any tips on how to convert an x,y co-ordinate on a mode1 screen into an address would be great, thanks.
« Last Edit: 16:42, 28 January 10 by scheeba »

Offline Bryce

  • The Hardware Guy.
  • Supporter
  • 6128 Plus
  • *
  • Posts: 11.795
  • Country: wf
  • It's not broken, it just hasn't been fixed yet.
    • index.php?action=treasury
  • Liked: 4249
  • Likes Given: 446
Re: Newbie screen address question
« Reply #1 on: 16:47, 28 January 10 »
Have you read this.....

http://www.cpcwiki.eu/index.php/Programming:Fast_Sprites

There might be some tips for you in there.

Bryce.

Offline scheeba

  • CPC464
  • **
  • Posts: 26
  • hapless codephobe
  • Liked: 0
  • Likes Given: 0
Re: Newbie screen address question
« Reply #2 on: 16:59, 28 January 10 »
I read that and a couple of other bits n' pieces, but it all seems well above my level as well as being much more than I need. I just need some reference I can use when placing images onscreen, I don't need to calculate movement or anything. Thanks anyway though.

Offline Grim

  • CPC6128
  • ****
  • Posts: 202
  • Country: gp
  • La pak ba, bèf ka pasé
    • THERE IS NO GAME
  • Liked: 133
  • Likes Given: 67
Re: Newbie screen address question
« Reply #3 on: 17:00, 28 January 10 »
The screen address can be calculated with something like this:
Quote
screenaddr = screenbase + (y AND 7)*&800 + int(y/8)*2*R1 + int(x/M)
with:
x and y your 2D coordinate.

R1 = value of CRTC register 1 (eg. 40 with the default firmware settings)

M = 2 (for screen mode 0, 2 pixels/byte)
M = 4 (for screen mode 1, 4 pixels/byte)
M = 8 (for screen mode 2, 8 pixels/byte)

screenbase = &C000

Offline robcfg

  • Supporter
  • 6128 Plus
  • *
  • Posts: 2.337
  • Country: se
  • 8-Bit Technomancer
    • index.php?action=treasury
  • Liked: 1043
  • Likes Given: 2515
Re: Newbie screen address question
« Reply #4 on: 17:01, 28 January 10 »
As far as I remember, the screen memory in the cpc is not linear, but character based.

Each character has 8 rows, so the first 80 bytes of memory would be 320 pixels in mode 1, that is the first scanline of the first row of characters. Then, instead of the second scanline of the 1st row of characters, you have the 1st scanline of the second line of characters, and so on until the you get the 1st scanline of the 8th line of characters. Then you begin with the second scanline of the 1st line of chars, etc...

I also think that every 2000 bytes, you have to jump to the next 2048 byte block.

Also, in mode 1 there are 4 pixels packed in one byte, so you'll have to get the byte where the pixel is and unpack it to get the value or pack it to modify the value.

Maybe I'm mistaken, but that's what I remember, hope this helps.

Offline scheeba

  • CPC464
  • **
  • Posts: 26
  • hapless codephobe
  • Liked: 0
  • Likes Given: 0
Re: Newbie screen address question
« Reply #5 on: 17:09, 28 January 10 »
That's certainly enough for me to start playing around with things some more, thanks for the advice all!

Offline robcfg

  • Supporter
  • 6128 Plus
  • *
  • Posts: 2.337
  • Country: se
  • 8-Bit Technomancer
    • index.php?action=treasury
  • Liked: 1043
  • Likes Given: 2515
Re: Newbie screen address question
« Reply #6 on: 17:30, 28 January 10 »
I put up a little example to clarify things a bit:

Code: [Select]
10 MODE 1
20 FOR L=0 to 7
30 color=255
35 IF (L mod 2) = 1 THEN color=240
40 POKE &C000+(2048*L),color
50 NEXT L
60 GOTO 60

Pixels in mode 1 are stored as 2 bit values within a byte. The byte look like this "12341234", being the 1st bit and the 5th bit the two bits of the first point, and so on. The value of the 2 bits indicate the pen (or ink, I don't remember well) that should be used to paint the pixel.

Try to change the 240 for a 53, you'll see the four colors together.
« Last Edit: 17:32, 28 January 10 by robcfg »

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Newbie screen address question
« Reply #7 on: 17:39, 28 January 10 »
I think of the CPC video RAM as being arranged as a venetian blind - if you watch this video of a tape loading on the 464 you can see how the memory is arranged by watching the picture being loaded into &C000.

To work out the next character line on the screen, you add &50 to the screen address (so &C000 becomes &C050).  To work out the next pixel line down on the screen, you add &800 (so &C000 becomes &C800).  You then just need something to work out if the address needs to be wrapped around to the start again if the addition goes over &FFFF (as the screen RAM is &C000 to &FFFF).

This routine will plot a simple sprite for you and is easy to understand:

Code: [Select]
org &8000

ld hl,&4000 ;address of sprite data
ld de,&C000 ;screen address
ld b,32 ;height in pixels
ld c,8 ;width in bytes
call sprite
ret

.sprite
push de           ;preserve screen address
push bc           ;and the dimensions
ld b,0               ;BC now equals the width in bytes
ldir                   ;copy the bytes!
pop bc             ;get back the dimensions
pop de             ;and then the screen address

ex de,hl           ;to move DE onto the next screen line,
call nline          ;we use the routine to move HL onto the
ex de,hl           ;the next line, but swap DE and HL either side
djnz sprite       ;if not all lines done, loop back
ret

.nline: 
ld a,8
add a,h           ;add 8 to H (the high byte)
ld h,a
ret nc             ;return if no overflow
push de         ;otherwise preserve DE
ld de,&C050
add hl,de       ;add &C000+&50 to HL
pop de           ;and get DE back again!
ret

Offline scheeba

  • CPC464
  • **
  • Posts: 26
  • hapless codephobe
  • Liked: 0
  • Likes Given: 0
Re: Newbie screen address question
« Reply #8 on: 20:05, 28 January 10 »
Outstanding, cheers. My sprites seem to be doing what I tell them to at last, it's all starting to make sense now. I'll get back to work then, +rep for all ;D

Offline mr_lou

  • 6128 Plus
  • ******
  • Posts: 3.136
  • Country: dk
    • index.php?action=treasury
    • 8-bit Memoirs - a Blu-ray diskmag-like eBook about the 8-bit era
  • Liked: 1300
  • Likes Given: 2595
Re: Newbie screen address question
« Reply #9 on: 11:50, 31 January 10 »
Outstanding, cheers. My sprites seem to be doing what I tell them to at last, it's all starting to make sense now. I'll get back to work then, +rep for all ;D

Are you coding a game? Lemme know when you need music.  ;)

Offline fano

  • Supporter
  • 6128 Plus
  • *
  • Posts: 835
  • Country: fr
  • Easter Egg Programmer
    • Easter Egg
  • Liked: 278
  • Likes Given: 614
Re: Newbie screen address question
« Reply #10 on: 14:11, 31 January 10 »
Are you coding a game? Lemme know when you need music.
off topic but I may need you.Have a bit of free time for me ?
"NOP" is the perfect program : short , fast and (known) bug free

Follow Easter Egg products on Facebook !

Offline scheeba

  • CPC464
  • **
  • Posts: 26
  • hapless codephobe
  • Liked: 0
  • Likes Given: 0
Re: Newbie screen address question
« Reply #11 on: 16:20, 31 January 10 »
Are you coding a game? Lemme know when you need music.  ;)
Cool, thanks for the offer, I may well take you up on that! I'm playing with a couple of game ideas at the moment, I'll post some shots and stuff once I have some decent mockups and/or prototypes together. It'll probably be a little while until I have anything good to go, but I'll definitely need some help with the soundtrack.

Offline Ygdrazil

  • Global Moderator
  • 464 Plus
  • *****
  • Posts: 491
  • Country: dk
  • Liked: 48
  • Likes Given: 257
Re: Newbie screen address question
« Reply #12 on: 16:29, 31 January 10 »
Screenaddreses on the CPC can be quite confusion in the beginning but when been doing a while its real simple!!  ;D

Looking forward to som nice sprites moving around!

/Ygdrazil

Cool, thanks for the offer, I may well take you up on that! I'm playing with a couple of game ideas at the moment, I'll post some shots and stuff once I have some decent mockups and/or prototypes together. It'll probably be a little while until I have anything good to go, but I'll definitely need some help with the soundtrack.

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Newbie screen address question
« Reply #13 on: 07:26, 01 February 10 »
The problem with this sprite routine (working demo below) is that when you use it to plot across the screen memory boundary (i.e. it goes over &FFFF and loops back round) the first pixel line of the sprite at the top of the screen is not printed due to the nline routine simply adding &C050, and not looking for &FFFF+1 (&0000) and if true just adding &C000.  This, of course, creates problems when you've been scrolling the screen...

Can anyone suggest a better way of doing the nline routine to accommodate this...?

Code: [Select]
org &8000

ld a,0 ;mode 0
call &BC0E
ld bc,0 ;border 0
call &BC38

ld hl,&B90C ;address of sprite data (use firmware area just to plot something to see)
ld de,&C380 ;screen address
ld b,160 ;height in pixels
ld c,4 ;width in bytes
call sprite

caLL &BB18 ;wait for keypress

ret

sprite: push de         ;preserve screen address
        push bc         ;and the dimensions
        ld b,0          ;BC now equals the width in bytes
        ldir            ;copy the bytes!
        pop bc          ;get back the dimensions
        pop de          ;and then the screen address

        ex de,hl        ;to move DE onto the next screen line,
        call nline      ;we use the routine to move HL onto the
        ex de,hl        ;the next line, but swap DE and HL either side
        djnz sprite     ;if not all lines done, loop back
        ret

nline:  ld a,8
        add a,h         ;add 8 to H (the high byte)
        ld h,a
        ret nc          ;return if no overflow

        push de         ;otherwise preserve DE
        ld de,&C050
        add hl,de       ;add &C000+&50 to HL
        pop de          ;and get DE back again!
        ret

Offline Axelay

  • 6128 Plus
  • ******
  • Posts: 585
  • Country: au
  • Liked: 383
  • Likes Given: 87
Re: Newbie screen address question
« Reply #14 on: 12:49, 01 February 10 »
The problem with this sprite routine (working demo below) is that when you use it to plot across the screen memory boundary (i.e. it goes over &FFFF and loops back round) the first pixel line of the sprite at the top of the screen is not printed due to the nline routine simply adding &C050, and not looking for &FFFF+1 (&0000) and if true just adding &C000.  This, of course, creates problems when you've been scrolling the screen...

Can anyone suggest a better way of doing the nline routine to accommodate this...?


When you cross that screen end boundary you are also crossing the &800 byte boundary that makes up each set of 8 horizontal pixel lines in each character row.  If the screen has been scrolled so byte &ffff is visible, the byte below it should be &c04f on a 40 character wide screen, so instead of adding &c050, it should be &b850, as nline always adds &800 to start with.  Otherwise, you will skip one pixel line as you describe.

Here's that code with nline modified, but it might be wrong, it can probably be done better, but it seemed to work!

Code: [Select]
org &8000

;   ld a,0      ;mode 0
;   call &BC0E
   ld bc,0      ;border 0
   call &BC38

   ld hl,&B90C   ;address of sprite data (use firmware area just to plot something to see)
   ld de,&C380   ;screen address
   ld b,160   ;height in pixels
   ld c,4      ;width in bytes
   call sprite
   
   caLL &BB18   ;wait for keypress

   ret

sprite: push de         ;preserve screen address
        push bc         ;and the dimensions
        ld b,0          ;BC now equals the width in bytes
        ldir            ;copy the bytes!
        pop bc          ;get back the dimensions
        pop de          ;and then the screen address

        ex de,hl        ;to move DE onto the next screen line,
        call nline      ;we use the routine to move HL onto the
        ex de,hl        ;the next line, but swap DE and HL either side
        djnz sprite     ;if not all lines done, loop back
        ret

nline:  ld a,8
        add a,h         ;add 8 to H (the high byte)
        ld h,a
        ret nc          ;return if no overflow

        push de         ;otherwise preserve DE

        ld a,&07        ; if hl was &ffb0 or higher
        cp h            ; before &800 was added
        jr nz,NextLine  ; it was the last char line
        ld a,&af        ; when screen is 40 chars wide
        cp l
        jr nc,NextLine
        ld de,&c050-&800 ; if it was the last char line
        jr TopLine       ; subtract the &800 already added
.NextLine
        ld de,&c050
.TopLine

        add hl,de       ;add &C000+&50 to HL
        pop de          ;and get DE back again!
        ret

However, if you are h/w scrolling the screen you also need to consider that those 8 separate &800 byte pixel rows all loop.  As far as the video h/w is concerned, the byte to the right of &c7ff is &c000 for example, the LDIR used here is going to drop down a pixel row by moving to &c800.

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2274
  • Likes Given: 3478
Re: Newbie screen address question
« Reply #15 on: 15:46, 01 February 10 »
The problem with this sprite routine (working demo below) is that when you use it to plot across the screen memory boundary (i.e. it goes over &FFFF and loops back round) the first pixel line of the sprite at the top of the screen is not printed due to the nline routine simply adding &C050, and not looking for &FFFF+1 (&0000) and if true just adding &C000.  This, of course, creates problems when you've been scrolling the screen...

Can anyone suggest a better way of doing the nline routine to accommodate this...?

Code: [Select]
org &8000

ld a,0 ;mode 0
call &BC0E
ld bc,0 ;border 0
call &BC38

ld hl,&B90C ;address of sprite data (use firmware area just to plot something to see)
ld de,&C380 ;screen address
ld b,160 ;height in pixels
ld c,4 ;width in bytes
call sprite

caLL &BB18 ;wait for keypress

ret

sprite: push de         ;preserve screen address
        push bc         ;and the dimensions
        ld b,0          ;BC now equals the width in bytes
        ldir            ;copy the bytes!
        pop bc          ;get back the dimensions
        pop de          ;and then the screen address

        ex de,hl        ;to move DE onto the next screen line,
        call nline      ;we use the routine to move HL onto the
        ex de,hl        ;the next line, but swap DE and HL either side
        djnz sprite     ;if not all lines done, loop back
        ret

nline:  ld a,8
        add a,h         ;add 8 to H (the high byte)
        ld h,a
        ret nc          ;return if no overflow

        push de         ;otherwise preserve DE
        ld de,&C050
        add hl,de       ;add &C000+&50 to HL
        pop de          ;and get DE back again!
        ret

next line: (also works for screen at &40)
Code: [Select]
ld a,h
add a,8
ld h,a
and &38
ret nz
ld a,h
and &c0
ld c,a
ld a,l
add a,&50
ld l,a
ld a,h
adc a,&8
and 7
or c
ld h,a
ret

only need to do this every 2 bytes:
Code: [Select]
next_byte:
inc hl
ld a,h
and &7
ret nz
or l
ret nz
ld a,h
sub 8
ld h,a
ret
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Newbie screen address question
« Reply #16 on: 16:35, 01 February 10 »
Here's that code with nline modified, but it might be wrong, it can probably be done better, but it seemed to work!

This is along the lines of what I was thinking and a good example of a fix - thanks!

However, if you are h/w scrolling the screen you also need to consider that those 8 separate &800 byte pixel rows all loop.  As far as the video h/w is concerned, the byte to the right of &c7ff is &c000 for example, the LDIR used here is going to drop down a pixel row by moving to &c800.

Yes, I have come across this when I was using the hardware scrolling.  My thinking was that after scrolling the screen around once totally you have &C000 as the top line again, and if you start writing to &C800+ you are printing 1 pixel line down.  Therefore, I look to see if the screen address has reached &C800 and then reset it to &C000 if true:

Code: [Select]
inc de ;increase screen address by 1 byte for next time

ld a,d ;load a with high byte of screen address
and &c8 ;is it &C8 yet (we have scrolled right round as &C800 is one pixel line down)
jp pe,notyet ;no, not yet so jump to update screen address else...
ld de,&c000 ;yes, so reset screen address to &C000
ld (scrloc),de ;store it in pointer
jp main_loop ;do the loop again

notyet: ld (scrloc),de ;store increased screen location

jp main_loop ;loop round again

This works in my example, where I am scrolling the whole screen and printing a sprite on the right far line every time.
« Last Edit: 18:53, 01 February 10 by redbox »

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Newbie screen address question
« Reply #17 on: 16:36, 01 February 10 »
only need to do this every 2 bytes:

I would like to try this code too - which two bytes do you need to call this routine after?

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2274
  • Likes Given: 3478
Re: Newbie screen address question
« Reply #18 on: 16:50, 01 February 10 »
I would like to try this code too - which two bytes do you need to call this routine after?

Well it works like this:

generic code for drawing a box to screen (no transparency/mask):
Code: [Select]
ld de,sprite
ld hl,scr_addr
ld b,16 ;; height
ld c,8 ;; width
height_loop: push bc
push hl
width_loop: ld a,(de)   ;; pixel data
ld (hl),a ;; screen
inc de
call next_byte
dec c
jr nz,width_loop
pop hl
call next_line
pop bc
djnz height_loop

if the screen address is even (e.g. bit 0 is 0) then you can do this:

Code: [Select]
.
.
.

ld a,(de)
ld (hl),a
inc de
inc l
ld a,(de)
ld (hl),a
inc de
call next_byte
.
.
.

if the screen address is odd then you can do this:

Code: [Select]
.
.
.
ld a,(de)
ld (hl),a
inc de
call next_byte
ld a,(de)
ld (hl),a
inc l
.
.
.

So what this basically means is that you don't have to call next_byte for ever byte you write to the screen. if you know your screen address is odd/even and you want to unroll your sprite drawing code you can choose the specific routine to call and this ends up being a bit faster.

also, because the screen is effectively scrolled 2 bytes at a time this works fine.

You will find that if you use the next_byte for every byte you plot, drawing software sprites like this on a hardware scrolling screen eats up cpu time.

The next_byte routine works but as you can see is not fast :(

Drawing sprites, and even drawing sprites on a hardware scrolling screen is not necessarily fast :(

My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Newbie screen address question
« Reply #19 on: 18:05, 01 February 10 »
So what this basically means is that you don't have to call next_byte for ever byte you write to the screen. if you know your screen address is odd/even and you want to unroll your sprite drawing code you can choose the specific routine to call and this ends up being a bit faster.

Ah, I see what you mean and I understand the theory of this routine now.

However, when I combine it all into one piece of code it doesn't work...:

Code: [Select]

org &8000

ld a,0      ;mode 0
call &BC0E
ld bc,0      ;border 0
call &BC38

ld de,sprite
ld hl,scr_addr
ld b,160 ;; height
ld c,4 ;; width

height_loop: push bc
push hl
width_loop: ld a,(de)    ;; pixel data
ld (hl),a ;; screen
inc de
call next_byte
dec c
jr nz,width_loop
pop hl
call next_line
pop bc
djnz height_loop

call &BB18

ret

next_byte: inc hl
ld a,h
and &7
ret nz
or l
ret nz
ld a,h
sub 8
ld h,a
ret

next_line: ld a,h
add a,8
ld h,a
and &38
ret nz
ld a,h
and &c0
ld c,a
ld a,l
add a,&50
ld l,a
ld a,h
adc a,&8
and 7
or c
ld h,a
ret

sprite: dw &B90C
scr_addr: dw &C380

Offline arnoldemu

  • Supporter
  • 6128 Plus
  • *
  • Posts: 5.336
  • Country: gb
    • Unofficial Amstrad WWW Resource
  • Liked: 2274
  • Likes Given: 3478
Re: Newbie screen address question
« Reply #20 on: 11:46, 02 February 10 »
Ah, I see what you mean and I understand the theory of this routine now.

However, when I combine it all into one piece of code it doesn't work...:
what does it do? the width is in bytes (so this means sprites are multiple of 4 pixels in mode 1, multiple of 2 pixels in mode 0 and multiple of 8 pixels in mode 2).

The code looks correct to me.
My games. My Games
My website with coding examples: Unofficial Amstrad WWW Resource

Offline redbox

  • Supporter
  • 6128 Plus
  • *
  • Posts: 1.751
  • Country: gb
    • redbox
  • Liked: 326
  • Likes Given: 267
Re: Newbie screen address question
« Reply #21 on: 22:01, 02 February 10 »
The code looks correct to me.

It was calculating the next character (not pixel) row down incorrectly.  I will go through it and try to see what the problem is.