问题描述:

I have an assignment for my university. I need to make a program in assembly for the z80 processor. The program should do the following.

There are 20 numbers stored in 20 continuous memory addresses starting from 0201H.

The program should check the number stored in 0200H first. If the value is 0 the program should sort the number in ascending order, otherwise descending. Finally after the sorting is over the program should calculate the average of these 20 numbers.

I know that this question is not formed according to the rules of the site but I really need help.

网友答案:

Here's a quick guide to Z-80 assembly language. Think of it like C but with most of the features removed. Fundamentally you only have a few variables to work with:

unsigned char A;
unsigned short BC, DE, HL, IX, IY;
unsigned char memory[65536];

There are no structures, no blocks, no for or while loops and a sort of mangled if. There is goto which will jump to a label. Or you can call a label which is like a subroutine but it takes no arguments and returns void. We can always build up more functionality from these pieces, but for simple programs the variables we have and a few pieces of memory[] will be enough.

You can use = for assignment but there are a lot of rules. For instance, all these are legal:

A = memory[26];
memory[738] = A;
A = memory[BC];
memory[DE] = A;

But you can't use any old expression like:

A = memory[BC + DE];

The short variables can be assigned to and from memory[] but since they're bigger than char they are automatically split up. When you say memory[15] = BC; it recognizes that it won't fit and does this on your behalf:

memory[15] = BC % 256;
memory[16] = BC / 256;

And to be useful, the opposite happens when you say BC = memory[15]:

BC = memory[15] + memory[16] * 256;

To get even weirder, you can talk about these high and low parts of BC independently as B and C. But you can't do B = memory[25]. If you want that, you need to use A as in intermediary:

A = memory[25];
B = A;

DE can be addressed as D and E and HL as H and L. But not IX and IY (well, not legally, but let's not get into that).

All of the variables can be incremented and decremented. A = A + 1 or A++ if you like. A = A - 1. Same with BC, DE, HL, IX, B, C and so on.

Can you do D = D + 2? No! You have to do D++; D++;. When it comes to math, A is special. You can add any unsigned character to it or a constant. Or subtract. There are some logical operations like &, | and ^ but let's not worry about those. You can also add or subtract any of our unsigned char variables like D or L. But you can't add or subtract memory[]. That also has to be done in pieces. Suppose you want A = A + memory[84]:

B = A; // save A
A = memory[84];
A = A + B;

Yes, it can get tedious. One comes to appreciate compilers. You can also add and subtract from HL, IX and IY but in even more limited cases:

HL = HL + BC;
HL = HL + DE;
HL = HL + HL;  // Same as HL = 2 * HL.
IX = IX + BC;
IX = IX + DE;
IX = IX + IX;
// And follow the same pattern for IY

What about multiplying and dividing? Nope, not supported. With loops and bit shift operators you can manage to simulate them, though. Or less efficiently you can do multiplication by repeated addition and division by repeated subtraction.

But what about loops, anyways? Well, you could fill memory with something like this:

HL = 9; // You can assign constants to all your variables, thankfully.
A = 0;
loop:
  memory[HL] = A;
  HL = HL + 1;
  goto loop;

Did I mention that your program lives in memory[]? No? Well, it does so that's a problem. If you only want to clear only 10 of memory[], how would you do that? Here's where the deranged if comes in. You'd like to write something like this:

HL = 9;
A = 0;
B = 10;
loop:
  memory[HL] = A;
  HL++;
  B = B - 1;
  if (B == 0) goto loop

[ Ooops, that's a bug. Should be != 0, but pretend it was the right thing; I'm too lazy to retype this all. ] But if (B == 0) isn't something that is supported. Too complicated. But there is a goto loop if zero; What does that mean? Well, every time you do math the processor remembers a few things about the result. One is if the result was equal to zero. As it turns out we can simply replace that if with:

goto loop if zero;

And we'll do our loop 10 times. We can even generalize the code to, say, clear out a number of memory[] entries as given by C. How? By having B count up and looping back as long as it isn't equal to C yet. We can't do if (B == C), but we can subtract B from C and check on a zero result which is the same thing. Well, we can't do C = C - B, only A can do that. The loop will look something like this:

loop:
  memory[HL] = 0; // turns out we can do this
  HL = HL + 1;
  B = B + 1;
  A = C;
  A = A - B;
  goto done if zero;
  goto loop;
done:

Awkward, but it'll work. The designers have some mercy. You can goto if the result is not zero:

A = A - B;
goto loop if not zero;

There are some other tests allowed like if the result is less than or greater than zero. But simple testing against zero can take us far.

Special cases abound. What if we accidentally wrote this:

loop:
  memory[HL] = 0;
  B = B - 1;
  HL = HL + 1;
  goto loop if not-zero;

Looks like a bug, doesn't it. Instead of doing B iterations we'll just keep on going until HL wraps around to 0. But it isn't so. When we add or subtract a short value like HL it doesn't affect the zero/not-zero condition. The code will work. In fact, this is considered a feature by the designers of the Z-80.

About the only other thing I'll mention is function calls or subroutines, if you will. Given how every little thing has to be spelled out you can see how even a simple function that multiplies a number by 4 would be nice. But there are no arguments and no return values. Can't say things like B = mult4(B); Instead we just set up conventions where we decide what variables or memory locations are used specially to pass parameters and where the results go. The caller then as to figure it out. We might define a subroutine that multiplies B by 4 and returns the result in A:

mult4:
  A = B;
  A = A + A; // that is, B * 2
  A = A + A; // B * 4, we're don!
  return;

If we wan't to multiply D by 4 we'd do this:

B = D;
mult4();
D = A;

Fair enough. We'd also have to remember that mult4() wiped out the value in A so we better have saved it if we needed it. And for that matter we ourselves have to use up B to make the call. If we needed those variables then we'd just have to find somewhere to stash them away in memory[].

memory[20] = A; // can't forget A
B = D;
mult4();
D = A;
A = memory[20]; // back to what it was.

So that's Z-80 assembly language programming. Oh, there's a bunch of other stuff. And you have do say everything with an accent, but if you look up information about Z-80 assembly language I'm sure you'll get the idea. Here are a few examples:

A = memory[4];               LD A,(4)
A = 3;                       LD A,3
memory[2] = A;               LD (2),A
A = A + B;                   ADD A,B
B = B + 1;                   INC B

Good luck on your assignment.

网友答案:

Programming Z80 by Rodnay Zaks is a must-read for anyone working with Z80 assembler code. It is available for download with permission from the author.

And by the way, one of the code examples included in that book is the bubble sort, explained from top to bottom.

Lots of more Z80 related information are available at the home of the Z80 CPU.

相关阅读:
Top