EmbeddedRelated.com
Forums
The 2026 Embedded Online Conference

filling remaining array elements with fixed value

Started by blisca June 12, 2014
On 25/06/14 20:55, Simon Clubley wrote:
> I'll admit I didn't realise the 6502 was still around - I last used > it back in the BBC Model B days (ie: my school days). At the time I > seem to remember preferring the Z80 (can't remember why)
The Z80 had a decent simple interface to external hardware, and many of the software writers were actually hardware engineers. Many Z80 operations looked good until you tried to use them, e.g. "you can do a print function with one instruction", or trying to use the index registers when pointing to linked list nodes. > although some classmates seemed to prefer the 6502 architecture Z80 had "conventional" stacks and none of the page-zero tricks/limitations.
On 25/06/14 21:55, Simon Clubley wrote:
> On 2014-06-25, Don Y <this@is.not.me.com> wrote: >> On 6/16/2014 5:27 AM, Simon Clubley wrote: >>> >>> No, but it's been through a incompatible language revision. >> >> This is true of other languages, as well. E.g., foo =- 2; >> >> Every language (tool) you rely upon in a project creates a dependency >> on that tool and the staff who must be able to *use* it. Try taking >> someone accustomed to (i.e., recent grad) C11 and sit them down in >> front of a C89 codebase WITH C89 TOOLS (because your industry requires >> tools to be formally certified prior to use). They will spend >> countless hours wondering why their "perfect" code is throwing >> compiler errors. >> > > Oh, that could be interesting. :-) > > I've actually used those early C compilers (a long time ago).
Moving from C99 back to C90 for some small changes to an old project recently was a bit of a pain. Serious work would quickly be annoying.
> >> And, once you've clued them in to the reason, they'll be fuming /sotto >> voce/ each time they stumble on another "gotcha". I.e., the tool >> is "fighting them" -- they're not a "happy camper". >> >>> (Which is probably a sign of language maturity. :-)) >>> >>> Don't forget as well that a decade ago Perl was very popular and now >>> it's a legacy language. >> >> Where will C++, Java, Python, Perl, Ruby, etc. be "a few years hence"? >> Where will the folks who can efficiently *develop* with them be? >> > > This is why it's so important to learn the concepts first and the > language second. With that grounding, it's a lot easier to adapt > to new languages.
Agreed.
> >> (e.g., 6502 ASM programmers are still in demand -- because no one >> *writes* 6502 ASM nowadays, "mainstream") > > I'll admit I didn't realise the 6502 was still around - I last used > it back in the BBC Model B days (ie: my school days). At the time I > seem to remember preferring the Z80 (can't remember why) although > some classmates seemed to prefer the 6502 architecture. [*] >
I preferred the 6502 - mainly because the BBC had a decent assembler and a reliable disk drive, while my old Spectrum (with a Z80A) had no tools at all (hand assembling - and I even had to write BASIC code to interpret the hex code and put it into memory) and an unreliable tape recorder. Debugging machine code on the spectrum was mainly a matter of listen to the noises made by the power supply, and if it hung then everything had to be typed in again on a chewing-gum keyboard. It taught me the value of simple, careful and correct coding! The Z80 was a more powerful architecture, with a number of 16-bit operations and many more registers than the 6502 - though the 6502 code do more operations per clock cycle.
> What is the 6502 still used in ? > > Simon. > > [*] Of course, these days, schoolchildren are more likely to be > comparing social media platforms instead of computer architectures. :-) >
Op 25-Jun-14 23:14, David Brown schreef:
> The Z80 was a more powerful architecture, with a number of 16-bit > operations and many more registers than the 6502 - though the 6502 code > do more operations per clock cycle.
In those days it was more like less clock cycles per operation; an instruction took 2-7 clocks on the 6502 (and many more on the Z80).
On 26/06/14 00:09, Dombo wrote:
> Op 25-Jun-14 23:14, David Brown schreef: >> The Z80 was a more powerful architecture, with a number of 16-bit >> operations and many more registers than the 6502 - though the 6502 code >> do more operations per clock cycle. > > In those days it was more like less clock cycles per operation; an > instruction took 2-7 clocks on the 6502 (and many more on the Z80). >
Yes, I meant more MIPS/MHz - or a greater fraction of an instruction per clock cycle. The 6502 had a pipelined architecture with instructions overlapping with each other, which was unusual at the time for processors of that size.
On 6/25/2014 3:09 PM, Dombo wrote:
> Op 25-Jun-14 23:14, David Brown schreef: >> The Z80 was a more powerful architecture, with a number of 16-bit >> operations and many more registers than the 6502 - though the 6502 code >> do more operations per clock cycle. > > In those days it was more like less clock cycles per operation; an > instruction took 2-7 clocks on the 6502 (and many more on the Z80).
A Z80 would require 4-23 (?) clocks per instruction. E.g., 4 clocks required the operand(s) to be "implied" (like "INC r8") or encoded directly in that one-byte opcode. Towards the other extreme, something like a CALL <addr16> required 10 (4+3+3) to fetch the instruction (including the target address) and 6 more (3+3) to push the PC. What you really want to look at is how much "work per memory dollar" (or similar metric). E.g., you could clock a Z80 at a higher rate which effectively compensated for the fact that it took 4 clocks to "fetch an opcode". By contrast, the 6502 (and 68xx family from Motogorilla) ran with a slower overall clock -- and "wasted" half the period of the memory access. So, put a given speed memory in the design and diddle the CPU "frequencies" so that the memory was being used at its capacity. *Then*, see which is doing more work! With memory speeds (UV EPROM) in the ~450ns range, this effectively limited the "single clock per reference" architectures to ~1MHz. By contrast, the Z80 could run at ~3MHz for the same memory dollars. So, a "direct CALL" (CALL <addr16>) on a 3MHz Z80 would require a bit over 5us. The same sort of instruction on a 1MHz 6502 would require 6 "clocks" -- 6us. [A friend was in the Moto camp back then and we would continuously be having these discussions -- trying to adjust our respective metrics to reflect this inherent "oscillator difference". It didn't take long to learn that creating reliable benchmarks is a waste of time -- as you would tend to solve problems differently based on the hardware at your disposal and its assets/limitations. E.g., with the 6502's "zero page", you though really hard about what you crammed into those precious locations! The Z80, having nothing comparable, "freed" you to worry about *other* issues]
Hi Tom,

On 6/25/2014 2:08 PM, Tom Gardner wrote:
> On 25/06/14 20:55, Simon Clubley wrote: >> I'll admit I didn't realise the 6502 was still around - I last used >> it back in the BBC Model B days (ie: my school days). At the time I >> seem to remember preferring the Z80 (can't remember why) > > The Z80 had a decent simple interface to external hardware, > and many of the software writers were actually hardware > engineers.
One of the best things the Z80 had going for it was the separate 64K "I/O space". You could be really sloppy in your address decoding without worrying that you were throwing away precious parts of the "program/data memory" space. [The other best thing was using the alternate register set for very low latency IRQ's!]
> Many Z80 operations looked good until you tried to use them, > e.g. "you can do a print function with one instruction", > or trying to use the index registers when pointing to linked > list nodes.
The index registers were intended to reference structs. People often failed to realize this and tried to use them in the way that HL (and BC/DE to a lesser extent) was used. The extra penalty brought about by the support for the <offset> and the "escape" prefix made this impractical. OTOH, you could bring them to bear for incredibly structured codign that would be a nightmare if you were constantly forced to explicitly perform "address arithmetic" on a "base register" PUSH HL ; preserve pointer to struct LD DE,offset ; offset of member ADD HL,DE ; r.hl points to member; r.de is trash LD E,(HL) ; get low byte of member INC HL ; point to high byte LD D,(HL) ; get high byte POP HL ; recover pointer to struct vs. LD E,(IX+memberL) LD D,(IX+memberH) This made the code easier to understand and discouraged microoptimizing algorithms and struct layouts (so, for example, you could "walk through it" sequentially instead of "random access")
> > although some classmates seemed to prefer the 6502 architecture > > Z80 had "conventional" stacks and none of the page-zero > tricks/limitations.
On 26/06/14 02:34, Don Y wrote:
> Hi Tom, > > On 6/25/2014 2:08 PM, Tom Gardner wrote: >> On 25/06/14 20:55, Simon Clubley wrote: >>> I'll admit I didn't realise the 6502 was still around - I last used >>> it back in the BBC Model B days (ie: my school days). At the time I >>> seem to remember preferring the Z80 (can't remember why) >> >> The Z80 had a decent simple interface to external hardware, >> and many of the software writers were actually hardware >> engineers. > > One of the best things the Z80 had going for it was the separate > 64K "I/O space". You could be really sloppy in your address decoding > without worrying that you were throwing away precious parts of the > "program/data memory" space. >
Separate I/O space is very nice for hardware, and for assembly programming - but it can be a real pain for C where memory-mapped I/O is more natural (especially for bigger blocks of data, such as buffers).
> [The other best thing was using the alternate register set for very > low latency IRQ's!]
That was a nice feature. I remember you could also swap AF and A'F' with a single instruction, which was handy for intermediate data in calculations.
> >> Many Z80 operations looked good until you tried to use them, >> e.g. "you can do a print function with one instruction", >> or trying to use the index registers when pointing to linked >> list nodes. > > The index registers were intended to reference structs. People > often failed to realize this and tried to use them in the way that > HL (and BC/DE to a lesser extent) was used. The extra penalty > brought about by the support for the <offset> and the "escape" > prefix made this impractical.
I vaguely remember doing some "object oriented" assembly programming using IX and IY to point to objects - but it was a very long time ago, and just hobby stuff.
> > OTOH, you could bring them to bear for incredibly structured > codign that would be a nightmare if you were constantly forced > to explicitly perform "address arithmetic" on a "base register" > PUSH HL ; preserve pointer to struct > LD DE,offset ; offset of member > ADD HL,DE ; r.hl points to member; r.de is trash > LD E,(HL) ; get low byte of member > INC HL ; point to high byte > LD D,(HL) ; get high byte > POP HL ; recover pointer to struct > vs. > LD E,(IX+memberL) > LD D,(IX+memberH) > This made the code easier to understand and discouraged microoptimizing > algorithms and struct layouts (so, for example, you could "walk through > it" sequentially instead of "random access") > >> > although some classmates seemed to prefer the 6502 architecture >> >> Z80 had "conventional" stacks and none of the page-zero >> tricks/limitations. >
On 26/06/14 01:34, Don Y wrote:
> Hi Tom, > > On 6/25/2014 2:08 PM, Tom Gardner wrote: >> On 25/06/14 20:55, Simon Clubley wrote: >>> I'll admit I didn't realise the 6502 was still around - I last used >>> it back in the BBC Model B days (ie: my school days). At the time I >>> seem to remember preferring the Z80 (can't remember why) >> >> The Z80 had a decent simple interface to external hardware, >> and many of the software writers were actually hardware >> engineers. > > One of the best things the Z80 had going for it was the separate > 64K "I/O space". You could be really sloppy in your address decoding > without worrying that you were throwing away precious parts of the > "program/data memory" space. > > [The other best thing was using the alternate register set for very > low latency IRQ's!] > >> Many Z80 operations looked good until you tried to use them, >> e.g. "you can do a print function with one instruction", >> or trying to use the index registers when pointing to linked >> list nodes. > > The index registers were intended to reference structs. People > often failed to realize this and tried to use them in the way that > HL (and BC/DE to a lesser extent) was used. The extra penalty > brought about by the support for the <offset> and the "escape" > prefix made this impractical. > > OTOH, you could bring them to bear for incredibly structured > codign that would be a nightmare if you were constantly forced > to explicitly perform "address arithmetic" on a "base register" > PUSH HL ; preserve pointer to struct > LD DE,offset ; offset of member > ADD HL,DE ; r.hl points to member; r.de is trash > LD E,(HL) ; get low byte of member > INC HL ; point to high byte > LD D,(HL) ; get high byte > POP HL ; recover pointer to struct > vs. > LD E,(IX+memberL) > LD D,(IX+memberH) > This made the code easier to understand and discouraged microoptimizing > algorithms and struct layouts (so, for example, you could "walk through > it" sequentially instead of "random access")
That's what I initially thought, and is valid if most of the "work" is "in" a single struct. But it wasn't the dominant factor in "my" programs. Overall, it was a right royal pain finding the right struct in the first place, and inefficient if the work once there was minimal. For example, given a liked-list of nodes/structs where IX+2 contained a pointer to the next node/struct in the linked list. You couldn't move to the next node using LD IX,(IX+2) so IIRC IX had to moved into HL via the stack(!), incremented, then moved back via the stack. That was appalling when traversing a linked list to find the relevant node - much easier just to keep everything in the HL register. The 6800 was much better in that respect, the 6809 was even better.
>> > although some classmates seemed to prefer the 6502 architecture >> >> Z80 had "conventional" stacks and none of the page-zero >> tricks/limitations. >
Hi Tom,

On 6/26/2014 1:02 AM, Tom Gardner wrote:
>> OTOH, you could bring them to bear for incredibly structured >> codign that would be a nightmare if you were constantly forced >> to explicitly perform "address arithmetic" on a "base register" >> PUSH HL ; preserve pointer to struct >> LD DE,offset ; offset of member >> ADD HL,DE ; r.hl points to member; r.de is trash >> LD E,(HL) ; get low byte of member >> INC HL ; point to high byte >> LD D,(HL) ; get high byte >> POP HL ; recover pointer to struct >> vs. >> LD E,(IX+memberL) >> LD D,(IX+memberH) >> This made the code easier to understand and discouraged microoptimizing >> algorithms and struct layouts (so, for example, you could "walk through >> it" sequentially instead of "random access") > > That's what I initially thought, and is valid if most > of the "work" is "in" a single struct. But it wasn't > the dominant factor in "my" programs. > > Overall, it was a right royal pain finding the right struct in > the first place, and inefficient if the work once there > was minimal. > > For example, given a liked-list of nodes/structs where > IX+2 contained a pointer to the next node/struct in the > linked list. You couldn't move to the next node using > LD IX,(IX+2) so IIRC IX had to moved into HL via the > stack(!), incremented, then moved back via the stack.
The Z80 was an 8 bit processor. There were very few 16b operations (other than immediates, push/pop, etc.). PUSH HL ;preserve HL (in case it is important) LD L,(IX+offsetL) ; get low byte of pointer to next node LD H,(IX+offsetH) ; and high byte into r.DE EX SP,HL ; restore r.HL, ToS -> next node POP IX ; r.IX points to next node The only lost item is the pointer to the current node. But, there's a lot of "activity" in those few instructions ; r.HL points to current node INC HL INC HL ; r.HL points to member referencing next node LD E,(HL) INC HL LD D,(HL) ; r.DE points to next node EX DE,HL ; r.HL points to next node This forfeits r.DE's original contents. However, this forces you to do any other references into that "node" relative to HL: EX DE,HL ; r.DE -> current node LD HL,offset(MEMBER) ADD HL,DE ; r.HL -> member in this node; r.de -> current node This tends to cause you to redefine the layouts of your structs to avoid the 16b ADD. E.g., you would, instead, *walk* r.HL (or r.DE, etc.) through the struct *consuming* its members in whatever order your algorithm requires then, "conveniently" ending up with r.HL pointing at the member that serves to link to the next node... I.e., moving the pointer was cheaper than computing a *new* value for that pointer. With the (relatively) big register set, you could do a lot without having to reload pointers (from immediate data). When I was writing Z*80 code, my statement comments usually were preoccupied with keeping track of what was in each register, ToS, etc. Tedious but often saved a fair bit of memory accesses. Of course, if you actually have an *array* of nodes (i.e., not a linked list), then it is easier to: PUSH DE LD DE,sizeof(NODE) ADD IX,DE POP DE This, of course, being more expensive than PUSH DE LD DE,sizeof(NODE) ADD HL,DE POP DE *but*, the former preserves the ability to do indexed references (instead of having to keep diddling with HL)
> That was appalling when traversing a linked list to find > the relevant node - much easier just to keep everything > in the HL register. > > The 6800 was much better in that respect, the 6809 was > even better. > >>> > although some classmates seemed to prefer the 6502 architecture >>> >>> Z80 had "conventional" stacks and none of the page-zero >>> tricks/limitations.
The 6502/68xx required "persistent memory" to reside at 0xFFFX. And, to use zero page, you also had to decode *that* address range (which was typically to R/W memory, not "persistent" memory/ROM). Given the sizes of memory devices at that time, you either had to complicate the address decoder *or* "waste" pieces of the address space. The 65816 was a much nicer processor -- but relatively *late* onto the scene. *All* of these processors were total *dogs* when it came to HLL's! :<
On 26/06/14 10:03, Don Y wrote:
> Hi Tom, > > On 6/26/2014 1:02 AM, Tom Gardner wrote: >>> OTOH, you could bring them to bear for incredibly structured >>> codign that would be a nightmare if you were constantly forced >>> to explicitly perform "address arithmetic" on a "base register" >>> PUSH HL ; preserve pointer to struct >>> LD DE,offset ; offset of member >>> ADD HL,DE ; r.hl points to member; r.de is trash >>> LD E,(HL) ; get low byte of member >>> INC HL ; point to high byte >>> LD D,(HL) ; get high byte >>> POP HL ; recover pointer to struct >>> vs. >>> LD E,(IX+memberL) >>> LD D,(IX+memberH) >>> This made the code easier to understand and discouraged microoptimizing >>> algorithms and struct layouts (so, for example, you could "walk through >>> it" sequentially instead of "random access") >> >> That's what I initially thought, and is valid if most >> of the "work" is "in" a single struct. But it wasn't >> the dominant factor in "my" programs. >> >> Overall, it was a right royal pain finding the right struct in >> the first place, and inefficient if the work once there >> was minimal. >> >> For example, given a liked-list of nodes/structs where >> IX+2 contained a pointer to the next node/struct in the >> linked list. You couldn't move to the next node using >> LD IX,(IX+2) so IIRC IX had to moved into HL via the >> stack(!), incremented, then moved back via the stack. > > The Z80 was an 8 bit processor. There were very few 16b > operations (other than immediates, push/pop, etc.).
Yup, the earlier 6800 was better in this respect! I was unpleasantly surprised by the Z80 - after having used it I thought the 8080 instruction set was better designed.
> PUSH HL ;preserve HL (in case it is important) > LD L,(IX+offsetL) ; get low byte of pointer to next node > LD H,(IX+offsetH) ; and high byte into r.DE > EX SP,HL ; restore r.HL, ToS -> next node > POP IX ; r.IX points to next node > > The only lost item is the pointer to the current node. > But, there's a lot of "activity" in those few instructions > > ; r.HL points to current node > INC HL > INC HL ; r.HL points to member referencing next node > LD E,(HL) > INC HL > LD D,(HL) ; r.DE points to next node > EX DE,HL ; r.HL points to next node > > This forfeits r.DE's original contents. > > However, this forces you to do any other references into that "node" > relative to HL: > > EX DE,HL ; r.DE -> current node > LD HL,offset(MEMBER) > ADD HL,DE ; r.HL -> member in this node; r.de -> current node > > This tends to cause you to redefine the layouts of your structs > to avoid the 16b ADD. E.g., you would, instead, *walk* r.HL > (or r.DE, etc.) through the struct *consuming* its members in > whatever order your algorithm requires then, "conveniently" > ending up with r.HL pointing at the member that serves to link > to the next node... > > I.e., moving the pointer was cheaper than computing a *new* > value for that pointer. > > With the (relatively) big register set, you could do a lot > without having to reload pointers (from immediate data).
The trouble was that you *needed* a larger register set because of the "poor" instructions/addressing modes. The 6800 worked very nicely with fewer registers since they were closer to being orthogonal general purpose registers. By comparison, the Z80 forced lots of register shuffling.
> When I was writing Z*80 code, my statement comments usually > were preoccupied with keeping track of what was in each > register, ToS, etc. Tedious but often saved a fair bit of > memory accesses.
At that time the memory accesses weren't a problem since for many processors (99000 famously) the instruction timing was the same as memory access timing. That changed later, of course.
> Of course, if you actually have an *array* of nodes (i.e., > not a linked list), then it is easier to: > > PUSH DE > LD DE,sizeof(NODE) > ADD IX,DE > POP DE > > This, of course, being more expensive than > > PUSH DE > LD DE,sizeof(NODE) > ADD HL,DE > POP DE > > *but*, the former preserves the ability to do indexed references > (instead of having to keep diddling with HL) > >> That was appalling when traversing a linked list to find >> the relevant node - much easier just to keep everything >> in the HL register. >> >> The 6800 was much better in that respect, the 6809 was >> even better. >> >>>> > although some classmates seemed to prefer the 6502 architecture >>>> >>>> Z80 had "conventional" stacks and none of the page-zero >>>> tricks/limitations. > > The 6502/68xx required "persistent memory" to reside at 0xFFFX. > And, to use zero page, you also had to decode *that* address > range (which was typically to R/W memory, not "persistent" > memory/ROM). Given the sizes of memory devices at that time, > you either had to complicate the address decoder *or* "waste" > pieces of the address space. > > The 65816 was a much nicer processor -- but relatively *late* > onto the scene. > > *All* of these processors were total *dogs* when it came to > HLL's! :<
I'll disagree, for embedded systems at least. The code emitted by ?WhiteSmith's? C compiler for the Z80 was perfectly respectable. The only graunch I remember was i/o to a computed address having to be done by constructing the code on the stack, then executing it.
The 2026 Embedded Online Conference