Hey there! Recently I have downloaded Mimas 0.4 onto my TI-84+SE because of reasons that aren't important for what I'm about to ask of you. So just to test myself, I wrote some basic "Hello world" programs and they worked perfectly, but obviously, you can't stay there forever, you have to move somewhere. That's when I started to look and see what other people had created to get an idea of what to do myself and I came across this: https://www.youtube.com/watch?v=31mWIY6NGJw. It's over an hour long, so you don't have to watch the entire thing if you don't want to, but some of the important routines for this post are the ones in the graphics section.

Their names along with inputs, outputs, and other stats are as follows:

Name: gPrint
Inputs: B = x position (0-95), C = y position (0-63)
Outputs: pixel on the screen at (x, y) (origin top left of screen)
Destroyed: AF, BC, DE, HL
Status: Complete

Name: gLine
Inputs: H = x0, L = y0, D = x1, E = y1
Outputs: draws a line from (x0, y0) to (x1, y1)
Destroyed: AF, BC, DE, HL
Status: vertical and horizontal lines work, just need to implement Bresenham's line algorithm for any other line

Name: gSprite
Inputs: HL = address of sprite, B = x position, C = y position, D = sprite width, E = sprite height
Outputs: draws a sprite on the screen at position (x, y) with a given width and height
Destroyed: AF, BC, DE, HL
Status: so far, it draws pixels at the correct place with the correct width and height, but it doesn't get the right data from the sprite (probably me not knowing how exactly the data is stored/how to access it properly)

My question, or rather I'm asking what to do to complete/fix gSprite. I'm not an efficient z80 programmer as I do not yet know all the different things it offers so go easy on me. I know there's better ways to do the things I'm doing, I just don't know what they are yet. Anyways, here's the code:


Code:

gSprite:
  ld a,b
  ld (gSpriteSX),a
  ld a,c
  ld (gSpriteSY),a
  ld (gSpriteY),a
  ld a,d
  ld (gSpriteW),a
  ld a,e
  ld (gSpriteH),a
  push hl
gSpriteLoopY:
  ld a,(gSpriteSX)
  ld (gSpriteX),a
gSpriteLoopX:
  ;--------The section I think is wrong---------
  pop hl
  ld a,(hl)
  inc hl
  push hl
  and %00000001
  call z,gSpritePrint
  ;------------------------------------------------
  ;gSpriteLoopX check
  ld a,(gSpriteW)
  ld b,a
  ld a,(gSpriteSX)
  add a,b
  ld b,a
  ld a,(gSpriteX)
  inc a
  ld (gSpriteX),a
  sub b
  jp nz,gSpriteLoopX
  ;gSpriteLoopY check
  ld a,(gSpriteH)
  ld b,a
  ld a,(gSpriteSY)
  add a,b
  ld b,a
  ld a,(gSpriteY)
  inc a
  ld (gSpriteY),a
  sub b
  jp nz,gSpriteLoopY
  pop hl
  ret
gSpritePrint:
  ld a,(gSpriteX)
  ld b,a
  ld a,(gSpriteY)
  ld c,a
  call gPrint
  ret
gSpriteX:
  dw appBackUpScreen
gSpriteY:
  dw gSpriteX+2
gSpriteW:
  dw gSpriteY+2
gSpriteH:
  dw gSpriteW+2
gSpriteSX:
  dw gSpriteH+2
gSpriteSY:
  dw gSpriteSX+2


EDIT: I don't know why the

Code:
thing didn't work Neutral
With my cursory glance, I didn't notice anything obviously wrong. Can you give the code for gPrint as well?

Also, as you may already recognize, this is a rather inefficient sprite drawing routine. But I'm assuming that your goal is to learn, so you're not immediately concerned with this and want to have the experience of writing your own from scratch. If your goal is just to get a really good sprite routine, though, I or someone else could point you in the direction of an existing one.
You're absolutely correct; I am just trying to learn at first, the optimizations can come later. Thank you for recognizing that. Smile

As for the code for gPrint...

Code:

; in b=x, c=y
; out: pixel at (x,y)
; x range: 0-95
; y range: 0-63
gprint:
ld h,0
ld l,c
ld de,12
push bc
call mul16
pop bc
ld a,b
ld b,8
call div8
push bc
ld b,0
ld c,a
add hl,bc
ld bc,plotsscreen
add hl,bc
pop bc
ld c,%10000000
gprintloop:
ld a,0
cp b
jp z,gprintexit
ld a,c
sra a
and %01111111
ld c,a
dec b
jp gprintloop
gprintexit:
ld a,(hl)
or c
ld (hl),a
ret


