Kerm and Mateo pushed me to create a thread with all of my questions.
My current project is a port of the World's Hardest Game for the TI 84+CE.
On this thread, if you answer a question, please include the why and how, and possibly some sample code to get me started.
Thank you for your help!

My first question is... how do I move a square around the screen in ez80 asm?
While that is a fantastic idea for a starting program, you may want to start even simpler. Such as, how would I draw a rectangle on the screen in the first place? The CE's LCD supports many different modes, and is in 16bpp as default. The location of the LCD's data is located at the memory-mapped location called VRAM, which equates to 0xD40000. You will need to come up with some code that draws a line at an (x,y) point, and then repeat this same horizontal line for the height of the rectangle. If you need some coding guidance, we would be more than happy to help. Smile
That probably is a better program to write. How do you write to the LCD? I think that is a better question.
WikiTI is your friend. Take a look at the LCD controller and the memory layout. To get things to display on the screen, the easiest thing to do is to write to the LCD memory (VRAM/GRAM, starting at address D40000h). It's arranged as 240 rows, each 320 2-byte pixels wide, for a total of 320*240*2 bytes. The pixels are each 16 bytes representing bit-packed red, green, and blue values: 5 bits of red, followed by 6 bits of green, followed by 5 bits of blue. SourceCoder and other tools can help you translate between 24-bit color (8 bits each of red, green, and blue) and the 5-6-5 format.

Edit: For example, set the top-left pixel to white, and the bottom-right pixel to black:
Code:
    ld de,$ffff ; white
    ld hl,VRAM + (0 * 320 * 2) + (0 * 2)
    ld (hl),e
    inc hl
    ld (hl),d

    ld de,$0000 ; black
    ld hl,VRAM + (239 * 320 * 2) + (319 * 2)
    ld (hl),e
    inc hl
    ld (hl),d
Kerm, that code is exceptional!
My next question comes from trying to implement that as a routine. I have the following code:

Code:
.nolist
#include "includes\ti84pce.inc"   
.list
.org userMem-2
.db tExtTok,tAsm84CeCmp
.assume ADL=1
pixelX equ cmdpixelshadow
pixelY equ cmdpixelshadow+16

 LD HL,20
 LD (pixelX),HL
 LD HL,30
 LD (pixelY),HL
drawFrame:
 call _GetCSC
 cp $01
 jp Z,downPressed
 jp renderFrame
downPressed:
 LD HL,(pixelY)
 DEC HL
 LD (pixelY),HL
 jp renderFrame
 ret
renderFrame:
 ld DE,$F800
 LD BC,(pixelX)
 LD l,pixelY
 call drawPixel
 jp drawFrame

drawPixel:
 ld h,160
 mlt hl
 add hl,hl \ add hl,hl
 add hl,bc
 ld bc,vram
 add hl,bc
 ld (hl),e
 inc hl
 ld (hl),d
 ret


This immediately crashes the calculator.
Does anyone know what I am doing wrong?
The problem is you are treating addresses like variables. They are not variables, just locations in memory where you can store things to. This should work better; hopefully this is a better thing to learn off of. Note this is untested.


Code:
.nolist
#include "includes\ti84pce.inc"   
.list
.assume ADL=1
.org userMem-2
.db tExtTok,tAsm84CeCmp
   
pixelX equ cmdpixelshadow
pixelY equ pixelX+3
color equ pixelY+1

 ld hl,20
 ld (pixelX),hl
 ld a,30
 ld (pixelY),a
 ld de,0000h
 ld (color),de
drawFrame:
 call _GetCSC
 cp skDown
 call z,downPressed
 call renderFrame
 jr drawFrame

downPressed:
 ld hl,pixelY
 dec (hl)
 ret

renderFrame:
 ld de,(color)
 ld bc,(pixelX)
 ld hl,(pixelY)
 call drawPixel
 ret

drawPixel:
 ld h,160
 mlt hl
 add hl,hl \ add hl,hl
 add hl,bc
 ld bc,vram
 add hl,bc
 ld (hl),e
 inc hl
 ld (hl),d
 ret
Thank's mateo. Now I am trying to figure out how to clear the screen on each draw, implementing solid frame rate. Code is as follows:


