1
0
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:
Karol Stasiak 2019-09-15 19:47:19 +02:00
parent f036de0ee1
commit 635870585e
11 changed files with 385 additions and 77 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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 "*'=" =>

View File

@ -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 {

View File

@ -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"),

View File

@ -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)"
}
}
}