News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu
avatar_redbox

Mode 3

Started by redbox, 15:00, 06 March 11

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

TFM

Quote from: sigh on 21:14, 15 April 11
A weekend start? (Not to sure how long it takes to do these things...)

Really am interested in these results!

Why don't you do a favour for us. Just define:

- Sprite size in X and Y
- Screen format (chars per line, number of lines)
- Use background of not?
- other things you have in mind...

And then we can start  :)
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

andycadley

How about:


         
  • 24*21 Mode 1 pixels or equivalent (the size of a C64 hardware sprite, which seems like an unbiased point of reference)
  • Background composed of 16*16 Mode 1 (or equivalent) tiles that sprite can pass in front of.
  • Sprites, background and tiles can be stored however is appropriate for the routine.
  • No need to support hardware scrolling.
  • Sprites and tile drawing should be generic enough that they can reasonably be used in a 64K game
Anything I've missed?

sigh

Quote from: andycadley on 22:48, 15 April 11
How about:


       
  • 24*21 Mode 1 pixels or equivalent (the size of a C64 hardware sprite, which seems like an unbiased point of reference)
  • Background composed of 16*16 Mode 1 (or equivalent) tiles that sprite can pass in front of.
  • Sprites, background and tiles can be stored however is appropriate for the routine.
  • No need to support hardware scrolling.
  • Sprites and tile drawing should be generic enough that they can reasonably be used in a 64K game
Anything I've missed?

But isn't it mode 2 thats under investigation and was the reason for this friendly contest?

andycadley

Well it was a case of Mode 2 is the fastest for drawing sprites in, so you need some submissions from all different modes to see for sure. Of course people can always submit multiple different routines for different modes (or one routine that works in all modes if they'd like).

Probably should add that everyone will share their code as as they go or at least at the end (somewhere on the wiki?) so that we can all learn some new tricks. At the end of the day, the "contest" is less interesting than getting everyone to do a bit of cpc coding, even if they've never really tried before.

TFM

Quote from: andycadley on 22:48, 15 April 11
How about:   

         
  • 28*21 Mode 2 pixels (an unbiased point of reference)
  • Background composed of 32*16 Mode 2 tiles that sprite can pass in front of.
  • Sprites, background and tiles can be stored however is appropriate for the routine.
  • No scrolling.
  • Sprites and tile drawing must be generic
Anything I've missed?

Nope! All defined! Ok, let's take that parameters.

Yes, sure we've been talking about MODE 2! So I adjusted (in your quote) the pixel to Mode 2, ok?

TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

andycadley

Quote from: TFM/FS on 01:02, 16 April 11
Yes, sure we've been talking about MODE 2! So I adjusted (in your quote) the pixel to Mode 2, ok?
Well the sprite size should be 48*21 Mode 2 pixels, but I think that was probably a typo. Other than that all sounds good to me.  :)

TFM

Quote from: andycadley on 07:20, 16 April 11
Well the sprite size should be 48*21 Mode 2 pixels, but I think that was probably a typo. Other than that all sounds good to me.  :)

Yes, right :-) ...was a long day! Let's get it started... :-)
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

sigh

So how is it all going for you programmers out there? Any news?

redbox

Quote from: sigh on 14:18, 18 April 11
So how is it all going for you programmers out there? Any news?


Fast sprites are easy:


1) 'Compile' them
2) Set your screen to 32x32 chars - can use 8-bit INC/DEC for next byte
3) Use faster LD instructions where possible - LD (HL),C is faster then LD (HL),&40 etc
4) ANDing the byte and ORing it with what you want is a quick way to mask
5) Use SET and RES to go up and down screen lines


What I'm really interested in is what TFM will do to make his MODE 2 masked sprites faster than anything else  :)

TFM

Quote from: redbox on 14:43, 18 April 11

Fast sprites are easy:


1) 'Compile' them
2) Set your screen to 32x32 chars - can use 8-bit INC/DEC for next byte
3) Use faster LD instructions where possible - LD (HL),C is faster then LD (HL),&40 etc
4) ANDing the byte and ORing it with what you want is a quick way to mask
5) Use SET and RES to go up and down screen lines


What I'm really interested in is what TFM will do to make his MODE 2 masked sprites faster than anything else  :)

Well, leave point 3 away. Sprites have been defined as generic. BTW: I'm not goint to present too quick here.
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

Axelay

This discussion got me thinking about how to do the lookup table method I've been using, but how to do it without being restricted to even lines, so I thought I'd give it a go.  I'm including the result, though it is slower by some margin.  62 scan lines to be precise.  It would be faster with the screen width reduced and height increased, and interupts disabled, but I guess that wouldnt be very generic.

Anyway, it's mode 1, but uses a method that would be identical in any mode, using a sprite with the mask included in it.  That lets the sprite use the entire palatte, no colour being used for transparent, but the end sprite is huge at 252 bytes.  I've included a disk image with the graphics and basic programs used to get the graphics into asm format.

