I recently got back into z80 asm after a few months of other stuff. I'm just curious as to how efficient or inefficient my code is. Also, two questions, what is the significance of .nolist and .list for the assembler? Do I have to put my #defines between them as if in a scope?

Code:
.nolist
#include "ti83plus.inc"

;******************
;*     Screen     *
;******************

; I typically only use these in one place, so defining them as macros
; rather than calling them is fine, at least in this scenario. Further
; developments may prove otherwise, but it is not important right now.
#define OpenGraphScreen bcall(_RunIndicOff) \ bcall(_ClrLCDFull) \ bcall(_GrBufClr)
#define CloseGraphScreen bcall(_GrBufClr) \ bcall(_RunIndicOn)
#define UpdateGraphScreen bcall(_GrBufCpy)
#define ClearHomeScreen bcall(_HomeUp) \ bcall(_ClrLCDFull) \ bcall(_ClrTxtShd)

#define WaitForKey bcall(_GetKey)
.list

.org userMem-2
.db $BB,$6D

Start:
    ClearHomeScreen
    OpenGraphScreen
   
    ; basic test case
    ld   h,11
    ld   l,27
    ld   e,156
    call DrawVerticalLine
   
    UpdateGraphScreen
    WaitForKey
    CloseGraphScreen
    ClearHomeScreen
    ret

;******************
;*    Graphics    *
;******************

; Desc:     sets hl to the address of the pixel at (b,c)
;           sets a to the mask of the pixel at (b,c)
; Inputs:   bc
; Outputs:  hl
; Destroys: af,bc,de,hl
; T-States: 412/167
; Size:     39
GetPixel:                             ; T-States ; Size
    ; calculate byte y offset (y*12)
    sla  c                            ; 8        ; 2
    sla  c                            ; 8        ; 2
    ld   d,0                          ; 7        ; 2
    ld   e,c                          ; 4        ; 1
    ld   h,0                          ; 7        ; 2
    ld   l,c                          ; 4        ; 1
    add  hl,hl                        ; 11       ; 1
    add  hl,de                        ; 11       ; 1
    ; calculate byte x offset (x/8)
    ld   a,b                          ; 4        ; 1
    rra                               ; 4        ; 1
    rra                               ; 4        ; 1
    rra                               ; 4        ; 1
    and  $1F                          ; 7        ; 2
    ; calculate pixel address
    ld   e,a                          ; 4        ; 1
    add  hl,de                        ; 11       ; 1
    ld   de,plotSScreen               ; 10       ; 3
    add  hl,de                        ; 11       ; 1
    ; calculate pixel mask
    ld   a,b                          ; 4        ; 1
    and  $07                          ; 7        ; 2
    ld   b,$80                        ; 7        ; 2
_GetPixel_Loop:
    or   a                            ; 4        ; 1
    jr   z,_GetPixel_Exit             ; 12/7     ; 2
    dec  a                            ; 4        ; 1
    srl  b                            ; 8        ; 2
    jr   _GetPixel_Loop               ; 12       ; 2
_GetPixel_Exit:
    ld   a,b                          ; 4        ; 1
    ret                               ; 10       ; 1

; Desc:     Turns on a pixel at (b,c)
; Inputs:   bc
; Outputs:  pixel on at (b,c)
; Destroys: af,bc,de,hl
; T-States: 453/208
; Size:     6
PixelOn:                              ; T-States ; Size
    call GetPixel                     ; 429/184  ; 3
    or   (hl)                         ; 7        ; 1
    ld   (hl),a                       ; 7        ; 1
    ret                               ; 10       ; 1

; Desc:     Turns off a pixel at (b,c)
; Inputs:   bc
; Outputs:  pixel off at (b,c)
; Destroys: af,bc,de,hl
; T-States: 457/212
; Size:     7
PixelOff:                             ; T-States ; Size
    call GetPixel                     ; 429/184  ; 3
    cpl                               ; 4        ; 1
    and  (hl)                         ; 7        ; 1
    ld   (hl),a                       ; 7        ; 1
    ret                               ; 10       ; 1

