So I've started to learn z80, and since I'll probably have a lot of questions, I made this thread.

Calc: TI84+SE running on the jsTIfied Emulator
ASM: using z80
Assembler: Brass (with DCS sdk)

First Problem: I've been using the tutorial from here: http://jacob.heliohost.org/calc/doc/28days/lesson/toc.html

And on day 1, I copied down the 'Hello World' sample program and assembled it using Brass. I uploaded it on jsTIfied and ran the program with the Asm( program. However i only get two periods as an output like: '..'

Here is a picture:



and the code I'm using:


Code:
.nolist
#include "ti83plus.inc"
#define    ProgStart    $9D95
.list
.org    ProgStart - 2
    .db    t2ByteTok, tAsmCmp
    b_call(_ClrLCDFull)
    ld    hl, 0
    ld    (PenCol), hl
    ld    hl, msg
    b_call(_PutS)            ; Display the text
    b_call(_NewLine)
    ret

msg:
    .db "Hello world!", 0
.end
.end


What is going wrong?
That program unfortunately has some errors. Try this version instead:
Code:
.nolist
#include "ti83plus.inc"
#define    ProgStart    $9D95
.list
.org    ProgStart - 2
    .db    t2ByteTok, tAsmCmp
    b_call(_ClrLCDFull)
    ld    hl, 0
    ld    (CurRow), hl
    ld    hl, msg
    b_call(_PutS)            ; Display the text
    b_call(_NewLine)
    ret

msg:
    .db "Hello world!", 0
.end
.end
It looks the exact same, what did you change?
TIOs has two different cursors for printing, the Pen and Cur(sor). When you print with PutS, it uses the Pen. When you print to the graphscreen, then the Cursor is used. The main difference: The pen uses character units and is for the homescreen whereas the cursor is for the graphscreen and is in pixels.

(I might be wrong as I don't program for TIOS)
Hmm, that worked Kerm! and that explaniation is confusing AHelper, I get that there is a difference between Pen and Cursor, but I don't understand what it seems to be.
AHelper wrote:
TIOs has two different cursors for printing, the Pen and Cur(sor). When you print with PutS, it uses the Pen. When you print to the graphscreen, then the Cursor is used. The main difference: The pen uses character units and is for the homescreen whereas the cursor is for the graphscreen and is in pixels.

(I might be wrong as I don't program for TIOS)
Almost, but backwards. Currow/curcol are for the homescreen, and pencol/penrow are for the graphscreen.
Oh, okay, that makes much much more sense. Thanks Kerm and AHelper!

But what is the difference between say Currow/curcol and penrow/pencol? I mean withing their own set, I get the cursor and Pen part, but what is the difference between the row and col?
Ah. Row is row and col is col, if you access them as single bytes. However, in those two programs you're accessing two bytes at a time, which is a shortcut to set both curcol/currow or pencol/penrow at the same time.
Okay, thanks. What's the difference between row and col? and what do you mean by single and two bytes?
_PutS uses a grid of... 16x8? Basically, every character has the same dimensions (i think it's 5 pixels wide x 7 pixels tall, with an empty pixel on the right and bottom so that they aren't cluttered together). A row for _PutS/curRow is basically the Y value you want to display it at.
------------------------- row 0
------------------------- row 1
------------------------- row 2
...
------------------------- row 7

A column is essentially the X value:

Code:
 | | | |
 | | | |
 | | | |
 | | | |
 | | | |
 0 1 2 3


For _VPutS, instead of using the 16x8 grid it uses exact pixel coordinates. So penRow tells it how many pixels from the top to draw (the Y value, 0-63) and penCol tells it how many pixels from the left to draw (the X value, 0-95).

One vs two bytes: the registers a, b, c, d, e, h, and l (and i/r/f) are one-byte (8-bit) registers. When using these registers you'll only be loading/storing one-byte values. The registers af, bc, de, hl, ix, and iy (and sp/pc) are two-byte (16-bit) registers. When using these registers, you will by loading/storing two-byte values. To store a one-byte value to an address in RAM (penCol/penRow and curRow/curCol are actually places in the calculator's RAM), you can only use 'a'. So:
ld (penRow),a
would load whatever value is in a to the byte at address penRow.

To store a two-byte value into an address in RAM, you can only use bc, de, hl, ix, iy, and sp. Storing a two-byte register first stores the LSB (least significant byte, the smaller part of the register, here c, e, and l) then the MSB (most significant byte, the larger part, b, d, and h). So:
ld (penCol),hl
first loads the value of l to the byte at address penCol, then loads the value of h to the byte at address penCol+1. penCol+1 just happens to be penRow 🙂 It's worth noting, though, that because TI is dumb penCol comes before penRow but curRow comes before curCol. So if you want to update both values at once you need to do:
ld (penCol),hl
ld (curRow),hl
Not that we load to penCOL not penROW!
But if a column is the x value, why are we using the currow instead?
Because HL is a two-byte register. What you are really doing is loading 0 into CurRow and 0 into CurCol, because CurCol is the next byte in memory. When you load 0 into hl, you're really loading $00 into L and $00 into H. L gets stored into CurRow, H gets put into CurRow+1 (aka CurCol). If you load $0102 into HL, $02 will get put into CurRow and $01 will be put into CurCol.

In short, since HL is a two-byte register, you write two bytes starting at CurRow.
Okay then. But can you explain rather simply without registers the point of currow and curcol? Is one or the other used for text in specific directions?
Link wrote:
Okay then. But can you explain rather simply without registers the point of currow and curcol? Is one or the other used for text in specific directions?
When you use the _PutS or _PutC system calls, the OS uses CurCol and CurRow to figure out where on the (home)screen it should print the letter or string that you provide. _Newline also increments CurRow and moves CurCol to the start of the line. Likewise, PenCol and PenRow are used with _vputs and _vputmap (the graphscreen versions of _PutS and _PutC).
So the system automatically figures out where the next character goes? Then what's the point of doing this:

Code:

ld hl, 0
ld    (CurRow), hl

Is it just that it starts off the printing at the start of the line?
That tells _PutS and _PutC that the calculator should print the next character (or string) starting at CurRow = 0 (the very top of the screen) and CurCol = 0 (the very left of the screen). I'm not sure if _PutC updates CurRow/CurCol, but _PutS does. So if you draw a string with three letters, CurCol will be incremented 3 times. So if we have:

Code:
   ld hl,0
   ld (curRow),hl
   ld hl,text
   bcall(_PutS)
   ret
text:
.db "Hi!",0
At the end curRow will = 0 and curCol will = 3 (and HL will point to the byte after the 0 of the text label). If you don't tell it where to print, it will use whatever value is currently stored in curRow/Col, which probably isn't where you want to draw your text. That's why we set it beforehand.

EDIT: It may be useful to see another example.

Code:
   ld a,0
   ld (curRow),a
   ld hl,text
   bcall(_PutS)
   ret
text:
.db "Hi!",0
This sets curRow to 0, so the text will print at the top of the screen, but because 'a' is an 8-bit register it does not set curCol. It only updates one byte (HL updates 2 bytes). Therefore, _PutS will draw at the top of the screen, but the X position will be whatever was previously stored into curCol.
I am not sure which programming languages you are familiar with, but here is another explanation.

In TI-BASIC, you might do something like Output(2,1,"HELLO"). The calculator will take the coordinates (subtract one from both) and essentially do:

Code:

     ld a,1
     ld (curRow),a
     ld a,0
     ld (curCol),a

This sets the Row and Column for where to draw the text. Then it can use a _PutC loop to draw each of the characters in the string.

Note: This is not how the OS actually does it, but it should give you an idea for how it is done.

So curRow and curCol simply hold the coordinates for where to draw text on the homescreen.

However, writing to each byte separately like that is not needed. To set those same coordinates:

Code:

     ld hl,$0001    ;same as 'ld h,0 \ ld l,1' but faster and smaller
     ld (curRow),hl  ;L→(curRow), H→(curCol)

Now that the coordinates are loaded, the _PutS routine and others will draw the text at those coordinates.

I do not know if this will help at all, but here is a screenshot showing what happens when you adjust the values of 'ld hl,**' for the cordinates:

The code translates like this:

Code:

     ld hl,0         ;21 0000  editing the 00 bytes changes the value
     ld (curRow),hl  ;224B84
     ld hl,string    ;21A29D
     bcall(_PutPS)   ;EF1045
     ret             ;C9
string:
     .db 5,"Hello"   ;0548656C6C6F
Oh, wow! I get it now. Thank you guys. So just to be clear if currow is 1, would it be on the second line of the calc?

Edit: Just tried it and yup, works like I thought 😁

-----
Next thing: Can people please explain what the point of flags are in the F register? What's the point of them? From here: http://jacob.heliohost.org/calc/doc/28days/lesson/day04.html

and 2) what is extension, the differences between them, and why it's needed? http://jacob.heliohost.org/calc/doc/28days/lesson/day05.html

Edit 2: Get this now too 😁
Well, next main question, is there something like 'functions' in z80? and how would you use them?
You can use calls to subroutines and you have to come up with a way to pass arguments. Typically, arguments are passed through registers, sometimes RAM, and not very often through the stack. So for example, say you wanted to make a subroutine to clear a buffer (such as plotSScreen , saveSScreen , or AppBackUpScreen). Your subroutine might be:

Code:

ClrBuf:
;Inputs:
;     HL is the 768-byte buffer to clear
     ld (hl),0
     ld d,h
     ld e,l
     inc de
     ld bc,767
     ldir
     ret

Then you might do something like :

Code:

     ld hl,plotSScreen
     call ClrBuf

If you are looking more for something like ClearBuf(plotSScreen) syntax, you can define it this way in most of the assemblers I am used to:

Code:

#define     ClearBuf(addr)     ld hl,addr \ call ClrBuf



That tells the assembler to replace ClearBuf(addr) with the given code.
  
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