I can already see your next question: "Can I see the code for mul16 and div8?" Here they are as well:

Code:

; in: a/b
; out: a remainder b
div8:
push af
ld e,8
ld d,0
ld c,0
div8loop:
pop af
bit 7,a
push af
jr z,div8skip1
inc d
div8skip1:
ld a,d
cp b
jr c,div8skip2
sub b
ld d,a
inc c
div8skip2:
pop af
sla a
and %11111110
push af
dec e
ld a,0
cp e
jp z,div8exit
ld a,d
sla a
and %11111110
ld d,a
ld a,c
sla a
and %11111110
ld c,a
jp div8loop
div8exit:
pop af
ld a,c
ld b,d
ret
; in: hl*de
; out: hl
mul16:
ld bc,0
mul16loop:
bit 0,e
jp z,mul16skip
push hl
add hl,bc
ld b,h
ld c,l
pop hl
mul16skip:
push hl
ld h,d
ld l,e
call sra16
ld d,h
ld e,l
pop hl
call sla16
ld a,0
cp d
jp nz,mul16loop
cp e
jp nz,mul16loop
ld h,b
ld l,c
ret
; in: hl
; out: hl<<1
sla16:
ld a,h
sla a
and %11111110
bit 7,l
jp z,sla16skip
inc a
sla16skip:
ld h,a
ld a,l
sla a
and %11111110
ld l,a
ret
; in: hl
; out: hl>>1
sra16:
ld a,l
sra a
and %01111111
bit 0,h
jp z,sra16skip
inc a
sra16skip:
ld l,a
ld a,h
sra a
and %01111111
ld h,a
ret
Actually, I think I found the problem, but I'm not 100% sure. Let's say I have the address of a sprite stored in the hl register.

Code:

ld hl,spr1
*code here*
ret

spr1:
db %00100100
db %00100100
db %00000000
db %10000001
db %01111110


If I want to read from spr1 into a, I can say:

Code:

ld a,(hl)


What I'm not accounting for is that it reads a full byte into the a register instead of one bit, which is how I'm treating it. All I need to do is change that I think. Smile
Yeah, I thought it was weird that you were reading a whole byte to write a single pixel, but didn't think to say anything because I thought it might've just been a lack of optimization thing. If you're storing 8 pixels per byte (as you should), then yes, you need to draw each bit individually.
A very good exercise, well worth it imo!

Also you may or may not know that there are some special case optimisations that you can make to mul/div by powers of 2; namely bit shifting.

For example HL * 16:

ADD HL,HL ; x 2
ADD HL,HL ; x 4
ADD HL,HL ; x 8
ADD HL,HL ; x 16

For division C / 8:
SRL C ; / 2
SRL C ; / 4
SRL C ; / 8

There are other shift commands so some further optimisations to be had depending on your registers (particularly A), and on the EZ80 its even better with MUL instructions etc. But might be worth investigating.
Yes, while this is correct, in my case, since I'm using all of amihart's code and just adding my own on top of it, I don't want to mess with their's too much as I feel I will break it. I could always start another program and do some testing there, which is what I will be doing after I have the line algorithms in place for lines that are not vertical nor horizontal. I'm going to see what I can do regarding that, and if I can't, I'll ask again on this same thread.

Thank you both for your help! Very Happy
Okay, so I've been at it for 4 or so hours and I've made some progress, but I'm stuck and can't seem to find the problem, which is somewhere in here:

Code:

; in: hl=(x0,y0), de=(x1,y1)
gLineDiagonal:
  push hl
  push de
  ld a,d
  sub h
  ld (gLineDiagonalDX),a
  ld b,a
  ld a,e
  sub l
  ld (gLineDiagonalDY),a
  call div8
  ld (gLineDiagonalDErrI),a
  ld a,b
  ld (gLineDiagonalDErrF),a
 
  ld a,(gLineDiagonalDErrI)
  ld b,a
  ld a,(gLineDiagonalDX)
  call mul8
  ld (gLineDiagonalDErrI),a
  ld a,0
  ld (gLineDiagonalErr),a
  pop de
  pop hl
  push hl
  push de
  ld a,l
  ld (gLineDiagonalY),a
  ld a,h
  ld (gLineDiagonalX),a
 