With the source code, you can uncomment the two 'halts' to move the code execution down the screen so you can see how many scan lines each part of the code takes, the background block has the left edge designed just for this purpose.

org &8000
nolist
run start
SpriteAddr equ &200+16
BGSave equ &300
SpriteData equ &400
.SpritesYX
   defb 115,46
.SpritesYXMv
   defb 1,1
.Mode1Pal
defb &55,&5f,&4e,&54

.start
di      ;; disable interrupts
im 1     ;; interrupt mode 1 (CPU will jump to &0038 when a interrupt occrs)
ld hl,&c9fb    ;; C9 FB are the bytes for the Z80 opcodes EI:RET
ld (&0038),hl   ;; setup interrupt handler
ei      ;; re-enable interrupts
; set colours 0-3
    ld a,3
    ld hl,Mode1Pal
    call SetColours
; fill the screen with the background block
call PrintBlocks
; copy the Y address table to &100
ld hl,YTable
ld de,&100
ld bc,232
ldir
; copy the sprite to &400. Sprite is 6 bytes by 21 = 126 bytes, but contains mask data
; which doubles it's size
ld hl,Sprite
ld de,SpriteData
ld bc,252
ldir
; fill the sprite address list with dummy values for first frame
ld hl,&cfd0
ld (SpriteAddr),hl
ld hl,SpriteAddr
ld de,SpriteAddr+2
ld bc,21*2-2
ldir
.main
; wait for vsync
call FrameFlyB
; wait to start code at same time every frame
; un-comment additional halts to see entire execution
; time, though sprite will disappear during this time
halt
;halt
;halt
; set border to different blues for each stage
ld bc,&7f10
out (c),c
ld bc,&7f55
out (c),c
; restore the background from last frame
; (requires the dummy addresses for the first frame, set previously)
call RestoreBlock
ld bc,&7f10
out (c),c
ld bc,&7f44
out (c),c
; generate a new list of screen addresses based on current XY coord (in bytes)
call CalcSpriteAddr
ld bc,&7f10
out (c),c
ld bc,&7f5d
out (c),c
; save the background for the new position of the sprite
call SaveBlock
ld bc,&7f10
out (c),c
ld bc,&7f5b
out (c),c
; finaly...
call PrintSprite
; and now set border back to black
ld bc,&7f10
out (c),c
ld bc,&7f54
out (c),c
; now move the sprite about
call MoveSprite
; repeat
jr main
.RestoreBlock
; is the last time the address list is used, so SP can be used to pop the addresses
    ld (SaveSP+1),sp
    ld sp,SpriteAddr ; load SP with top of address list
    ld bc,21*&100+&ff ; ld b with 21 and c with dummy to stop ldi corrupting b
    ld hl,BGsave ; load hl with saved background data
.ResBlkLp
; restore each line by popping address of screen to de and copying back the screen data
    pop de
    ldi
    ldi
    ldi
    ldi
    ldi
    ldi
    djnz ResBlkLp
.SaveSP
    ld sp,0
    ret

.SaveBlock
; with interrupts enabled, is generally not possible unless writing code with fixed
; execution times to assume can still use SP to pop the address list without an interupt
; corrupting the address list if it happens along while reading it
; so for this example a much lousier (slower) method is used
    ld hl,SpriteAddr ; ld hl with pointer to screen address list of sprite
    ld b,21 ; height of sprite
    exx
    ld de,BGsave ; load de' with destination to back up screen data to
    exx
.SavBlkLp
; instead of pop, ugly code using a and a' to get (hl) into hl'
    ld a,(hl)
    inc l
    ex af,af'
    ld a,(hl)
    inc l
    exx
    ld h,a
    ex af,af'
    ld l,a
    ldi
    ldi
    ldi
    ldi
    ldi
    ldi
    exx
    djnz SavBlkLp
    ret
.PrintSprite
    ld ix,SpriteAddr ; use ix register to read address list this time
    ld hl,SpriteData ; hl points to sprite data, which includes the mask byte
                     ; mask precedes the data byte it relates to
    ld b,21 ; pixel lines to print
.PrnSprLp
; normally I'd use the alternate registers to work out what frame to print
; so assuming the alt registers are not available, IX is used to read address
; list instead
    ld e,(ix+0)
    inc ixl
    ld d,(ix+0)
    inc ixl ; de now has screen address for this row
; for each byte, read the screen data from (de), 'and' with (hl) and 'or' with (hl+1)
; then write back to (de)
; 1
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc de
; 2
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc de
; 3
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc de
; 4
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc de
; 5
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc de
; 6
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    djnz PrnSprLp
    ret
