• Please review our updated Terms and Rules here

Z80 assembly inconsistency

I guess the straight jacket you know is better than all the powerful instructions you don't.
 
The whole 'MOV' for move is just weird; it is NOT a move, it's a copy. Or a LoaD. And stores are just reversed loads....
It may seem weird, but it is not unusual. Before the 8080, and even in modern times. The PowerPC instruction set calls register-register transfers "moves", and uses "load" and "store" terminology as well. It may not be entirely consistent across all architectures, but it is common. Even 8080 is not entirely consistent: MVI "move immediate" is really a load from PC+1, LXI "load double-reg immediate" is the same idea but called a load. Zilog's use of LD for nearly everything is at least as weird. It's probably unlikely anyone could point to an existing architecture and get agreement that it is perfect.
 
At the risk of reopening the whole "Intel vs Zilog mnemonics" war, I'll just say that when I first saw Zilogs mnemonics I thought "cool". But then I saw things like that "ld" was a crude catch-all for all move, load, and store operations. Plus, the mnemonics imply a symmetry that does not exist (you can do "sbc a,b" but not "sbc h,b", etc ad nausium). You basically still need to know the underlying machine language in order to program effectively, so I just chose to stick with Intel mnemonics where that architecture was obvious.
Consider the 8086, then, where MOV isn't even isomorphic; that is, there are several encodings (depending on the assembler being used) for a single MOV instruction. That is, the assembler can generate a "short form" move or one that uses the MOD R/M field to specify operations.
Worse, there was at least one version of MASM (2.0?) that would take an LEA instruction and convert it to a MOV.
 
The ld thing is funny because that was one of the things I liked - the concept of ld dest, source just makes sense to me even if not all possibilities are present. I agree that it is a serious catch-all though!
 
My first experience programming was building tools in 'C' on a PDP11/70 at work to cut down on some of the mind numbing repetition of building translations to add, modify, or remove trunk circuits in ESS switches.. I soon wanted to program at home and built one of the first versions of the Heathkit H89. I got frustrated in that while there were a few 'C' compilers starting to become available for microcomputers, they didn't run on HDOS and were way above my budget. So I ported Ron Cain's 'C' compiler as published in "Dr. Dobbs" to HDOS to run on my H89. One of the steps I took was get it running on the 11/70s at work. In so doing, my first exposure to any assembler programming was on the 11/70. One characteristic of the PDP11, the VAX that followed it, and the MC68K series that the 5ESS switches used extensively, is that they all have a more or less orthogonal instruction set. With a few exceptions, you can use just about any instruction on just about any register in just about any addressing mode. So even though I did a lot on a Z80 machine (the H89 has two) my programming "baby duck" self finds all of the early microprocessors' assemblers and mnemonics to be wierd.
 
Not true; the z80 macro library was incomplete; the IX & IY addressing modes especially. And none of the undocumented instructions.
Macro library. You can add any instructions you want. and I did. But I've gone back and purged all the undocumented instructions from my code because it needs to run on Z180 and beyond. Indexed addressing instructions were present in the DRI Z80.LIB.
 
Macro library. You can add any instructions you want. and I did. But I've gone back and purged all the undocumented instructions from my code because it needs to run on Z180 and beyond. Indexed addressing instructions were present in the DRI Z80.LIB.
"And beyound" would include the Z280 which formalized all the undocumented ones. It's my current Z80 hardware of choice (perhaps ironically I use a Z280 macro library to generate Z80 code for when Z280 hardware isn't available) . Only some of the indexed instructions were present; but not all.
 
