• Please review our updated Terms and Rules here

I found the SAVEALL opcode

dreNorteR

Experienced Member
Joined
Dec 19, 2016
Messages
203
Everyone who is interested in this sort of thing already knows about the LOADALL instruction. But what Intel didn't include in this no-longer-secret document is that there is an instruction that does the reverse, and it can be used on a regular 286 chip without extra pins.

There is one hint in Intel's attempts at misdirection:

The opcode 0D6H is a proprietary single byte instruction. No restrictions apply to its execution. It can be emulated as a NOP.

The 0F1H opcode is a prefix which performs no function. It counts like any other prefix towards the maximum instruction length. No restrictions apply to its execution.

D6 does do something of course, and I suspected F1 might as well. Note it says that it is a prefix!

Executing 0F 04 will lock up the CPU without doing anything. With the F1 prefix, it appears at first to do the same, but the internal state is actually written to memory at 000800H! All we need is a short timeout before resetting the CPU, and the keyboard controller is slow enough to work for that.

This might be helpful to investigate some internal details, the 10 extra words ignored by LOADALL are written too. Maybe also for a debugger (if only there was a way to interrupt without changing any descriptor caches!)
 

Attachments

Is this 80286 only or does it also apply to later CPUs? Is it uniformly implemented in the second source 80286 and 80C286 CPUs?

I assume it works on every 286, but not on any later generation, since they don't have the 286 version of LOADALL. Maybe there is something similar on 386s, I don't have any to test this with.
 
N80C286-12
ET 037E6KX
(M) AMD
(C) INTEL 1982

Results:
Code:
(clean boot, no HIMEM.SYS)
???? ???? ????  MSW stk? ???? ???? ???? ???? ???? ????
0018 9310 00AD FFF0 03AE 0000 00E6 0000 0000 03B0 0002

(with HIMEM.SYS)
0000 9315 00AD ...

My other 286:
Harris
CS80C286-16
F3360 (delta) 8929
(C) INTEL '82

Hangs, which shouldn't happen - likely a problem with the mainboard, BIOS or my reset code. I'll try the chip in the first machine later.
 
Tested the Harris chip and another AMD in the same machine, they all work. There are some differences in both unitialized descriptors and the unknown registers, as seen after a clean boot:

Code:
N80C286-12
ET 037E6KX
(M) AMD
(C) INTEL 1982

???? ???? ????  MSW stk? ???? ???? ???? ???? ???? ????
0018 9310 0164 FFF0 03BE 0000 00E6 0000 0000 03C0 0060 

TASK FLAG   IP  LDT   DS   SS   CS   ES
0000 0046 016B 0000 0F9A 0F9A 0F7E 0F9A 

  DI   SI   BP   SP   BX   DX   CX   AX
04C0 0900 091C 03C0 0000 0171 0F7E 00FE 

ESbase ar  lim CSbase ar  lim SSbase ar  lim DSbase ar  lim
00F9A0 82 FFFF 00F7E0 82 FFFF 00F9A0 82 FFFF 00F9A0 82 FFFF 

GDTbas ar  lim LDTbas ar  lim IDTbas ar  lim TSSbas ar  lim
08F866 FF FFFF 000000 7F 0000 000000 FF FFFF 000000 FF 0000 
------

HARRIS
CS80C286-16
F3360 Δ 8929
(C) INTEL '82

???? ???? ????  MSW stk? ???? ???? ???? ???? ???? ????
0018 9310 0164 FFF0 03BE 5EEC 00E6 CD8D 8BFC 03C0 0060

TASK FLAG   IP  LDT   DS   SS   CS   ES
0000 0046 016B 0000 0F9A 0F9A 0F7E 0F9A

  DI   SI   BP   SP   BX   DX   CX   AX
04C0 0900 091C 03C0 0000 0171 0F7E 00FE

ESbase ar  lim CSbase ar  lim SSbase ar  lim DSbase ar  lim
00F9A0 82 FFFF 00F7E0 82 FFFF 00F9A0 82 FFFF 00F9A0 82 FFFF

GDTbas ar  lim LDTbas ar  lim IDTbas ar  lim TSSbas ar  lim
08F866 FF FFFF FFFFFF FF FFFF 000000 FF FFFF FFFFFF FF FFFF

------