; YTable contains only the odd address lines, so the table fits in one 256 byte page
; however, both odd and even addresses are determined from this table
.CalcSpriteAddr
; write screen addresses with SP, possible interrupts are no problem as they are being
; written, not read
    ld (SaveSPCalc+1),sp
    ld sp,SpriteAddr+42 ; set SP to 'bottom' of address list
    ld hl,SpritesYX+1 ; set hl to point to sprite co-ord, stored y first
    exx
    ld h,1 ; base ytable is at &100, so preload h' with 1
    exx
    ld b,1 ; only 1 sprite to print
.CalcSprLoop
    ld a,(hl) ; ld x co-ord into a
    dec l
    ex af,af'
    ld a,(hl) ; ld y co-ord into a'
    add a,20 ; start from bottom of y and work up
    dec l
    exx
    ld l,a ; ld l with y co-ord
    ex af,af'
    ld b,&c0 ; if screen flipping, this value would change to base of current screen
    ld c,a   ; ld c' with x co-ord
             ; now bc' contains what needs to be added to the base screen address
    bit 0,l ; if y address is even, need to start with single address
    jr z,EvenYAddr
    ld iy,OddAddrExit
    jr OddYAddr
.EvenYAddr
    ld iy,EvenAddrExit
    inc l ; need to bump up to high byte from address table
    ld d,(hl) ; get contents of (hl), add bc, and put result in de
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    res 3,d ; only want even address
    push de
    dec l
.OddYAddr
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de ; de contains odd address, so push
    res 3,d ; move to even line above it
    push de ; and push that as well
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    res 3,d
    push de
    jp (iy) ; skip the last line if y co-ord was even
.OddAddrExit ; if y co-ord was odd, get one last address line
    dec l
    ld d,(hl)
    dec l
    ld a,(hl)
    add a,c
    ld e,a
    ld a,b
    adc a,d
    ld d,a
    push de
    dec l
.EvenAddrExit
    exx
    dec b
    jp nz,CalcSprLoop
;
.SaveSPCalc
    ld sp,0
    ret
.MoveSprite
    ld hl,SpritesYXMv+1
; x first
    ld a,(hl)
    res 1,l
    add a,(hl)
    ld (hl),a
    set 1,l
    cp a,0
    jr z,SwapXDir
    cp a,74
    jr nz,DontSwapXDir
.SwapXDir
    ld a,(hl)
    neg
    ld (hl),a
.DontSwapXDir
    dec l
; now y move
    ld a,(hl)
    res 1,l
    add a,(hl)
    ld (hl),a
    set 1,l
    cp a,16
    jr z,SwapYDir
    cp a,195
    jr nz,DontSwapYDir
.SwapYDir
    ld a,(hl)
    neg
    ld (hl),a
.DontSwapYDir
    ret
.FrameFlyB
    ld b,&f5    ;; B = I/O address of PPI port B
.vsync
    in a,(c)    ;; read PPI port B input
    rra      ;; transfer bit 0 into carry
    jr nc,vsync    ;; if carry=0 then vsync= 0 (inactive),
      ;; if carry=1 then vsync=1 (active).
    ret
list
.SetColours
nolist
; HL points to list, A holds 15 or 3
    ld b,&7f
    ld c,(hl)
    out (c),a
    out (c),c
    inc hl
    or a
    jr z,SetColEnd
    dec a
    jr SetColours
.SetColEnd
    ld a,&10 ; set border same as ink 0
    out (c),a
    out (c),c
    ret
.PrintBlocks
    ld de,&c000
    ld c,20
.PBsLpOuter
    push de
    ld b,13
.PBsLLpInner
    push bc
    ld a,1
    cp a,b
    jr nz,PrintBlock
    jr PrintHalfBlock
.PrintBlockReturn
    pop bc
    djnz PBsLLpInner
    pop de
    inc de
    inc de
    inc de
    inc de
    dec c
    jr nz,PBsLpOuter
    ret
PrintHalfBlock
    ld c,1+32
    jr PrnBlkCommon
PrintBlock
    ld c,2+64
.PrnBlkCommon
    ld hl,BackGndBlock
.PBLpO
    ld b,8
.PBLpI
    ldi
    ldi
    ldi
    ldi
    ex de,hl
    push de
    ld de,&800-4
    add hl,de
    pop de
    ex de,hl
    djnz PBLpI
    ex de,hl
    push de
    ld de,&c000+&50
    add hl,de
    pop de
    ex de,hl
    dec c
    jr nz,PBLpO
    jr PrintBlockReturn
.BackGndBlock
    defb 143,15,15,15
    defb 71,15,15,15
    defb 239,175,175,143
    defb 119,95,95,79
    defb 239,143,15,143
    defb 119,95,79,79
    defb 239,239,175,143
    defb 119,95,79,79
    defb 239,239,175,143
    defb 119,95,79,79
    defb 239,255,239,143
    defb 119,95,95,79
    defb 239,175,175,143
    defb 119,255,255,207
    defb 255,255,255,239
    defb 85,85,85,85
