iz68000

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

iz68000 - Motorola MC68000 emulator in Go

Simple Motorola MC68000 emulator library for Go, with instruction level timing. Extensive usage of the test suite SingleStepTests/m68000 by David Harte.

The library is being used in:

Example

package main

import (
	"github.com/ivanizag/iz68000"
)

func main() {
	// Prepare cpu and memory
	memory := iz68000.NewFlatMemory()
	cpu := iz68000.NewM68000(memory)

	// Load the reset vectors and a program
	memory.Poke(0x0003, 0x00) // Supervisor stack pointer at $8000
	memory.Poke(0x0002, 0x80)
	memory.Poke(0x0007, 0x00) // Start at $1000
	memory.Poke(0x0006, 0x10)

	cpu.Reset()
	cpu.SetTrace(true)
	for i := 0; i < 10; i++ {
		cpu.ExecuteInstruction()
	}
}

Status

All the MC68000 instructions are implemented, 82% of the 65536 opcode words:

MOVE, MOVEA, MOVEQ, MOVEM, MOVEP, LEA, PEA, EXT, SWAP, EXG, LINK, UNLK, ORI, ANDI, SUBI, ADDI, EORI, CMPI, ADDQ, SUBQ, OR, AND, SUB, ADD, CMP, EOR, SUBA, ADDA, CMPA, ADDX, SUBX, CMPM, MULU, MULS, DIVU, DIVS, ABCD, SBCD, NBCD, NEGX, CLR, NEG, NOT, TST, TAS, CHK, BTST, BCHG, BCLR, BSET, ASL, ASR, LSL, LSR, ROL, ROR, ROXL, ROXR, Bcc, BRA, BSR, DBcc, Scc, JMP, JSR, RTS, RTR, RTE, TRAP, TRAPV, NOP, RESET, STOP, ILLEGAL, MOVE to and from SR, CCR and USP, the ORI, ANDI and EORI variants that reach CCR and SR, and the line A and line F traps that the Macintosh uses for the toolbox calls.

The remaining opcode words are the encodings that are illegal on the 68000.

The cycle counts are verified against the test suite.

Test suite resuls

Against the whole test suite, with the cycle counts checked, 311278 of 315000 scenarios pass. The 3722 that do not are all documented on harteSuite_test.go:

  • The V flag of ABCD, SBCD and NBCD, that the manual leaves undefined.
  • MOVE.L, ADDX.L, SUBX.L and CMPM.L aborted by an address error on their second operand, where which half of the long was transferred first decides what has already been committed.
  • Three of the seven words of the group 0 exception frame.
  • The cycles of the CHK trap and of the bit operations on the high half of a data register, where the times are bimodal and the condition that picks between them is not always reproduced.

The cycles of an instruction aborted by an address error are not compared. It stops part way through, after a number of cycles that depends on the instruction and the addressing mode, so only a fixed approximation is charged. Every instruction that completes is cycle exact.

Both that and the exception frame details are the prefetch queue and the bus cycles of the real processor showing through, out of reach of an emulator with instruction level timing. The tests of STOP are skipped, its final states can't be reached by executing a single instruction.

Example machine

examples/tinyBasic emulates the Motorola MC68000 Educational Computer Board of 1981, the most minimal 68000 computer there is: a processor, RAM and two serial ports on 6850 ACIAs. It runs Gordon Brandly's Tiny BASIC of 1985, that was written for that board and whose whole interface with the hardware is the status and data registers of the console port:

  • $10040 status, receiver ready on the bit 0, transmitter ready on the bit 1
  • $10042 data

To exeute the example, run:

cd examples/tinyBasic
go run .

Tests

The unit tests run with go test. To also run the SingleStepTests/m68000 suite, clone it next to this repo and enable it in harteSuite_test.go:

git clone https://github.com/SingleStepTests/m68000 ../m68000-tests

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type FlatMemory

type FlatMemory struct {
	// contains filtered or unexported fields
}

FlatMemory puts RAM on the 16Mb addressable by the processor. As it is too big to be embedded on the struct, it must be created with NewFlatMemory().

func NewFlatMemory

func NewFlatMemory() *FlatMemory

NewFlatMemory returns 16Mb of RAM covering the full address space

func (*FlatMemory) Peek

func (m *FlatMemory) Peek(address uint32) uint8

Peek returns the data on the given address

func (*FlatMemory) PeekCode

func (m *FlatMemory) PeekCode(address uint32) uint8

PeekCode returns the data on the given address

func (*FlatMemory) Poke

func (m *FlatMemory) Poke(address uint32, value uint8)

Poke sets the data at the given address

type Memory

type Memory interface {
	Peek(address uint32) uint8
	Poke(address uint32, value uint8)

	// PeekCode can be used to optimize the memory manager to requests with
	// more locality. It must return the same as a call to Peek()
	PeekCode(address uint32) uint8
}

Memory represents the addressable space of the processor. Note that the 68000 is big endian and that words and longs are built from the bytes here.

type State

type State struct {
	// contains filtered or unexported fields
}

State represents the state of the simulated device

func NewM68000

func NewM68000(m Memory) *State

NewM68000 returns an initialized M68000. Call Reset() to load the stack pointer and the program counter from the vector table.

func (*State) DisasmInstruction

func (s *State) DisasmInstruction(pc uint32) (string, uint32)

DisasmInstruction disassembles the instruction at the given address, returning the text and the address of the next instruction

func (*State) ExecuteInstruction

func (s *State) ExecuteInstruction()

ExecuteInstruction transforms the state given after a single instruction is executed. An instruction aborted by an exception counts as executed.

func (*State) GetA

func (s *State) GetA(i int) uint32

GetA returns an address register, from A0 to A7. A7 is the stack pointer of the mode the processor is in.

func (*State) GetCycles

func (s *State) GetCycles() uint64

GetCycles returns the count of CPU cycles since the last reset

func (*State) GetD

func (s *State) GetD(i int) uint32

GetD returns a data register, from D0 to D7. The Macintosh power on tests leave their result on D6 and D7, which is how an emulator can report what the Sad Mac shows without rendering it.

func (*State) GetPC

func (s *State) GetPC() uint32

GetPC returns the program counter

func (*State) GetSR

func (s *State) GetSR() uint16

GetSR returns the status register

func (*State) GetTrace

func (s *State) GetTrace() bool

GetTrace gets the tracing state of the cpu execution

func (*State) IsSupervisor

func (s *State) IsSupervisor() bool

IsSupervisor tells if the processor is in supervisor mode

func (*State) Load

func (s *State) Load(r io.Reader) error

Load loads the CPU state (registers and cycle counter)

func (*State) Reset

func (s *State) Reset()

Reset resets the processor. The supervisor stack pointer and the program counter are loaded from the two first vectors.

func (*State) Save

func (s *State) Save(w io.Writer) error

Save saves the CPU state (registers and cycle counter)

func (*State) SetIRQ

func (s *State) SetIRQ(level uint8)

SetIRQ sets the level of the interrupt request lines, from 0 for no interrupt to 7 for the non maskable one. While it is asserted, an interrupt is serviced before each instruction if the mask on the status register allows it.

func (*State) SetMemory

func (s *State) SetMemory(mem Memory)

SetMemory changes the memory provider

func (*State) SetPC

func (s *State) SetPC(pc uint32)

SetPC changes the program counter, as a JMP instruction

func (*State) SetTrace

func (s *State) SetTrace(trace bool)

SetTrace activates tracing of the cpu execution

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL