1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-05-31 18:41:30 +00:00
millfork/src/test/scala/millfork/test/WordMathSuite.scala
2019-09-20 18:33:41 +02:00

737 lines
20 KiB
Scala
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package millfork.test
import millfork.Cpu
import millfork.test.emu.{EmuBenchmarkRun, EmuCmosBenchmarkRun, EmuCrossPlatformBenchmarkRun, EmuUnoptimizedCrossPlatformRun}
import org.scalatest.{AppendedClues, FunSuite, Matchers}
/**
* @author Karol Stasiak
*/
class WordMathSuite extends FunSuite with Matchers with AppendedClues {
test("Word addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Intel8085, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| word a
| void main () {
| a = 640
| output = a
| output += a
| }
""".stripMargin)(_.readWord(0xc000) should equal(1280))
}
test("Cast word addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Intel8085, Cpu.Sharp, Cpu.Intel8086)("""
| byte output @$c000
| word a
| void main () {
| output = add(155, 166)
| }
| byte add(byte a, byte b) {
| return hi(word(a) + word(b))
| }
""".stripMargin)(_.readByte(0xc000) should equal(1))
}
test("Word subtraction") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Intel8085, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| word a
| void main () {
| a = 640
| output = 740
| output -= a
| }
""".stripMargin)(_.readWord(0xc000) should equal(100))
}
test("Word subtraction 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Intel8085, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| word a
| void main () {
| a = 640
| output = a
| output -= 400
| }
""".stripMargin)(_.readWord(0xc000) should equal(240))
}
test("Word subtraction 3") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Cmos, Cpu.Z80, Cpu.Intel8080, Cpu.Intel8085, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| word a
| void main () {
| a = 640
| f()
| output = a - f()
| }
| word f() {
| return 400
| }
""".stripMargin)(_.readWord(0xc000) should equal(240))
}
test("Byte-to-word addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| word pair
| void main () {
| pair = $A5A5
| pair.lo = 1
| output = 640
| output += pair.lo
| }
""".stripMargin)(_.readWord(0xc000) should equal(641))
}
test("Literal addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = 640
| output += -0050
| }
""".stripMargin)(_.readWord(0xc000) should equal(590))
}
test("Array element addition") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| word pair
| array b[2]
| void main () {
| byte i
| i = 1
| b[1] = 5
| pair = $A5A5
| pair.lo = 1
| output = 640
| output += b[i]
| }
""".stripMargin)(_.readWord(0xc000) should equal(645))
}
test("nesdev.com example") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| byte output @$c000
| byte tile @$C3A6
| array map [256] @$c300
| array b[2]
| void main () {
| tile = 77
| output = get(5, 6)
| }
| byte get(byte mx, byte my) {
| pointer p
| p = mx
| p <<= 5
| p += map
| return p[my]
| }
""".stripMargin) { m =>
m.readByte(0xc3a6) should equal(77)
}
}
test("nesdev.com example 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| byte output @$c000
| byte tile @$C3A6
| array map [256] @$c300
| array b[2]
| void main () {
| tile = 77
| output = get(5, 6)
| }
| byte get(byte mx, byte my) {
| return map[(word(mx) << 5) + my]
| }
""".stripMargin){ m =>
m.readByte(0xc3a6) should equal(77)
}
}
test("hi()/lo()") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| array output [7] @$c000
| void main () {
| output[0] = lo(33)
| output[1] = hi(33)
| output[2] = hi(w($504))
| output[3] = lo(w($209))
| output[4] = hi(s(-2))
| output[5] = lo(s(-2))
| output[6] = hi(b(200) + b(200))
| }
| word w(word w) {
| return w
| }
| byte b(byte b) {
| return b
| }
| sbyte s(sbyte s) {
| return s
| }
""".stripMargin){ m =>
m.readByte(0xc000) should equal(33)
m.readByte(0xc001) should equal(0)
m.readByte(0xc002) should equal(5)
m.readByte(0xc003) should equal(9)
m.readByte(0xc004) should equal(255)
m.readByte(0xc005) should equal(254)
m.readByte(0xc006) should equal(0)
}
}
test("Word addition 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| word v
| v = w($482)
| output = v + w($482) - 3
| }
| noinline word w(word w) {
| return w
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x901)
}
}
test("Word addition 3") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| byte c
| c = b($82)
| output = $482 + c
| }
| noinline byte b(byte b) {
| return b
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x504)
}
}
test("Word addition 4") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| word v
| word u
| v = w($308)
| u = w($601)
| barrier()
| output = u + v
| }
| noinline void barrier() { }
| noinline word w(word w) {
| return w
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x909)
}
}
test("Word addition 5") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Sixteen, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| word v
| word u
| v = $308
| output = $102
| barrier()
| output = (output.lo:output.hi) + v
| }
| noinline void barrier() { }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x509)
}
}
test("Word bit ops 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| word v
| v = w($692)
| output = (v & w($ca2)) | 3
| }
| noinline word w(word w) {
| return w
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x483)
}
}
test("Word shift") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| word v
| v = w($123)
| barrier()
| output = v << 1
| }
| noinline void barrier() { }
| noinline word w(word w) {
| return w
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(0x246)
}
}
test("Word shift 2") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = five()
| output <<= 5
| }
| noinline byte five() {
| return 5
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(5 * 32)
}
}
test("Word shift 3") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = five()
| output <<= 8
| }
| noinline byte five() {
| return 5
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(5 * 256)
}
}
test("Word shift 4") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = five()
| output <<= 7
| }
| noinline byte five() {
| return 5
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(5 * 128)
}
}
test("Word shift 5") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = five()
| output <<= 1
| output >>= 1
| }
| noinline byte five() {
| return 5
| }
""".stripMargin){ m =>
m.readWord(0xc000) should equal(5)
}
}
test("Word multiplication 5") {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = alot()
| output *= five()
| }
| noinline word alot() {
| return 4532
| }
| noinline byte five() {
| return 5
| }
| import zp_reg
""".stripMargin){ m =>
m.readWord(0xc000) should equal(4532 * 5)
}
}
test("Word multiplication optimization") {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)("""
| word output @$c000
| void main () {
| output = alot()
| output *= two()
| output *= four()
| }
| noinline word alot() {
| return 4532
| }
| inline byte four() {
| return 4
| }
| inline byte two() {
| return 2
| }
| import zp_reg
""".stripMargin){ m =>
m.readWord(0xc000) should equal(4532 * 8)
}
}
test("In-place word/byte multiplication") {
multiplyCase1(0, 0)
multiplyCase1(0, 1)
multiplyCase1(0, 2)
multiplyCase1(0, 5)
multiplyCase1(1, 0)
multiplyCase1(5, 0)
multiplyCase1(7, 0)
multiplyCase1(2, 5)
multiplyCase1(7, 2)
multiplyCase1(100, 2)
multiplyCase1(1000, 2)
multiplyCase1(54, 4)
multiplyCase1(2, 100)
multiplyCase1(500, 50)
multiplyCase1(4, 54)
}
private def multiplyCase1(x: Int, y: Int): Unit = {
EmuCrossPlatformBenchmarkRun(Cpu.Sixteen, Cpu.Mos, Cpu.Z80, Cpu.Intel8086)(
s"""
| import zp_reg
| word output @$$c000
| void main () {
| output = $x
| output *= $y
| }
""".
stripMargin)(_.readWord(0xc000) should equal(x * y) withClue s"= $x * $y")
}
test("Not-in-place word/byte multiplication") {
multiplyCase2(0, 0)
multiplyCase2(0, 1)
multiplyCase2(0, 2)
multiplyCase2(0, 5)
multiplyCase2(1, 0)
multiplyCase2(5, 0)
multiplyCase2(7, 0)
multiplyCase2(2, 5)
multiplyCase2(7, 2)
multiplyCase2(100, 2)
multiplyCase2(1000, 2)
multiplyCase2(54, 4)
multiplyCase2(2, 100)
multiplyCase2(500, 50)
multiplyCase2(4, 54)
multiplyCase2(1, 10)
multiplyCase2(12, 10)
multiplyCase2(123, 10)
multiplyCase2(1234, 10)
}
private def multiplyCase2(x: Int, y: Int): Unit = {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8086)(
s"""
| import zp_reg
| word output1 @$$c000
| word output2 @$$c004
| word tmp
| noinline void init() {
| tmp = $x
| }
| void main () {
| init()
| output1 = $y * tmp
| output2 = tmp * $y
| }
""".
stripMargin) { m =>
m.readWord(0xc000) should equal(x * y) withClue s"= $y * $x"
m.readWord(0xc004) should equal(x * y) withClue s"= $x * $y"
}
}
test("Word division 1") {
divisionCase1(0, 1)
divisionCase1(1, 1)
divisionCase1(1, 5)
divisionCase1(6, 5)
divisionCase1(420, 11)
divisionCase1(1210, 11)
divisionCase1(35000, 45)
divisionCase1(127, 127)
divisionCase1(128, 127)
divisionCase1(51462, 1)
}
test("Word division 1 large divisor") {
divisionCase1(127, 128)
divisionCase1(128, 128)
divisionCase1(45323, 250)
divisionCase1(4530, 200)
divisionCase1(9039, 247)
}
private def divisionCase1(x: Int, y: Int): Unit = {
EmuCrossPlatformBenchmarkRun(Cpu.Mos /*,Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086*/)(
s"""
| import zp_reg
| word output_q1 @$$c000
| word output_m1 @$$c002
| word output_q2 @$$c004
| word output_m2 @$$c006
| void main () {
| word a
| a = f()
| output_q1 = a / $y
| output_m1 = a %% $y
| output_q2 = a
| output_m2 = a
| output_q2 /= $y
| output_m2 %%= $y
| }
| word f() = $x
""".
stripMargin) { m =>
m.readWord(0xc000) should equal(x / y) withClue s"= $x / $y"
m.readWord(0xc002) should equal(x % y) withClue s"= $x %% $y"
m.readWord(0xc004) should equal(x / y) withClue s"= $x / $y"
m.readWord(0xc006) should equal(x % y) withClue s"= $x %% $y"
}
}
test("Word division 2") {
divisionCase2(0, 1)
divisionCase2(1, 1)
divisionCase2(2, 1)
divisionCase2(250, 1)
divisionCase2(0, 3)
divisionCase2(0, 5)
divisionCase2(1, 5)
divisionCase2(6, 5)
divisionCase2(73, 5)
divisionCase2(73, 8)
divisionCase2(127, 127)
divisionCase2(128, 127)
divisionCase2(75, 5)
divisionCase2(42, 11)
divisionCase2(420, 11)
divisionCase2(1210, 11)
divisionCase2(35000, 45)
divisionCase2(35000, 2)
divisionCase2(51462, 3)
divisionCase2(51462, 1)
}
test("Word division 2 large divisor") {
divisionCase2(127, 128)
divisionCase2(128, 128)
divisionCase2(45323, 250)
divisionCase2(4530, 200)
divisionCase2(9039, 247)
}
private def divisionCase2(x: Int, y: Int): Unit = {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(
s"""
| import zp_reg
| word output_q1 @$$c000
| word output_m1 @$$c002
| word output_q2 @$$c004
| word output_m2 @$$c006
| void main () {
| word a
| byte b
| a = f()
| b = g()
| output_q1 = a / b
| output_m1 = a %% b
| output_q2 = a
| output_m2 = a
| output_q2 /= b
| output_m2 %%= b
| }
| word f() = $x
| noinline byte g() = $y
""".
stripMargin) { m =>
m.readWord(0xc000) should equal(x / y) withClue s"= $x / $y"
m.readWord(0xc002) should equal(x % y) withClue s"= $x %% $y"
m.readWord(0xc004) should equal(x / y) withClue s"= $x / $y"
m.readWord(0xc006) should equal(x % y) withClue s"= $x %% $y"
}
}
test("Word division 4") {
divisionCase4(0, 2)
divisionCase4(1, 2)
divisionCase4(2, 2)
divisionCase4(250, 128)
divisionCase4(0, 4)
divisionCase4(0, 8)
divisionCase4(1, 4)
divisionCase4(6, 8)
divisionCase4(73, 16)
divisionCase4(2534, 2)
divisionCase4(2534, 32)
divisionCase4(35000, 2)
divisionCase4(51462, 4)
divisionCase4(51462, 1)
}
test("Word division 4 large divisor") {
divisionCase4(75, 128)
divisionCase4(42, 128)
divisionCase4(142, 128)
divisionCase4(9039, 247)
}
private def divisionCase4(x: Int, y: Int): Unit = {
EmuCrossPlatformBenchmarkRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080, Cpu.Sharp, Cpu.Intel8086)(
s"""
| import zp_reg
| word output_q1 @$$c000
| byte output_m1 @$$c002
| word output_q2 @$$c004
| word output_m2 @$$c006
| void main () {
| byte a
| output_q2 = g()
| output_m2 = g()
| a = f()
| output_q1 = $x / a
| output_m1 = $x %% a
| output_q2 /= a
| output_m2 %%= a
| }
| byte f() {return $y}
| noinline word g() {return $x}
""".
stripMargin) { m =>
m.readWord(0xc000) should equal(x / y) withClue s"= $x / $y"
m.readByte(0xc002) should equal(x % y) withClue s"= $x %% $y"
m.readWord(0xc004) should equal(x / y) withClue s"= $x / $y"
m.readByte(0xc006) should equal(x % y) withClue s"= $x %% $y"
}
}
test("Word/word multiplication 1") {
multiplyCaseWW1(0, 0)
multiplyCaseWW1(0, 5)
multiplyCaseWW1(7, 0)
multiplyCaseWW1(2, 5)
multiplyCaseWW1(7, 2)
multiplyCaseWW1(100, 2)
multiplyCaseWW1(1000, 2)
multiplyCaseWW1(2, 1000)
multiplyCaseWW1(1522, 1000)
multiplyCaseWW1(54, 4)
multiplyCaseWW1(35000, 9)
multiplyCaseWW1(43, 35000)
multiplyCaseWW1(53459, 48233)
}
private def multiplyCaseWW1(x: Int, y: Int): Unit = {
EmuUnoptimizedCrossPlatformRun(Cpu.Mos, Cpu.Z80, Cpu.Intel8080)(
s"""
| import zp_reg
| word output0 @$$c000
| word output1 @$$c002
| word output2 @$$c004
| void main () {
| output0 = $x
| output0 *= word($y)
| output1 = id($x) * id($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)"
}
}
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)"
}
}
test("Sign extension in subtraction") {
for {
i <- Seq(5324, 6453, 1500)
j <- Seq(0, 1, -1, -3, -7, -128, 127)
// i <- Seq(5324)
// j <- Seq(-1)
} {
EmuUnoptimizedCrossPlatformRun(/*Cpu.Mos, */Cpu.Z80)(
s"""
| word output0 @$$c000
| word output1 @$$c002
| word output2 @$$c004
| void main () {
| sbyte tmp
| output0 = $i
| output2 = $i
| tmp = $j
| memory_barrier()
| output1 = output0 - sbyte(${j&0xff})
| memory_barrier()
| output0 -= sbyte(${j&0xff})
| output2 -= tmp
| }
| noinline word id(word w) = w
""".
stripMargin){m =>
m.readWord(0xc000) should equal((i - j) & 0xffff) withClue s"= $i - $j (c000)"
m.readWord(0xc002) should equal((i - j) & 0xffff) withClue s"= $i - $j (c002)"
m.readWord(0xc004) should equal((i - j) & 0xffff) withClue s"= $i - $j (c004)"
}
}
}
}