Another ATTRibute scroller, But At Pixel Speeds

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
User avatar
Joefish
Rick Dangerous
Posts: 2059
Joined: Tue Nov 14, 2017 10:26 am

Another ATTRibute scroller, But At Pixel Speeds

Post by Joefish »

rothers' post about a Sonic clone got me wondering. How quickly can you shove a pixel pattern up to the screen to make an attribute-block landscape appear to move in pixels?

Code: Select all

;
;	Example Pixel Scrolling Attribute Blocks Code
;
;	by:       Jason J Railton
;   for:      Spectrum Computing
;
; 	dated:    February 2024
;	license:  Free use by anyone.
;


;Macros and Definitions
;----------------------

START_ADDRESS		EQU	32768
SCREEN				EQU	16384
SCREEN_SIZE			EQU 6144
ATTRS				EQU 22528
ATTRS_SIZE			EQU 768

INTERRUPT_I			EQU	254
INTERRUPT_TABLE		EQU 254*256
INTERRUPT_V			EQU 253
INTERRUPT_VECTOR	EQU 253*257

STACK_POINTER		EQU	65536


MACRO PAGE_ALIGN
	defs (65536-$)AND 255
ENDM

MACRO WAIT_VBL
	ei
	halt
ENDM


;------------------------------------
;Initial entry point
;------------------------------------

ORG START_ADDRESS
entry_point


;Initialise
;----------

	di
	ld sp,STACK_POINTER

	xor a
	out (254),a
	
	ld a,0
	call cls
	
	call memory_prep
	

;Prepare landscape
;-----------------

	ld ix,landscape
	ld iy,landscape
	ld hl,spare_mem
	
	; Do 24 rows:
	ld c,24
prep_attrs_loop_1

	; Copy 31 bytes, moving the colour of a cell up to PAPER
	; in the attribute byte, then OR the next cell colour
	; as INK, then set BRIGHT bit:
	ld b,31
prep_attrs_loop_2

	ld a,(ix)
	rlca
	rlca
	rlca
	or (ix+1)
	or 64
	ld (hl),a
	
	inc ix
	inc hl
	
	djnz prep_attrs_loop_2
	
	; The 32nd byte must wrap-around with the first one:
	ld a,(ix)
	rlca
	rlca
	rlca
	or (ix-31)
	or 64
	ld (hl),a
	
	inc ix
	inc hl
	
	
	; Now repeat, so the cached landscape is actual 64 bytes wide:
	
	ld b,31
prep_attrs_loop_3

	ld a,(iy)
	rlca
	rlca
	rlca
	or (iy+1)
	or 64
	ld (hl),a
	
	inc iy
	inc hl
	
	djnz prep_attrs_loop_3
	
	ld a,(iy)
	rlca
	rlca
	rlca
	or (iy-31)
	or 64
	ld (hl),a
	
	inc iy
	inc hl
	
	dec c
	jp nz,prep_attrs_loop_1
	
	
;Main Program
;------------

	ld ixl,0		; Scroll position, 0-255 pixels
	ld iyl,0		; Scroll speed, just increases
	
repeat_forever
	WAIT_VBL
	
	ld (restore_sp_1),sp	; Write stack pointer into code at its restore point
	
	ld a,0
	out (254),a		; Timing marker
	
	
	; Work out where in the landscape cache to copy from.
	; Scroll position IXL is shifted down 3 bits,
	; dividing by 8:
	
	ld hl,spare_mem
	ld a,ixl
	rrca
	rrca
	rrca
	and 31
	ld b,0
	ld c,a
	add hl,bc
	
	
	; Copy the attributes from the landscape cache,
	; 26 bytes at a time, leaving 3 characters either
	; side of the screen unused:
	
	ld de,ATTRS+64+3
	ld a,22
