- name
- cpu-vm1
- description
- KR580VM1 (КР580ВМ1) for z88dk: 8080-compatible plus RS/MB prefixes, H1L1, dual 64K banks, OF/MF flags, and ten extra ops. Use when writing or reviewing vm1 library asm, +test -clib=vm1 / +z80 -clib=vm1, -mvm1, pop-af bank traps, DSUB/LHLX/SHLX/DCMP, or /cpu-vm1. Not 8085 — LDSI/RDEL/ARHL encodings differ.
# CPU — KR580VM1
Soviet 8080 extension of the KR580VM80A. **Binary compatible with the 8080.** Every 8080 program runs unchanged. It is **not** an 8085: the two chips reuse the same undocumented opcode slots for different instructions.
z88dk writes **Zilog** mnemonics. The Russian document uses Intel-style names (DSUB, LHLX, ANX). Translate on the way in.
Classic path `libsrc/l/sccz80/7-vm1/`, products `vm1_crt0.lib` / `testvm1_clib.lib` / `vm1_clib.lib`. Assemble `-mvm1`. `zcc +test -clib=vm1` or `+z80 -clib=vm1`. From **v2.5**, **`-lm`** links `math32_vm1.lib` (`asm/vm1/`), the same selection as **`--math32`**. Before v2.5 the documented default was **`--math-mbf32`** (`mbf32_vm1.lib`); that alias is still the Microsoft 32-bit opt-in. No math16 vm1 product — those 8085 cores use `ld de,sp+*` and `rl de`, which are **prefixes** on this CPU.
**This skill is complete for vm1 work.** Load `cpu-8080` only when the binary must also run on a plain 8080. Do not load `cpu-8085` for this CPU.
## Conventions (always follow)
1. **Never `pop af` unless the word is a previous `push af`.** Unknown bits in F bit 3 load MF and **swap the data RAM bank**. Matched `push af` … `pop af` is safe. Detail below.
2. **Mnemonics are Zilog** (`ld a,b`, `sub hl,bc`, `andl (hl)`). Do not emit Intel `MOV` / `DSUB` / `LHLX` in tree sources (fixtures may list Intel for compat).
3. **Opcode bytes and timings are 8080 + VM1 extras**, not Z80 or 8085.
4. Immediate forms: `*` = d8, `**` = d16/a16. Placeholders only.
5. Conditional cycle counts: the Russian tables list **taken only**. z88dk uses 8080 not-taken (`jp` always 10; `call` 17/11; `ret` 11/5).
6. Each prefix byte adds **4T**. Base timings match the 8080.
7. One major function per file under `libsrc`.
Human page of record: `doc/kr580vm1.md`. When that page and an external Russian source disagree, **the local page wins** (it already folds the errata). Assembler last resort: `cpu_test_vm1_ok.asm`.
## Sources
| Topic | Source |
|-------|--------|
| ISA, prefixes, flags, timings, errata | **`doc/kr580vm1.md`** (primary) |
| Secondary Russian | [emuverse КР580ВМ1/Система_команд](https://emuverse.ru/wiki/КР580ВМ1/Система_команд) |
| Raw radio86 wiki (JS shell — fetch storage) | `https://storage.googleapis.com/google-code-archive/v2/code.google.com/radio86/wiki/KP580BM1.wiki` |
| z80asm source forms | `src/z80asm/dev/cpu/cpu_test_vm1_ok.asm` / `_strict_ok.asm` |
| sccz80 overrides | `libsrc/l/sccz80/7-vm1/` |
| Emulator | `src/ticks/vm1_inst.c`, `-mvm1` in `ticks.c` |
8080-portable sequences, stack frames, and `jr`→`jp` synthetics: **`cpu-8080`**. This skill is the extras, the prefixes, MF, and the traps.
### Errata in the original Russian document
Resolved in `doc/kr580vm1.md` against emuverse and 8085 precedent. **Use the local encodings.**
| Claim in the radio86 / emuverse tables | Truth |
|----------------------------------------|--------|
| SHLX listed as `11001101` (`$CD`, 8080 `CALL`) | **`$D9`**, same as 8085 SHLX |
| SMF0/SMF1 tables show prefix `$38` (RS) | Prefix is **MB `$28`**: `smf0` = `$28 $00`, `smf1` = `$28 $7F` |
| JOF / cond. `jp` / cond. `call` “ELSE PC ← PC+1” | Address field is two bytes; skip both (z88dk `pc += 2` after the opcode) |
| ACI listed as `$EE` (that is `xri *`) | 8080 **`$CE`** |
| RAR listed as `$17` (that is `rla`) | 8080 **`$1F`** |
| CMA algorithm `A ← A` | Invert: `cpl` |
LHLX at `$ED` is listed correctly in the original.
## Registers
8080 set plus one extra pointer pair. No IX/IY, no `exx`, no AF′/BC′/DE′.
```
15 ...... 8 7 ...... 0
A F
B C
D E
H L → HL (pointer pair)
H' L' → HL' (H1L1; z88dk `h'` `l'` `hl'`)
15 ............... 0
SP
PC
```
H1L1 is **not** a Z80 shadow bank. There is no exchange. The **RS** prefix selects H1L1 as source, destination, or `(hl)` pointer for **one** following instruction. `ex de,hl` always uses real HL (document gives no RS form).
z88dk ticks parks HL′ in the unused Z80 `h_`/`l_` for the duration of one prefixed instruction.
## Flag register (F)
8080 layout plus OF and MF in bits the 8080 left constant / copies of A:
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|-----|---|---|---|---|---|---|---|---|
| | S | Z | **OF** | AC | **MF** | P | 1 | CY |
| Flag | Meaning |
|------|---------|
| **S** | Sign |
| **Z** | Zero |
| **OF** | Signed overflow (carry into bit 7 xor carry out of bit 7). **Not** 8085 K |
| **AC** | Half-carry |
| **MF** | Data bank: 0 = main, 1 = additional |
| **P** | Parity (even). **Always parity**, not Z80 P/V |
| **1** | Unused; permanently 1 |
| **CY** | Carry |
OF occupies the same bit as 8085 **K**. They are not the same flag. Do not use `jp k` / `jp nk` — those encodings are DCMP D / JOF on this CPU.
Condition codes: `nz` `z` `nc` `c` `po` `pe` `p` `m`, plus **`of`** (`jp of,**`).
### Flag rules agents must not get wrong
Base 8080 ops keep 8080 SZAPC behaviour, plus OF as below. Bit 1 stays 1. MF only changes on `smf0` / `smf1` / `pop af`.
| Group | S Z OF AC P CY | Notes |
|-------|----------------|-------|
| 8-bit `add`/`adc`/`sub`/`sbc`/`inc`/`dec`/`cp` (and immediates) | ● ● ● ● ● ● (`inc`/`dec`: CY unchanged) | OF is 8-bit signed overflow |
| 8-bit `and`/`xor`/`or` (and immediates) | ● ● — 0 ● 0 | **Leave OF.** CY and AC cleared |
| `rlca`/`rrca`/`rla`/`rra` | — — ? — — ● | **C only** on 8080. Russian tables mark OF written; do not `jp of` after a rotate |
| `add hl,rp` (DAD) | — — — — — ● | **CY only.** CS form (`adc hl,rp`) same mask |
| `sub hl,bc/de` (DSUB) | ● ● — — — ● | **No OF.** S from bit 15. CS form consumes CY |
| `cp hl,bc/de` (DCMP) | ● ● — — — ● | Result discarded. **No OF.** CS form consumes CY |
| `andl`/`orl`/`xorl` (ANX/ORX/XRX) | ● ● — — ● 0 | CY cleared. OF and AC unchanged |
| `inc`/`dec` **16-bit** | — — ? — — — | Russian tables mark OF. **Z is not written.** Never `dec bc; jp nz`. Never `inc bc; jp of` as a count test |
| `daa` | 8080 SZAPC | Russian table (Z+CY only) is incomplete |
| `cpl` | — — — — — — | No flags |
| `scf`/`ccf` | — — — — — ● | |
| `smf0`/`smf1` | — — — — — — | **MF only** |
| `pop af` | all | Restores S Z OF AC **MF** P CY |
DSUB/DCMP **do not** set OF. Signed 16-bit order cannot use `jp of` the way 8085 uses `jp k` after `sub hl,bc`.
## Memory and MF
Two 64K banks. **Instructions fetch only from the main bank.** Data uses the bank named by MF. The MB prefix flips the bank for the **next** instruction only. `smf0` / `smf1` set MF until changed.
The document does not except the stack. z88dk therefore lets `push` / `pop` / `call` / `ret` follow MF. That is an inference. Changing MF between a `call` and its `ret` will not work.
## Hard rule: do not `pop af` of unknown contents
`pop af` loads **MF from bit 3** of the flags byte. MF selects the data RAM bank for **every** later data access (loads, stores, pushes, pops, `call`, `ret`). If that bit is not the MF you pushed, the CPU **switches bank**.
| Allowed | Forbidden (unknown F, so unknown MF) |
|---------|--------------------------------------|
| `push af` … later `pop af` of **that same word** (MF round-trips) | `pop af` to **discard** a stack slot |
| | `push bc` / `push de` / `push hl` then `pop af` as a 16-bit **move** |
| | `pop af` of a **return address** or any other non-PSW word |
The test is: **do you know this word is a PSW you pushed?** If not, do not pop it into AF. Bit 3 of arbitrary data is effectively random, so the bank swap is data-dependent and easy to miss in testing.
`sccz80` and `80cc` already suppress the unsafe idiom (`CPU_POP_AF_IS_SAFE()`). Hand-written asm must not invent it. Discard or move through BC/DE/HL.
A `pop af` of a **return address** is also broken on 8080/8085/gbz80 (hardwired flag bits). On VM1 it is worse: it can switch banks as well. Keep return addresses in BC/DE/HL.
## Prefixes
Up to two prefix bytes before any opcode. Opcode byte must not be `$28` or `$38`. A run of prefixes does not cancel. Interrupts are held off between a prefix and its instruction (same idea as Rabbit `altd`).
### RS — `$38`, 4T
Substitute HL′ for HL in the next instruction.
```asm
ld a,(hl') ; 38 7E
ld a,h'
ld hl',nn
push hl'
add hl',bc
```
### MB / CS — `$28`, 4T
**MB** in front of a memory op: access the **other** data bank.
```asm
mb ld a,(hl) ; 28 7E A ← other bank at HL
```
**CS** in front of `add hl,rp` / `sub hl,rp` / `cp hl,rp`: consume carry. z88dk folds CS into the mnemonic (no separate `cs` word in the source):
| Bare | Prefixed | Meaning |
|------|----------|---------|
| `add hl,bc` (`09`) | `adc hl,bc` (`28 09`) | HL ← HL + BC + CY |
| `sub hl,bc` (`08`) | `sbc hl,bc` (`28 08`) | HL ← HL − BC − CY |
| `cp hl,bc` (`CB`) | `cpc hl,bc` (`28 CB`) | flags from HL − BC − CY |
`cpc` is a **z88dk name**; the document has no Zilog spelling for compare-with-borrow.
Both prefixes: the human page shows **MB then RS** (`28 38 08` = `sbc hl',bc`). z80asm currently emits **RS then MB** for the CS+HL′ forms (`38 28 08`). Both orders are equivalent: one prefix selects the pair, the other consumes carry.
## Added instructions
Timings are **bare**; add 4T per prefix.
| Op | Zilog (write this) | Doc | T | Effect |
|----|--------------------|-----|--:|--------|
| `08` | `sub hl,bc` | DSUB B | 10 | HL ← HL − BC |
| `18` | `sub hl,de` | DSUB D | 10 | HL ← HL − DE |
| `10` | `andl (hl)` | ANX | 10 | (HL) ← (HL) & A |
| `20` | `orl (hl)` | ORX | 10 | (HL) ← (HL) \| A |
| `30` | `xorl (hl)` | XRX | 10 | (HL) ← (HL) ^ A |
| `CB` | `cp hl,bc` | DCMP B | 10 | flags from HL − BC |
| `DD` | `cp hl,de` | DCMP D | 10 | flags from HL − DE |
View on GitHub