AMD
N80L286-10/S
003D42S
(M) (C) INTEL 1982

???? ???? ????  MSW stk? ???? ???? ???? ???? ???? ????
0018 9310 0164 FFF0 03BE FFFF 00E6 FFFF FFFF 03C0 0060

TASK FLAG   IP  LDT   DS   SS   CS   ES
0000 0046 016B 0000 0F9A 0F9A 0F7E 0F9A

  DI   SI   BP   SP   BX   DX   CX   AX
04C0 0900 091C 03C0 0000 0171 0F7E 00FE

ESbase ar  lim CSbase ar  lim SSbase ar  lim DSbase ar  lim
00F9A0 82 FFFF 00F7E0 82 FFFF 00F9A0 82 FFFF 00F9A0 82 FFFF

GDTbas ar  lim LDTbas ar  lim IDTbas ar  lim TSSbas ar  lim
08F866 FF FFFF FFFFFF FF FFFF 000000 FF FFFF FFFFFF FF FFFF

I also did a quick check of which registers can be changed using LOADALL (on the 80C286-12):

0800 writable
0802 only some bits, CPL/access rights?
0804 no change
0808 no change, copy of SP before last push/pop?
080A writable
080C writable
080E writable
0810 writable
0812 no change
0814 no change
 
dreNorteR,

This is very interesting. A couple of questions:
What happens if you run the following in DEBUG under DOS 3.3 or 4.0/4.01 (those versions of DOS reserve 102 bytes at 0:800h for LOADALL use by HIMEM.SYS):

e 100 f1 0f 04 cc 90
g
d 0:800

Theoretically since DOS 3.3 and 4.0/4.01 reserve 102 bytes in the IBMBIO/IO.SYS data area this should not hang DOS since no system data will be overwritten.

Also what happens if you use F0 instead of F1 as the first byte ? If F0 works the same as F1 then this would show that F1 is an alias for F0 (LOCK) on the 286.
 
I made a program that lets you interactively change the registers. A "Load+Save" function sets everything up to immediately start the reset and save operation:

Code:
;come here by LOADALL, DX=0064, AL=FE
out dx,al
db 0f1h,0fh,04h
;can resume here with another LOADALL
jmp hello_world

All registers except for X1 and maybe X8 are writable, some just don't keep their value for much more than these minimal instructions. Changing them doesn't have any observable effect so far. Would be kind of disappointing if there isn't some secret debug mode to be enabled...

800 'X0' ???
802 'X1' ??? (read-only? high byte always 93 or 83)
804 'X2' IP after last CALL or near RET
808 'X3' Value of SP before last stack access
80A 'X4' ???
80C 'X5' ???
80E 'X6' ???
810 'X7' ???
812 'X8' (loses written value) SP after last RET
814 'X9' IP after last RETF or IRET

X4 (but not X5), X6 and X7 persist after Ctrl-Alt-Del on my machine.
X5 (but not X4), X6 and X7 after CPU reset.
X8 seems to have another temporary use that makes it change immediately. Maybe I/O instructions?

Can anyone try if this works at all on their machine?

edit: very quick & dirty user interface, doesn't support mono video card
 

Attachments

Made a few improvements, like monochrome support and more reliable reset code. I didn't enable A20 before, since it worked for me without that, but many machines apparently require this. Also disabling the keyboard so it can't interfere during reset, which seems to have fixed some random hangs for me.

Still only works on one of my two machines, so I really want someone else to try it!
(Reset and LOADALL work on both, only SAVEALL hangs)

F1: Set up defaults for LOADALL (also done at start)
F2: Test CPU reset
F3: Test SAVEALL on its own
F4: Test LOADALL on its own
F5: LOADALL -> reset+SAVEALL immediately afterwards
F6: Same, but starts reset even before LOADALL

Starting the reset command before LOADALL seems to be just as reliable. And X8 is used by the LOADALL microcode itself, which is why it is set to 0864 - the address of the last word loaded.

-----

So what is the F1 prefix really? I don't think Intel added this just to be extra sneaky, there must be a technical reason. Some research / speculation:

The original purpose of LOADALL was to return from ICE mode (similar to SMM on newer CPUs), loading all the internal registers is just a necessary part of that. When LOADALL is executed in ICE mode, the state would be loaded from its own special address space that uses extra pins, which are likely not connected on a normal 80286 chip.

