I know this is the third new thread I've made on the z80 board, but I think that this is a thread that could help anybody interested in beginning z80 assembly.

I'm learning z80 asm from the 28 days tut. While the tutorial is, for the most part, clear and concise, the author doesn't have any exercises!! I'm not always the most imaginative person, so I've had some difficulty designing exercises for my new asm learnin'

I think it would be cool if this forum could convene and create a list of problems (and maybe solutions to the problems), easy and difficult, to stretch the minds of fledgling z80 asm coders like myself.

So, post a problem, post 10 problems, post ones you made up yourself or ones you stole from a jenky old c++ book, post anything you can think of!

I'll start with a few that I've made over the past week

1. Make a program that increments a number when you press up/down and displays the number on the screen. Bound the number between 0 and 100 or something like that.

2. Take the previous program and modify it so that when enter is pressed the current number is multiplied by 2.

3. Make a program that moves a character around the screen. When you press up, it goes up, down, it goes down, yada, yada.

4. Make a game where a horizontally moving character at the bottom of the screen has to move laterally to catch randomly generated falling characters. Display a score and make it so that each character that falls off the bottom of the screen loses you a life.
If you have succeeded in making all four of those work, then I congratulate you; your knowledge is moving along very fast indeed! For your program (3), where you move a character around the screen, I might like to challenge you to use a sprite and the graphscreen instead. Smile You'd need a sprite XOR function to draw and erase the sprite (more efficient than erasing the entire screen) and a routine to copy the graph buffer to the LCD fast; luckily Doors CS (and other shells, if you prefer) have iPutSprite and iFastCopy for such purposes. I'm trying to think how you might be able to expand your examples for more functionality besides better graphics.
I think for 0.5, one of the programs should be a basic counter program (count from 0 to 100 and display each number). That was my first program that I didn't just copy code for, and I think it really helped me with z80 ASM. For (2), I'm curious what you chose to do for multiplying by two. Basically, are you using a loop, or a single instruction? And I second Kerm's suggestion of using sprites and the graphscreen as a way to make 3 and 4 more challenging Smile
Ah yes, thank you for saying that, _player. I remembered that you had a first program that you've quoted a few times before, but I couldn't remember what it was exactly. I would like to know if Rascherite has indeed made those programs, or just made the ideas for them, as that would dictate how difficult my suggested exercises might be.
I'd like to do it in the graph buffer, but I want to get the hang of using basic functions before I start working with graphics, so I'm trying to seek out exercises to test my asm ability that don't require use of advanced i/o.
Rascherite wrote:
I'd like to do it in the graph buffer, but I want to get the hang of using basic functions before I start working with graphics, so I'm trying to seek out exercises to test my asm ability that don't require use of advanced i/o.
Sounds smart. To clarify our questions above and help us tailor exercises to you, therefore, did you or did you not complete those exercises that you set for yourself in your first post yet?
Yes, I have completed all of them (except for the last one, I haven't added a score tracker yet)
I actually did everything until number 4 before reading this post Shock
Rascherite wrote:
Yes, I have completed all of them (except for the last one, I haven't added a score tracker yet)
Very impressive! Then sticking with things you can do with the homescreen, how about a menu sort of thing similar to TI-BASIC's Menu() command? Also, I vote that you show us some of your code so we can critique your style and see if you're doing anything accidentally inefficient, if you don't mind. Smile
Sounds fantastic! Tear it apart for me. Should I upload it somewhere or can I just put it inline here?
I think this is what I did for exercise 3:

Code:
;Skipped the obvious header

   b_call(_ClrLCDFull)
   call INITCOOR
LOOP:   
   b_call(_getKey)
   cp kUp
   call z,GOUP
   cp kDown
   call z,GODOWN
   cp kLeft
   call z,GOLEFT
   cp kRight
   call z,GORIGHT
   cp kClear
   ret z
   call INITCOOR
   b_call(_PutS)
   jr LOOP
GOUP:
   ld hl,COOR+1
   ld a,(hl)
   dec a
   ret
GODOWN:
   ld hl,COOR+1
   ld a,(hl)
   inc a
   ret
GOLEFT:
   ld hl,COOR
   ld a,(hl)
   dec a
   ret
GORIGHT:
   ld hl,COOR
   ld a,(hl)
   inc a
   ret
INITCOOR:
   b_call(_ClrLCDFull)
   ld hl,COOR
   ld a,(hl)
   ld (CurCol),a
   ld hl,COOR+1
   ld a,(hl)
   ld (CurRow),a
   ret
COOR:
.db 0,0
STR:
.db "T",0

I know that it's not optimized.[/code]
You've fallen into the classic trap of that program, I'm afraid. Smile You repeatedly cp and call z, but you're modifying the accumulator in the routines that you call! If you hit the wrong coordinates, it will make the character move unexpectedly. Smile It might not show up in the coordinate range of the homescreen, but switch that to the graphscreen and you'll the issue.
ah! That's why the T moved randomly sometimes XD
thanks.
EDIT: is there a way to fix it, then? Confused
You could jr to your routines instead of calling them, and have them jr back into LOOP to display the string.
ah. so like jr z,GOUP?
yeongJIN_COOL wrote:
I actually did everything until number 4 before reading this post Shock

hahah I'm not suprised other people have done exercises like these, I'm not the most creative sometimes... Rolling Eyes

Here's code for...
1/2. I just looked at this code and it is BAD. I don't really wanna go back and correct it, so I don't wanna post it here. If anybody thinks itd be THAT valuable, I guess I could fix it up

3.

Code:

.nolist
    #include    "ti83plus.inc"
.list
.org    $9D93
.db    t2ByteTok, tAsmCmp

    b_call(_ClrLCDFull)
   ld      a, 0
   ld      hl, 0
   ld      (CurCol), a
   ld      (CurRow), a
   ld      (AppBackUpScreen), hl
   ld      (AppBackUpScreen+2), hl
   jp      disp

while:
   b_call(_GetKey)
   cp      kUp
   jr      z, up
   cp      kDown
   jr      z, down
   cp      kLeft
   jr      z, left
   cp      kRight
   jr      z, right
   cp      kClear
   jp      z, return
   jr      while
   
up:
   ld      a, (AppBackUpScreen+1)
   cp      0
   jr      z, disp
   dec      a
   ld      (AppBackUpScreen+1), a
   jr      collide
   
down:
   ld      a, (AppBackUpScreen+1)
   cp      7
   jr      z, disp
   inc      a
   ld      (AppBackUpScreen+1), a
   jr      collide
   
left:
   ld      a, (AppBackUpScreen)
   cp      0
   jr      z, disp
   dec      a
   ld      (AppBackUpScreen), a
   jr      collide
   
right:
   ld      a, (AppBackUpScreen)
   cp      15
   jr      z, disp
   inc      a
   ld      (AppBackUpScreen), a
   jr      collide
   
collide:
   ;Checks to see if 'x' is at the same coords as 'o'
   ld      hl, AppBackUpScreen+2
   ld      a, (AppBackUpScreen)      ;load x coords into hl and a for comparison
   cp      (hl)
   jr      nz, disp               ;if x coords are unequal, jump straight to display routine
   ld      hl, AppBackUpScreen+3
   ld      a, (AppBackUpScreen+1)      ;load y coords into hl and a for comparison
   cp      (hl)
   jr      nz, disp               ;unequal y coords, jump to disp routine.
   ;If 'x' coords were equal to 'o' coords, generate new 'o' pos and increment score
   ld      a, (AppBackUpScreen+4)      ;increment score
   inc      a
   ld      (AppBackUpScreen+4), a
   ld      a, r                  ;"randomize" 'o' pos
   and      %00001111
   ld      (AppBackUpScreen+2), a      ;x coord
   ld      a, r
   and      %00000111
   ld      (AppBackUpScreen+3), a      ;y coord
   jr      disp
   

disp:
   b_call(_ClrLCDFull)
   ld      a, (AppBackUpScreen)
   ld      (CurCol), a
   ld      a, (AppBackUpScreen+1)
   ld      (CurRow), a
   ld      a, 'x'
   b_call(_PutC)
   ld      a, (AppBackUpScreen+2)
   ld      (CurCol), a
   ld      a, (AppBackUpScreen+3)
   ld      (CurRow), a
   ld      a, 'o'
   b_call(_PutC)
   jp      while
   
return:
   b_call(_NewLine)
   b_call(_ClrLCDFull)
   ld      a, 1
   ld      (CurRow), a
   ld      (CurCol), a
   ret

.end
.end


4. (still without score)

Code:

.nolist
#include   "ti83plus.inc"
.list
.org   $9D93
.db   t2ByteTok, tAsmCmp

   b_call(_ClrLCDFull)
   ld   a, 0
   ld   (CurRow), a            ;CurRow is y
   ld   (CurCol), a            ;CurCol is x
   ld   hl, 0
   ld   (AppBackUpScreen), hl         ;position of "character" (a 'u').  (AppBackUpScreen) = x, (AppBackUpScreen+1) = y.
   ld   (AppBackUpScreen+2), hl          ;position of "thing to catch" (an 'o').
   ld   (AppBackUpScreen+4), hl         ;score
   ld   hl, 3
   ld   (AppBackUpScreen+5), hl         ;lives
   ld   a, 7
   ld   (AppBackUpScreen+1), a         ;constant y pos of 'u'
   ld   hl, 0
   push   hl

waitkey:
   b_call(_GetCSC)
   cp   skLeft
   jr   z, moveleft
   cp   skRight
   jr   z, moveright
   cp   skClear
   jp   z, return
   pop   hl                        ;Increment hl
   inc hl
   push hl
   ld   de, $0500                  ;load de with the #of cycles to increment o after.
   scf                           ;set and clear carry flag to make sure sbc hl, de is accurate
   ccf
   sbc   hl, de
   jp   nc, incr_o                  ;if hl is greater than $400 (the carry flag is not triggered by subtracting $400 from hl), jump to incr_o
   jr   waitkey                     ;otherwise, just jump back to waitkey

moveleft:
   ld   a, (AppBackUpScreen)      ;load the player's x coord into the a register
   cp   0
   jr   z, disp                  ;if 'u'x is equal to 0, jump to display routine without decrementing the x coordinate to prevent it from going offscreen
   dec   a                     ;dec x position
   ld   (AppBackUpScreen), a
   pop   hl
   ld   de, $120               ;increment hl more than usual, the b_calls _PutC and _ClrLCDFull at the disp label will take a lot of time   
   add   hl, de
   push   hl
   jr   disp

moveright:
   ld   a, (AppBackUpScreen)      ;load the player's x coord into the a register
   cp   14
   jr   z, disp                  ;if 'u'x is equal to 14, jump to display routine without incrementing the x coordinate to prevent it from going into that one glitchy spot
   inc   a                     ;increment x position
   ld   (AppBackUpScreen), a
   pop   hl
   ld   de, $120               ;increment hl more than usual, the b_calls _PutC and _ClrLCDFull at the disp label will take a lot of time   
   add   hl, de
   push   hl
   jr   disp

incr_o:                        ;routine for incrementing o position
   pop   hl
   ld   hl, 0
   push   hl
   ld   a, (AppBackUpScreen+3)      ;Move down 'o'
   inc   a
   ld   (AppBackUpScreen+3), a

   ;now, check to see if 'o' pos is equal to 'u' pos.
   ld   a, (AppBackUpScreen+2)      ;loading 'o'x into a
   ld   hl, AppBackUpScreen         ;loading &'u'x into hl
   cp   (hl)
   jr   nz, chkbottomout         ;if 'o'x != 'u'x, then skip to check if o is bottomed out.
   ld   a, (AppBackUpScreen+3)      ;loading 'o'y into a
   ld   hl, AppBackUpScreen+1      ;loading &'u'y into hl
   cp   (hl)
   jr   nz, chkbottomout         ;if ('o'x != 'u'x) AND ('o'y != 'u'y), then skip to check if o is bottomed out.
   ;if we reach this line, then the o and the u occupy the same location.  Increment score and generate new o.
   ld   a, (AppBackUpScreen+4)      ;Increment score
   inc   a
   ld   (AppBackUpScreen+4), a
   jr   generate_o               ;and jump to new o generation
   
chkbottomout:
   ld   a, (AppBackUpScreen+3)      ;load 'o'y into a register
   cp   8
   jr   nz, disp               ;jump to disp if 'o'y - 9 is not 0.
   ;If the 'o' was not caught...
   ld   a, (AppBackUpScreen+5)      ;decrement 1 from the lives
   dec   a
   jr   z, gameOver               ;if a is now 0, all lives are gone.  End game.
   ld   (AppBackUpScreen+5), a

generate_o:
   ld   a, r                  ;"Random" number is put into a register
   and   $0F                     ;Bitmask "random" int with 00001111 and use if-then to make sure it's not over 14
   cp   15
   jr   nz, good_o
   dec a
good_o:
   ld   (AppBackUpScreen+2), a      ;load "random" a into 'o'x
   ld   a, 0
   ld   (AppBackUpScreen+3), a
   
disp:
   b_call(_ClrLCDFull)
   ld   a, (AppBackUpScreen)      ;load up curcol and currow.  Remember, 'u'x is stored right at AppBackUpScreen
   ld   (CurCol), a
   ld   a, 7
   ld   (CurRow), a
   ld   a, 'u'
   b_call(_PutC)               ;print the u
   ld   a, (AppBackUpScreen+2)      ;load up curcol and currow for o's
   ld   (CurCol), a
   ld   a, (AppBackUpScreen+3)
   ld   (CurRow), a
   ld   a, 'o'
   b_call(_PutC)               ;print the o
   
   ;display score/lives
   ld   a, 15
   ld   (CurCol), a
   ld   a, 5
   ld   (CurRow), a
   ld   hl, (AppBackUpScreen+5)      ;load lives into hl register
   
liveswhile:
   ld   a, 'u'                  ;put 'u' into a register because we're displaying a 'u'
   push   hl                  ;protect hl, yada yada yada
   b_call(_PutC)
   pop   hl
   ld   a, 15                  ;load 15 into curcol
   ld   (CurCol), a
   ld   a, (CurRow)               ;Load current CurRow-2 into CurRow.  CurRow automatically increments when b_call(_PutC) is called, so we have to decrement it not once, but twice.
   sub   2
   ld   (CurRow), a   
   dec   hl                     ;Decrease # of lives left to render
   ld   a, h                  ;Because dec reg16 does not affect the flags, we have to dec hl and then do an or on h and l.  Think about it, if (H V L) = 0, then both h and l are 0.  To do this, we must first load h (or l) into a.
   or   l                     ;thanks Kerm Martian =)
   jr   nz, liveswhile
   
   jp   waitkey

gameOver:
   b_call(_ClrLCDFull)                    ;clear LCD, reset curcol and currow, and display endgame msg.
   ld   hl, 0
   ld   (CurRow), hl
   ld   hl, gameovermsg
   b_call(_PutS)
   b_call(_GetKey)

return:
   b_call(_ClrLCDFull)
   pop   hl
   ret
   
gameovermsg:
.db      "You are a loser =(              Press any key toexit", 0

.end
.end

I know the way the timing work is odd, tips on that please.

Criticism is appreciated!! I want all the knowlege this glorious forum has to give.
yeongJIN_COOL wrote:
ah. so like jr z,GOUP?

Yes, exactly. And, in GOUP (and similar routines), you would jr back into the place in LOOP where you display the string instead of returning.
ah, ok.
I'll try that soon.
First-pass tips from looking through your program, on instruction-level things. I'll do a higher-level second pass over your program structure later.

1. ccf is "change carry flag", not "clear carry flag". Therefore, scf/ccf is the same as the non-existent "reset carry flag". Faster and smaller way to clear the carry flag: "or a".


Code:
   scf                           ;set and clear carry flag to make sure sbc hl, de is accurate
   ccf


2. Whenever you want to ld a,0, do xor a instead. It XORs the accumulator with itself, which of course (if you are familiar with xor) sets A to all zero bits, faster and one byte smaller than ld a,0.

3.

Code:
   ld   a, h                  ;Because dec reg16 does not affect the flags, we have to dec hl and then do an or on h and l.  Think about it, if (H V L) = 0, then both h and l are 0.  To do this, we must first load h (or l) into a.
   or   l                     ;thanks Kerm Martian =)
You're most welcome! Very Happy

4. Regarding randomness:

Code:
generate_o:
   ld   a, r                  ;"Random" number is put into a register
   and   $0F                     ;Bitmask "random" int with 00001111 and use if-then to make sure it's not over 14

I don't know how opposed you are to using shell/DCS functions, but you can get much, much better randomness and uniformity by using something like the iRandom function. 'r' is based on simple counting of clock cycles, so in a fairly structured program patterns will appear very quickly.
http://dcs.cemetech.net/index.php?title=IRandom

5. I continue to suggest that you name things like "AppBackupScreen+4" instead of having those throughout your code.

6. Try to think about where you can re-order instructions to save bytes. For example:


Code:
   ld      a, 0
   ld      hl, 0

to

ld hl,0
ld a,h
Thank you for all the info, Kerm!
1-3. Make perfect sense

4. All due respect to you and your fantastic shell, but I'm a programmer who likes to reinvent the wheel, for better or for worse. I'll eventually write a legit random number generator, but I want it to be my own.

5. I will start doing that, but in my defense, when we talked about that earlier, more than 3/4 (if not all) of this program was already written.

6. So, ld a, h has a shorter opcode than ld a, 0 ?
  
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 2
» 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