I think this code should help: http://pastebin.com/FaQVAdzt

The main things I've done are this:
1. Push whatever registers you're using before you call routines that mess them up (like a sprite routine, which probably uses nearly all of the registers) and pop them off after.
2. ld a,8 \ ld b,a \ ld a,8 \ ld c,a is entirely unnecessary on multiple levels. First of all, you can simply use ld b,8 \ ld c,8 without using register A. If you're wondering what ld instructions are legal, I've made a Google Doc thing for that (green/yellow means it's allowed, red means it isn't). But even ld b,8 \ ld c,8 can be optimized further. This may take a while to explain...

Let's say you have a 2-digit number (in base 10) and you want both digits to be eight. You could do this: ld FirstDigit,8 \ ld SecondDigit,8 or you could just load 88 into the number in the first place, making both digits equal to eight. You can do the same in assembly, kinda. Think about how you go from two eights to 88, mathematically. You take the number that comes first and multiply it by 10 (because we are dealing with base 10).

So in assembly, since each register (in this case, B and C) each hold a number between 0 and 255, we are sort of dealing with base 256. So we multiply the first number by 256, add in the second one, then load that into BC. So you get ld bc,(8*256)+8. I hope that makes sense...
Can someone teach me indirection, or the effect parenthesis have on LD with examples? Every time I learn, I don't understand it. Thanks very much.

For example, this is my code:

Code:
 push de
 push hl
 ld DE,8
 ld (hl),tilex
 add HL,DE
 ld (tileX),HL
 pop hl
 pop de

I have tried switching the parenthesis around, but Kerm has told me that just changing code and seeing if it works is not beneficial.
Hactar, i'm not sure how it breaks down on the ez80, but another smaller (than the original) option in that case would be ld b,8 \ ld c,b. Another way to visualize the *256 method is by just using hex numbers. Every two digits of a hex number represent a byte:

Code:
ld b,$08
ld c,$09
;.. or ...
ld bc,$0809

those two do the exact same thing.

Code:
ld l,$20 ;hex $20 = dec 32
ld h,$1A
;.. or ...
ld hl,$1A20

To explain this, the $09 (also 9 in decimal), gets stored into the smaller byte of bc, that is, the c register. $08 gets started in the MSB of bc, that is, the b register. In the second example, $20 gets stored into l and $1A into h.

Regarding indirection, have you looked at the stickied topic in this forum? Basically, indirection is a way to change or retrieve values in the calculator's memory. The parentheses go around the address you want to change/retrieve.

For example, if you want to change the value at $8000, you would write ($8000). If you just want to use the value $8000 (ie. the immediate number), then you leave the parentheses off. You can also use a 16-bit register to hold an address. For example, let's say hl=$8000. If you want to store/retrieve something to/from $8000, you would put hl in parentheses (hl). If you want to use the value $8000, you would leave the parentheses off: hl. Let's look at your code:

Code:
 ld DE,8
 ld (hl),tilex
 add HL,DE
 ld (tileX),HL
What does ld (hl),tilex do? (hl) tells me that i want to store a value to the address in hl. Let's say hl=$8000, here we're trying to store a byte to ($8000). What are we trying to store? tilex. I assume tilex is an address somewhere in memory, let's say $8500. So we could think of this instruction as "ld ($8000),$8500" (but this instruction doesn't actually exist). There are a couple of issues here. First, (hl) will only store/retrieve one byte, but tilex ($8500) is a 2-byte number. The assembler will probably give you a warning and clip the number's MSB off (the $85). So Assuming the address of tilex is $8500, ld (hl),tilex will load the LSB ($00) into the address in hl (in this example, $8000). So the byte at ($8000) will be set to zero.

What I think you really wanted to do was load the value in (tilex) into hl. Writing tilex tells us to use the immediate number. If we want to get the value stored at tilex, we need to use indirection: (tilex). As for hl, we don't want to modify the value stored at the address in hl, we want to change the value of hl itself. When you use (hl), the value of hl doesn't change, only the value stored at the address in hl. So:
ld hl,(tilex)
What does this do? This takes the value stored at (tilex), that is, ($8500), and puts it into l. Then it takes the value stored at (tilex+1) and stores that in h. Why? Because HL is a 16-bit register. If you only want the value at (tilex) you have two options:
1. You can set h to 0 afterwards (ld hl,(tilex) \ ld h,0)
2. You can use an 8-bit register. The only 8-bit register that supports indirection is the accumulator: ld a,(tilex).

Lastly, let's take a quick look at the next indirection: ld (tilex),hl.
Here, are we reading from an address or writing to an address? Since the indirection is on the lefthand side (the "result" side), we are writing to an address. We are writing to the address of (tilex). What are we writing? The value of hl. l is written to (tilex), and h is written to (tilex+1). Remember, hl is a 16-bit register. If tilex is only supposed to be one byte, you need to use an 8-bit register to write to it (the only register available for this is a).

Understanding indirection is essential in assembly, so if you have any questions please ask. I have a feeling my explanation still isn't that clear, so let me know if i can clear anything up. Btw, answering your questions made me pick up one of my old projects again, please keep asking questions!

Btw, here are a couple indirections. I'd like you to analyze them for me and tell me:
A. Is the indirection reading a value from an address or writing a value to an address?
If reading:
B. What is the value of the address being read?
C. How many bytes are we reading (1 or 2)
D. Where is the result stored/what is its value?
If writing:
B. Where are we storing the result?
C. How many bytes (1 or 2) are we storing?
D. What is the value we are writing?

Let's assume:

Code:
HP = $8000
time = $8002
X = $8004
Y = $8005
level = $8006
    ld hl,$0280
    ld (HP),hl    ;1 ***
    ld h,$04
    ld (time),hl ;2 ***
    ld de,(HP)  ;3 ***
    inc de
    ld (HP),de  ;4 ***
    ld hl,X
    ld (hl),20   ;5 ***
    ld a,(hl)    ;6 ***
    inc hl
    ld (hl),l   ;7 ***
    inc (hl)   ;8 ***
I'm going to try the first one and get some feedback before attempting the rest. I'll update the post when I can confirm I did this correctly or not.
1. ld (HP),hl
A. Writing
B. Address $8000
C. 2 bytes
D. $0280
Alright, let's analyze this a bit. We have "ld (HP),hl". In this instruction, we are indeed writing to an address, not reading from an address. We're writing to the address $8000, up until here we're good. However, hl is a 2-byte register, so ld (HP),hl will store two bytes into (HP). L will be stored at $8000 and H will be stored at $8001 (this is because the (e)z80 is little endian: the LSB/least significant byte gets stored first, followed by the MSB/most significant byte). If we had done ld (HP),a, only one byte would be stored (at $8000) because a is a 1-byte register. The value we are storing here is the value of HL ($0280). If we wanted to store the value stored at address $0280, we'd have to use (hl) (though the instruction ld (HP),(hl) doesn't exist, you can only have indirection on one side of an instruction).

Compare:
ld (HP),hl
ld a,(hl)
.. The first stores the value of hl into ($8000) and ($8001), that is the value at $8000 = $80 and $8001 = $02. The second takes the byte stored at address $0280 (whatever that may be) and puts that byte into a.

Code:

HP = $8000
time = $8002
X = $8004
Y = $8005
level = $8006
    ld hl,$0280
    ld (HP),hl    ;1 ***
    ld h,$04
    ld (time),hl ;2 ***

Challenge 2:
A. Writing, as (time) is the destination, and HL is the source.
B. Inside time, at address ($8002) ^
C. 2 bytes, because HL is 2 bits.
D. First L, which is $02, then H, which is $04. This makes $0402 (I think this is right, but I'm not sure, I may have gotten H and L flipped around)
Yep, this time around you got it all right except for one thing, HL = $0480. When we do "ld hl,$0280", the LSB (smaller part of the number) goes in L, the MSB (larger part of the number) goes in H. So L = $80 and H = $02. Later we change the value of H to $04, so HL = $0480 (the value of L didn't change). Ah, also HL is 2 bytes, not 2 bits Wink

I think you're starting to get the hang of indirection. It might seem a bit weird now, but soon you'll be really thankful that it exists (without indirection you'd have no data/variables other than what you put in the registers yourself, that is there'd be no way to read/write data to/from the calc's memory). A lot of people have trouble understanding indirection at first, but it'll soon seem so obvious that you'll wonder how it ever confused you in the first place, heh.
I think I understand indirection now!

I think...

    When you put parenthesis on the left argument, you are interpreting it as an address, and writing the right argument to it.

    When you put parenthesis on the right argument, you are interpreting it as an address, and reading from that address and writing it to the left argument.


If this is correct, using your example...

Code:
HP = $8000
level = $8006
    ld hl,$0280
    ld (HP),hl

$0280 is put into HL. $0280 is put into the address $8000 because the parenthesis are there.

This would also be equal to saying:

Code:
    ld hl,$0280
    ld ($8000),hl


or even, although not possible:

Code:
    ld ($8000),$0280

Also if this is true, does that mean HP can never be on the left side without parenthesis?

--Merge Double Post--


Code:
 ld hl,picture
 ld a,(hl)
picture:
 .db "Test",0


I'm not exactly sure... is picture a memory address automatically that indirection can be done on?
KingInfinity wrote:
I think I understand indirection now!
...
Yep, it sounds like you've got it down now.
KingInfinity wrote:
Also if this is true, does that mean HP can never be on the left side without parenthesis?
That's correct, you can only write to a register or a memory address. What would ld 5,3 do? Change the value of 5 into 3? It doesn't make sense, so on the left hand side HP can only be used with parentheses.

KingInfinity wrote:

Code:
 ld hl,picture
 ld a,(hl)
picture:
 .db "Test",0


I'm not exactly sure... is picture a memory address automatically that indirection can be done on?
"picture" is just a pointer to a location in memory. "ld hl,picture" loads the address of picture into hl. "ld a,(hl)" loads the first byte (remember, a is a one-byte register) at that address, essentially loading the value of "T" into a (decimal value 84 in TI's ASCII chart). If next you did "inc hl \ ld a,(hl)" a would equal "e". If you did "ld hl,(picture)", L would hold the value "T" and H would hold "e".
Hello, all!
My question today is how to set textInverse on the CE!
I have this code:

Code:
 bit textInverse,(IY+textFlags)
 jp NZ,unInvert
 set textInverse,(IY+textFlags)
 ret
unInvert:
 res textInverse,(IY+textFlags)
 ret

However, when I run it, nothing happens!
KingInfinity wrote:
Hello, all!
My question today is how to set textInverse on the CE!
I have this code:

Code:
 bit textInverse,(IY+textFlags)
 jp Z,unInvert
 set textInverse,(IY+textFlags)
 ret
unInvert:
 res textInverse,(IY+textFlags)
 ret

However, when I run it, nothing happens!


Using the Z flag with bit can be a bit confusing. Remember this:
• If the tested bit is zero, then Z.
• If the tested bit is one (non-zero), then NZ.

Hope that helps! Wink
Another option is using xor:

Code:
ld a,(IY+textFlags)
xor a,1<<textInverse
ld (IY+textFlags),a
This should just toggle the value on and off. If it's set to 1, it'll reset it. If it's set to 0, it'll set the bit.

EDIT: 1<<textInverse just takes 1 (you could think of it as binary %00000001) and shifts it left 'textInverse' times.I dunno what bit the textInverse flag corresponds to, but if it were say bit 2 1<<textInverse would shift 1 left 2 times: %00000100.
chickendude wrote:
Another option is using xor:

Code:
ld a,(IY+textFlags)
xor a,1<<textInverse
ld (IY+textFlags),a
This should just toggle the value on and off. If it's set to 1, it'll reset it. If it's set to 0, it'll set the bit.

EDIT: 1<<textInverse just takes 1 (you could think of it as binary %00000001) and shifts it left 'textInverse' times.I dunno what bit the textInverse flag corresponds to, but if it were say bit 2 1<<textInverse would shift 1 left 2 times: %00000100.


Exactly the same size and speed, one could use this:

Code:
ld a,1<<textInverse
xor a,(iy+textFlags)
ld (iy+textFlags),a

Wink
So I'm back to working on my tilemap program. Unfortunately, rendering does not seem to be working too well.

It's easier to describe line numbers inside a Pastebin: http://pastebin.com/7WRYRwQd
(Maybe

Line 151-157 should pause for a keypress to draw the tile, instead, it just makes the screen permanently white.

However, line 151-157 were added as debugging to see in which order which tiles were drawn, as they are drawn incorrectly.

The point of the program is to draw 40 tiles, based on the letters inside a string, and then go to the next line, and so on until all the tiles are gone.

Unfortunately, the tiles are drawn wrong...
Any help is appreciated!
Your tilemap is a bit more complicated than it needs to be, rather than use a string i would use tile ids starting from 0, like this:
.db 1,1,1,1,1,1
.db 1,0,0,0,0,1
.db 1,1,1,1,1,1
You read through the tilemap one tile at a time until you've reached the edge of the screen, then you skip the remaining tiles (add width_of_tilemap - columns_on_screen) and jump to the next row. This is basically what you do, but you've added an extra variable to keep track of X/Y. Assuming your tilemap covers the whole screen and your tiles are 8x8, you'll have 40 sprites per row on screen. This means you'll want to loop 40 times:

Code:
   ld b,40         ;loop 40 times
drawRow:
   push hl
      ld l,(hl)   ;tile id
      ld h,64
      mlt hl      ;hl*64, each sprite is 8*8 bytes
      ld bc,tile_data
      add hl,bc
      call drawTile   ;hl points to start of tile's sprite data
   pop hl
   inc hl         ;draw next tile
   djnz drawRow
Also, if you wanna check if bc=0, a quick way is:

Code:
   ld a,c
   or b
It'll return z if bc = 0, nz if bz != 0. Also, it might be easier if you split the columns and rows. Something like ld bc,40*256+30 (that is, ld b,40 / ld c,30). Loop 40 times to draw one row, then reset b to 40, decrement c, and repeat until c = 0 (30 rows have been drawn).
Hello, I am looking for a circle routine that takes a radius and the center X and Y coordinates to draw a circle into a vRam buffer. This is for ez80 asm (the +CE) Very Happy All the ones I seem to find are for C or z80
If anyone has one, it would be super very much appreciated.
Mateo has created a Circle Demo. Maybe you could change the code *a bit*, but I hope this will help you!
PT_ wrote:
Mateo has created a Circle Demo. Maybe you could change the code *a bit*, but I hope this will help you!

This moves an object in a circle, the object itself is a sprite.
So a circle routine for the CE? Will this be using the stock LCD setup? (Not in simulated half-res or palette modes?)
tr1p1ea wrote:
So a circle routine for the CE? Will this be using the stock LCD setup? (Not in simulated half-res or palette modes?)

8bpp mode, but otherwise the same.
  
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 2 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