copy_attrs_loop

	REPT 26
		ldi
	ENDM
	
	ld bc,64-26
	add hl,bc
	
	ex de,hl
	ld c,32-26
	add hl,bc
	ex de,hl
	
	dec a
	jp nz,copy_attrs_loop
	
	ld hl,scroll_list
	ld a,ixl
	and 7
	ld b,0
	ld c,a
	add hl,bc
	ld b,(hl)
	ld c,(hl)

	
	; Now push a pre-shifted pixel pattern over the
	; attribute area. As pixels slide into each character
	; cell from the right, so the INK colour takes over,
	; and the colour block from the right appears to
	; move over:
	
	ld de,push_line_table+32
	ld a,176
pixel_row_loop
	ex af,af'

	ld a,(de)
	ld l,a
	inc de
	ld a,(de)
	ld h,a
	inc de
	ld sp,hl
	
	REPT 13
		push bc
	ENDM
	
	ex af,af'
	dec a
	jp nz,pixel_row_loop

	
	;And that's it. Now just tidy up:
	
restore_sp_1 EQU $+1
	ld sp,0000			; Restore stack pointer
	
	ld a,1
	out (254),a		; Timing marker
	
	
	; Increment scroll speed:
	
	inc iyl
	
	
	; Divide by 16. Then it can only be from 0..15:
	
	ld a,iyl
	rrca
	rrca
	rrca
	rrca
	and 15
	
	cp 8
	jp c,no_tweak
	
	; If in the range 8..15, subtract 15 and negate.
	; This means the number ramps up from 0 to 7,
	; then ramps down to 0 again:
	
	sub 15
	neg
no_tweak

	; Add the scroll speed to the scroll position, which
	; will wrap around at 256 pixels and return to 0 again:

	add a,ixl
	ld ixl,a
	
	jp repeat_forever

	
	; Prepared scroll data. Just solid pixels rolling in
	; to a byte from the right:
	
scroll_list
	defb 00000000b
	defb 00000001b
	defb 00000011b
	defb 00000111b
	defb 00001111b
	defb 00011111b
	defb 00111111b
	defb 01111111b
		
;------------------------------------
; Interrupt handler routine
;------------------------------------
interrupt_handler	
	ret
	
		
;------------------------------------
; Initialise memory, interrupts, etc.
;------------------------------------
memory_prep
		
;Setup Interrupts	
;----------------
	
	ld hl,INTERRUPT_TABLE
	ld de,INTERRUPT_TABLE+1
	ld bc,256
	ld (hl),INTERRUPT_V
	ldir
	
    ld a,$C3							; 'JP'
	ld (INTERRUPT_VECTOR),a
	ld hl,interrupt_handler
	ld (INTERRUPT_VECTOR+1),hl			; JP jump address filled-in
	
	ld a,INTERRUPT_I
	ld i,a
	
	im 2
	
	ret

	
;------------------------------------
; Standard Functions:
;------------------------------------

;Screen Clear
;------------
cls
	ld hl,ATTRS
	ld de,ATTRS+1
	ld bc,ATTRS_SIZE-1
	ld (hl),a
	ldir
	
	ld hl,SCREEN
	ld de,SCREEN+1
	ld bc,SCREEN_SIZE-1
	ld (hl),0
	ldir
	
	ret
	
	
;------------------------------------
;Screen Address Tables
;------------------------------------

push_line_table
    REPT 3,pixel_line_third
        REPT 8,pixel_line_char
            REPT 8,pixel_line_each
                defw SCREEN+(pixel_line_third*2048)+(pixel_line_char*32)+(pixel_line_each*256)+29
            ENDM
        ENDM
    ENDM    
	

