Hello all!
I'm having a bit of a problem with the discontinuity that appears when you hardware scroll the CPC screen. I'm developing a CPC side scroller shoot'em up with some guys at RetroWorks (which is formed by some former CEZ GS members).
I can draw sprites and get/restore backgrounds while scrolling the screen, but when I'm painting the sprite at the last character I'm having problems. I think that my routine to calculate the memory address is not working correctly for that case.
I'm using two tables to precalculate memory addresses of the leftmost and rightmost pixels for each Y coordinate. The routine to precalculate them is working correctly. The rightmost pixel table is used to know where should I paint a new column of pixels every time I do a scroll, and it's working ok (mainly because everything is painted correctly when I scroll the screen for a long, long time). The leftmost pixel table is used to calculate the memory address of a sprite. I perform a lookup based on the Y coordinate, add to that the X coordinate (taking into account when I have passed from 7FF to 800 to correct the value).
This is what I'm using:
; H -> X coord
; L -> Y coord
.GET_SCREEN_COORD
LD C,H ;; store H coordinate for later
ld H,0 ;; H used to hold X coordinate, need to zero this out
;; because we want HL to contain the Y coordinate
ADD HL, HL ;; each element of the look-up table is 2 bytes
;; convert y position to a byte offset from the start
;; of the look up table
LD DE, SCREEN_ADDR_TABLE
ADD HL, DE ;; add start of lookup table to get address of element
;; in lookup table
LD A, (HL)
INC HL
LD H,(HL)
LD L, A ;; read element from lookup table (memory address of the start
;; of the line defined by the y coordinate)
;; hl has the start of current line. Let's check the 4th byte:
LD A, H
AND 7 ;; A holds the 0,1,2 bits before adding X coord.
LD D, A ;; D holds the 0,1,2 bits before adding X coord.
LD B, 0
ADD HL, BC ;; add on X byte coordinate
LD A, H
AND 7 ;; A holds the 0,1,2 bits after adding X coord.
RET NZ
XOR D ;; If we have passed a discontinuity, XOR will return 7 (111b)
CP 7
RET NZ
LD A, H
SUB 8 ;; Go back 0x0800 bytes.
LD H, A
;; HL = final memory address
RET
The thing is that when I have to paint a sprite which part of it is before the end of the scanline and part of it is past the end of the scanline, it's not being painted correctly.
Any hints?
I had similar problem as i am working on a similar project.To solve it i removed table and i computed like this :
for 11 first bits of HL : y/8 * line_width + x/pixel_per_byte
for bits 11,12,13 : y mod 8
for bits 14,15 : block (00/40/80/C0)
When you increment, it's like you have cycle on first 11 bits (if you understand french this is a topic that gave me a valuable help http://cpcrulez.fr/forum/viewtopic.php?f=4&t=3450&start=14 (http://cpcrulez.fr/forum/viewtopic.php?f=4&t=3450&start=14) )
For tiles , solution is simplier than using table because tiles are 8 pixels high.
fano, could you please elaborate a bit more on this? I mean, explain a little more about:
what do you mean by block for bits 14/15? Something like this?
address 00 = bits 00
address 40 = bits 01
address 80 = bits 10
address C0 = bits 11
line_width... in bytes or in pixels? Or in Horizontal Chars (Register 01 of CRTC)
I need the rightmost table, but for sprites, the leftmost table, if such a calculus works, I can ignore it, and will save some cycles updating it for each scroll increment...
Thanks!
For block this is fine.For line_width, i mean in bytes.
It could be optimized more but this is how i compute address for projectiles (1*1 pixel mode 0) in my project (uses SSCR so don't care about SCROLL_pixOffset)
;get xpos in (PROJ_xPos) and convert in byte/pixel offset
ld A,(SCROLL_pixOffset)
neg
add A,(HL) ;sub pix offset to x
or A ;clean carry
rra ;divide per 2, parity bit in carry
ld E,A ;store in E
ld A,%01010101
jr c,not_pair_pixel
rla ;shift on left (carry is supposed to be NULL)
.not_pair_pixel
ex AF,AF' ;keep pixel in AF'
xor A
ld D,A
push DE ;save DE
;get ypos in (PROJ_yPos) and convert to line address
inc L ;decimal of PROJ_xPos +2
inc L ;PROJ_yPos +3
;
ld A,(HL)
ld IYL,A ;temp store 2nop
;compute char address
and #F8
ld L,A ;already *8
ld H,0
;adr=char * 96
add HL,HL ;*2=16
push HL ;save on stack
add HL,HL ;*2=32
pop DE ;get *16 in DE
add HL,DE ;+16=48
add HL,HL ;*2=96
pop DE
add HL,DE ;add to x offset
ld DE,(SCROLL_scrOffset)
add HL,DE ;so we get offset from real screen
ld A,IYL
and 7 ;get line on char and fix wrapping bug
add A
add A
add A
res 3,H
add H
;and #3F ;add if screen_adr is not #C0
or screen_adr ;start of the screen high
ld H,A
As I am using overscan (102 bytes of width) I'm going to precalculate the y/8 * line_width part. Shouldn't be a big table, but I'll save some precious time ;)
Thank you very much!
I don't think it is incompatible w/ tables but that was more simple w/out
I think i'll use them when optimising ( plz save our little nops :D).
fano... x means World coordinate x, not screen coordinate x, isn't it?
hum... i'd say local coordinates in opposition w/ global coordinates
or
relative coordinates to camera pos like this : pos.x = obj.x - cam.x
but what is the interest to use global/world coords in a shmup ?
I add scroll offset (CRTC r12/13 offset *2) to be more clear :
for 11 first bits of HL (cyclic) : (y/8 * line_width + x/pixel_per_byte) + (CRTC_offset *2)
Finally, I managed to make it!! Thank you very much, fano :)
this is great, good luck for your project ;)