Skip to content

PANCAL_68705_CLOCK

Source: Verilog/CPU-BOARD-3202/circuit/PANCAL_68705_CLOCK.v

Where it sits (Nexys): nd120_nexys4ddr_top > ND120_CORE > ND3202D > IO_37 > IO_PANCAL_40 > PANCAL_68705_CLOCK - instance path: CORE.CPU_BOARD.IO.PANCAL.CHIP_35C

Used in: IO_PANCAL_40 (Nexys, MiSTer, MEGA65 R6, MEGA65 R3, QMTECH)

Contains: no other modules.

Module hierarchy - All modules

PANCAL_68705_CLOCK symbol

Schematic

Drawn from the Verilog: the yosys netlist of the Nexys 4 DDR build, instance CORE.CPU_BOARD.IO.PANCAL.CHIP_35C. Sub-modules are boxes (click the picture to open it full size; there every sub-module box links to its page, and every wire shows its Verilog name).

PANCAL_68705_CLOCK schematic

Description

ND120 CPU, MM&M PANCAL_68705_CLOCK Behavioural stand-in for the MC68705U3 panel processor (44A/35C) and the MM58274 calendar chip (34A) on sheet 40 - CLOCK PATH ONLY. Source of truth: the ROM dump Code/68705/MC68705U3_35C.BIN, disassembled by hand (28-AUG-2026). Addresses below are ROM addresses. Sheet 40 (Code/68705/3202D_PANCAL_SHEET40.png) gives the pin names. WHAT THE REAL CHIP DOES (clock part) Idle loop 0x014E: polls PD7 = EMP~ (DGA FIFO "not empty"). Read a FIFO byte 0x09BC: BCLR 3,PORTB (RMM~ low) ; LDA PORTA ; BSET 3,PORTB. The DGA pops one byte per XCLK while RMM~ is low. Command byte = high byte of the TRR PANC operand (microcode RPANC o1045 does IDBS,SWAP + LDPANC twice: A[15:8] first, then A[7:0]): bit 5 = PANC bit 13 "read request" bit 3 = PANC bit 11, always 0 for the clock bit 2:0 = PFUNC (PANC bits 10:8) Dispatch 0x04D8: bit3=1 -> display/text commands (RAM only, never answered). bit3=0 -> 0x06BE, the clock path: PORTC[2:0] = PFUNC (STAT2:0 -> PANS bits 10:8) if PFUNC bit2 == 0: RTS (PFUNC 0-3: no answer at all) BCLR 5,PORTB (STAT4 low = busy) idx = (PFUNC & 3) ^ 3 (RAM $47+idx) bit5=0 (CPU WRITES the clock): BCLR 6,PORTB (READ=0); byte = next FIFO byte (WPAN); $47[idx] = byte; latch byte to the 74LS374 (WMM~ pulse, 0x09C5) BSET 5,PORTB; if idx==0 (PFUNC 7, the last of the four): convert $47..$4A -> calendar (0x0715) and write the MM58274. bit5=1 (CPU READS the clock): BSET 6,PORTB (READ=1); read and discard the WPAN byte; if idx==3 (PFUNC 4, the first of the four): read the MM58274 and convert calendar -> $47..$4A (0x0AC0 / 0x0803); latch $47[idx] to the 74LS374; BSET 5,PORTB. Every command ends with the display pipeline (~ms) and then BCLR 5,PORTB at 0x0195, so STAT4 is a millisecond-wide "answered" level. At reset PORTB = 0x2F, i.e. STAT4 = 1 until the first command has been processed. THE 4-BYTE TIME (RAM $47..$4A, ND-100 hardware-clock format): $47 = PFUNC 7 = half-days since 1979-01-01, high byte $48 = PFUNC 6 = half-days, low byte $49 = PFUNC 5 = seconds within the half-day (0..43199), high $4A = PFUNC 4 = seconds, low byte Proof in the ROM: constants at $80..$83 = 02DA (730 half-days/year), 0E10 (3600 s/hour), 07BB (1979); the year loop adds 2 for leap years; the odd half-day sets hour=12. Same format as the ND-100 panel (ND-06.014 ch. 4.3) and RetroCore's ControlPanel.cs. WHAT THIS MODULE DOES Keeps {half-days, seconds} as two 16-bit counters ticking at 1 Hz (BOARD_CLK_FREQ sysclk cycles per second), and answers PFUNC 4-7 exactly the way the ROM does, without any calendar arithmetic: the CPU only ever sees the four raw bytes, so a calendar is not needed. The counters survive CLEAR_n like the battery-backed MM58274 does. NOT MODELLED (display only, no effect on the CPU side): DISP1-5 shift-register output, the Port D statistics (PCR/PONI/IONI/ LHIT/LEV0), the text commands' display effects. Text commands ARE drained from the FIFO with the right byte counts so a text write never leaves stray bytes behind to be mis-read as a command. STAT3 (PB4) stays 0 - see the note in IO_37.v; the ROM pulses it in the idle loop (0x0153) but that is a separate decision. 28-AUG-2026 Ronny Hansen

Parameters

Parameter Default
TICK_CYCLES 100_000_000
HOLD_CYCLES 200_000

Verilog source

Verilog/CPU-BOARD-3202/circuit/PANCAL_68705_CLOCK.v on GitHub.

Show the Verilog of PANCAL_68705_CLOCK (398 lines)
/**************************************************************************
** ND120 CPU, MM&M                                                       **
** PANCAL_68705_CLOCK                                                    **
** Behavioural stand-in for the MC68705U3 panel processor (44A/35C) and  **
** the MM58274 calendar chip (34A) on sheet 40 - CLOCK PATH ONLY.        **
**                                                                       **
** Source of truth: the ROM dump Code/68705/MC68705U3_35C.BIN,           **
** disassembled by hand (28-AUG-2026). Addresses below are ROM addresses.**
** Sheet 40 (Code/68705/3202D_PANCAL_SHEET40.png) gives the pin names.   **
**                                                                       **
** WHAT THE REAL CHIP DOES (clock part)                                  **
**  Idle loop 0x014E: polls PD7 = EMP~ (DGA FIFO "not empty").           **
**  Read a FIFO byte 0x09BC: BCLR 3,PORTB (RMM~ low) ; LDA PORTA ;       **
**    BSET 3,PORTB. The DGA pops one byte per XCLK while RMM~ is low.    **
**  Command byte = high byte of the TRR PANC operand (microcode RPANC     **
**    o1045 does IDBS,SWAP + LDPANC twice: A[15:8] first, then A[7:0]):  **
**      bit 5 = PANC bit 13 "read request"                               **
**      bit 3 = PANC bit 11, always 0 for the clock                      **
**      bit 2:0 = PFUNC (PANC bits 10:8)                                 **
**  Dispatch 0x04D8: bit3=1 -> display/text commands (RAM only, never    **
**    answered). bit3=0 -> 0x06BE, the clock path:                       **
**      PORTC[2:0] = PFUNC             (STAT2:0 -> PANS bits 10:8)       **
**      if PFUNC bit2 == 0: RTS         (PFUNC 0-3: no answer at all)    **
**      BCLR 5,PORTB                    (STAT4 low = busy)               **
**      idx = (PFUNC & 3) ^ 3           (RAM $47+idx)                    **
**      bit5=0 (CPU WRITES the clock):                                   **
**        BCLR 6,PORTB (READ=0); byte = next FIFO byte (WPAN);           **
**        $47[idx] = byte; latch byte to the 74LS374 (WMM~ pulse, 0x09C5)**
**        BSET 5,PORTB; if idx==0 (PFUNC 7, the last of the four):       **
**        convert $47..$4A -> calendar (0x0715) and write the MM58274.   **
**      bit5=1 (CPU READS the clock):                                    **
**        BSET 6,PORTB (READ=1); read and discard the WPAN byte;         **
**        if idx==3 (PFUNC 4, the first of the four): read the MM58274   **
**        and convert calendar -> $47..$4A (0x0AC0 / 0x0803);            **
**        latch $47[idx] to the 74LS374; BSET 5,PORTB.                   **
**    Every command ends with the display pipeline (~ms) and then         **
**    BCLR 5,PORTB at 0x0195, so STAT4 is a millisecond-wide "answered"  **
**    level. At reset PORTB = 0x2F, i.e. STAT4 = 1 until the first        **
**    command has been processed.                                        **
**                                                                       **
** THE 4-BYTE TIME (RAM $47..$4A, ND-100 hardware-clock format):         **
**      $47 = PFUNC 7 = half-days since 1979-01-01, high byte            **
**      $48 = PFUNC 6 = half-days, low byte                              **
**      $49 = PFUNC 5 = seconds within the half-day (0..43199), high     **
**      $4A = PFUNC 4 = seconds, low byte                                **
**  Proof in the ROM: constants at $80..$83 = 02DA (730 half-days/year), **
**  0E10 (3600 s/hour), 07BB (1979); the year loop adds 2 for leap years;**
**  the odd half-day sets hour=12. Same format as the ND-100 panel        **
**  (ND-06.014 ch. 4.3) and RetroCore's ControlPanel.cs.                 **
**                                                                       **
** WHAT THIS MODULE DOES                                                 **
**  Keeps {half-days, seconds} as two 16-bit counters ticking at 1 Hz    **
**  (BOARD_CLK_FREQ sysclk cycles per second), and answers PFUNC 4-7     **
**  exactly the way the ROM does, without any calendar arithmetic: the   **
**  CPU only ever sees the four raw bytes, so a calendar is not needed.  **
**  The counters survive CLEAR_n like the battery-backed MM58274 does.   **
**                                                                       **
** NOT MODELLED (display only, no effect on the CPU side):               **
**  DISP1-5 shift-register output, the Port D statistics (PCR/PONI/IONI/ **
**  LHIT/LEV0), the text commands' display effects. Text commands ARE    **
**  drained from the FIFO with the right byte counts so a text write     **
**  never leaves stray bytes behind to be mis-read as a command.         **
**  STAT3 (PB4) stays 0 - see the note in IO_37.v; the ROM pulses it     **
**  in the idle loop (0x0153) but that is a separate decision.           **
**                                                                       **
** 28-AUG-2026                                                           **
** Ronny Hansen                                                          **
***************************************************************************/

