2019-07-15 14:21:50 +02:00
|
|
|
|
[< back to index](../doc_index.md)
|
2018-07-03 23:28:05 +02:00
|
|
|
|
|
2018-07-27 19:07:12 +02:00
|
|
|
|
# Using 8080/LR35902/Z80 assembly within Millfork programs
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
|
|
|
|
There are two ways to include raw assembly code in your Millfork programs:
|
|
|
|
|
|
|
|
|
|
* inline assembly code blocks
|
|
|
|
|
|
|
|
|
|
* whole assembly functions
|
|
|
|
|
|
|
|
|
|
## Assembly syntax
|
|
|
|
|
|
2019-05-31 17:27:38 +02:00
|
|
|
|
By default, Millfork uses Zilog syntax for Z80 and LR35902 assembly and Intel syntax for Intel 8080/8085 assembly.
|
2018-08-03 17:26:26 +02:00
|
|
|
|
This can be overridden per file by a pragma directive or by several other means.
|
|
|
|
|
Using both kinds of syntax in one file is not supported.
|
2018-08-01 21:16:20 +02:00
|
|
|
|
|
2018-07-25 00:04:00 +02:00
|
|
|
|
Indexing via the IX/IY register uses the following syntax: `IX(1)`
|
2018-07-27 19:07:12 +02:00
|
|
|
|
|
|
|
|
|
LR35902 instructions that load/store the accumulator indirectly via HL and then increment/decrement HL are written
|
|
|
|
|
`LD A,(HLI)`, `LD, A,(HLD)`, `LD (HLI),A` and `LD (HLD),A`
|
2018-07-25 00:04:00 +02:00
|
|
|
|
|
2018-08-03 16:43:31 +02:00
|
|
|
|
LR35902 instructions for faster access to the $FFxx addresses use the `LDH` mnemonic: `LDH A,(4)`, `LDH (C),A` etc.
|
|
|
|
|
|
2018-07-25 00:04:00 +02:00
|
|
|
|
Only instructions available on the current CPU architecture are available.
|
2020-07-24 17:27:37 +02:00
|
|
|
|
Undocumented Z80 instructions are partially supported:
|
|
|
|
|
* `SLL` – supported
|
|
|
|
|
* instructions using the IXH, IXL, IYH, IYL registers – supported (can only be used in Zilog syntax)
|
|
|
|
|
* instructions of the form `RLC IX(1),B` – not supported
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
2020-07-24 17:27:37 +02:00
|
|
|
|
Intel syntax supports the 8080 instructions, the documented Z80 instructions and `SLL`.
|
|
|
|
|
It does not support instructions that are unavailable on the Z80 or other undocumented Z80 instructions.
|
|
|
|
|
|
|
|
|
|
Not all ZX Spectrum Next instructions are supported. `JP (C)`, `BSLA` and similar instructions are not supported.
|
2019-10-01 00:46:15 +02:00
|
|
|
|
|
2018-07-12 18:30:35 +02:00
|
|
|
|
Labels have to be followed by a colon and they can optionally be on a separate line.
|
|
|
|
|
Indentation is not important:
|
|
|
|
|
|
2018-08-03 17:26:26 +02:00
|
|
|
|
// Zilog syntax
|
2018-07-12 18:30:35 +02:00
|
|
|
|
first: INC a
|
|
|
|
|
second:
|
|
|
|
|
INC b
|
|
|
|
|
INC c
|
2018-08-03 17:26:26 +02:00
|
|
|
|
|
|
|
|
|
// Intel syntax
|
|
|
|
|
first: INR a
|
|
|
|
|
second:
|
|
|
|
|
INR b
|
|
|
|
|
INR c
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
|
|
|
|
|
2020-09-01 22:00:07 +02:00
|
|
|
|
Global label names have to start with a letter and can contain digits, underscores and letters.
|
|
|
|
|
Local label names (available since Millfork 0.3.22) start with a period and are visible only in the given function.
|
|
|
|
|
Anonymous labels designated with `+` or `-` are also not supported.
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
|
|
|
|
Assembly can refer to variables and constants defined in Millfork,
|
|
|
|
|
but you need to be careful with using absolute vs immediate addressing:
|
|
|
|
|
|
|
|
|
|
const byte fiveConstant = 5
|
|
|
|
|
byte fiveVariable = 5
|
|
|
|
|
|
|
|
|
|
byte ten() {
|
|
|
|
|
byte result
|
|
|
|
|
asm {
|
2018-08-03 17:26:26 +02:00
|
|
|
|
// Zilog syntax
|
|
|
|
|
LD A, (fiveVariable) // not LD A,fiveVariable
|
2018-07-12 18:30:35 +02:00
|
|
|
|
ADD A,fiveConstant
|
|
|
|
|
LD (result), A
|
2018-08-03 17:26:26 +02:00
|
|
|
|
|
|
|
|
|
// Intel syntax
|
|
|
|
|
LDA fiveVariable // not MVI A,fiveVariable
|
|
|
|
|
ADD fiveConstant
|
|
|
|
|
STA result
|
2018-07-12 18:30:35 +02:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Any assembly opcode can be prefixed with `?`, which allows the optimizer change it or elide it if needed.
|
|
|
|
|
Opcodes without that prefix will be always compiled as written.
|
|
|
|
|
|
2018-12-16 21:07:04 +01:00
|
|
|
|
The '!' prefix marks the statement as volatile, which means it will be a subject to certain, but not all optimizations,
|
|
|
|
|
in order to preserve its semantics.
|
|
|
|
|
|
2018-07-12 18:30:35 +02:00
|
|
|
|
You can insert macros into assembly, by prefixing them with `+` and using the same syntax as in Millfork:
|
|
|
|
|
|
|
|
|
|
macro void run(byte x) {
|
|
|
|
|
output = x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte output @$c000
|
|
|
|
|
|
|
|
|
|
void main () {
|
|
|
|
|
byte a
|
|
|
|
|
a = 7
|
|
|
|
|
asm {
|
|
|
|
|
+ run(a)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
You can insert raw bytes into your assembly using the array syntax:
|
|
|
|
|
|
|
|
|
|
[ $00, $00 ]
|
|
|
|
|
"this is a string to print" bbc
|
|
|
|
|
["this is a string to print but this time it's zero-terminated so it will actually work" bbc, 0]
|
|
|
|
|
[for x,0,until,8 [x]]
|
|
|
|
|
|
|
|
|
|
## Assembly functions
|
|
|
|
|
|
|
|
|
|
Assembly functions can be declared as `macro` or not.
|
|
|
|
|
|
|
|
|
|
A macro assembly function is inserted into the calling function like an inline assembly block,
|
|
|
|
|
and therefore usually it shouldn't end with `RET`, `RETI` or `RETN`.
|
|
|
|
|
|
2018-08-03 17:26:26 +02:00
|
|
|
|
A non-macro assembly function should end with `RET`, `JP`, `RETI` or `RETN` (Zilog) / `RET` or `JMP` (Intel) as appropriate,
|
2018-07-12 18:30:35 +02:00
|
|
|
|
or it should be an external function.
|
|
|
|
|
|
|
|
|
|
For both macro and non-macro assembly functions,
|
|
|
|
|
the return type can be any valid return type, like for Millfork functions.
|
|
|
|
|
If the size of the return type is one byte,
|
|
|
|
|
then the result is passed via the A register.
|
|
|
|
|
If the size of the return type is two bytes,
|
|
|
|
|
then the result is passed via the HL register pair.
|
|
|
|
|
|
|
|
|
|
### Assembly function parameters
|
|
|
|
|
|
|
|
|
|
An assembly function can have parameters.
|
|
|
|
|
They differ from what is used by Millfork functions.
|
|
|
|
|
|
|
|
|
|
Macro assembly functions can have the following parameter types:
|
|
|
|
|
|
|
|
|
|
* reference parameters: `byte ref paramname`: every occurrence of the parameter will be replaced with the variable given as an argument
|
|
|
|
|
|
|
|
|
|
* constant parameters: `byte const paramname`: every occurrence of the parameter will be replaced with the constant value given as an argument
|
|
|
|
|
|
|
|
|
|
For example, if you have:
|
|
|
|
|
|
2018-08-03 17:26:26 +02:00
|
|
|
|
// Zilog syntax
|
2018-07-12 18:30:35 +02:00
|
|
|
|
macro asm void increase(byte ref v, byte const inc) {
|
|
|
|
|
LD A,(v)
|
|
|
|
|
ADD A,inc
|
|
|
|
|
LDA (v),A
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-03 17:26:26 +02:00
|
|
|
|
// Intel syntax
|
|
|
|
|
macro asm void increase(byte ref v, byte const inc) {
|
|
|
|
|
LDA v
|
|
|
|
|
ADD inc
|
|
|
|
|
STA v
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-12 18:30:35 +02:00
|
|
|
|
and call `increase(score, 10)`, the entire call will compile into:
|
|
|
|
|
|
2018-08-03 17:26:26 +02:00
|
|
|
|
// Zilog syntax
|
2018-07-12 18:30:35 +02:00
|
|
|
|
LD A,(score)
|
|
|
|
|
ADD A,10
|
|
|
|
|
LD (score),A
|
2018-08-03 17:26:26 +02:00
|
|
|
|
|
|
|
|
|
// Intel syntax
|
|
|
|
|
LDA score
|
|
|
|
|
ADD 10
|
|
|
|
|
STA score
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
|
|
|
|
Non-macro functions can only have their parameters passed via registers:
|
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
* `byte a`, `byte b`, `byte c`, `byte d`, `byte e`, `byte h`, `byte l`: a single byte passed via the given CPU register; any 1-byte type can be used
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
* `word hl`, `word bc`, `word de`: a 2-byte word byte passed via given 16-bit register; any 2-byte type can be used
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
2020-03-30 19:23:48 +02:00
|
|
|
|
* the above, but written more explicitly: `byte register(a) paramname`, `byte register(b) paramname`, `word register(hl) paramname` etc.
|
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
Parameters passed via other registers (`I`, `IX`, `IY`, `IXH` etc.) or combinations of registers do not work yet.
|
2018-07-31 01:00:17 +02:00
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
**Work in progress**:
|
|
|
|
|
Only the following combinations of register parameters work reliably:
|
2018-07-31 01:00:17 +02:00
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
* zero or one register parameters
|
2018-07-31 01:00:17 +02:00
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
* two register parameters where at least one of them is a 16-bit parameter
|
2018-07-31 01:00:17 +02:00
|
|
|
|
|
2019-07-09 22:40:14 +02:00
|
|
|
|
Other combinations are guaranteed to work only with constant arguments.
|
2018-07-12 18:30:35 +02:00
|
|
|
|
|
|
|
|
|
Macro assembly functions cannot have any parameter passed via registers.
|
2018-07-03 23:28:05 +02:00
|
|
|
|
|
|
|
|
|
## Safe assembly
|
|
|
|
|
|
|
|
|
|
Since assembly gives the programmer unlimited access to all machine features,
|
|
|
|
|
certain assumptions about the code may be broken.
|
|
|
|
|
In order to make assembly cooperate with the rest of the Millfork code,
|
|
|
|
|
it should abide to the following rules:
|
|
|
|
|
|
|
|
|
|
* don't change the IX register
|
|
|
|
|
|
2018-07-25 00:04:00 +02:00
|
|
|
|
* don't change the IY register if the target platform doesn't allow it
|
|
|
|
|
(for example: ZX Spectrum in interrupt mode 1)
|
|
|
|
|
|
2018-07-03 23:28:05 +02:00
|
|
|
|
* don't jump between functions if either of functions has stack variables
|
|
|
|
|
|
|
|
|
|
* don't do `RET`, `RETI` or `RETN` if the function has stack variables
|
|
|
|
|
|
|
|
|
|
* don't jump or call things that are not functions or labels
|
|
|
|
|
|
|
|
|
|
* don't store data in locations other than variables or arrays
|
|
|
|
|
|
|
|
|
|
* don't change the stack pointer
|
|
|
|
|
|
2018-08-03 17:26:26 +02:00
|
|
|
|
* end non-inline assembly functions with `RET`, `JP`, `RETI` or `RETN` (Zilog) / `RET` or `JMP` (Intel) as appropriate
|
2018-07-03 23:28:05 +02:00
|
|
|
|
|
|
|
|
|
The above list is not exhaustive.
|
2020-07-24 17:27:37 +02:00
|
|
|
|
|
|
|
|
|
## Z80 instructions in the Intel syntax
|
|
|
|
|
|
|
|
|
|
Millfork uses the same extensions for Intel syntax as Z80.LIB from Digital Research.
|
|
|
|
|
Some mnemonics from the TDL Z80 Relocating/Linking Assembler are also supported.
|
|
|
|
|
|
|
|
|
|
In the list below, `c` is a flag, `r` is a register, and `n` and `d` are parameters.
|
|
|
|
|
For instructions using the index registers, only the IY variant is given;
|
|
|
|
|
the IX variant has the same mnemonic, but with `Y` replaced with `X`.
|
|
|
|
|
|
|
|
|
|
Intel syntax | Zilog syntax
|
|
|
|
|
----|----
|
|
|
|
|
**EXAF** | **EX AF,AF'**
|
|
|
|
|
**JR n**, JMPR n | **JR n**
|
|
|
|
|
**JRc n** | **JR c,n**
|
|
|
|
|
**INP r** | **IN r,(C)**
|
|
|
|
|
**OUTP r** | **OUT r,(C)**
|
|
|
|
|
**CCI** | **CPI**
|
|
|
|
|
**CCIR** | **CPIR**
|
|
|
|
|
**CCD** | **CPD**
|
|
|
|
|
**CCDR** | **CPDR**
|
|
|
|
|
**OUTIR** | **OTIR**, OUTIR
|
|
|
|
|
**OUTDR** | **OTDR**, OUTDR
|
|
|
|
|
**IM0** | **IM 0**
|
|
|
|
|
**IM1** | **IM 1**
|
|
|
|
|
**IM2** | **IM 2**
|
|
|
|
|
**DSBC r** | **SBC HL,rr**
|
|
|
|
|
**DADC r** | **ADC HL,rr**
|
|
|
|
|
**DADY r** | **ADD IY,rr**
|
|
|
|
|
**INXIY**, INX IY | **INC IY**
|
|
|
|
|
**DCXIY**, DCX IY | **DEC IY**
|
|
|
|
|
**SBCD nn** | **LD (nn),BC**
|
|
|
|
|
**SDED nn** | **LD (nn),DE**
|
|
|
|
|
**SSPD nn** | **LD (nn),SP**
|
|
|
|
|
**SIYD nn** | **LD (nn),IY**
|
|
|
|
|
**LBCD nn** | **LD BC,(nn)**
|
|
|
|
|
**LDED nn** | **LD DE,(nn)**
|
|
|
|
|
**LSPD nn** | **LD SP,(nn)**
|
|
|
|
|
**LIYD nn** | **LD IY,(nn)**
|
|
|
|
|
**SETB n,r**, SET n,r | **SET n,r**
|
|
|
|
|
**BITY n,d** | **BIT n,IY(d)**
|
|
|
|
|
**SETY n,d** | **SET n,IY(d)**
|
|
|
|
|
**RESY n,d** | **RES n,IY(d)**
|
|
|
|
|
**PCIY** | **JP IY**
|
|
|
|
|
**RLCR r** | **RLC r**
|
|
|
|
|
**RALR r** | **RL r**
|
|
|
|
|
**RRCR r** | **RRC r**
|
|
|
|
|
**RARR r** | **RR r**
|
|
|
|
|
**SLAR r** | **SLA r**
|
|
|
|
|
**SRAR r** | **SRA r**
|
|
|
|
|
**SRLR r** | **SRL r**
|
|
|
|
|
**RLCX r** | **RLC r**
|
|
|
|
|
**RALY d** | **RL IY(d)**
|
|
|
|
|
**RRCY d** | **RRC IY(d)**
|
|
|
|
|
**RARY d** | **RR IY(d)**
|
|
|
|
|
**SLAY d** | **SLA IY(d)**
|
|
|
|
|
**SRAY d** | **SRA IY(d)**
|
|
|
|
|
**SRLY d** | **SRL IY(d)**
|
2020-09-27 18:46:19 +02:00
|
|
|
|
**SLLR r** | **SLL r**, SLS r
|
|
|
|
|
**SLLY d** | **SLL IY(d)**, SLS IY(d)
|
2020-07-24 17:27:37 +02:00
|
|
|
|
**SPIY** | **LD SP,IY**
|
|
|
|
|
**PUSHIY**, PUSH IY | **PUSH IY**
|
|
|
|
|
**POPIY**, POP IY | **POP IY**
|
|
|
|
|
**XTIY** | **EX (SP),IY**
|
|
|
|
|
**LDAI** | **LD A,I**
|
|
|
|
|
**LDAR** | **LD A,R**
|
|
|
|
|
**STAI** | **LD I,A**
|
|
|
|
|
**STAR** | **LD R,A**
|
|
|
|
|
**LXIY nn**, LXI IY,nn | **LD IY,nn**
|
|
|
|
|
**ADDY d** | **ADD A,IY(d)**
|
|
|
|
|
**ADCY d** | **ADC A,IY(d)**
|
|
|
|
|
**SUBY d** | **SUB IY(d)**
|
|
|
|
|
**SBCY d** | **SBC A,IY(d)**
|
|
|
|
|
**ANDY d** | **AND IY(d)**
|
|
|
|
|
**XORY d** | **XOR IY(d)**
|
|
|
|
|
**ORY d** | **OR IY(d)**
|
|
|
|
|
**CMPY d** | **CMP IY(d)**
|
|
|
|
|
**INRY d** | **INC IY(d)**
|
|
|
|
|
**DCRY d** | **DEC IY(d)**
|
|
|
|
|
**MVIY n,d** | **LD IY(d),n**
|
|
|
|
|
**LDY r,d** | **LD r,IY(d)**
|
|
|
|
|
**STY r,d** | **LD IY(d),r**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Instructions that are the same in both syntaxes:
|
|
|
|
|
|
|
|
|
|
**BIT n,r**,
|
|
|
|
|
**RES n,r**,
|
|
|
|
|
**DJNZ n**,
|
|
|
|
|
**EXX**,
|
|
|
|
|
**NEG**,
|
|
|
|
|
**RETI**,
|
|
|
|
|
**RETN**,
|
|
|
|
|
**RLD**,
|
|
|
|
|
**RRD**,
|
|
|
|
|
**LDI**,
|
|
|
|
|
**LDIR**,
|
|
|
|
|
**LDD**,
|
|
|
|
|
**LDDR**,
|
|
|
|
|
**INI**,
|
|
|
|
|
**INIR**,
|
|
|
|
|
**IND**,
|
|
|
|
|
**INDR**,
|
|
|
|
|
**OUTI**,
|
|
|
|
|
**OUTD**
|