The ld thing is funny because that was one of the things I liked - the concept of ld dest, source just makes sense to me even if not all possibilities are present. I agree that it is a serious catch-all though!
Yeah, it made sense to me to read LD A,H as ' load A with the contents of H.' The word 'move' implies, at least to me, that the source is deleted or zeroed; the 'mv' shell command in Bourne and similar Unix shells, for instance. And reading MOV A,H as 'move to A from H' is just backwards (the more natural read is 'move A to H' but that's wrong). But, there are those who enjoy such things (RPN users, for instance), I guess.

But Z80 is way far from orthogonal. Even Z280, which fixes a lot of things, mainly the Z80 lack of stack pointer relative loads, is way far from orthogonal (and real Z280 silicon, depending on revision, has some really squirrelly bugs).

There are better instruction sets out there, for sure, but I tend to enjoy the Z80 way.
 
IBM has used many terms in the past for copying the contents of one memory location or register to another. "Transmit" was popular back in the day; e.g. 1620 TF or TR (transmit field/record). Move came in with S/360 as in MVC (move characters). Copying a register to or from memory has pretty much always been "load" or "store".

The CDC 6000/Cyber systems had an interesting syntax. The machines are 3-address with certain fixed relationships between registers and functional units. So "SA5 B6+10" would mean "Set A register 5 to the address obtained by adding the contents of B register 6 and 10, then load X register 5 with the contents of that address." The "S" in the operation field stated that the function was to be performed by one of the increment functional units. Similarly, "BX5 X6*X4" says "Using the boolean unit, take the logical AND of registers X6 and X4 and put it in register X5." On the other hand, 6000 PPU code is more conventional; e.g. "LDC 1234" means "load the A register wtih the constant 1234".

Historically, there has been a very wide range of assembler syntax and mnemonics. I think it was IBM that pioneered the ordering of (destination,source) in the operand field. ANSI rolled out a standard that specified that the destination comes last--I think most vendors ignored it.
 
But Z80 is way far from orthogonal. Even Z280, which fixes a lot of things, mainly the Z80 lack of stack pointer relative loads, is way far from orthogonal (and real Z280 silicon, depending on revision, has some really squirrelly bugs).
Yeah, for the Z280 don't accept anything before rev G. IIRC the newest rev was J. The only known bug there was having to put a NOP after some OUT instructions. Still a great processor.

I cut my teeth on PDP-11's and moved up to VAXes… the 68000 and PowerPC; all nicely orthogonal (compared to 8080/Z80). Nothing Intel did ever made sense until they went 64-bit (what I call their RISK mode).
 
I got bitten by the SUB A,B versus SUB B inconsistency while programming the NEC TK-80. Assembling was done on my laptop using asm8080 assembler and it did not complain about SUB A,B but the result of that instruction was always zero.. Took me a while before I found out (being used to Z80 more than 8080)...
 
The memory is vague, most of them are these days, but I remember dumping an assembler that insisted that I didn't really want to MOV A,0. I really wanted to XOR A AND SAVE A WHOLE FREAKING BYTE!
 
I got bitten by the SUB A,B versus SUB B inconsistency while programming the NEC TK-80. Assembling was done on my laptop using asm8080 assembler and it did not complain about SUB A,B but the result of that instruction was always zero.. Took me a while before I found out (being used to Z80 more than 8080)...

This is a bug in asm8080 of course.

Try "sub a,xyzzy" for fun...

Dave
 
The memory is vague, most of them are these days, but I remember dumping an assembler that insisted that I didn't really want to MOV A,0. I really wanted to XOR A AND SAVE A WHOLE FREAKING BYTE!
That sounds like the classic MOV vs. MVI mistake when using Intel mnemonics. The assembler probably wasn't telling you "MVI A,0" was better done by "XRA A", but rather that "MOV A,0" was not legal syntax. Many assemblers were not this sophisticated, though, and "MOV A,0" would have just been silently turned into "MOV A,B". Likewise, "MVI A,B" was silently turned into "MVI A,0" (similar to intentional, useful, things like "MVI A,JMP"). I know I got bit several times by converting code between using immediates and registers, and failing to change a "MOV" to "MVI" or vice versa.
 
I wrote a very simple test program for the 6502 recently.

During the last 3 weeks (or so) I have been hand transcribing old 6800 source code listings into source code text files.

I mistakenly entered "BRA GO" into my 6502 program (being a 2-byte relative branch).

Unfortunately, there is no such 6502 instruction! The assembler quite happily took it and turned it into one of the instruction bytes. There is (of course) a 6800 equivalent...

Interestingly, the later 65C02 added a relative branch instruction at that opcode byte - so it seems as though the assembler writer has been too clever and implemented the enhanced instruction set without any apparent means (I did re-check the documentation) of disabling the enhancements!

Beware buggy assemblers and enhancements!

Dave
 
Try "sub a,xyzzy" for fun...
This is why assemblers that let you use comments without a semicolon are a problem. They simply ignore everything else after they think they got what they wanted. But even then, at least they could bother to check for stuff before the next blank and complain about the extra stuff. As for me, I like being able to put blanks in my operands for readability, so it's semicolon always for comments.

implemented the enhanced instruction set
That's why I make an effort to support different instruction set variants when I can, it's belt and suspenders to make sure you don't do stuff that won't work. It's important in a disassembler too.
 
Back
Top