.Sprite
    defb 238,0,119,0,0,240,0,240,238,0,119,0
    defb 204,1,0,56,0,75,0,30,0,193,51,8
    defb 136,19,0,60,0,225,0,135,0,211,17,12
    defb 0,103,0,158,0,210,0,90,0,167,0,142
    defb 0,87,0,79,0,240,0,180,0,223,0,78
    defb 136,51,0,175,0,240,0,224,0,175,17,172
    defb 136,64,0,222,0,240,0,240,0,119,17,96
    defb 0,176,0,48,0,240,0,240,0,128,0,240
    defb 0,208,0,240,0,195,0,60,0,240,0,180
    defb 0,160,0,240,0,167,0,30,0,240,0,30
    defb 0,144,0,112,0,71,0,30,0,225,0,150
    defb 0,160,0,176,0,39,0,158,0,240,0,90
    defb 0,144,0,80,0,147,0,124,0,240,0,180
    defb 0,128,0,176,0,192,0,112,0,240,0,210
    defb 136,64,0,6,0,240,0,240,0,150,17,224
    defb 136,99,0,175,0,176,0,224,0,207,17,44
    defb 0,87,0,79,0,80,0,96,0,175,0,142
    defb 0,103,0,174,0,32,0,176,0,87,0,78
    defb 136,51,0,76,0,0,0,80,0,35,17,140
    defb 204,17,0,184,0,0,0,32,0,209,51,8
    defb 238,0,119,0,0,240,0,240,238,0,119,0
.YTable
    defw &fd0,&1fd0,&2fd0,&3fd0
    defw &fd0,&1fd0,&2fd0,&3fd0
;
    defw &800,&1800,&2800,&3800
    defw &850,&1850,&2850,&3850
    defw &8a0,&18a0,&28a0,&38a0
    defw &8f0,&18f0,&28f0,&38f0
;
    defw &940,&1940,&2940,&3940
    defw &990,&1990,&2990,&3990
    defw &9e0,&19e0,&29e0,&39e0
    defw &a30,&1a30,&2a30,&3a30
;
    defw &a80,&1a80,&2a80,&3a80
    defw &ad0,&1ad0,&2ad0,&3ad0
    defw &b20,&1b20,&2b20,&3b20
    defw &b70,&1b70,&2b70,&3b70
;
    defw &bc0,&1bc0,&2bc0,&3bc0
    defw &c10,&1c10,&2c10,&3c10
    defw &c60,&1c60,&2c60,&3c60
    defw &cb0,&1cb0,&2cb0,&3cb0
;
    defw &d00,&1d00,&2d00,&3d00
    defw &d50,&1d50,&2d50,&3d50
    defw &da0,&1da0,&2da0,&3da0
    defw &df0,&1df0,&2df0,&3df0
;
    defw &e40,&1e40,&2e40,&3e40
    defw &e90,&1e90,&2e90,&3e90
    defw &ee0,&1ee0,&2ee0,&3ee0
    defw &f30,&1f30,&2f30,&3f30
;
    defw &f80,&1f80,&2f80,&3f80
;
    defw &fd0,&1fd0,&2fd0,&3fd0
    defw &fd0,&1fd0,&2fd0,&3fd0





Devilmarkus

nice example, Axelay!
The result looks fine!
I captured it for all who dont know how to handle assembler ;)


I hope it's accurate...
On real CPC its of course much smoother and faster!
When you put your ear on a hot stove, you can smell how stupid you are ...

Amstrad CPC games in your webbrowser

JavaCPC Desktop Full Release

TFM

#87
Sprite size for sprite with mask is ok.

Switching INTs off would bother nobody IMHO.

If I use Maxam to assemble it, then it looks quite different from the pic above. Which assembler do you use?

@all
EDIT: Just to remember, it was more about fast sprite routines for Mode 2. But nice to see some Mode 1 examples too.
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

andycadley

Quote from: TFM/FS on 20:43, 19 April 11
Switching INTs off would bother nobody IMHO.

If I use Maxam to assemble it, then it looks quite different from the pic above. Which assembler do you use?

@all
EDIT: Just to remember, it was more about fast sprite routines for Mode 2. But nice to see some Mode 1 examples too.
Looks fine to me using WinAPE's assembler, though pasting straight code out of the forum goes all broken in IE.  >:(

For the sake of keeping routines generic, it'd be preferable to keep interrupts enabled I think. At the very least, bonus points for not being restricted to turning them off. Shouldn't be a problem to use the alternate registers though, which might speed that code up a bit.

And, as I said originally, the point was to determine if Mode 2 really is the fastest for drawing sprites. Can't really answer that question if the only routines written are all for Mode 2. So all modes are welcome, double bonus points if you can find a way to write a routine which is fastest in Mode 3, bringing this whole thread full circle.

TFM

