Uncovering the R800

Pagina 1/7
| 2 | 3 | 4 | 5 | 6

Door Grauw

Enlighted (8015)

afbeelding van Grauw

06-12-2018, 21:22

Very little is known about the turboR’s R800.

For the R800, it is said it is part of the Z800 and Z280 family, but what this means exactly is unclear. It’s known that there are differences in the instruction set, but there is very little actual information.

Additionally if one inspects the pinout of the R800 one can see DMA-related pins, as well as the MA13-MA23 pins which could suggest the presence of an MMU with 8K pages as well, and 8 interrupt signals. Even if not connected (like the DMA pins are in the turboR), how are these functions accessed?

In this thread I hope we can perhaps discover some new things. I did some research which I will post below, and hopefully it can spark some discussion and further investigation.

Aangemeld of registreer om reacties te plaatsen

Van Grauw

Enlighted (8015)

afbeelding van Grauw

07-12-2018, 00:53

The R800 instruction timing is pretty well-documented. However the processor’s capabilities leave much unknown. Let’s start off with the claims that it’s derived from the Z800.

Wikipedia mentions that the Z800 was rebranded Z280, and if we compare the Z280 technical manual with the Z800 information in Zilog’s 1983/84 components databook (p. 599 and onward) this seems to be correct. So here we have quite documentation. Maybe this can aid our understanding of the R800.

The Z280 has DMA functions, an MMU with 4K pages, and eight trap interrupts for amongst others stack overflow protection, breakpoints and memory protection. I see similarities to those R800 pins mentioned earlier.

The opcodes of MULUB and MULUW also match the Z280 opcodes for MULTU and MULTUW. So there is indeed upward compatibility, unlike the Z180 and eZ80.

Unfortunately I can break your dreams right now; I did some tests, and I was not able to execute any of the other Z280 instructions on the R800. Looks like if the R800 was based on the Z800, it was severely stripped.

I tested all the undocumented op-codes in the DD and ED ranges, results below.

CB range:

The undocumented opcodes for CB 30-37 seem to have the same function as SLA (CB 20-27). If these are considered as the logical complement of SRA & SRL, an undocumented instruction called SLL, this makes sense since a logical and arithmetic left shift are the same thing.

This is unlike the Z80, where these shift a 1 into the right bit. This undocumented Z80 instruction is often also referred to as SLL, but does not behave like a logical left shift.

This is also unlike the Z280, which uses this range for a “TSET” (test-and-set) instruction which tests a bit and then sets the value to FFH.

DD range:

All undocumented opcodes have a length of 2, with no visible effect on AF, BC, DE, HL, IX or IY, and thus likely are a no-op. Specifically these are:

00-08, 0A-18, 1A-20, 27-28, 2F-33, 37-38, 3A-43, 47-4B, 4F-53, 57-5B, 5F, 76, 78-7B, 7F-83, 87-8B, 8F-93, 97-9B, 9F-A3, A7-AB, AF-B3, B7-BB, BF-CA, CC-E0, E2, E4, E6-E8, EA-F8, FA-FF

This is unlike the Z80, where many of these behave like their un-prefixed counterparts (a slower version of it).

DD CB range:

All undocumented opcodes have a length of 4, with no visible effect on AF, BC, DE, HL, IX or IY, and thus likely are a no-op.

This is unlike the Z80 too, where the undocumented variants store the operated value in a register.

ED range:

Most undocumented opcodes have a length of 2, with no visible effect on AF, BC, DE, HL, IX or IY, and thus likely are a no-op. Specifically these are:

00-3F, 4C, 4E, 54-55, 5C-5D, 64-66, 6C, 74-77, 7C-81, 83-89, 8B-91, 93-99, 9B-9F, A4-A7, AC-AF, B4-B7, BC-C0, C2, C4-C8, CA, CC-D0, D2, D4-D8, DA, DC-E0, E2, E4-D8, EA, EC-F0, F2, F4-D8, FA, FC-FF

