Ocean-240.2-Emulator/okean240/computer.go

482 lines
9.7 KiB
Go

package okean240
import (
_ "embed"
"encoding/binary"
"encoding/json"
"fmt"
"image/color"
"okemu/config"
"okemu/debug"
"okemu/okean240/fdc"
"okemu/okean240/pic"
"okemu/okean240/pit"
"okemu/okean240/usart"
"okemu/z80"
"os"
"strconv"
"sync/atomic"
//"okemu/z80"
"okemu/z80/c99"
//"okemu/z80/js"
log "github.com/sirupsen/logrus"
)
const DefaultCPUFrequency = 2_500_000
const CPUFrequencyLow float64 = 2.47
const CPUFrequencyHi float64 = 2.53
const TimerFrequencyLow float64 = 1.47
const TimerFrequencyHi float64 = 1.53
type ComputerType struct {
cpu *c99.Z80
memory Memory
ioPorts [256]byte
cycles uint64
tstatesPartial uint64
dd17EnableOut bool
colorMode bool
screenWidth int
screenHeight int
vRAM *RamBlock
palette byte
bgColor byte
pit *pit.I8253
usart *usart.I8251
pic *pic.I8259
fdc *fdc.FloppyDriveController
kbdBuffer []byte
vShift byte
hShift byte
cpuFrequency uint32
//
debugger *debug.Debugger
config *config.OkEmuConfig
kbAck atomic.Bool
}
type Snapshot struct {
CPU *z80.CPU `json:"cpu,omitempty"`
Memory string `json:"memory,omitempty"`
}
const VRAMBlock0 = 3
const VRAMBlock1 = 7
const VidVsuBit = 0x80
const VidColorBit = 0x40
//type ComputerInterface interface {
// Run()
// Reset()
// GetPixel(x uint16, y uint16) color.RGBA
// Do() uint64
// TimerClk()
// PutKey(key *fyne.KeyEvent)
// PutRune(key rune)
// PutCtrlKey(shortcut fyne.Shortcut)
// SaveFloppy()
// LoadFloppy()
// CPUState() *z80.CPU
// SetCPUState(state *z80.CPU)
//}
func (c *ComputerType) CPUState() *z80.CPU {
return c.cpu.GetState()
}
func (c *ComputerType) SetCPUState(state *z80.CPU) {
c.cpu.SetState(state)
}
func (c *ComputerType) M1MemRead(addr uint16) byte {
return c.memory.M1MemRead(addr)
}
func (c *ComputerType) MemRead(addr uint16) byte {
return c.memory.MemRead(addr)
}
func (c *ComputerType) MemWrite(addr uint16, val byte) {
c.memory.MemWrite(addr, val)
}
// NewComputer Builds new computer
func NewComputer(cfg *config.OkEmuConfig, deb *debug.Debugger) *ComputerType {
c := ComputerType{}
c.config = cfg
c.memory = Memory{}
c.memory.Init(cfg.MonitorFile, cfg.CPMFile)
c.cpu = c99.New(&c)
c.cycles = 0
c.tstatesPartial = 0
c.dd17EnableOut = false
c.screenWidth = 512
c.screenHeight = 256
c.vRAM = c.memory.allMemory[3]
c.palette = 0
c.bgColor = 0
c.vShift = 0
c.hShift = 0
//c.aOffset = 0x100
c.pit = pit.New()
c.kbAck.Store(false)
c.usart = usart.New()
c.pic = pic.NewI8259()
c.fdc = fdc.NewFDC(cfg)
c.cpuFrequency = DefaultCPUFrequency
c.debugger = deb
return &c
}
func (c *ComputerType) Reset() {
c.cpu.Reset()
c.cycles = 0
c.tstatesPartial = 0
}
func (c *ComputerType) getContext() map[string]interface{} {
context := make(map[string]interface{})
s := c.cpu.GetState()
context["A"] = s.A
context["B"] = s.B
context["C"] = s.C
context["D"] = s.D
context["E"] = s.E
context["H"] = s.H
context["L"] = s.L
context["A'"] = s.AAlt
context["B'"] = s.BAlt
context["C'"] = s.CAlt
context["D'"] = s.DAlt
context["E'"] = s.EAlt
context["H'"] = s.HAlt
context["L'"] = s.LAlt
context["PC"] = s.PC
context["SP"] = s.SP
context["IX"] = s.IX
context["IY"] = s.IY
context["ZF"] = s.Flags.Z
context["SF"] = s.Flags.S
context["NF"] = s.Flags.N
context["PF"] = s.Flags.P
context["HF"] = s.Flags.H
context["YF"] = s.Flags.Y
context["XF"] = s.Flags.X
context["CF"] = s.Flags.C
context["BC"] = uint16(s.B)<<8 | uint16(s.C)
context["DE"] = uint16(s.D)<<8 | uint16(s.E)
context["HL"] = uint16(s.H)<<8 | uint16(s.L)
context["AF"] = uint16(s.A)<<8 | uint16(s.Flags.GetFlags())
context["MEM"] = c.memory.MemRead
return context
}
func (c *ComputerType) Do() (uint32, uint16, byte) {
ticks := uint32(0)
var memAccess *map[uint16]byte
if c.debugger.StepMode() {
if c.debugger.RunMode() || c.debugger.DoStep() {
if c.debugger.RunInst() > 0 {
// skip first instruction after run-mode activated
bpHit, bp := c.debugger.CheckBreakpoints(c.getContext())
if bpHit {
//c.debugger.SetRunMode(false)
return 0, bp, 0
}
}
c.debugger.SaveHistory(c.cpu.GetState())
ticks, memAccess = c.cpu.RunInstruction()
mHit, mAddr, mTyp := c.debugger.CheckMemBreakpoints(memAccess)
if mHit {
return ticks, mAddr, mTyp
}
}
} else {
ticks, memAccess = c.cpu.RunInstruction()
}
c.cycles += uint64(ticks)
c.tstatesPartial += uint64(ticks)
return ticks, 0, 0
}
func (c *ComputerType) GetPixel(x uint16, y uint16) color.RGBA {
if y > 255 {
return CWhite
}
var addr uint16
var resColor color.RGBA
if c.colorMode {
if x > 255 {
return CWhite
}
var offset uint16
if (c.vShift != 0) && (y > 255-uint16(c.vShift)) {
offset = 0x100
} else {
offset = 0
}
y += uint16(c.vShift) & 0x00ff
x += uint16(c.hShift-7) & 0x00ff
// Color 256x256 mode
addr = ((x & 0xf8) << 6) | y
a1 := (addr - offset) & 0x3fff
a2 := (a1 + 0x100) & 0x3fff
cl := (c.vRAM.memory[a1] >> (x & 0x07)) & 1
cl |= ((c.vRAM.memory[a2] >> (x & 0x07)) & 1) << 1
if cl == 0 {
//resColor = BgColorPalette[c.bgColor]
resColor = ColorPalette[c.palette][cl]
} else {
resColor = ColorPalette[c.palette][cl]
}
} else {
if x > 511 {
return CWhite
}
var offset uint16
if (c.vShift != 0) && (y > 255-uint16(c.vShift)) {
offset = 0x100
} else {
offset = 0
}
// Shifts
y += uint16(c.vShift) & 0x00ff
x += uint16(c.hShift-7) & 0x001ff
// Mono 512x256 mode
addr = (((x & 0x1f8) << 5) + y) - offset
pix := c.vRAM.memory[addr&0x3fff] >> (x & 0x07) & 1
if c.palette == 6 {
if pix == 0 {
resColor = CBlack
} else {
resColor = CLGreen
}
} else {
if pix == 0 {
resColor = BgColorPalette[c.bgColor]
} else {
resColor = MonoPalette[c.palette]
}
}
}
return resColor
}
func (c *ComputerType) ScreenWidth() int {
return c.screenWidth
}
func (c *ComputerType) ScreenHeight() int {
return c.screenHeight
}
func (c *ComputerType) Cycles() uint64 {
return c.cycles
}
func (c *ComputerType) ResetTStatesPartial() {
c.tstatesPartial = 0
}
func (c *ComputerType) TStatesPartial() uint64 {
return c.tstatesPartial
}
func (c *ComputerType) TimerClk() {
// DD70 KR580VI53 CLK0, CKL1 @ 1.5MHz
c.pit.Tick(0)
c.pit.Tick(1)
// IRQ from timer
if c.pit.Fired(0) {
c.pic.SetIRQ(RstTimerNo)
}
// clock for SIO KR580VV51
if c.pit.Fired(1) {
c.usart.Tick()
}
}
func (c *ComputerType) LoadFloppy(drive byte) error {
return c.fdc.LoadFloppy(drive)
}
func (c *ComputerType) SaveFloppy(drive byte) error {
return c.fdc.SaveFloppy(drive)
}
func (c *ComputerType) SetSerialBytes(bytes []byte) {
c.usart.SetRxBytes(bytes)
}
func (c *ComputerType) SetRamBytes(bytes []byte) {
addr := 0x100
for i := 0; i < len(bytes); i++ {
c.memory.MemWrite(uint16(addr), bytes[i])
addr++
}
pages := len(bytes) / 256
if len(bytes)%256 != 0 {
pages++
}
log.Debugf("Loaded bytes: %d; blocks: %d", len(bytes), pages)
//c.cpu.SP = 0x100
//c.cpu.PC = 0x100
}
func (c *ComputerType) Dump(start uint16, length uint16) {
file, err := os.Create("dump.dat")
if err != nil {
log.Error(err)
return
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
log.Error(err)
}
}(file)
var buffer []byte
for addr := start; addr < start+length; addr++ {
buffer = append(buffer, c.memory.MemRead(addr))
}
_, err = file.Write(buffer)
err = binary.Write(file, binary.LittleEndian, buffer)
if err != nil {
log.Error("Save memory dump failed:", err)
} else {
log.Debug("Memory dump saved successfully")
}
}
func (c *ComputerType) CPUFrequency() uint32 {
return c.cpuFrequency
}
func (c *ComputerType) SetCPUFrequency(frequency uint32) {
c.cpuFrequency = frequency
}
func (c *ComputerType) DebuggerState() string {
if c.debugger.StepMode() {
if c.debugger.RunMode() {
return "Run"
}
return "Step"
}
return "Off"
}
func (c *ComputerType) memoryAsHexStr() string {
res := ""
for addr := 0; addr <= 65535; addr++ {
res += fmt.Sprintf("%02X", c.memory.MemRead(uint16(addr)))
}
return res
}
func (c *ComputerType) SaveSnapshot(fn string) error {
// take snapshot
s := Snapshot{
CPU: c.cpu.GetState(),
Memory: c.memoryAsHexStr(),
}
// convert to JSON
b, err := json.Marshal(s)
if err != nil {
return err
}
// and save
err = os.WriteFile(fn, b, 0644)
if err != nil {
return err
}
return nil
}
func (c *ComputerType) LoadSnapshot(fn string) error {
// read snapshot file
b, err := os.ReadFile(fn)
if err != nil {
return err
}
// unmarshal from JSON
var result Snapshot
err = json.Unmarshal(b, &result)
if err != nil {
return err
}
c.cpu.SetState(result.CPU)
return c.restoreMemoryFromHex(result.Memory)
}
func (c *ComputerType) restoreMemoryFromHex(memory string) error {
for addr := 0; addr <= 65535; addr++ {
b, e := strconv.ParseUint(memory[addr*2:addr*2+2], 16, 8)
if e != nil {
log.Error(e)
return e
}
c.memory.MemWrite(uint16(addr), byte(b))
}
return nil
}
func (c *ComputerType) AutoSaveFloppy() {
for drv := byte(0); drv < fdc.TotalDrives; drv++ {
if c.config.FDC[drv].AutoSave {
e := c.fdc.SaveFloppy(drv)
if e != nil {
log.Error(e)
}
}
}
}
func (c *ComputerType) AutoLoadFloppy() {
for drv := byte(0); drv < fdc.TotalDrives; drv++ {
if c.config.FDC[drv].AutoLoad {
e := c.fdc.LoadFloppy(drv)
if e != nil {
log.Error(e)
}
}
}
}
func (c *ComputerType) ClearCodeCoverage() {
c.cpu.ClearCodeCoverage()
}
func (c *ComputerType) SetCodeCoverage(enabled bool) {
c.cpu.SetCodeCoverage(enabled)
}
func (c *ComputerType) CodeCoverage() map[uint16]bool {
return c.cpu.CodeCoverage()
}
func (c *ComputerType) SetExtendedStack(enabled bool) {
c.cpu.SetExtendedStack(enabled)
}
func (c *ComputerType) ExtendedStack() ([]byte, error) {
return c.cpu.ExtendedStack()
}