mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-03-13 10:31:36 +00:00
Support for the Softcard Z80 card and CP/M
This commit is contained in:
parent
c89aa36f1d
commit
87dbe86f78
@ -38,6 +38,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
|
||||
- MultiROM card
|
||||
- Dan ][ Controller card
|
||||
- ProDOS ROM card
|
||||
- Microsoft Z80 Softcard using the [Z80](https://github.com/koron-go/z80) emulation from Koron
|
||||
- Useful cards not emulating a real card
|
||||
- Bootable SmartPort / ProDOS card with the following smartport devices:
|
||||
- Block device (hard disks)
|
||||
@ -230,6 +231,7 @@ The available pre-configured models are:
|
||||
2plus: Apple ][+
|
||||
base64a: Base 64A
|
||||
basis108: Basis 108
|
||||
cpm: Apple ][+ with CP/M
|
||||
dos32: Apple ][ with 13 sectors disk adapter and DOS 3.2x
|
||||
swyft: swyft
|
||||
|
||||
@ -256,6 +258,7 @@ The available cards are:
|
||||
thunderclock: Clock card
|
||||
videx: Videx compatible 80 columns card
|
||||
vidhd: Firmware signature of the VidHD card to trick Total Replay to use the SHR mode
|
||||
z80softcard: Microsoft Z80 SoftCard to run CP/M
|
||||
|
||||
The available tracers are:
|
||||
cpm65: Trace CPM65 BDOS calls
|
||||
|
@ -61,6 +61,7 @@ func getCardFactory() map[string]*cardBuilder {
|
||||
cardFactory["thunderclock"] = newCardThunderClockPlusBuilder()
|
||||
cardFactory["videx"] = newCardVidexBuilder()
|
||||
cardFactory["vidhd"] = newCardVidHDBuilder()
|
||||
cardFactory["z80softcard"] = newCardZ80SoftCardBuilder()
|
||||
return cardFactory
|
||||
}
|
||||
|
||||
|
117
cardZ80Softcard.go
Normal file
117
cardZ80Softcard.go
Normal file
@ -0,0 +1,117 @@
|
||||
package izapple2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/koron-go/z80"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
Microsoft Z80 SoftCard
|
||||
See:
|
||||
http://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Interface%20Cards/Z80%20Cards/Microsoft%20SoftCard/
|
||||
|
||||
|
||||
This card activates DMA to take control of the system. DMA is actiavted or
|
||||
deactivated by writing to the Csxx area.
|
||||
|
||||
The emulation works on the Apple II+, but doesn't work when 80 columns are
|
||||
available. It is not working then on the Apple IIe or the Apple II+ with a
|
||||
Videx card.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// CardVidHD represents a VidHD card
|
||||
type CardZ80SoftCard struct {
|
||||
cardBase
|
||||
|
||||
cpu *z80.CPU
|
||||
z80Active bool
|
||||
}
|
||||
|
||||
func newCardZ80SoftCardBuilder() *cardBuilder {
|
||||
return &cardBuilder{
|
||||
name: "Microsoft Z80 SoftCard",
|
||||
description: "Microsoft Z80 SoftCard to run CP/M",
|
||||
buildFunc: func(params map[string]string) (Card, error) {
|
||||
var c CardZ80SoftCard
|
||||
c.romCxxx = &cardROMWriteTrap{
|
||||
callback: func() {
|
||||
c.flipDMA()
|
||||
},
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CardZ80SoftCard) assign(a *Apple2, slot int) {
|
||||
mem := &cardZ80SoftCardMMU{
|
||||
mmu: a.mmu,
|
||||
}
|
||||
|
||||
c.cpu = &z80.CPU{
|
||||
Memory: mem,
|
||||
}
|
||||
|
||||
c.cardBase.assign(a, slot)
|
||||
}
|
||||
|
||||
func (c *CardZ80SoftCard) flipDMA() {
|
||||
c.tracef("Z80 DMA flip\n")
|
||||
c.z80Active = !c.z80Active
|
||||
if c.z80Active {
|
||||
c.activateDMA()
|
||||
} else {
|
||||
c.deactivateDMA()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CardZ80SoftCard) runDMACycle() {
|
||||
if c.a.cpuTrace {
|
||||
fmt.Printf("Z80 pc=$%04x ($%04x for the 6502) Opcode: $%02x \n",
|
||||
c.cpu.PC, z80AddressTranslation(c.cpu.PC), c.cpu.Memory.Get(c.cpu.PC))
|
||||
}
|
||||
c.cpu.Step()
|
||||
}
|
||||
|
||||
type cardROMWriteTrap struct {
|
||||
callback func()
|
||||
}
|
||||
|
||||
func (r *cardROMWriteTrap) peek(address uint16) uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *cardROMWriteTrap) poke(address uint16, value uint8) {
|
||||
if address >= 0xC000 && address < 0xC800 {
|
||||
r.callback()
|
||||
}
|
||||
}
|
||||
|
||||
type cardZ80SoftCardMMU struct {
|
||||
mmu *memoryManager
|
||||
}
|
||||
|
||||
func (m *cardZ80SoftCardMMU) Get(addr uint16) uint8 {
|
||||
return m.mmu.Peek(z80AddressTranslation(addr))
|
||||
}
|
||||
|
||||
func (m *cardZ80SoftCardMMU) Set(addr uint16, value uint8) {
|
||||
m.mmu.Poke(z80AddressTranslation(addr), value)
|
||||
}
|
||||
|
||||
func z80AddressTranslation(addr uint16) uint16 {
|
||||
if addr < 0xb000 {
|
||||
return addr + 0x1000
|
||||
} else if addr < 0xe000 {
|
||||
return addr + 0x2000
|
||||
} else if addr < 0xf000 {
|
||||
return addr - 0x2000
|
||||
} else {
|
||||
return addr - 0xf000
|
||||
}
|
||||
}
|
28
cardZ80softcard_test.go
Normal file
28
cardZ80softcard_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
package izapple2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCPMBoot(t *testing.T) {
|
||||
at, err := makeApple2Tester("cpm", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
banner := "APPLE ][ CP/M"
|
||||
prompt := "A>"
|
||||
at.terminateCondition = buildTerminateConditionTexts([]string{banner, prompt}, testTextMode40, 10_000_000)
|
||||
|
||||
at.run()
|
||||
|
||||
text := at.getText(testTextMode40)
|
||||
if !strings.Contains(text, banner) {
|
||||
t.Errorf("Expected '%s', got '%s'", banner, text)
|
||||
}
|
||||
if !strings.Contains(text, prompt) {
|
||||
t.Errorf("Expected prompt '%s', got '%s'", prompt, text)
|
||||
}
|
||||
|
||||
}
|
6
configs/cpm.cfg
Normal file
6
configs/cpm.cfg
Normal file
@ -0,0 +1,6 @@
|
||||
name: Apple ][+ with CP/M
|
||||
parent: 2plus
|
||||
# Fails with 80 columns
|
||||
s3: empty
|
||||
s4: z80softcard
|
||||
s6: diskii,disk1=<internal>/cpm_2.20B_56K.po
|
@ -51,6 +51,7 @@ The available pre-configured models are:
|
||||
2plus: Apple ][+
|
||||
base64a: Base 64A
|
||||
basis108: Basis 108
|
||||
cpm: Apple ][+ with CP/M
|
||||
dos32: Apple ][ with 13 sectors disk adapter and DOS 3.2x
|
||||
swyft: swyft
|
||||
|
||||
@ -77,6 +78,7 @@ The available cards are:
|
||||
thunderclock: Clock card
|
||||
videx: Videx compatible 80 columns card
|
||||
vidhd: Firmware signature of the VidHD card to trick Total Replay to use the SHR mode
|
||||
z80softcard: Microsoft Z80 SoftCard to run CP/M
|
||||
|
||||
The available tracers are:
|
||||
cpm65: Trace CPM65 BDOS calls
|
||||
|
1
go.mod
1
go.mod
@ -6,6 +6,7 @@ require (
|
||||
fyne.io/fyne/v2 v2.1.4
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240118000515-a250818d05e3
|
||||
github.com/ivanizag/iz6502 v1.4.0
|
||||
github.com/koron-go/z80 v0.10.1
|
||||
github.com/pkg/profile v1.7.0
|
||||
github.com/veandco/go-sdl2 v0.4.38
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
||||
|
2
go.sum
2
go.sum
@ -42,6 +42,8 @@ github.com/ivanizag/iz6502 v1.4.0 h1:7eYygUkCPwFRH0tf2JSg1k+Sy27wwPi3ActuVNVv1Uc
|
||||
github.com/ivanizag/iz6502 v1.4.0/go.mod h1:h4gbw3IK6WCYawi00kBhQ4ACeQkGWgqbUeAgDaQpy6s=
|
||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||
github.com/koron-go/z80 v0.10.1 h1:Jfb0esP/QFL4cvcr+eFECVG0Y/mA9JBLC4EKbMU5zAY=
|
||||
github.com/koron-go/z80 v0.10.1/go.mod h1:ry+Zl9kRKelzaDG9UzEtUpUnXy0Yv/kk1YEaX958xdk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
|
BIN
resources/cpm_2.20B_56K.po
Normal file
BIN
resources/cpm_2.20B_56K.po
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user