/* verilator lint_off UNUSEDSIGNAL */  // CLK or CLK_EN is unused depending on the build mode
module PANCAL_68705_CLOCK #(
    // sysclk cycles per second = one RTC tick. IO_PANCAL_40 passes
    // BOARD_CLK_FREQ; a testbench passes something small.
    parameter integer TICK_CYCLES = 100_000_000,
    // How long STAT4 stays high after a command (the ROM's display pipeline
    // time, a few ms). Also the wait limit for a missing data byte.
    parameter integer HOLD_CYCLES = 200_000,
    // Cycles between the last FIFO byte and the answer latch. The ROM needs
    // ~30 us (write) to ~1 ms (read with MM58274 access); the DGA only
    // needs STAT4 to be low across at least one CLK rise, so keep it short
    // but not zero.
    parameter integer EXEC_CYCLES = 64
) (
    input        sysclk,
    input        CLEAR_n,   //! 68705 RESET pin. Resets the ports and the FSM, NOT the time.
    input        CLK,       //! board CLK = the DGA FIFO clock (latch mode)
    input        CLK_EN,    //! CLK-rise clock-enable pulse (FPGA_FF_MODE, else 0)
    input        EMP_n,     //! PD7: DGA FIFO not empty (XEMN = ~fifo_empty)
    input  [7:0] PA_IN,     //! PA7:0 as driven by the DGA while RMM~ is low

    output reg       RMM_n,     //! PB3: FIFO read strobe to the DGA
    output reg       WMM_n,     //! PB0: 74LS374 clock - answer byte latched on the rise
    output reg       READ,      //! PB6: 1 = answer byte is clock data (PANS bit 13)
    output reg       STAT4,     //! PB5: 1 = command answered (PANS bit 12 via DGA VAL)
    output reg [2:0] STAT_2_0,  //! PC2:0: PFUNC echo (PANS bits 10:8)
    output reg [7:0] PA_OUT,    //! PA7:0 while the 68705 drives it (DDRA = FF)
    output reg       PA_DRIVE,  //! 1 while PA_OUT is on the bus

    // Observation only (waveforms, tests, a future host preset)
    output [15:0] TIME_HALFDAYS,
    output [15:0] TIME_SECONDS,

    //! ACTIVE LEVEL word as the microcode last sent it: command 0x0A + two
    //! data bytes (docs/panel-clock-68705.md - it arrives every 20 ms once
    //! the DGA FIFO path works). This is what the real panel's ACTIVE LEVEL
    //! row shows, and since 29-AUG-2026 the VGA panel row shows it too
    //! (term_panel.v). Byte order MEASURED 29-AUG-2026 (ND120_PANEL_CLOCK_TRACE
    //! at boot, machine on level 0): "cmd 0a, data 01, data 00" - the FIRST
    //! data byte is the LOW byte (levels 7:0), the second the high byte.
    output reg [15:0] ACTLV
);

  /*******************************************************************************
   ** 1 Hz clock: seconds 0..43199 inside a half-day, then half-days++          **
   *******************************************************************************/
  localparam [15:0] LAST_SECOND = 16'd43199;

  reg [31:0] r_tick_cnt = 32'd0;
  reg [15:0] r_sec      = 16'd0;   // seconds within the half-day
  reg [15:0] r_hd       = 16'd0;   // half-days since 1979-01-01 00:00

  wire s_tick = (r_tick_cnt == TICK_CYCLES - 1);

  assign TIME_HALFDAYS = r_hd;
  assign TIME_SECONDS  = r_sec;

  /*******************************************************************************
   ** FIFO clock event: the edge at which the DGA FIFO pops while RMM~ is low   **
   *******************************************************************************/
