23 |
24 |
25 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | The MIT Licence (MIT)
2 |
3 | Copyright (c) 2019 Josh Bassett
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.idea/copyright/nullobject.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/main/scala/z80/Instructions.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (c) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | /** Instruction set */
41 | object Instructions {
42 | def NOP = 0x00
43 | def INC_B = 0x04
44 | def LD_B = 0x06
45 | def INC_C = 0x0c
46 | def INC_D = 0x14
47 | def INC_E = 0x1c
48 | def INC_H = 0x24
49 | def INC_L = 0x2c
50 | def INC_HL = 0x34
51 | def INC_A = 0x3c
52 | def LD_A = 0x3e
53 | def HALT = 0x76
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/scala/z80/Counter.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (c) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chisel3.internal.firrtl.Width
42 |
43 | class Counter(width: Width) extends Module {
44 | val io = IO(new Bundle {
45 | val n = Input(UInt(width))
46 | val enable = Input(Bool())
47 | val reset = Input(Bool())
48 | val value = Output(UInt(width))
49 | val wrap = Output(Bool())
50 | })
51 |
52 | private val valueReg = RegInit(0.U(width))
53 | private val wrap = valueReg === io.n - 1.U
54 |
55 | when(io.enable) { valueReg := valueReg + 1.U }
56 | when(io.reset || (io.enable && wrap)) { valueReg := 0.U }
57 |
58 | io.value := valueReg
59 | io.wrap := wrap
60 | }
61 |
62 | object Counter {
63 | def apply(n: UInt, enable: Bool = true.B, reset: Bool = false.B): (UInt, Bool) = {
64 | val counter = Module(new Counter(4.W))
65 | counter.io.n := n
66 | counter.io.enable := enable
67 | counter.io.reset := reset
68 | (counter.io.value, counter.io.wrap)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/scala/z80/Adder.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (c) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chisel3.util._
42 |
43 | /**
44 | * Performs an addition or subtraction operation with two 8-bit values,
45 | * including carry and half-carry flags.
46 | */
47 | class Adder extends Module {
48 | val io = IO(new Bundle {
49 | /** Assert the subtract flag to perform a subtract operation */
50 | val subtract = Input(Bool())
51 | /** Operand A */
52 | val a = Input(UInt(8.W))
53 | /** Operand B */
54 | val b = Input(UInt(8.W))
55 | /** Carry input */
56 | val carryIn = Input(Bool())
57 | /** The result of the addition/subtraction */
58 | val result = Output(UInt(8.W))
59 | /** Half-carry output */
60 | val halfCarryOut = Output(Bool())
61 | /** Carry output */
62 | val carryOut = Output(Bool())
63 | })
64 |
65 | // Invert the B operand for a subtract operation
66 | val b = Mux(io.subtract.asBool(), ~io.b, io.b).asUInt()
67 |
68 | // Low nibble
69 | val lowNibble = (io.a(3, 0) +& b(3, 0)) + (io.carryIn ^ io.subtract)
70 | val halfCarry = lowNibble(4)
71 |
72 | // High nibble
73 | val highNibble = (io.a(7, 4) +& b(7, 4)) + halfCarry
74 | val carry = highNibble(4)
75 |
76 | // Outputs
77 | io.result := highNibble(3, 0) ## lowNibble(3, 0)
78 | io.halfCarryOut := halfCarry ^ io.subtract
79 | io.carryOut := carry ^ io.subtract
80 | }
81 |
--------------------------------------------------------------------------------
/src/test/scala/z80/DecoderTest.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (dut) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chiseltest._
42 | import org.scalatest._
43 |
44 | class DecoderTest extends FlatSpec with ChiselScalatestTester with Matchers {
45 | behavior of "instructions"
46 |
47 | it should "NOP" in {
48 | test(new Decoder) { dut =>
49 | dut.io.instruction.poke(Instructions.NOP.U)
50 | dut.io.op.expect(Ops.NOP.U)
51 | dut.io.busIndex.expect(0.U)
52 | }
53 | }
54 |
55 | it should "INC A" in {
56 | test(new Decoder) { dut =>
57 | dut.io.instruction.poke(Instructions.INC_A.U)
58 | dut.io.op.expect(Ops.INC.U)
59 | dut.io.busIndex.expect(Reg8.A.U)
60 | }
61 | }
62 |
63 | it should "INC B" in {
64 | test(new Decoder) { dut =>
65 | dut.io.instruction.poke(Instructions.INC_B.U)
66 | dut.io.op.expect(Ops.INC.U)
67 | dut.io.busIndex.expect(Reg8.B.U)
68 | }
69 | }
70 |
71 | it should "LD A" in {
72 | test(new Decoder) { dut =>
73 | dut.io.instruction.poke(Instructions.LD_A.U)
74 | dut.io.mCycle.poke(0.U)
75 | dut.io.op.expect(Ops.NOP.U)
76 | dut.io.busIndex.expect(0.U)
77 | }
78 | }
79 |
80 | it should "HALT" in {
81 | test(new Decoder) { dut =>
82 | dut.io.instruction.poke(Instructions.HALT.U)
83 | dut.io.mCycle.poke(0.U)
84 | dut.io.op.expect(Ops.NOP.U)
85 | dut.io.busIndex.expect(0.U)
86 | dut.io.halt.expect(true.B)
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/test/scala/z80/CounterTest.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (dut) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chiseltest._
42 | import org.scalatest._
43 |
44 | class CounterTest extends FlatSpec with ChiselScalatestTester with Matchers {
45 | "counter" should "increment the value when ENABLE is asserted" in {
46 | test(new Counter(2.W)) { dut =>
47 | dut.io.n.poke(4.U)
48 | dut.clock.step()
49 | dut.io.enable.poke(true.B)
50 | dut.io.value.expect(0.U)
51 | dut.clock.step()
52 | dut.io.value.expect(1.U)
53 | }
54 | }
55 |
56 | it should "wrap when the value reaches the maximum" in {
57 | test(new Counter(2.W)) { dut =>
58 | dut.io.n.poke(4.U)
59 | dut.io.enable.poke(true.B)
60 | dut.io.value.expect(0.U)
61 | dut.clock.step(4)
62 | dut.io.value.expect(0.U)
63 | }
64 | }
65 |
66 | it should "assert the WRAP signal when the value reaches the maximum" in {
67 | test(new Counter(2.W)) { dut =>
68 | dut.io.n.poke(4.U)
69 | dut.io.enable.poke(true.B)
70 | dut.io.wrap.expect(false.B)
71 | dut.clock.step(3)
72 | dut.io.wrap.expect(true.B)
73 | }
74 | }
75 |
76 | it should "reset the value to zero when RESET is asserted" in {
77 | test(new Counter(2.W)) { dut =>
78 | dut.io.n.poke(4.U)
79 | dut.io.enable.poke(true.B)
80 | dut.clock.step()
81 | dut.io.reset.poke(true.B)
82 | dut.io.value.expect(1.U)
83 | dut.clock.step()
84 | dut.io.value.expect(0.U)
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/scala/z80/AdderTest.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (dut) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chiseltest._
42 | import org.scalatest._
43 |
44 | class AdderTest extends FlatSpec with ChiselScalatestTester with Matchers {
45 | behavior of "An add operation"
46 |
47 | it should "add the inputs (without carry)" in {
48 | test(new Adder) { dut =>
49 | dut.io.a.poke(3.U)
50 | dut.io.b.poke(2.U)
51 | dut.io.result.expect(5.U)
52 | dut.io.halfCarryOut.expect(false.B)
53 | dut.io.carryOut.expect(false.B)
54 | }
55 | }
56 |
57 | it should "add the inputs (with carry)" in {
58 | test(new Adder) { dut =>
59 | dut.io.a.poke(3.U)
60 | dut.io.b.poke(2.U)
61 | dut.io.carryIn.poke(true.B)
62 | dut.io.result.expect(6.U)
63 | dut.io.halfCarryOut.expect(false.B)
64 | dut.io.carryOut.expect(false.B)
65 | }
66 | }
67 |
68 | it should "set the half-carry flag" in {
69 | test(new Adder) { dut =>
70 | dut.io.a.poke(15.U)
71 | dut.io.b.poke(1.U)
72 | dut.io.result.expect(16.U)
73 | dut.io.halfCarryOut.expect(true.B)
74 | dut.io.carryOut.expect(false.B)
75 | }
76 | }
77 |
78 | it should "set the carry flag" in {
79 | test(new Adder) { dut =>
80 | dut.io.a.poke(255.U)
81 | dut.io.b.poke(1.U)
82 | dut.io.result.expect(0.U)
83 | dut.io.halfCarryOut.expect(true.B)
84 | dut.io.carryOut.expect(true.B)
85 | }
86 | }
87 |
88 | behavior of "A subtract operation"
89 |
90 | it should "subtract the inputs (without carry)" in {
91 | test(new Adder) { dut =>
92 | dut.io.subtract.poke(true.B)
93 | dut.io.a.poke(3.U)
94 | dut.io.b.poke(2.U)
95 | dut.io.result.expect(1.U)
96 | dut.io.halfCarryOut.expect(false.B)
97 | dut.io.carryOut.expect(false.B)
98 | }
99 | }
100 |
101 | it should "subtract the inputs (with carry)" in {
102 | test(new Adder) { dut =>
103 | dut.io.subtract.poke(true.B)
104 | dut.io.a.poke(3.U)
105 | dut.io.b.poke(2.U)
106 | dut.io.carryIn.poke(true.B)
107 | dut.io.result.expect(0.U)
108 | dut.io.halfCarryOut.expect(false.B)
109 | dut.io.carryOut.expect(false.B)
110 | }
111 | }
112 |
113 | it should "set the half-carry flag" in {
114 | test(new Adder) { dut =>
115 | dut.io.subtract.poke(true.B)
116 | dut.io.a.poke(16.U)
117 | dut.io.b.poke(1.U)
118 | dut.io.result.expect(15.U)
119 | dut.io.halfCarryOut.expect(true.B)
120 | dut.io.carryOut.expect(false.B)
121 | }
122 | }
123 |
124 | it should "set the carry flag" in {
125 | test(new Adder) { dut =>
126 | dut.io.subtract.poke(true.B)
127 | dut.io.a.poke(0.U)
128 | dut.io.b.poke(1.U)
129 | dut.io.result.expect(255.U)
130 | dut.io.halfCarryOut.expect(true.B)
131 | dut.io.carryOut.expect(true.B)
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/test/scala/z80/CPUTest.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (dut) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chiseltest._
42 | import org.scalatest._
43 |
44 | class CPUTest extends FlatSpec with ChiselScalatestTester with Matchers {
45 | behavior of "FSM"
46 |
47 | it should "assert M1 during T1 and T2" in {
48 | test(new CPU) { dut =>
49 | dut.io.m1.expect(true.B) // T1
50 | dut.clock.step()
51 | dut.io.m1.expect(true.B) // T2
52 | dut.clock.step()
53 | dut.io.m1.expect(false.B) // T3
54 | dut.clock.step()
55 | dut.io.m1.expect(false.B) // T4
56 | }
57 | }
58 |
59 | it should "fetch an instruction during T2" in {
60 | test(new CPU) { dut =>
61 | dut.io.mreq.expect(false.B)
62 | dut.io.rd.expect(false.B)
63 | dut.clock.step()
64 | dut.io.mreq.expect(true.B)
65 | dut.io.rd.expect(true.B)
66 | dut.clock.step()
67 | dut.io.mreq.expect(false.B)
68 | dut.io.rd.expect(false.B)
69 | dut.clock.step()
70 | dut.io.mreq.expect(false.B)
71 | dut.io.rd.expect(false.B)
72 | }
73 | }
74 |
75 | behavior of "program counter"
76 |
77 | it should "increment the program counter every four clock cycles" in {
78 | test(new CPU) { dut =>
79 | dut.io.addr.expect(0.U)
80 | dut.clock.step(4)
81 | dut.io.addr.expect(1.U)
82 | dut.clock.step(4)
83 | dut.io.addr.expect(2.U)
84 | }
85 | }
86 |
87 | "INC A" should "increment the A register" in {
88 | test(new CPU) { dut =>
89 | dut.io.din.poke(Instructions.INC_A.U)
90 | dut.io.debug.registers8(Reg8.A).expect(0.U)
91 | dut.clock.step(4)
92 | dut.io.debug.registers8(Reg8.A).expect(1.U)
93 | }
94 | }
95 |
96 | "INC B" should "increment the B register" in {
97 | test(new CPU) { dut =>
98 | dut.io.din.poke(Instructions.INC_B.U)
99 | dut.io.debug.registers8(Reg8.B).expect(0.U)
100 | dut.clock.step(4)
101 | dut.io.debug.registers8(Reg8.B).expect(1.U)
102 | }
103 | }
104 |
105 | "LD A" should "load a value into the A register" in {
106 | test(new CPU) { dut =>
107 | dut.io.din.poke(Instructions.LD_A.U)
108 | dut.clock.step(4)
109 | dut.io.din.poke(1.U)
110 | dut.io.debug.registers8(Reg8.A).expect(0.U)
111 | dut.clock.step(3)
112 | dut.io.debug.registers8(Reg8.A).expect(1.U)
113 | }
114 | }
115 |
116 | "LD B" should "load a value into the B register" in {
117 | test(new CPU) { dut =>
118 | dut.io.din.poke(Instructions.LD_B.U)
119 | dut.clock.step(4)
120 | dut.io.din.poke(1.U)
121 | dut.io.debug.registers8(Reg8.B).expect(0.U)
122 | dut.clock.step(3)
123 | dut.io.debug.registers8(Reg8.B).expect(1.U)
124 | }
125 | }
126 |
127 | "HALT" should "halt the CPU" in {
128 | test(new CPU) { dut =>
129 | dut.io.din.poke(Instructions.HALT.U)
130 | dut.io.halt.expect(false.B)
131 | dut.clock.step(4)
132 | dut.io.halt.expect(true.B)
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/main/scala/z80/Decoder.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (c) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 |
42 | sealed trait Cycle
43 |
44 | /** Represents a cycle where an opcode is fetched */
45 | case class OpcodeFetch(op: Int = Ops.NOP, busIndex: Option[Int] = None, halt: Boolean = false) extends Cycle
46 |
47 | /** Represents a cycle where a value is read from memory */
48 | case class MemRead(busIndex: Option[Int] = None, wr: Boolean = false) extends Cycle
49 |
50 | /** Represents a cycle where a value is written to memory */
51 | case class MemWrite(busIndex: Option[Int] = None, wr: Boolean = false) extends Cycle
52 |
53 | /**
54 | * Decodes the instruction register value into an operation and address bus
55 | * indexes.
56 | */
57 | class Decoder extends Module {
58 | val io = IO(new Bundle {
59 | /** Instruction */
60 | val instruction = Input(UInt(8.W))
61 | /** Time state */
62 | val tState = Input(UInt(3.W))
63 | /** Machine cycle */
64 | val mCycle = Input(UInt(3.W))
65 | /** Number of time states required for the current machine cycle */
66 | val tStates = Output(UInt(3.W))
67 | /** Number of machine cycles required for the current instruction */
68 | val mCycles = Output(UInt(3.W))
69 | /** Operation */
70 | val op = Output(UInt(5.W))
71 | /** Register bus index */
72 | val busIndex = Output(UInt(4.W))
73 | /** Asserted when the result should be stored to the accumulator */
74 | val wr = Output(Bool())
75 | /** Halt */
76 | val halt = Output(Bool())
77 | })
78 |
79 | /** Decodes the given microcode and sets the operation and register index outputs. */
80 | private def decodeCycle(cycle: Cycle) = {
81 | cycle match {
82 | case OpcodeFetch(op, busIndex, halt) =>
83 | io.op := op.U
84 | busIndex match {
85 | case Some(i) => io.busIndex := i.U
86 | case None =>
87 | }
88 | io.halt := halt.B
89 |
90 | case MemRead(busIndex, store) =>
91 | io.tStates := 3.U
92 | io.op := Ops.NOP.U
93 | busIndex match {
94 | case Some(i) => io.busIndex := i.U
95 | case None =>
96 | }
97 | io.wr := store.B
98 | }
99 | }
100 |
101 | // Default outputs
102 | io.tStates := 4.U
103 | io.mCycles := 1.U
104 | io.op := 0.U
105 | io.busIndex := 0.U
106 | io.wr := false.B
107 | io.halt := false.B
108 |
109 | // Decode the instructions
110 | for ((code, cycles) <- Decoder.instructions) {
111 | when(io.instruction === code.U) {
112 | io.mCycles := cycles.length.U
113 | for ((cycle, i) <- cycles.zipWithIndex) {
114 | when(io.mCycle === i.U) {
115 | decodeCycle(cycle)
116 | }
117 | }
118 | }
119 | }
120 | }
121 |
122 | object Decoder {
123 | import Instructions._
124 |
125 | val instructions = Seq(
126 | NOP -> Seq(OpcodeFetch()),
127 | INC_A -> Seq(OpcodeFetch(op = Ops.INC, busIndex = Some(Reg8.A))),
128 | INC_B -> Seq(OpcodeFetch(op = Ops.INC, busIndex = Some(Reg8.B))),
129 | LD_A -> Seq(OpcodeFetch(), MemRead(busIndex = Some(Reg8.A), wr = true)),
130 | LD_B -> Seq(OpcodeFetch(), MemRead(busIndex = Some(Reg8.B), wr = true)),
131 | HALT -> Seq(OpcodeFetch(halt = true))
132 | )
133 | }
134 |
--------------------------------------------------------------------------------
/project/scalastyle-config.xml:
--------------------------------------------------------------------------------
1 |
2 | Scalastyle standard configuration
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | No lines ending with a ;
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | |\|\||&&|:=|<>|<=|>=|!=|===|<<|>>|##|unary_(~|\-%?|!))$]]>
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/project/scalastyle-test-config.xml:
--------------------------------------------------------------------------------
1 |
2 | Scalastyle configuration for Chisel3 unit tests
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | No lines ending with a ;
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | |\|\||&&|:=|<>|<=|>=|!=|===|<<|>>|##|unary_(~|\-%?|!))$]]>
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/main/scala/z80/CPU.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (c) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chisel3.util.{is, _}
42 |
43 | /** 8-bit registers */
44 | object Reg8 {
45 | val A = 0; val F = 1
46 | val B = 2; val C = 3
47 | val D = 4; val E = 5
48 | val H = 6; val L = 7
49 | }
50 |
51 | /** 16-bit registers */
52 | object Reg16 {
53 | val AF = 0
54 | val BC = 1
55 | val DE = 2
56 | val HL = 3
57 | val IX = 4
58 | val IY = 5
59 | val SP = 6
60 | val PC = 7
61 | }
62 |
63 | /** Z80 CPU */
64 | class CPU extends Module {
65 | val io = IO(new Bundle {
66 | /** Memory request */
67 | val mreq = Output(Bool())
68 | /** Read */
69 | val rd = Output(Bool())
70 | /** Write */
71 | val wr = Output(Bool())
72 | /** Halt state */
73 | val halt = Output(Bool())
74 | /** Address bus */
75 | val addr = Output(UInt(CPU.ADDR_WIDTH.W))
76 | /** Data input */
77 | val din = Input(UInt(CPU.DATA_WIDTH.W))
78 | /** Data output */
79 | val dout = Output(UInt(CPU.DATA_WIDTH.W))
80 | /** M1 cycle */
81 | val m1 = Output(Bool())
82 | /** Debug output */
83 | val debug = new Bundle {
84 | val tState = Output(UInt(2.W))
85 | val mCycle = Output(UInt(2.W))
86 | val registers8 = Output(Vec(CPU.NUM_REG_8, UInt(8.W)))
87 | val registers16 = Output(Vec(CPU.NUM_REG_16, UInt(16.W)))
88 | }
89 | })
90 |
91 | // Time state counter
92 | val tStates = Wire(UInt(3.W))
93 | val (tStateCounter, tStateWrap) = Counter(tStates)
94 |
95 | // Machine cycle counter
96 | val mCycles = Wire(UInt(3.W))
97 | val (mCycleCounter, mCycleWrap) = Counter(mCycles, enable = tStateWrap)
98 |
99 | // Program counter register
100 | val pcReg = RegInit(0.U(CPU.ADDR_WIDTH.W))
101 |
102 | // Instruction register
103 | val instructionReg = RegInit(0.U(CPU.DATA_WIDTH.W))
104 |
105 | // Data input register
106 | val dinReg = RegInit(0.U(CPU.DATA_WIDTH.W))
107 |
108 | // Halt register
109 | val haltReg = RegInit(false.B)
110 |
111 | // Register files
112 | val registers16 = RegInit(VecInit(Seq.fill(CPU.NUM_REG_16) { 0.U(16.W) }))
113 | val registers8 = RegInit(VecInit(registers16.take(CPU.NUM_REG_8/2).flatMap { r => Seq(r(15, 8), r(7, 0)) }))
114 |
115 | // Instruction decoder
116 | val decoder = Module(new Decoder)
117 | decoder.io.instruction := instructionReg
118 | decoder.io.tState := tStateCounter
119 | decoder.io.mCycle := mCycleCounter
120 | tStates := decoder.io.tStates
121 | mCycles := decoder.io.mCycles
122 |
123 | // Arithmetic logic unit
124 | val alu = Module(new ALU)
125 | alu.io.op := decoder.io.op
126 | alu.io.a := registers8(decoder.io.busIndex)
127 | alu.io.b := registers8(Reg8.A.U)
128 | alu.io.flagsIn := registers8(Reg8.F)
129 |
130 | // Default outputs
131 | io.mreq := false.B
132 | io.rd := false.B
133 | io.wr := false.B
134 | io.halt := haltReg
135 | io.addr := 0.U
136 | io.dout := 0.U
137 | io.m1 := false.B
138 | io.debug.tState := tStateCounter
139 | io.debug.mCycle := mCycleCounter
140 | io.debug.registers8 := registers8
141 | io.debug.registers16 := registers16
142 |
143 | printf(p"T: $tStateCounter ($tStates), M: $mCycleCounter ($mCycles), PC: $pcReg, IR: $instructionReg, dinReg: $dinReg\n")
144 |
145 | switch(tStateCounter) {
146 | is(0.U) {
147 | // Increment program counter
148 | pcReg := pcReg + 1.U
149 |
150 | // Place program counter on the address bus
151 | io.addr := pcReg
152 |
153 | // Assert M1 during first machine cycle
154 | io.m1 := mCycleCounter === 0.U
155 | }
156 |
157 | is(1.U) {
158 | // Fetch instruction
159 | io.mreq := true.B
160 | io.rd := true.B
161 |
162 | // Assert M1 during first machine cycle
163 | io.m1 := mCycleCounter === 0.U
164 | }
165 |
166 | is(2.U) {
167 | // Latch an instruction only during first machine cycle. If the CPU is halted
168 | // then a NOP is forced.
169 | when(mCycleCounter === 0.U) {
170 | instructionReg := Mux(haltReg, Instructions.NOP.U, io.din)
171 | }
172 |
173 | // Write the data input to the register bus.
174 | when(decoder.io.wr) {
175 | registers8(decoder.io.busIndex) := io.din
176 | }
177 | }
178 |
179 | is(3.U) {
180 | // Write the result from the ALU to the register bus
181 | when(!decoder.io.wr) {
182 | registers8(decoder.io.busIndex) := alu.io.result
183 | }
184 |
185 | // Write the flags from the ALU to the F register
186 | registers8(Reg8.F) := alu.io.flagsOut
187 |
188 | // Set halt register
189 | haltReg := haltReg || decoder.io.halt
190 | }
191 | }
192 | }
193 |
194 | object CPU {
195 | val ADDR_WIDTH = 16
196 | val DATA_WIDTH = 8
197 | val NUM_REG_8 = 8
198 | val NUM_REG_16 = 8
199 | }
200 |
--------------------------------------------------------------------------------
/src/main/scala/z80/ALU.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (c) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chisel3.util._
42 |
43 | /** Operations */
44 | object Ops {
45 | val NOP = 0x00
46 | val ADD = 0x01
47 | val ADC = 0x02
48 | val SUB = 0x03
49 | val SBC = 0x04
50 | val CP = 0x05
51 | val AND = 0x06
52 | val OR = 0x07
53 | val XOR = 0x08
54 | val INC = 0x09
55 | val DEC = 0x0a
56 | val BIT = 0x0b
57 | val SET = 0x0c
58 | val RES = 0x0d
59 | val RL = 0x0e
60 | val RLC = 0x0f
61 | val RR = 0x10
62 | val RRC = 0x11
63 | val SLA = 0x12
64 | val SLL = 0x13
65 | val SRA = 0x14
66 | val SRL = 0x15
67 | val RLD = 0x16
68 | val RRD = 0x17
69 | }
70 |
71 | /** Processor flags */
72 | class Flags extends Bundle {
73 | val sign = Bool()
74 | val zero = Bool()
75 | val unused2 = Bool()
76 | val halfCarry = Bool()
77 | val unused1 = Bool()
78 | val overflow = Bool()
79 | val subtract = Bool()
80 | val carry = Bool()
81 | }
82 |
83 | /**
84 | * The 8-bit arithmetic and logical instructions of the CPU are executed in the
85 | * Arithmetic Logic Unit (ALU).
86 | */
87 | class ALU extends Module {
88 | val io = IO(new Bundle {
89 | /** Operation */
90 | val op = Input(UInt(5.W))
91 | /** Operand A */
92 | val a = Input(UInt(8.W))
93 | /** Operand B */
94 | val b = Input(UInt(8.W))
95 | /** Input flags */
96 | val flagsIn = Input(Bits(8.W))
97 | /** The result of the operation */
98 | val result = Output(UInt(8.W))
99 | /** Output flags */
100 | val flagsOut = Output(Bits(8.W))
101 | })
102 |
103 | /** Calculates the bitmask for a given number. */
104 | private def bitmask(n: UInt) = (1.U << n).asUInt()
105 |
106 | val result = WireDefault(0.U(8.W))
107 |
108 | // Flags
109 | val flagsIn = io.flagsIn.asTypeOf(new Flags)
110 | val flagsOut = Wire(new Flags)
111 |
112 | val overflow = (io.a(7) && io.b(7) && !result(7)) || (!io.a(7) && !io.b(7) && result(7))
113 | val parity = !result.xorR()
114 |
115 | // Arithmetic core for addition/subtraction
116 | val adder = Module(new Adder)
117 | adder.io.subtract := io.op === Ops.SUB.U || io.op === Ops.SBC.U || io.op === Ops.CP.U || io.op === Ops.DEC.U
118 | adder.io.a := io.a
119 | adder.io.b := io.b
120 | adder.io.carryIn := flagsIn.carry && (io.op === Ops.ADC.U || io.op === Ops.SBC.U)
121 |
122 | // Default flags
123 | flagsOut.sign := false.B
124 | flagsOut.zero := false.B
125 | flagsOut.unused2 := false.B
126 | flagsOut.halfCarry := false.B
127 | flagsOut.unused1 := false.B
128 | flagsOut.overflow := false.B
129 | flagsOut.subtract := adder.io.subtract
130 | flagsOut.carry := false.B
131 |
132 | switch (io.op) {
133 | is(Ops.ADD.U) {
134 | result := adder.io.result
135 | flagsOut.zero := result === 0.U
136 | flagsOut.sign := result(7)
137 | flagsOut.halfCarry := adder.io.halfCarryOut
138 | flagsOut.overflow := overflow
139 | flagsOut.carry := adder.io.carryOut
140 | }
141 |
142 | is(Ops.ADC.U) {
143 | result := adder.io.result
144 | flagsOut.zero := result === 0.U
145 | flagsOut.sign := result(7)
146 | flagsOut.halfCarry := adder.io.halfCarryOut
147 | flagsOut.overflow := overflow
148 | flagsOut.carry := adder.io.carryOut
149 | }
150 |
151 | is(Ops.SUB.U) {
152 | result := adder.io.result
153 | flagsOut.zero := result === 0.U
154 | flagsOut.sign := result(7)
155 | flagsOut.halfCarry := adder.io.halfCarryOut
156 | flagsOut.overflow := overflow
157 | flagsOut.carry := adder.io.carryOut
158 | }
159 |
160 | is(Ops.SBC.U) {
161 | result := adder.io.result
162 | flagsOut.zero := result === 0.U
163 | flagsOut.sign := result(7)
164 | flagsOut.halfCarry := adder.io.halfCarryOut
165 | flagsOut.overflow := overflow
166 | flagsOut.carry := adder.io.carryOut
167 | }
168 |
169 | is(Ops.CP.U) {
170 | result := adder.io.result
171 | flagsOut.zero := result === 0.U
172 | flagsOut.sign := result(7)
173 | flagsOut.halfCarry := adder.io.halfCarryOut
174 | flagsOut.overflow := overflow
175 | flagsOut.carry := adder.io.carryOut
176 | }
177 |
178 | is(Ops.AND.U) {
179 | result := io.a & io.b
180 | flagsOut.zero := result === 0.U
181 | flagsOut.sign := result(7)
182 | flagsOut.halfCarry := true.B
183 | flagsOut.overflow := parity
184 | }
185 |
186 | is(Ops.XOR.U) {
187 | result := io.a ^ io.b
188 | flagsOut.zero := result === 0.U
189 | flagsOut.sign := result(7)
190 | flagsOut.overflow := parity
191 | }
192 |
193 | is(Ops.OR.U) {
194 | result := io.a | io.b
195 | flagsOut.zero := result === 0.U
196 | flagsOut.sign := result(7)
197 | flagsOut.overflow := parity
198 | }
199 |
200 | is(Ops.INC.U) {
201 | adder.io.b := 1.U
202 | result := adder.io.result
203 | flagsOut.zero := result === 0.U
204 | flagsOut.sign := result(7)
205 | flagsOut.halfCarry := adder.io.halfCarryOut
206 | flagsOut.overflow := io.a === 127.U
207 | flagsOut.carry := flagsIn.carry
208 | }
209 |
210 | is(Ops.DEC.U) {
211 | adder.io.b := 1.U
212 | result := adder.io.result
213 | flagsOut.zero := result === 0.U
214 | flagsOut.sign := result(7)
215 | flagsOut.halfCarry := adder.io.halfCarryOut
216 | flagsOut.overflow := io.a === 128.U
217 | flagsOut.carry := flagsIn.carry
218 | }
219 |
220 | is(Ops.BIT.U) {
221 | result := io.a & bitmask(io.b(2, 0))
222 | flagsOut.zero := result === 0.U
223 | flagsOut.sign := result(7)
224 | flagsOut.halfCarry := true.B
225 | flagsOut.overflow := parity
226 | flagsOut.carry := flagsIn.carry
227 | }
228 |
229 | is(Ops.SET.U) {
230 | result := io.a | bitmask(io.b(2, 0))
231 | }
232 |
233 | is(Ops.RES.U) {
234 | result := io.a & (~bitmask(io.b(2, 0))).asUInt()
235 | }
236 |
237 | is(Ops.RL.U) {
238 | result := io.a(6, 0) ## flagsIn.carry
239 | flagsOut.zero := result === 0.U
240 | flagsOut.sign := result(7)
241 | flagsOut.overflow := parity
242 | flagsOut.carry := io.a(7)
243 | }
244 |
245 | is(Ops.RLC.U) {
246 | result := io.a(6, 0) ## io.a(7)
247 | flagsOut.zero := result === 0.U
248 | flagsOut.sign := result(7)
249 | flagsOut.overflow := parity
250 | flagsOut.carry := io.a(7)
251 | }
252 |
253 | is(Ops.RR.U) {
254 | result := flagsIn.carry ## io.a(7, 1)
255 | flagsOut.zero := result === 0.U
256 | flagsOut.sign := result(7)
257 | flagsOut.overflow := parity
258 | flagsOut.carry := io.a(0)
259 | }
260 |
261 | is(Ops.RRC.U) {
262 | result := io.a(0) ## io.a(7, 1)
263 | flagsOut.zero := result === 0.U
264 | flagsOut.sign := result(7)
265 | flagsOut.overflow := parity
266 | flagsOut.carry := io.a(0)
267 | }
268 |
269 | is(Ops.SLA.U) {
270 | result := io.a(6, 0) ## 0.U
271 | flagsOut.zero := result === 0.U
272 | flagsOut.sign := result(7)
273 | flagsOut.overflow := parity
274 | flagsOut.carry := io.a(7)
275 | }
276 |
277 | is(Ops.SLL.U) {
278 | result := io.a(6, 0) ## 1.U
279 | flagsOut.zero := result === 0.U
280 | flagsOut.sign := result(7)
281 | flagsOut.overflow := parity
282 | flagsOut.carry := io.a(7)
283 | }
284 |
285 | is(Ops.SRA.U) {
286 | result := io.a(7) ## io.a(7, 1)
287 | flagsOut.zero := result === 0.U
288 | flagsOut.sign := result(7)
289 | flagsOut.overflow := parity
290 | flagsOut.carry := io.a(0)
291 | }
292 |
293 | is(Ops.SRL.U) {
294 | result := 0.U ## io.a(7, 1)
295 | flagsOut.zero := result === 0.U
296 | flagsOut.sign := result(7)
297 | flagsOut.overflow := parity
298 | flagsOut.carry := io.a(0)
299 | }
300 |
301 | is(Ops.RLD.U) {
302 | result := io.a(7, 4) ## io.b(7, 4)
303 | flagsOut.zero := result === 0.U
304 | flagsOut.sign := result(7)
305 | flagsOut.overflow := parity
306 | }
307 |
308 | is(Ops.RRD.U) {
309 | result := io.a(7, 4) ## io.b(3, 0)
310 | flagsOut.zero := result === 0.U
311 | flagsOut.sign := result(7)
312 | flagsOut.overflow := parity
313 | }
314 | }
315 |
316 | // Outputs
317 | io.result := result
318 | io.flagsOut := flagsOut.asUInt()
319 | }
320 |
--------------------------------------------------------------------------------
/src/test/scala/z80/ALUTest.scala:
--------------------------------------------------------------------------------
1 | /*
2 | * __ __ __ __ __ __
3 | * /\ "-.\ \ /\ \/\ \ /\ \ /\ \
4 | * \ \ \-. \ \ \ \_\ \ \ \ \____ \ \ \____
5 | * \ \_\\"\_\ \ \_____\ \ \_____\ \ \_____\
6 | * \/_/ \/_/ \/_____/ \/_____/ \/_____/
7 | * ______ ______ __ ______ ______ ______
8 | * /\ __ \ /\ == \ /\ \ /\ ___\ /\ ___\ /\__ _\
9 | * \ \ \/\ \ \ \ __< _\_\ \ \ \ __\ \ \ \____ \/_/\ \/
10 | * \ \_____\ \ \_____\ /\_____\ \ \_____\ \ \_____\ \ \_\
11 | * \/_____/ \/_____/ \/_____/ \/_____/ \/_____/ \/_/
12 | *
13 | * https://joshbassett.info
14 | * https://twitter.com/nullobject
15 | * https://github.com/nullobject
16 | *
17 | * Copyright (dut) 2020 Josh Bassett
18 | *
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy
20 | * of this software and associated documentation files (the "Software"), to deal
21 | * in the Software without restriction, including without limitation the rights
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | * copies of the Software, and to permit persons to whom the Software is
24 | * furnished to do so, subject to the following conditions:
25 | *
26 | * The above copyright notice and this permission notice shall be included in all
27 | * copies or substantial portions of the Software.
28 | *
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | * SOFTWARE.
36 | */
37 |
38 | package z80
39 |
40 | import chisel3._
41 | import chiseltest._
42 | import org.scalatest._
43 |
44 | class ALUTest extends FlatSpec with ChiselScalatestTester with Matchers {
45 | // ALU I/O value object
46 | case class Value(a: UInt, b: UInt, result: UInt, flagsIn: UInt, flagsOut: UInt)
47 |
48 | def testALU(op: UInt, value: Value, alu: ALU) = {
49 | alu.io.op.poke(op)
50 | alu.io.a.poke(value.a)
51 | alu.io.b.poke(value.b)
52 | alu.io.flagsIn.poke(value.flagsIn)
53 | alu.io.result.expect(value.result)
54 | alu.io.flagsOut.expect(value.flagsOut)
55 | }
56 |
57 | "NOP" should "do nothing" in {
58 | val values = Seq(
59 | Value(1.U, 0.U, 0.U, "b0000_0000".U, "b0000_0000".U),
60 | Value(0.U, 1.U, 0.U, "b0000_0000".U, "b0000_0000".U)
61 | )
62 |
63 | for (value <- values) {
64 | test(new ALU) { testALU(Ops.NOP.U, value, _) }
65 | }
66 | }
67 |
68 | "ADD" should "add the operands" in {
69 | val values = Seq(
70 | Value(0.U, 0.U, 0.U, "b0000_0001".U, "b0100_0000".U),
71 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
72 | Value(0.U, 1.U, 1.U, "b0000_0000".U, "b0000_0000".U),
73 | Value(64.U, 64.U, 128.U, "b0000_0000".U, "b1000_0100".U),
74 | Value(255.U, 1.U, 0.U, "b0000_0000".U, "b0101_0001".U),
75 | )
76 |
77 | for (value <- values) {
78 | test(new ALU) { testALU(Ops.ADD.U, value, _) }
79 | }
80 | }
81 |
82 | "ADC" should "add the operands with carry" in {
83 | val values = Seq(
84 | Value(0.U, 0.U, 1.U, "b0000_0001".U, "b0000_0000".U),
85 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
86 | Value(0.U, 1.U, 1.U, "b0000_0000".U, "b0000_0000".U),
87 | Value(64.U, 64.U, 128.U, "b0000_0000".U, "b1000_0100".U),
88 | Value(255.U, 1.U, 0.U, "b0000_0000".U, "b0101_0001".U),
89 | )
90 |
91 | for (value <- values) {
92 | test(new ALU) { testALU(Ops.ADC.U, value, _) }
93 | }
94 | }
95 |
96 | "SUB" should "subtract the operands" in {
97 | val values = Seq(
98 | Value(0.U, 0.U, 0.U, "b0000_0001".U, "b0100_0010".U),
99 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0010".U),
100 | Value(0.U, 1.U, 255.U, "b0000_0000".U, "b1001_0111".U),
101 | Value(128.U, 128.U, 0.U, "b0000_0000".U, "b0100_0110".U),
102 | Value(255.U, 1.U, 254.U, "b0000_0000".U, "b1000_0010".U),
103 | )
104 |
105 | for (value <- values) {
106 | test(new ALU) { testALU(Ops.SUB.U, value, _) }
107 | }
108 | }
109 |
110 | "SBC" should "subtract the operands with carry" in {
111 | val values = Seq(
112 | Value(0.U, 0.U, 255.U, "b0000_0001".U, "b1001_0111".U),
113 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0010".U),
114 | Value(0.U, 1.U, 255.U, "b0000_0000".U, "b1001_0111".U),
115 | Value(128.U, 128.U, 0.U, "b0000_0000".U, "b0100_0110".U),
116 | Value(255.U, 1.U, 254.U, "b0000_0000".U, "b1000_0010".U),
117 | )
118 |
119 | for (value <- values) {
120 | test(new ALU) { testALU(Ops.SBC.U, value, _) }
121 | }
122 | }
123 |
124 | "CP" should "compare the operands" in {
125 | val values = Seq(
126 | Value(0.U, 0.U, 0.U, "b0000_0001".U, "b0100_0010".U),
127 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0010".U),
128 | Value(0.U, 1.U, 255.U, "b0000_0000".U, "b1001_0111".U),
129 | Value(128.U, 128.U, 0.U, "b0000_0000".U, "b0100_0110".U),
130 | Value(255.U, 1.U, 254.U, "b0000_0000".U, "b1000_0010".U),
131 | )
132 |
133 | for (value <- values) {
134 | test(new ALU) { testALU(Ops.CP.U, value, _) }
135 | }
136 | }
137 |
138 | "AND" should "logical AND the operands" in {
139 | val values = Seq(
140 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0101_0100".U),
141 | Value(1.U, 0.U, 0.U, "b0000_0000".U, "b0101_0100".U),
142 | Value(1.U, 1.U, 1.U, "b0000_0000".U, "b0001_0000".U),
143 | Value(255.U, 0.U, 0.U, "b0000_0000".U, "b0101_0100".U),
144 | Value(0.U, 255.U, 0.U, "b0000_0000".U, "b0101_0100".U),
145 | Value(255.U, 255.U, 255.U, "b0000_0000".U, "b1001_0100".U),
146 | )
147 |
148 | for (value <- values) {
149 | test(new ALU) { testALU(Ops.AND.U, value, _) }
150 | }
151 | }
152 |
153 | "OR" should "logical OR the operands" in {
154 | val values = Seq(
155 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
156 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
157 | Value(0.U, 1.U, 1.U, "b0000_0000".U, "b0000_0000".U),
158 | Value(1.U, 1.U, 1.U, "b0000_0000".U, "b0000_0000".U),
159 | Value(255.U, 0.U, 255.U, "b0000_0000".U, "b1000_0100".U),
160 | Value(0.U, 255.U, 255.U, "b0000_0000".U, "b1000_0100".U),
161 | Value(255.U, 255.U, 255.U, "b0000_0000".U, "b1000_0100".U),
162 | )
163 |
164 | for (value <- values) {
165 | test(new ALU) { testALU(Ops.OR.U, value, _) }
166 | }
167 | }
168 |
169 | "XOR" should "logical XOR the operands" in {
170 | val values = Seq(
171 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
172 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
173 | Value(0.U, 1.U, 1.U, "b0000_0000".U, "b0000_0000".U),
174 | Value(1.U, 1.U, 0.U, "b0000_0000".U, "b0100_0100".U),
175 | Value(255.U, 0.U, 255.U, "b0000_0000".U, "b1000_0100".U),
176 | Value(0.U, 255.U, 255.U, "b0000_0000".U, "b1000_0100".U),
177 | Value(255.U, 255.U, 0.U, "b0000_0000".U, "b0100_0100".U),
178 | )
179 |
180 | for (value <- values) {
181 | test(new ALU) { testALU(Ops.XOR.U, value, _) }
182 | }
183 | }
184 |
185 | "INC" should "increment the operand" in {
186 | val values = Seq(
187 | Value(0.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
188 | Value(0.U, 0.U, 1.U, "b0000_0001".U, "b0000_0001".U),
189 | Value(1.U, 0.U, 2.U, "b0000_0000".U, "b0000_0000".U),
190 | Value(127.U, 0.U, 128.U, "b0000_0000".U, "b1001_0100".U),
191 | Value(255.U, 1.U, 0.U, "b0000_0000".U, "b0101_0000".U),
192 | )
193 |
194 | for (value <- values) {
195 | test(new ALU) { testALU(Ops.INC.U, value, _) }
196 | }
197 | }
198 |
199 | "DEC" should "decrement the operand" in {
200 | val values = Seq(
201 | Value(0.U, 0.U, 255.U, "b0000_0000".U, "b1001_0010".U),
202 | Value(0.U, 0.U, 255.U, "b0000_0001".U, "b1001_0011".U),
203 | Value(1.U, 0.U, 0.U, "b0000_0000".U, "b0100_0010".U),
204 | Value(128.U, 0.U, 127.U, "b0000_0000".U, "b0001_0110".U),
205 | Value(255.U, 1.U, 254.U, "b0000_0000".U, "b1000_0010".U),
206 | )
207 |
208 | for (value <- values) {
209 | test(new ALU) { testALU(Ops.DEC.U, value, _) }
210 | }
211 | }
212 |
213 | "BIT" should "test whether a bit is set" in {
214 | val values = Seq(
215 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0101_0100".U),
216 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0001_0000".U),
217 | Value(0.U, 1.U, 0.U, "b0000_0000".U, "b0101_0100".U),
218 | Value(128.U, 7.U, 128.U, "b0000_0000".U, "b1001_0000".U),
219 | )
220 |
221 | for (value <- values) {
222 | test(new ALU) { testALU(Ops.BIT.U, value, _) }
223 | }
224 | }
225 |
226 | "SET" should "set a bit" in {
227 | val values = Seq(
228 | Value(0.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
229 | Value(1.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
230 | Value(0.U, 1.U, 2.U, "b0000_0000".U, "b0000_0000".U),
231 | Value(128.U, 7.U, 128.U, "b0000_0000".U, "b0000_0000".U),
232 | )
233 |
234 | for (value <- values) {
235 | test(new ALU) { testALU(Ops.SET.U, value, _) }
236 | }
237 | }
238 |
239 | "RES" should "reset a bit" in {
240 | val values = Seq(
241 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0000_0000".U),
242 | Value(1.U, 0.U, 0.U, "b0000_0000".U, "b0000_0000".U),
243 | Value(0.U, 1.U, 0.U, "b0000_0000".U, "b0000_0000".U),
244 | Value(255.U, 7.U, 127.U, "b0000_0000".U, "b0000_0000".U),
245 | )
246 |
247 | for (value <- values) {
248 | test(new ALU) { testALU(Ops.RES.U, value, _) }
249 | }
250 | }
251 |
252 | "RL" should "rotate the bits to the left" in {
253 | val values = Seq(
254 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
255 | Value(1.U, 0.U, 3.U, "b0000_0001".U, "b0000_0100".U),
256 | Value(64.U, 0.U, 128.U, "b0000_0000".U, "b1000_0000".U),
257 | Value(128.U, 0.U, 0.U, "b0000_0000".U, "b0100_0101".U),
258 | )
259 |
260 | for (value <- values) {
261 | test(new ALU) { testALU(Ops.RL.U, value, _) }
262 | }
263 | }
264 |
265 | "RLC" should "rotate the bits to the left (circular)" in {
266 | val values = Seq(
267 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
268 | Value(1.U, 0.U, 2.U, "b0000_0001".U, "b0000_0000".U),
269 | Value(64.U, 0.U, 128.U, "b0000_0000".U, "b1000_0000".U),
270 | Value(128.U, 0.U, 1.U, "b0000_0000".U, "b0000_0001".U),
271 | )
272 |
273 | for (value <- values) {
274 | test(new ALU) { testALU(Ops.RLC.U, value, _) }
275 | }
276 | }
277 |
278 | "RR" should "rotate the bits to the right" in {
279 | val values = Seq(
280 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
281 | Value(1.U, 0.U, 128.U, "b0000_0001".U, "b1000_0001".U),
282 | Value(64.U, 0.U, 32.U, "b0000_0000".U, "b0000_0000".U),
283 | Value(128.U, 0.U, 64.U, "b0000_0000".U, "b0000_0000".U),
284 | )
285 |
286 | for (value <- values) {
287 | test(new ALU) { testALU(Ops.RR.U, value, _) }
288 | }
289 | }
290 |
291 | "RRC" should "rotate the bits to the right (circular)" in {
292 | val values = Seq(
293 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
294 | Value(1.U, 0.U, 128.U, "b0000_0000".U, "b1000_0001".U),
295 | Value(64.U, 0.U, 32.U, "b0000_0000".U, "b0000_0000".U),
296 | Value(128.U, 0.U, 64.U, "b0000_0000".U, "b0000_0000".U),
297 | )
298 |
299 | for (value <- values) {
300 | test(new ALU) { testALU(Ops.RRC.U, value, _) }
301 | }
302 | }
303 |
304 | "SLA" should "shift the bits to the left (arithmetic)" in {
305 | val values = Seq(
306 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
307 | Value(1.U, 0.U, 2.U, "b0000_0000".U, "b0000_0000".U),
308 | Value(64.U, 0.U, 128.U, "b0000_0000".U, "b1000_0000".U),
309 | Value(128.U, 0.U, 0.U, "b0000_0000".U, "b0100_0101".U),
310 | )
311 |
312 | for (value <- values) {
313 | test(new ALU) { testALU(Ops.SLA.U, value, _) }
314 | }
315 | }
316 |
317 | "SLL" should "shift the bits to the left (logical)" in {
318 | val values = Seq(
319 | Value(0.U, 0.U, 1.U, "b0000_0000".U, "b0000_0000".U),
320 | Value(1.U, 0.U, 3.U, "b0000_0000".U, "b0000_0100".U),
321 | Value(64.U, 0.U, 129.U, "b0000_0000".U, "b1000_0100".U),
322 | Value(128.U, 0.U, 1.U, "b0000_0000".U, "b0000_0001".U),
323 | )
324 |
325 | for (value <- values) {
326 | test(new ALU) { testALU(Ops.SLL.U, value, _) }
327 | }
328 | }
329 |
330 | "SRA" should "shift the bits to the right (arithmetic)" in {
331 | val values = Seq(
332 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
333 | Value(1.U, 0.U, 0.U, "b0000_0000".U, "b0100_0101".U),
334 | Value(64.U, 0.U, 32.U, "b0000_0000".U, "b0000_0000".U),
335 | Value(128.U, 0.U, 192.U, "b0000_0000".U, "b1000_0100".U),
336 | )
337 |
338 | for (value <- values) {
339 | test(new ALU) { testALU(Ops.SRA.U, value, _) }
340 | }
341 | }
342 |
343 | "SRL" should "shift the bits to the right (logical)" in {
344 | val values = Seq(
345 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
346 | Value(1.U, 0.U, 0.U, "b0000_0000".U, "b0100_0101".U),
347 | Value(64.U, 0.U, 32.U, "b0000_0000".U, "b0000_0000".U),
348 | Value(128.U, 0.U, 64.U, "b0000_0000".U, "b0000_0000".U),
349 | )
350 |
351 | for (value <- values) {
352 | test(new ALU) { testALU(Ops.SRL.U, value, _) }
353 | }
354 | }
355 |
356 | "RLD" should "rotate the upper BCD digit" in {
357 | val values = Seq(
358 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
359 | Value(48.U, 16.U, 49.U, "b0000_0000".U, "b0000_0000".U),
360 | Value(144.U, 16.U, 145.U, "b0000_0000".U, "b1000_0000".U),
361 | )
362 |
363 | for (value <- values) {
364 | test(new ALU) { testALU(Ops.RLD.U, value, _) }
365 | }
366 | }
367 |
368 | "RRD" should "rotate the lower BCD digit" in {
369 | val values = Seq(
370 | Value(0.U, 0.U, 0.U, "b0000_0000".U, "b0100_0100".U),
371 | Value(48.U, 1.U, 49.U, "b0000_0000".U, "b0000_0000".U),
372 | Value(144.U, 1.U, 145.U, "b0000_0000".U, "b1000_0000".U),
373 | )
374 |
375 | for (value <- values) {
376 | test(new ALU) { testALU(Ops.RRD.U, value, _) }
377 | }
378 | }
379 | }
380 |
--------------------------------------------------------------------------------
/doc/z80-arch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------