Quote from: andycadley on 21:57, 19 April 11
For the sake of keeping routines generic, it'd be preferable to keep interrupts enabled I think. At the very least, bonus points for not being restricted to turning them off. Shouldn't be a problem to use the alternate registers though, which might speed that code up a bit.

Agree in other things. But no commercial game uses interrupts enable. Yes, they use their own routines, but not the slow firmware interrupt. So I think, switching them OFF or to an own routine will be fine.
Leaving them switched on takes so much time, that it may double the used time to display a sprite, in you add it to an interrrupt (0-5). And this is what must be done to omit flickering.

TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

andycadley

Point taken, the firmware routine is incredibly slow. I think switching them off is ok, as long as you don't then write the sprite routine such that it abuses the stack pointer in a way that would break if interrupts were on. It can be a very powerful trick, but makes everything a lot less reusable.

sigh

Quote from: Axelay on 12:38, 19 April 11
It would be faster with the screen width reduced and height increased, and interupts disabled, but I guess that wouldnt be very generic.

Are you going to try a mode 2? It would be interesting to see the sprite on a similar background.

arnoldemu

Quote from: TFM/FS on 22:10, 19 April 11

Agree in other things. But no commercial game uses interrupts enable. Yes, they use their own routines, but not the slow firmware interrupt. So I think, switching them OFF or to an own routine will be fine.
Leaving them switched on takes so much time, that it may double the used time to display a sprite, in you add it to an interrrupt (0-5). And this is what must be done to omit flickering.
Well I think this would be ok for "interrupts enabled":

di
ld hl,&c9fb
ld (&0038),hl
ei

A dummy interrupt, but interrupts are executing ;)

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

Axelay

Quote from: TFM/FS on 20:43, 19 April 11
If I use Maxam to assemble it, then it looks quite different from the pic above. Which assembler do you use?

Maxam, in Winape.

Quote from: andycadley on 21:57, 19 April 11
So all modes are welcome, double bonus points if you can find a way to write a routine which is fastest in Mode 3, bringing this whole thread full circle.

Damn your crazy suggestsions, you got me trying to *think* of something now!  Happily I havent thought of anygthing that wouldnt be faster and more colourful in mode 0 with 2 bit colour yet.  Could always do 1 bit colour in mode 3 for laughs  :laugh:

Quote from: sigh on 09:07, 20 April 11
Are you going to try a mode 2? It would be interesting to see the sprite on a similar background.

It wouldnt be faster in mode 2, I wrote it with any mode in mind.  I guess I could do a mode 2 version with the screen dimensions altered like I mentioned for fun  ;)


On the interrupts, I have disabled the firmware, so that isnt impacting the speed.

sigh

Quote from: Axelay on 12:16, 20 April 11


Maxam, in Winape.
 

Damn your crazy suggestsions, you got me trying to *think* of something now!  Happily I havent thought of anygthing that wouldnt be faster and more colourful in mode 0 with 2 bit colour yet.  Could always do 1 bit colour in mode 3 for laughs  :laugh:
 

It wouldnt be faster in mode 2, I wrote it with any mode in mind.  I guess I could do a mode 2 version with the screen dimensions altered like I mentioned for fun  ;)


On the interrupts, I have disabled the firmware, so that isnt impacting the speed.

So just to clarify - in your demonstration, a mode 2 sprite wouldn't draw any faster? All the modes would draw at the same speed? Sorry if I'm repeating myself (I'm not a programmer  ;D )

andycadley

Quote from: sigh on 13:21, 20 April 11
So just to clarify - in your demonstration, a mode 2 sprite wouldn't draw any faster? All the modes would draw at the same speed? Sorry if I'm repeating myself (I'm not a programmer  ;D )
Yes, he's using a classic sprite with a saved background and seperate mask technique, so the only difference in another screen mode is the values held in the mask.

TFM

Quote from: andycadley on 22:33, 19 April 11
Point taken, the firmware routine is incredibly slow. I think switching them off is ok, as long as you don't then write the sprite routine such that it abuses the stack pointer in a way that would break if interrupts were on. It can be a very powerful trick, but makes everything a lot less reusable.

Agree! Sprites should be generic.
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

TFM

Quote from: arnoldemu on 09:51, 20 April 11
Well I think this would be ok for "interrupts enabled":

di
ld hl,&c9fb
ld (&0038),hl
ei

A dummy interrupt, but interrupts are executing ;)

Hmm. In this case I see not a real difference from DI.

However, doesn't matter for me, since I use another OS for my routines. But they still stay generic. Commercial games usually kick out the firmware for good reasons (gain memory). So the OS of choice will not matter.

BTW: The idea of making the circle round and present something in Mode 3 is good. But 3 comes after 2 ;-)
TFM of FutureSoft
Also visit the CPC and Plus users favorite OS: FutureOS - The Revolution on CPC6128 and 6128Plus

Axelay

