• Welcome to Videopac / Odyssey2 forum.

"Horizontal flip" by software

Started by LD, March 22, 2015, 04:40:44 PM

Previous topic - Next topic

LD

On Atari 2600 we have a register (REFPx) that turns the sprite horizontally, so if you have a sprite facing to right, set this register and the sprite is displayed facing to the left.
There's no such feature on Odyssey 2, you need to draw the same sprite twice, one for each facing.
Each sprite facing waste 8 bytes of ROM, so I had the idea to make a code to switch the facing by software.

There are 3 ways to make this (that I know) :

1 - Set individual pixels each time using byte shift and carry. The problem this code is very slow and need 64 loops to change all the 8 bytes of a single sprite. Probably this take more of the available time you have on VBlank.

2 - Flip each nibble at time. This is the code I'm using, still slow, and require a table of 16 bytes. The code itself including the table takes about 50 bytes of ROM space. It require 8 loops to flip the sprite.

3 - Flip each byte at time - The faster one, done in 8 loops,  but require a table of 256 bytes, you can store 32 sprites in this amount of ROM.

So my code flips each nibble at time, and like I said, uses about 50 bytes of ROM. You can store 6 sprites on that amount of ROM, so this code only is useful if you need flip more than 6 individual sprites, less than that you should flip them manually and not using the code, else you're wasting ROM and VBlank Time.

How it works : put the sprite on VDC RAM normally, after that, call a subroutine and it will flip the sprite that is pointed at R0, here's the code :
(no RAM needed and uses registers R0 to R4)


   mov R0,#080h       ;Sprite to be mirrored (80 or 88 or 90 or 98)
  call Mirroring     ;go to mirror code

;----------------------------------------------
Mirroring ;Subroutine
;----------------------------------------------
mov R4,#8
GetNibble
movx a,@R0
mov R1,a
;---
anl a,#00Fh
mov R2,#TableMirror & 0ffh
add a,R2
movp a,@a
swap a
mov R3,a
;---
mov a,R1 ;xxxx0000
swap a ;0000xxxx
anl a,#00Fh
mov R2,#TableMirror & 0ffh
add a,R2
movp a,@a
orl a,R3
movx @R0,a
inc R0
djnz R4,GetNibble
ret
TableMirror
db 0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15


The code needs to be run during Vblank (VDC Off) but you can store the result on extra RAM and later update to the sprite RAM, for calculate it off VBlank time.

EDIT : Code was optimized

gertk

There are several ways to flip a byte but each of these routines will consume more memory than the 8 bytes you try to spare...  :)
>>G7000 G7200(P+S) G7400 N60 JET27 VG5000 ZX80 ZX81 ORIC-1 COMX35 Aquarius<<

LD

If you know a faster way please, share :)

I'm not trying to make something smaller than 8 bytes, but something small and fast as possible. :)

This code based on nibble is half way between others two.

Rene_G7400

Although this is a very interesting experiment, I think that CPU time (especially in Vblank) will usually be a bigger problem than ROM space (especially on NTSC consoles).

gertk

Quote from: LD on March 22, 2015, 09:35:21 PM
If you know a faster way please, share :)

I'm not trying to make something smaller than 8 bytes, but something small and fast as possible. :)

This code based on nibble is half way between others two.


The fastest and smallest way is to pre-calculate every sprite which needs reversing and store that data in rom, then use (unrolled) loops to copy them to the VDC. If you place them smartly into different pages you can use a single pointer/index register.
>>G7000 G7200(P+S) G7400 N60 JET27 VG5000 ZX80 ZX81 ORIC-1 COMX35 Aquarius<<

LD

Quote from: Rene_G7400 on March 23, 2015, 09:24:07 AM
Although this is a very interesting experiment, I think that CPU time (especially in Vblank) will usually be a bigger problem than ROM space (especially on NTSC consoles).

Usually games for Odyssey 2 / Videopac are minimalist in usage of sprites and animations.
But in games that you need a lot of animation frames, perhaps this code can help to save some ROM.
I don't know exactly how many cycles this code will take, but assuming it's something like 30 cycles, it will takes 1.5 scanline to flip the sprite.
Not that bad, actually.

Mux

Couple of years late but optimization is always a trade-off between CPU and memory. A bit of an in-between would be to have a 16-byte look-up table and use SWAP to get the upper / lower nibbles. Won't be as fast but faster than using rotates only..

-Mux

LD

That's what I'm doing ;)

Actually, now reading my old code, I found some optimizations....
I don't need R1 and R2 at all... this saves 2 cycles and 3 bytes... not much but works!
The new code uses now registers R0 to R2 :)
It takes about 26 cycles and waste 39 bytes.

Here is  :


   mov R0,#080h       ;Sprite to be mirrored (80 or 88 or 90 or 98)
   call Mirroring     ;go to mirror code

;----------------------------------------------
Mirroring ;Subroutine
;----------------------------------------------
mov R1,#8
GetNibble
movx a,@R0
anl a,#00Fh
add a,#TableMirror & 0ffh
movp a,@a
swap a
mov R2,a
;---
movx a,@R0 ;xxxx0000
swap a ;0000xxxx
anl a,#00Fh
add a,#TableMirror & 0ffh
movp a,@a
orl a,R2
movx @R0,a
inc R0
djnz R1,GetNibble
ret
TableMirror
db 0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15


And the ROM for you try on emulators.
Hold button to flip the sprite! What a demo!

Janzl

Quote from: LD on May 02, 2019, 11:27:22 PM
And the ROM for you try on emulators.
Hold button to flip the sprite! What a demo!

You should start up a scene  :D
Ne tirez pas sur le administrator!

LD

I have some ideas for a demo, actually...
The problem I'm not that good for code it.  :'(