Where do you stand on hexadecimal z-80?
What?
 14%  [ 1 ]
Oh I've heard of it
 28%  [ 2 ]
I've messed with it a bit
 28%  [ 2 ]
Use it all the time!
 28%  [ 2 ]
Total Votes : 7

Hello,

LONG WINDED POST WARNING!

I recently decided to pick up Hexadecimal on-calc-programming; for a couple of reasons,

1. I got frustrated w/ trying to figure out compilers
2. I don't have a cable (got one for a penny on amazon, but I'm impatient)
3. You can't help but feel hard-core when everyone is programming in Ti-basic and you sit there and jam out hexadecimal assembly Wink


ANYWAY,______________________________________________

I got basic stuff down, but I wanted to get a (not so) basic program made, and build off of that. So like 9/10 programmers, pong was in sight.

Problems I've encountered so far,
1. How can I make a main game loop to run indefinitely? I got djnz working, but that only goes a set amount of times

2. what is the best method for output? I figure a b_call for the 'output' command would work, but I don't know what that would be.

3. how do comparisons work? (like an 'if' statement)
___________________________________________________

These problems have had me longingly looking at my o-so-simple c++, but I forge ahead Very Happy

P.S. I'm using Zeda's chart and Ti wiki's B_Call list....

Thank you in advance,
Blackhawk
LONG WINDED POST WARNING!

1. To make a basic loop, you would use either jr or jp. jp is generally slightly faster, but takes up one byte more than jr. You use jr the same way you use djnz, that is you can jump forward 128 bytes or backwards 127. With jp, you will use the full exact address in memory you want to jump to, but keep in mind that it's Little Endian, ie the Least Significant Byte (LSB) is stored first, followed by the Most Significant Byte (MSB). In the value $8070, the MSB is $80 and the LSB is $70. So what does this mean? After the hex opcode for jp (whatever it is), instead of writing JP8070, you'll need to put the MSB first, like this: JP7080.

2. What do you mean, output? Drawing a sprite? I believe there is a B_CALL called _DisplaySprite, though i've never used it and it's probably awfully slow. Probably not a big deal for Pong, but for a game with more than 3 sprites... You'll eventually want to write your own, but doing that in hex is going to be a huge pain. Often times, what will happen is a routine will write to what's called the graphbuffer (or gbuf, or plotSScreen), which is a buffer in RAM that you write to before copying it to the screen. It's quicker to write/read RAM (the LCD is really slow), so that's why that's done. If trying to display something you find the screen doesn't change, you might need to output the contents of the gbuf to the LCD. There's a B_CALL to do that, something like _GrBufCpy, again it's really slow, but coding in hex i imagine it'd be a pain to code your own copy routine. If you can get a copy of Ion, MirageOS, DoorsCS, or some other shell on your calculator, you can use the routines provided by the shell, which will generally include sprite routines, a fastcopy (copy the gbuf to the LCD more quickly than the B_CALL) routine, and some other random stuff.

3. For ComParisons, you'll use 'cp'. There are other tricks you can use, too, but cp is the most straightforward. cp will compare an 8-bit register (a,b,c,d,e,h,l) or number (0-255) to a and set the flags based on the result of the comparison. You can think of cp as a subtraction that doesn't store the result.
The main flags are:
z: the two values are the same. With cp, this means a = whatever value you compared. z stands for zero, so if a = c, cp c means the result was 0 Smile
nz: the two values are different (the result was Not Zero)
c: carry. This means a is less than the value compared to it. If a = 10, cp 13 will set the c flag because 10-13 = -3, the result carried over. (Note that the value of a doesn't change with a cp instruction, you could use the 'sub' instruction to do the exact same thing, this time the value of a WOULD change. So 'cp 10' and 'sub 10' set the exact same flags, the only difference being that with cp, a doesn't change, whereas with 'sub 10', the value of a is ten less afterwards.
nc: this means there was no carry. If the two values are the same, NC will also be set. So if a = 30, comparing a value of 0-30 would return NC, whereas 31-255 would set the c flag.

The z, c, and nc flags can also be used in additions, when the result is >255 (for 8-bit numbers, >65535 for 16-bit numbers) it will set the c flag. When the result = 0, (ie 256 or 65536, since the numbers will wrap), the z and nc flags will be "set" (or rather, the c flag will be "reset"). And when the result doesn't carry over, the carry flag will be reset (ie nc).

Those are the main flags, there are a couple others that can't be used with jr (they can be used with jp, call, and ret):
p: positive. The result of the operation was a positive number
m: minus? (who knows why it isn't 'n') the result was a negative number

The last few you probably won't use, at least not for now:
pe/po: parity even/odd: the number of bits set (number of ones in the binary version of the number) is even/odd.
overflow: uses the same flag bit as parity. Overflow is kinda like a signed carry, you can think of it as when there is a sign change (when the carry flag and bit 7 are different). pe means there was an overflow, po means no overflow.

And for these, i think the only instruction that makes use of them is 'daa' (other instructions obviously set/reset these flags, though):
h: half-carry. This is like the carry flag, but when there is a carry from bit 3 to bit 4 (the regular carry is from bit 7 to 8, bits start counting at 0 so bit 7 is actually the 8th bit Wink)
n: additioN (or perhaps subtractioN?): the last operation was either an addition or subtraction.


Whew. Sorry about the lack of formatting, if you have any questions or can't be bothered to read all that, i'd be glad to answer/try to be more concise. In the end, i think you're going to want to get a cable and move to assembly programming on the computer (or at least downloading an on-calc assembly editor like Mimas or ASMDREAM).
I'll edit this to make it more complete, but I have to say one thing that I keep saying:
@OP: HEX is not a thing. Hexadecimal, abbreviated as HEX, is not an acronym for anything. You don't code in HEX or Hex or hex. You code in assembly and happen to do the opcode-to-machine-code transformation in your head. And chickendude, shame on you for perpetuating the HEX thing. Wink
Shots fired by Kerm! Very Happy


1. Ok, I got a basic loop running, Thanks!

2. I was looking around at the b_call list and I couldn't find anything for drawing sprites. how would I go about making a routine? Are there instructions for turning on one pixel at a time?

3. Once compared, how would I use the flags? For example, If z flag = 1(or true, or set, or raised, whatever its called), do this.

Thanks sooooooooooo much chickendude!
blackhawk
Have you heard of Z80 Assembly in 28 Days? This z80 asm guide should cover most of your questions.

And hexadecimal is a numerical system with a base of 16. Just because I write the program's assembled hexadecimal dump
Code:
240200070000000C462000860000000C46201300240200030000000C2402000A000000C
which lets you input two numbers (Allows decimal values) and prints out their sum doesn't mean that I am writing in HEX. The above isn't a language. If you ran that on your calc, I have no idea what it would do. Because the program I wrote is for a 32 bit MIPS CPU using SPIM-style syscalls in assembler, assembled it, and copied the text segment dump here. Hexadecimal is not a language, it is a number system.

For loops, you would use jr for short relative jumps or jp for absolute jumps. By themselves, you can jp unconditionally to keep a game loop running.

For drawing sprites, I don't think there are built-in ones. You can use the source for Ion's PutSprite and FastCopy for blitting sprites and flushing the buffer to LCD.

You would use flags with control opcodes like jr, jp, ret, call, and others. Day 7 of Asm in 28 Days goes over this.
I think the hex idea has been nailed in. And really, it's not that big a deal. Everyone knows what you're talking about, it's much shorter than any of the alternatives you've offered, and, for most people (aka people who aren't Xeda Wink), it's not even a viable solution to assembly programming, at least not in the long-run. You'll either have to use an assembler or realize you just won't be able to make the complicated programs you want. Btw, every time someone mentions assembly in the z80 assembly sub-forum, should they prefix asm/assembly with z80 so we know it's not ARM or some other assembly language? Maybe i should stop calling myself an assembly programmer.. Razz

Anyway... onto the questions Very Happy

ti83plus.inc mentions: _DisplayImage EQU 4D9Bh. The 83 Plus System Routines PDF says:
Quote:
DisplayImage

Description
Displays a bitmap image stored in RAM.

Inputs
HL = pointer to image structure
DE = location on screen to place the upper left corner of the image.
(row, column) (0,0) = upper left corner of the screen. The image can be oriented off of the screen: ffh = -1. The only restriction is that the image cannot be entirely off screen.

The first byte contains the data for the first eight-pixels of the first row. Bit 7 is the left-most pixel of the first row. Each new row starts on a byte boundary. There may be unused bits in the last byte of each row if the image is not a multiple of eight in width.

Flags
plotLoc, (IY + plotFlags)
= 1 if image drawn to display only.
= 0 if image drawn to display and graph buffer.
bufferOnly, (IY + plotFlags)
= 1 if image drawn to graph buffer only.


There's also a section on it on z80-heaven. I've never used it as i assume it's awfully slow.

Also, one thing you should know (as it's going to cause you a lot of headaches, but otherwise it would just take forever to draw to the screen) is that you don't (normally) update the LCD one pixel at a time. You update it 8 pixels at a time, since you can only output bytes to the LCD. There are 12 columns, each column holds 8 pixels, which conveniently fits into a byte. Every bit in a byte (8 bits in a byte) represents one pixel. This sounds great, updating 8 pixels at a time is faster than updating 1 at a time! The problem, however, is when you want to draw something where it spans two columns. You'll need to rotate or shift the byte that many bits over, and update two bytes: one byte for the left-hand side of the sprite, and another for the right-hand side of the sprite.

As for how to use flags, in mnemonics you would generally have something like this:

Code:
     ld a,10
     cp 10
      jr z,label
Here, you would jump to 'label'. If you'd written 'jr nz,label', it wouldn't jump to 'label' and would instead just continue on to the next instruction.

Converting this mentally to hex might be a bit more complicated, since the condition doesn't take up a full byte in the instruction but rather two bits of the first byte (the opcode) for jr and and three bits for the others (ret, call, jp). Let's take a look:
The normal jr instruction is %00011000, or $18 in hex (followed by an 8-bit number which tells it how far to jump from the current location: jr = Jump Relative). Cool! Now how do i add in conditions? The conditions actually have a different opcode:
%001XX000, where XX is the condition.
%00 = nz
%01 = z
%10 = nc
%11 = c

Thus, jr nz = %00100000, or $20, z = $28 (%00101000), nc = $30, c = $38.

A perhaps simpler way of looking at it would be like this. To add a(n)...
nz condition, add $0 to the conditional opcode.
z condition, add $8
nc condition, add $10
c condition, add $18

The condtion will always be in bits 3 and 4 (bits are counted like this: %76543210), even for the other conditional instructions. Just for completeness' sake, here are the other conditions that can be used for jp, call, and ret:
po = %100 (+$16)
pe = %101 (+$20)
p = %110 (+$24)
m = %111 (+$28 )

As you can say, they take up three bits. The nz/z and nc/c flags just start with a zero (%000, %001, etc.).

Being able to write z, c, nz, nc, etc. is MUCH more convenient, not to mention you don't need to calculate how far to jump/count code to find the address for jp's and call's or recalculate all your absolute jumps and affected relative jumps every time you add a new line of code... But if you've got the time and patience to do it...
I call it "hex." It might be slang, but I see lots of slang thrown about in any community and I like it. It's easy, and as long as everybody knows what is being referred to, then the purpose of language has been fulfilled Wink

Anyways, if you look on the chart, there is a section to the right of the mnemonic/opcode chart that does a brief explanation of commands and flags. I think it should have a very short explanation of the "cp" instruction and the flags it returns in case you forget.

An easy way for me to remember is that it returns the z and c flag the same way as if you had done a "sub" instruction. It returns c if there was overflow (result is "negative") z flag if the result was 0 (both inputs are the same). So cp c returns the flags the way sub c would, but without modifying register a.
Thanks so much for all the replies! You guys are awesome....

Anyway, I was looking at the display image b_call, and my question here would be, how do I load an image in? I get to load an image of a box to a bit of memory would be-

picBox:
db. 11111111
db. 10000001
db. 10000001
db. 10000001
db. 10000001
db. 10000001
db. 10000001
db. 11111111

so to use this in hexadecimal, would I convert each 8 bit of binary to Hex? so its;

db. FF
db. 81
db. 81
db. 81
db. 81
db. 81
db. 81
db. FF

I assume this is how its done, but how do I load the address of this into HL? I need to count out the address in memory, but how would I implement this?

Thanks in advance,
blackhawk
The address would be where it is in memory. The start of the program on the 83+/84+/SE is $9D95. So if your sprite data was 16 bytes into your program, its address would be $9DA5. I don't know the arguments for that bcall off the top of my head, but if it is to send the address in HL, load 9DA5h to HL:

Code:
21A59D

Don't forget that it is little-endian, so the lowest byte of the address comes first.
Ooooooooo thank you Xeda- so I got that far, how does the db. Command work? I can't find it on the op-code chart....


Thanks again,
Blackhawk
".db" or "db" just tells the assembler that it is a sequence of bytes,. So for example:

Code:

.db "Hello",0     ;48656C6C6F00
.db $3C,$42,$81,$81,$81,$81,$42,$3C   ;3C4281818181423C
.db %11110000   ;F0

There is also ".dw" which stores a word (2 bytes, little endian for the Z80):

Code:

.dw 9D95h     ;959D, start of the program code
.dw 8000h     ;0080, start of RAM
Ok... I've been tinkering around with this for awhile now, and the output just doesn't want to work!

say I plug this into my calculator;


Code:
AsmPrgm
110000       ;Load 0 into de
2AAD95       ;Load the address of pic into HL (10 Bites in)
EF984D      ;Display image in position of DE, and stored at pointer HL
FF   ;BOX PIC
81       
81
81
81
81
81
FF
C9 ;end


All I get are codes and random pixels....
blackhawk (p.S. xeda, how do you know this stuff?!)
You have to put the C9 before the pic, else the program counter will fall into the image and execute it like code.
Ok, I put a c9 before the pic, that stopped the random pixels... Now all I'm getting is the done message when I run it.. I toyed around with de to try to see it, but no dice, nothing comes up on either the graph screen or the home screen... Making progress! Any other ideas?

Eternal gratitude,
Blackhawk
I think you have to copy plotSScreen to the LCD, for example with EF6A48 (_grBufCpy).
A few things: 9D95h is the address of the beginning of the program. Adding 10 is 9D95h+0Ah = 9D9Fh.

Next, you used "2A" instead of "21." The first loads a value into HL from the given address of 95ADh. I have no idea what value is there, except that you were reading a value from the graph screen Razz The latter "21" loads a value directly into HL.

At the next point, you actually used "bcall(_IBoundsFull)" which is the one before _DisplayImage. _DisplayImage is 4D9Bh.

Also, as was pointed out the C9 comes before the data. "C9" doesn't just mean to end the source file. It is an actual opcode that pops the last item off the stack, storing it to the program counter. In your original code, after the bcall, it tried to execute the data, resulting in:

Code:
rst 38h \ add a,c \ add a,c \ add a,c \ add a,c \ add a,c \ add a,c \ rst 38h
. Luckily, this wouldn't cause a crash.

Finally, I decided to read TI's 83psyroutines.pdf to figure out the syntax. It says:
Quote:

HL = pointer to image structure
Height of image in pixels — one-byte
Width of image in pixels — one-byte
Image data by rows
The first byte contains the data for the first eight-pixels of the first row. Bit 7
is the left-most pixel of the first row.
Each new row starts on a byte boundary.
There may be unused bits in the last byte of each row if the image is not a
multiple of eight in width.
DE = location on screen to place the upper left corner of the image.
(row, column)
(0,0) = upper left corner of the screen.
The image can be oriented off of the screen: ffh = -1. The only restriction is
that the image cannot be entirely off screen.


The first two bytes are meant for height and width. Basically, you passed that it was supposed to be 255 pixels tall, 129 pixels wide. Instead, what you want is:

Code:

110000   ;   ld de,0   ;coordinates
219F9D   ;   ld hl,9D9Fh   ;pointer to the image data
EF9B4D   ;   bcall(_DisplayImage)
C9   ;   ret
08   ;Height
08   ;Width
FF818181818181FF   ;Data


From my tests, it seems like a width of less than 8 does not work.

As to how I know this stuff, I read documentation and I experiment. I had very little computer access when I learned assembly. My first assembly program was made via trial and error based on hex codes I found on TI Basic Developer. I had already known how to do math in other bases, and I recognized that the code was in hexadecimal. I saw this code:

Code:

AsmPrgm210000115F3FEF5F4DC9

I knew most programs had a C9 at the end, but then I looked at the "0000" and "5F3F" and I converted those bytes to base 10. I got 0, 0, 95, 63 and so I suspected they were coordinates. After editing the values, I realized I was right-- it made a smaller inverted rectangle-- so then I tried to make two boxes in one program and that worked. I was working on an RPG at that time, so I made two maps using an assembly program that drew various rectangles.
IT WORKED!!!! HAHAHAHAHAHAHAHAHAHAHAHA Thank you ZEDA!! HAHAHAHAHAHAHAHA!!!!! It looks fantastic, one step closer to pong!!! HAHAHAHAHA!

THANK YOU,
blackhawk
Ah, if it is pong that you wanted to make, good luck! It will be a bit slow since the TI-OS bcall trades being powerful for speed. Basically, it is slow, but it has a lot more uses than a much faster sprite drawing routine.

It is probably best to just get a working version, though, and this should be fast enough. I once made a speed optimized pong game and it is a little too fast to see anything besides the paddles 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