Extended usage
Skills for Agents to code 8085 Assembly Language
npx -y skills add feilipu/8085-skills --skill extended-usageAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 23 days oldThe repository was created 23 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Usage hints and coding preferences for Intel 8085 extended (undocumented) instructions, using Zilog mnemonics. Strong rule: stack-only locals and intermediates; static/BSS only for state that must survive across function calls — never for intermediate storage. Prefer when choosing idioms for stack frames, reentrancy, 16/32-bit shifts, K-flag loops, sub hl,bc compares, or optimizing 8085 assembly beyond the bare opcode map. Complements /opcode-reference. Use when the user runs /extended-usage or asks how to use 8085 extended ops effectively.
SKILL.md
13.1 KB, ~3.9k tokens by cl100k_base, as published. Nobody here has run it
8085 Extended Instruction Usage
When and how to use the ten 8085 extended instructions. Encodings, timings, and flags are in opcode-reference 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/BSS | Forbidden 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
| Zilog | Prefer for | Avoid / 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),hl | 16-bit load/store through DE | Not Z80 prefix encodings |
sub hl,bc | 16-bit subtract; == / !=; signed compares with K | No borrow-in; not multi-word subtract chains |
sra hl | Signed 16-bit arithmetic right shift | Clears V; C ← old bit 0 |
rl de | Rotate DE left through C; ×2 on DE; 32-bit with HL | Pair with add hl,hl / ex de,hl as needed |
jp k,** / jp nk,** | After 16-bit dec; signed compare outcomes | K after dec rp sets on −1, not on 0 |
rst v | Branch to handler if V set | Vector 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):
| Goal | Sequence | Bytes | Cycles | Flags | DE | Notes |
|---|---|---|---|---|---|---|
| Pointer in DE, HL untouched | ld de,sp+n | 2 | 10 | none | = SP+n | Prefer when HL must stay |
| HL = SP+n, DE free | ld de,sp+n / ex de,hl | 3 | 14 | none | becomes old HL | 6c faster, 1B smaller than classic |
| HL = SP+n, preserve DE | ex de,hl / ld de,sp+n / ex de,hl | 4 | 18 | none | restored | 2c 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, so a word popped into AF can never be a faithful 16-bit value ($FFFF → $FF7F). 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:
- 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. Afterpop bc; pop de; pop hlof pushed temps, restore withpush bc; push de; push hlonly if that matches the desired top word — verify with a depth diagram. - Overlapping copy (memmove). Copying a block upward when
dest = src + kandk < sizeoverlaps. Copy high → low (last byte first). Forward copy corrupts the tail. - 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. - 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:
; code body to be repeated bc times.
dec c
jr NZ,loop
dec b
jr NZ,loop
6. Multiply / divide building blocks — rl de + sub hl,bc
Shift-add mul and restoring div center on:
rl de(and oftenadd hl,hl) to shiftsub hl,bc/add hl,bcto trial-subtract and restoreccfinto 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.
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. 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 → $FF7F 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 8085 | Avoid (Z80-only or wrong) |
|---|---|
out (*),a / in a,(*) (byte in A) | outi, in r,(c), block I/O |
dec b / jp nz | djnz |
| Stack + DE for second long | exx, IX/IY as default temps |
sub hl,bc | Assuming sbc hl,de exists |
| Open-coded extended-op sequences | Assuming Z80 library mul/div cores |
Assembler must be 8085-aware (these encodings are not Z80 prefixes).
10. Synthetic opcodes
The z88dk-z80asm assembler has a MACRO capability, and it has the capability to support "synthetic" opcodes. Synthetic opcodes are usually created by a series of useful operations that can be chained together without side effects. For example ld bc,de which is a 16-bit emulation by two 8-bit operations, or for example ld a,(hl+) which is an indirect load followed by an increment of the index register. These operations can make code more concise and easier to debug, or allow an operation supported by one CPU to be used across multiple CPUs without deploying conditional assembly.
Pitfalls
pop afnever for function return — F bit 3 is hardwired 0, so AF cannot hold a correct return address. Use BC/DE/HL for the return word.pop afis OK only to discard intermediate stack values when the popped data is unused. AF is also not a clean 16-bit temp ($FFFF→$FF7F).sub hl,bchas no borrow-in — multi-precision use A +sbc.- K ≠ Z on 16-bit dec — pre-dec +
jp k/jp nk. - Offsets on
ld de,sp+*/ld de,hl+*are unsigned. rst vonly if 0040h is defined.rla/rra/rlca/rrcado not set Z — neverrla; jp z,.... Test withor a/and a/ explicit mask first, or useinc/decon a copy.- No
exx, IX, IY,djnz— second long operand on stack; counted loops viadec b/jp nzor K pre-dec (§5). - Forward overlapping stack copy corrupts — see multi-word frame rebuild above.
- No copt pass on library asm — hand-written
libsrc/**is assembled as-is. Remove copy-backs (ld r,athenld a,r) and other dead moves yourself. Match the target file’s whitespace (spaces vs tabs); do not reformat to sccz80/copt tab style. See z88dk-tooling §9.
Preference order (when writing 8085-only code)
- Stack-only locals/temps/intermediates; static/BSS only for state that must survive across calls — never intermediate storage.
ld de,sp+*for stack pointers;ex de,hlforms for HL←SP+n (see §2) overld hl,nn/add hl,spwhen offset is u8.ld hl,(de)/ld (de),hl/ld a,(de)for stack traffic through DE.sub hl,bcfor 16-bit ==, !=, and signed compares with K.- K + pre-dec for 16-bit counted loops.
rl defor ×2, mul/div shifts, 32-bit with HL.sra hlfor signed 16-bit >>; logical multi-byte >> via A.- Fall back to 8080-portable sequences only when the binary must run without 8085 extended ops.
Related
- Opcode map, flags, cycles:
/opcode-reference - Host tools, measurement, copt / z80rules:
/z88dk-tooling - Design notes: feilipu.me — 8085 Software
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.