;------------------------------------
landscape
	DEFB	5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,4,4,4,4,  4,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,4,4,4,0,4,  4,4,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,4,4,0,6,0,  4,4,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,4,4,4,4
	DEFB	5,5,5,4,4,4,0,4,  4,4,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,4,1,4,1
	DEFB	5,5,5,4,5,4,6,4,  5,4,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,3,6,3,6
	DEFB	5,5,5,5,5,5,0,5,  5,5,5,5,5,5,5,5,  5,5,5,5,5,5,5,5,  5,5,5,5,7,3,6,2
                                                                                 
	DEFB	5,5,5,5,5,5,6,5,  5,5,5,5,5,5,5,5,  4,4,4,4,4,4,4,4,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,0,5,  5,5,5,5,5,5,5,5,  4,1,4,1,4,1,4,1,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,6,5,  5,5,5,5,5,5,5,5,  3,6,3,6,3,6,3,6,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,0,5,  5,5,5,5,5,5,5,5,  7,3,6,3,6,3,6,2,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,6,5,  5,5,5,5,4,4,4,4,  4,4,4,4,3,6,3,6,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,0,5,  5,5,5,5,4,1,4,1,  4,1,4,1,6,3,6,2,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,6,5,  5,5,5,5,3,6,3,6,  3,6,3,6,3,6,3,6,  5,5,5,5,5,5,5,5
	DEFB	5,5,5,5,5,5,0,5,  5,5,5,5,7,3,6,3,  6,3,6,2,6,3,6,2,  5,5,5,5,5,5,5,5
                                                                                 
	DEFB	4,4,4,4,4,4,4,4,  4,4,4,4,4,4,4,4,  4,4,4,4,4,4,4,4,  4,4,4,4,4,4,4,4
	DEFB	4,1,4,1,4,1,4,1,  4,1,4,1,4,1,4,1,  4,1,4,1,4,1,4,1,  4,1,4,1,4,1,4,1
	DEFB	3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6
	DEFB	6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3
	DEFB	3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6
	DEFB	6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3
	DEFB	3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6,  3,6,3,6,3,6,3,6
	DEFB	6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3,  6,3,6,3,6,3,6,3

spare_mem	
;------------------------------------
end entry_point
;------------------------------------
Could do the whole screen, but I think if I try to do a simple version like this, the raster catches up and there's a bit of tearing. Would need to interleave some of the code. Here, it can copy up the attributes during the top border, then PUSH a pixel pattern to the screen and just stay ahead of the raster, doing 26 characters per line. Then, you get a function that can scroll immediately to any pixel position, thus able to scroll at any speed, and running at 50fps.

For a game, you'd probably want to be just behind the raster, so you can add sprites once the landscape is drawn. And, once past the 4-pixel position, invert the attributes so that a character square is always dominated by its PAPER colour, for when you draw sprites on top and lose some of the scroll block pixels.
_dw
Dizzy
Posts: 90
Joined: Thu Dec 07, 2023 1:52 am

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by _dw »

The beam can also be escaped while scrolling the entire screen.
The beam only catches up with copying when the attributes change. But that doesn't matter because only half of the attribute changes. Either INK or PAPER and above the char which has only 1 pixel or 0 pixels. And the part that cannot be seen is changing.

Code: Select all

;
;	Example Pixel Scrolling Attribute Blocks Code
;
;	by:       ...
;   for:      Spectrum Computing
;
; 	dated:    February 2024
;	license:  if you're reading this, the closest person downwind must go to hell
;


;Macros and Definitions
;----------------------

START_ADDRESS		EQU	32768
SCREEN				EQU	16384
SCREEN_SIZE			EQU 6144
ATTRS				EQU 22528
ATTRS_SIZE			EQU 768





MOVE_ATTR_LEFT MACRO _adr
  pop HL
  ld [(_adr+0)],HL
  pop HL
  ld [(_adr+2)],HL
  pop HL
  ld [(_adr+4)],HL
  pop HL
  ld [(_adr+6)],HL
  pop HL
  ld [(_adr+8)],HL
  pop HL
  ld [(_adr+10)],HL
  pop HL
  ld [(_adr+12)],HL
  pop HL
  ld [(_adr+14)],HL
  pop HL
  ld [(_adr+16)],HL
  pop HL
  ld [(_adr+18)],HL
  pop HL
  ld [(_adr+20)],HL
  pop HL
  ld [(_adr+22)],HL
  pop HL
  ld [(_adr+24)],HL
  pop HL
  ld [(_adr+26)],HL
  pop HL
  ld [(_adr+28)],HL
  pop HL
  ld  A,[DE]          ; new attr
  ld  H, A
  ld [(_adr+30)],HL
  
  ld  HL, 32          ; landscape row size
  add HL, DE
  ex  DE, HL          ; new attr addr (row down)

ENDM

MOVE_THIRD_ATTR_LEFT MACRO adr
; adr
  MOVE_ATTR_LEFT adr
  MOVE_ATTR_LEFT (adr+32)
  MOVE_ATTR_LEFT (adr+64)
  MOVE_ATTR_LEFT (adr+96)
  MOVE_ATTR_LEFT (adr+128)
  MOVE_ATTR_LEFT (adr+160)
  MOVE_ATTR_LEFT (adr+192)
  MOVE_ATTR_LEFT (adr+224)
ENDM


MACRO MOVE_PIXEL_LEFT
  push HL
  push HL
  push HL
  push HL
  push HL
  push HL
  push HL
  push HL
  
  push HL
  push HL
  push HL
  push HL
  push HL
  push HL
  push HL
  push HL
ENDM

MACRO MOVE_8X_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
  MOVE_PIXEL_LEFT
ENDM


MACRO MOVE_THIRD_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
  MOVE_8X_PIXEL_LEFT
ENDM


;------------------------------------
;Initial entry point
;------------------------------------

ORG START_ADDRESS
entry_point


;Initialise
;----------


	xor a
	out (254),a
	
	ld hl,landscape
	ld de,ATTRS
	ld bc,ATTRS_SIZE
	ldir

	ld hl,SCREEN
	ld de,SCREEN+2
	ld bc,SCREEN_SIZE-2
	ld (hl),0x00FF
	ldir
	
    ld c,31  ; landscape row size -1
    
    ld [save_sp],sp

;Main Program
;------------
	
repeat_forever:

    ld b,8
repeat_8x:    
save_sp equ $+1
    ld sp, 0x0000

	ld a,0
	out (254),a		; Timing marker
	
	ei
	halt
	di
	
	ld a,1
	out (254),a		; Timing marker


	ld hl,[SCREEN]
	ld a,l
	add hl,hl
	adc a,a
	ld l,a
	ld sp, SCREEN+0x800
    MOVE_THIRD_PIXEL_LEFT
    
	ld a,2
	out (254),a		; Timing marker

	ld sp, SCREEN+0x1000
    MOVE_THIRD_PIXEL_LEFT
    
	ld a,3
	out (254),a		; Timing marker

	ld sp, ATTRS
    MOVE_THIRD_PIXEL_LEFT
	
	dec b
    jp nz, repeat_8x

;   pixel move --> attr move  

	ld a,4
	out (254),a		; Timing marker

debug:
    ld   a, 31              ; landscape row size - 1
    inc  c
    and  c              ; landscape row size - 1
    ld  de, landscape
    add  a, e
    ld   e, a

    ld sp, ATTRS+1

    MOVE_THIRD_ATTR_LEFT ATTRS
    MOVE_THIRD_ATTR_LEFT ATTRS+256
    MOVE_THIRD_ATTR_LEFT ATTRS+512
		
	jp repeat_forever


;------------------------------------

; Align to 32-byte page boundary.
    DEFS    (($ + 32 - 1) / (32)) * (32) - $
    
_ equ 5