However this range has some interesting exceptions:


Although the R800 instruction tables found in amongst others the R800 Technical Handbook state that MULUB only works for B, C, D and E, in my tests these also seem to work fine for H, L and A. I have not tested all possible input values though, so this should be verified. But LD A,11H / MULUB A,A yields 0121H as expected, and H=55H -> 05A5H / L=66H -> 06C6H for the other two.


This one has the same output as ED F9 (MULUB A,A), so it does not read (hl) as the register bits of the opcode (bits 3-5) would suggest.


These correspond to the DE and HL variants of the MULUW instruction. The technical handbook says these do not work, and indeed this seems to be the case, since the output is not the multiplication of the values I input.

They both yield the same result, and it changes when the input values do; it would be interesting to try with many different values and derive what is happening here exactly, to see if it is somehow useful or at least to improve emulation.


These yield the same results as the above ED D3 and ED E3. Although they are not specified as part of the MULUW opcode range, since the 16-bit register bits are set in bits 4-5. However if the decoder of this instruction actually looks at bits 3-5, it could be that it’s processed by the multiplication unit as well.

ED 6B nn nn

This opcode has a length of 4 and changes HL to 6B6BH no matter what value I specify in nn nn.

On Z80 this is LD HL,(nn), a slower version of 2A nn nn, part of the LD rr,(nn) series.

I think maybe this is a broken instruction? Further investigation is needed.

ED 63 nn nn

This instruction has a length of 4 and does not modify any registers.

It may be the LD (nn),HL counterpart of ED 6B, in which case perhaps it writes HL to address 6363H? I haven’t tested this.

ED 82 xx
ED 8A xx
ED 92 xx
ED 9A xx

These instructions have a length of 3 and do not modify any registers. I don’t know what they do.

I did observe some strange behaviour; infrequently they seem to have a length of 2 and the opcode in the place of xx is executed. Maybe this is a problem with my test program, or maybe they do not like it if an interrupt or refresh occurs in the middle of the instruction? Just a theory.

Further investigation is needed.

ED 71 xx nn nn

This instruction has a length of 5 and modifies DE. The value of DE seems to be specified in the bytes nn nn. I don’t know what xx does.

Here too I have observed that strange behaviour where infrequently the opcode in the place of xx is executed. I think I recall if xx is a 2-opcode instruction like LD A,n, then the value of n is fetched from the byte after nn nn (I tried this yesterday so I’m not 100% sure now).

This is by the way where the undocumented “OUT (C),0” Z80 instruction is located.

Further investigation is needed.

Especially those last few op-codes are interesting. I’m wondering if perhaps they allow access to R800 on-chip functions like an MMU.

Van Grauw

Enlighted (8015)

afbeelding van Grauw

06-12-2018, 21:33

And here is my test program:

