A86: Re: 3 *5 PutSprite routine


[Prev][Next][Index][Thread]

A86: Re: 3 *5 PutSprite routine




I suggest a FindAddress (not FindPixel) routine like the one below.  A
findpixel routine isn't exactly what you need for a putsprite, since you
need to find the number of times to shift the sprite, not the pixel offset
in the byte (unless it's a Mardell style putsprite).  The routine below is
fairly fast, returns the offset in the buffer and the buffer doesn't have to
be aligned.

As for a putsprite, if you are doing sprites with width's that are not
multiples of eight, the easiest way is to treat them as if they are a
multiple of eight.  This will waste more space, but will usually result in a
more efficient sprite routine.  If you don't like my method, I encourage you
to read scabby's sources, as he's the only programmer I know who's made big
games with odd sprite sizes.  Reading any of his code (even and especially
Repton) will give great insight on optimization and coding practices.

A non-masked, non-grayscale putsprite should be pretty easy to write,
especially after you have a FindAddress routine.  We can walk through the
steps of writing the routine.  First thing to note is that I haven't tested
it, and thus it might not work.  Second, is that in real code, I would
prefix all labels with PutSprite.  For example, Loop would be PutSpriteLoop.
This will allow you to have many routines in a large project and will allow
you to easily share code files between projects without having name
conflicts.  I used short names to save space and make the code more
readable.

This routine is a good example of using self-modifying code to use only
normal registers without pushing/popping anything:

; in: hl = sprite, bc = x,y location
PutSprite:
 ex de,hl          ; save the sprite pointer in DE
 call FindAddress  ; calculate the offset in the buffer and
                   ;  the byte offset in the video buffer
 ld bc,$fc00       ; normal video memory starts at the address $fc00
 add hl,bc         ; add the start and the offset to get the
                   ;  starting byte of the sprite in the video buffer
 ld (_@ShiftC),a   ; use self-modifying code to load the number
                   ;  of times to shift into the OR operation below
                   ;  instead of <OR 0>, it will <OR A>, but with the
                   ;  value that A is currently
                   ;  using self-modifying code like this can save you
                   ;  from pushing/popping registers in a critical loop,
                   ;  and effectively create registers from nowhere
 ld a,ROWS         ; load the height of the sprite, which is how many times
                   ;  that the routine needs to loop
Loop:
 ld (_@LoopC),a    ; update the counter below, with self-modifying code
 sub a             ; clear the accumlator and flags, since A - A = 0
 ld b,(de)         ; load the byte from the sprite
 inc de            ; move the pointer to the next byte of the sprite
 ld c,a            ; clear the low byte of BC
                   ;  BC will be used as a 16-bit register as the sprite is
                   ;  shifted to fit into two bytes in video memory
_@ShiftC =$+1      ; a label will point to the start of an instruction
                   ;  the $ symbol is a constant used by the assembler to
                   ;  represent the current value of the program counter, PC
                   ;  by setting ShiftC to PC plus one, ShiftC will point to
                   ;  second byte of the OR instruction, the data byte
                   ;  the data byte, which is the zero below, will be loaded
                   ;  above by the number of times to shift
                   ;  by specifying a label's address instead of adding to
                   ;  a label's value, you will prevent mistakes will coding
                   ;  prefixing labels that are selfmodified with _@ will
                   ;  identify them from normal labels
 or 0              ; the zero will be the number of times to shift
                   ;  since A is zero, it is the same as a load, but
                   ;  it will also update the flags with the value of A
 jr z,DoneShift    ; if the shift counter is zero at the beginning, then
                   ;  the loop can be skipped entirely
Shift:
 srl b             ; shift the high byte to the right into the carry flag,
                   ;  and set the high bit to zero
 rr c              ; shift from the carry flag into the low byte
 dec a             ; decrease the shift counter and update flags
 jr nz,Shift       ; if there are more bits to shift, continue loop
Done:
 ld (hl),b         ; load the high byte into video memory
 inc hl            ; move to the next byte in video memory
 ld (hl),c         ; load the low byte into video memory
 ld bc,15          ; the row is 15 bytes ahead in the video buffer
 add hl,bc         ; advance the video memory pointer to the next row
LoopC =$+1         ; point to the data byte of the load
 ld a,0            ; load the row counter value, set at the top of the loop
 dec a             ; decrease the row/byte counter
 jr nz,Loop        ; if there are more rows to draw, then loop
 ret               ; all done!

; in: bc = x,y location
; out: hl = offset of byte, a = offset in video byte
FindAddress:
 ld a,c         ; get the y coord
 add a,a        ; multiply by 2
 add a,a        ; again by 4
 ld l,a         ; save as low byte
 ld h,0         ; clear high byte
 add hl,hl      ; multiply by 8
 add hl,hl      ; and again by 16
 ld a,b         ; now get the x coord
 and %11111000  ; clear lower 3 bits
 rra            ; divide by 2
 rra            ; this time by 4
 rra            ; and finally by 8
 or l           ; combine with low byte
 ld l,a         ; save it
 ld a,b         ; get x coord again
 and %00000111  ; clear all but last 3 bits
 ret            ; return to caller

> Hi, I need a putsprite routine that puts 3wide by 5high aligned sprites.
I
> am using Find_Pixel by Clem.
>
> I don't think that this would be too hard, but I don't know how to do it.




References: