1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-26 11:29:28 +00:00

Z80: Support IXH/IXL/IYH/IYL registers. Add Intel syntax for Z80 instructions.

This commit is contained in:
Karol Stasiak 2020-07-24 17:27:37 +02:00
parent 53973f081a
commit b24ac32932
7 changed files with 858 additions and 9 deletions

View File

@ -69,8 +69,11 @@ AHX, LAS, LXA, SHX, SHY, TAS, XAA.
## Z80
Original Z80 processors accidentally supported a bunch of extra undocumented instructions.
Millfork will not emit them.
The only exception is SLL, which will be emitted if it occurs in a handwritten assembly block.
Millfork will emit some of them if used in an assembly block:
* `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
## 8085

View File

@ -22,10 +22,15 @@ LR35902 instructions that load/store the accumulator indirectly via HL and then
LR35902 instructions for faster access to the $FFxx addresses use the `LDH` mnemonic: `LDH A,(4)`, `LDH (C),A` etc.
Only instructions available on the current CPU architecture are available.
Intel syntax does not support instructions that are unavailable on the 8080.
Undocumented Z80 instructions are not supported, except for `SLL`.
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
Not all ZX Spectrum Next are supported. `JP (C)`, `BSLA` and similar instructions are not supported.
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.
Labels have to be followed by a colon and they can optionally be on a separate line.
Indentation is not important:
@ -200,3 +205,108 @@ it should abide to the following rules:
* end non-inline assembly functions with `RET`, `JP`, `RETI` or `RETN` (Zilog) / `RET` or `JMP` (Intel) as appropriate
The above list is not exhaustive.
## 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)**
**SLLR r** | **SLL r**
**SLLY d** | **SLL IY(d)**
**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**

View File

@ -275,7 +275,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
override def isPrintable: Boolean = true
private def asAssemblyString(r: ZRegister.Value, offset: Int = 0): String = r match {
private def asAssemblyString(r: ZRegister.Value, offset: Int = 666): String = r match {
case ZRegister.A => "A"
case ZRegister.B => "B"
case ZRegister.C => "C"
@ -325,6 +325,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case ZRegister.IMM_8 => s"$parameter"
case ZRegister.IMM_16 => s"$parameter"
case ZRegister.MEM_HL => "M"
case ZRegister.IX => "IX"
case ZRegister.IY => "IY"
case _ => "???"
}
@ -467,6 +469,12 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case DISCARD_IY => " ; DISCARD_IY"
case BYTE => " DB " + parameter.toString
case LD => registers match {
case TwoRegistersOffset(MEM_IX_D, IMM_8, offset) => s" MVIX ${parameter.toIntelString}, ${offset}"
case TwoRegistersOffset(MEM_IY_D, IMM_8, offset) => s" MVIY ${parameter.toIntelString}, ${offset}"
case TwoRegistersOffset(MEM_IX_D, source, offset) => s" STX ${asIntelAssemblyString(source)}, ${offset}"
case TwoRegistersOffset(MEM_IY_D, source, offset) => s" STY ${asIntelAssemblyString(source)}, ${offset}"
case TwoRegistersOffset(target, MEM_IX_D, offset) => s" LDX ${asIntelAssemblyString(target)}, ${offset}"
case TwoRegistersOffset(target, MEM_IY_D, offset) => s" LDY ${asIntelAssemblyString(target)}, ${offset}"
case TwoRegisters(target, IMM_8) => s" MVI ${asIntelAssemblyString(target)}, ${parameter.toIntelString}"
case TwoRegisters(A, MEM_ABS_8) => s" LDA ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_8, A) => s" STA ${parameter.toIntelString}"
@ -474,50 +482,169 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case TwoRegisters(MEM_BC, A) => " STAX B"
case TwoRegisters(A, MEM_DE) => " LDAX D"
case TwoRegisters(MEM_DE, A) => " STAX D"
case TwoRegisters(I, A) => " STAI"
case TwoRegisters(A, I) => " LDAI"
case TwoRegisters(R, A) => " STAR"
case TwoRegisters(A, R) => " LDAR"
case TwoRegisters(target, source) => s" MOV ${asIntelAssemblyString(target)}, ${asIntelAssemblyString(source)}"
case _ => "???"
}
case LD_16 => registers match {
case TwoRegisters(SP, HL) => " SPHL"
case TwoRegisters(SP, IX) => " SPIX"
case TwoRegisters(SP, IY) => " SPIY"
case TwoRegisters(IX, IMM_16) => s" LXIX ${parameter.toIntelString}"
case TwoRegisters(IY, IMM_16) => s" LXIY ${parameter.toIntelString}"
case TwoRegisters(target, IMM_16) => s" LXI ${asIntelAssemblyString(target)}, ${parameter.toIntelString}"
case TwoRegisters(HL, MEM_ABS_16) => s" LHLD ${parameter.toIntelString}"
case TwoRegisters(BC, MEM_ABS_16) => s" LBCD ${parameter.toIntelString}"
case TwoRegisters(DE, MEM_ABS_16) => s" LDED ${parameter.toIntelString}"
case TwoRegisters(IX, MEM_ABS_16) => s" LIXD ${parameter.toIntelString}"
case TwoRegisters(IY, MEM_ABS_16) => s" LIYD ${parameter.toIntelString}"
case TwoRegisters(SP, MEM_ABS_16) => s" LSPD ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_16, HL) => s" SHLD ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_16, BC) => s" SBCD ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_16, DE) => s" SDED ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_16, IX) => s" SIXD ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_16, IY) => s" SIYD ${parameter.toIntelString}"
case TwoRegisters(MEM_ABS_16, SP) => s" SSPD ${parameter.toIntelString}"
case _ => "???"
}
case ADD_16 => registers match {
case TwoRegisters(IX, source) => s" DADX ${asIntelAssemblyString(source)}"
case TwoRegisters(IY, source) => s" DADY ${asIntelAssemblyString(source)}"
case TwoRegisters(HL, source) => s" DAD ${asIntelAssemblyString(source)}"
case _ => "???"
}
case ADC_16 => registers match {
case TwoRegisters(HL, source) => s" DADC ${asIntelAssemblyString(source)}"
case _ => "???"
}
case SBC_16 => registers match {
case TwoRegisters(HL, source) => s" DSBC ${asIntelAssemblyString(source)}"
case _ => "???"
}
case DEC_16 => registers match {
case OneRegister(IX) => s" DCXIX"
case OneRegister(IY) => s" DCXIY"
case OneRegister(register) => s" DCX ${asIntelAssemblyString(register)}"
case _ => "???"
}
case INC_16 => registers match {
case OneRegister(IX) => s" INXIX"
case OneRegister(IY) => s" INXIY"
case OneRegister(register) => s" INX ${asIntelAssemblyString(register)}"
case _ => "???"
}
case DEC => registers match {
case OneRegisterOffset(MEM_IX_D, offset) => s" DCRX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" DCRY ${offset}"
case OneRegister(register) => s" DCR ${asIntelAssemblyString(register)}"
case _ => "???"
}
case INC => registers match {
case OneRegisterOffset(MEM_IX_D, offset) => s" INRX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" INRY ${offset}"
case OneRegister(register) => s" INR ${asIntelAssemblyString(register)}"
case _ => "???"
}
case op if ZOpcodeClasses.RES(op) => registers match {
case OneRegisterOffset(MEM_IX_D, offset) => s" RESX ${ZOpcodeClasses.RES_seq.indexOf(op)},${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" RESY ${ZOpcodeClasses.RES_seq.indexOf(op)},${offset}"
case OneRegister(register) => s" RES ${ZOpcodeClasses.RES_seq.indexOf(op)},${asIntelAssemblyString(register)}"
case _ => "???"
}
case op if ZOpcodeClasses.SET(op) => registers match {
case OneRegisterOffset(MEM_IX_D, offset) => s" SETX ${ZOpcodeClasses.SET_seq.indexOf(op)},${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" SETY ${ZOpcodeClasses.SET_seq.indexOf(op)},${offset}"
case OneRegister(register) => s" SETB ${ZOpcodeClasses.SET_seq.indexOf(op)},${asIntelAssemblyString(register)}"
case _ => "???"
}
case op if ZOpcodeClasses.BIT(op) => registers match {
case OneRegisterOffset(MEM_IX_D, offset) => s" BITX ${ZOpcodeClasses.BIT_seq.indexOf(op)},${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" BITY ${ZOpcodeClasses.BIT_seq.indexOf(op)},${offset}"
case OneRegister(register) => s" BIT ${ZOpcodeClasses.BIT_seq.indexOf(op)},${asIntelAssemblyString(register)}"
case _ => "???"
}
case PUSH => registers match {
case OneRegister(IX) => s" PUSHIX"
case OneRegister(IY) => s" PUSHIY"
case OneRegister(register) => s" PUSH ${asIntelAssemblyString(register)}"
case _ => "???"
}
case POP => registers match {
case OneRegister(IX) => s" POPIX"
case OneRegister(IY) => s" POPIY"
case OneRegister(register) => s" POP ${asIntelAssemblyString(register)}"
case _ => "???"
}
case IM => parameter match {
case NumericConstant(0, _) => s" IM0"
case NumericConstant(1, _) => s" IM1"
case NumericConstant(2, _) => s" IM2"
case _ => "???"
}
case RL => registers match {
case OneRegister(register) => s" RALR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" RALX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" RALY $offset"
case _ => "???"
}
case RLC => registers match {
case OneRegister(register) => s" RLCR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" RLCX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" RLCY $offset"
case _ => "???"
}
case RR => registers match {
case OneRegister(register) => s" RARR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" RARX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" RARY $offset"
case _ => "???"
}
case RRC => registers match {
case OneRegister(register) => s" RRCR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" RRCX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" RRCY $offset"
case _ => "???"
}
case SLA => registers match {
case OneRegister(register) => s" SLAR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" SLAX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" SLAY $offset"
case _ => "???"
}
case SLL => registers match {
case OneRegister(register) => s" SLLR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" SLLX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" SLLY $offset"
case _ => "???"
}
case SRA => registers match {
case OneRegister(register) => s" SRAR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" SRAX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" SRAY $offset"
case _ => "???"
}
case SRL => registers match {
case OneRegister(register) => s" SRLR ${asIntelAssemblyString(register)}"
case OneRegisterOffset(MEM_IX_D, offset) => s" SRLX $offset"
case OneRegisterOffset(MEM_IY_D, offset) => s" SRLY $offset"
case _ => "???"
}
case DAA => " DAA"
case RLA => " RAL"
case RLCA => " RLC"
case RRA => " RAR"
case RRCA => " RRC"
case HALT => " HLT"
case LDI|LDIR|LDD|LDDR|INI|INIR|IND|INDR|OUTI|OUTD|EXX|NEG|RLD|RRD|RETN|RETI => " " + opcode
case OUTIR => " OUTIR"
case OUTDR => " OUTDR"
case CPI => " CCI"
case CPIR => " CCIR"
case CPD => " CCD"
case CPDR => " CCDR"
case RET => registers match {
case NoRegisters => " RET"
case IfFlagClear(ZFlag.C) => " RNC"
@ -532,6 +659,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
}
case JP => registers match {
case OneRegister(HL) => " PCHL"
case OneRegister(IX) => " PCIX"
case OneRegister(IY) => " PCIY"
case NoRegisters => s" JMP ${parameter.toIntelString}"
case IfFlagClear(ZFlag.C) => s" JNC ${parameter.toIntelString}"
case IfFlagClear(ZFlag.Z) => s" JNZ ${parameter.toIntelString}"
@ -545,6 +674,15 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case IfFlagSet(ZFlag.K) => s" JK ${parameter.toIntelString}"
case _ => "???"
}
case JR => registers match {
case NoRegisters => s" JR ${parameter.toIntelString}"
case IfFlagClear(ZFlag.C) => s" JRNC ${parameter.toIntelString}"
case IfFlagClear(ZFlag.Z) => s" JRNZ ${parameter.toIntelString}"
case IfFlagSet(ZFlag.C) => s" JRC ${parameter.toIntelString}"
case IfFlagSet(ZFlag.Z) => s" JRZ ${parameter.toIntelString}"
case _ => "???"
}
case DJNZ => s" DJNZ ${parameter.toIntelString}"
case CALL => registers match {
case NoRegisters => s" CALL ${parameter.toIntelString}"
case IfFlagClear(ZFlag.C) => s" CNC ${parameter.toIntelString}"
@ -559,52 +697,78 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
}
case ADD => registers match {
case OneRegister(IMM_8) => s" ADI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" ADDX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" ADDY ${offset}"
case OneRegister(register) => s" ADD ${asIntelAssemblyString(register)}"
case _ => "???"
}
case ADC => registers match {
case OneRegister(IMM_8) => s" ACI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" ADCX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" ADCY ${offset}"
case OneRegister(register) => s" ADC ${asIntelAssemblyString(register)}"
case _ => "???"
}
case SUB => registers match {
case OneRegister(IMM_8) => s" SUI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" SUBX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" SUBY ${offset}"
case OneRegister(register) => s" SUB ${asIntelAssemblyString(register)}"
case _ => "???"
}
case SBC => registers match {
case OneRegister(IMM_8) => s" SBI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" SBCX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" SBCY ${offset}"
case OneRegister(register) => s" SBB ${asIntelAssemblyString(register)}"
case _ => "???"
}
case AND => registers match {
case OneRegister(IMM_8) => s" ANI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" ANDX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" ANDY ${offset}"
case OneRegister(register) => s" ANA ${asIntelAssemblyString(register)}"
case _ => "???"
}
case OR => registers match {
case OneRegister(IMM_8) => s" ORI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" ORX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" ORY ${offset}"
case OneRegister(register) => s" ORA ${asIntelAssemblyString(register)}"
case _ => "???"
}
case XOR => registers match {
case OneRegister(IMM_8) => s" XRI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" XORX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" XORY ${offset}"
case OneRegister(register) => s" XRA ${asIntelAssemblyString(register)}"
case _ => "???"
}
case CP => registers match {
case OneRegister(IMM_8) => s" CPI ${parameter.toIntelString}"
case OneRegisterOffset(MEM_IX_D, offset) => s" CMPX ${offset}"
case OneRegisterOffset(MEM_IY_D, offset) => s" CMPY ${offset}"
case OneRegister(register) => s" CMP ${asIntelAssemblyString(register)}"
case _ => "???"
}
case EX_SP => registers match {
case OneRegister(HL) => s" XTHL"
case OneRegister(IX) => s" XTIX"
case OneRegister(IY) => s" XTIY"
case _ => "???"
}
case RST => parameter match {
case NumericConstant(n, _) if n % 8 == 0 => s" RST ${n / 8}"
case _ => "???"
}
case IN_C => registers match {
case OneRegister(register) => s" INP ${asIntelAssemblyString(register)}"
case _ => "???"
}
case OUT_C => registers match {
case OneRegister(register) => s" OUTP ${asIntelAssemblyString(register)}"
case _ => "???"
}
case IN_IMM => s" IN ${parameter.toIntelString}"
case OUT_IMM => s" OUT ${parameter.toIntelString}"
case EI => " EI"
@ -616,6 +780,8 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
case CCF => " CMC"
case EI => " EI"
case EX_AF_AF => " EXAF"
case DSUB => " DSUB"
case RRHL => " ARHL"
case RLDE => " RLDE"

View File

@ -32,6 +32,10 @@ class Z80Assembler(program: Program,
case ZRegister.E => 3
case ZRegister.H => 4
case ZRegister.L => 5
case ZRegister.IXH => 4
case ZRegister.IXL => 5
case ZRegister.IYH => 4
case ZRegister.IYL => 5
case ZRegister.MEM_HL => 6
case ZRegister.MEM_IX_D => 6
case ZRegister.MEM_IY_D => 6
@ -46,6 +50,8 @@ class Z80Assembler(program: Program,
private def prefixByte(reg: ZRegister.Value): Int = reg match {
case ZRegister.IX | ZRegister.MEM_IX_D => 0xdd
case ZRegister.IY | ZRegister.MEM_IY_D => 0xfd
case ZRegister.IXH | ZRegister.IXL => 0xdd
case ZRegister.IYH | ZRegister.IYL => 0xfd
}
override def emitInstruction(bank: String, options: CompilationOptions, index: Int, instr: ZLine): Int = {
@ -317,6 +323,12 @@ class Z80Assembler(program: Program,
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ZRegister.HL) * o.multiplier)
writeByte(bank, index + 2, instr.parameter)
index + 3
case ZLine0(op, OneRegister(ix@(IXH | IYH | IXL | IYL)), _) if oneRegister.contains(op) =>
requireZ80Illegals()
val o = oneRegister(op)
writeByte(bank, index, prefixByte(ix))
writeByte(bank, index + 1, o.opcode + internalRegisterIndex(ix) * o.multiplier)
index + 2
case ZLine0(op, OneRegister(reg), _) if reg != ZRegister.IMM_8 && oneRegister.contains(op) =>
val o = oneRegister(op)
writeByte(bank, index, o.opcode + internalRegisterIndex(reg) * o.multiplier)
@ -428,6 +440,33 @@ class Z80Assembler(program: Program,
writeByte(bank, index + 1, 0x40 + internalRegisterIndex(ZRegister.MEM_HL) + internalRegisterIndex(target) * 8)
writeByte(bank, index + 2, offset)
index + 3
case TwoRegisters(target@(IXH | IYH | IXL | IYL), source@(A | B | C | D | E)) =>
requireZ80Illegals()
writeByte(bank, index, prefixByte(target))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(A | B | C | D | E), source@(IXH | IYH | IXL | IYL)) =>
requireZ80Illegals()
writeByte(bank, index, prefixByte(source))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(IXH | IXL), source@(IXH | IXL)) =>
requireZ80Illegals()
writeByte(bank, index, prefixByte(source))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(IYH | IYL), source@(IYH | IYL)) =>
requireZ80Illegals()
writeByte(bank, index, prefixByte(source))
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 2
case TwoRegisters(target@(H | L), source@(H | L)) =>
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 1
case TwoRegisters(target@(IXH | IYH | IXL | IYL | H | L), source@(IXH | IYH | IXL | IYL | H | L)) =>
log.error("Cannot assemble " + instr)
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 1
case TwoRegisters(target, source) =>
writeByte(bank, index, 0x40 + internalRegisterIndex(source) + internalRegisterIndex(target) * 8)
index + 1

View File

@ -9,7 +9,7 @@ import millfork.{CompilationFlag, CompilationOptions, node}
import millfork.assembly.z80.{ZOpcode, _}
import millfork.env.{ByZRegister, Constant, ParamPassingConvention}
import millfork.error.ConsoleLogger
import millfork.node._
import millfork.node.{ZRegister, _}
import millfork.output.{MemoryAlignment, NoAlignment, WithinPageAlignment}
/**
@ -73,6 +73,13 @@ case class Z80Parser(filename: String,
"SP" -> ZRegister.SP, "sp" -> ZRegister.SP,
)
private val toIndexHalf: Map[String, ZRegister.Value] = Map(
"IXH" -> ZRegister.IXH, "ixh" -> ZRegister.IXH,
"IXL" -> ZRegister.IXL, "ixl" -> ZRegister.IXL,
"IYH" -> ZRegister.IYH, "iyh" -> ZRegister.IYH,
"IYL" -> ZRegister.IYL, "iyl" -> ZRegister.IYL,
)
private val toIntelRegister8: Map[String, ZRegister.Value] = Map(
"A" -> ZRegister.A, "a" -> ZRegister.A,
"B" -> ZRegister.B, "b" -> ZRegister.B,
@ -86,16 +93,25 @@ case class Z80Parser(filename: String,
private val toIntelRegister16: Map[String, ZRegister.Value] = Map(
"H" -> ZRegister.HL, "h" -> ZRegister.HL,
"HL" -> ZRegister.HL, "hl" -> ZRegister.HL,
"PSW" -> ZRegister.AF, "psw" -> ZRegister.AF,
"B" -> ZRegister.BC, "b" -> ZRegister.BC,
"BC" -> ZRegister.BC, "bc" -> ZRegister.BC,
"D" -> ZRegister.DE, "d" -> ZRegister.DE,
"DE" -> ZRegister.DE, "de" -> ZRegister.DE,
"SP" -> ZRegister.SP, "sp" -> ZRegister.SP,
"IX" -> ZRegister.IX, "ix" -> ZRegister.IX,
"IY" -> ZRegister.IY, "iy" -> ZRegister.IY,
)
private def param(allowAbsolute: Boolean, allowRI: Boolean = false, allowFfc: Boolean = false): P[(ZRegister.Value, Option[Expression])] = asmExpressionWithParens.map {
case (VariableExpression("R" | "r"), false) if allowRI => (ZRegister.R, None)
case (VariableExpression("I" | "i"), false) if allowRI => (ZRegister.I, None)
case (VariableExpression(r), false) if toRegister.contains(r)=> (toRegister(r), None)
case (VariableExpression(r), false)
if options.flag(CompilationFlag.EmitZ80Opcodes) &&
options.flag(CompilationFlag.EmitIllegals) &&
toIndexHalf.contains(r)=> (toIndexHalf(r), None)
case (SumExpression(List(
(false, LiteralExpression(0xff00, _)),
(false, VariableExpression("C" | "c"))
@ -138,6 +154,8 @@ case class Z80Parser(filename: String,
case (reg, addr) => (op, OneRegister(reg), None, addr.getOrElse(zero))
}
def one8RegisterIntel(op: ZOpcode.Value): P[(ZOpcode.Value, OneRegister, Option[Expression], Expression)] = intel8.map(reg => (op, OneRegister(reg), None, zero))
def one8RegisterOr8085Illegal(op: ZOpcode.Value, illegalTarget: ZRegister.Value, illegalOp: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] = param(allowAbsolute = false).map{
case (reg@(ZRegister.MEM_IX_D | ZRegister.MEM_IY_D), Some(e)) => (op, OneRegister(reg), Some(e), zero)
case (reg, _) if reg == illegalTarget && options.flag(CompilationFlag.EmitIntel8085Opcodes) && options.flag(CompilationFlag.EmitIllegals) =>
@ -237,6 +255,27 @@ case class Z80Parser(filename: String,
private val jumpConditionWithComma: P[ZRegisters] = (jumpCondition ~ "," ~/ HWS).?.map (_.getOrElse(NoRegisters))
private val jumpConditionWithoutComma: P[ZRegisters] = (jumpCondition ~/ HWS).?.map (_.getOrElse(NoRegisters))
private val indexHalves: Set[ZRegister.Value] = {
import ZRegister._
Set(IXH, IXL, IYH, IYL)
}
private val MEM_R: Set[ZRegister.Value] = {
import ZRegister._
Set(MEM_HL,MEM_IX_D,MEM_IY_D)
}
def invalidIllegalRegisters(r1: ZRegister.Value, r2: ZRegister.Value): Boolean = {
import ZRegister._
if (MEM_R(r1) && MEM_R(r2)) return true
if (!indexHalves(r1) && !indexHalves(r2)) return false
if (!options.flag(CompilationFlag.EmitIllegals) && !options.flag(CompilationFlag.EmitZ80Opcodes) && (indexHalves(r1) || indexHalves(r2))) return true
if ((r1 == H || r1 == L || r1 == MEM_IX_D || r1 == MEM_IY_D) && indexHalves(r2)) return true
if ((r2 == H || r2 == L || r2 == MEM_IX_D || r2 == MEM_IY_D) && indexHalves(r1)) return true
if ((r1 == IYH || r1 == IYL) && (r2 == IXH || r2 == IXL)) return true
if ((r1 == IXH || r1 == IXL) && (r2 == IYH || r2 == IYL)) return true
false // TODO
}
val zilogAsmInstruction: P[ExecutableStatement] = {
import ZOpcode._
for {
@ -449,7 +488,11 @@ case class Z80Parser(filename: String,
if options.flags(CompilationFlag.EmitSharpOpcodes) =>
(LD_HLDA, NoRegisters, None, zero)
case (r1, e1, (r2, e2)) => merge(LD, LD_16, skipTargetA = false)((r1, e1, r2, e2))
case (r1, e1, (r2, e2)) =>
if (invalidIllegalRegisters(r1,r2)) {
log.error("Invalid parameters for LD", Some(pos))
}
merge(LD, LD_16, skipTargetA = false)((r1, e1, r2, e2))
}
case "ADD" => (param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ param(allowAbsolute = false)).map {
@ -534,13 +577,73 @@ case class Z80Parser(filename: String,
case "RIM" => imm(RIM)
case "SIM" => imm(SIM)
case "EXAF" => imm(EX_AF_AF)
case "EXX" => imm(EXX)
case "NEG" => imm(NEG)
case "IM0" => P("").map(_ => (IM, NoRegisters, None, LiteralExpression(0, 1)))
case "IM1" => P("").map(_ => (IM, NoRegisters, None, LiteralExpression(1, 1)))
case "IM2" => P("").map(_ => (IM, NoRegisters, None, LiteralExpression(2, 1)))
case "RETI" => imm(RETI)
case "RETN" => imm(RETN)
case "RLD" => imm(RLD)
case "RRD" => imm(RRD)
case "INI" => imm(INI)
case "INIR" => imm(INIR)
case "IND" => imm(IND)
case "INDR" => imm(INDR)
case "OUTI" => imm(OUTI)
case "OUTIR" => imm(OUTIR)
case "OUTD" => imm(OUTD)
case "OUTDR" => imm(OUTDR)
case "LDI" => imm(LDI)
case "LDIR" => imm(LDIR)
case "LDD" => imm(LDD)
case "LDDR" => imm(LDDR)
case "CCI" => imm(CPI)
case "CCIR" => imm(CPIR)
case "CCD" => imm(CPD)
case "CCDR" => imm(CPDR)
case "RALR" => one8RegisterIntel(RL)
case "RARR" => one8RegisterIntel(RR)
case "RLCR" => one8RegisterIntel(RLC)
case "RRCR" => one8RegisterIntel(RRC)
case "SLAR" => one8RegisterIntel(SLA)
case "SLLR" => one8RegisterIntel(SLL)
case "SRAR" => one8RegisterIntel(SRA)
case "SRLR" => one8RegisterIntel(SRL)
case "RALX" => asmExpression.map(d => (RL, OneRegister(MEM_IX_D), Some(d), zero))
case "RARX" => asmExpression.map(d => (RR, OneRegister(MEM_IX_D), Some(d), zero))
case "RLCX" => asmExpression.map(d => (RLC, OneRegister(MEM_IX_D), Some(d), zero))
case "RRCX" => asmExpression.map(d => (RRC, OneRegister(MEM_IX_D), Some(d), zero))
case "SLAX" => asmExpression.map(d => (SLA, OneRegister(MEM_IX_D), Some(d), zero))
case "SLLX" => asmExpression.map(d => (SLL, OneRegister(MEM_IX_D), Some(d), zero))
case "SRAX" => asmExpression.map(d => (SRA, OneRegister(MEM_IX_D), Some(d), zero))
case "SRLX" => asmExpression.map(d => (SRL, OneRegister(MEM_IX_D), Some(d), zero))
case "RALY" => asmExpression.map(d => (RL, OneRegister(MEM_IY_D), Some(d), zero))
case "RARY" => asmExpression.map(d => (RR, OneRegister(MEM_IY_D), Some(d), zero))
case "RLCY" => asmExpression.map(d => (RLC, OneRegister(MEM_IY_D), Some(d), zero))
case "RRCY" => asmExpression.map(d => (RRC, OneRegister(MEM_IY_D), Some(d), zero))
case "SLAY" => asmExpression.map(d => (SLA, OneRegister(MEM_IY_D), Some(d), zero))
case "SLLY" => asmExpression.map(d => (SLL, OneRegister(MEM_IY_D), Some(d), zero))
case "SRAY" => asmExpression.map(d => (SRA, OneRegister(MEM_IY_D), Some(d), zero))
case "SRLY" => asmExpression.map(d => (SRL, OneRegister(MEM_IY_D), Some(d), zero))
case "HLT" => imm(HALT)
case "EI" => imm(EI)
case "DI" => imm(DI)
case "XCHG" => imm(EX_DE_HL)
case "XTHL" => P("").map(_ => (EX_SP, OneRegister(HL), None, zero))
case "XTIX" => P("").map(_ => (EX_SP, OneRegister(IX), None, zero))
case "XTIY" => P("").map(_ => (EX_SP, OneRegister(IY), None, zero))
case "SPHL" => P("").map(_ => (LD_16, TwoRegisters(SP, HL), None, zero))
case "SPIX" => P("").map(_ => (LD_16, TwoRegisters(SP, IX), None, zero))
case "SPIY" => P("").map(_ => (LD_16, TwoRegisters(SP, IY), None, zero))
case "PCHL" => P("").map(_ => (JP, z80.OneRegister(HL), None, zero))
case "PCIX" => P("").map(_ => (JP, z80.OneRegister(IX), None, zero))
case "PCIY" => P("").map(_ => (JP, z80.OneRegister(IY), None, zero))
case "MOV" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ intel8).map {
case (r1, r2) => (LD, TwoRegisters(r1, r2), None, zero)
@ -548,10 +651,38 @@ case class Z80Parser(filename: String,
case "LXI" => (intel16(false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, e) => (LD_16, TwoRegisters(r, IMM_16), None, e)
}
case "LXIX" => asmExpression.map {
case (e) => (LD_16, TwoRegisters(IX, IMM_16), None, e)
}
case "LXIY" => asmExpression.map {
case (e) => (LD_16, TwoRegisters(IY, IMM_16), None, e)
}
case "MVI" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, e) => (LD, TwoRegisters(r, IMM_8), None, e)
}
case "MVIX" => (asmExpression ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (e, d) => (LD, TwoRegisters(MEM_IX_D, IMM_8), Some(d), e)
}
case "MVIY" => (asmExpression ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (e, d) => (LD, TwoRegisters(MEM_IY_D, IMM_8), Some(d), e)
}
case "LDX" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(r, MEM_IX_D), Some(d), zero)
}
case "LDY" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(r, MEM_IY_D), Some(d), zero)
}
case "STX" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(MEM_IX_D, r), Some(d), zero)
}
case "STY" => (intel8 ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (r, d) => (LD, TwoRegisters(MEM_IY_D, r), Some(d), zero)
}
case "DAD" => intel16(false).map { r => (ADD_16, TwoRegisters(HL, r), None, zero)}
case "DADC" => intel16(false).map { r => (ADC_16, TwoRegisters(HL, r), None, zero)}
case "DSBC" => intel16(false).map { r => (SBC_16, TwoRegisters(HL, r), None, zero)}
case "DADX" => intel16(false).map { r => (ADD_16, TwoRegisters(IX, r), None, zero)}
case "DADY" => intel16(false).map { r => (ADD_16, TwoRegisters(IY, r), None, zero)}
case "STAX" => intel16(false).map {
case r@(ZRegister.BC | ZRegister.DE) => (LD, TwoRegisters(r, A), None, zero)
case _ =>
@ -565,19 +696,47 @@ case class Z80Parser(filename: String,
(NOP, NoRegisters, None, zero)
}
case "INX" => intel16(false).map { r => (INC_16, OneRegister(r), None, zero)}
case "INXIX" => P("").map(_ => (INC_16, OneRegister(IX), None, zero))
case "INXIY" => P("").map(_ => (INC_16, OneRegister(IY), None, zero))
case "DCX" => intel16(false).map { r => (DEC_16, OneRegister(r), None, zero)}
case "DCXIX" => P("").map(_ => (DEC_16, OneRegister(IX), None, zero))
case "DCXIY" => P("").map(_ => (DEC_16, OneRegister(IY), None, zero))
case "POP" => intel16(true).map { r => (POP, OneRegister(r), None, zero)}
case "POPIX" => P("").map(_ => (POP, OneRegister(IX), None, zero))
case "POPIY" => P("").map(_ => (POP, OneRegister(IY), None, zero))
case "PUSH" => intel16(true).map { r => (PUSH, OneRegister(r), None, zero)}
case "PUSHIX" => P("").map(_ => (PUSH, OneRegister(IX), None, zero))
case "PUSHIY" => P("").map(_ => (PUSH, OneRegister(IY), None, zero))
case "INR" => intel8.map { r => (INC, OneRegister(r), None, zero)}
case "INRX" => asmExpression.map { d => (INC, OneRegister(MEM_IX_D), Some(d), zero)}
case "INRY" => asmExpression.map { d => (INC, OneRegister(MEM_IY_D), Some(d), zero)}
case "DCR" => intel8.map { r => (DEC, OneRegister(r), None, zero)}
case "DCRX" => asmExpression.map { d => (DEC, OneRegister(MEM_IX_D), Some(d), zero)}
case "DCRY" => asmExpression.map { d => (DEC, OneRegister(MEM_IY_D), Some(d), zero)}
case "ADD" => intel8.map { r => (ADD, OneRegister(r), None, zero)}
case "ADDX" => asmExpression.map { d => (ADD, OneRegister(MEM_IX_D), Some(d), zero)}
case "ADDY" => asmExpression.map { d => (ADD, OneRegister(MEM_IY_D), Some(d), zero)}
case "SUB" => intel8.map { r => (SUB, OneRegister(r), None, zero)}
case "SUBX" => asmExpression.map { d => (SUB, OneRegister(MEM_IX_D), Some(d), zero)}
case "SUBY" => asmExpression.map { d => (SUB, OneRegister(MEM_IY_D), Some(d), zero)}
case "ADC" => intel8.map { r => (ADC, OneRegister(r), None, zero)}
case "ADCX" => asmExpression.map { d => (ADC, OneRegister(MEM_IX_D), Some(d), zero)}
case "ADCY" => asmExpression.map { d => (ADC, OneRegister(MEM_IY_D), Some(d), zero)}
case "SBB" => intel8.map { r => (SBC, OneRegister(r), None, zero)}
case "SBCX" => asmExpression.map { d => (SBC, OneRegister(MEM_IX_D), Some(d), zero)}
case "SBCY" => asmExpression.map { d => (SBC, OneRegister(MEM_IY_D), Some(d), zero)}
case "ORA" => intel8.map { r => (OR, OneRegister(r), None, zero)}
case "ORX" => asmExpression.map { d => (OR, OneRegister(MEM_IX_D), Some(d), zero)}
case "ORY" => asmExpression.map { d => (OR, OneRegister(MEM_IY_D), Some(d), zero)}
case "ANA" => intel8.map { r => (AND, OneRegister(r), None, zero)}
case "ANDX" => asmExpression.map { d => (AND, OneRegister(MEM_IX_D), Some(d), zero)}
case "ANDY" => asmExpression.map { d => (AND, OneRegister(MEM_IY_D), Some(d), zero)}
case "XRA" => intel8.map { r => (XOR, OneRegister(r), None, zero)}
case "XORX" => asmExpression.map { d => (XOR, OneRegister(MEM_IX_D), Some(d), zero)}
case "XORY" => asmExpression.map { d => (XOR, OneRegister(MEM_IY_D), Some(d), zero)}
case "CMP" => intel8.map { r => (CP, OneRegister(r), None, zero)}
case "CMPX" => asmExpression.map { d => (CP, OneRegister(MEM_IX_D), Some(d), zero)}
case "CMPY" => asmExpression.map { d => (CP, OneRegister(MEM_IY_D), Some(d), zero)}
case "ADI" => asmExpression.map { e => (ADD, OneRegister(IMM_8), None, e)}
case "ACI" => asmExpression.map { e => (ADC, OneRegister(IMM_8), None, e)}
case "SUI" => asmExpression.map { e => (SUB, OneRegister(IMM_8), None, e)}
@ -589,8 +748,22 @@ case class Z80Parser(filename: String,
case "SHLD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, HL), None, e)}
case "LHLD" => asmExpression.map { e => (LD_16, TwoRegisters(HL, MEM_ABS_16), None, e)}
case "LBCD" => asmExpression.map { e => (LD_16, TwoRegisters(BC, MEM_ABS_16), None, e)}
case "SBCD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, BC), None, e)}
case "LDED" => asmExpression.map { e => (LD_16, TwoRegisters(DE, MEM_ABS_16), None, e)}
case "SDED" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, DE), None, e)}
case "LSPD" => asmExpression.map { e => (LD_16, TwoRegisters(SP, MEM_ABS_16), None, e)}
case "SSPD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, SP), None, e)}
case "LIXD" => asmExpression.map { e => (LD_16, TwoRegisters(IX, MEM_ABS_16), None, e)}
case "SIXD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, IX), None, e)}
case "LIYD" => asmExpression.map { e => (LD_16, TwoRegisters(IY, MEM_ABS_16), None, e)}
case "SIYD" => asmExpression.map { e => (LD_16, TwoRegisters(MEM_ABS_16, IY), None, e)}
case "STA" => asmExpression.map { e => (LD, TwoRegisters(MEM_ABS_8, A), None, e)}
case "LDA" => asmExpression.map { e => (LD, TwoRegisters(A, MEM_ABS_8), None, e)}
case "LDAI" => P("").map(_ => (LD, TwoRegisters(A, I), None, zero))
case "LDAR" => P("").map(_ => (LD, TwoRegisters(A, R), None, zero))
case "STAI" => P("").map(_ => (LD, TwoRegisters(I, A), None, zero))
case "STAR" => P("").map(_ => (LD, TwoRegisters(R, A), None, zero))
case "RST" => asmExpression.map {
case LiteralExpression(value, _) if value >=0 && value <= 7=> (RST, NoRegisters, None, LiteralExpression(value * 8, 1))
case _ =>
@ -598,10 +771,14 @@ case class Z80Parser(filename: String,
(NOP, NoRegisters, None, zero)
}
case "INP" => intel8.map { r => (IN_C, OneRegister(r), None, zero) }
case "OUTP" => intel8.map { r => (OUT_C, OneRegister(r), None, zero) }
case "IN" => asmExpression.map { e => (IN_IMM, OneRegister(A), None, e) }
case "OUT" => asmExpression.map { e => (OUT_IMM, OneRegister(A), None, e) }
case "DJNZ" => asmExpression.map { e => (DJNZ, NoRegisters, None, e)}
case "JMP" => asmExpression.map { e => (JP, NoRegisters, None, e)}
case "JMPR" | "JR" => asmExpression.map { e => (JR, NoRegisters, None, e)}
case "JC" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.C), None, e)}
case "JZ" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.Z), None, e)}
case "JM" => asmExpression.map { e => (JP, IfFlagSet(ZFlag.S), None, e)}
@ -610,6 +787,14 @@ case class Z80Parser(filename: String,
case "JNZ" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.Z), None, e)}
case "JP" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.S), None, e)}
case "JPO" => asmExpression.map { e => (JP, IfFlagClear(ZFlag.P), None, e)}
case "JRC" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.C), None, e)}
case "JRZ" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.Z), None, e)}
case "JRM" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.S), None, e)}
case "JRPE" => asmExpression.map { e => (JR, IfFlagSet(ZFlag.P), None, e)}
case "JRNC" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.C), None, e)}
case "JRNZ" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.Z), None, e)}
case "JRP" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.S), None, e)}
case "JRPO" => asmExpression.map { e => (JR, IfFlagClear(ZFlag.P), None, e)}
case "RET" => imm(RET)
case "RC" => P("").map { _ => (RET, IfFlagSet(ZFlag.C), None, zero)}
@ -642,6 +827,18 @@ case class Z80Parser(filename: String,
case "SHLDE" | "SHLX" => imm(SHLX)
case "LHLDE" | "LHLX" =>imm(LHLX)
case "RES" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.RES_seq, "RES", pos)
case "SETB" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.SET_seq, "SETB", pos)
case "SET" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.SET_seq, "SET", pos)
case "BIT" => parseSingleBitOpWithIntelSyntax(ZOpcodeClasses.BIT_seq, "BIT", pos)
case "RESX" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.RES_seq, MEM_IX_D, "RESX", pos)
case "SETX" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.SET_seq, MEM_IX_D, "SETX", pos)
case "BITX" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.BIT_seq, MEM_IX_D, "BITX", pos)
case "RESY" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.RES_seq, MEM_IY_D, "RESY", pos)
case "SETY" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.SET_seq, MEM_IY_D, "SETY", pos)
case "BITY" => parseSingleBitIndexedOpWithIntelSyntax(ZOpcodeClasses.BIT_seq, MEM_IY_D, "BITY", pos)
case _ =>
log.error("Unsupported opcode " + opcode, Some(pos))
imm(NOP)
@ -652,6 +849,30 @@ case class Z80Parser(filename: String,
}
}
private def parseSingleBitOpWithIntelSyntax(OP_seq: IndexedSeq[ZOpcode.Value], opcodeString: String, pos: Position): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] = {
import ZOpcode.NOP
(param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ intel8).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), r2)
if n >= 0 && n <= 7 && r2 != ZRegister.MEM_BC && r2 != ZRegister.MEM_DE =>
(OP_seq(n.toInt), OneRegister(r2), None, zero)
case _ =>
log.error("Invalid parameters for " + opcodeString, Some(pos))
(NOP, NoRegisters, None, zero)
}
}
private def parseSingleBitIndexedOpWithIntelSyntax(OP_seq: IndexedSeq[ZOpcode.Value], memIndex: ZRegister.Value, opcodeString: String, pos: Position): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] = {
import ZOpcode.NOP
(param(allowAbsolute = false) ~ HWS ~ position("comma").map(_ => ()) ~ "," ~/ HWS ~ asmExpression).map {
case (ZRegister.IMM_8, Some(LiteralExpression(n, _)), e2)
if n >= 0 && n <= 7 =>
(OP_seq(n.toInt), OneRegister(memIndex), Some(e2), zero)
case _ =>
log.error("Invalid parameters for " + opcodeString, Some(pos))
(NOP, NoRegisters, None, zero)
}
}
val asmInstruction: P[ExecutableStatement] = if (useIntelSyntax) intelAsmInstruction else zilogAsmInstruction
private def imm(opcode: ZOpcode.Value): P[(ZOpcode.Value, ZRegisters, Option[Expression], Expression)] =

View File

@ -873,6 +873,281 @@ class Z80AssemblySuite extends FunSuite with Matchers {
""".stripMargin)
}
test("Extended I80 instructions (Intel syntax)") {
EmuUnoptimizedZ80Run(
"""
| #pragma intel_syntax
| asm void main () {
| ret
|
| reti
|
| jmpr main
| jrnz main
| jrz main
| jrnc main
| jrc main
| lspd 34
|
| rlcr b
| rlcr c
| rlcr d
| rlcr e
| rlcr h
| rlcr l
| rlcr m
| rlcr a
|
| rrcr b
| rrcr c
| rrcr d
| rrcr e
| rrcr h
| rrcr l
| rrcr m
| rrcr a
|
| ralr b
| ralr c
| ralr d
| ralr e
| ralr h
| ralr l
| ralr m
| ralr a
|
| rarr b
| rarr c
| rarr d
| rarr e
| rarr h
| rarr l
| rarr m
| rarr a
|
| slar b
| slar c
| slar d
| slar e
| slar h
| slar l
| slar m
| slar a
|
| srar b
| srar c
| srar d
| srar e
| srar h
| srar l
| srar m
| srar a
|
| srlr b
| srlr c
| srlr d
| srlr e
| srlr h
| srlr l
| srlr m
| srlr a
|
| bit 1,a
| res 1,a
| setb 1,a
| set 1,a
| bit 1,m
| res 1,m
| setb 1,m
| set 1,m
|
| ret
| }
""".stripMargin)
}
test("Z80 instructions with IX (Intel syntax)") {
EmuUnoptimizedZ80Run(
"""
| #pragma intel_syntax
| asm void main () {
| ret
| addx 0
| adcx 0
| subx 0
| sbcx 0
| andx 0
| xorx 0
| orx 0
| cmpx 0
|
| rrcx 0
| rarx 0
| rlcx 0
| ralx 0
| slax 0
| srax 0
| srlx 0
| sllx 0
|
| popix
| pushix
| pop ix
| push ix
| dadx sp
| dadx ix
| dadx d
| dadx b
| inxix
| dcxix
| inx ix
| dcx ix
| lxix 3
| lixd 3
| sixd 3
| xtix
| pcix
| spix
| ldx a,0
| stx a,0
|
| ret
| }
""".stripMargin)
}
test("Z80 instructions with IY (Intel syntax)") {
EmuUnoptimizedZ80Run(
"""
| #pragma intel_syntax
| asm void main () {
| ret
| addy 0
| adcy 0
| suby 0
| sbcy 0
| andy 0
| xory 0
| ory 0
| cmpy 0
|
| rrcy 0
| rary 0
| rlcy 0
| raly 0
| slay 0
| sray 0
| srly 0
| slly 0
|
| popiy
| pushiy
| pop iy
| push iy
| dady sp
| dady iy
| dady d
| dady b
| inxiy
| dcxiy
| inx iy
| dcx iy
| lxiy 3
| liyd 3
| siyd 3
| xtiy
| pciy
| spiy
| ldy a,0
| sty a,0
|
| ret
| }
""".stripMargin)
}
test("Other Z80 instructions (Intel syntax)") {
EmuUnoptimizedZ80Run(
"""
| #pragma intel_syntax
| asm void main () {
| ret
|
| djnz main
| exaf
| exx
|
| sllr b
| sllr c
| sllr d
| sllr e
| sllr h
| sllr l
| sllr m
| sllr a
|
| inp b
| outp b
| dsbc b
| lbcd 34
| neg
| retn
| im0
| stai
| inp c
| outp c
| dadc b
| lbcd 7
| star
| inp d
| outp d
| dsbc d
| lded 55
| im1
| ldai
| inp e
| outp e
| dadc d
| lded 33
| im2
| ldar
|
| inp h
| outp h
| dsbc h
| rrd
| inp l
| outp l
| dadc h
| rld
| dsbc sp
| inp a
| outp a
| dadc sp
| lspd 345
|
| ldi
| cci
| ini
| outi
| ldd
| ccd
| ind
| outd
| ldir
| ccir
| inir
| outir
| lddr
| ccdr
| indr
| outdr
|
| ret
| }
""".stripMargin)
}
test("Gameboy instructions") {
EmuUnoptimizedSharpRun(
"""
@ -1012,4 +1287,36 @@ class Z80AssemblySuite extends FunSuite with Matchers {
""".stripMargin)
}
test("Z80 halves of index registers") {
EmuUnoptimizedZ80NextRun(
"""
| #pragma zilog_syntax
| asm void main () {
| ret
| inc ixh
| inc ixl
| inc iyh
| inc iyl
| dec ixh
| dec ixl
| dec iyh
| dec iyl
| ld a,ixh
| ld a,ixl
| ld iyh,a
| ld iyl,a
| add a,iyl
| adc a,iyl
| sub iyl
| sbc a,iyl
| or iyl
| xor iyl
| and iyl
| cp iyl
| ld ixh,0
| ret
| }
""".stripMargin)
}
}

View File

@ -77,7 +77,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
val log = TestErrorReporting.log
println(source)
val platform = EmuPlatform.get(cpu)
val extraFlags = Map(
var extraFlags = Map(
CompilationFlag.DangerousOptimizations -> true,
CompilationFlag.EnableInternalTestSyntax -> true,
CompilationFlag.InlineFunctions -> this.inline,
@ -87,6 +87,9 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio
CompilationFlag.EmitIllegals -> (cpu == millfork.Cpu.Z80 || cpu == millfork.Cpu.Intel8085 || cpu == millfork.Cpu.Z80Next),
CompilationFlag.EmitZ80NextOpcodes -> (cpu == millfork.Cpu.Z80Next),
CompilationFlag.LenientTextEncoding -> true)
if (source.contains("intel_syntax")) {
extraFlags += CompilationFlag.UseIntelSyntaxForOutput -> true
}
val options = CompilationOptions(platform, millfork.Cpu.defaultFlags(cpu).map(_ -> true).toMap ++ extraFlags, None, 0, Map(), EmuPlatform.textCodecRepository, JobContext(log, new LabelGenerator))
println(cpu)
println(options.flags.filter(_._2).keys.toSeq.sorted)