100 AF=&H11FF
110 BC=&H2208
120 DE=&H3344
130 HL=&H5566
140 IX=&H7788
150 IY=&H99AA
160 '
180 '
190 POKE &HD000,AF AND 255:POKE &HD001,(AF AND &HFF00) \ 256 AND 255
200 POKE &HD002,BC AND 255:POKE &HD003,(BC AND &HFF00) \ 256 AND 255
210 POKE &HD004,DE AND 255:POKE &HD005,(DE AND &HFF00) \ 256 AND 255
220 POKE &HD006,HL AND 255:POKE &HD007,(HL AND &HFF00) \ 256 AND 255
230 POKE &HD008,IX AND 255:POKE &HD009,(IX AND &HFF00) \ 256 AND 255
240 POKE &HD00A,IY AND 255:POKE &HD00B,(IY AND &HFF00) \ 256 AND 255
250 '
260 AD=&HC000
280 DEFUSR=&HC000:U=USR(0)
290 '
300 AF=PEEK(&HD000) + PEEK(&HD001) * 256
310 BC=PEEK(&HD002) + PEEK(&HD003) * 256
320 DE=PEEK(&HD004) + PEEK(&HD005) * 256
330 HL=PEEK(&HD006) + PEEK(&HD007) * 256
340 IX=PEEK(&HD008) + PEEK(&HD009) * 256
350 IY=PEEK(&HD00A) + PEEK(&HD00B) * 256
360 '
370 GOSUB 390:END
380 '
390 PRINT "AF: ";RIGHT$("000"+HEX$(AF),4);" ";
400 PRINT "BC: ";RIGHT$("000"+HEX$(BC),4)
410 PRINT "DE: ";RIGHT$("000"+HEX$(DE),4);" ";
420 PRINT "HL: ";RIGHT$("000"+HEX$(HL),4)
430 PRINT "IX: ";RIGHT$("000"+HEX$(IX),4);" ";
440 PRINT "IY: ";RIGHT$("000"+HEX$(IY),4)
460 '
470 DATA &h2A,&h00,&hD0,&hE5
480 DATA &h2A,&h02,&hD0,&hE5
490 DATA &h2A,&h04,&hD0,&hE5
500 DATA &h2A,&h06,&hD0,&hE5
510 DATA &h2A,&h08,&hD0,&hE5
520 DATA &h2A,&h0A,&hD0,&hE5
530 DATA &hFD,&hE1,&hDD,&hE1,&hE1,&HD1,&HC1,&HF1
540 '
550 DATA &hCB,&h37,&h0B,&h0B,&h0B,&h0B,&h0B,&h0B
560 '
570 DATA &hFD,&hE5,&hDD,&hE5,&hE5,&HD5,&HC5,&HF5
580 DATA &hE1,&h22,&h00,&hD0
590 DATA &hE1,&h22,&h02,&hD0
600 DATA &hE1,&h22,&h04,&hD0
610 DATA &hE1,&h22,&h06,&hD0
620 DATA &hE1,&h22,&h08,&hD0
630 DATA &hE1,&h22,&h0A,&hD0
640 DATA &hC9,-1

To use it I modify lines 100-150 and 550 and then run it. To do this handily, I type “LIST -150”, then “LIST 550” and then RUN, and move my cursor up to modify the test values. To know the opcode length I look at the value of register C, since line 550 ends with DEC BC instructions.

Van ToriHino

Champion (307)

afbeelding van ToriHino

06-12-2018, 23:02

That's some interesting research you did. Too bad they stripped a lot from the Z800, but at least some more MULUB instructions are found.

In all ways, the turboR really feels like a rushed product which could have been so much more.

Van lintweaker

Master (148)

afbeelding van lintweaker

07-12-2018, 08:55

This code can be used to 'detect' running on a Z280, does it work for R800?

    ld a,40h
    defb 0cbh, 037h
    jp m,cpu_z80
    ; or
    ; jp p,z280

Did you test Z280 LDCTL opcodes on R800? I use this for Z280 :

LDCTL   MACRO   @opcode
›       defw›   @opcode

OUTW›   MACRO›  @opcode
›       defw›   @opcode
›       ENDM

›       defw›   093edh› ›       ›       ; OTIRW
›       ENDM
›       defw›   65edh›  ›       ›       ; PCACHE
›       ENDM

L_CHL›  equ›    06eedh› ›       ›       ; LDCTL (C),HL
O_CHL›  equ›    0bfedh› ›       ›       ; OUTW (C),HL

L_HLC   equ 066edh          ; LDCTL HL,(C)

Van Grauw

Enlighted (8015)

afbeelding van Grauw

07-12-2018, 09:59

lintweaker wrote:

This code can be used to 'detect' running on a Z280, does it work for R800?

    ld a,40h
    defb 0cbh, 037h
    jp m,cpu_z80
    ; or
    ; jp p,z280

Indeed I saw that in the Z280 manual. No, it won’t work, because it tests the TSET instruction behaviour and that acts as "SLL" (SLA) on R800. With this particular test, it will detect the R800 as Z80, because it shifts the high bit into bit 7 and thus flags the sign negative.

(Of course if the goal is to know if the R800 is active, on MSX you can simply call the BIOS, which inspects the F4H port. Possibly S1990 register 6 is also readable. If detecting by instruction, I would test MULUB.)

lintweaker wrote:

Did you test Z280 LDCTL opcodes on R800? I use this for Z280

I saw no observable effect from those opcodes (like most others). However it does not necessarily mean that there is no effect, e.g. LDCTL (C),HL could still be working. But at least its counterpart LDCTL HL,(C) did not modify HL.

Van erpirao

Paladin (875)

afbeelding van erpirao

07-12-2018, 14:40

The truth is that it is a thread that I have always wanted to open, the z280-R800 comparison.
Graw, the research you are developing is very interesting, this confirms my theory that the R800 is a much better processor than we thought in the 90s.
what level of similarity are these cpu, s? 80% .. 90% ?.
the truth is that the z280 are cpu, s VERY cheap (2.35 € by aliexpress the version of 10MHZ).
Sorry for my English "made by google"

Van Edevaldo

Expert (115)

afbeelding van Edevaldo

13-12-2018, 20:29

Great research!

Grauw, I imagine that some of the instructions that do nothing or always move the same value to A or HL could actually be accessing a status register or causing an interrupt trap to be taken. There may be equivalents that write to those control registers that govern the MMU, interrupt controller, DMA, etc.

I would suggest counting how long the instruction take to execute also, buy using the turbo-r timer. The resolution may not be enough in some cases. But if a trap is taken, for example, or a multiplication is performed there would be a noticeable increase in execution time.

Looking the contents of the top of the stack could be interesting as well.

Van Grauw

Enlighted (8015)

afbeelding van Grauw

13-12-2018, 21:26

Indeed such instructions would be great to find.

Checking the timing indeed sounds like a good idea to try next. I already have a working tool which can determine cycle-exact R800 timing using a visual reference.

Also the stack inspection is a good suggestion.

Van Grauw

Enlighted (8015)

afbeelding van Grauw

13-12-2018, 23:02

By the way, I think the R800 timing on described on the MAP is useful and accurate, but it’s what’s observed on the outside. Internally the R800 must be pipelined, because there is no way that it can fetch, decode and execute instructions in one cycle. It probably has a three-stage pipeline. It may also run at a higher clock rate internally.

Investigating this further could also explain the strange timing of call + ret versus call + nop + ret, in the former case the call takes 7 cycles whereas in the latter case it takes 8. That must be a side-effect of pipelining I think, somehow.

And has anyone ever tried to observe the effect of pipelining in the R800? E.g. write the byte following the current instruction, or bank switch the memory of the page that’s currently executing code?

It may be an interesting exercise to take the Z280 timing information, determine what configuration it would be set to mimick the R800, and see if it matches up. A quick shot at what this could be like; we know the R800 gets a 28.6 MHz clock. The Z280 operates at half the input clock rate (14.3 MHz), so maybe the R800 does as well. Then it has two external memory and one I/O access dividers, let’s say the internal memory divider is set to 2 (7.16 MHz), and the external memory and I/O divider is set to 4 (3.58 MHz). Then match this up with the Z280 instruction timing information (quite complex) and see if you would in practice end up at the timings described on the MAP.

Van lintweaker

Master (148)

afbeelding van lintweaker

14-12-2018, 08:49

Forgot to ask/mention, just popped up again. If I recall correctly those LDCTL instructions are privileged instructions. Could the R800 be running in non-privileged mode? (if they implemented that stuff at all, still likely the only took what the needed from the Z800/Z280).

Pagina 1/7
| 2 | 3 | 4 | 5 | 6