package cpm
import (
"fmt"
"time"
"github.com/Krawabbel/go-8080/intel8080"
)
const WARM_BOOT_MSG = "RE-ENTRY TO CP/M WARM BOOT"
func Run(path string) error {
prog, err := intel8080.Load(path)
if err != nil {
return err
}
orig := word(0x100)
memory := make([]byte, 0x10000)
memory[0x0005] = 0xC9
copy(memory[orig:], prog)
bus := Bus(memory)
i8080 := intel8080.NewIntel8080(&bus, orig)
tStart := time.Now()
for steps := 0; true; steps++ {
i8080.Step()
if err := call_bdos(i8080); err != nil {
switch err.Error() {
case WARM_BOOT_MSG:
duration := time.Since(tStart)
fmt.Printf("\nduration: %v\n", duration)
fmt.Printf("CPU clock: %v cycles\n", i8080.Clock())
fmt.Print(i8080.DebugSpeed(duration))
return nil
default:
return err
}
}
}
return nil
}
package cpm
type Bus []byte
func (bus *Bus) Write(addr word, val byte) {
(*bus)[addr] = val
}
func (mem Bus) Read(addr word) byte {
return mem[addr]
}
package cpm
import (
"fmt"
"github.com/Krawabbel/go-8080/intel8080"
)
type word = uint16
func call_bdos(i8080 *intel8080.Intel8080) error {
pc, _ := i8080.Register16(intel8080.REG_PC)
switch pc {
case 0x0005:
return write(i8080)
case 0x0000:
return fmt.Errorf(WARM_BOOT_MSG)
default:
if pc < 0x100 {
return fmt.Errorf("unexpected BDOS access at address 0x%04X", pc)
}
}
return nil
}
func write(i8080 *intel8080.Intel8080) error {
reg_c, _ := i8080.Register8(intel8080.REG_C)
switch reg_c {
case 0x09:
output := ""
do_continue := true
addr, _ := i8080.Register16(intel8080.REG_DE)
for ; do_continue; addr++ {
if c := i8080.ReadBus(addr); c != '$' {
output += string(c)
} else {
do_continue = false
}
}
fmt.Print(output)
case 0x02, 0x05:
reg_e, _ := i8080.Register8(intel8080.REG_E)
fmt.Print(string(rune(reg_e)))
default:
return fmt.Errorf("unexpected BDOS function %v called", reg_c)
}
return nil
}
package intel8080
import (
"fmt"
"log"
)
const (
FLAG_CARRY = iota
set_1
FLAG_PARITY
unset_2
FLAG_HALF_CARRY
unset_3
FLAG_ZERO
FLAG_SIGN
)
const (
REG_A = iota
REG_B
REG_C
REG_D
REG_E
REG_F
REG_H
REG_L
REG_BC
REG_DE
REG_AF
REG_HL
REG_PC
REG_SP
)
type word = uint16
type Intel8080 struct {
sp, pc word // stack pointer, program counter
a, b, c, d, e, h, l byte // registers
f [8]bool // zero/parity/sign/carry/half-carry flags
iff bool // interrupt flip-flop
clock uint64 // cpu clock (in cycles)
halted bool // has program been halted?
interrupt_pending bool // is an interrupt pending?
interrupt_vector byte // command to be executed after interrupt
interrupt_delay byte // interrupts are only serviced 1 instruction after calling "DI"
bus Bus // ROM, RAM, VRAM, etc.
InputDevice [0x100]func() byte // input ports
OutputDevice [0x100]func(byte) // output ports
}
func NewIntel8080(bus Bus, pc word) *Intel8080 {
i8080 := new(Intel8080)
for i := 0; i <= 0xFF; i++ {
port := byte(i)
i8080.OutputDevice[port] = func(b byte) {
log.Fatalf("unexpected data sent to port %v of output device: %v\n", hex(port), hex(b))
}
i8080.InputDevice[port] = func() byte {
log.Fatalf("unexpected input requested on port %v of input device\n", hex(port))
return 0
}
}
i8080.bus = bus
i8080.pc = pc
return i8080
}
func (i8080 *Intel8080) Step() {
if i8080.interrupt_pending && i8080.iff && i8080.interrupt_delay == 0 {
i8080.interrupt_pending = false
i8080.iff = false
i8080.halted = false
i8080.clock += execute(i8080, i8080.interrupt_vector)
} else if !i8080.halted {
i8080.clock += execute(i8080, i8080.nextByte())
}
}
func (i8080 *Intel8080) Interrupt(code byte) {
i8080.interrupt_pending = true
i8080.interrupt_vector = code
}
func (i8080 Intel8080) Clock() uint64 {
return i8080.clock
}
func (i8080 Intel8080) ReadBus(addr word) byte {
return i8080.bus.Read(addr)
}
func (i8080 Intel8080) Register8(reg int) (byte, error) {
switch reg {
case REG_A:
return i8080.a, nil
case REG_B:
return i8080.b, nil
case REG_C:
return i8080.c, nil
case REG_D:
return i8080.d, nil
case REG_E:
return i8080.e, nil
case REG_F:
return i8080.getF(), nil
case REG_H:
return i8080.h, nil
case REG_L:
return i8080.l, nil
default:
return 0, fmt.Errorf("unknown 8-bit register %d", reg)
}
}
func (i8080 Intel8080) Register16(reg_pair int) (word, error) {
switch reg_pair {
case REG_AF:
return i8080.getAF(), nil
case REG_BC:
return i8080.getBC(), nil
case REG_DE:
return i8080.getDE(), nil
case REG_HL:
return i8080.getHL(), nil
case REG_PC:
return i8080.pc, nil
case REG_SP:
return i8080.sp, nil
default:
return 0, fmt.Errorf("unknown 16-bit register %d", reg_pair)
}
}
func (i8080 Intel8080) Flag(flag int) (bool, error) {
switch flag {
case FLAG_CARRY, FLAG_HALF_CARRY, FLAG_PARITY, FLAG_SIGN, FLAG_ZERO:
return i8080.f[flag], nil
default:
return false, fmt.Errorf("unknown flag %d", flag)
}
}
package intel8080
import (
"fmt"
"time"
)
func (i8080 Intel8080) DebugState() string {
return fmt.Sprintf(
"PC: %s, AF: %s, BC: %s, DE: %s, HL: %s, SP: %s, CYC: %v",
hex(i8080.pc),
hex(i8080.getAF()),
hex(i8080.getBC()),
hex(i8080.getDE()),
hex(i8080.getHL()),
hex(i8080.sp),
i8080.clock)
}
func (i8080 Intel8080) DebugProg() string {
next := make([]byte, 5)
for i := range next {
next[i] = i8080.readByte(i8080.pc + word(i))
}
return fmt.Sprintf("%s: %-15s >> %s", hex(i8080.pc), hexs(next), mnemonics[i8080.readByte(i8080.pc)])
}
func (i8080 Intel8080) DebugSpeed(duration time.Duration) string {
freq := float64(i8080.clock) / float64(duration.Microseconds())
speedup := freq / 2
return fmt.Sprintf("clock rate: %0.2f MHz (%0.2fx original Intel 8080 frequency)\n", freq, speedup)
}
package intel8080
func (i8080 Intel8080) valFC() byte {
if i8080.f[FLAG_CARRY] {
return 1
}
return 0
}
func (i8080 Intel8080) getF() byte {
F := byte(0)
for i := 0; i < 8; i++ {
switch {
case i == unset_2, i == unset_3:
// do nothing
case i == set_1:
F |= (1 << i)
case i8080.f[i]:
F |= (1 << i)
}
}
return F
}
func (i8080 *Intel8080) setF(F byte) {
for i := 0; i < 8; i++ {
i8080.f[i] = F&(1<<i) > 0
}
i8080.f[set_1] = true
i8080.f[unset_2] = false
i8080.f[unset_3] = false
}
func (i8080 *Intel8080) setZSP(val byte) {
i8080.f[FLAG_ZERO] = val == 0
i8080.f[FLAG_SIGN] = val&(1<<7) > 0
i8080.f[FLAG_PARITY] = parity(val)
}
func (i8080 Intel8080) getAF() word {
return Join(i8080.a, i8080.getF())
}
func (i8080 *Intel8080) setAF(af word) {
a, f := Split(af)
i8080.a = a
i8080.setF(f)
}
func (i8080 Intel8080) getPSW() word {
return i8080.getAF()
}
func (i8080 *Intel8080) setPSW(psw word) {
i8080.setAF(psw)
}
func (i8080 Intel8080) getBC() word {
return Join(i8080.b, i8080.c)
}
func (i8080 *Intel8080) setBC(bc word) {
i8080.b, i8080.c = Split(bc)
}
func (i8080 Intel8080) getDE() word {
return Join(i8080.d, i8080.e)
}
func (i8080 *Intel8080) setDE(de word) {
i8080.d, i8080.e = Split(de)
}
func (i8080 Intel8080) getHL() word {
return Join(i8080.h, i8080.l)
}
func (i8080 *Intel8080) setHL(hl word) {
i8080.h, i8080.l = Split(hl)
}
func (i8080 Intel8080) readByte(addr word) byte {
return i8080.bus.Read(addr)
}
func (i8080 *Intel8080) writeByte(addr word, val byte) {
i8080.bus.Write(addr, val)
}
func (i8080 Intel8080) getM() byte {
addr := Join(i8080.h, i8080.l)
return i8080.readByte(addr)
}
func (i8080 *Intel8080) setM(val byte) {
addr := Join(i8080.h, i8080.l)
i8080.writeByte(addr, val)
}
func (i8080 *Intel8080) nextByte() byte {
val := i8080.readByte(i8080.pc)
i8080.pc++
return val
}
func (i8080 *Intel8080) nextWord() word {
lo := i8080.nextByte()
hi := i8080.nextByte()
return Join(hi, lo)
}
package intel8080
func execute(i8080 *Intel8080, opcode byte) uint64 {
if i8080.interrupt_delay > 0 {
i8080.interrupt_delay--
}
switch opcode {
case 0x00, 0x10, 0x20, 0x30, 0x08, 0x18, 0x28, 0x38:
return 4
case 0x01:
i8080.setBC(i8080.nextWord())
return 10
case 0x11:
i8080.setDE(i8080.nextWord())
return 10
case 0x21:
i8080.setHL(i8080.nextWord())
return 10
case 0x31:
i8080.sp = i8080.nextWord()
return 10
case 0x02:
i8080.writeByte(i8080.getBC(), i8080.a)
return 7
case 0x12:
i8080.writeByte(i8080.getDE(), i8080.a)
return 7
case 0x22:
addr := i8080.nextWord()
i8080.writeByte(addr, i8080.l)
i8080.writeByte(addr+1, i8080.h)
return 16
case 0x32:
i8080.writeByte(i8080.nextWord(), i8080.a)
return 13
case 0x03:
i8080.setBC(i8080.getBC() + 1)
return 5
case 0x13:
i8080.setDE(i8080.getDE() + 1)
return 5
case 0x23:
i8080.setHL(i8080.getHL() + 1)
return 5
case 0x33:
i8080.sp++
return 5
case 0x04:
inr(i8080, &i8080.b)
return 5
case 0x14:
inr(i8080, &i8080.d)
return 5
case 0x24:
inr(i8080, &i8080.h)
return 5
case 0x34:
m := i8080.getM()
inr(i8080, &m)
i8080.setM(m)
return 10
case 0x05:
dcr(i8080, &i8080.b)
return 5
case 0x15:
dcr(i8080, &i8080.d)
return 5
case 0x25:
dcr(i8080, &i8080.h)
return 5
case 0x35:
m := i8080.getM()
dcr(i8080, &m)
i8080.setM(m)
return 10
case 0x06:
i8080.b = i8080.nextByte()
return 7
case 0x16:
i8080.d = i8080.nextByte()
return 7
case 0x26:
i8080.h = i8080.nextByte()
return 7
case 0x36:
i8080.setM(i8080.nextByte())
return 10
case 0x07:
rlc(i8080)
return 4
case 0x17:
ral(i8080)
return 4
case 0x27:
daa(i8080)
return 4
case 0x37:
i8080.f[FLAG_CARRY] = true
return 4
case 0x09:
dad(i8080, i8080.getBC())
return 10
case 0x19:
dad(i8080, i8080.getDE())
return 10
case 0x29:
dad(i8080, i8080.getHL())
return 10
case 0x39:
dad(i8080, i8080.sp)
return 10
case 0x0A:
i8080.a = i8080.readByte(i8080.getBC())
return 7
case 0x1A:
i8080.a = i8080.readByte(i8080.getDE())
return 7
case 0x2A:
addr := i8080.nextWord()
i8080.l = i8080.readByte(addr)
i8080.h = i8080.readByte(addr + 1)
return 16
case 0x3A:
i8080.a = i8080.readByte(i8080.nextWord())
return 13
case 0x0B:
i8080.setBC(i8080.getBC() - 1)
return 5
case 0x1B:
i8080.setDE(i8080.getDE() - 1)
return 5
case 0x2B:
i8080.setHL(i8080.getHL() - 1)
return 5
case 0x3B:
i8080.sp--
return 5
case 0x0C:
inr(i8080, &i8080.c)
return 5
case 0x1C:
inr(i8080, &i8080.e)
return 5
case 0x2C:
inr(i8080, &i8080.l)
return 5
case 0x3C:
inr(i8080, &i8080.a)
return 5
case 0x0D:
dcr(i8080, &i8080.c)
return 5
case 0x1D:
dcr(i8080, &i8080.e)
return 5
case 0x2D:
dcr(i8080, &i8080.l)
return 5
case 0x3D:
dcr(i8080, &i8080.a)
return 5
case 0x0E:
i8080.c = i8080.nextByte()
return 7
case 0x1E:
i8080.e = i8080.nextByte()
return 7
case 0x2E:
i8080.l = i8080.nextByte()
return 7
case 0x3E:
i8080.a = i8080.nextByte()
return 7
case 0x0F:
rrc(i8080)
return 4
case 0x1F:
rar(i8080)
return 4
case 0x2F:
i8080.a = ^i8080.a
return 4
case 0x3F:
i8080.f[FLAG_CARRY] = !i8080.f[FLAG_CARRY]
return 4
case 0x40:
// i8080.b = i8080.b
return 5
case 0x41:
i8080.b = i8080.c
return 5
case 0x42:
i8080.b = i8080.d
return 5
case 0x43:
i8080.b = i8080.e
return 5
case 0x44:
i8080.b = i8080.h
return 5
case 0x45:
i8080.b = i8080.l
return 5
case 0x46:
i8080.b = i8080.getM()
return 7
case 0x47:
i8080.b = i8080.a
return 5
case 0x50:
i8080.d = i8080.b
return 5
case 0x51:
i8080.d = i8080.c
return 5
case 0x52:
// i8080.d = i8080.d
return 5
case 0x53:
i8080.d = i8080.e
return 5
case 0x54:
i8080.d = i8080.h
return 5
case 0x55:
i8080.d = i8080.l
return 5
case 0x56:
i8080.d = i8080.getM()
return 7
case 0x57:
i8080.d = i8080.a
return 5
case 0x60:
i8080.h = i8080.b
return 5
case 0x61:
i8080.h = i8080.c
return 5
case 0x62:
i8080.h = i8080.d
return 5
case 0x63:
i8080.h = i8080.e
return 5
case 0x64:
// i8080.h = i8080.h
return 5
case 0x65:
i8080.h = i8080.l
return 5
case 0x66:
i8080.h = i8080.getM()
return 7
case 0x67:
i8080.h = i8080.a
return 5
case 0x70:
i8080.setM(i8080.b)
return 7
case 0x71:
i8080.setM(i8080.c)
return 7
case 0x72:
i8080.setM(i8080.d)
return 7
case 0x73:
i8080.setM(i8080.e)
return 7
case 0x74:
i8080.setM(i8080.h)
return 7
case 0x75:
i8080.setM(i8080.l)
return 7
case 0x76:
i8080.halted = true
return 7
case 0x77:
i8080.setM(i8080.a)
return 7
case 0x48:
i8080.c = i8080.b
return 5
case 0x49:
// i8080.c = i8080.c
return 5
case 0x4A:
i8080.c = i8080.d
return 5
case 0x4B:
i8080.c = i8080.e
return 5
case 0x4C:
i8080.c = i8080.h
return 5
case 0x4D:
i8080.c = i8080.l
return 5
case 0x4E:
i8080.c = i8080.getM()
return 7
case 0x4F:
i8080.c = i8080.a
return 5
case 0x58:
i8080.e = i8080.b
return 5
case 0x59:
i8080.e = i8080.c
return 5
case 0x5A:
i8080.e = i8080.d
return 5
case 0x5B:
// i8080.e = i8080.e
return 5
case 0x5C:
i8080.e = i8080.h
return 5
case 0x5D:
i8080.e = i8080.l
return 5
case 0x5E:
i8080.e = i8080.getM()
return 7
case 0x5F:
i8080.e = i8080.a
return 5
case 0x68:
i8080.l = i8080.b
return 5
case 0x69:
i8080.l = i8080.c
return 5
case 0x6A:
i8080.l = i8080.d
return 5
case 0x6B:
i8080.l = i8080.e
return 5
case 0x6C:
i8080.l = i8080.h
return 5
case 0x6D:
// i8080.l = i8080.l
return 5
case 0x6E:
i8080.l = i8080.getM()
return 7
case 0x6F:
i8080.l = i8080.a
return 5
case 0x78:
i8080.a = i8080.b
return 5
case 0x79:
i8080.a = i8080.c
return 5
case 0x7A:
i8080.a = i8080.d
return 5
case 0x7B:
i8080.a = i8080.e
return 5
case 0x7C:
i8080.a = i8080.h
return 5
case 0x7D:
i8080.a = i8080.l
return 5
case 0x7E:
i8080.a = i8080.getM()
return 7
case 0x7F:
// i8080.a = i8080.a
return 5
case 0x80:
add(i8080, i8080.b, 0)
return 4
case 0x81:
add(i8080, i8080.c, 0)
return 4
case 0x82:
add(i8080, i8080.d, 0)
return 4
case 0x83:
add(i8080, i8080.e, 0)
return 4
case 0x84:
add(i8080, i8080.h, 0)
return 4
case 0x85:
add(i8080, i8080.l, 0)
return 4
case 0x86:
add(i8080, i8080.getM(), 0)
return 7
case 0x87:
add(i8080, i8080.a, 0)
return 4
case 0x88:
add(i8080, i8080.b, i8080.valFC())
return 4
case 0x89:
add(i8080, i8080.c, i8080.valFC())
return 4
case 0x8A:
add(i8080, i8080.d, i8080.valFC())
return 4
case 0x8B:
add(i8080, i8080.e, i8080.valFC())
return 4
case 0x8C:
add(i8080, i8080.h, i8080.valFC())
return 4
case 0x8D:
add(i8080, i8080.l, i8080.valFC())
return 4
case 0x8E:
add(i8080, i8080.getM(), i8080.valFC())
return 7 //, "ADC M", nil
case 0x8F:
add(i8080, i8080.a, i8080.valFC())
return 4
case 0x90:
sub(i8080, i8080.b, 0)
return 4
case 0x91:
sub(i8080, i8080.c, 0)
return 4
case 0x92:
sub(i8080, i8080.d, 0)
return 4
case 0x93:
sub(i8080, i8080.e, 0)
return 4
case 0x94:
sub(i8080, i8080.h, 0)
return 4
case 0x95:
sub(i8080, i8080.l, 0)
return 4
case 0x96:
sub(i8080, i8080.getM(), 0)
return 7
case 0x97:
sub(i8080, i8080.a, 0)
return 4
case 0x98:
sub(i8080, i8080.b, i8080.valFC())
return 4
case 0x99:
sub(i8080, i8080.c, i8080.valFC())
return 4
case 0x9A:
sub(i8080, i8080.d, i8080.valFC())
return 4
case 0x9B:
sub(i8080, i8080.e, i8080.valFC())
return 4
case 0x9C:
sub(i8080, i8080.h, i8080.valFC())
return 4
case 0x9D:
sub(i8080, i8080.l, i8080.valFC())
return 4
case 0x9E:
sub(i8080, i8080.getM(), i8080.valFC())
return 7
case 0x9F:
sub(i8080, i8080.a, i8080.valFC())
return 4
case 0xA0:
ana(i8080, i8080.b)
return 4
case 0xA1:
ana(i8080, i8080.c)
return 4
case 0xA2:
ana(i8080, i8080.d)
return 4
case 0xA3:
ana(i8080, i8080.e)
return 4
case 0xA4:
ana(i8080, i8080.h)
return 4
case 0xA5:
ana(i8080, i8080.l)
return 4
case 0xA6:
ana(i8080, i8080.getM())
return 7
case 0xA7:
ana(i8080, i8080.a)
return 4
case 0xA8:
xra(i8080, i8080.b)
return 4
case 0xA9:
xra(i8080, i8080.c)
return 4
case 0xAA:
xra(i8080, i8080.d)
return 4
case 0xAB:
xra(i8080, i8080.e)
return 4
case 0xAC:
xra(i8080, i8080.h)
return 4
case 0xAD:
xra(i8080, i8080.l)
return 4
case 0xAE:
xra(i8080, i8080.getM())
return 7
case 0xAF:
xra(i8080, i8080.a)
return 4
case 0xB0:
ora(i8080, i8080.b)
return 4
case 0xB1:
ora(i8080, i8080.c)
return 4
case 0xB2:
ora(i8080, i8080.d)
return 4
case 0xB3:
ora(i8080, i8080.e)
return 4
case 0xB4:
ora(i8080, i8080.h)
return 4
case 0xB5:
ora(i8080, i8080.l)
return 4
case 0xB6:
ora(i8080, i8080.getM())
return 7
case 0xB7:
ora(i8080, i8080.a)
return 4
case 0xB8:
cmp(i8080, i8080.b)
return 4
case 0xB9:
cmp(i8080, i8080.c)
return 4
case 0xBA:
cmp(i8080, i8080.d)
return 4
case 0xBB:
cmp(i8080, i8080.e)
return 4
case 0xBC:
cmp(i8080, i8080.h)
return 4
case 0xBD:
cmp(i8080, i8080.l)
return 4
case 0xBE:
cmp(i8080, i8080.getM())
return 7
case 0xBF:
cmp(i8080, i8080.a)
return 4
case 0xC0:
cond_ret(i8080, !i8080.f[FLAG_ZERO])
return 5
case 0xD0:
cond_ret(i8080, !i8080.f[FLAG_CARRY])
return 5
case 0xE0:
cond_ret(i8080, !i8080.f[FLAG_PARITY])
return 5
case 0xF0:
cond_ret(i8080, !i8080.f[FLAG_SIGN])
return 5
case 0xC1:
i8080.setBC(pop_stack(i8080))
return 10
case 0xD1:
i8080.setDE(pop_stack(i8080))
return 10
case 0xE1:
i8080.setHL(pop_stack(i8080))
return 10
case 0xF1:
i8080.setPSW(pop_stack(i8080))
return 10
case 0xC2:
cond_jmp(i8080, !i8080.f[FLAG_ZERO])
return 10
case 0xD2:
cond_jmp(i8080, !i8080.f[FLAG_CARRY])
return 10
case 0xE2:
cond_jmp(i8080, !i8080.f[FLAG_PARITY])
return 10
case 0xF2:
cond_jmp(i8080, !i8080.f[FLAG_SIGN])
return 10
case 0xC3, 0xCB:
jmp(i8080, i8080.nextWord())
return 10
case 0xD3:
i8080.OutputDevice[i8080.nextByte()](i8080.a)
return 10
case 0xE3:
xthl(i8080)
return 18
case 0xF3:
i8080.iff = false
return 4
case 0xC4:
cond_call(i8080, !i8080.f[FLAG_ZERO])
return 11
case 0xD4:
cond_call(i8080, !i8080.f[FLAG_CARRY])
return 11
case 0xE4:
cond_call(i8080, !i8080.f[FLAG_PARITY])
return 11
case 0xF4:
cond_call(i8080, !i8080.f[FLAG_SIGN])
return 11
case 0xC5:
push_stack(i8080, i8080.getBC())
return 11
case 0xD5:
push_stack(i8080, i8080.getDE())
return 11
case 0xE5:
push_stack(i8080, i8080.getHL())
return 11
case 0xF5:
push_stack(i8080, i8080.getPSW())
return 11
case 0xC6:
add(i8080, i8080.nextByte(), 0)
return 7
case 0xD6:
sub(i8080, i8080.nextByte(), 0)
return 7
case 0xE6:
ana(i8080, i8080.nextByte())
return 7
case 0xF6:
ora(i8080, i8080.nextByte())
return 7
case 0xC7:
call(i8080, 0x00)
return 11
case 0xD7:
call(i8080, 0x10)
return 11
case 0xE7:
call(i8080, 0x20)
return 11
case 0xF7:
call(i8080, 0x30)
return 11
case 0xC8:
cond_ret(i8080, i8080.f[FLAG_ZERO])
return 5
case 0xD8:
cond_ret(i8080, i8080.f[FLAG_CARRY])
return 5
case 0xE8:
cond_ret(i8080, i8080.f[FLAG_PARITY])
return 5
case 0xF8:
cond_ret(i8080, i8080.f[FLAG_SIGN])
return 5
case 0xC9, 0xD9:
ret(i8080)
return 10
case 0xE9:
i8080.pc = Join(i8080.h, i8080.l)
return 5
case 0xF9:
i8080.sp = i8080.getHL()
return 5
case 0xCA:
cond_jmp(i8080, i8080.f[FLAG_ZERO])
return 10
case 0xDA:
cond_jmp(i8080, i8080.f[FLAG_CARRY])
return 10
case 0xEA:
cond_jmp(i8080, i8080.f[FLAG_PARITY])
return 10
case 0xFA:
cond_jmp(i8080, i8080.f[FLAG_SIGN])
return 10
case 0xDB:
i8080.a = i8080.InputDevice[i8080.nextByte()]()
return 10
case 0xEB:
xchg(i8080)
return 5
case 0xFB:
i8080.iff = true
i8080.interrupt_delay = 1
return 4
case 0xCC:
cond_call(i8080, i8080.f[FLAG_ZERO])
return 11
case 0xDC:
cond_call(i8080, i8080.f[FLAG_CARRY])
return 11
case 0xEC:
cond_call(i8080, i8080.f[FLAG_PARITY])
return 11
case 0xFC:
cond_call(i8080, i8080.f[FLAG_SIGN])
return 11
case 0xCD, 0xDD, 0xED, 0xFD:
call(i8080, i8080.nextWord())
return 11
case 0xCE:
add(i8080, i8080.nextByte(), i8080.valFC())
return 7
case 0xDE:
sub(i8080, i8080.nextByte(), i8080.valFC())
return 7
case 0xEE:
xra(i8080, i8080.nextByte())
return 7
case 0xFE:
cmp(i8080, i8080.nextByte())
return 7
case 0xCF:
call(i8080, 0x08)
return 11
case 0xDF:
call(i8080, 0x18)
return 11
case 0xEF:
call(i8080, 0x28)
return 11
case 0xFF:
call(i8080, 0x38)
return 11
}
panic("unexpected operation: " + hex(opcode))
}
package intel8080
func push_stack(i8080 *Intel8080, addr word) {
hi, lo := Split(addr)
i8080.writeByte(i8080.sp-1, hi)
i8080.writeByte(i8080.sp-2, lo)
i8080.sp -= 2
}
func pop_stack(i8080 *Intel8080) word {
lo := i8080.readByte(i8080.sp)
hi := i8080.readByte(i8080.sp + 1)
i8080.sp += 2
return Join(hi, lo)
}
func add(i8080 *Intel8080, data byte, cy byte) {
result := i8080.a + data + cy
i8080.setZSP(result)
i8080.f[FLAG_CARRY], i8080.f[FLAG_HALF_CARRY] = carry(i8080.a, data, cy)
i8080.a = result
}
func sub(i8080 *Intel8080, data byte, cy byte) {
add(i8080, ^data, cy^1)
i8080.f[FLAG_CARRY] = !i8080.f[FLAG_CARRY]
}
func dad(i8080 *Intel8080, add word) {
hl := i8080.getHL()
i8080.f[FLAG_CARRY] = 0xFFFF-add < hl
i8080.setHL(hl + add)
}
func inr(i8080 *Intel8080, val *byte) {
res := *val + 1
i8080.setZSP(res)
i8080.f[FLAG_HALF_CARRY] = (*val & 0xF) == 0xF
*val = res
}
func dcr(i8080 *Intel8080, val *byte) {
res := *val - 1
i8080.setZSP(res)
i8080.f[FLAG_HALF_CARRY] = !((res & 0xF) == 0xF)
*val = res
}
func ana(i8080 *Intel8080, val byte) {
res := i8080.a & val
i8080.setZSP(res)
i8080.f[FLAG_HALF_CARRY] = (i8080.a|val)&0x08 > 0
i8080.f[FLAG_CARRY] = false
i8080.a = res
}
func xra(i8080 *Intel8080, val byte) {
i8080.a = i8080.a ^ val
i8080.setZSP(i8080.a)
i8080.f[FLAG_HALF_CARRY] = false
i8080.f[FLAG_CARRY] = false
}
func ora(i8080 *Intel8080, val byte) {
i8080.a = i8080.a | val
i8080.setZSP(i8080.a)
i8080.f[FLAG_HALF_CARRY] = false
i8080.f[FLAG_CARRY] = false
}
func cmp(i8080 *Intel8080, val byte) {
a := i8080.a
sub(i8080, val, 0)
i8080.a = a
}
func jmp(i8080 *Intel8080, addr word) {
i8080.pc = addr
}
func cond_jmp(i8080 *Intel8080, cond bool) {
addr := i8080.nextWord()
if cond {
jmp(i8080, addr)
}
}
func call(i8080 *Intel8080, addr word) {
push_stack(i8080, i8080.pc)
jmp(i8080, addr)
}
func cond_call(i8080 *Intel8080, cond bool) {
addr := i8080.nextWord()
if cond {
call(i8080, addr)
i8080.clock += 6
}
}
func ret(i8080 *Intel8080) {
i8080.pc = pop_stack(i8080)
}
func cond_ret(i8080 *Intel8080, cond bool) {
if cond {
ret(i8080)
i8080.clock += 6
}
}
func rlc(i8080 *Intel8080) {
cy := (i8080.a >> 7)
i8080.f[FLAG_CARRY] = cy > 0
i8080.a = (i8080.a << 1) | cy
}
func rrc(i8080 *Intel8080) {
cy := i8080.a & 1
i8080.f[FLAG_CARRY] = cy > 0
i8080.a = (i8080.a >> 1) | (cy << 7)
}
func ral(i8080 *Intel8080) {
cy := i8080.valFC()
i8080.f[FLAG_CARRY] = (i8080.a >> 7) > 0
i8080.a = (i8080.a << 1) | (cy << 7)
}
func rar(i8080 *Intel8080) {
cy := i8080.valFC()
i8080.f[FLAG_CARRY] = (i8080.a & 1) > 0
i8080.a = (i8080.a >> 1) | (cy << 7)
}
func daa(i8080 *Intel8080) {
if i8080.a&0x0F > 0x09 || i8080.f[FLAG_HALF_CARRY] {
i8080.f[FLAG_HALF_CARRY] = (i8080.a & 0x0F) > (0x0F - 0x06)
i8080.a += 0x06
} else {
i8080.f[FLAG_HALF_CARRY] = false
}
if (i8080.a&0xF0) > 0x90 || i8080.f[FLAG_CARRY] {
i8080.f[FLAG_CARRY] = (i8080.a & 0xF0) > (0xF0 - 0x60)
i8080.a += 0x60
} else {
i8080.f[FLAG_CARRY] = false
}
i8080.setZSP(i8080.a)
}
func xchg(i8080 *Intel8080) {
i8080.h, i8080.l, i8080.d, i8080.e = i8080.d, i8080.e, i8080.h, i8080.l
}
func xthl(i8080 *Intel8080) {
l := i8080.l
h := i8080.h
i8080.l = i8080.readByte(i8080.sp)
i8080.h = i8080.readByte(i8080.sp + 1)
i8080.writeByte(i8080.sp, l)
i8080.writeByte(i8080.sp+1, h)
}
package intel8080
import (
"fmt"
"io"
"os"
"strings"
)
type unsigned interface {
uint8 | uint16
}
func hex[T unsigned](value T) string {
maxT := ^T(0)
n := len(fmt.Sprintf("%X", maxT))
format := "0x%0" + fmt.Sprint(n) + "X"
return fmt.Sprintf(format, value)
}
func hexs[T unsigned](values []T) string {
s := make([]string, len(values))
for i, value := range values {
s[i] = hex(value)
}
return "[" + strings.Join(s, " ") + "]"
}
func Join(hi, lo byte) word {
return word(lo) | word(hi)<<8
}
func Split(addr word) (hi, lo byte) {
return byte(addr >> 8), byte(addr)
}
func carry(a, b, cy byte) (carry, half_carry bool) {
sum := word(a) + word(b) + word(cy)
carries := sum ^ word(a) ^ word(b)
return carries&(1<<8) > 0, carries&(1<<4) > 0
}
func parity(val byte) bool {
val = val ^ (val >> 4)
val = val ^ (val >> 2)
val = val ^ (val >> 1)
return (val & 1) == 0
}
func Load(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("load file error: %s", err)
}
defer f.Close()
prog, err := io.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("load file error: %s", err)
}
return prog, nil
}