0F 04 must then be the opcode to enter ICE mode, and the CPU switches immediately to this separate address space, waiting forever for a ready signal on the non-connected pins. But there must be a way for ICE mode to access the memory of the program being debugged. The 80386 has the UMOV instruction for this.

So it is likely that this is the function performed by the F1 prefix on the 286 (which is different from the 386's ICEBP opcode): it modifies an instruction to access memory using the normal bus pins. Outside of ICE mode, it has no effect, as Intel documented. The one exception is 0F 04, because when executing the state save it has already switched modes.

Unfortunately, the mode switch seems to clear the prefetch queue, so we can't get it to execute an instruction in ICE mode (like F1 0F 05 to resume normal execution, although there is another reason this couldn't work - can you see why?).

I also couldn't get a triple fault by setting the IDT limit to zero and enabling the trap flag just before switching. Either these registers are reset, or the CPU gets stuck before a trap can occur.
 

Attachments

Last edited:
Well, you can see why the F1 instruction went undocumented--on the 80386 and higher, it serves a different function--so called "ICEBP" INT 01 instruction. I wonder if other F1 xx opcodes on the 80286 tie in with the bond-out 80286 used in ICE.

The instruction clutter got so bad with later CPUs that Intel eventually had to have two "official" undocumented opcodes for the software emulators (UD1 and UD2).
 
Well, you can see why the F1 instruction went undocumented--on the 80386 and higher, it serves a different function--so called "ICEBP" INT 01 instruction. I wonder if other F1 xx opcodes on the 80286 tie in with the bond-out 80286 used in ICE.

Yes, F1h on the 286 is a prefix, and different from ICEBP which is a single byte opcode for 386+

Its likely function is to access user memory from ICE mode, which explains how it interacts with 0F 04. But of course we can't know for sure, unless someone who actually worked at Intel on the 286 design confirms this.
 
Yes, F1h on the 286 is a prefix, and different from ICEBP which is a single byte opcode for 386+

Its likely function is to access user memory from ICE mode, which explains how it interacts with 0F 04. But of course we can't know for sure, unless someone who actually worked at Intel on the 286 design confirms this.

Try replacing F1h with F0h and executing F0h 0Fh 04h and see what happens (see my earlier post in this thread).
 
Try replacing F1h with F0h and executing F0h 0Fh 04h and see what happens (see my earlier post in this thread).

Sorry, didn't see your post!

F1 is not an alias for F0, nothing will get written to memory if you use the F0 prefix. Also, whether you use the correct prefix or not, the CPU will hang and has to be reset to regain control. Using the keyboard controller (output FEh to port 64h) works because there is a delay long enough that the instruction can execute first.

My test program saves the data at 0800, so it will run under DOS 6.22.
 
Having tested some more, it seems that all the extra registers are simply temporary values used by certain instructions. X1 must be involved in privilege checking and FPU emulation; for some reason, LOADALL will leave a copy of the word containing access rights of the ES (not CS or SS) descriptor there. Floating point opcodes load it with MSW.

X0 and X4-X7 are all used in protected mode, as can be seen after starting and exiting Windows 3.0.

So, no hidden features to unlock, and apart from the descriptor caches it seems rather useless for debugging too. I wonder if Intel actually had a need to save this particular information, or if it is just a side effect of how the state saving works?

I think there is not much more to discover about this, except for the actual code used by Intel in their I²ICE system. From reading the documentation archived on bitsavers, the code for the probe module is not in ROM but loaded by the host software, from a file called I2ICE.286. A disassembly of that could confirm the actual intended use of the F1 prefix.

edit:
Apparently Intel referred to this instruction as STOREALL - https://www.pcjs.org/pubs/pc/reference/intel/80386/loadall/
Thanks for the link (now I can see your post for some reason, must be the "new user" filter), wouldn't have expected this page to have information on the 286. This seems to confirm what I found out with my experiments:

The 286 also loads a number of temp registers, but the values in these registers are “dead” when the next instruction (other than STOREALL) begins execution. Consequently, the values loaded into the temp registers can have no effecton 286 program execution, so these can be ignored. As long as the 386 temps are also “dead” when the next instruction begins (except for STOREALL), there will be no problems with 386-specific temp registers.
 
Last edited:
Apologies for the necropost, but this is directly related to the topic at hand.

I've got a Harris 80C286 under Arduino control, and so I am able to execute STOREALL directly from reset.

The bus trace is here:

What's interesting is it does one final read - I wonder if providing the appropriate value there might be able to avoid the shutdown?

Thanks for your excellent research on LOADALL and STOREALL. The discovery of STOREALL will enable generation of 286 CPU tests for emulators, which will be a great resource to have.
 
Apologies for the necropost, but this is directly related to the topic at hand.

I've got a Harris 80C286 under Arduino control, and so I am able to execute STOREALL directly from reset.

The bus trace is here:

Very interesting to me, since I only played around with what could be done using software running on a normal AT compatible! Was thinking for a time about building an interface like yours, but it's not really something I have experience with. Seems like there is something going on internally while it's storing the descriptor caches, since there are 4 idle clocks at the start, then 2 or 3 between each write.

From the values written, I assume you did this after previously using the LOADALL instruction, with most of the registers being initialized to zero. Reset doesn't affect what is in the general purpose registers, the TR and LDTR caches, or all but one of the temporary ones (X2 is always set to 002Ah, decimal 42, the ultimate question is why :))

Just for curiosity's sake, can you also dump the state immediately after powering up the chip? From my limited sample, I observed that Intel/AMD chips initially have either all 0000 or FFFFh, but Harris have different values which might - speculating here - be the effect of different microcode, random manufacturing differences, or some kind of ID?

What's interesting is it does one final read - I wonder if providing the appropriate value there might be able to avoid the shutdown?

Thanks for your excellent research on LOADALL and STOREALL. The discovery of STOREALL will enable generation of 286 CPU tests for emulators, which will be a great resource to have.

There's only 16 bits, so if the value read back has any effect it should be easy to brute-force. But strange that it would be reading from 864H, which is the TSS limit it has just written and should read back like any normal RAM. It would also be interesting to try putting code on the bus to run in ICE mode, you can't get the S0/S1 state, but should be able to see the CPU driving address FFFFF0H on the bus some time after 'halting'. At this point it should be effectively in the same state as after a normal reset (except for the different bus control lines), and executing F1 0F 05 should be able to restore it.

Glad this might be of use to someone else! I also noticed that you have a file apparently containing the 386 microcode ROM from a few months ago. What is the source for that, and have you done anything with it?
 
Last edited:
Just saw this thread. I used the LOADALL and SAVEALL instructions and modified IBM Xenix (I had source code) so that I could run DOS programs as well as native Xenix programs. At one point I had 4 simultaneous DOS sessions running as well as a couple of Xenix programs running. It wasn’t fast but it worked!
 
Just saw this thread. I used the LOADALL and SAVEALL instructions and modified IBM Xenix (I had source code) so that I could run DOS programs as well as native Xenix programs. At one point I had 4 simultaneous DOS sessions running as well as a couple of Xenix programs running. It wasn’t fast but it worked!

That should be possible with just LOADALL, Digital Research did this as well but apparently their method didn't work on all steppings - could you explain more about what you used STOREALL for?

So far, the one intended use for this instruction I've seen was to generate an ICEBP via software while already in ICE mode, with the sole purpose of clearing the 'protection enable' bit, so the monitor can then return to user code running in real mode. I've written about it here: https://rep-lodsb.mataroa.blog/blog/intel-286-secrets-ice-mode-and-f1-0f-04/
 
That should be possible with just LOADALL, Digital Research did this as well but apparently their method didn't work on all steppings - could you explain more about what you used STOREALL for?

So far, the one intended use for this instruction I've seen was to generate an ICEBP via software while already in ICE mode, with the sole purpose of clearing the 'protection enable' bit, so the monitor can then return to user code running in real mode. I've written about it here: https://rep-lodsb.mataroa.blog/blog/intel-286-secrets-ice-mode-and-f1-0f-04/
It’s been quite a while since I did this. I was part of the team that was doing investigations into how OS/2 would do DOS support. A few of us disagreed with the management dictated direction of mode switching (e.g. faulting to get back to real mode). Since I was already playing around with the Xenix kernel, I decided to make use of the undocumented instructions to see if a reasonable implementation could be done with them.
 
Back
Top