Once again, a seemingly simple programming task has me baffled... The program below should make the message "Hello!" display diagonally down and right on the screen, starting from the top-left corner. When I run the program on an emulator, it does successfully output the text, however the text doesn't start in the top-left, and some extra pixels get activated in a pattern that means nothing to me. What's more, the emulator seems to go into an endless loop, and I can't understand why my loop structures never work. Suggestions please!!


Code:

;Display the text "Hello!" diagonally across the screen

#include    "ti83plus.inc"
.org   $9D93
.db    $BB, $6D
   
   bcall(_clrlcdfull)   ;Clear screen
   
   ld hl, curRow      ;initialize curRow and curCol to 0
   ld (hl), $0000

   ld b, msg      ;b will point to the next character of the string

loop:
   ld a, (b)
   bcall(_putC)   ;Display the character in msg at location a

   inc b      ;increment the character counter
   inc (hl)   ;increment the row counter

   cp 0      ;if the null-character at the end of msg is reached, end program
   jr nz, loop

   ret

msg:
   .db "Hello!",0

.end
That.. shouldn't even assemble.

Code:
   ld hl, curRow      ;initialize curRow and curCol to 0
   ld (hl), $0000
(hl) acts like an 8 bit register, so you're only resetting curRow (and not curCol). You can also just bcall _HomeUp, which will save you a few bytes.


Code:
   ld b, msg      ;b will point to the next character of the string
B is only 8 bits, so that's definitely not a pointer. DE might be a good choice for that use, but B is totally wrong.


Code:
    ld a,(b)
This isn't even a valid instruction. You can use any 16-bit register pair as an indirect operand when loading to or from A, so (bc), (de), (hl), (ix+nn), (iy+nn) are all valid. (b) is definitely not.

(Maybe) working version:

Code:
    bcall(_clrlcdfull)
    bcall(_homeup)
    ld hl, curRow
    ld de, msg

loop:
    ld a,(de)    ; Read a character
    inc de
    or a         ; Faster way to do 'cp a'
    ret z        ; Finish if null

    bcall(_putc) ; Output character
    inc (hl)     ; Increment curRow
    jr loop
I don't know offhand what _PutC clobbers, so you may need to preserve some registers before calling it.
Mmk lets see...
1) I'm pretty sure that the ld (curRow), $0000 statement should work, since that's what Kerm Martian told me in this thread: http://www.cemetech.net/forum/viewtopic.php?t=8390&highlight=. As I understand it, $0000 defines a 16-bit 0, which would make curRow and the byte next to it both =0. Conveniently though, curCol is the byte after curRow, so that statement should make them both 0.
2) Also, _putC doesn't destroy any registers (though it does update curRow and curCol), so where I call it from shouldn't be an issue.
3) Now, regarding your advice about registers, I admit that the use of registers confuse me more than anything... With the ld a, (b) statement, let me see if I understand why its wrong. Memory addresses in the z80 processor are represented by 2 bytes, therefore register pairs like hl, de, or bc must be used to hold a specific address. However, the value stored in each address can only be 1 byte. So although the contents of b, (b), will fit in a, the address that points to b requires 2 bytes of storage, so I need a 2 byte register like bc or de to serve this purpose. Is that all correct?
4) I think I discovered that the null-character is actually printable on TI calculators, showing up as a little black square. Is this correct or am I just seeing some glitch?
Ok I think I answered some of my own questions... Below is my newer code, which works just right, and doesn't display any random designs (like the black dot, which I'm pretty certain is a null character printed by _putC). Thanks for your help though!


Code:

;Display the text "Hello!" diagonally across the screen

#include    "ti83plus.inc"
.org   $9D93
.db    $BB, $6D
   
   bcall(_clrlcdfull)   ;Clear screen
   
   ld hl, curRow      ;initialize curRow and curCol to 0
   ld (hl), $0000

   ld bc, msg      ;b will point to the next character of the string

loop:
   ld a, (bc)
   cp 0      ;if the null-character at the end of msg is reached, end program
   ret z
   bcall(_putC)   ;Otherwise, display the character in msg at the next location

   inc bc      ;increment the character counter
   inc (hl)   ;increment the row counter

   jr loop

msg:
   .db "Hello!",0

.end
Yeah, you can never work with JUST immediate values (ld ($ADDR),####), you need a register in there somewhere. HL is like the accumulator of the 16 bit registers, it has many more instructions available to it (for example, you can only add/subtract to/from hl). As you go along, you'll get used to all of these things and they'll become second nature.

From your new program i can see you're already starting to get a good grasp on a lot of things, just as Tari said you really need to pay attention to the registers especially when using bcalls/your own subroutines, unless you know specifically (like here it seems you do) that the bcall doesn't touch/modify those registers. Most times you will need to push/pop the registers you want to use (or use SMC: self-modifying code) before/after using a bcall.

For your questions from other post:
1. I think you figured out why this didn't work Wink
3. You've got it. Addresses in the z80 are 16-bit, so you need a 16-bit register to store them/access them. You CAN however load the byte at a certain address directly into the accumulator: ld a,(address). (hl) can also be used to load the byte at an address into an 8-bit register (including h and l! ld l,(hl) and ld h,(hl) are perfectly valid). The accumulator can also ld values from (bc) and (de). The other registers can't, though, just a. So there's no ld e,(bc) opcode. Keep in mind, too, that it's faster and smaller to work with registers than with immediate values.
4. If you want to see a list of all the characters available, you could write a program that starts at 0 and prints a screen of text, from 0-127 (0-15 1st row, 16-31 2nd row, etc.) showing the characters for all of those values. Then wait for a keypress (bcall(_GetKey), which very likely WILL destroy your registers) and display the rest. Smile

Btw, when you start to feel comfortable, you might want to try an IDE like WabbitCode which incorporates an assembler and emulator, as well as shows you how much space (how many bytes) each command or a section of code takes up as well as how many t-states, it can be useful to get an idea of what is faster/more efficient. For example, in your code you use "cp 0" which is perfectly fine, for simple instructions like cp, ld, etc. using immediate (non-register) values are generally 1 byte larger and 3 t-states slower. Something like "or a" (or "and a", though most people use or a for some reason) is 1 byte and 4 t-states, both smaller and faster and doesn't affect the readability of your code very much as pretty much everyone uses or a instead of cp 0 Wink
Thanks chickendude; bitwise logic operations are definitely something I'm still getting the hang of. Its kinda hard to tell when to use them, how they affect flags, etc. I'll definitely give that print screen program a try though, I'm anxious to see what characters my TI has that I never knew about Razz
  
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