nascom: add support for tickless mode
authorAlan Cox <alan@linux.intel.com>
Thu, 20 Sep 2018 00:52:50 +0000 (01:52 +0100)
committerAlan Cox <alan@linux.intel.com>
Thu, 20 Sep 2018 00:52:50 +0000 (01:52 +0100)
We will need this once we get it up and running as many of the clock chips were
not wired for interrupt or some more bizarrely wired it to NMI!

Kernel/platform-nascom/README
Kernel/platform-nascom/config.h
Kernel/platform-nascom/discard.c
Kernel/platform-nascom/main.c

index 4f9bab3..2f604d8 100644 (file)
@@ -12,8 +12,7 @@ FC00 - FFFF           Udata and common data (unbanked block)
 
 Once we have a vaguely sensible port and the split banking we need to move
 things around a bit to avoid stacks FC00-FFFF because of video contention.
-However that means supporting double stack switches as the Cromenco and
-32K/32K banked pair code will need.
+However that means switching to the thunked model (as the SC108 uses)
 
 Target Hardware
 
@@ -23,7 +22,7 @@ Required:
 Nascom I/II/II
        192K "page mode" RAM (may be able to do a 128K port as well) eg
        RAM64/GM802
-       A timer interrupt source (* indicates possible options)
+       GM816 or compatible 58174 clock at 0x20
 
 Options:
        GM833 Ramdisc. Driver written, also steals up to 1MB of it for
@@ -45,16 +44,16 @@ Boot Loaders Needed
 
 
 Options:
-Nascom AVC     (384x256 / 768x256)     (* if jumpered for Vblank port A bit1)
+Nascom AVC     (384x256 / 768x256)     (can be jumpered for Vblank port A bit1)
 Nascom Floppy Controller
 Nascom I/O card (PIO, CTC, UART)
-       Mostek 3882 CTC (Z80 CTC) *
+       Mostek 3882 CTC (Z80 CTC)
        6402 UART
        MK3881 PIO      (Z80 PIO)
 
 GM805 'Henelec' single density floppy
 GM810 IVC
-GM816 (CTC 3x PIO optional serial GM818 - dual 8250) *
+GM816 (CTC 3x PIO optional serial GM818 - dual 8250)
 GM822 RTC over PIO ?
 GM832 SVC
 GM837 Climax Colour
@@ -62,10 +61,11 @@ BE847 Maths card
 GM848 quad serial
 GM862 RAM
 MAP80 256K RAM
-MAP80 MPI (serial, CTC, FDC, SASI) ? *
+MAP80 MPI (serial, CTC, FDC, SASI) ?
 MAP80 VFC
 WT625 Viewdata
