mirror of
https://github.com/KarolS/millfork.git
synced 2025-01-11 12:29:46 +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 `-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.
|
* Added preliminary support for EasyFlash.
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ TODO
|
|||||||
* `/`, `%%`: unsigned division and unsigned modulo
|
* `/`, `%%`: unsigned division and unsigned modulo
|
||||||
`unsigned byte / unsigned byte` (zpreg)
|
`unsigned byte / unsigned byte` (zpreg)
|
||||||
`word / unsigned byte` (zpreg)
|
`word / unsigned byte` (zpreg)
|
||||||
|
`word / word` (zpreg)
|
||||||
`constant word / constant word`
|
`constant word / constant word`
|
||||||
`constant long / constant long`
|
`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
|
* `/=`, `%%=`: unsigned division and modulo in place
|
||||||
`mutable unsigned byte /= unsigned byte` (zpreg)
|
`mutable unsigned byte /= unsigned byte` (zpreg)
|
||||||
`mutable word /= unsigned byte` (zpreg)
|
`mutable word /= unsigned byte` (zpreg)
|
||||||
|
`mutable word /= word` (zpreg)
|
||||||
|
|
||||||
There are no `||=`, `&&=` or `>>>>=` operators.
|
There are no `||=`, `&&=` or `>>>>=` operators.
|
||||||
|
|
||||||
|
@ -110,43 +110,217 @@ __mul_u16u8u16_skip:
|
|||||||
|
|
||||||
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||||
noinline asm word __mul_u16u16u16() {
|
noinline asm word __mul_u16u16u16() {
|
||||||
ld hl,0
|
LD HL,0
|
||||||
ld a,16
|
LD A,16
|
||||||
__mul_u16u16u16_loop:
|
__mul_u16u16u16_loop:
|
||||||
add hl,hl
|
ADD HL,HL
|
||||||
rl e
|
RL E
|
||||||
rl d
|
RL D
|
||||||
jr nc,__mul_u16u16u16_skip
|
JR NC,__mul_u16u16u16_skip
|
||||||
add hl,bc
|
ADD HL,BC
|
||||||
__mul_u16u16u16_skip:
|
__mul_u16u16u16_skip:
|
||||||
dec a
|
DEC A
|
||||||
jr nz,__mul_u16u16u16_loop
|
JR NZ,__mul_u16u16u16_loop
|
||||||
ret
|
RET
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
noinline asm word __mul_u16u16u16() {
|
noinline asm word __mul_u16u16u16() {
|
||||||
ld hl,0
|
ld hl,0
|
||||||
call __mul_u16u16u16_q
|
CALL __mul_u16u16u16_q
|
||||||
call __mul_u16u16u16_q
|
CALL __mul_u16u16u16_q
|
||||||
call __mul_u16u16u16_q
|
CALL __mul_u16u16u16_q
|
||||||
jp __mul_u16u16u16_q
|
JP __mul_u16u16u16_q
|
||||||
}
|
}
|
||||||
noinline asm word __mul_u16u16u16_q(word hl) {
|
noinline asm word __mul_u16u16u16_q(word hl) {
|
||||||
call __mul_u16u16u16_s
|
CALL __mul_u16u16u16_s
|
||||||
call __mul_u16u16u16_s
|
CALL __mul_u16u16u16_s
|
||||||
call __mul_u16u16u16_s
|
CALL __mul_u16u16u16_s
|
||||||
jp __mul_u16u16u16_s
|
JP __mul_u16u16u16_s
|
||||||
}
|
}
|
||||||
noinline asm word __mul_u16u16u16_s(word hl) {
|
noinline asm word __mul_u16u16u16_s(word hl) {
|
||||||
add hl,hl
|
ADD HL,HL
|
||||||
ld a,e
|
LD A,E
|
||||||
add a,e
|
ADD A,E
|
||||||
ld e,a
|
LD E,A
|
||||||
ld a,d
|
LD A,D
|
||||||
adc a,d
|
ADC A,D
|
||||||
ld d,a
|
LD D,A
|
||||||
ret nc
|
RET NC
|
||||||
add hl,bc
|
ADD HL,BC
|
||||||
ret
|
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
|
? PHA
|
||||||
? LDA __reg+2
|
? LDA __reg+2
|
||||||
? PHA
|
? PHA
|
||||||
? TSX
|
|
||||||
#else
|
#else
|
||||||
? LDA __reg+2
|
? LDA __reg+2
|
||||||
? STA __reg+4
|
? STA __reg+4
|
||||||
@ -218,6 +217,7 @@ noinline asm word __divmod_u16u16u16u16() {
|
|||||||
#if ZPREG_SIZE < 6
|
#if ZPREG_SIZE < 6
|
||||||
? LDA #16
|
? LDA #16
|
||||||
? PHA
|
? PHA
|
||||||
|
? TSX
|
||||||
#else
|
#else
|
||||||
? LDX #16
|
? LDX #16
|
||||||
#endif
|
#endif
|
||||||
|
@ -187,10 +187,10 @@ object ReverseFlowAnalyzer {
|
|||||||
val readsA: Set[String] = Set("__mul_u8u8u8", "__mul_u16u8u16", "call")
|
val readsA: Set[String] = Set("__mul_u8u8u8", "__mul_u16u8u16", "call")
|
||||||
val readsB: Set[String] = Set("__mul_u16u16u16")
|
val readsB: Set[String] = Set("__mul_u16u16u16")
|
||||||
val readsC: 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 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")
|
val readsE: Set[String] = Set("__mul_u16u8u16", "call", "__mul_u16u16u16", "__divmod_u16u16u16u16")
|
||||||
val readsH: Set[String] = Set("__divmod_u16u8u16u8", "call")
|
val readsH: Set[String] = Set("__divmod_u16u8u16u8", "call", "__divmod_u16u16u16u16")
|
||||||
val readsL: Set[String] = Set("__divmod_u16u8u16u8", "call")
|
val readsL: Set[String] = Set("__divmod_u16u8u16u8", "call", "__divmod_u16u16u16u16")
|
||||||
|
|
||||||
val preservesH: Set[String] = Set("__mul_u8u8u8")
|
val preservesH: Set[String] = Set("__mul_u8u8u8")
|
||||||
val preservesL: Set[String] = Set("__mul_u8u8u8")
|
val preservesL: Set[String] = Set("__mul_u8u8u8")
|
||||||
|
@ -77,15 +77,24 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
|||||||
val lSize = lType.size
|
val lSize = lType.size
|
||||||
val rType = getExpressionType(ctx, params(1))
|
val rType = getExpressionType(ctx, params(1))
|
||||||
val rSize = rType.size
|
val rSize = rType.size
|
||||||
if (lSize > 2 || rSize > 2) {
|
if (lSize != 1 && lSize != 2) {
|
||||||
ctx.log.error("Long division not supported", params.head.position)
|
ctx.log.error("Long division not supported", params.head.position)
|
||||||
}
|
}
|
||||||
if (rSize > 1) {
|
if (rSize != 1 && rSize != 2) {
|
||||||
ctx.log.error("Division by words not supported", params.head.position)
|
ctx.log.error("Long division not supported", params.head.position)
|
||||||
}
|
}
|
||||||
if (lType.isSigned || rType.isSigned) {
|
if (inPlace) {
|
||||||
|
if (lSize == 2 && rSize == 1 && rType.isSigned) {
|
||||||
ctx.log.error("Signed division not supported", params.head.position)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def assertAllArithmeticBytes(msg: String, ctx: CompilationContext, params: List[Expression]): Unit = {
|
def assertAllArithmeticBytes(msg: String, ctx: CompilationContext, params: List[Expression]): Unit = {
|
||||||
|
@ -683,9 +683,58 @@ object PseudoregisterBuiltIns {
|
|||||||
load ++ calculate
|
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] = {
|
def compileUnsignedWordByByteDivision(ctx: CompilationContext, param1: Expression, param2: Expression, modulo: Boolean): List[AssemblyLine] = {
|
||||||
(AbstractExpressionCompiler.getExpressionType(ctx, param1).size,
|
(AbstractExpressionCompiler.getExpressionType(ctx, param1).size,
|
||||||
AbstractExpressionCompiler.getExpressionType(ctx, param2).size) match {
|
AbstractExpressionCompiler.getExpressionType(ctx, param2).size) match {
|
||||||
|
case (2 | 1, 2) => return compileWordWordDivision(ctx, Some(param1), param2, modulo)
|
||||||
case (2 | 1, 1) => // ok
|
case (2 | 1, 1) => // ok
|
||||||
case _ => ctx.log.fatal("Invalid code path", param2.position)
|
case _ => ctx.log.fatal("Invalid code path", param2.position)
|
||||||
}
|
}
|
||||||
|
@ -1092,27 +1092,29 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
case 2 =>
|
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 {
|
calculateAddressToAppropriatePointer(ctx, l, forWriting = true) match {
|
||||||
case Some((LocalVariableAddressViaHL, List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)))) =>
|
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.ldAbs8(addr, ZRegister.A),
|
||||||
ZLine.register(XOR, ZRegister.A),
|
ZLine.register(XOR, ZRegister.A),
|
||||||
ZLine.ldAbs8(addr+1, ZRegister.A)
|
ZLine.ldAbs8(addr+1, ZRegister.A)
|
||||||
)
|
)
|
||||||
case Some((lvo@LocalVariableAddressViaHL, code)) =>
|
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.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||||
ZLine.register(INC_16, ZRegister.HL),
|
ZLine.register(INC_16, ZRegister.HL),
|
||||||
ZLine.ldImm8(ZRegister.MEM_HL, 0)
|
ZLine.ldImm8(ZRegister.MEM_HL, 0)
|
||||||
)
|
)
|
||||||
case Some((lvo@LocalVariableAddressViaIX(offset), code)) =>
|
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.ldViaIx(offset, ZRegister.A),
|
||||||
ZLine.ld0ViaIx(offset + 1)
|
ZLine.ld0ViaIx(offset + 1)
|
||||||
)
|
)
|
||||||
case Some((lvo@LocalVariableAddressViaIY(offset), code)) =>
|
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.ldViaIy(offset, ZRegister.A),
|
||||||
ZLine.ld0ViaIy(offset + 1)
|
ZLine.ld0ViaIy(offset + 1)
|
||||||
)
|
)
|
||||||
@ -1125,7 +1127,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
case Some((lvo@LocalVariableAddressViaHL, code)) =>
|
case Some((lvo@LocalVariableAddressViaHL, code)) =>
|
||||||
code ++
|
code ++
|
||||||
stashHLIfChanged(ctx,
|
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))
|
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))
|
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)) =>
|
case Some((lvo@LocalVariableAddressViaIX(offset), code)) =>
|
||||||
code ++
|
code ++
|
||||||
Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = false) ++
|
Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo, rhsWord) ++
|
||||||
storeHLViaIX(ctx, offset, 2, signedSource = false)
|
storeHLViaIX(ctx, offset, 2, signedSource = false)
|
||||||
case Some((lvo@LocalVariableAddressViaIY(offset), code)) =>
|
case Some((lvo@LocalVariableAddressViaIY(offset), code)) =>
|
||||||
code ++
|
code ++
|
||||||
Z80Multiply.compileUnsignedWordByByteDivision(ctx, Left(lvo), r, modulo = false) ++
|
Z80Multiply.compileUnsignedWordDivision(ctx, Left(lvo), r, modulo, rhsWord) ++
|
||||||
storeHLViaIY(ctx, offset, 2, signedSource = false)
|
storeHLViaIY(ctx, offset, 2, signedSource = false)
|
||||||
case _ =>
|
case _ =>
|
||||||
ctx.log.error("Invalid left-hand side", l.position)
|
ctx.log.error("Invalid left-hand side", l.position)
|
||||||
@ -1153,14 +1155,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||||||
assertSizesForDivision(ctx, params, inPlace = false)
|
assertSizesForDivision(ctx, params, inPlace = false)
|
||||||
val (l, r, size) = assertArithmeticBinary(ctx, params)
|
val (l, r, size) = assertArithmeticBinary(ctx, params)
|
||||||
val modulo = f.functionName == "%%"
|
val modulo = f.functionName == "%%"
|
||||||
|
val rhsWord = getExpressionType(ctx, r).size == 2
|
||||||
size match {
|
size match {
|
||||||
case 1 =>
|
case 1 =>
|
||||||
targetifyA(ctx, target, Z80Multiply.compileUnsignedByteDivision(ctx, Right(l), r, modulo), isSigned = false)
|
targetifyA(ctx, target, Z80Multiply.compileUnsignedByteDivision(ctx, Right(l), r, modulo), isSigned = false)
|
||||||
case 2 =>
|
case 2 =>
|
||||||
if (modulo) {
|
if (modulo && !rhsWord) {
|
||||||
targetifyA(ctx, target, Z80Multiply.compileUnsignedWordByByteDivision(ctx, Right(l), r, modulo = true), isSigned = false)
|
targetifyA(ctx, target, Z80Multiply.compileUnsignedWordDivision(ctx, Right(l), r, modulo = true, rhsWord = false), isSigned = false)
|
||||||
} else {
|
} else {
|
||||||
targetifyHL(ctx, target, Z80Multiply.compileUnsignedWordByByteDivision(ctx, Right(l), r, modulo = false))
|
targetifyHL(ctx, target, Z80Multiply.compileUnsignedWordDivision(ctx, Right(l), r, modulo, rhsWord))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "*'=" =>
|
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 {
|
val pb = p match {
|
||||||
case Right(pp) => Z80ExpressionCompiler.compileToHL(ctx, pp)
|
case Right(pp) => Z80ExpressionCompiler.compileToHL(ctx, pp)
|
||||||
case Left(LocalVariableAddressViaHL) => List(
|
case Left(LocalVariableAddressViaHL) => List(
|
||||||
@ -136,14 +136,16 @@ object Z80Multiply {
|
|||||||
return pb
|
return pb
|
||||||
case Some(NumericConstant(1, _)) =>
|
case Some(NumericConstant(1, _)) =>
|
||||||
if (modulo) {
|
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 {
|
} else {
|
||||||
return pb
|
return pb
|
||||||
}
|
}
|
||||||
case Some(NumericConstant(qc, _)) if qc <= 255 && isPowerOfTwoUpTo15(qc) =>
|
case Some(NumericConstant(qc, _)) if qc <= 255 && isPowerOfTwoUpTo15(qc) =>
|
||||||
val count = Integer.numberOfTrailingZeros(qc.toInt)
|
val count = Integer.numberOfTrailingZeros(qc.toInt)
|
||||||
if (modulo) {
|
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 {
|
} else {
|
||||||
val extendedOps = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
val extendedOps = ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)
|
||||||
val shiftHL = if (extendedOps) {
|
val shiftHL = if (extendedOps) {
|
||||||
@ -164,8 +166,14 @@ object Z80Multiply {
|
|||||||
}
|
}
|
||||||
return pb ++ shiftHL
|
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 _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
|
if (!rhsWord) {
|
||||||
val qb = Z80ExpressionCompiler.compileToA(ctx, q)
|
val qb = Z80ExpressionCompiler.compileToA(ctx, q)
|
||||||
val load = if (qb.exists(Z80ExpressionCompiler.changesHL)) {
|
val load = if (qb.exists(Z80ExpressionCompiler.changesHL)) {
|
||||||
pb ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, qb) ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
pb ++ Z80ExpressionCompiler.stashHLIfChanged(ctx, qb) ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||||
@ -175,6 +183,26 @@ object Z80Multiply {
|
|||||||
pb ++ qb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
pb ++ qb ++ List(ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||||
}
|
}
|
||||||
load :+ ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u8u16u8").toAddress)
|
load :+ ZLine(ZOpcode.CALL, NoRegisters, ctx.env.get[FunctionInMemory]("__divmod_u16u8u16u8").toAddress)
|
||||||
|
} else {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,7 +244,7 @@ object Z80Multiply {
|
|||||||
compileUnsignedByteDivisionImpl(ctx, p, qq.toInt, modulo)
|
compileUnsignedByteDivisionImpl(ctx, p, qq.toInt, modulo)
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
val call = compileUnsignedWordByByteDivision(ctx, p, q, modulo = modulo)
|
val call = compileUnsignedWordDivision(ctx, p, q, modulo = modulo, rhsWord = false)
|
||||||
if (modulo) {
|
if (modulo) {
|
||||||
call
|
call
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,23 +17,30 @@ object UnusedFunctions extends NodeOptimization {
|
|||||||
("*=", 2, "__mul_u8u8u8"),
|
("*=", 2, "__mul_u8u8u8"),
|
||||||
("*=", 2, "__mul_u16u8u16"),
|
("*=", 2, "__mul_u16u8u16"),
|
||||||
("*=", 4, "__mul_u16u16u16"),
|
("*=", 4, "__mul_u16u16u16"),
|
||||||
("/=", 0, "__divmod_u16u8u16u8"),
|
|
||||||
("/", 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, "__mod_u8u8u8u8"),
|
||||||
("/", 2, "__mod_u16u8u16u8"),
|
("%%", 4, "__divmod_u16u16u16u16"),
|
||||||
("/=", 2, "__div_u16u8u16u8"),
|
("%%", 4, "__mod_u16u16u16u16"),
|
||||||
|
("%%=", 0, "__divmod_u16u8u16u8"),
|
||||||
|
("%%=", 2, "__mod_u16u8u16u8"),
|
||||||
|
("%%=", 2, "__mod_u8u8u8u8"),
|
||||||
|
("%%=", 4, "__divmod_u16u16u16u16"),
|
||||||
|
("%%=", 4, "__mod_u16u16u16u16"),
|
||||||
|
("/", 0, "__divmod_u16u8u16u8"),
|
||||||
("/", 2, "__div_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, "__adc_decimal"),
|
("+'=", 4, "__adc_decimal"),
|
||||||
("-'", 4, "__sub_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)"
|
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…
x
Reference in New Issue
Block a user