gLineDiagonalLoop:
  ld a,(gLineDiagonalX)
  ld b,a
  ld a,(gLineDiagonalY)
  ld c,a
  call gPrint
  ld a,(gLineDiagonalDErrI)
  ld b,a
  ld a,(gLineDiagonalDErrF)
  add a,b
  ld b,a
  ld a,(gLineDiagonalErr)
  add a,b
  ld (gLineDiagonalErr),a
  ld (gLineDiagonalDX),a
  srl a
  ld b,a
  ld (gLineDiagonalErr),a
  sub b
  jp p,gLineDiagonalLoop
 
  ld a,(gLineDiagonalDY)
  or a
  jp m,gLineDiagonalP
  jr gLineDiagonalN
gLineDiagonalP:
  ld a,(gLineDiagonalY)
  inc a
  ld (gLineDiagonalY),a
gLineDiagonalN:
  ld a,(gLineDiagonalY)
  dec a
  ld (gLineDiagonalY),a
 
  ld a,(gLineDiagonalDX)
  ld b,a
  ld a,(gLineDiagonalErr)
  sub b
  ld (gLineDiagonalErr),a
 
  ld a,(gLineDiagonalX)
  ; i think I need to say inc or dec depending on the start x and end x of the line are in relation to one another
  inc a
  ld (gLineDiagonalX),a
 
  pop hl
  push hl
  sub h
  jp nz,gLineDiagonalLoop
  pop de
  pop hl
  ret
gLineDiagonalDX:
  dw appBackUpScreen
gLineDiagonalDY:
  dw gLineDiagonalDX+1
gLineDiagonalDErrI:
  dw gLineDiagonalDY+1
gLineDiagonalDErrF:
  dw gLineDiagonalDErrI+1
gLineDiagonalDErr:
  dw gLineDiagonalDErrF+1
gLineDiagonalX:
  dw gLineDiagonalDErr+1
gLineDiagonalY:
  dw gLineDiagonalX+1


I'm also starting to realize how out of hand my usage of ld is getting, wow.
It might be possible to not have to rely on memory and keep some of those critical values in registers throughout your routine.
Yes, that is true and it's my end goal to be optimized, but first I want to get it working. Any ideas, or should/could I use someone else's no-clip line drawing algorithm?
Okay, I just finished the sprite routine! Beware of how ugly it may look; optimizations are coming soon:

Code:

; in: hl=address of sprite
; in: b=x, c=y
; in: d=height of sprite
; out: the sprite with height d pointed to by hl displayed on the screen at the coordinates (b,c)
gSprite:
  ld a,b
  ld (gSpriteSX),a
  ld a,c
  ld (gSpriteSY),a
  ld (gSpriteY),a
  ld a,d
  ld (gSpriteH),a
  push hl
gSpriteLoopY:
  ld a,(gSpriteSX)
  ld (gSpriteX),a
  pop hl
  ld a,(hl)
  inc hl
  push hl
  ld (gSpriteL),a
  ld c,%00000001
  ld b,%10000000
gSpriteLoopX:
  ld a,(gSpriteL)
  and b
  push bc
  push af
  call nz,gSpritePrint
  pop af
  call z,gSpriteErase
  ld a,(gSpriteX)
  inc a
  ld (gSpriteX),a
  pop bc
  ld a,b
  srl a
  ld b,a
  sla c
  jr nc,gSpriteLoopX

; gSpriteLoopY check
  ld a,(gSpriteH)
  ld b,a
  ld a,(gSpriteSY)
  add a,b
  ld b,a
  ld a,(gSpriteY)
  inc a
  ld (gSpriteY),a
  sub b
  jp nz,gSpriteLoopY
  pop hl
  ret
gSpritePrint:
  ld a,(gSpriteX)
  ld b,a
  ld a,(gSpriteY)
  ld c,a
  call gPrint
  ret
gSpriteErase:
  ld a,(gSpriteX)
  ld b,a
  ld a,(gSpriteY)
  ld c,a
  call gErase
  ret
gSpriteX:
  dw appBackUpScreen
gSpriteY:
  dw gSpriteX+1
gSpriteH:
  dw gSpriteY+1
gSpriteSX:
  dw gSpriteH+1
gSpriteSY:
  dw gSpriteSX+1
gSpriteL:
  dw gSpriteSY+1


Also I have a question: are sla and sra faster or slower than sll and srl respectively (which is faster: arithmetic or logical) Question
There's no difference in speed between different rotate operations on the same register.

If you're really interested in instruction timings, I suggest referring to something like this.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 1
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement