Note: In ALL posts, if it's red, I have not started on it yet. If it's yellow, I'm working on it. If it's green, that means it's solved.

All right then. I was hoping that I could keep this a secret, and have a surprise for everyone for New years, but I can't hide it anymore. Sad

It's a ASM program that simply draws a green Christmas tree on the screen and has color-changing lights. So far, I've worked out the colors that I want it to be(here they are), and I know how to make a specific pixel a specific color, as illustrated by Kerm here:
KermMartian wrote:

Code:
    ld de,$ffff ; white
    ld hl,VRAM + (0 * 320 ) + (0 * 2)
    ld (hl),e
    inc hl
    ld (hl),d

    ld de,$0000 ; black
    ld hl,VRAM + (239 * 320) + (319 * 2)
    ld (hl),e
    inc hl
    ld (hl),d
call _getkey
Ret

My problems are:

    1. Getting a actual tree onto the screen.
    2. Getting the program to exit on the press of any key
    3. I might have to put a delay in here. Just to make sure that the lights on the tree aren't going so fast that you can barely tell what color each light is.


And a question: Does eZ80 need a loop like TI-Basic does? Because with basic, there has to be a Repeat or a While if you want the commands to execute more than once. Also, would it be better to have it in 16bpp mode, or 8bpp mode?

EDIT: Looking at the above code, I just realized that I would have to write each block of code HUNDREDS of times to get each light to be every color that I want it to be. Is there a simpler/shorter way to write the above code? This way, I wouldn't have to write hundreds of blocks of code. To be specific, I don't want to have to write a individual block of code for each pixel, for each color. Because if I have 100 pixels that change color, and 100 colors, that works out to 50,000 lines of code, which is not counting setup, or drawing a green tree, or anything else. I think there's a better way to do this, rather than writing... well, you know.

To do list (so far)
    1. Figure out how to write a sprite routine.
    2. Get a pixel onscreen.
    3. Get that pixel to change color
    4. Get a bunch of pixels to change color
Quote:
I might have to put a delay in here. Just to make sure that the lights on the tree aren't going so fast that you can barely tell what color each light is.


You can put the following code :

Code:

     ld b,100
loop:
     halt
     halt
     djnz loop


(If it does not work, replace the two HALT by one "call _homeup")
Also make sure interrupts are enabled before running halt (otherwise you'll be waiting for an interrupt that never comes).

To answer your questions:
1. You'll want to look into how sprites work. You'll draw a sprite of a tree, and use a sprite routine to display it.
2. You'll probably want to look at _GetCSC. You'll loop until the accumulator (a) doesn't equal zero.
3. Epharius gave a nice solution to this for you Smile
4. Assembly only needs a loop if you want your code to loop. If you just want your code to loop once, then there's no need for a loop. You can use the jp/jr instructions to form your loop.

As to how to draw your lights, what I would do is make a table with a list (x and y coordinates) of where you want all the lights to be (unless there's a mathematical way to calculate their positions). You can also add an extra color/palette byte if you want. Then you would loop through that list, drawing each 'bulb' as you read its coordinates. First, let's get your tree showing, then get one bulb to show up and change colors. Once you've got one bulb working, it should be pretty simple to get multiple bulbs working.
chickendude wrote:
Also make sure interrupts are enabled before running halt (otherwise you'll be waiting for an interrupt that never comes).

1. What is a interrupt?
2. How would I put a interrupt in?
chickendude wrote:
1. You'll want to look into how sprites work. You'll draw a sprite of a tree, and use a sprite routine to display it.
2. You'll probably want to look at _GetCSC. You'll loop until the accumulator (a) doesn't equal zero.
3. Epharius gave a nice solution to this for you Smile
4. Assembly only needs a loop if you want your code to loop. If you just want your code to loop once, then there's no need for a loop. You can use the jp/jr instructions to form your loop.

I don't know much about sprites; could you explain them?
And when I went to the webpage, it just showed it. No description whatsoever.
And from what I've read about jp and jr, it would look like I would want to use jp, becuause jr might not jump far enough. And how do I put something in for the jp to jump to?
caleb1997 wrote:
1. What is a interrupt?
2. How would I put a interrupt in?

In this case, all you care about is enabling or disabling them. You can use the ei to enable interrupts or di to disable them, but none of that should be necessary since TI-OS has interrupts enabled by default (right?).

caleb1997 wrote:
chickendude wrote:
1. You'll want to look into how sprites work. You'll draw a sprite of a tree, and use a sprite routine to display it.
2. You'll probably want to look at _GetCSC. You'll loop until the accumulator (a) doesn't equal zero.
3. Epharius gave a nice solution to this for you Smile
4. Assembly only needs a loop if you want your code to loop. If you just want your code to loop once, then there's no need for a loop. You can use the jp/jr instructions to form your loop.

I don't know much about sprites; could you explain them?
And when I went to the webpage, it just showed it. No description whatsoever.
And from what I've read about jp and jr, it would look like I would want to use jp, becuause jr might not jump far enough. And how do I put something in for the jp to jump to?

Much of WikiTI is not very useful for someone starting out in assembly. Sprites are, simply put (haha, pun!), an image to display to the screen. You need a routine that can display them to the screen, and usually that routine takes an X position and a Y position as inputs.
call _GetCSC will check if a key is pressed and, if one is, put a value in register A that corresponds to that key. If no key was pressed, it will put zero into A. (It also destroys all the other registers, so if there was anything important in HL, BC or DE, you should push it to the stack first.) In this case, since you don't care which key was pressed (AFAICT) you can use the following code:
[code]
Start:
call DrawTree ; Assumes you have a routine that can draw the tree, probably using a sprite routine; I don't have one that works in 16bpp mode right now
KeyLoop:
call ChangeColors ; This is where you change the colors of the Christmas tree (not sure 100% how to explain how to approach this in 16bpp mode)
call Delay ; This is where you put your delay
call _GetCSC
or a,a ; Sets the Z flag if A is zero, otherwise resets it (basically checks if A is zero)
jr z,KeyLoop ; If A is zero (i.e. no key has been pressed) then repeat
jp Quit ; This is where you exit

Code:

Start:
   call DrawTree ; Assumes you have a routine that can draw the tree, probably using a sprite routine; I don't have one that works in 16bpp mode right now
KeyLoop:
   call ChangeColors ; This is where you change the colors of the Christmas tree (not sure 100% how to explain how to approach this in 16bpp mode)
   call Delay ; This is where you put your delay
   call _GetCSC
   or a,a ; Sets the Z flag if A is zero, otherwise resets it (basically checks if A is zero)
   jr z,KeyLoop ; If A is zero (i.e. no key has been pressed) then repeat
   jp Quit ; This is where you exit


Please note that I will be doing this in 8bpp mode, so that I can use a bigger palette of colors for a more interesting Christmas Tree. And how would I get started on making a sprite routine?

To do:

    1. Figure out setting up 8 bpp mode.
    2. Figure out a sprite routine.
    3.Get a single pixel on-screen.


I'll go from here.
I'm not sure about how to set up 8bpp, one of the CE programmers can probably help you out with that (or look at some of the other help topics, it may have been discussed before). Ah, just found it here. To set 8bpp mode you just write a value to the LCD control register:

Code:
ld a,lcdBpp8
ld (mpLcdCtrl),a

To get a pixel on screen, you'll need to set up your palette (KingInfinity's topic has some code for this, i believe, Kerm and Mateo also answered a couple of your questions) then write a byte to the VRAM. In 8bpp mode, each pixel will take up one byte, this makes drawing sprites really simple. You just calculate the offset of your sprite on screen and copy each row of sprite data to the VRAM. To calculate the X offset, you can just add the number of pixels you want to move to the right. To calculate the Y offset, you need to multiply your Y position by the width of the screen in pixels. For example:

Code:
;de = x position
;bc = y position
calcOffset8bpp:
    ld hl,VRAM :the start of VRAM is the top left pixel in the screen
    add hl,de ;add x offset
    ld b,160
    mlt bc :multiply Y by screen width (320 doesn't fit into an 8-bit register)
    add hl,bc ;.. so we essentially do 160*Y*2
    add hl,bc :adding bc twice is the same as bc*2
;hl points to the position in VRAM, now you can start drawing your sprite/pixel/whatever.
There may be a more efficient way to do this, i'm not too familiar with the ez80, but here you get the general idea. Drawing a sprite is also simple, you can just LDIR the sprite to the VRAM one row at a time. At the end of each row you need to add (320-sprite_width) to move down to the next row. Repeat until all rows have been drawn.
Other than that extra add hl,bc at the end of your code there chickendude, that is precisely correct. If you want to make it easier to draw any size sprites, simply push de before you ldir, and pop de and add 320 to de, or you can use SMC before you draw the sprite to do 320-sprite width, which should be faster. Good luck! Smile
The extra add hl,bc is because the mlt instruction only multiplied the Y value by 160, so we need to add it twice (160*2 = width of screen). Anyway, hopefully you've got enough info to be able to play around with the LCD a bit, turn some pixels on and off, and perhaps even get started on a sprite routine Smile
All right then. I have enough data to write a pixel to a screen, but how would I get started on writing a sprite routine?

EDIT:
To do list:

    1. Figure out setting up 8 bpp mode.
    2. Figure out a sprite routine.
    3.Get a single pixel on-screen to change colors.
    4.Figure out how to return to 16 bpp mode.

Fortunately, Mateo (Thanks, Mateo!) had a solution for returning back to 16 bpp mode.
MateoConLechuga wrote:

Code:

Code:

ld a,lcdBpp16
ld (mpLcdCtrl),a
caleb1997 wrote:
All right then. I have enough data to write a pixel to a screen, but how would I get started on writing a sprite routine?


Excellent. I'm going to give you some semi-pseudo-code for a sprite routine:

Code:

HL is the sprite data
Somehow get A to be the sprite height and BC to be the sprite width
DE must point to the top-left pixel of where the sprite will go.
(Put the loop label here)
    push bc ; Save the sprite width for later on
    push de ; Save the current pixel for later on
    ldir ; Does "ld (de),(hl) \  inc hl \ inc de \ dec bc" until BC=0 (so effectively it copies BC bytes from (HL) to (DE)
    pop de ; Grab the far left pixel of the current row
    ld bc,320
    ex de,hl ; Swap HL and DE temporarily so we can add a number into DE
    add hl,bc ; Add 320 to the screen pixel pointer (normally DE, but we swapped it with HL) to advance to the next row on the screen
    ex de,hl ; Swap them back to normal
    pop bc ; Retrieve the sprite width again
    dec a
    jr nz,LoopLabel
    ; "dec a \ jr nz" is basically DJNZ but using the A register instead of B


Hope this helps! (Warning: the above code *should* work, but is untested.)
Hactar wrote:
caleb1997 wrote:
All right then. I have enough data to write a pixel to a screen, but how would I get started on writing a sprite routine?


Excellent. I'm going to give you some semi-pseudo-code for a sprite routine:

Code:

HL is the sprite data
Somehow get A to be the sprite height and BC to be the sprite width
DE must point to the top-left pixel of where the sprite will go.
(Put the loop label here)
    push bc ; Save the sprite width for later on
    push de ; Save the current pixel for later on
    ldir ; Does "ld (de),(hl) \  inc hl \ inc de \ dec bc" until BC=0 (so effectively it copies BC bytes from (HL) to (DE)
    pop de ; Grab the far left pixel of the current row
    ld bc,320
    ex de,hl ; Swap HL and DE temporarily so we can add a number into DE
    add hl,bc ; Add 320 to the screen pixel pointer (normally DE, but we swapped it with HL) to advance to the next row on the screen
    ex de,hl ; Swap them back to normal
    pop bc ; Retrieve the sprite width again
    dec a
    jr nz,LoopLabel
    ; "dec a \ jr nz" is basically DJNZ but using the A register instead of B


Hope this helps! (Warning: the above code *should* work, but is untested.)


All right then. Now that I have this, how would I get it to produce a tree? Never mind the color right now, because that can be fixed later.

And for reference, I would like it to look something like this, but minus the curves.
You have to draw the sprite yourself, there are some tools out to convert images to a format that can be included in your source file, i remember Mateo wrote a program to do this:
https://www.cemetech.net/forum/viewtopic.php?t=11449

I dunno what tools people are using now, there may have been something else that's come around since then.
After reading the topic that was posted above, I understand how it helps. But after I convert the pic, how would I include it in my program? And what would it be outputted as? A pic file, hex string... What? And how would I incorporate this into my program?
I assume you can just include "imagename.bin" at the bottom of your source. It appears it'll create the palette and sprite data for you. Here's some code from the sample included:

Code:
.nolist
#include "includes\ti84pce.inc"
.list

 .org userMem-2
ProgramStart:
 .db tExtTok,tAsm84CeCmp
;...
;---> 8bpp picture
 ld a,lcdBpp8
 ld (mpLcdCtrl),a
 ld hl,cemetechlogo_paletteStart    ; palette mem
 ld de,mpLcdPalette
 ld bc,cemetechlogo_paletteEnd-cemetechlogo_paletteStart
 ldir
 ld hl,cemetechlogo_sprite
 call drawSprite8bpp
;...
 ld a,lcdBpp16
 ld (mpLcdCtrl),a
 ret

#include "cemetechlogo.bin"
The cemetechlogo_paletteStart/paletteEnd/sprite labels i assume are created automatically based off the filename and put in the .bin file. You can just run "PicConvert -8 imagename.bmp" and it'll create "imagename.bin" for you with the necessary palette/sprite data.

Code:
.nolist
#include "includes\ti84pce.inc"
.list

 .org userMem-2
ProgramStart:
 .db tExtTok,tAsm84CeCmp
;...
;---> 8bpp picture
 ld a,lcdBpp8
 ld (mpLcdCtrl),a
 ld hl,christmastree_paletteStart    ; palette mem
 ld de,mpLcdPalette
 ld bc,christmastree_paletteEnd-christmastree_paletteStart
 ldir
 ld hl,christmastree_sprite
 call drawSprite8bpp
;...
 ld a,lcdBpp16
 ld (mpLcdCtrl),a
 ret

#include "christmastree.bin"


I'm thinking something like this; am I right?
Yep, but the routine "drawSprite8bpp" you'll need to either write yourself or use the Mateo or someone else's sprite routine. Drawing the sprite should be relatively simple, though. You can put the width of the sprite in bc, the start of vram in de, then use ldir to copy one row at a time. After copying one row, you'll have to move de down one row in VRAM and make sure bc is set to the width again. Repeat for however many pixels tall the sprite is.
chickendude wrote:
Yep, but the routine "drawSprite8bpp" you'll need to either write yourself or use the Mateo or someone else's sprite routine. Drawing the sprite should be relatively simple, though. You can put the width of the sprite in bc, the start of vram in de, then use ldir to copy one row at a time. After copying one row, you'll have to move de down one row in VRAM and make sure bc is set to the width again. Repeat for however many pixels tall the sprite is.


How would I manually write the routine? I looks like it'll be a while before I can use something like Mateo's routine generator (or any other generator for that matter), and I would like to get started on this.... thus limiting me to something long and tedious: writing it manually.

How would I get started?
That's what i outlined in the second part of the post. This is Mateo's code (i made a couple changes, hopefully it still works):

Code:
; hl points to start of sprite data, first two bytes are width,height
drawSprite8bpp:
 ld de,vRAM      ; where to draw sprite
 ld a,(hl)      ; first byte of sprite is width
 ld (AddMe+1),a   ; how many bytes to copy in ldir
 inc hl
 ld b,(hl)      ; second byte of sprite is height
 inc hl         ; hl now points to start of sprite data
InLoop:
 push bc      ; b = sprite height
AddMe:
 ld bc,0      ; how many bytes to copy (max width 255)
 push de      ; de points to start of vram
  ldir         ; hl->data
 pop de         ; after ldir, de will = de + width bytes, reset de
 ex de,hl      ; hl now = vram location
  ld bc,320      ; move down one row in vram (one row = 320 pixels/bytes)
  add hl,bc
 ex de,hl      ; de = vram location
 pop bc
 djnz InLoop
 ret

This expects your sprites to be in the following format:

Code:
sprite:
.db WIDTH,HEIGHT
.db SPRITE_DATA,...

It also only draws to the top left of the screen, if you want to draw at an offset (x/y coordinates) you'll need to add the X and Y*320 to vram.
  
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