; Desc:     Inverts a pixel at (b,c)
; Inputs:   bc
; Outputs:  pixel inverted at (b,c)
; Destroys: af,bc,de,hl
; T-States: 453/208
; Size:        6
PixelInvert:                          ; T-States ; Size
    call GetPixel                     ; 429/184  ; 3
    xor  (hl)                         ; 7        ; 1
    ld   (hl),a                       ; 7        ; 1
    ret                               ; 10       ; 1

; Desc:     Displays a vertical clipped line from (x0,y0)=(h,l) to (x1,y1)=(h,e)
; Inputs:   e,hl
; Outputs:  None
; Destroys: af,bc,de,hl
; T-States: 5012/36
; Size:     42
DrawVerticalLine:                     ; T-States ; Size
    ; if y1 < y0, swap them
    ld   a,e                          ; 4        ; 1
    cp   l                            ; 4        ; 1
    jr   nc,_DrawVerticalLine_Skip    ; 12/7     ; 2
    ld   e,l                          ; 4        ; 1
    ld   l,a                          ; 4        ; 1
_DrawVerticalLine_Skip:
    ; check if line is completely off screen
    ld   a,63                         ; 7        ; 2
    cp   l                            ; 4        ; 1
    ret  c                            ; 11/5     ; 1
    ; clamp y1 to bottom of screen
    cp   e                            ; 4        ; 1
    jr   nc,DrawVerticalLineUnsafe    ; 12/7     ; 2
    ld   e,63                         ; 7        ; 2
; Only call this if the line is completely on screen and y0 <= y1
DrawVerticalLineUnsafe:
    ; get the address and mask of the first pixel
    ; Note: the mask stays the same for all pixels
    ld   b,h                          ; 4        ; 1
    ld   c,l                          ; 4        ; 1
    push bc                           ; 11       ; 1
    push de                           ; 11       ; 1
    call GetPixel                     ; 429/184  ; 3
    pop  de                           ; 10       ; 1
    pop  bc                           ; 10       ; 1
    ld   d,a                          ; 4        ; 1
_DrawVerticalLine_Loop:
    ; draw the pixel
    or   (hl)                         ; 7        ; 1
    ld   (hl),a                       ; 7        ; 1
    ; check if the line is done being drawn
    ld   a,c                          ; 4        ; 1
    cp   e                            ; 4        ; 1
    ret  z                            ; 11/5     ; 1
    ; get the address of the next pixel (hl+=12)
    ld   a,l                          ; 4        ; 1
    add  a,12                         ; 7        ; 2
    ld   l,a                          ; 4        ; 1
    ld   a,h                          ; 4        ; 1
    adc  a,0                          ; 7        ; 2
    ld   h,a                          ; 4        ; 1
    ; calculate next pixel information
    inc  c                            ; 4        ; 1
    ld   a,d                          ; 4        ; 1
    jr   _DrawVerticalLine_Loop       ; 12       ; 2

; Desc:     Displays a horizontal clipped line from (x0,y0)=(h,l) to (x1,y1)=(d,l)
; Inputs:   d,hl
; Outputs:  None
; Destroys:
; T-States:
; Size:     
DrawHorizontalLine:                   ; T-States ; Size
    ; TODO
    ret

; Desc:     Displays a clipped line from (x0,y0)=(h,l) to (x1,y1)=(d,e)
; Inputs:   de,hl
; Outputs:  None
; Destroys:
; T-States:
; Size:     
DrawLine:                             ; T-States ; Size
    ; TODO
    ret

Edit: Misaligned comments.
Any text inbetween .nolist and .list will simply excluded from the listing file generated by the assembler.

As for your code it looks pretty good imo - a couple of little wins around loading with constants and such.

I guess there are 2 important aspects to code efficiency. The first is the actual code itself as in using registers appropriately and coding to the advantage of the platform. The second is thinking about the most efficient or effective procedure to achieve your goal, considering the platform.