Argh!  My eyes!  I mean, here's the mode 2 example.  ;)


org &8000
nolist
run start
SpriteAddr equ &200+16
BGSave equ &300
SpriteData equ &400
.SpritesYX
   defb 115,46
.SpritesYXMv
   defb 1,1
.Mode1Pal
defb &4e,&5f,&5f,&54

.start
di      ;; disable interrupts
im 1     ;; interrupt mode 1 (CPU will jump to &0038 when a interrupt occrs)
ld hl,&c9fb    ;; C9 FB are the bytes for the Z80 opcodes EI:RET
ld (&0038),hl   ;; setup interrupt handler
ei      ;; re-enable interrupts
; set colours 0-3
    ld a,3
    ld hl,Mode1Pal
    call SetColours
    ld bc,&7F8e
    out (c),c ; set mode 2
    call SetupGameScr
; fill the screen with the background block
call PrintBlocks
; copy the Y address table to &100
ld hl,YTable
ld de,&100
ld bc,256
ldir
; copy the sprite to &400. Sprite is 6 bytes by 21 = 126 bytes, but contains mask data
; which doubles it's size
ld hl,Sprite
ld de,SpriteData
ld bc,252
ldir
; fill the sprite address list with dummy values for first frame
ld hl,&cfc0
ld (SpriteAddr),hl
ld hl,SpriteAddr
ld de,SpriteAddr+2
ld bc,21*2-2
ldir
.main
; wait for vsync
call FrameFlyB
; wait to start code at same time every frame
; un-comment additional halts to see entire execution
; time, though sprite will disappear during this time
halt
;halt
;halt
; set border to different blues for each stage
ld bc,&7f10
out (c),c
ld bc,&7f55
out (c),c
; restore the background from last frame
; (requires the dummy addresses for the first frame, set previously)
call RestoreBlock
ld bc,&7f10
out (c),c
ld bc,&7f44
out (c),c
; generate a new list of screen addresses based on current XY coord (in bytes)
call CalcSpriteAddr
ld bc,&7f10
out (c),c
ld bc,&7f5d
out (c),c
; save the background for the new position of the sprite
call SaveBlock
ld bc,&7f10
out (c),c
ld bc,&7f5b
out (c),c
; finaly...
call PrintSprite
; and now set border back to black
ld bc,&7f10
out (c),c
ld bc,&7f54
out (c),c
; now move the sprite about
call MoveSprite
; repeat
jr main
.RestoreBlock
; is the last time the address list is used, so SP can be used to pop the addresses
    ld (SaveSP+1),sp
    ld sp,SpriteAddr ; load SP with top of address list
    ld bc,21*&100+&ff ; ld b with 21 and c with dummy to stop ldi corrupting b
    ld hl,BGsave ; load hl with saved background data
.ResBlkLp
; restore each line by popping address of screen to de and copying back the screen data
    pop de
    ldi
    ldi
    ldi
    ldi
    ldi
    ldi
    djnz ResBlkLp
.SaveSP
    ld sp,0
    ret

.SaveBlock
; with interrupts enabled, is generally not possible unless writing code with fixed
; execution times to assume can still use SP to pop the address list without an interupt
; corrupting the address list if it happens along while reading it
; so for this example a much lousier (slower) method is used
    ld hl,SpriteAddr ; ld hl with pointer to screen address list of sprite
    ld b,21 ; height of sprite
    exx
    ld de,BGsave ; load de' with destination to back up screen data to
    exx
.SavBlkLp
; instead of pop, ugly code using a and a' to get (hl) into hl'
    ld a,(hl)
    inc l
    ex af,af'
    ld a,(hl)
    inc l
    exx
    ld h,a
    ex af,af'
    ld l,a
    ldi
    ldi
    ldi
    ldi
    ldi
    ldi
    exx
    djnz SavBlkLp
    ret
.PrintSprite
    ld ix,SpriteAddr ; use ix register to read address list this time
    ld hl,SpriteData ; hl points to sprite data, which includes the mask byte
                     ; mask precedes the data byte it relates to
    ld b,21 ; pixel lines to print
.PrnSprLp
; normally I'd use the alternate registers to work out what frame to print
; so assuming the alt registers are not available, IX is used to read address
; list instead
    ld e,(ix+0)
    inc ixl
    ld d,(ix+0)
    inc ixl ; de now has screen address for this row
; for each byte, read the screen data from (de), 'and' with (hl) and 'or' with (hl+1)
; then write back to (de)
; 1
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc e
; 2
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc e
; 3
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc e
; 4
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc e
; 5
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    inc e
; 6
    ld a,(de)
    and a,(hl)
    inc l
    or a,(hl)
    ld (de),a
    inc l
    djnz PrnSprLp
    ret
