mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-12-31 06:30:18 +00:00
No Slot Clock support, enable with -nsc
This commit is contained in:
parent
c82e57d895
commit
0c7e4b7db2
@ -27,6 +27,7 @@ Portable emulator of an Apple II+ or //e. Written in Go.
|
|||||||
- Apple //e 80 columns with 64Kb extra RAM and optional RGB modes
|
- Apple //e 80 columns with 64Kb extra RAM and optional RGB modes
|
||||||
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
|
- VidHd, limited to the ROM signature and SHR as used by Total Replay, only for //e models with 128Kb
|
||||||
- FASTChip, limited to what Total Replay needs to set and clear fast mode
|
- FASTChip, limited to what Total Replay needs to set and clear fast mode
|
||||||
|
- No Slot Clock based on the DS1216
|
||||||
- Graphic modes:
|
- Graphic modes:
|
||||||
- Text 40 columns
|
- Text 40 columns
|
||||||
- Text 80 columns (Apple //e only)
|
- Text 80 columns (Apple //e only)
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/ivanizag/apple2/core6502"
|
"github.com/ivanizag/apple2/core6502"
|
||||||
)
|
)
|
||||||
|
|
||||||
// newApple2 instantiates an apple2
|
|
||||||
func newApple2plus() *Apple2 {
|
func newApple2plus() *Apple2 {
|
||||||
var a Apple2
|
var a Apple2
|
||||||
a.Name = "Apple ][+"
|
a.Name = "Apple ][+"
|
||||||
@ -191,6 +190,12 @@ func (a *Apple2) AddRAMWorks(banks int) {
|
|||||||
setupRAMWorksCard(a, banks)
|
setupRAMWorksCard(a, banks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddNoSlotClock inserts a DS1215 no slot clock under the main ROM
|
||||||
|
func (a *Apple2) AddNoSlotClock() {
|
||||||
|
nsc := newNoSlotClockDS1216(a, a.mmu.physicalROM[0])
|
||||||
|
a.mmu.physicalROM[0] = nsc
|
||||||
|
}
|
||||||
|
|
||||||
// AddCardLogger inserts a fake card that logs accesses
|
// AddCardLogger inserts a fake card that logs accesses
|
||||||
func (a *Apple2) AddCardLogger(slot int) {
|
func (a *Apple2) AddCardLogger(slot int) {
|
||||||
a.insertCard(&cardLogger{}, slot)
|
a.insertCard(&cardLogger{}, slot)
|
||||||
|
@ -81,6 +81,10 @@ func MainApple() *Apple2 {
|
|||||||
"thunderClockCardSlot",
|
"thunderClockCardSlot",
|
||||||
4,
|
4,
|
||||||
"slot for the ThunderClock Plus card. -1 for none")
|
"slot for the ThunderClock Plus card. -1 for none")
|
||||||
|
nsc := flag.Bool(
|
||||||
|
"nsc",
|
||||||
|
false,
|
||||||
|
"add a DS1216 No-Slot-Clock")
|
||||||
mono := flag.Bool(
|
mono := flag.Bool(
|
||||||
"mono",
|
"mono",
|
||||||
false,
|
false,
|
||||||
@ -280,6 +284,10 @@ func MainApple() *Apple2 {
|
|||||||
a.AddRGBCard()
|
a.AddRGBCard()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *nsc {
|
||||||
|
a.AddNoSlotClock()
|
||||||
|
}
|
||||||
|
|
||||||
//a.AddCardInOut(2)
|
//a.AddCardInOut(2)
|
||||||
//a.AddCardLogger(4)
|
//a.AddCardLogger(4)
|
||||||
|
|
||||||
|
197
noSlotClockDS1216.go
Normal file
197
noSlotClockDS1216.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package apple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
No slot clock with DS1216 Phantom Time Chip for ROM
|
||||||
|
|
||||||
|
See:
|
||||||
|
- http://ctrl.pomme.reset.free.fr/index.php/hardware/no-slot-clock-ds1216e/
|
||||||
|
- http://ctrl.pomme.reset.free.fr/wp-content/uploads/NSC/DS1216.pdf
|
||||||
|
- https://mirrors.apple2.org.za/Apple%20II%20Documentation%20Project/Chips/SMT%20No-Slot%20Clock/
|
||||||
|
- https://www.digchip.com/datasheets/parts/datasheet/000/DS1215-pdf.php
|
||||||
|
|
||||||
|
Test software:
|
||||||
|
- http://ctrl.pomme.reset.free.fr/wp-content/uploads/NSC/NSC_UTILITIES_V14.dsk
|
||||||
|
|
||||||
|
Following the spirit of a phantom chip, the DS1215 will replace a memoryHandler do its things and
|
||||||
|
delegate to the replaced memoryHandler when needed.
|
||||||
|
|
||||||
|
On the Apple IIe it is usually installed under the ROM CD (under CF on later models). Similar for the Apple IIc.
|
||||||
|
It is usually not compatible with the ROMs of the Apple II+, but could be installed in a card with 28 pins ROM,
|
||||||
|
working on different addresses. We will install it under the main ROM for all models.
|
||||||
|
|
||||||
|
Actually software looks like it only uses: 0xC300, 0xC301 and 0xC304
|
||||||
|
*/
|
||||||
|
|
||||||
|
type noSlotClockDS1216 struct {
|
||||||
|
memory memoryHandler
|
||||||
|
state uint8
|
||||||
|
index uint8
|
||||||
|
timeCapture uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var nscBitPattern = [64]bool{
|
||||||
|
true, false, true, false, false, false, true, true, //C5
|
||||||
|
false, true, false, true, true, true, false, false, //3A
|
||||||
|
true, true, false, false, false, true, false, true, //A3
|
||||||
|
false, false, true, true, true, false, true, false, //5C
|
||||||
|
true, false, true, false, false, false, true, true, //C5
|
||||||
|
false, true, false, true, true, true, false, false, //3A
|
||||||
|
true, true, false, false, false, true, false, true, //A3
|
||||||
|
false, false, true, true, true, false, true, false, //5C
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
nscStateDisabled = uint8(0)
|
||||||
|
nscStatePattern = uint8(1)
|
||||||
|
nscStateEnabled = uint8(2)
|
||||||
|
)
|
||||||
|
|
||||||
|
func newNoSlotClockDS1216(a *Apple2, memory memoryHandler) *noSlotClockDS1216 {
|
||||||
|
var nsc noSlotClockDS1216
|
||||||
|
nsc.memory = memory
|
||||||
|
nsc.state = nscStateDisabled
|
||||||
|
nsc.index = 0
|
||||||
|
return &nsc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nsc *noSlotClockDS1216) peek(address uint16) uint8 {
|
||||||
|
read := (address & 0x04) != 0 // Bit A2 of the address bus
|
||||||
|
value := (address & 0x01) != 0 // Bit A0 of the address bus
|
||||||
|
|
||||||
|
var data uint8
|
||||||
|
switch nsc.state {
|
||||||
|
case nscStateDisabled:
|
||||||
|
if read {
|
||||||
|
// Prior to executing the first of 64 write cycles, a read cycle should be executed
|
||||||
|
// by holding A2 high. The read cycle will reset the comparison register pointer
|
||||||
|
// within the SmartWatch, ensuring the pattern recognition starts with the first
|
||||||
|
// bit of the sequence.
|
||||||
|
nsc.state = nscStatePattern
|
||||||
|
nsc.index = 0
|
||||||
|
}
|
||||||
|
data = nsc.memory.peek(address)
|
||||||
|
|
||||||
|
case nscStatePattern:
|
||||||
|
// Communication with the SmartWatch is established by pattern recognition of a serial
|
||||||
|
// bit stream of 64 bits that must be matched by executing 64 consecutive write cycles,
|
||||||
|
// placing address bit A2 low with the proper data on address bit A0. The 64 write cycles
|
||||||
|
// are used only to gain access to the SmartWatch.
|
||||||
|
if read {
|
||||||
|
// If a read cycle occurs at any time during pattern recognition, the present
|
||||||
|
// sequence is aborted and the comparison register pointer is reset.
|
||||||
|
nsc.index = 0
|
||||||
|
} else {
|
||||||
|
// When the first write cycle is executed, it is compared to bit 0 of the 64-bit
|
||||||
|
// comparison register. Pattern recognition continues for a total of 64 write cycles
|
||||||
|
// until all the bits in the comparison register have been matched.
|
||||||
|
if value == nscBitPattern[nsc.index] {
|
||||||
|
// If a match is found, the pointer increments to the next location of the
|
||||||
|
// comparison register and awaits the next write cycle.
|
||||||
|
nsc.index++
|
||||||
|
if nsc.index == 64 {
|
||||||
|
// With a correct match for 64 bits, the SmartWatch is enabled and data transfer to or
|
||||||
|
// from the timekeeping registers can proceed.
|
||||||
|
nsc.state = nscStateEnabled
|
||||||
|
nsc.index = 0
|
||||||
|
nsc.loadTime()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If a match is not found, the pointer does not advance and all subsequent write
|
||||||
|
// cycles are ignored.
|
||||||
|
nsc.state = nscStateDisabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = nsc.memory.peek(address)
|
||||||
|
|
||||||
|
case nscStateEnabled:
|
||||||
|
// The next 64 cycles will cause the SmartWatch to either receive data on data in (A0) or
|
||||||
|
// transmit data on data out (DQ0), depending on the level of /WRITE READ (A2).
|
||||||
|
if read {
|
||||||
|
// Get info
|
||||||
|
data = uint8(nsc.timeCapture>>nsc.index) & 1
|
||||||
|
// The info is set on the LSB. The rest of bits are zero. Should they be the value in ROM?
|
||||||
|
} else {
|
||||||
|
// Store info
|
||||||
|
if value {
|
||||||
|
nsc.timeCapture |= (1 << nsc.index)
|
||||||
|
} else {
|
||||||
|
nsc.timeCapture &= ^(1 << nsc.index)
|
||||||
|
}
|
||||||
|
data = 0 // What is returned on write?
|
||||||
|
}
|
||||||
|
nsc.index++
|
||||||
|
if nsc.index == 64 {
|
||||||
|
// Is this right?
|
||||||
|
nsc.state = nscStateDisabled
|
||||||
|
nsc.index = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nsc *noSlotClockDS1216) poke(address uint16, value uint8) {
|
||||||
|
// This should not happen as we are dealing with ROM
|
||||||
|
nsc.memory.poke(address, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (nsc *noSlotClockDS1216) loadTime() {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
var register uint64
|
||||||
|
|
||||||
|
year := uint64(now.Year()) % 100
|
||||||
|
register = year / 10
|
||||||
|
register <<= 4
|
||||||
|
register += year % 10
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
month := uint64(now.Month())
|
||||||
|
register += month / 10
|
||||||
|
register <<= 4
|
||||||
|
register += month % 10
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
day := uint64(now.Day())
|
||||||
|
register += day / 10
|
||||||
|
register <<= 4
|
||||||
|
register += day % 10
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
// Bits 4 and 5 of the day register are used to control the RST and oscillator
|
||||||
|
// functions. These bits are shipped from the factory set to logic 1.
|
||||||
|
register += 0x0 //0x3, but zero on read.
|
||||||
|
register <<= 4
|
||||||
|
register += uint64(now.Weekday()) + 1
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
hour := uint64(now.Hour())
|
||||||
|
register += 0x0 // 0x8 for 24 hour mode, but zero on read.
|
||||||
|
register += hour / 10
|
||||||
|
register <<= 4
|
||||||
|
register += hour % 10
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
minute := uint64(now.Minute())
|
||||||
|
register += minute / 10
|
||||||
|
register <<= 4
|
||||||
|
register += minute % 10
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
second := uint64(now.Second())
|
||||||
|
register += second / 10
|
||||||
|
register <<= 4
|
||||||
|
register += second % 10
|
||||||
|
register <<= 4
|
||||||
|
|
||||||
|
centisecond := uint64(now.Nanosecond() / 10000000)
|
||||||
|
register += centisecond / 10
|
||||||
|
register <<= 4
|
||||||
|
register += centisecond % 10
|
||||||
|
|
||||||
|
nsc.timeCapture = register
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user