package izapple2 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 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(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 }