; YTable contains only the odd address lines, so the table fits in one 256 byte page
; however, both odd and even addresses are determined from this table
.CalcSpriteAddr
; write screen addresses with SP, possible interrupts are no problem as they are being
; written, not read
    ld (SaveSPCalc+1),sp
    ld sp,SpriteAddr+42 ; set SP to 'bottom' of address list
    ld hl,SpritesYX+1 ; set hl to point to sprite co-ord, stored y first
    exx
    ld h,1 ; base ytable is at &100, so preload h' with 1
           ; in this example the whole of the most significant byte is in the table
           ; as it is never altered by the sprite x co-ord, so to screen flip would
           ; require a second table and loading h with a different pointer
    exx
    ld b,1 ; only 1 sprite to print
.CalcSprLoop
    ld a,(hl) ; ld x co-ord into a
    dec l
    ex af,af'
    ld a,(hl) ; ld y co-ord into a'
    add a,20 ; start from bottom of y and work up
    dec l
    exx
    ld l,a ; ld l with y co-ord
    ex af,af'
    ld c,a   ; ld c' with x co-ord for adding to base screen address
    bit 0,l ; if y address is even, need to start with single address
    jr z,EvenYAddr
    ld iy,OddAddrExit
    jr OddYAddr
.EvenYAddr
    ld iy,EvenAddrExit
    inc l ; need to bump up to high byte from address table
    ld d,(hl) ; get contents of (hl), add bc, and put result in de
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    res 3,d ; only want even address
    push de
    dec l
.OddYAddr
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de ; de contains odd address, so push
    res 3,d ; move to even line above it
    push de ; and push that as well
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    dec l
;
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    res 3,d
    push de
    jp (iy) ; skip the last line if y co-ord was even
.OddAddrExit ; if y co-ord was odd, get one last address line
    dec l
    ld d,(hl)
    dec l
    ld a,(hl)
    or a,c
    ld e,a
    push de
    dec l
.EvenAddrExit
    exx
    dec b
    jp nz,CalcSprLoop
;
.SaveSPCalc
    ld sp,0
    ret
.MoveSprite
    ld hl,SpritesYXMv+1
; x first
    ld a,(hl)
    res 1,l
    add a,(hl)
    ld (hl),a
    set 1,l
    cp a,0
    jr z,SwapXDir
    cp a,58
    jr nz,DontSwapXDir
.SwapXDir
    ld a,(hl)
    neg
    ld (hl),a
.DontSwapXDir
    dec l
; now y move
    ld a,(hl)
    res 1,l
    add a,(hl)
    ld (hl),a
    set 1,l
    cp a,8
    jr z,SwapYDir
    cp a,227
    jr nz,DontSwapYDir
.SwapYDir
    ld a,(hl)
    neg
    ld (hl),a
.DontSwapYDir
    ret
.FrameFlyB
    ld b,&f5    ;; B = I/O address of PPI port B
.vsync
    in a,(c)    ;; read PPI port B input
    rra      ;; transfer bit 0 into carry
    jr nc,vsync    ;; if carry=0 then vsync= 0 (inactive),
      ;; if carry=1 then vsync=1 (active).
    ret
list
.SetColours
nolist
; HL points to list, A holds 15 or 3
    ld b,&7f
    ld c,(hl)
    out (c),a
    out (c),c
    inc hl
    or a
    jr z,SetColEnd
    dec a
    jr SetColours
.SetColEnd
    ld a,&10 ; set border same as ink 0
    out (c),a
    out (c),c
    ret
.PrintBlocks
    ld de,&c040
    ld c,16
.PBsLpOuter
    push de
    ld b,15
.PBsLLpInner
    push bc
;    ld a,1
;    cp a,b
    jr PrintBlock
;    jr PrintHalfBlock
.PrintBlockReturn
    pop bc
    djnz PBsLLpInner
    pop de
    inc de
    inc de
    inc de
    inc de
    dec c
    jr nz,PBsLpOuter
    ret
PrintHalfBlock
    ld c,1+32
    jr PrnBlkCommon
PrintBlock
    ld c,2+64
.PrnBlkCommon
    ld hl,BackGndBlock
.PBLpO
    ld b,8
.PBLpI
    ldi
    ldi
    ldi
    ldi
    ex de,hl
    push de
    ld de,&800-4
    add hl,de
    pop de
    ex de,hl
    djnz PBLpI
    ex de,hl
    push de
    ld de,&c000+&40
    add hl,de
    pop de
    ex de,hl
    dec c
    jr nz,PBLpO
    jr PrintBlockReturn
.SetupGameScr
    ld hl,GameDimensions
    ld a,6
    ld b,&bc
.ScrDimLp
    ld c,(hl)
    inc hl
    ld e,(hl)
    inc hl
    out (c),c
    inc b
    out (c),e
    dec b
    dec a
    jr nz,ScrDimLp
    ret
.GameDimensions
    defb 2,42,1,32,7,33,6,30,12,&30,13,&20