`ifdef FPGA_FF_MODE
  wire s_fifo_evt = CLK_EN;              // FIFO clocks on sysclk gated by CLK_EN
`else
  reg  r_clk_d = 1'b0;
  always @(posedge sysclk) r_clk_d <= CLK;
  wire s_fifo_evt = CLK & ~r_clk_d;      // FIFO clocks on the real CLK rise
`endif

  /*******************************************************************************
   ** Command state machine                                                     **
   *******************************************************************************/
  localparam [3:0] S_IDLE     = 4'd0,   // wait for EMP~ (FIFO has a byte)
                   S_CMD_RD   = 4'd1,   // RMM~ low, wait for the pop
                   S_CMD_GET  = 4'd2,   // take the command byte, RMM~ high
                   S_DAT_WAIT = 4'd3,   // wait for the next data byte (or give up)
                   S_DAT_RD   = 4'd4,   // RMM~ low, wait for the pop
                   S_DAT_GET  = 4'd5,   // take the data byte, RMM~ high
                   S_EXEC     = 4'd6,   // "processing" delay, then decide
                   S_LATCH_LO = 4'd7,   // PA driven, WMM~ low
                   S_LATCH_HI = 4'd8,   // WMM~ high = 74LS374 latches
                   S_DONE     = 4'd9;   // release PA, back to idle

  reg [3:0]  r_state    = S_IDLE;
  reg [7:0]  r_cmd      = 8'h00;    // RAM $1C: the command byte
  reg [7:0]  r_actlv_lo = 8'h00;    // first data byte of an 0x0A command = levels 7:0
  reg [7:0]  r_data     = 8'h00;    // the data byte just read
  reg [2:0]  r_bytes    = 3'd0;     // data bytes still to drain (text commands)
  reg        r_is_clock = 1'b0;     // bit3=0 and bit2=1: answered clock command
  reg [1:0]  r_idx      = 2'd0;     // RAM $47 index = (PFUNC & 3) ^ 3
  reg [17:0] r_wait     = 18'd0;    // data-byte wait / exec delay
  reg [31:0] r_hold     = 32'd0;    // STAT4 hold-down after a command, 0 = idle
  reg [7:0]  r_buf [0:3];           // RAM $47..$4A

  // Text (bit3=1) commands and how many data bytes each one takes from the
  // FIFO - straight from the handlers at 0x051B/0575/0594/05C3/05DF/0608/
  // 0610/0627.
  function [2:0] text_bytes(input [2:0] f);
    case (f)
      3'd0: text_bytes = 3'd5;
      3'd1: text_bytes = 3'd4;
      3'd2: text_bytes = 3'd2;
      3'd3: text_bytes = 3'd3;
      3'd4: text_bytes = 3'd1;
      3'd5: text_bytes = 3'd1;
      3'd6: text_bytes = 3'd4;
      default: text_bytes = 3'd2;
    endcase
  endfunction

  integer i;
  initial begin
    for (i = 0; i < 4; i = i + 1) r_buf[i] = 8'h00;
    RMM_n    = 1'b1;
    WMM_n    = 1'b1;
    READ     = 1'b0;
    STAT4    = 1'b1;
    STAT_2_0 = 3'b000;
    PA_OUT   = 8'h00;
    PA_DRIVE = 1'b0;
  end

  always @(posedge sysclk) begin
    // ---- the clock itself: free-running, untouched by CLEAR_n --------------
    if (s_tick) begin
      r_tick_cnt <= 32'd0;
      if (r_sec >= LAST_SECOND) begin
        r_sec <= 16'd0;
        r_hd  <= r_hd + 16'd1;
      end else begin
        r_sec <= r_sec + 16'd1;
      end
    end else begin
      r_tick_cnt <= r_tick_cnt + 32'd1;
    end

    if (!CLEAR_n) begin
      // ROM 0x0128..0x013A: DDRA=0, PORTB=0x2F (PB0=1 PB1=1 PB2=1 PB3=1
      // PB4=0 PB5=1 PB6=0 PB7=0), PORTC=0xF8 (PC2:0 = 0).
      r_state  <= S_IDLE;
      r_hold   <= 32'd0;
      RMM_n    <= 1'b1;
      WMM_n    <= 1'b1;
      READ     <= 1'b0;
      STAT4    <= 1'b1;
      STAT_2_0 <= 3'b000;
      PA_DRIVE <= 1'b0;
      ACTLV    <= 16'd0;
    end else begin
      // ---- STAT4 hold-down = ROM 0x0195 BCLR 5,PORTB after the pipeline --
      if (r_hold != 32'd0) begin
        r_hold <= r_hold - 32'd1;
        if (r_hold == 32'd1) STAT4 <= 1'b0;
      end

      case (r_state)
        // ---------------------------------------------------------------
        S_IDLE: begin
          if (EMP_n) begin               // ROM 0x0158 BRSET 7,PORTD
`ifdef ND120_PANEL_CLOCK_TRACE
            $display("[panel] t=%0t FIFO not empty, reading command", $time);
