├── project ├── build.properties ├── scalastyle-config.xml └── scalastyle-test-config.xml ├── Makefile ├── .gitignore ├── doc ├── z80-arch.png ├── z80-manual.pdf └── z80-arch.svg ├── .idea ├── .gitignore ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── vcs.xml ├── misc.xml ├── dictionaries │ └── josh.xml ├── compiler.xml ├── copyright │ ├── profiles_settings.xml │ └── nullobject.xml ├── scala_compiler.xml ├── sbt.xml └── runConfigurations │ ├── ALUTest.xml │ ├── CPUTest.xml │ ├── AdderTest.xml │ ├── CounterTest.xml │ ├── DecoderTest.xml │ └── Unit_Tests.xml ├── .travis.yml ├── README.md ├── LICENCE └── src ├── main └── scala │ └── z80 │ ├── Instructions.scala │ ├── Counter.scala │ ├── Adder.scala │ ├── Decoder.scala │ ├── CPU.scala │ └── ALU.scala └── test └── scala └── z80 ├── DecoderTest.scala ├── CounterTest.scala ├── AdderTest.scala ├── CPUTest.scala └── ALUTest.scala /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.3.10 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | test: 4 | sbt test 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .run 2 | project/target 3 | target 4 | test_run_dir 5 | -------------------------------------------------------------------------------- /doc/z80-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullobject/z80-chisel/HEAD/doc/z80-arch.png -------------------------------------------------------------------------------- /doc/z80-manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nullobject/z80-chisel/HEAD/doc/z80-manual.pdf -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /libraries/ 3 | /modules/ 4 | /modules.xml 5 | /shelf/ 6 | /uiDesigner.xml 7 | /workspace.xml 8 | /*.iml 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/dictionaries/josh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | addr 5 | bassett 6 | microcodes 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | jdk: openjdk8 3 | scala: 4 | - 2.12.10 5 | script: 6 | - sbt test 7 | cache: 8 | directories: 9 | - $HOME/.cache/coursier 10 | - $HOME/.ivy2/cache 11 | - $HOME/.sbt 12 | before_cache: 13 | - rm -fv $HOME/.ivy2/.sbt.ivy.lock 14 | - find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete 15 | - find $HOME/.sbt -name "*.lock" -print -delete 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Z80 Chisel 2 | 3 | [![Build Status](https://travis-ci.com/nullobject/z80-chisel.svg?branch=master)](https://travis-ci.com/nullobject/z80-chisel) 4 | 5 | A Z80 CPU implemented in [Chisel](https://www.chisel-lang.org/). 6 | 7 | ## Architecture 8 | 9 | Z80 Architecture 10 | 11 | ## Licence 12 | 13 | This project is licensed under the MIT licence. See the LICENCE file for more details. 14 | -------------------------------------------------------------------------------- /.idea/scala_compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/sbt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/runConfigurations/ALUTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CPUTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/AdderTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/CounterTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/DecoderTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 15 | -------------------------------------------------------------------------------- /.idea/runConfigurations/Unit_Tests.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 18 | 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 | 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 | 4 | 5 | 6 | 7 | 8 | 9 | image/svg+xml 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | InstructionRegister 31 | 32 | InstructionDecoder 33 | 34 | ControlLogic 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 | I 60 | R 61 | 62 | 1 63 | 64 | + 65 | _ 66 | 67 | 68 | 69 | 1 70 | 71 | + 72 | _ 73 | 74 | 75 | MUX 76 | MUX 77 | W 78 | Z 79 | W' 80 | Z' 81 | B 82 | B' 83 | C 84 | C' 85 | D 86 | D' 87 | E 88 | E' 89 | H 90 | L 91 | H' 92 | L' 93 | IX 94 | IY 95 | PC 96 | SP 97 | 98 | 99 | 100 | TEMP 101 | 102 | ACU 103 | 104 | 105 | A 106 | A' 107 | F 108 | F' 109 | 110 | 111 | + 112 | 113 | 114 | BUFFERS 115 | 116 | BUFFERS 117 | 118 | BUFFERS 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | ALU 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Internal Data Bus 8 Bit 173 | Address Bus 16 Bit 174 | Control Bus 175 | Control Section 176 | Z80 Architecture 177 | 178 | 179 | --------------------------------------------------------------------------------