-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathnoSlotClockDS1216.go
218 lines (189 loc) · 6.52 KB
/
noSlotClockDS1216.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package izapple2
import (
"errors"
"fmt"
"strconv"
"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 or under a card ROM.
Actually software looks like it uses: 0xCs00, 0xCs01 and 0xCs04, usually slot 3
*/
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(_ *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) {
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
}
func setupNoSlotClock(a *Apple2, arg string) error {
if arg == "main" {
nsc := newNoSlotClockDS1216(a, a.mmu.physicalROM)
a.mmu.physicalROM = nsc
} else {
slot, err := strconv.ParseUint(arg, 10, 8)
if err != nil || slot < 1 || slot > 7 {
return errors.New("invalid slot for the no slot clock, use 'none', 'main' or a slot number from 1 to 7")
}
cardRom := a.mmu.cardsROM[slot]
if cardRom == nil {
return fmt.Errorf("no ROM available on slot %d to add a no slot clock", slot)
}
nsc := newNoSlotClockDS1216(a, cardRom)
a.mmu.cardsROM[slot] = nsc
}
return nil
}