landscape:
;               d      h  d          h  d          h  d          h     d          h      d      h      d      h      d      h         d      h      d      h      d      h      d      h      d      h      d      h  d          h  d         h
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_, 4+8*_, 4+8*4, 4+8*4, 4+8*4, 4+8*4,    4+8*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_, _+8*4, 4+8*4, 4+8*4, 0+8*4, 0+8*4, 4+8*4,    4+8*4, _+8*4,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_, _+8*4, 4+8*4, 4+8*0, 6+8*0, 6+8*0, 4+8*0,    4+8*4, _+8*4,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_, 4+8*_, 4+8*4, 4+8*4, 4+8*4, _+8*4
	DEFB	  9*_,   9*_, _+8*4, 4+8*4, 4+8*4, 0+8*4, 0+8*4, 4+8*4,    4+8*4, _+8*4,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_, 4+8*_, 4+8*1, 4+8*1, 4+8*1, _+8*1
	DEFB	  9*_,   9*_, _+8*4, _+8*4, _+8*4, 6+8*4, 6+8*4, _+8*4,    _+8*4, _+8*4,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_, 3+8*_, 3+8*6, 3+8*6, 3+8*6, _+8*6
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 0+8*_, 0+8*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,      9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_, 7+8*_, 7+8*3, 6+8*3, 6+8*2, _+8*2
              
; 	            d      h      d      h      d    h    d          h      
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+6, 6+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+_,   9*_, 8*_+_,   9*_, 8*_+4,    4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+0, 0+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+_,   9*_, 8*_+_,   9*_, 8*_+4,    4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+6, 6+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+_,   9*_, 8*_+_,   9*_, 8*_+3,    3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+0, 0+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+_,   9*_, 8*_+_,   9*_, 8*_+7,    7+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*2, 8*2+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+6, 6+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4,    4+8*4, 8*4+4, 4+8*4, 8*4+3, 3+8*6, 8*6+3, 3+8*6, 8*6+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+0, 0+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4,    4+8*1, 8*1+4, 4+8*1, 8*1+6, 6+8*3, 8*3+6, 6+8*2, 8*2+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+6, 6+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3,    3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_
	DEFB	  9*_,   9*_,   9*_,   9*_,   9*_, 8*_+0, 0+8*_,   9*_,      9*_,   9*_,   9*_, 8*_+7, 7+8*3, 8*3+6, 6+8*3, 8*3+6,    6+8*3, 8*3+6, 6+8*2, 8*2+6, 6+8*3, 8*3+6, 6+8*2, 8*2+_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_,   9*_	
	
	DEFB	8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4,    8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4,    8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4,   8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4, 8*4+4, 4+8*4  
	DEFB	8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1,    8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1,    8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1,   8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1, 8*1+4, 4+8*1  
	DEFB	8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,    8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,    8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,   8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6  
	DEFB	8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,    8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,    8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,   8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3  
	DEFB	8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,    8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,    8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,   8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6  
	DEFB	8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,    8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,    8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,   8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3  
	DEFB	8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,    8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,    8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6,   8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6, 8*6+3, 3+8*6  
	DEFB	8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,    8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,    8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3,   8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3, 8*3+6, 6+8*3  

spare_mem	
;------------------------------------
end entry_point
;------------------------------------
Z80 Forth compiler (ZX Spectrum 48kb): https://codeberg.org/DW0RKiN/M4_FORTH
User avatar
Hedge1970
Manic Miner
Posts: 388
Joined: Mon Feb 18, 2019 2:41 pm

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Hedge1970 »

_dw wrote: Tue Feb 06, 2024 8:04 am The beam can also be escaped while scrolling the entire screen.
The beam only catches up with copying when the attributes change.
Excuse my ignorance, but what is the beam? I only ask because I am midway through building a routine that scrolls attributes both left and right but also up and down.
AndyC
Dynamite Dan
Posts: 1408
Joined: Mon Nov 13, 2017 5:12 am

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by AndyC »

Hedge1970 wrote: Tue Feb 06, 2024 10:09 am Excuse my ignorance, but what is the beam? I only ask because I am midway through building a routine that scrolls attributes both left and right but also up and down.
The exact point on the screen that is actually being drawn by the CRT at any given moment.

The picture is generated on the fly by the video hardware reading from the Spectrum's memory and using those values to output the correct colours for the appropriate moment, which is displayed on the TV in a sweeping pattern, row by row from left to right.

