2
\$\begingroup\$

Recently I was reading about 8051 microcontroller instructions, and come to know that it has a set of registers from \$R_0 - R_7\$.Later on, when I was introduced with register banks then I feel like these registers \$R_0 - R_7\$ are not actual registers, rather, they are just different 8 slots of a selected register-bank. Is it true that \$R_0 - R_7\$ are just different 8 slots of the selected register-bank and are not 'actual registers' like the accumulator or register-B?

Is this the reason why when you push something in the stack you can't use push R1, and rather you have to use push <RAM address of the register>? Because, there is not any "fixed" \$R_1\$ register, and rather it varies with the bank selected?

\$\endgroup\$
9
  • 3
    \$\begingroup\$ Rather than trying to understand this core, which is barely manufactured by anyone nowadays. Intel closed down their production of it 14 years ago. Philips/NXP too, I believe. Even Silabs have labelled their 8051 stuff NRDR and I think that's the last of the rats clinging to the sunken ship. So why not study a core which isn't one of the most dysfunctional ever made and instead study something actually available in production? \$\endgroup\$ Commented Jun 29, 2021 at 11:34
  • 4
    \$\begingroup\$ @Lundin 128 bytes of RAM should be enough for anybody:) \$\endgroup\$ Commented Jun 29, 2021 at 11:41
  • 5
    \$\begingroup\$ @Lundin "barely manufactured"? A lot of chips that contain an embedded with MCU have a 8051 core. \$\endgroup\$ Commented Jun 29, 2021 at 11:48
  • 1
    \$\begingroup\$ Anyhoo I'm not qualified by knowledge to give a proper answer, but looking at the datasheet, yes, "R0 to R7" refers to the 8 bytes in a register bank. Bear in mind that any "actual" registers in any CPU are just internal RAM, even if they have been given fancy names like the Accumulator. Or Registers Tom, DIck and Harry. \$\endgroup\$ Commented Jun 29, 2021 at 12:01
  • 2
    \$\begingroup\$ Conceptually, what is the difference between a ‘register’ implemented in ram vs a register (block of flip/flops)? They both perform the same function. As for the push/pop question, i’ve not had the reason to examine the 8051 microarchitecture. Suffice to say it is quirky, but there always seems to be an instruction that does what you want. \$\endgroup\$ Commented Jun 29, 2021 at 13:00

2 Answers 2

6
\$\begingroup\$

Well, we need to differentiate between the real silicon and the programmer's model. Since just the latter is interesting for us, I will limit my answer to the model.

As you found, there are 4 banks of 8 registers each. The term "register" is a bit misleading here, since these are simply RAM bytes with the option to access them quickly and simple.

You can address each of these 32 registers by direct or indirect addressing. This is the same as addressing any other of the first 128 bytes of the internal RAM.

But the disadvantage is either a bigger instruction (direct addressing needs the address as a parameter) or complicated code (indirect addressing).

So many instructions allow to address a register just by its number, 0 to 7. The remaining bits of its address are built from constant 0s and the RS1 and RS0 bits of the PSW. Such instructions reserve 3 bits for the register number in their opcode.

Example: mov a,r3 with RS1 = 1 and RS0 = 0.

The instruction is coded as binary 11101011 with the register number in its lower bits (emphasized).

The RAM address is built like this:

  • 000..... for the constant zeroes in the MSBs,
  • ...10... from the RS1 and RS0 bits,
  • .....011 from the instruction,

giving a value of 00010011 (or 0x13) for R3 in bank #2.

Aside from the shorter instructions, are there other advantages of banking the general purpose registers?

Yes, you could for example write interrupt service routines using several registers without the need to push or to pop all of them. Dedicate a register bank to this ISR and just switch the bank. You only need to save the PSW.

Oh, and push is not such an instruction, unfortunately. The simple design of the 8051 does not provide such a variant of the instruction. That's why you need to write push acc instead of push a, BTW.

\$\endgroup\$
1
\$\begingroup\$

The 8051 was designed the particular way it was to support multi-threaded programming ... in a day and age before "threads" were in most peoples' vocabulary. The 8051 references didn't explicitly call it out as such, because the actual term "threads" and all things related to it wasn't in their vocabulary at the time either. But, multi-threaded programming has been around, in one form or another, since at least the 1960's.

In today's language: the registers R0-R7 are meant to be used as thread-local registers, so that when you do a context-switch from one thread to another, you basically only need to save A, B, DPTR and PSW, before switching the stack pointer SP and R0-R7 register window. The base address of the R0-R7 window is just PSW&0x18 (thus: either 0x00, 0x08, 0x10 or 0x18), so the switch already happens when PSW is context-switched. All of that makes for blazingly-fast thread-switching and you could literally make all your interrupt handlers as simple thread switchers and use the low interrupt priority as a de facto level 2 priority for threads, with the bottom level 1 priority being that of being outside of interrupt-handlers. Level 3 would be that of being inside a high-priority interrupt handler, which would be mostly reserved for time-critical tasks and spooling.

Unfortunately, there's hardly anyone who's used R0-R7 that way, though it has been. For instance, the original firmware for some of the Rise Tools Displays was entirely thread-based, in 8051 assembly, and used R0-R7 that way. It was also multiprocessor-based with synchronization between units taking place through the 8051 synchronous communications mode.

By way of contrast, the Stepper Motor Driver Demo here was thread-based, but treated R0-R7 as synonymous with addresses 0x00-0x08, which is not the best way to use those registers. It could be adapted and improved upon (particularly, the handlers in the run-time kernel) so that it doesn't need to separately stack any of R0-R7 when handling an interrupt. If you want practice, try modifying it appropriately and try to set it up in a simulator. The design and rationales are contained in The 'Drive' Reference. You'll need the assembler CAS for it.

So, in answer to your question: you shouldn't have to do any push'es or pop's on R0-R7 at all. Instead, you set them up, for different processes, in different areas (regardless of whether your design is thread-based or not). So, when you do a switch to another process, it all comes down to re-loading the PSW for the process in question, and the rest is taken care of, just by that alone, since PSW&0x18 sets the base address for the R0-R7 register window. The demo application just cited, in contrast, did "push 0" and "push 1", along with "pop 1" and "pop 0", which is what you actually want to avoid doing.

Similarly, the need to do push'es and pop's within a process (or thread) is minimized by having 8 such registers around, in addition to the large internal register area. Register windows and large register caches are design features characteristic of RISC processors, thereby giving the 8051 some RISC attributes in its design. Compare it to the 8080/8085 and 8086 evolutionary lineage, and you'll see that it followed an entirely different design direction.

\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.