Since efficiency can be measured in different ways (consider speed v size), there is no set in stone answer in the general sense I guess.
I threw together some untested code to compare to, partially stolen from the usual source.
Code:
; Desc:     sets hl to the address of the pixel at (b,c), or plotSScreen if out of bounds
;           sets a to the mask of the pixel at (b,c), or 0 if out of bounds
;           sets cf to whether (b,c) is out of bounds
; Inputs:   bc
; Outputs:  cf,a,hl
; Destroys: af,bc,hl
; T-States: 289/171
; Size:     39
GetPixel:                             ; T-States ; Size
    ld   hl,plotSScreen               ; 10       ; 3
    ld   a,95                         ; 7        ; 2
    cp   b                            ; 4        ; 1
    ld   a,0                          ; 7        ; 2
    ret   c                           ; 11/5     ; 1
    sla   c                           ; 8        ; 2
    ret   c                           ; 11/5     ; 1
    sla   c                           ; 8        ; 2
    ret   c                           ; 11/5     ; 1
    ld   a,b                          ; 4        ; 1
    ld   b,0                          ; 7        ; 2
    add  hl,bc                        ; 11       ; 1
    add  hl,bc                        ; 11       ; 1
    add  hl,bc                        ; 11       ; 1
    ld   c,a                          ; 4        ; 1
    srl  c                            ; 8        ; 2
    srl  c                            ; 8        ; 2
    srl  c                            ; 8        ; 2
    add  hl,bc                        ; 11       ; 1
    and  %00000111                    ; 7        ; 2
    ld   b,a                          ; 4        ; 1
    ld   a,%10000000                  ; 7        ; 2
    ret  z                            ; 11/5     ; 1
_GetPixel_Loop:
    rrca                              ; 4        ; 1
    djnz _GetPixel_Loop               ; 13/8     ; 2
    ret                               ; 10       ; 1

; Desc:     Displays a vertical clipped line from (x0,y0)=(b,c) to (x1,y1)=(b,e)
; Inputs:   bc,e
; Outputs:  None
; Destroys: af,bc,de,hl
; T-States: 3086/318
; Size:     31
DrawVerticalLine:                     ; T-States ; Size
    ld   a,e                          ; 4        ; 1
    cp   c                            ; 4        ; 1
    jr   nc,_DrawVerticalLine_Swap    ; 12/7     ; 2
    ld   e,c                          ; 4        ; 1
    ld   c,a                          ; 4        ; 1
_DrawVerticalLine_Swap:
    ld   a,95                         ; 7        ; 2
    cp   e                            ; 4        ; 1
    jr   c,_DrawVerticalLine_Clip     ; 12/7     ; 2
    ld   a,e                          ; 4        ; 1
_DrawVerticalLine_Clip:
    sub  c                            ; 4        ; 1
    ld   e,a                          ; 4        ; 1
    call GetPixel                     ; 306/188  ; 3
    ret  c                            ; 11/5     ; 1
    ld   c,a                          ; 4        ; 1
    ld   b,e                          ; 4        ; 1
    inc  b                            ; 4        ; 1
    ld   de,12                        ; 10       ; 3
_DrawVerticalLine_Loop:
    ld   a,(hl)                       ; 7        ; 1
    or   c                            ; 4        ; 1
    ld   (hl),a                       ; 7        ; 1
    add  hl,de                        ; 11       ; 1
    djnz _DrawVerticalLine_Loop       ; 13/8     ; 2
    ret                               ; 10       ; 1
I have since then created my own clipped and unclipped line functions/methods/whatever in ez80 asm for 8bpp mode. After I created it, I referenced the example provided by jacobly to optimize out one opcode to make it slightly smaller and faster. A consequence of that has the line rendering from bottom up instead of top down. However, I do have a double buffering context setup so it's literally impossible to tell without looking at the source code. Speaking of which, here it is:

Code:
; Description:  Gets the address of a pixel in the currentDrawBuffer or vRamEnd if off screen
; Inputs:       a, bc
;               x = bc
;               y = a
; Outputs:      hl = address of the pixel in the currentDrawBuffer or vRamEnd if off screen
; Destroys:     f, de, hl
GetPixel:
    ; check if pixel is within y bounds
    cp   lcdHeight
    jr   nc, __GetPixel_OffScreen__
    ; check if pixel is within x bounds
    ld   hl, lcdWidth
    sbc  hl, bc
    jr   nc, GetPixelUnchecked
__GetPixel_OffScreen__:
    ld   hl, vRamEnd
    ret