.BackGndBlock
    defb 63,255,255,255
    defb 15,255,255,255
    defb 130,170,170,175
    defb 1,85,85,95
    defb 66,191,255,175
    defb 1,74,171,95
    defb 130,133,87,175
    defb 1,74,171,95
    defb 66,133,87,175
    defb 1,74,171,95
    defb 130,128,3,175
    defb 1,85,85,95
    defb 66,170,170,175
    defb 0,0,0,7
    defb 128,0,0,3
    defb 17,17,17,17
.Sprite
    defb 255,0,240,0,0,0,0,0,15,0,255,0
    defb 252,0,0,0,0,255,0,255,0,0,63,0
    defb 224,2,0,175,0,239,0,255,0,245,7,64
    defb 192,21,0,87,0,255,0,255,0,202,3,160
    defb 128,42,0,170,0,191,0,191,0,85,1,80
    defb 128,5,0,85,0,255,0,252,0,170,1,160
    defb 128,0,0,163,0,238,0,239,0,21,1,56
    defb 0,10,0,15,0,255,0,255,0,192,0,252
    defb 0,0,0,187,0,177,0,75,0,187,0,252
    defb 0,9,0,111,0,207,0,247,0,255,0,254
    defb 0,0,0,187,0,149,0,250,0,254,0,254
    defb 0,5,0,87,0,138,0,251,0,255,0,254
    defb 0,0,0,138,0,197,0,87,0,187,0,188
    defb 0,2,0,21,0,80,0,15,0,255,0,252
    defb 128,0,0,0,0,170,0,238,0,239,1,232
    defb 128,0,0,84,0,85,0,219,0,85,1,112
    defb 128,2,0,168,0,42,0,180,0,170,1,160
    defb 192,5,0,84,0,5,0,94,0,85,3,80
    defb 224,10,0,160,0,18,0,42,0,138,7,160
    defb 252,1,0,64,0,0,0,141,0,5,63,0
    defb 255,0,240,0,0,0,0,0,15,0,255,0
.YTable
;
    defw &c800,&d800,&e800,&f800
    defw &c840,&d840,&e840,&f840
    defw &c880,&d880,&e880,&f880
    defw &c8c0,&d8c0,&e8c0,&f8c0
;
    defw &c900,&d900,&e900,&f900
    defw &c940,&d940,&e940,&f940
    defw &c980,&d980,&e980,&f980
    defw &c9c0,&d9c0,&e9c0,&f9c0
;
    defw &ca00,&da00,&ea00,&fa00
    defw &ca40,&da40,&ea40,&fa40
    defw &ca80,&da80,&ea80,&fa80
    defw &cac0,&dac0,&eac0,&fac0
;
    defw &cb00,&db00,&eb00,&fb00
    defw &cb40,&db40,&eb40,&fb40
    defw &cb80,&db80,&eb80,&fb80
    defw &cbc0,&dbc0,&ebc0,&fbc0
;
    defw &cc00,&dc00,&ec00,&fc00
    defw &cc40,&dc40,&ec40,&fc40
    defw &cc80,&dc80,&ec80,&fc80
    defw &ccc0,&dcc0,&ecc0,&fcc0
;
    defw &cd00,&dd00,&ed00,&fd00
    defw &cd40,&dd40,&ed40,&fd40
    defw &cd80,&dd80,&ed80,&fd80
    defw &cdc0,&ddc0,&edc0,&fdc0
;
    defw &ce00,&de00,&ee00,&fe00
    defw &ce40,&de40,&ee40,&fe40
    defw &ce80,&de80,&ee80,&fe80
    defw &cec0,&dec0,&eec0,&fec0
;
    defw &cf00,&df00,&ef00,&ff00
    defw &cf40,&df40,&ef40,&ff40
    defw &cf80,&df80,&ef80,&ff80
    defw &cfc0,&dfc0,&efc0,&ffc0



Source also in the attached file.  Narrowing the screen has saved only 2 scan lines (down to 60) but it would be multiplied by the number of sprites you have in a game, so it all adds up.  Could save even more by disabling interupts, or as I have in my last two projects, restricting y co-ordinates to even addresses.

sigh

Quote from: Axelay on 02:21, 21 April 11
Argh!  My eyes!  I mean, here's the mode 2 example.  ;)

Source also in the attached file.  Narrowing the screen has saved only 2 scan lines (down to 60) but it would be multiplied by the number of sprites you have in a game, so it all adds up.  Could save even more by disabling interupts, or as I have in my last two projects, restricting y co-ordinates to even addresses.

I had at look at this in winape. So when you say that "Narrowing the screen has saved only 2 scan lines (down to 60) but it would be multiplied by the number of sprites you have in a game, so it all adds up.", 
Does this mean that that the more sprites you have in the game, the more scanlines that you save in comparison to mode 1 and mode 0?

Powered by SMFPacks Menu Editor Mod