diff --git a/emulator/fdc/fdc.go b/emulator/fdc/fdc.go index aafd9b2..43309e8 100644 --- a/emulator/fdc/fdc.go +++ b/emulator/fdc/fdc.go @@ -1,13 +1,10 @@ package fdc import ( - "github.com/laullon/b2t80s/emulator" "github.com/laullon/b2t80s/emulator/files" ) type FDC interface { - emulator.PortManager // DEPRECATED - ReadData() byte ReadStatus() byte WriteData(val byte) diff --git a/emulator/fdc/fdc765.go b/emulator/fdc/fdc765.go index 36740ce..8fb3672 100644 --- a/emulator/fdc/fdc765.go +++ b/emulator/fdc/fdc765.go @@ -154,7 +154,7 @@ func (fdc *fdc765) LOAD_RESULT_WITH_CHRN() { func (fdc *fdc765) isDriveReady() bool { val := fdc.cmd.args[CMD_UNIT] & 0b111 - println("isDriveReady", fdc.discActive, fdc.motor) + // println("isDriveReady", fdc.discActive, fdc.motor) if fdc.discs[fdc.discActive] == nil || (!fdc.motor) { val |= 0x48 // Abnormal Termination + Not Ready } @@ -220,34 +220,3 @@ func (fdc *fdc765) ReadStatus() byte { func (fdc *fdc765) SetMotor(on bool) { fdc.motor = on } - -// TODO: move theses 2 methods to a cpc specific -func (fdc *fdc765) ReadPort(port uint16) (byte, bool) { - if (port & (1 << 10)) == 0 { - f := ((port & (1 << 8)) >> (8 - 1)) | (port & 0x01) - switch f { - case 2: - return fdc.ReadStatus(), false - case 3: - return fdc.ReadData(), false - default: - panic(f) - } - } else { - panic(fmt.Sprintf("port: 0x%04X", port)) - } -} - -func (fdc *fdc765) WritePort(port uint16, data byte) { - if (port & (1 << 7)) == 0 { - f := ((port & 0x0100) >> (8 - 1)) | (port & 0x01) - switch f { - case 0: - fdc.motor = data == 1 - case 3: - fdc.WriteData(data) - default: - panic(fmt.Sprintf("port: 0x%04X data: 0x%02X f:%d", port, data, f)) - } - } -} diff --git a/machines/cpc/cpc.go b/machines/cpc/cpc.go index c77d9d4..d6d155c 100644 --- a/machines/cpc/cpc.go +++ b/machines/cpc/cpc.go @@ -9,7 +9,6 @@ import ( "github.com/laullon/b2t80s/data" "github.com/laullon/b2t80s/emulator" "github.com/laullon/b2t80s/emulator/ay8912" - "github.com/laullon/b2t80s/emulator/fdc" "github.com/laullon/b2t80s/emulator/files" "github.com/laullon/b2t80s/machines" "github.com/laullon/b2t80s/z80" @@ -79,11 +78,11 @@ func NewCPC(cpc464 bool, cassette emulator.Cassette) machines.Machine { // cpu.RegisterPort(emulator.PortMask{Mask: 0xDF00, Value: 0xDF00}, mem) cpu.RegisterPort(emulator.PortMask{Mask: 0x2000, Value: 0x0000}, mem) - fdc := fdc.New765() + fdc := NewCPCFDC765() cpu.RegisterPort(emulator.PortMask{Mask: 0x0400, Value: 0x0000}, fdc) if len(*machines.DskAFile) > 0 { disc := files.LoadDsk(*machines.DskAFile) - fdc.SetDiscA(disc) + fdc.chip.SetDiscA(disc) // fmt.Printf("%v\n", disc) } // if len(*dskBFile) > 0 { @@ -105,10 +104,10 @@ func NewCPC(cpc464 bool, cassette emulator.Cassette) machines.Machine { cpu.SetClock(cpc.clock) mem.SetClock(cpc.clock) - cpc.clock.AddTicker(0, crtc) + cpc.clock.AddTicker(4, crtc) cpc.clock.AddTicker(0, cassette) - cpc.clock.AddTicker(0, ga) - cpc.clock.AddTicker(2, ay8912) + cpc.clock.AddTicker(4, ga) + cpc.clock.AddTicker(4, ay8912) cpc.clock.AddTicker(80, sound) // if *machines.LoadSlow { @@ -156,7 +155,7 @@ func (m *cpc) OnKeyEvent(event *fyne.KeyEvent) { } func (m *cpc) Display() image.Image { - return m.ga.displayScaled + return m.ga.display } func (m *cpc) GetVolumeControl() func(float64) { @@ -184,7 +183,7 @@ func (m *cpc) Run() { frames++ frameTime := time.Now().Sub(frameStart) runTime := time.Now().Sub(runStart) - m.Debugger().SetStatus(fmt.Sprintf("frame rate:%6.2f time:%6.2fms (%v) (ear:%v) (crtc.sl:%d)", frames/runTime.Seconds(), float64(frameTime.Microseconds())/1000, wait, m.cassette.Ear(), m.ga.crtc.cycles)) + m.Debugger().SetStatus(fmt.Sprintf("frame rate:%6.2f time:%6.2fms (%v) (ear:%v)", frames/runTime.Seconds(), float64(frameTime.Microseconds())/1000, wait, m.cassette.Ear())) } }() } diff --git a/machines/cpc/crtc.go b/machines/cpc/crtc.go index faaca19..1f008fe 100644 --- a/machines/cpc/crtc.go +++ b/machines/cpc/crtc.go @@ -14,14 +14,14 @@ type crtcStatus struct { vSync bool hSync bool disPen bool - ma uint16 + ma uint32 + ra int } type crtcCounters struct { - h int - sl int - row int - raster int + hcc int // horizontal char counter + vlc int // vertical line counter + vcc int // vertical char counter // ra } type crtc struct { @@ -33,9 +33,6 @@ type crtc struct { regs []byte selectReg byte - cycles uint32 - clock uint32 - addr uint32 vSyncOn, vSyncOff int @@ -63,7 +60,7 @@ func (crtc *crtc) WritePort(port uint16, data byte) { case 1: crtc.regs[crtc.selectReg] = data & regMasks[crtc.selectReg] - fmt.Printf("[crtc] reg: %2d = %d\n", crtc.selectReg, data) + // fmt.Printf("[crtc] reg: %2d = %d\n", crtc.selectReg, data) crtc.recalcule() default: @@ -76,7 +73,7 @@ func (crtc *crtc) recalcule() { // println("[crtc] addr:", crtc.addr) crtc.vSyncOn = int(crtc.regs[7]) - vSyncSize := int((crtc.regs[3] >> 4) & 0x0f) + vSyncSize := int((crtc.regs[3] >> 4) & 0x0f / 8) crtc.vSyncOff = crtc.vSyncOn + vSyncSize // println("[crtc] vSyncOn:", crtc.vSyncOn, "vSyncOff:", crtc.vSyncOff) @@ -84,59 +81,43 @@ func (crtc *crtc) recalcule() { hSyncSize := int(crtc.regs[3] & 0x0f) crtc.hSyncOff = crtc.hSyncOn + hSyncSize // println("[crtc] hSyncOn:", crtc.hSyncOn, "hSyncOff:", crtc.hSyncOff) - } func (crtc *crtc) Tick() { - clock := crtc.cycles / 4 - crtc.cycles++ - - if crtc.clock == clock { - return - } - crtc.clock = clock - R0 := int(crtc.regs[0]) R1 := int(crtc.regs[1]) R4 := int(crtc.regs[4]) R6 := int(crtc.regs[6]) R9 := int(crtc.regs[9]) - if crtc.counters.h < R0 { - crtc.counters.h++ + if crtc.counters.hcc < R0 { + crtc.counters.hcc++ } else { - crtc.counters.h = 0 - if crtc.counters.sl < R9 { - crtc.counters.sl++ + crtc.counters.hcc = 0 + if crtc.counters.vlc < R9 { + crtc.counters.vlc++ } else { - crtc.counters.sl = 0 - if crtc.counters.row < R4 { - crtc.counters.row++ + crtc.counters.vlc = 0 + if crtc.counters.vcc < R4 { + crtc.counters.vcc++ } else { - crtc.counters.row = 0 + crtc.counters.vcc = 0 } } - crtc.counters.raster = int(crtc.counters.row*(R9+1)) + int(crtc.counters.sl) - } - - if crtc.counters.h == crtc.hSyncOff { - if crtc.counters.raster%52 == 0 { - crtc.cpu.Interrupt(true) - } } - crtc.status.vSync = (crtc.counters.row >= crtc.vSyncOn) && (crtc.counters.row <= crtc.vSyncOff) - crtc.status.hSync = (crtc.counters.h >= crtc.hSyncOn) && (crtc.counters.h <= crtc.hSyncOff) - crtc.status.disPen = (crtc.counters.h < R1) && (crtc.counters.row < R6) + crtc.status.vSync = (crtc.counters.vcc >= crtc.vSyncOn) && (crtc.counters.vcc <= crtc.vSyncOff) + crtc.status.hSync = (crtc.counters.hcc >= crtc.hSyncOn) && (crtc.counters.hcc <= crtc.hSyncOff) + crtc.status.disPen = (crtc.counters.hcc < R1) && (crtc.counters.vcc < R6) - MA := crtc.addr + uint32(crtc.counters.row)*uint32(R1) + uint32(crtc.counters.h) - crtc.status.ma = uint16(((MA & 0x3FF) << 1) | ((uint32(crtc.counters.raster) & 7) << 11) | ((MA & 0x3000) << 2)) + crtc.status.ma = crtc.addr + uint32(crtc.counters.vcc)*uint32(R1) + uint32(crtc.counters.hcc) + crtc.status.ra = crtc.counters.vlc // if crtc.counters.h == 0 { // fmt.Printf("=> %+v => %+v => 0x%04X\n", crtc.counters, crtc.status, crtc.status.ma) // } } -func (crtc *crtc) FrameEnded() { - crtc.cycles = 0 +func (st *crtcStatus) getAddress() uint16 { + return uint16(((st.ma & 0x3FF) << 1) | ((uint32(st.ra) & 7) << 11) | ((st.ma & 0x3000) << 2)) } diff --git a/machines/cpc/fdc765.go b/machines/cpc/fdc765.go new file mode 100644 index 0000000..d45ddfb --- /dev/null +++ b/machines/cpc/fdc765.go @@ -0,0 +1,44 @@ +package cpc + +import ( + "fmt" + + "github.com/laullon/b2t80s/emulator/fdc" +) + +type fdc765 struct { + chip fdc.FDC +} + +func NewCPCFDC765() *fdc765 { + return &fdc765{ + chip: fdc.New765(), + } +} + +func (fdc *fdc765) ReadPort(port uint16) (byte, bool) { + if (port & (1 << 10)) == 0 { + f := ((port & (1 << 8)) >> (8 - 1)) | (port & 0x01) + switch f { + case 2: + return fdc.chip.ReadStatus(), false + case 3: + return fdc.chip.ReadData(), false + default: + panic(f) + } + } else { + panic(fmt.Sprintf("port: 0x%04X", port)) + } +} + +func (fdc *fdc765) WritePort(port uint16, data byte) { + switch port { + case 0xFA7E: + fdc.chip.SetMotor(data == 1) + case 0xFB7F: + fdc.chip.WriteData(data) + default: + fmt.Printf("port: 0x%04X data: 0x%02X \n", port, data) + } +} diff --git a/machines/cpc/gatearray.go b/machines/cpc/gatearray.go index 63faa5f..90d4bb9 100644 --- a/machines/cpc/gatearray.go +++ b/machines/cpc/gatearray.go @@ -4,17 +4,12 @@ import ( "fmt" "image" "image/color" - - "golang.org/x/image/draw" ) type gatearray struct { mem *memory crtc *crtc - cycles uint32 - clock uint32 - screenMode byte decode func(byte) []byte ppc int @@ -25,83 +20,147 @@ type gatearray struct { pen byte palette []color.RGBA - display *image.RGBA - displayFrame image.Rectangle - displayScaled *image.RGBA + display *image.RGBA + + prevHSync, prevVSync bool + hSyncCount, hSyncsInVSyncCount byte + + x, y int } var colours = []color.RGBA{ - color.RGBA{0x7f, 0x7f, 0x7f, 0xff}, - color.RGBA{0x7f, 0x7f, 0x7f, 0xff}, - color.RGBA{0x00, 0xff, 0x7f, 0xff}, - color.RGBA{0xff, 0xff, 0x7f, 0xff}, - color.RGBA{0x00, 0x00, 0x7f, 0xff}, - color.RGBA{0xff, 0x00, 0x7f, 0xff}, - color.RGBA{0x00, 0x7f, 0x7f, 0xff}, - color.RGBA{0xff, 0x7f, 0x7f, 0xff}, - color.RGBA{0xff, 0x00, 0x7f, 0xff}, - color.RGBA{0xff, 0xff, 0x7f, 0xff}, - color.RGBA{0xff, 0xff, 0x00, 0xff}, - color.RGBA{0xff, 0xff, 0xff, 0xff}, - color.RGBA{0xff, 0x00, 0x00, 0xff}, - color.RGBA{0xff, 0x00, 0xff, 0xff}, - color.RGBA{0xff, 0x7f, 0x00, 0xff}, - color.RGBA{0xff, 0x7f, 0xff, 0xff}, - color.RGBA{0x00, 0x00, 0x7f, 0xff}, - color.RGBA{0x00, 0xff, 0x7f, 0xff}, - color.RGBA{0x00, 0xff, 0x00, 0xff}, - color.RGBA{0x00, 0xff, 0xff, 0xff}, - color.RGBA{0x00, 0x00, 0x00, 0xff}, - color.RGBA{0x00, 0x00, 0xff, 0xff}, - color.RGBA{0x00, 0x7f, 0x00, 0xff}, - color.RGBA{0x00, 0x7f, 0xff, 0xff}, - color.RGBA{0x7f, 0x00, 0x7f, 0xff}, - color.RGBA{0x7f, 0xff, 0x7f, 0xff}, - color.RGBA{0x7f, 0xff, 0x00, 0xff}, - color.RGBA{0x7f, 0xff, 0xff, 0xff}, - color.RGBA{0x7f, 0x00, 0x00, 0xff}, - color.RGBA{0x7f, 0x00, 0xff, 0xff}, - color.RGBA{0x7f, 0x7f, 0x00, 0xff}, - color.RGBA{0x7f, 0x7f, 0xff, 0xff}, + {0x7f, 0x7f, 0x7f, 0xff}, + {0x7f, 0x7f, 0x7f, 0xff}, + {0x00, 0xff, 0x7f, 0xff}, + {0xff, 0xff, 0x7f, 0xff}, + {0x00, 0x00, 0x7f, 0xff}, + {0xff, 0x00, 0x7f, 0xff}, + {0x00, 0x7f, 0x7f, 0xff}, + {0xff, 0x7f, 0x7f, 0xff}, + {0xff, 0x00, 0x7f, 0xff}, + {0xff, 0xff, 0x7f, 0xff}, + {0xff, 0xff, 0x00, 0xff}, + {0xff, 0xff, 0xff, 0xff}, + {0xff, 0x00, 0x00, 0xff}, + {0xff, 0x00, 0xff, 0xff}, + {0xff, 0x7f, 0x00, 0xff}, + {0xff, 0x7f, 0xff, 0xff}, + {0x00, 0x00, 0x7f, 0xff}, + {0x00, 0xff, 0x7f, 0xff}, + {0x00, 0xff, 0x00, 0xff}, + {0x00, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0xff}, + {0x00, 0x00, 0xff, 0xff}, + {0x00, 0x7f, 0x00, 0xff}, + {0x00, 0x7f, 0xff, 0xff}, + {0x7f, 0x00, 0x7f, 0xff}, + {0x7f, 0xff, 0x7f, 0xff}, + {0x7f, 0xff, 0x00, 0xff}, + {0x7f, 0xff, 0xff, 0xff}, + {0x7f, 0x00, 0x00, 0xff}, + {0x7f, 0x00, 0xff, 0xff}, + {0x7f, 0x7f, 0x00, 0xff}, + {0x7f, 0x7f, 0xff, 0xff}, } func newGateArray(mem *memory, crtc *crtc) *gatearray { - return &gatearray{ - mem: mem, - crtc: crtc, - palette: make([]color.RGBA, 16), - displayScaled: image.NewRGBA(image.Rect(0, 0, 768, 576)), - display: image.NewRGBA(image.Rect(0, 0, 640, 200)), - displayFrame: image.Rect((768-640)/2, (576-400)/2, 640+(768-640)/2, 400+(576-400)/2), - - decode: to2bpp, - ppc: 8, + ga := &gatearray{ + mem: mem, + crtc: crtc, + palette: make([]color.RGBA, 16), + display: image.NewRGBA(image.Rect(0, 0, 512, 312)), + + decode: to1bpp, + ppc: 16, } + + return ga } func (ga *gatearray) Tick() { - clock := ga.cycles / 4 - ga.cycles++ + if !ga.prevHSync && ga.crtc.status.hSync { + ga.x = 0 + ga.y++ + } - if ga.clock == clock { - return + if !ga.prevVSync && ga.crtc.status.vSync { + ga.y = 0 + ga.display.Rect.Min = image.Point{X: int(ga.crtc.regs[3]&0x0f) * 8, Y: 34} + ga.display.Rect.Max = image.Point{X: int(ga.crtc.regs[3]&0x0f)*8 + 384, Y: 34 + 272} } - ga.clock = clock if ga.crtc.status.disPen { - page := ga.crtc.status.ma >> 14 - pos := ga.crtc.status.ma & 0x3fff - cs := ga.decode(ga.mem.banks[page][pos]) - cs = append(cs, ga.decode(ga.mem.banks[page][(pos+1)])...) + addr := ga.crtc.status.getAddress() + cs := ga.decode(ga.mem.getScreenByte(addr)) + cs = append(cs, ga.decode(ga.mem.getScreenByte(addr+1))...) for off, c := range cs { - ga.display.Set((ga.crtc.counters.h*ga.ppc)+off, ga.crtc.counters.raster, ga.palette[c]) + ga.display.Set((ga.x*8)+off, ga.y, ga.palette[c]) } + } else { + for i := 0; i < 8; i++ { + ga.display.Set((ga.x*8)+i, ga.y, ga.borderColor) + } + + // if ga.crtc.status.hSync || ga.crtc.status.vSync { + // for i := 0; i < 8; i += 2 { + // ga.display.Set((ga.x*8)+i, ga.y, color.RGBA{0x00, 0x00, 0x00, 0xff}) + // } + // } + + // if ga.x == 0 { + // if ga.screenMode == 0 { + // for i := 0; i < 8; i++ { + // ga.display.Set((ga.x*8)+i, ga.y, color.RGBA{0x00, 0xff, 0x00, 0xff}) + // } + // } else if ga.screenMode == 1 { + // for i := 0; i < 8; i++ { + // ga.display.Set((ga.x*8)+i, ga.y, color.RGBA{0x00, 0x00, 0xff, 0xff}) + // } + // } + // } + + // if ga.x == 1 { + // if ga.hSyncCount == 0 { + // for i := 0; i < 8; i++ { + // ga.display.Set((ga.x*8)+i, ga.y, color.RGBA{0xff, 0x00, 0x00, 0xff}) + // } + // } + // } } + + if !ga.prevVSync && ga.crtc.status.vSync { + ga.hSyncsInVSyncCount = 0 + } + + if !ga.prevHSync && ga.crtc.status.hSync { + ga.hSyncCount++ + if ga.hSyncCount == 52 { + ga.hSyncCount = 0 + ga.crtc.cpu.Interrupt(true) + } + + ga.hSyncsInVSyncCount++ + if ga.hSyncsInVSyncCount == 2 { + if ga.hSyncCount >= 32 { + ga.crtc.cpu.Interrupt(true) + } + ga.hSyncCount = 0 + } + } + + ga.x++ + + ga.prevHSync = ga.crtc.status.hSync + ga.prevVSync = ga.crtc.status.vSync + + if ga.crtc.status.disPen { + ga.mem.clock.AddTStates(4) + } + } func (ga *gatearray) FrameEnded() { - ga.cycles = 0 - draw.NearestNeighbor.Scale(ga.displayScaled, ga.displayFrame, ga.display, ga.display.Bounds(), draw.Over, nil) + // draw.NearestNeighbor.Scale(ga.display, ga.displayFrame, ga.display, ga.display.Bounds(), draw.Over, nil) } func bit2value(b, bit, v byte) byte { @@ -114,7 +173,7 @@ func bit2value(b, bit, v byte) byte { func to4bpp(b byte) []byte { pixel1 := bit2value(b, 1, 0b1000) | bit2value(b, 5, 0b0100) | bit2value(b, 3, 0b0010) | bit2value(b, 7, 0b0001) pixel2 := bit2value(b, 0, 0b1000) | bit2value(b, 4, 0b0100) | bit2value(b, 2, 0b0010) | bit2value(b, 6, 0b0001) - return []byte{pixel1, pixel2} + return []byte{pixel1, pixel1, pixel2, pixel2} } func to2bpp(b byte) []byte { @@ -152,7 +211,8 @@ func (ga *gatearray) WritePort(port uint16, data byte) { } else { if ga.borderColor != colours[data&0x1f] { ga.borderColor = colours[data&0x1f] - draw.Draw(ga.displayScaled, ga.displayScaled.Bounds(), &image.Uniform{ga.borderColor}, image.ZP, draw.Src) + println("ga.borderColor:", data&0x1f, ga.y) + // draw.Draw(ga.display, ga.display.Bounds(), &image.Uniform{ga.borderColor}, image.ZP, draw.Src) } } } else if f == 2 { @@ -161,29 +221,23 @@ func (ga *gatearray) WritePort(port uint16, data byte) { if data&0x10 != 0 { ga.crtc.cpu.Interrupt(false) - ga.crtc.counters.sl = 0 + ga.hSyncCount = 0 } screenMode := data & 0b00000011 if ga.screenMode != screenMode { ga.screenMode = screenMode - // println("screenMode", screenMode) switch screenMode { case 0: - ga.display.Rect = image.Rect(0, 0, 160, 200) ga.decode = to4bpp - ga.ppc = 4 case 1: - ga.display.Rect = image.Rect(0, 0, 320, 200) ga.decode = to2bpp - ga.ppc = 8 case 2: - ga.display.Rect = image.Rect(0, 0, 640, 200) ga.decode = to1bpp - ga.ppc = 16 default: // panic(screenMode) } + println("screenMode", screenMode, ga.y) } // println("[ga]", "lowerRomEnable:", ga.mem.lowerRomEnable, "upperRomEnable:", ga.mem.upperRomEnable, "screenMode:", ga.screenMode) } else if f == 3 { diff --git a/machines/cpc/memory.go b/machines/cpc/memory.go index d84663d..6320d6b 100644 --- a/machines/cpc/memory.go +++ b/machines/cpc/memory.go @@ -97,6 +97,11 @@ func (mem *memory) GetByte(addr uint16) byte { return mem.banks[bank][pos] } +func (mem *memory) getScreenByte(addr uint16) byte { + _, bank, pos := mem.decodeAddress(addr) + return mem.banks[bank][pos] +} + func (mem *memory) PutByte(addr uint16, b byte) { // fmt.Printf("-> addr:0x%08x b:0x%02x \n", addr, b) _, bank, pos := mem.decodeAddress(addr) diff --git a/z80/z80_switch.go b/z80/z80_switch.go index 8edc06d..346af20 100644 --- a/z80/z80_switch.go +++ b/z80/z80_switch.go @@ -490,9 +490,11 @@ func (cpu *z80) runSwitch(ins emulator.Instruction) bool { case 0xF3: // DI cpu.iff1 = false + cpu.iff2 = false case 0xfb: // EI cpu.iff1 = true + cpu.iff2 = true case 0xed46: // IM 0 cpu.interruptsMode = 0