; Description:  Gets the address of a pixel in the currentDrawBuffer
;               Doesn't check bounds, so only call if you're sure the pixel is on screen
; Inputs:       a, bc
;               x = bc
;               y = a
; Outputs:      hl = address of the pixel
; Destroys:     f, de, hl
GetPixelUnchecked:
    ; hl = a * lcdWidth
    ld   hl, lcdWidth / 2
    ld   h, a
    mlt  hl
    add  hl, hl
    ; hl = a * lcdWidth + bc
    add  hl, bc
    ; hl = (currentDrawBuffer) + a * lcdWidth + bc
    ld   de, (currentDrawBuffer)
    add  hl, de
    ret

; Description:  Draws a clipped, vertical line to the currentDrawBuffer
; Inputs:       bc, hl, ixl
;               x = bc
;               y0 = h
;               y1 = l
;               color = ixl
; Outputs:      None
; Destroys:     af, bc, de, hl
DrawVerticalLine:
    ; check if line is completely off screen
    ex   de, hl
    ld   hl, lcdWidth - 1
    or   a
    sbc  hl, bc
    ret  c
    ex   de, hl
    ; if y0 > y1; swap them
    ld   a, l
    cp   h
    jr   nc, __DrawVerticalLine_Skip1__
    ld   l, h
    ld   h, a
__DrawVerticalLine_Skip1__:
    ; check if line is completely off screen still
    ld   a, lcdHeight - 1
    cp   h
    ret  c
    ; clamp y1 to lcdHeight - 1
    cp   l
    jr   nc, DrawVerticalLineUnchecked
    ld   l, a
    ; FALLTHROUGH INTENDED

; Description:  Draws an unclipped, vertical line from (x, y0) to (x, y1) to the currentDrawBuffer
;               Doesn't clip line nor check if y0 > y1, so only call if you're sure the line is completely on screen
; Inputs:       bc, hl, ixl
;               x = bc
;               y0 = h
;               y1 = l
;               color = ixl
; Outputs:      None
; Destroys:     af, bc, de, hl
DrawVerticalLineUnchecked:
    ; get the address of the first pixel
    ld   a, l
    push hl
    call GetPixelUnchecked
    pop  bc
    ; setup for drawing pixels
    sub  b
    inc  a
    ld   b, a
    ld   de, lcdWidth
    ld   a, ixl
__DrawVerticalLineUnchecked_Loop__:
    ld   (hl), a ; draw the pixel
    sbc  hl, de ; prepare for next pixel
    djnz __DrawVerticalLineUnchecked_Loop__
    ret

Also my code style has slightly changed due to me using VS Code, SPASM-ng, and CEmu as opposed to jsTIfied and CEmu. Specifically, I made a small python script to use spasm to compile my files, and python's double-underscore-for-private-members thing stuck with me. Although this setup isn't perfect, it's far better in my opinion to the point where I deem it unnecessary to code my own ide for it; yes I did actually consider that.
You should not be using spasm. Use fasmg: https://github.com/CE-Programming/asm-docs
There's two three obvious optimizations saving 4 bytes/fetches:
Code:
GetPixelUnchecked:
    ld   hl, (currentDrawBuffer)
    add  hl, bc
    ld   e, lcdWidth / 2
    ld   d, a
    mlt  de
    add  hl, de
    add  hl, de
    ret

DrawVerticalLineUnchecked:
    ; get the address of the first pixel
    ld   a, l
    push hl
    call GetPixelUnchecked
    pop  bc
    ; setup for drawing pixels
    sub  b
    inc  a
    ld   b, a
    ld   de, -lcdWidth
    ld   a, ixl
__DrawVerticalLineUnchecked_Loop__:
    ld   (hl), a ; draw the pixel
    add  hl, de ; prepare for next pixel
    djnz __DrawVerticalLineUnchecked_Loop__
    ret
MateoConLechuga wrote:
You should not be using spasm. Use fasmg: https://github.com/CE-Programming/asm-docs


I have done so, and it works fine, but given the slightly different syntax with the whole "namespace ti" thing, I was just wondering if there was an ide that recognizes that. Otherwise, I'd have to change my current syntax highlighter or write my own.
I have a sorta-working sublime syntax define for fasmg:
https://github.com/commandblockguy/ez80-sublime-syntax/tree/fasmg
Overall, syntax highlighting is a really minor issue, though.
  
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