I've written a very simple ProDOS-compatible clockcard module for linapple. It provides read-only access to the system clock in local time. (The time zone is the same as what the linapple process sees with the localtime() function. Use the TZ variable for your own customization.)
The interaction with ProDOS is based on the documentations in chapter 6, ProDOS 8 Tech. Ref.
http://apple2.info/wiki/index.php?title=P8_Tech_Ref_Chapter_6
Note that ProDOS does not get the year from this card. it uses the month, day of month and day of week to calculate the year using a built-in table. This table needs to be updated every 6 years. The tool PDOSCLOCKUP
can be used to effect such an update.
http://www.apple2.org.za/mirrors/ftp.gno.org/prodos/system/pdosclockup.shk
Adds a ProDOS-compatible clock card to linapple
Logged In: NO
There is a tiny bug in the previous patch. Setting "Clock Enable = 0" should disable the clock card.
This is an update corrects it:
diff -uNr linapple-src_1.1b/linapple.conf linapple-src_1.1b-clock2/linapple.conf
--- linapple-src_1.1b/linapple.conf 2007-12-10 18:57:51.000000000 +0800
+++ linapple-src_1.1b-clock2/linapple.conf 2008-04-03 16:17:07.000000000 +0800
@@ -155,6 +155,13 @@
Harddisk Enable = 0
+#
+# A ProDOS-compatible clock card
+# To enable, give the slot# for which the card is to be inserted
+# To disable, say "0" here. (This card won't work in slot 0.)
+#
+ Clock Enable = 4
+
# HDV Starting Directory is the starting directory for choose HDV disk images
# Default: your home directory (if not set)
diff -uNr linapple-src_1.1b/src/Applewin.cpp linapple-src_1.1b-clock2/src/Applewin.cpp
--- linapple-src_1.1b/src/Applewin.cpp 2007-12-10 18:39:07.000000000 +0800
+++ linapple-src_1.1b-clock2/src/Applewin.cpp 2008-04-08 22:56:27.677920014 +0800
@@ -400,6 +400,7 @@
bool hddenabled = false;
+DWORD clockslot;
//===========================================================================
// Let us load main configuration from config file. Y_Y --bb
void LoadConfiguration ()
@@ -469,6 +470,9 @@
if(LOAD(TEXT(REGVALUE_HDD_ENABLED), &dwTmp)) hddenabled = (bool) dwTmp;// after MemInitialize
// HD_SetEnabled(dwTmp ? true : false);
// printf("g_bHD_Enabled = %d\n", g_bHD_Enabled);
+ LOAD(TEXT(REGVALUE_CLOCK_SLOT), &clockslot);
+ if (clockslot < 1 || clockslot > 7)
+ clockslot = 0;
char *szHDFilename = NULL;
@@ -876,7 +880,8 @@
MemInitialize();
HD_SetEnabled(hddenabled ? true : false);
// printf("g_bHD_Enabled = %d\n", g_bHD_Enabled);
-
+ if (clockslot)
+ Clock_Insert(clockslot);
VideoInitialize();
diff -uNr linapple-src_1.1b/src/Clock.cpp linapple-src_1.1b-clock2/src/Clock.cpp
--- linapple-src_1.1b/src/Clock.cpp 1970-01-01 08:00:00.000000000 +0800
+++ linapple-src_1.1b-clock2/src/Clock.cpp 2008-04-05 17:05:34.484107926 +0800
@@ -0,0 +1,244 @@
+/*
+AppleWin : An Apple //e emulator for Windows
+
+Copyright (C) 1994-1996, Michael O'Brien
+Copyright (C) 1999-2001, Oliver Schmidt
+Copyright (C) 2002-2005, Tom Charlesworth
+Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski
+Copyright (C) 2008, LEE Sau Dan
+
+AppleWin is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+AppleWin is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with AppleWin; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* Description: Emulation of a ProDOS-compatible clock card
+ *
+ * Author: Copyright (c) 2008, LEE Sau Dan
+ *
+ * Note: Since ProDOS calculates the year using day+month+{day of week},
+ * the year calculated has a period of 7 years. Every 6 years,
+ * you need to update ProDOS's built-in year-table. A utility
+ * for doing this can be found at:
+ *
+ * http://www.apple2.org.za/mirrors/ftp.gno.org/prodos/system/pdosclockup.shk
+ */
+
+#include <time.h>
+#include <stdio.h>
+#include <assert.h>
+#include "stdafx.h"
+#include "Clock.h"
+
+
+/*
+I/O map: (please add an offset of Slot#*16 to the address below)
+
+ C080 (r) latched month (tens)
+ C081 (r) latched month (units)
+ C082 (r) always zero
+ C083 (r) latched day-of-week
+ C084 (r) latched day-of-month (tens)
+ C085 (r) latched day-of-month (units)
+ C086 (r) latched hour (tens)
+ C087 (r) latched hour (units)
+ C088 (r) latched minute (tens)
+ C089 (r) latched minute (units)
+ C08F (r) get system clock and update the latched values
+*/
+
+/*
+ This emulates a ProDOS-compatible clock card.
+ The interface is specified in chapter 6 of
+
+ ProDOS 8 Tech. Ref.
+ http://apple2.info/wiki/index.php?title=P8_Tech_Ref_Chapter_6
+
+ Briefly speaking, ProDOS detects the clock card if it finds that
+ $Cx00 => $08
+ $Cx02 => $28
+ $Cx04 => $58
+ $Cx06 => $70
+
+ When ProDOS needs to get the time, it calls $Cx0B with A=0xA3 ("#")
+ (which this ROM ignores) and then calls $Cx08.
+ Upon returning from the latter, it expects the line buffer (beginning
+ at $0200) to contain an ASCII (with 8-th bit on) like:
+ 01,02,03,04,05
+ terminated by a trailing $80.
+ The five 2-digit numbers, separated by commas, are:
+ month (1--12), day-of-week (0=Sun - 6=Sat), day-of-month,
+ hour (24hr clock), minute
+
+ You can easily test this interface by enterint the monitor (CALL -151)
+ and then type (the first "*" is the prompt character):
+
+ * Cx08G 200.20F
+
+ where x is the slot number of this clock card.
+ Please leave at least 16 blanks before the character "C" so that
+ the command line buffer won't be trashed by the ROM routine before
+ it is processed.
+*/
+
+
+BYTE Clock_ROM[] =
+/*
+*
+* ROM code for a simplistic ProDOS-compatible clock card
+* for the Apple II emulator 'linapple'
+*
+* (C) 2008 by LEE Sau Dan
+*
+ ORG $C000
+STACK EQU $100
+IN EQU $200
+DEVSEL EQU $C080
+MONRTS EQU $FF58
+LATCHIT EQU DEVSEL+$F
+
+ST
+ PHP ; this opcode byte must be $08 for prodos to recognize
+ BCC D1 ; offset byte must be $28 for prodos to recognize
+B1
+ BCS D2 ; offset byte must be $58 for prodos to recognize
+B2
+ DB 00
+ DB $70 ; byte must be $70 for prodos to recognize
+ DB 00
+RDCLK ; ProDOS calls $Cx08 to get clock data
+ NOP
+ NOP
+ DB $A9 ; opcode for LDA #xx, just to skip the next byte
+WRCLK ; ProDOS calls $Cx0B with AX="#" before calling RDCLK
+ RTS
+
+RDCLK1
+ PHP
+ SEI
+ JSR MONRTS ; known to be RTS
+ TSX
+ LDA STACK,X ; high byte of PC
+ PLP
+ ASL
+ ASL
+ ASL
+ ASL
+ TAY
+
+ LDA LATCHIT,Y ; latch current time to regs
+ LDX #$0
+ BEQ RDCLK1_CONT
+
+
+ DS B1+$28-*
+D1
+ PLP
+ RTS
+
+
+RDCLK1_CONT
+ LDA DEVSEL,Y
+ INY
+ ORA #"0"
+ STA IN,X
+ INX
+ LDA DEVSEL,Y
+ INY
+ ORA #"0"
+ STA IN,X
+ INX
+ LDA #","
+ STA IN,X
+ INX
+ TYA
+ AND #$0F
+ CMP #10
+ BCC RDCLK1_CONT
+ LDA #$80 ; EOS
+ STA IN-1,X ; overwrite the last ','
+ RTS
+
+
+ DS B2+$58-*
+D2
+ BCS D1
+
+
+ END
+*/
+ {
+ 0x08, 0x90, 0x28, 0xb0, 0x58, 0x00, 0x70, 0x00, 0xea, 0xea, 0xa9, 0x60, 0x08, 0x78, 0x20, 0x58,
+ 0xff, 0xba, 0xbd, 0x00, 0x01, 0x28, 0x0a, 0x0a, 0x0a, 0x0a, 0xa8, 0xb9, 0x8f, 0xc0, 0xa2, 0x00,
+ 0xf0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x60, 0xb9, 0x80, 0xc0,
+ 0xc8, 0x09, 0xb0, 0x9d, 0x00, 0x02, 0xe8, 0xb9, 0x80, 0xc0, 0xc8, 0x09, 0xb0, 0x9d, 0x00, 0x02,
+ 0xe8, 0xa9, 0xac, 0x9d, 0x00, 0x02, 0xe8, 0x98, 0x29, 0x0f, 0xc9, 0x0a, 0x90, 0xdf, 0xa9, 0x80,
+ 0x9d, 0xff, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb0, 0xcc,
+ };
+
+
+static BYTE latches[10];
+
+static void set_latch_pair(int index, int value) {
+ latches[index&=0x0E] = (value%=100) / 10;
+ latches[index|1] = value%10;
+}
+
+static void update_latches() {
+ time_t t;
+ struct tm tm;
+
+ time(&t);
+ localtime_r(&t, &tm);
+ set_latch_pair(0, 1+tm.tm_mon);
+ set_latch_pair(2, tm.tm_wday);
+ set_latch_pair(4, tm.tm_mday);
+ set_latch_pair(6, tm.tm_hour);
+ set_latch_pair(8, tm.tm_min);
+}
+
+
+static BYTE /*__stdcall*/ Clock_IORead (WORD pc, WORD addr,
+ BYTE bWrite, BYTE d, ULONG nCyclesLeft)
+{
+ switch(addr &= 0x0F) {
+ case 0: case 1:
+ case 2: case 3:
+ case 4: case 5:
+ case 6: case 7:
+ case 8: case 9:
+ return latches[addr];
+
+ case 0xF:
+ update_latches();
+ return 0;
+
+ default:
+ return IO_Null(pc, addr, bWrite, d, nCyclesLeft);
+ }
+ assert(0 == "Compiler Bug?!");
+}
+
+
+void Clock_Insert(int slot) {
+ memset(MemGetCxRomPeripheral() + slot*256, 0, 256);
+ memcpy(MemGetCxRomPeripheral() + slot*256, Clock_ROM, sizeof(Clock_ROM));
+
+ RegisterIoHandler(slot,
+ Clock_IORead, NULL,
+ NULL, NULL,
+ NULL, NULL);
+}
+
+
+//end
diff -uNr linapple-src_1.1b/src/Clock.h linapple-src_1.1b-clock2/src/Clock.h
--- linapple-src_1.1b/src/Clock.h 1970-01-01 08:00:00.000000000 +0800
+++ linapple-src_1.1b-clock2/src/Clock.h 2008-04-03 16:15:32.000000000 +0800
@@ -0,0 +1,2 @@
+extern void Clock_Insert(int);
+/*end*/
diff -uNr linapple-src_1.1b/src/Common.h linapple-src_1.1b-clock2/src/Common.h
--- linapple-src_1.1b/src/Common.h 2007-12-10 18:15:25.000000000 +0800
+++ linapple-src_1.1b-clock2/src/Common.h 2008-04-03 16:06:50.000000000 +0800
@@ -94,6 +94,7 @@
#define REGVALUE_HDD_IMAGE2 "Harddisk Image 2"
#define REGVALUE_DISK_IMAGE1 "Disk Image 1"
#define REGVALUE_DISK_IMAGE2 "Disk Image 2"
+#define REGVALUE_CLOCK_SLOT "Clock Enable"
#define REGVALUE_PPRINTER_FILENAME "Parallel Printer Filename"
diff -uNr linapple-src_1.1b/src/Makefile linapple-src_1.1b-clock2/src/Makefile
--- linapple-src_1.1b/src/Makefile 2007-12-10 17:54:36.000000000 +0800
+++ linapple-src_1.1b-clock2/src/Makefile 2008-04-03 16:00:43.000000000 +0800
@@ -8,7 +8,7 @@
Harddisk.o DiskImage.o Disk.o MouseInterface.o \ Keyboard.o Joystick.o ParallelPrinter.o Memory.o \ CPU.o Frame.o Applewin.o SerialComms.o \ -Log.o Registry.o \ +Log.o Registry.o Clock.o \ Riff.o Speaker.o SoundCore.o Mockingboard.o Debug.o \
diff -uNr linapple-src_1.1b/src/stdafx.h linapple-src_1.1b-clock2/src/stdafx.h
--- linapple-src_1.1b/src/stdafx.h 2007-12-01 18:18:42.000000000 +0800
+++ linapple-src_1.1b-clock2/src/stdafx.h 2008-04-03 16:12:14.000000000 +0800
@@ -44,6 +44,7 @@
#include "Frame.h"
#include "Harddisk.h"
+#include "Clock.h"
#include "Joystick.h"
#include "Keyboard.h"
#include "Log.h"