-Other RTC options (some RTC have 2Hz or so interrupt - not much use!)
+Other RTC options (some RTC have 2Hz or so interrupt - not much use but
+better than nothing - it'll get you out of a stuck process loop)
 
 
 Useful Disk Formats
index 9824ccb..f852772 100644 (file)
@@ -1,5 +1,7 @@
 /* Set if you want RTC support */
-#undef CONFIG_RTC
+#define CONFIG_RTC
+/* We don't have a clock interrupt */
+#define CONFIG_NO_CLOCK
 /* Enable to make ^Z dump the inode table for debug */
 #undef CONFIG_IDUMP
 /* Enable to make ^A drop back into the monitor */
index e977c6e..b7d58c5 100644 (file)
@@ -14,7 +14,7 @@ void device_init(void)
 {
 #ifdef CONFIG_RTC
        /* Time of day clock */
-       inittod();
+       // FIXME : once we merge both versions of the RTC handling inittod();
 #endif
        devscsi_init();
        /* Must come last as we want to allocate this for swap if no other
index 6ceee02..5ab287c 100644 (file)
@@ -7,15 +7,19 @@
 uint16_t ramtop = PROGTOP;
 uint8_t vtattr_cap;
 uint16_t swap_dev;
+uint8_t clk_irq;               /* Set if the clock can cause interrupts */
 
 struct blkbuf *bufpool_end = bufpool + NBUFS;
 
+/* FIXME: missing prototype ? */
+extern int strcmp(const char *, const char *);
 /* On idle we spin checking for the terminals. Gives us more responsiveness
    for the polled ports */
 void platform_idle(void)
 {
        irqflags_t irq = di();
        tty_poll();
+       sync_clock();
        irqrestore(irq);
 }
 
@@ -23,18 +27,42 @@ void do_beep(void)
 {
 }
 
+__sfr __at 0x21        clk_tenths;
+__sfr __at 0x22 clk_secs;
+__sfr __at 0x23 clk_tsecs;
+__sfr __at 0x2F clk_stat;
+
 uint8_t platform_param(char *p)
 {
+       if (strcmp(p, "clkint") == 0) {
+               clk_irq = 1;
+               /* FIXME: do a reset cycle first */
+               clk_stat = 0x9; /* Repeating 0.5 sec - really 0.516.6 sec
+                                  so we need to fix clock tracking on
+                                  tickless FIXME */
+               clk_stat | clk_stat | clk_stat;
+               return 1;
+       }
        used(p);
        return 0;
 }
 
 void platform_interrupt(void)
 {
-       /* TODO */
+       /* We don't have interrupts for the keyboard */
        kbd_poll();
        tty_poll();
-       timer_interrupt();
+       if (clk_irq) {
+               if (clk_stat & 0x08) {  /* Check 4 or 8 - need datasheet */
+                       /* Not ideal but we need to work out how to handle
+                          the different clocks gracefully */
+                       timer_interrupt();
+                       timer_interrupt();
+                       timer_interrupt();
+                       timer_interrupt();
+                       /* Do we need to read again ? */
+               }
+       }
 }
 
 /*
@@ -58,3 +86,82 @@ void platform_discard(void)
                bp->bf_busy = BF_FREE;
        }
 }
+
+/*
+ *     Logic for tickless system. If you have an RTC with a timer tick
+ *     you can ignore this.
+ */
+
+static uint16_t newticks = 0xFFFF;
+static uint16_t oldticks;
+
+static uint8_t re_enter;
+
+/*
+ *     We have a 1/10ths timer which is nice but it's easy to overrun so we
+ *     collect up tens/units/tenths.
+ *
+ *     Any ready can return 0xF not a BCD digit which means 'the time changed'
+ */
+static void sync_clock_read(void)
+{
+       uint8_t r[3];
+       oldticks = newticks;
+
+       do {
+               r[0] = clk_tenths & 0x0F ;
+               r[1] = clk_secs & 0x0F;
+               r[2] = clk_tsecs & 0x0F;
+       } while (r[0] == 0xF || r[1] == 0xF || r[2] == 0xF);
+
+       newticks = r[0] + 10 * r[1] + 100 * r[2];
+}
+
+/*
+ *     The OS core will invoke this routine when idle (via platform_idle) but
+ *     also after a system call and in certain other spots to ensure the clock
+ *     is roughly valid. It may be called from interrupts, without interrupts
+ *     or even recursively so it must protect itself using the framework
+ *     below.
+ *
+ *     Having worked out how much time has passed in 1/10ths of a second it
+ *     performs that may timer_interrupt events in order to advance the clock.
+ *     The core kernel logic ensures that we won't do anything silly from a
+ *     jump forward of many seconds.
+ *
+ *     We also choose to poll the ttys here so the user has some chance of
+ *     getting control back on a messed up process.
+ */
+void sync_clock(void)
+{
+       irqflags_t irq = di();
+       int16_t tmp;
+
+       if (clk_irq)
+               return;
+
+       if (!re_enter++) {
+               sync_clock_read();
+               if (oldticks != 0xFF) {
+                       tmp = newticks - oldticks;
+                       if (tmp < 0)
+                               tmp += 600;
+                       while(tmp--) {
+                               timer_interrupt();
+                       }
+                       /* Poll the keyboard */
+                       platform_interrupt();
+               }
+       }
+       re_enter--;
+       irqrestore(irq);
+}
+
+/*
+ *     This method is called if the kernel has changed the system clock. We
+ *     don't work out how much work we need to do by using it as a reference
+ *     so we don't care.
+ */
+void update_sync_clock(void)
+{
+}