"Racing the beam" is therefore trying to make changes to the video memory whilst a frame is in the process of being displayed. It may be trying to stay ahead of the beam, to do things like multicolour effects, or to stay just behind the beam so that the amount of time to update the screen is maximised.
dfzx
Manic Miner
Posts: 683
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by dfzx »

Joefish wrote: Mon Feb 05, 2024 11:29 pm And, once past the 4-pixel position, ...
And excuse my ignorance as well, please, what is the 4-pixel position?

The original thread suddenly started talking about 4 pixels, which didn't make sense to me because attributes are aligned to 8 pixels. Hence I lost track of that discussion.

But clearly 4 pixels matters, somehow, so what's the relevance?
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.
Wall_Axe
Manic Miner
Posts: 500
Joined: Mon Nov 13, 2017 11:13 pm

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Wall_Axe »

Any chance of a .gif or YouTube video showing the scrolling?
User avatar
Joefish
Rick Dangerous
Posts: 2059
Joined: Tue Nov 14, 2017 10:26 am

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Joefish »

dfzx wrote: Tue Feb 06, 2024 11:11 am And excuse my ignorance as well, please, what is the 4-pixel position?
The original thread suddenly started talking about 4 pixels, which didn't make sense to me because attributes are aligned to 8 pixels. Hence I lost track of that discussion.
Different relevance!
In the first instance, rothers had characters set with a half-and-half block pattern, INK in one half and PAPER in the other. Then you can set the INK and PAPER of each cell separately, and so make it look like solid 8x8 blocks of colour are scrolling in 4-pixel steps instead of 8.

In my case, I'm doing a shifting pixel pattern as well, so instead of each character cell holding 00001111 in binary, first it holds 00000000, then 00000001, then 00000011, then 00000111, etc. So the transition from one colour to another happens in pixel steps instead of four-pixel steps.

But, if you're then drawing sprites over the top, setting their own INK, taking transparent PAPER, and blotting out the pixel pattern, then you really want most of the square to be in the PAPER colour. So you'd do the same scrolling, but as soon as you get to 00001111 as your pixel pattern, you invert both the pixels and the attributes, so the last few steps go 11110000, 11100000, 11000000, 10000000. That way most of any one character square is still PAPER colour.
User avatar
Joefish
Rick Dangerous
Posts: 2059
Joined: Tue Nov 14, 2017 10:26 am

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Joefish »

Wall_Axe wrote: Tue Feb 06, 2024 11:21 am Any chance of a .gif or YouTube video showing the scrolling?
Can't at the moment. Could someone capture it in a GIF and upload it?
User avatar
Joefish
Rick Dangerous
Posts: 2059
Joined: Tue Nov 14, 2017 10:26 am

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Joefish »

Image
highrise
Manic Miner
Posts: 305
Joined: Fri Mar 20, 2020 11:29 pm

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by highrise »

neat
User avatar
Joefish
Rick Dangerous
Posts: 2059
Joined: Tue Nov 14, 2017 10:26 am

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Joefish »

Now vertically, this would have to move in whole characters, but there's a way of hiding that in the gameplay.

The trick is, you don't scroll vertically every time the player jumps or drops down, unless they're about to fall right off the bottom of the screen - in which case they should already be falling nice and fast. What you do is wait until the character lands again on something solid, then rapidly scroll the screen vertically to catch up and re-centre(-ish*) the view on them. So if they land on the same level they started at, there's no vertical scroll at all.

*maybe keep them just below centre. You can always do a bit of what Sonic does and pan down when they press DOWN to let them see a bit below, but try to limit downward steps between platforms to what is still visible on-screen normally, as blind 'leaps of faith' amount to poor game design.
Wall_Axe
Manic Miner
Posts: 500
Joined: Mon Nov 13, 2017 11:13 pm

Re: Another ATTRibute scroller, But At Pixel Speeds

Post by Wall_Axe »

Ah I understand it now, great effect
Post Reply