`endif
            r_wait  <= 18'd0;
            RMM_n   <= 1'b0;             // ROM 0x09BD BCLR 3,PORTB
            r_state <= S_CMD_RD;
          end
        end

        S_CMD_RD: begin
          if (s_fifo_evt) r_state <= S_CMD_GET;   // the DGA popped the byte
`ifdef ND120_PANEL_CLOCK_TRACE
          else if (r_wait == 18'd1000) $display("[panel] t=%0t WARNING: no FIFO clock event in 1000 sysclk (CLK_EN/CLK not reaching the panel?)", $time);
          r_wait <= r_wait + 18'd1;
`endif
        end

        S_CMD_GET: begin                 // ROM 0x09BF LDA PORTA ; BSET 3,PORTB
          r_cmd   <= PA_IN;
`ifdef ND120_PANEL_CLOCK_TRACE
          $display("[panel] t=%0t cmd byte %02x (bit3=%0d rd=%0d pfunc=%0d)", $time, PA_IN, PA_IN[3], PA_IN[5], PA_IN[2:0]);
`endif
          RMM_n   <= 1'b1;
          r_wait  <= 18'd0;
          if (PA_IN[3]) begin
            // Text/display command: drain its data bytes, no answer.
            r_is_clock <= 1'b0;
            r_bytes    <= text_bytes(PA_IN[2:0]);
            r_state    <= S_DAT_WAIT;
          end else begin
            // ROM 0x06BE: STAT2:0 = PFUNC for every bit3=0 command
            STAT_2_0 <= PA_IN[2:0];
            if (PA_IN[2]) begin
              // ROM 0x06D3: BCLR 5 (busy), idx = (PFUNC&3)^3, one data byte
              STAT4      <= 1'b0;
              r_hold     <= 32'd0;
              r_is_clock <= 1'b1;
              r_idx      <= PA_IN[1:0] ^ 2'b11;
              r_bytes    <= 3'd1;
              READ       <= PA_IN[5];    // ROM 0x06E1 BCLR 6 / 0x06FA BSET 6
              r_state    <= S_DAT_WAIT;
            end else begin
              // PFUNC 0-3: ROM 0x06D2 RTS - nothing else happens
              r_is_clock <= 1'b0;
              r_bytes    <= 3'd0;
              r_state    <= S_EXEC;
            end
          end
        end

        // ---------------------------------------------------------------
        S_DAT_WAIT: begin
          if (r_bytes == 3'd0) begin
            r_wait  <= 18'd0;
            r_state <= S_EXEC;
          end else if (EMP_n) begin
            RMM_n   <= 1'b0;
            r_state <= S_DAT_RD;
          end else if (r_wait >= HOLD_CYCLES[17:0]) begin
            // The byte never came. The ROM would have read PA anyway and
            // got 0 from an empty FIFO - do the same and carry on.
            r_data  <= 8'h00;
            r_bytes <= r_bytes - 3'd1;
            r_wait  <= 18'd0;
            if (r_is_clock) r_buf[r_idx] <= (READ ? r_buf[r_idx] : 8'h00);
            r_state <= S_DAT_WAIT;
          end else begin
            r_wait <= r_wait + 18'd1;
          end
        end

        S_DAT_RD: begin
          if (s_fifo_evt) r_state <= S_DAT_GET;
        end

        S_DAT_GET: begin
          r_data  <= PA_IN;
          // Command 0x0A (text_bytes(2) = 2 bytes) carries the ACTLV word.
          if (r_cmd == 8'h0A) begin
            if (r_bytes == 3'd2)      r_actlv_lo <= PA_IN;               // low byte first
            else if (r_bytes == 3'd1) ACTLV      <= {PA_IN, r_actlv_lo};  // then the high byte
          end
`ifdef ND120_PANEL_CLOCK_TRACE
          $display("[panel] t=%0t data byte %02x", $time, PA_IN);
`endif
          RMM_n   <= 1'b1;
          r_bytes <= r_bytes - 3'd1;
          r_wait  <= 18'd0;
          // ROM 0x06E8: write path stores WPAN in $47[idx] as it arrives.
          if (r_is_clock && !READ) r_buf[r_idx] <= PA_IN;
          r_state <= S_DAT_WAIT;
        end

        // ---------------------------------------------------------------
        S_EXEC: begin
          if (!r_is_clock) begin
            // Text command or PFUNC 0-3: pipeline runs, then 0x0195 BCLR 5.
            r_hold  <= HOLD_CYCLES;
            r_state <= S_DONE;
          end else if (r_wait >= EXEC_CYCLES[17:0]) begin
            if (READ) begin
              // ROM 0x0701: PFUNC 4 (idx 3) is the first read of the four -
              // take a fresh snapshot of the clock, later reads reuse it.
              if (r_idx == 2'd3) begin
                r_buf[0] <= r_hd[15:8];
                r_buf[1] <= r_hd[7:0];
                r_buf[2] <= r_sec[15:8];
                r_buf[3] <= r_sec[7:0];
                PA_OUT   <= r_sec[7:0];
              end else begin
                PA_OUT   <= r_buf[r_idx];
              end
            end else begin
              // ROM 0x06EA: the written byte is echoed back to the CPU.
              PA_OUT <= r_data;
              // ROM 0x06EF: PFUNC 7 (idx 0) is the last write of the four -
              // now the whole 4-byte time becomes the clock.
              if (r_idx == 2'd0) begin
                r_hd       <= {r_data, r_buf[1]};
                r_sec      <= {r_buf[2], r_buf[3]};
                r_tick_cnt <= 32'd0;
              end
            end
            PA_DRIVE <= 1'b1;            // ROM 0x09CA DDRA = FF
            WMM_n    <= 1'b0;            // ROM 0x09CC BCLR 0,PORTB
            r_wait   <= 18'd0;
            r_state  <= S_LATCH_LO;
          end else begin
            r_wait <= r_wait + 18'd1;
          end
        end

        S_LATCH_LO: begin
          // Give the sysclk-sampled 74LS374 model a clean low before the rise.
          if (r_wait >= 18'd3) begin
            WMM_n   <= 1'b1;             // ROM 0x09CE BSET 0,PORTB -> latch
            r_wait  <= 18'd0;
            r_state <= S_LATCH_HI;
          end else begin
            r_wait <= r_wait + 18'd1;
          end
        end

        S_LATCH_HI: begin
          // Hold PA a few cycles past the rise (setup/hold for the model),
          // then ROM 0x09D0 CLR DDRA and 0x06ED/0x0712 BSET 5,PORTB.
          if (r_wait >= 18'd3) begin
`ifdef ND120_PANEL_CLOCK_TRACE
            $display("[panel] t=%0t answer %02x READ=%0d STAT2:0=%0d  time hd=%0d sec=%0d", $time, PA_OUT, READ, STAT_2_0, r_hd, r_sec);
`endif
            PA_DRIVE <= 1'b0;
            STAT4    <= 1'b1;
            r_hold   <= HOLD_CYCLES;
            r_state  <= S_DONE;
          end else begin
            r_wait <= r_wait + 18'd1;
          end
        end

        S_DONE: begin
          r_state <= S_IDLE;
        end

        default: r_state <= S_IDLE;
      endcase
    end
  end

  /* verilator lint_on UNUSEDSIGNAL */

endmodule