mirror of
https://github.com/KarolS/millfork.git
synced 2024-12-23 08:29:35 +00:00
Add unsigned 16-bit division
This commit is contained in:
parent
f036de0ee1
commit
635870585e
@ -6,7 +6,7 @@
|
||||
|
||||
* Added `-R` option for specifying extra commandline parameters for emulators.
|
||||
|
||||
* Added full 16-bit multiplication.
|
||||
* Added full 16-bit multiplication and unsigned division.
|
||||
|
||||
* Added preliminary support for EasyFlash.
|
||||
|
||||
|
@ -97,6 +97,7 @@ TODO
|
||||
* `/`, `%%`: unsigned division and unsigned modulo
|
||||
`unsigned byte / unsigned byte` (zpreg)
|
||||
`word / unsigned byte` (zpreg)
|
||||
`word / word` (zpreg)
|
||||
`constant word / constant word`
|
||||
`constant long / constant long`
|
||||
|
||||
@ -210,6 +211,7 @@ An expression of form `a[f()] += b` may call `f` an undefined number of times.
|
||||
* `/=`, `%%=`: unsigned division and modulo in place
|
||||
`mutable unsigned byte /= unsigned byte` (zpreg)
|
||||
`mutable word /= unsigned byte` (zpreg)
|
||||
`mutable word /= word` (zpreg)
|
||||
|
||||
There are no `||=`, `&&=` or `>>>>=` operators.
|
||||
|
||||
|
@ -110,43 +110,217 @@ __mul_u16u8u16_skip:
|
||||
|
||||
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
noinline asm word __mul_u16u16u16() {
|
||||
ld hl,0
|
||||
ld a,16
|
||||
LD HL,0
|
||||
LD A,16
|
||||
__mul_u16u16u16_loop:
|
||||
add hl,hl
|
||||
rl e
|
||||
rl d
|
||||
jr nc,__mul_u16u16u16_skip
|
||||
add hl,bc
|
||||
ADD HL,HL
|
||||
RL E
|
||||
RL D
|
||||
JR NC,__mul_u16u16u16_skip
|
||||
ADD HL,BC
|
||||
__mul_u16u16u16_skip:
|
||||
dec a
|
||||
jr nz,__mul_u16u16u16_loop
|
||||
ret
|
||||
DEC A
|
||||
JR NZ,__mul_u16u16u16_loop
|
||||
RET
|
||||
}
|
||||
#else
|
||||
noinline asm word __mul_u16u16u16() {
|
||||
ld hl,0
|
||||
call __mul_u16u16u16_q
|
||||
call __mul_u16u16u16_q
|
||||
call __mul_u16u16u16_q
|
||||
jp __mul_u16u16u16_q
|
||||
CALL __mul_u16u16u16_q
|
||||
CALL __mul_u16u16u16_q
|
||||
CALL __mul_u16u16u16_q
|
||||
JP __mul_u16u16u16_q
|
||||
}
|
||||
noinline asm word __mul_u16u16u16_q(word hl) {
|
||||
call __mul_u16u16u16_s
|
||||
call __mul_u16u16u16_s
|
||||
call __mul_u16u16u16_s
|
||||
jp __mul_u16u16u16_s
|
||||
CALL __mul_u16u16u16_s
|
||||
CALL __mul_u16u16u16_s
|
||||
CALL __mul_u16u16u16_s
|
||||
JP __mul_u16u16u16_s
|
||||
}
|
||||
noinline asm word __mul_u16u16u16_s(word hl) {
|
||||
add hl,hl
|
||||
ld a,e
|
||||
add a,e
|
||||
ld e,a
|
||||
ld a,d
|
||||
adc a,d
|
||||
ld d,a
|
||||
ret nc
|
||||
add hl,bc
|
||||
ret
|
||||
ADD HL,HL
|
||||
LD A,E
|
||||
ADD A,E
|
||||
LD E,A
|
||||
LD A,D
|
||||
ADC A,D
|
||||
LD D,A
|
||||
RET NC
|
||||
ADD HL,BC
|
||||
RET
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CPUFEATURE_Z80
|
||||
|
||||
// HL/DE = DE rem HL
|
||||
noinline asm word __divmod_u16u16u16u16() {
|
||||
LD A,H
|
||||
LD C,L
|
||||
LD HL,0
|
||||
LD B,16
|
||||
__divmod_u16u16u16u16_loop:
|
||||
#if CPUFEATURE_Z80_ILLEGALS
|
||||
SLL C
|
||||
#else
|
||||
SCF
|
||||
RL C
|
||||
#endif
|
||||
RLA
|
||||
ADC HL,HL
|
||||
SBC HL,DE
|
||||
JR NC,__divmod_u16u16u16u16_skip
|
||||
ADD HL,DE
|
||||
DEC C
|
||||
__divmod_u16u16u16u16_skip:
|
||||
DJNZ __divmod_u16u16u16u16_loop
|
||||
LD D,A
|
||||
LD E,C
|
||||
RET
|
||||
}
|
||||
|
||||
#elseif CPUFEATURE_8080
|
||||
|
||||
// HL/DE = DE rem HL
|
||||
// ABI could be changed, but the optimizer relies on it
|
||||
asm word __divmod_u16u16u16u16() {
|
||||
? LD B,D
|
||||
? LD C,E
|
||||
? LD DE,0
|
||||
#if OPTIMIZE_FOR_SPEED
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
#else
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
#endif
|
||||
? EX DE,HL
|
||||
? RET
|
||||
}
|
||||
|
||||
#if not(OPTIMIZE_FOR_SPEED)
|
||||
asm void __divmod_u16u16u16u16_q(){
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? JP __divmod_u16u16u16u16_u
|
||||
}
|
||||
#endif
|
||||
|
||||
noinline asm void __divmod_u16u16u16u16_u(){
|
||||
ADD HL,HL
|
||||
INC L
|
||||
|
||||
LD A,E
|
||||
RLA
|
||||
LD E,A
|
||||
LD A,D
|
||||
RLA
|
||||
LD D,A
|
||||
|
||||
LD A,E
|
||||
SUB C
|
||||
LD E,A
|
||||
LD A,D
|
||||
SBC A,B
|
||||
LD D,A
|
||||
RET NC
|
||||
|
||||
EX DE,HL
|
||||
ADD HL, BC
|
||||
EX DE,HL
|
||||
DEC L
|
||||
RET
|
||||
}
|
||||
|
||||
#elseif CPUFEATURE_GAMEBOY
|
||||
|
||||
// HL/DE = DE rem HL
|
||||
// ABI could be changed, but the optimizer relies on it
|
||||
asm word __divmod_u16u16u16u16() {
|
||||
? LD B,D
|
||||
? LD C,E
|
||||
? LD D,H
|
||||
? LD E,L
|
||||
? LD HL,0
|
||||
#if OPTIMIZE_FOR_SPEED
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? JP __divmod_u16u16u16u16_u
|
||||
#else
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
? CALL __divmod_u16u16u16u16_q
|
||||
? JP __divmod_u16u16u16u16_q
|
||||
#endif
|
||||
}
|
||||
|
||||
#if not(OPTIMIZE_FOR_SPEED)
|
||||
asm void __divmod_u16u16u16u16_q(){
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? CALL __divmod_u16u16u16u16_u
|
||||
? JP __divmod_u16u16u16u16_u
|
||||
}
|
||||
#endif
|
||||
|
||||
noinline asm void __divmod_u16u16u16u16_u() {
|
||||
SCF
|
||||
RL E
|
||||
RL D
|
||||
RL L
|
||||
RL H
|
||||
|
||||
LD A,L
|
||||
SUB C
|
||||
LD L,A
|
||||
LD A,H
|
||||
SBC A,B
|
||||
LD H,A
|
||||
RET NC
|
||||
|
||||
ADD HL,BC
|
||||
DEC E
|
||||
RET
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#warn No implementation of 16-bit division for this target
|
||||
|
||||
#endif
|
||||
|
@ -200,7 +200,6 @@ noinline asm word __divmod_u16u16u16u16() {
|
||||
? PHA
|
||||
? LDA __reg+2
|
||||
? PHA
|
||||
? TSX
|
||||
#else
|
||||
? LDA __reg+2
|
||||
? STA __reg+4
|
||||
@ -218,6 +217,7 @@ noinline asm word __divmod_u16u16u16u16() {
|
||||
#if ZPREG_SIZE < 6
|
||||
? LDA #16
|
||||
? PHA
|
||||
? TSX
|
||||
#else
|
||||
? LDX #16
|
||||
#endif
|
||||
|
@ -187,10 +187,10 @@ object ReverseFlowAnalyzer {
|
||||
val readsA: Set[String] = Set("__mul_u8u8u8", "__mul_u16u8u16", "call")
|
||||
val readsB: Set[String] = Set("__mul_u16u16u16")
|
||||
val readsC: Set[String] = Set("__mul_u16u16u16")
|
||||
val readsD: Set[String] = Set("__mul_u8u8u8","__mul_u16u8u16", "__divmod_u16u8u16u8", "call", "__mul_u16u16u16")
|
||||
val readsE: Set[String] = Set("__mul_u16u8u16", "call", "__mul_u16u16u16")
|
||||
val readsH: Set[String] = Set("__divmod_u16u8u16u8", "call")
|
||||
val readsL: Set[String] = Set("__divmod_u16u8u16u8", "call")
|
||||
val readsD: Set[String] = Set("__mul_u8u8u8","__mul_u16u8u16", "__divmod_u16u8u16u8", "call", "__mul_u16u16u16", "__divmod_u16u16u16u16")
|
||||
val readsE: Set[String] = Set("__mul_u16u8u16", "call", "__mul_u16u16u16", "__divmod_u16u16u16u16")
|
||||
val readsH: Set[String] = Set("__divmod_u16u8u16u8", "call", "__divmod_u16u16u16u16")
|
||||
val readsL: Set[String] = Set("__divmod_u16u8u16u8", "call", "__divmod_u16u16u16u16")
|
||||
|
||||
val preservesH: Set[String] = Set("__mul_u8u8u8")
|
||||
val preservesL: Set[String] = Set("__mul_u8u8u8")
|
||||
|
@ -77,14 +77,23 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
||||
val lSize = lType.size
|
||||
val rType = getExpressionType(ctx, params(1))
|
||||
val rSize = rType.size
|
||||
if (lSize > 2 || rSize > 2) {
|
||||
if (lSize != 1 && lSize != 2) {
|
||||
ctx.log.error("Long division not supported", params.head.position)
|
||||
}
|
||||
if (rSize > 1) {
|
||||
ctx.log.error("Division by words not supported", params.head.position)
|
||||
if (rSize != 1 && rSize != 2) {
|
||||
ctx.log.error("Long division not supported", params.head.position)
|
||||
}
|
||||
if (lType.isSigned || rType.isSigned) {
|
||||
ctx.log.error("Signed division not supported", params.head.position)
|
||||
if (inPlace) {
|
||||
if (lSize == 2 && rSize == 1 && rType.isSigned) {
|
||||
ctx.log.error("Signed division not supported", params.head.position)
|
||||
}
|
||||
} else {
|
||||
if (lSize == 2 && rSize == 1 && rType.isSigned) {
|
||||
ctx.log.error("Signed division not supported", params.head.position)
|
||||
}
|
||||
if (rSize == 2 && lSize == 1 && lType.isSigned) {
|
||||
ctx.log.error("Signed division not supported", params.head.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,9 +683,58 @@ object PseudoregisterBuiltIns {
|
||||
load ++ calculate
|
||||
}
|
||||
|
||||
def compileWordWordDivision(ctx: CompilationContext, param1OrRegister: Option[Expression], param2: Expression, modulo: Boolean): List[AssemblyLine] = {
|
||||
if (ctx.options.zpRegisterSize < 4) {
|
||||
ctx.log.error("Variable word-word division requires the zeropage pseudoregister of size at least 4", param1OrRegister.flatMap(_.position))
|
||||
return Nil
|
||||
}
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val load: List[AssemblyLine] = param1OrRegister match {
|
||||
case Some(param1) =>
|
||||
val code1 = MosExpressionCompiler.compile(ctx, param1, Some(w -> RegisterVariable(MosRegister.AX, w)), BranchSpec.None)
|
||||
val code2 = MosExpressionCompiler.compile(ctx, param2, Some(w -> RegisterVariable(MosRegister.AX, w)), BranchSpec.None)
|
||||
if (!usesRegLo(code2) && !usesRegHi(code2)) {
|
||||
code1 ++ List(AssemblyLine.zeropage(STA, reg), AssemblyLine.zeropage(STX, reg, 1)) ++ code2 ++ List(AssemblyLine.zeropage(STA, reg, 2), AssemblyLine.zeropage(STX, reg, 3))
|
||||
} else if (!usesReg2(code1) && !usesReg3(code1)) {
|
||||
code2 ++ List(AssemblyLine.zeropage(STA, reg, 2), AssemblyLine.zeropage(STX, reg, 3)) ++ code1 ++ List(AssemblyLine.zeropage(STA, reg), AssemblyLine.zeropage(STX, reg, 1))
|
||||
} else {
|
||||
code2 ++ List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(PHA)
|
||||
) ++ code1 ++ List(
|
||||
AssemblyLine.zeropage(STA, reg),
|
||||
AssemblyLine.zeropage(STX, reg, 1),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 3),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 2)
|
||||
)
|
||||
}
|
||||
case None =>
|
||||
val code2 = MosExpressionCompiler.compile(ctx, param2, Some(w -> RegisterVariable(MosRegister.AX, w)), BranchSpec.None)
|
||||
if (!usesRegLo(code2) && !usesRegHi(code2)) {
|
||||
List(AssemblyLine.zeropage(STA, reg), AssemblyLine.zeropage(STX, reg, 1)) ++ code2 ++ List(AssemblyLine.zeropage(STA, reg, 2), AssemblyLine.zeropage(STX, reg, 3))
|
||||
} else {
|
||||
List(AssemblyLine.implied(PHA), AssemblyLine.implied(TXA), AssemblyLine.implied(PHA)) ++ code2 ++ List(
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.zeropage(STX, reg, 3),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg)
|
||||
)
|
||||
}
|
||||
}
|
||||
val functionName = if(modulo) "__mod_u16u16u16u16" else "__div_u16u16u16u16"
|
||||
load :+ AssemblyLine.absoluteOrLongAbsolute(JSR, ctx.env.get[FunctionInMemory](functionName), ctx.options)
|
||||
}
|
||||
|
||||
def compileUnsignedWordByByteDivision(ctx: CompilationContext, param1: Expression, param2: Expression, modulo: Boolean): List[AssemblyLine] = {
|
||||
(AbstractExpressionCompiler.getExpressionType(ctx, param1).size,
|
||||
AbstractExpressionCompiler.getExpressionType(ctx, param2).size) match {
|
||||
case (2 | 1, 2) => return compileWordWordDivision(ctx, Some(param1), param2, modulo)
|
||||
case (2 | 1, 1) => // ok
|
||||
case _ => ctx.log.fatal("Invalid code path", param2.position)
|
||||
}
|
||||
|
@ -1092,27 +1092,29 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
Nil
|
||||
}
|
||||
case 2 =>
|
||||
if (f.functionName == "%%=") {
|
||||
val modulo = f.functionName == "%%="
|
||||
val rhsWord = getExpressionType(ctx, r).size == 2
|
||||
if (modulo && !rhsWord) {
|
||||
calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
|
||||
case Some((LocalVariableAddressViaHL, List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)))) =>
|
||||
Z80Multiply.compileUnsignedWordByByteDivision(ctx, Right(l), r, modulo = true) ++ List(
|
||||
Z80Multiply.compileUnsignedWordDivision(ctx, Right(l), r, modulo = true, rhsWord = false) ++ List(
|
||||
ZLine.ldAbs8(addr, ZRegister.A),
|
||||
ZLine.register(XOR, ZRegister.A),
|
||||
ZLine.ldAbs8(addr+1, ZRegister.A)
|
||||
)
|
||||
case Some((lvo@LocalVariableAddressViaHL, code)) =>
|
||||
code ++ stashHLIfChanged(ctx, Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = true)) ++ List(
|
||||
code ++ stashHLIfChanged(ctx, Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo = true, rhsWord = false)) ++ List(
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ldImm8(ZRegister.MEM_HL, 0)
|
||||
)
|
||||
case Some((lvo@LocalVariableAddressViaIX(offset), code)) =>
|
||||
code ++ Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = true) ++ List(
|
||||
code ++ Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo = true, rhsWord = false) ++ List(
|
||||
ZLine.ldViaIx(offset, ZRegister.A),
|
||||
ZLine.ld0ViaIx(offset + 1)
|
||||
)
|
||||
case Some((lvo@LocalVariableAddressViaIY(offset), code)) =>
|
||||
code ++ Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = true) ++ List(
|
||||
code ++ Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo = true, rhsWord = false) ++ List(
|
||||
ZLine.ldViaIy(offset, ZRegister.A),
|
||||
ZLine.ld0ViaIy(offset + 1)
|
||||
)
|
||||
@ -1125,7 +1127,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
case Some((lvo@LocalVariableAddressViaHL, code)) =>
|
||||
code ++
|
||||
stashHLIfChanged(ctx,
|
||||
Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(LocalVariableAddressViaHL), r, modulo = false) ++ (
|
||||
Z80Multiply.compileUnsignedWordDivision(ctx, Left(LocalVariableAddressViaHL), r, modulo, rhsWord) ++ (
|
||||
if (ctx.options.flags(CompilationFlag.EmitIntel8080Opcodes)) List(ZLine.implied(EX_DE_HL))
|
||||
else List(ZLine.ld8(ZRegister.E, ZRegister.L), ZLine.ld8(ZRegister.D, ZRegister.H))
|
||||
)
|
||||
@ -1137,11 +1139,11 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
)
|
||||
case Some((lvo@LocalVariableAddressViaIX(offset), code)) =>
|
||||
code ++
|
||||
Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = false) ++
|
||||
Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo, rhsWord) ++
|
||||
storeHLViaIX(ctx, offset, 2, signedSource = false)
|
||||
case Some((lvo@LocalVariableAddressViaIY(offset), code)) =>
|
||||
code ++
|
||||
Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = false) ++
|
||||
Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo, rhsWord) ++
|
||||
storeHLViaIY(ctx, offset, 2, signedSource = false)
|
||||
case _ =>
|
||||
ctx.log.error("Invalid left-hand side", l.position)
|
||||
@ -1153,14 +1155,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
||||
assertSizesForDivision(ctx, params, inPlace = false)
|
||||
val (l, r, size) = assertArithmeticBinary(ctx, params)
|
||||
val modulo = f.functionName == "%%"
|
||||
val rhsWord = getExpressionType(ctx, r).size == 2
|
||||
size match {
|
||||
case 1 =>
|
||||
targetifyA(ctx, target, Z80Multiply.compileUnsignedByteDivision(ctx, Right(l), r, modulo), isSigned = false)
|
||||
case 2 =>
|
||||
if (modulo) {
|
||||
targetifyA(ctx, target, Z80Multiply.compileUnsignedWordByByteDivision(ctx, Right(l), r, modulo = true), isSigned = false)
|
||||
if (modulo && !rhsWord) {
|
||||
targetifyA(ctx, target, Z80Multiply.compileUnsignedWordDivision(ctx, Right(l), r, modulo = true, rhsWord = false), isSigned = false)
|
||||
} else {
|
||||
targetifyHL(ctx, target, Z80Multiply.compileUnsignedWordByByteDivision(ctx, Right(l), r, modulo = false))
|
||||
targetifyHL(ctx, target, Z80Multiply.compileUnsignedWordDivision(ctx, Right(l), r, modulo, rhsWord))
|
||||
}
|
||||
}
|
||||
case "*'=" =>
|
||||
|
@ -116,9 +116,9 @@ object Z80Multiply {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate HL = p / q and A = p %% q
|
||||
* Calculate HL = p / q and/or (if rhsWord then HL else A) = p %% q
|
||||
*/
|
||||
def compileUnsignedWordByByteDivision(ctx: CompilationContext, p: Either[LocalVariableAddressOperand, Expression], q: Expression, modulo: Boolean): List[ZLine] = {
|
||||
def compileUnsignedWordDivision(ctx: CompilationContext, p: Either[LocalVariableAddressOperand, Expression], q: Expression, modulo: Boolean, rhsWord: Boolean): List[ZLine] = {
|
||||
val pb = p match {
|
||||
case Right(pp) => Z80ExpressionCompiler.compileToHL(ctx, pp)
|
||||
case Left(LocalVariableAddressViaHL) => List(
|
||||
@ -136,14 +136,16 @@ object Z80Multiply {
|
||||
return pb
|
||||
case Some(NumericConstant(1, _)) =>
|
||||
if (modulo) {
|
||||
return pb :+ ZLine.ldImm8(ZRegister.A, 0)
|
||||
if (rhsWord) return pb :+ ZLine.ldImm16(ZRegister.HL, 0)
|
||||
else return pb :+ ZLine.ldImm8(ZRegister.A, 0)
|
||||
} else {
|
||||
return pb
|
||||
}
|
||||
case Some(NumericConstant(qc, _)) if qc <= 255 && isPowerOfTwoUpTo15(qc) =>
|
||||
val count = Integer.numberOfTrailingZeros(qc.toInt)
|
||||
if (modulo) {
|
||||
return pb ++ List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.imm8(ZOpcode.AND, qc.toInt - 1))
|
||||
if (rhsWord) return pb ++ List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.imm8(ZOpcode.AND, qc.toInt - 1), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
|
||||
else return pb ++ List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.imm8(ZOpcode.AND, qc.toInt - 1))
|
||||
} else {
|
||||
val extendedOps = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||
val shiftHL = if (extendedOps) {
|
||||
@ -164,17 +166,43 @@ object Z80Multiply {
|
||||
}
|
||||
return pb ++ shiftHL
|
||||
}
|
||||
case Some(NumericConstant(256, _)) if rhsWord =>
|
||||
if (modulo) return pb ++ List(ZLine.ldImm8(ZRegister.H, 0))
|
||||
else return pb ++ List(ZLine.ld8(ZRegister.L, ZRegister.H), ZLine.ldImm8(ZRegister.H, 0))
|
||||
case Some(NumericConstant(qc, _)) if modulo && rhsWord && qc <= 0xffff && isPowerOfTwoUpTo15(qc) =>
|
||||
return pb ++ List(ZLine.ld8(ZRegister.A, ZRegister.L), ZLine.imm8(ZOpcode.AND, (qc.toInt >> 8) - 1), ZLine.ld8(ZRegister.H, ZRegister.A))
|
||||
case _ =>
|
||||
}
|
||||
val qb = Z80ExpressionCompiler.compileToA(ctx, q)
|
||||
val load = if (qb.exists(Z80ExpressionCompiler.changesHL)) {
|
||||
pb ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, qb) ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||
} else if (pb.exists(Z80ExpressionCompiler.changesDE)) {
|
||||
qb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A)) ++ Z80ExpressionCompiler.stashDEIfChanged(ctx, pb)
|
||||
if (!rhsWord) {
|
||||
val qb = Z80ExpressionCompiler.compileToA(ctx, q)
|
||||
val load = if (qb.exists(Z80ExpressionCompiler.changesHL)) {
|
||||
pb ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, qb) ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||
} else if (pb.exists(Z80ExpressionCompiler.changesDE)) {
|
||||
qb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A)) ++ Z80ExpressionCompiler.stashDEIfChanged(ctx, pb)
|
||||
} else {
|
||||
pb ++ qb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||
}
|
||||
load :+ ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u8u16u8").toAddress)
|
||||
} else {
|
||||
pb ++ qb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||
val qb = Z80ExpressionCompiler.compileToDE(ctx, q)
|
||||
val load = if (qb.exists(Z80ExpressionCompiler.changesHL)) {
|
||||
pb ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, qb)
|
||||
} else if (pb.exists(Z80ExpressionCompiler.changesDE)) {
|
||||
qb ++ Z80ExpressionCompiler.stashDEIfChanged(ctx, pb)
|
||||
} else {
|
||||
pb ++ qb
|
||||
}
|
||||
if (modulo) {
|
||||
load :+ ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u16u16u16").toAddress)
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
load ++ List(ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u16u16u16").toAddress), ZLine.implied(ZOpcode.EX_DE_HL))
|
||||
} else {
|
||||
load ++ List(
|
||||
ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u16u16u16").toAddress),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.D),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.E))
|
||||
}
|
||||
}
|
||||
load :+ ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u8u16u8").toAddress)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -216,7 +244,7 @@ object Z80Multiply {
|
||||
compileUnsignedByteDivisionImpl(ctx, p, qq.toInt, modulo)
|
||||
}
|
||||
case _ =>
|
||||
val call = compileUnsignedWordByByteDivision(ctx, p, q, modulo = modulo)
|
||||
val call = compileUnsignedWordDivision(ctx, p, q, modulo = modulo, rhsWord = false)
|
||||
if (modulo) {
|
||||
call
|
||||
} else {
|
||||
|
@ -17,23 +17,30 @@ object UnusedFunctions extends NodeOptimization {
|
||||
("*=", 2, "__mul_u8u8u8"),
|
||||
("*=", 2, "__mul_u16u8u16"),
|
||||
("*=", 4, "__mul_u16u16u16"),
|
||||
("/=", 0, "__divmod_u16u8u16u8"),
|
||||
("/", 0, "__divmod_u16u8u16u8"),
|
||||
("%%=", 0, "__divmod_u16u8u16u8"),
|
||||
("%%", 0, "__divmod_u16u8u16u8"),
|
||||
("%%=", 2, "__mod_u8u8u8u8"),
|
||||
("%%", 2, "__mod_u8u8u8u8"),
|
||||
("/=", 2, "__mod_u8u8u8u8"),
|
||||
("/", 2, "__mod_u8u8u8u8"),
|
||||
("/=", 2, "__div_u8u8u8u8"),
|
||||
("/", 2, "__div_u8u8u8u8"),
|
||||
("%%=", 2, "__mod_u16u8u16u8"),
|
||||
("%%", 2, "__mod_u16u8u16u8"),
|
||||
("/=", 2, "__mod_u16u8u16u8"),
|
||||
("/", 2, "__mod_u16u8u16u8"),
|
||||
("/=", 2, "__div_u16u8u16u8"),
|
||||
("%%", 2, "__mod_u8u8u8u8"),
|
||||
("%%", 4, "__divmod_u16u16u16u16"),
|
||||
("%%", 4, "__mod_u16u16u16u16"),
|
||||
("%%=", 0, "__divmod_u16u8u16u8"),
|
||||
("%%=", 2, "__mod_u16u8u16u8"),
|
||||
("%%=", 2, "__mod_u8u8u8u8"),
|
||||
("%%=", 4, "__divmod_u16u16u16u16"),
|
||||
("%%=", 4, "__mod_u16u16u16u16"),
|
||||
("/", 0, "__divmod_u16u8u16u8"),
|
||||
("/", 2, "__div_u16u8u16u8"),
|
||||
// TODO: u16u16u16u16 division
|
||||
("/", 2, "__div_u8u8u8u8"),
|
||||
("/", 2, "__mod_u16u8u16u8"),
|
||||
("/", 2, "__mod_u8u8u8u8"),
|
||||
("/", 4, "__div_u16u16u16u16"),
|
||||
("/", 4, "__divmod_u16u16u16u16"),
|
||||
("/=", 0, "__divmod_u16u8u16u8"),
|
||||
("/=", 2, "__div_u16u8u16u8"),
|
||||
("/=", 2, "__div_u8u8u8u8"),
|
||||
("/=", 2, "__mod_u16u8u16u8"),
|
||||
("/=", 2, "__mod_u8u8u8u8"),
|
||||
("/=", 4, "__div_u16u16u16u16"),
|
||||
("/=", 4, "__divmod_u16u16u16u16"),
|
||||
("+'", 4, "__adc_decimal"),
|
||||
("+'=", 4, "__adc_decimal"),
|
||||
("-'", 4, "__sub_decimal"),
|
||||
|
@ -664,4 +664,40 @@ class WordMathSuite extends FunSuite with Matchers with AppendedClues {
|
||||
m.readWord(0xc002) should equal((x * y) & 0xffff) withClue s"= $x * $y (c002)"
|
||||
}
|
||||
}
|
||||
|
||||
test("Word/word division 1") {
|
||||
divisionCaseWW1(0, 1)
|
||||
divisionCaseWW1(0, 1000)
|
||||
divisionCaseWW1(1, 1000)
|
||||
divisionCaseWW1(1, 1)
|
||||
divisionCaseWW1(1000, 1)
|
||||
divisionCaseWW1(1000, 1000)
|
||||
divisionCaseWW1(1000, 33)
|
||||
divisionCaseWW1(33000, 999)
|
||||
divisionCaseWW1(33000, 256)
|
||||
divisionCaseWW1(33000, 16)
|
||||
divisionCaseWW1(33000, 1024)
|
||||
}
|
||||
|
||||
private def divisionCaseWW1(x: Int, y: Int): Unit = {
|
||||
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp)(
|
||||
s"""
|
||||
| import zp_reg
|
||||
| word output0 @$$c000
|
||||
| word output1 @$$c002
|
||||
| void main () {
|
||||
| output0 = $x
|
||||
| memory_barrier()
|
||||
| output0 /= word($y)
|
||||
| output1 = $x
|
||||
| memory_barrier()
|
||||
| output1 %%= word($y)
|
||||
| }
|
||||
| noinline word id(word w) = w
|
||||
""".
|
||||
stripMargin){m =>
|
||||
m.readWord(0xc000) should equal((x / y) & 0xffff) withClue s"= $x / $y (c000)"
|
||||
m.readWord(0xc002) should equal((x % y) & 0xffff) withClue s"= $x %% $y (c002)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user