CPU — 8085

SkillDev tools

Intel 8085 assembly for z88dk: Zilog mnemonics, full opcode map (flags K/V, timings), and extended-instruction usage. Strong rule: stack-only locals and intermediates; static/BSS only for cross-call state. Prefer when writing or reviewing 8085 library asm, mapping Intel↔Zilog, choosing stack frames, K-flag loops, restoring float/integer divide, legal (de) stores, jr-as-jp synthetics, or /cpu-8085.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the CPU — 8085 skill

What this skill tells your AI

The instructions your AI receives, as published by z88dk/z88dk in .agents/skills/cpu-8085/SKILL.md and read by ahel’s review.

Compatible extension of the Intel 8080 (April 1974). Same documented 8080 ops; ten extra ops in 8080 unused cells. This skill is complete for 8085 work. Load cpu-8080 only when the binary must also run on 8080.

z88dk uses Zilog mnemonics for 8085 sources. Extended opcodes are first-class on every 8085. Design notes: https://feilipu.me/2021/09/27/8085-software/

Conventions (always follow)

  1. Mnemonics are Zilog, as in Z80 assembly — not Intel 8080/8085 names.
  2. Opcode bytes and timings are 8085, not 8080 and not Z80.
  3. Undocumented / extended opcodes are noted in tables (column or section).
  4. Immediate forms: * = 8-bit immediate (d8), ** = 16-bit immediate/address (d16/a16). These */** are operand placeholders only.
  5. For LDHI / LDSI equivalents (ld de,hl+* / ld de,sp+*), the 8-bit offset is unsigned.
  6. Conditional cycle counts use taken/not-taken (e.g. 12/6, 10/7, 18/9).

Prefer the full tables in references/opcodes.md. Use this skill body for rules, flags, mnemonic mapping, and undocumented ops.

Coding preferences for extended ops are in the Extended instruction usage section below.

Sources

TopicSource
Zilog mnemonics & descriptionsfeilipu/8085-opcodes 8085_instructions.html
Flag effects (S Z K A P V C), timingspastraiser i8085 opcodes

When sources conflict on flags, trust pastraiser (8085-specific K and V). When they conflict on mnemonic spelling, trust 8085_instructions.html (Zilog).

Registers

15 ...... 8  7 ...... 0
     A            F      → AF (PSW); Zilog: af
     B            C      → BC
     D            E      → DE
     H            L      → HL
15 ............... 0
        SP
        PC

Memory via HL is written (hl) (Intel M). Stack grows downward; push stores high byte first at sp-1, low at sp-2.

Flag register (F)

Bit76543210
SZKA0PVC
FlagMeaning
SSign
ZZero
KUndocumented; also X5 / UI (underflow/overflow indicator)
AAuxiliary carry (half-carry / AC)
0Unused; always zero
PParity
VUndocumented overflow
CCarry

Flag columns in tables are always S Z K A P V C:

  • letter → that flag is affected as defined by the instruction
  • - → unchanged
  • 0 / 1 → forced clear / set

Important: On the Z80, P and V share one bit (P/V). On the 8085 they are separate bits (P bit 2, V bit 1). Do not collapse them.

Condition codes for jumps/calls/returns:

Zilog ccMeaning
nz / zZ clear / set
nc / cC clear / set
po / peP odd / even (parity)
p / mS clear (plus) / set (minus)
nk / kK clear / set (undocumented)
v (rst v only)V set

Intel → Zilog mnemonic map (primary)

Use Zilog in all generated/edited z88dk code. Intel names appear in fixtures and external sources; when importing external code, translate to Zilog.

IntelZilog
NOPnop
LXI rp,d16ld bc/de/hl/sp,**
STAX B/Dld (bc),a / ld (de),a
LDAX B/Dld a,(bc) / ld a,(de)
INX/DCX rpinc/dec bc/de/hl/sp
INR/DCR rinc/dec r ; M(hl)
MVI r,d8ld r,*
MOV r1,r2ld r1,r2
RLC/RRCrlca / rrca
RAL/RARrla / rra
DAD rpadd hl,bc/de/hl/sp
LDA/STA a16ld a,(**) / ld (**),a
LHLD/SHLD a16ld hl,(**) / ld (**),hl
DAAdaa
CMAcpl
STC/CMCscf / ccf
HLThalt
ADD/ADC radd a,r / adc a,r
SUB/SBB rsub r / sbc a,r
ANA/XRA/ORA/CMP rand r / xor r / or r / cp r
ADI/ACI/SUI/SBIadd a,* / adc a,* / sub * / sbc a,*
ANI/XRI/ORI/CPIand * / xor * / or * / cp *
JMP/Jccjp ** / jp cc,**
CALL/Ccccall ** / call cc,**
RET/Rccret / ret cc
PCHLjp (hl)
SPHLld sp,hl
XCHGex de,hl
XTHLex (sp),hl
PUSH/POP B|D|H|PSWpush/pop bc|de|hl|af
IN/OUT d8in a,(*) / out (*),a
EI/DIei / di
RIM/SIMrim / sim (8085 only)
RST nrst 00hrst 38h

Undocumented / extended

OpIntelZilogBytesCyclesFlags (SZKAPVC)Effect
08DSUBsub hl,bc110SZKAPVCHL ← HL − BC
10ARHLsra hl17-----0CArithmetic right shift HL; V←0; Z unchanged
18RDELrl de110-----VCRotate DE left through C; Z unchanged
28LDHI d8ld de,hl+*210-------DE ← HL + unsigned *
38LDSI d8ld de,sp+*210-------DE ← SP + unsigned *
CBRSTVrst v112/6-------If V set: push PC, PC←40h
D9SHLXld (de),hl110-------(DE)←L, (DE+1)←H
DDJNK a16jp nk,**310/7-------Jump if K=0 (also jnx5/jnui)
EDLHLXld hl,(de)110-------L←(DE), H←(DE+1)
FDJK a16jp k,**310/7-------Jump if K=1 (also jx5/jui)

These are not standard Z80 opcodes at those encodings (Z80 uses CB/DD/ED/FD as prefixes). On 8085 they are single-byte (or 3-byte jump) instructions.

Flag rules agents must not get wrong

GroupFlagsNotes
inc/dec 16-bit (inc bcdec sp)--K----Only K changes
inc/dec 8-bit (incl. (hl))SZKAPV-All but C
rlca / rla-----VCV and C
rrca / rra-----0CV forced 0
add hl,rp-----VCV and C (not C alone)
8-bit ALU (addcp, immediates)SZKAPVCFull set
daaSZKAPVC
cpl-------No flags (Z80 cpl sets H,N)
sub hl,bc (undoc)SZKAPVCFull set
sra hl (undoc)-----0CV←0, C from bit 0, Z unchanged
rl de (undoc)-----VCV and C, Z unchanged
scf------1C←1
ccf------CC toggled
pop afSZKAPVCRestores all flags including K,V. Bit 3 stays 0

Logical ops still use the full pastraiser mask SZKAPVC (how individual bits are computed is instruction-defined; do not invent Z80 N-flag behavior — 8085 has no N flag).

Timing notes

Pastraiser T-states (8085 clocks). Not 8080 (many documented ops differ by 1T).

  • Conditional ret: 12 taken / 6 not taken.
  • Conditional jp: 10 / 7 (address bytes skipped when not taken).
  • Conditional call: 18 / 9.
  • call ** 18, ret 10, rst 12.
  • halt is 5.
  • push 12, pop 10.
  • 8-bit ALU register 4, (hl) 7, immediate 7.
  • ld r,r' 4, ld r,(hl) / ld (hl),r 7.
  • 8-bit inc/dec r 4; (hl) 10. 16-bit inc/dec rp 6.
  • add hl,rp 10, ex de,hl 4, ex (sp),hl 16.
  • ld hl,(**) / ld (**),hl 16; ld a,(**) / ld (**),a 13.
  • jp (hl) / ld sp,hl 6.

Coding rules for this project

  1. Emit Zilog mnemonics only (ld a,b not MOV A,B; jp nz,label not JNZ).
  2. Register pairs: bc, de, hl, af, sp — never Intel B, D, H, PSW in new code.
  3. Use (hl), (bc), (de), (**) for memory; never M.
  4. Prefer undocumented ops when they clearly win (e.g. sub hl,bc, ld hl,(de), ld (de),hl, ld de,hl+*) and the target assembler/CPU path supports them.
  5. Never assume Z80 instruction timings or prefix opcodes exist on 8085. jr / jr cc are allowed in normal mode (synthetics on): z80asm emits jp / jp cc (3 bytes; cond jp is 10/7). Same source then assembles for Z80, where jr is native. Strict / -no-synth rejects jr. Do not expect a 2-byte relative branch (18 is rl de).
  6. When optimizing, consult references/opcodes.md for exact size/cycle/flag data.
  7. Assembler support last resort: fixtures src/z80asm/dev/cpu/cpu_test_8085_{ok,err}.asm (and *_strict_*). ok = z80asm accepts that source form (native, synthetic, or call __z80asm__*). err = rejected. _strict_ = synthetics forbidden. Fixtures may include Intel spellings for external-compat testing; z88dk always writes Zilog. Full decode: tool-z80asm. rg only; do not bulk-read.

Quick lookup

Full 16×16 opcode grid, Intel cross-ref, and macro helpers:

references/opcodes.md


Extended instruction usage

When and how to use the ten 8085 extended instructions. Encodings, timings, and flags are in this skill + references/opcodes.md and references/opcodes.md.

Background: 8085 Software — Extended Instructions (feilipu, 2021). Present on every 8085 (also Tundra CA80C85B).

Always emit Zilog mnemonics (project convention).

Hard rule: stack variables, not static/BSS

Static memory (BSS / label: ds n / fixed absolute cells) must only hold state that must survive across function calls. Never use it for intermediate variable storage.

Allowed in static/BSSForbidden in static/BSS
Values that must outlive the current call (true globals, module state, buffers callers re-enter later)Locals, temps, intermediate results, scratch across a few instructions
MMIO, interrupt vectors, ROM constants“Scratch” cells to avoid a push or stack frame
Anything justified only by fewer cycles or easier coding
  • Function locals, temporaries, and intermediate results live only on the stack (arguments, return slots, pushes, explicit frames).
  • Access with ld de,sp+*, ld hl,(de), ld (de),hl, ld a,(de), push/pop, ex (sp),hl.
  • Prefer pointers passed on the stack over new static cells, even for long-lived data when the caller already owns the buffer.
  • “Slightly fewer cycles” or “easier to write” is not enough justification for static/BSS scratch.

Instruction preferences

ZilogPrefer forAvoid / watch
ld de,sp+*SP-relative address of a byte/word on the stack* is unsigned 8-bit
ld de,hl+*DE ← HL + unsigned offset (struct/buffer)Same unsigned rule
ld hl,(de) / ld (de),hl16-bit load/store through DENot Z80 prefix encodings
sub hl,bc16-bit subtract; == / !=; signed compares with KNo borrow-in; not multi-word subtract chains
sra hlSigned 16-bit arithmetic right shiftV←0; C ← old bit 0; Z unchanged — never sra hl; jp z
rl deRotate DE left through C; ×2 on DE; 32-bit with HLZ unchanged — never rl de; jp z. Pair with add hl,hl
jp k,** / jp nk,**After 16-bit dec; signed compare outcomesK after dec rp sets on −1, not on 0
rst vBranch to handler if V setVector 0040h must exist

Core formulations

1. Stack access (primary working storage)

    ld  de,sp+n        ; n = unsigned offset (0…255)
    ld  hl,(de)        ; word load
    ld  a,(de)         ; byte load
    ld  (de),hl        ; word store

Often best: leave the pointer in DE and use (de) / ld hl,(de) without swapping. Second 32-bit value: keep it on the stack, not a shadow register bank.

2. HL ← SP+n — prefer extended over ld hl,nn / add hl,sp

Classic (any 16-bit offset):

    ld  hl,nn          ; 10c, 3B
    add hl,sp          ; 10c, 1B  → total 20c / 4B; sets V,C; DE preserved

Unsigned 8-bit offset — use ld de,sp+n (10c, 2B, no flags) plus ex de,hl (4c, 1B, no flags):

GoalSequenceBytesCyclesFlagsDENotes
Pointer in DE, HL untouchedld de,sp+n210none= SP+nPrefer when HL must stay
HL = SP+n, DE freeld de,sp+n / ex de,hl314nonebecomes old HL6c faster, 1B smaller than classic
HL = SP+n, preserve DEex de,hl / ld de,sp+n / ex de,hl418nonerestored2c faster than classic; same size; no flag damage

DE-preserving form (only final HL is the new value; DE restored; flags untouched):

    ex  de,hl          ; 4c   DE↔HL
    ld  de,sp+n        ; 10c  DE = SP+n  (old HL in DE is overwritten — OK)
    ex  de,hl          ; 4c   HL = SP+n, DE = original DE

Trace: start DE=D₀, HL=H₀ → after 1st ex: DE=H₀, HL=D₀ → after ld de,sp+n: DE=SP+n, HL=D₀ → after 2nd ex: DE=D₀, HL=SP+n.

Temporary use then restore previous HL (DE ends as SP+n, not original DE):

    ld  de,sp+n
    ex  de,hl          ; HL = SP+n, DE = old HL
    ; ... use HL ...
    ex  de,hl          ; HL restored; DE = SP+n

Still prefer classic when offset is not an unsigned 0…255, or when you need the C/V from add hl,sp.

3. Stack frame

    ; HL = SP+n (pick a sequence from §2)
    ; adjust HL as needed, then:
    ld  sp,hl

Document every slot. Drop consumed args in one epilogue:

    pop bc             ; return address — never pop af for this
    ; pop/discard arg words as required (pop af is OK here to discard only)
    push bc            ; return

pop af and the return address: F bit 3 is hardwired 0 on the 8085 (K and V are restored), so a word popped into AF can never be a faithful 16-bit value ($FFFF$FFF7). Never pop af the return address (and never push af / ret a return path that depends on an intact address). Do use pop af to discard intermediate stack words on return when A/F need not be preserved — the corrupted F is irrelevant because the value is thrown away.

Multi-word frame rebuild (no exx)

Without alternate registers, a second long value lives on the stack, not in a shadow bank. When assembling a clean frame on top of junk:

  1. Push order vs layout. Stack grows down. For layout top→bottom W0, W1, W2 (W0 at lowest address / first pop), push W2, then W1, then W0. After pop bc; pop de; pop hl of pushed temps, restore with push bc; push de; push hl only if that matches the desired top word — verify with a depth diagram.
  2. Overlapping copy (memmove). Copying a block upward when dest = src + k and k < size overlaps. Copy high → low (last byte first). Forward copy corrupts the tail.
  3. Raise SP over junk. After a correct prefix of N good bytes sits above J junk bytes: copy the N-byte frame up by J (non-overlapping if J ≥ N, else high→low), then ld hl,J / add hl,sp / ld sp,hl.
  4. Product / result in BC·DE·HL while scrubbing. Hold the full result in registers; do not park the return address in AF. Typical pattern: write result over a callee-owned slot, drop temps with SP math, then:
    pop hl             ; ret
    pop bc             ; result.bc
    pop de             ; result.ml
    ex  (sp),hl        ; HL = result.mh; (sp) = ret
    ex  de,hl          ; DE = mh, HL = ml
    ret                ; BC DEHL = result; only ret on stack

4. 16-bit compare and subtract — sub hl,bc

    ld  bc,de          ; if second operand is in DE
    sub hl,bc          ; HL − BC
    jp  z,equal        ; or jp nz,not_equal

Signed order (illustrative — tune K/Z/C to the relation):

    ld  bc,de
    sub hl,bc
    jp  k,...          ; use K together with Z/S/C as required

For multi-word subtract with borrow, use sub / sbc through A, not sub hl,bc.

5. Counted loops — K and pre-decrement

16-bit dec bc / dec de / dec hl / dec sp update K (not Z as the loop signal).

  • K sets when the pair underflows to −1, not when it hits 0
  • Pre-decrement the counter; branch with jp k / jp nk
loop:
    ; body
    dec bc
    jp  nk,loop        ; adjust sense to match your initial count

Do not copy Z80 dec bc; jp nz semantics.

Alternative 16-bit counted loop structure.

Alternatively 16-bit loops can be created using the dec bc / inc b / inc c set up to create inner and outer loops, using any 16 bit register pair. Typically bc would be used, as hl and de have other priority uses.

    dec bc
    inc b
    inc c
loop:
    ; body, repeated BC times
    dec c
    jr  nz,loop        ; → jp nz on 8085; native jr on Z80
    dec b
    jr  nz,loop

jr nz is allowed in normal mode. Use it when the same source may also build for Z80. Strict mode: write jp nz. jp k / jp nk have no jr form (Z80 has no K).

6. Multiply / divide building blocks — rl de + sub hl,bc

Shift-add mul and restoring div center on:

  • rl de (and often add hl,hl) to shift
  • sub hl,bc / add hl,bc to trial-subtract and restore
  • ccf into quotient bits when dividing

Partial unroll when the body is small. Entry style: public DE/HL form → ld bc,de (or ld bc,hl) → HL/BC core so callers that already have BC can join mid-routine.

Float divide (math32 / math16) — restoring wins
LessonDetail
Prefer restoring fsdiv / f16_div over NR fsinv×mul for general /z80 + 8085 cores; large TIMER win on divide-hot code (whetstone ~1.4×)
fsinv stays NRUse for reciprocal-as-primitive; HW mul (z80n mul de / z180 mlt) helps inv, not the restoring loop
8085 coreStack-only second operand / temps; sub hl,bc + rl de; no EXX / IX / IY
Loop counterDo not reuse the count register as a working BC/DE mid-loop (classic hang: never reaches TIMER_STOP)
Rem / count clobberKeep remainder high and step count off the hot DEHL path (stack slots)
(de) stores — only legal forms

On 8085, ld (de),r / ld (de),n are illegal. Valid: ld a,(de), ld (de),a, ld hl,(de), ld (de),hl, post-inc synthetics (ld a,(de+)). Prefer ld (de),hl for word stores; never invent Z80-style (de),l.

Style (math32/math16 library asm)
  • Four-space indent; blank line only after unconditional jr/jp (not after every label).
  • Prefer synthetics: ld de,hl, ld bc,hl, …; if DE is dead after a transfer, ex de,hl over ld h,d / ld l,e.

7. Shifts

Signed 16-bit >>

    sra hl

Logical 16-bit >> (no srl hl):

    sra hl
    ld  a,$7f
    and h
    ld  h,a            ; force bit 15 clear

Logical multi-byte >> (24/32-bit etc.): chain rra through A across bytes — not Z80 srl.

32-bit << (value in DEHL):

    add hl,hl
    rl  de

32-bit rotate (sketch):

    rl  de
    ex  de,hl
    ; continue on the other half

rl de as ×2 on DE for table/struct scaling.

Bitfield open/close on DE (packed fields in D/E): open with rl de; repack with rra via A into D then E. Test a register for zero without destroying it: inc r / dec r / jp z.

8. Extra 16-bit slot — ex (sp),hl

Push a scratch word; ex (sp),hl swaps with it when AF/BC/DE/HL are full (16c). Do not use push af/pop af as a free 16-bit temp or to hold a return address: F bit 3 is hardwired 0 ($FFFF$FFF7 on the round trip). pop af is fine only when the popped word is discarded (e.g. clearing intermediates off the stack in an epilogue).

9. I/O and Z80-only ops to avoid

Prefer on 8085Avoid (Z80-only or wrong)
out (*),a / in a,(*) (byte in A)outi, in r,(c), block I/O
dec b / jr nz (→ jp nz)native djnz (10 is sra hl)
Stack + DE for second longexx, IX/IY as default temps
sub hl,bcAssuming sbc hl,de exists
Open-coded extended-op sequencesAssuming Z80 library mul/div cores

Assembler must be 8085-aware (these encodings are not Z80 prefixes).

10. Synthetic opcodes

z80asm expands many synthetic source forms into short real-op sequences (no harmful flag/side effects for the forms below). They keep library code readable and portable across CPUs without #if CPU for the same transfer.

16-bit register-pair copies (full set)

Synthetics of the form ld dst,src where dst and src are word register pairs. Each expands to two 8-bit lds (high then low, or as the assembler tables define — e.g. ld de,hlld d,h / ld e,l).

Allowed pairsForbidden as word-copy synthetics
bc, de, hlany → any (including “same” pair)af, sp (not part of this word-copy set)
    ld  bc,de          ; B←D, C←E
    ld  bc,hl
    ld  de,bc
    ld  de,hl
    ld  hl,bc
    ld  hl,de

Prefer these over hand-rolled two-byte moves (ld b,d / ld c,e, …) and over swap dances when you only need to park one pair:

    ; Hold DEHL; need HL free for SP adjust — park lo only:
    ld  bc,hl          ; park lo; DE (hi) stays
    ld  hl,14
    add hl,sp
    ld  sp,hl
    ld  hl,bc          ; restore lo

Do not invent ld bc,de + ex de,hl + SP math + reverse swaps when ld bc,hl / ld hl,bc is enough.

Other common synthetics (not pair-copy): e.g. ld a,(hl+) (load + inc index).

jr / jr cc: allowed in normal mode as jp / jp cc. Use them when the same source may also build for Z80. Strict / -no-synth rejects jr. Cost as a 3-byte jp (cond 10/7), not as Z80’s 2-byte jr. 18 is native rl de, not jr.

Strict assemble (*_strict_* / -no-synth) forbids free synthetics — fixtures under src/z80asm/dev/cpu/ (see tool-z80asm).

Pitfalls

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
1k
Forks
206
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
cpu-8085
Source
github.com/z88dk/z88dk