mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-12-21 18:29:45 +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
|
||||
- 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
|
||||
- No Slot Clock based on the DS1216
|
||||
- Graphic modes:
|
||||
- Text 40 columns
|
||||
- Text 80 columns (Apple //e only)
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"github.com/ivanizag/apple2/core6502"
|
||||
)
|
||||
|
||||
// newApple2 instantiates an apple2
|
||||
func newApple2plus() *Apple2 {
|
||||
var a Apple2
|
||||
a.Name = "Apple ][+"
|
||||
@ -191,6 +190,12 @@ func (a *Apple2) AddRAMWorks(banks int) {
|
||||
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
|
||||
func (a *Apple2) AddCardLogger(slot int) {
|
||||
a.insertCard(&cardLogger{}, slot)
|
||||
|
@ -81,6 +81,10 @@ func MainApple() *Apple2 {
|
||||
"thunderClockCardSlot",
|
||||
4,
|
||||
"slot for the ThunderClock Plus card. -1 for none")
|
||||
nsc := flag.Bool(
|
||||
"nsc",
|
||||
false,
|
||||
"add a DS1216 No-Slot-Clock")
|
||||
mono := flag.Bool(
|
||||
"mono",
|
||||
false,
|
||||
@ -280,6 +284,10 @@ func MainApple() *Apple2 {
|
||||
a.AddRGBCard()
|
||||
}
|
||||
|
||||
if *nsc {
|
||||
a.AddNoSlotClock()
|
||||
}
|
||||
|
||||
//a.AddCardInOut(2)
|
||||
//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