Code:
.nolist 
#include "includes\ti84pce.inc"     
.list 
.assume ADL=1 
.org userMem-2 
.db tExtTok,tAsm84CeCmp 
   
pixelX equ cmdpixelshadow 
pixelY equ pixelX+3
color equ pixelY+1

 ld hl,20 
 ld (pixelX),hl
 ld a,30 
 ld (pixelY),a 
 ld de,0000h
 ld (color),de
 call _RunIndicOff
 call clrScreen
 call drawFrame
clrScreen:
 ld hl,vram
 push hl
 pop de
 inc de
 ld (hl),0ffh
 ld bc,(320*240*2 )-1
 ldir
 ret
drawFrame: 
 call clrScreen
 call _GetCSC 
 cp skDown
 call z,downPressed 
 call renderFrame
 jr drawFrame

downPressed: 
 ld hl,pixelY
 dec (hl) 
 ret

renderFrame:
 ld de,(color)
 ld bc,(pixelX)
 ld hl,(pixelY)
 call drawPixel
 ret

drawPixel:
 ld h,160
 mlt hl
 add hl,hl \ add hl,hl
 add hl,bc
 ld bc,vram
 add hl,bc
 ld (hl),e
 inc hl
 ld (hl),d
 ret
I would really, really recommend you take a look at the manual and see how jumps and calls actually behave. You only need to clear the screen if the down key is pressed, otherwise the data should remain fixed. Here's an example of what I mean:


Code:
.nolist   
#include "includes\ti84pce.inc"     
.list   
.assume ADL=1   
.org userMem-2   
.db tExtTok,tAsm84CeCmp   
     
pixelX equ cmdpixelshadow   
pixelY equ pixelX+3   

 ld hl,20   
 ld (pixelX),hl 
 ld a,30   
 ld (pixelY),a   
 ld de,0000h
 ld (color),de
 call _RunIndicOff
 call clrScreen
 call renderFrame

drawFrame:
 call _GetCSC   
 cp skDown 
 call z,downPressed
 jr drawFrame 

downPressed:
 ld hl,pixelY 
 dec (hl)
 call clrScreen
 call renderFrame 
 ret 

renderFrame: 
 ld bc,(pixelX) 
 ld hl,(pixelY) 
 call drawPixel 
 ret 

drawPixel:
 ld h,160 
 mlt hl 
 add hl,hl \ add hl,hl 
 add hl,bc 
 ld bc,vram 
 add hl,bc
color: = $+1
 ld de,0
 ld (hl),e 
 inc hl 
 ld (hl),d 
 ret

clrScreen:
 ld hl,vram
 push hl
 pop de
 inc de
 ld (hl),0ffh
 ld bc,(320*240*2)-1
 ldir
 ret
Do you have to write pixel by pixel or is there a faster method than this: (this isn't even done)

Code:
 ld de,(color)
 ld bc,(pixelX)
 ld hl,(pixelY)
 call drawPixel
 ld de,(color)
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 inc hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 dec bc
 dec hl
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 dec hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 dec bc
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 dec hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 inc hl
 call drawPixel

And so on...
KingInfinity wrote:
Do you have to write pixel by pixel or is there a faster method than this: (this isn't even done)

Code:
 ld de,(color)
 ld bc,(pixelX)
 ld hl,(pixelY)
 call drawPixel
 ld de,(color)
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 inc hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 dec bc
 dec hl
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 dec hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 dec bc
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 dec hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 inc hl
 call drawPixel

And so on...

That's how the program flow works, but even if you wanted to do this exact thing, here's what you would do:

Code:

PixelDraw:
ld de,(color)
 ld bc,(pixelX)
 ld hl,(pixelY)
 call drawPixel

And call it from a loop. I don't remember exactly how to make loops so you'll have to do that yourself.
(Also man I need to get back into CSE ASM)
Here is my working code Very Happy
It creates a "World's Hardest Game" like sprite that moves around the screen. Press mode to exit.


Code:
.nolist 
#include "includes\ti84pce.inc"   
.list 
.assume ADL=1 
.org userMem-2 
.db tExtTok,tAsm84CeCmp 
   
pixelX equ cmdpixelshadow 
pixelY equ pixelX+3
color equ pixelY+1 

 ld hl,12 
 ld (pixelX),hl
 ld a,12
 ld (pixelY),a 
 ld de,$F861
 ld (color),de
 LD (fillRectColor),de
 ld de,$0000
 LD (drawFGColor),de
 call _RunIndicOff
 call clrScreen
 call drawFrame
clrScreen:
 ld hl,vram
 push hl
 pop de
 inc de
 ld (hl),0ffh
 ld bc,(320*240*2 )-1
 ldir
 ret
drawFrame: 
 call _GetCSC 
 cp skUp
 call z,upPressed 
 cp skDown
 call z,downPressed 
 cp skLeft
 call z,leftPressed 
 cp skRight
 call z,rightPressed 
 cp skMode
 ret z
 call renderFrame
 jr drawFrame
downPressed: 
 ld hl,(pixelY)
 ld DE,8
 ADD HL,DE
 ld (pixelY),HL
 call clrScreen
 ret
upPressed: 
 ld hl,(pixelY)
 ld DE,8
 SBC HL,DE
 ld (pixelY),HL
 call clrScreen
 ret
leftPressed: 
 ld hl,(pixelX)
 ld DE,8
 SBC HL,DE
 ld (pixelX),HL
 call clrScreen
 ret
rightPressed: 
 ld hl,(pixelX)
 ld DE,8
 ADD HL,DE
 ld (pixelX),HL
 call clrScreen
 ret
renderFrame:
 ;top edge coordinate is B
 ld a,(pixelY)
 ld B,A
 ;Add 20 to PixelY and put the value in C
 add a,15
 ld c,a

 LD HL,(pixelX) ;left edge coordinate
 ;right edge coordinate
 LD DE,15
 add HL,DE
 ex DE,HL
 LD HL,(pixelX) ;left edge coordinate
 call _DrawRectBorder
 call drawBorder
 call drawBorder
 INC HL
 DEC DE
 LD a,c
 sub a,1
 LD c,a
 LD a,b
 add a,1
 LD b,a
 call _FillRect
 ret
drawBorder:
 INC HL
 DEC DE
 LD a,c
 sub a,1
 LD c,a
 LD a,b
 add a,1
 LD b,a
 call _DrawRectBorder
 ret
drawPixel:
 ld h,160
 mlt hl
 add hl,hl \ add hl,hl
 add hl,bc
 ld bc,vram
 add hl,bc
 ld (hl),e
 inc hl
 ld (hl),d
 ret


Is there a way to modify this so that if you do, for example, both up and right, it does diagonally to the northeast?

Also, is there a ROM call to draw a circle of a certain radius at a certain coordinate?
I don't think _GetCSC supports multiple keypresses, it only returns one keypress (the first key pressed/held/read). You'd have to read from the key registers directly.
KingInfinity wrote:
Do you have to write pixel by pixel or is there a faster method than this: (this isn't even done)

Code:
 ld de,(color)
 ld bc,(pixelX)
 ld hl,(pixelY)
 call drawPixel
 ld de,(color)
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 inc hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 dec bc
 dec hl
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 dec hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 dec bc
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 dec hl
 call drawPixel
 
 ld bc,(pixelX)
 ld hl,(pixelY)
 inc bc
 call drawPixel

 ld bc,(pixelX)
 ld hl,(pixelY)
 inc hl
 call drawPixel

And so on...


For drawing horizontal lines (which is often what you'll want to do), there is a much faster way.

Code:

    ld hl,FirstPixel
    ld de,FirstPixel+1
    ld bc,LineLength-1
    ldir


For drawing arbitrary shapes (AKA sprites) you will have to write a sprite routine.

Note to Mateo: Wouldn't it be better to start learning graphical stuff in 8bpp mode? I find it a LOT easier to work with, and it doesn't require that much extra setup.
Adding a bit to what Hactar said, with a sprite routine what you are essentially doing is drawing a box. The VRAM is one continuous chunk of memory, so what you can do is something like this:

Code:
; hl = pointer to sprite data
: 8x8 sprite routine
drawSprite:
    ld a,8 ; there are 8 rows in the sprite (sprite's 8 pixels tall)
    ld de,vram
spriteLoop:
    ld bc,NUM_BYTES_PER_ROW ; 16 if 16bpp, 8 bytes if 8bpp
    ldir ; copies a row of sprite data to the VRAM
    ld bc,320 - NUM_BYTES_PER_ROW
    ex de,hl ; swap hl and de, there's no "add de,bc" so we swap de into hl
    add hl,bc ; move vram pointer down to next row in vram
    ex de,hl
    dec a ; loop counter
     jr nz,spriteLoop
    ret
That should (hopefully) draw a sprite to the top left corner. You'd probably want to add x/y coordinates for the sprite, too. You just need to change the starting VRAM position (add the x/y offset).
Hello all, this code isn't doing anything right, here it is, in case you know what's wrong xD
Known issues:
Wrong colors
Box is stretched out (wide)
Doesn't end on pressing mode, I'd assume because the loop continues to execute, but I dunno at this point
Screenshot:


Code:
.nolist 
#include "includes\ti84pce.inc"   
.list 
.assume ADL=1 
.org userMem-2 
.db tExtTok,tAsm84CeCmp 
_ClearVRAM equ 000374h
pixelX equ cmdpixelshadow 
pixelY equ pixelX+3

 ld hl,12 
 ld (pixelX),hl
 ld a,12
 ld (pixelY),a 
 call _RunIndicOff
 call _clearVRAM
 ld a,lcdbpp8
 ld (mpLcdCtrl),a
 call drawFrame
 
create1555Palette:
 ld hl,mpLcdPalette             ; MMIO address of LCD Palette
 ld b,0
_cp1555Loop:
 ld d,b
 ld a,b
 and a,%11000000
 srl d
 rra
 ld e,a
 ld a,%00011111
 and a,b
 or a,e
 ld (hl),a
 inc hl
 ld (hl),d
 inc hl
 inc b
 jr nz,_cp1555Loop
drawFrame: 
 call _GetCSC 
 cp skUp
 call z,upPressed 
 cp skDown
 call z,downPressed 
 cp skLeft
 call z,leftPressed 
 cp skRight
 call z,rightPressed 
 cp skMode
 call z,endGame
 ret z
 call renderFrame
 jr drawFrame
endGame:
 call _clearVRAM
 ld a,lcdbpp16
 ld (mpLcdCtrl),a
 call _DrawStatusBar
 ei
 ret
downPressed: 
 ld hl,(pixelY)
 ld DE,8
 ADD HL,DE
 ld (pixelY),HL
 call _clearVRAM
 ret
upPressed: 
 ld hl,(pixelY)
 ld DE,8
 SBC HL,DE
 ld (pixelY),HL
 call _clearVRAM
 ret
leftPressed: 
 ld hl,(pixelX)
 ld DE,8
 SBC HL,DE
 ld (pixelX),HL
 call _clearVRAM
 ret
rightPressed: 
 ld hl,(pixelX)
 ld DE,8
 ADD HL,DE
 ld (pixelX),HL
 call _clearVRAM
 ret
renderFrame:
 ld hl,Sprite_sprite
 ld a,(pixelX)
 ld B,A
 ld a,(pixelY)
 ld C,A
 call drawSprite8bpp
 ret
;--------------------------------------------------------------
; drawSprite8bpp
; 8bpp sprite drawn to screen
; b,c = x,y
; hl -> sprite
;--------------------------------------------------------------
drawSprite8bpp:
 ld de,SaveW8bpp+1   ; Width
 call setSpriteBoundary
 push ix
 pop hl
drawLoopLineNext8bpp:
 push bc
  push de
SaveW8bpp:
   ld b,0
drawLoopLine8bpp:
   ld a,(hl)
   ld (de),a
   inc de
   ld (de),a
   inc de
   inc hl
   djnz drawLoopLine8bpp
   ex de,hl      ; de = hl
  pop hl
  ld bc,160*2
  add hl,bc
  ex de,hl
 pop bc
 djnz drawLoopLineNext8bpp
 ret
setSpriteBoundary:
 ld a,(hl)      ; a = w, b = h
 push af
  inc hl
  ld a,(hl)
  ld (de),a      ; Width
  inc hl      ; hl->data
  push hl

  ld hl,0
  ld l,b 
  ld a,c
  add hl,hl      ; b*2
  add hl,hl      ; b*4
  push hl      ; Save X
   ld de,160*2
   inc a
   ld b,8
   ld hl,0
   
   add hl,hl
   rlca
   jr nc,$+3
   add hl,de
   djnz $-5
   ld de,vRam-(160*2)
   add hl,de
  pop de      ; de=X
  add hl,de      ; Add X
  ex de,hl      ; de->place to draw
  pop ix
 pop bc
 ret
#include "sprite.bin"

And sprite.bin:

Code:
Sprite_paletteStart:
 .dw $8000   ; 0
 .dw $fc00   ; 1
Sprite_paletteEnd:
Sprite_sprite:
 .db $10,$10   ; width, height
 .db $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
 .db $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
 .db $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$1,$1,$1,$1,$1,$1,$1,$1,$1,$1,$0,$0,$0
 .db $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
 .db $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
 .db $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0
KingInfinity wrote:
Hello all, this code isn't doing anything right, here it is, in case you know what's wrong xD
Known issues:
Wrong colors
Weird display issues
Doesn't end on pressing mode, I'd assume because the loop continues to execute, but I dunno at this point
What have you done to debug it so far? You probably already know this, but I strongly recommend trying to solve one of those issues at a time, rather than getting overwhelmed looking at the whole list. Also, as a general piece of advice, I usually try to build my programs one component at a time, making sure that component (for example, displaying a sprite) works correctly before adding the next component on top. That can help you track down which component a particular problem is coming from, even if you don't have an emulator or debugger available.
I agree with Kerm, adding components one at a time (and debugging them one at a time) is how i generally do things. That way you can make sure one thing is (more or less) working before moving on to the next. Writing a ton of code all at once is a great way to get a headache and spend more time debugging than coding. The mode issue is pretty simple. Look at your code here:

Code:
 cp skMode 
 call z,endGame
 ret z
Do you not see anything odd here? First off, your endGame routine is called. When it's done running, it'll return to where it was called from and continue running. I assume this is why you added the ret z. But keep in mind that the flags change very often, in your endGame routine you use two system routines. Both of those almost certainly change the values of the flags. If you want to preserve the flags, you'd have to put the af register on the stack and pop it before you exit endGame. But a better solution would be to directly exit from the endGame routine. Rather than calling it, what can you do so that it doesn't return back to the main loop?

For the other questions, i can't help much. Try drawing different types of sprites, for example a sprite like this:
111111111
101010101
101010101
...
111111111
You can maybe get an idea of what's going wrong and where. Though if your sprite is 8bpp, why do you load the value two times in drawLoopLine8bpp? (ld (de),a \ inc de \ ld (de),a \ inc de)

EDIT: Btw, call label \ ret is the same as jp label.
Thanks, Chickendude. I didn't see that I was calling instead of jumping. :
Also, I fixed the width issue. My issue is now only with the colors and another issue with ending:
I have to press mode twice to end, once to switch into 16bpp mode and again to finally quit.
The issue might be because you call drawFrame at the start and then the code continues running (back to drawFrame). Actually, now that i look at it, it looks like your palette code never runs until you exit with [Mode]. Try removing (or commenting out) the first call to drawFrame and seeing if that fixes anything.
Based on the discussion of your ASM code on IRC, I wanted to clarify and expound further on a few points:
  • The best use of ASM tutorials is as a way to learn techniques, test them out by running the code provided in the tutorials, extend them by modifying the given programs slightly to see if hypotheses about how you can modify them work, and then write and test your own code with those techniques. Tutorials shouldn't be considered a source of code to copy-and-paste into your own independent, released programs, because that usually implies that you don't understand the concept well enough to write it yourself.
  • With any language, especially ASM, start small and work your way up towards larger and more complex projects only as you're confident about it. Better to knock out a few projects that are at or below your skill level and build experience and confidence than to get frustrated trying something above your skill or concentration.
  • ASM is the extreme of a structured language, where what you write is exactly what the processor will do. Before you write the program, outline what you want it to do. If you must, make a flow chart or write pseudocode. When you're debugging your code, try to execute it line-by-line in your head as if you were the processor, tracking the values of registers and seeing where what happens diverges from what you expect to happen. Add debug print statements (and when we get an emulator, use the debugger!).
Good luck. And remember to post questions (including code) in this thread.
  
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 3
» 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