Monday, February 12, 2018

MKHBC-8-Rx, refactoring, development.

Hello 8-bit computing enthusiasts!

In recent weeks I concentrate on the software part of my project. First I cleaned up my code and put in on github as mentioned in previous blog update. Since then I added KbHit implementation and detection of RTC and Banked RAM to the firmware. I enhanced existing functions in enhshell.c to use added functionality. I also added small programs that provide one or few related functions in smaller package than the enhshell.c. Since I am still using serial port to load programs to the computer, I wanted to save on loading times.
I plan to create smaller standalone images of all functions currently implemented in enhshell.c.
I also plan to add few new tools that will help me to turn the computer into fully self hosting platform: text editor and on-line symbolic machine code assembler / disassembler.
Then I will move to further expand the hardware of my computer - build generic I/O card, control panel, local console (keyboard and CRT controller) and mass storage interface.

Updated kernel jump table and system internals documentation:

Programming API / Kernal Jump Table:

Function
Address (hex)
Parameters
Return
Description
CallDS1685Init
FFD2
RegB, RegA, RegXB, RegXA
RegC in Acc
Initialize RTC chip.
CallDS1685ReadClock
FFD5
n/a
Data is returned via hardware stack. Calling subroutine is responsible for allocating 8 bytes on stack before calling this function. Clock data are stored in following order below the subroutine return address: seconds, minutes, hours, dayofweek, date, month, year, century. Valid return data on stack only if Acc > 0 (Acc = # of bytes on stack). Calling subroutine still should de-allocate stack space by calling PLA x 8 after reading or discarding returned data.
Read RTC clock data.
CallDS1685SetClock
FFD8
Parameters are passed via hardware stack: seconds, minutes, hours, day of week, day of month, month, year, century. Calling subroutine is responsible for allocating 8 bytes on stack and filling the space with valid input data before calling this function. Calling subroutine is also responsible for freeing stack space (PLA x 8).
n/a
Set date/time of RTC chip.
CallDS1685SetTime
FFDB
Parameters are passed via hardware stack: seconds, minutes, hour. Calling subroutine is responsible for allocating space on stack and filling it with valid input data before calling this function.  Calling subroutine is also responsible for freeing stack space (PLA x 3).
n/a
Set time of RTC chip.
CallDS1685StoreRam
FFDE
BankNum, RamAddr, RamVal
n/a
Store a value in non-volatile RTC memory bank.
CallDS1685ReadRam
FFE1
BankNum, RamAddr
value in Acc
Read value from non-volatile RTC memory bank.
CallReadMem
FFE4
PromptLine (contains hexadecimal address range)
n/a (output)
Machine code monitor function - read memory.
CallWriteMem
FFE7
PromptLine (contains hexadecimal address and values)
n/a (memory is modified)
Machine code monitor function - write memory.
CallExecute
FFEA
PromptLine (contains hexadecimal address)
n/a (code is executed)
Machine code monitor function - execute code in memory.
CallGetCh
FFED
n/a
Character code in Acc
Standard I/O function - get character.
CallPutCh
FFF0
Character code in Acc.
n/a (output)
Standard I/O function - put/print character.
CallGets
FFF3
n/a (input)
PromptLine, PromptLen
Standard I/O function - get string.
CallPuts
FFF6
StrPtr
n/a (output)
Standard I/O function - put/print string.
CallBankRamSel
FFCF
Banked RAM bank # in Acc. (0..7)
n/a (selects RAM bank, updates shadow register in RAM)
Banked RAM bank selection.
CallKbHit
FFCC
n/a
Character in Acc or 0 if buffer empty.
Check if there is character in RX buffer (equivalent of check if key was pressed since this is UART I/O).

WARNING:
Disable interrupts before calling any RTC function:
SEI
<call to RTC API>
CLI

Registers, buffers, memory:

RTC RAM shadow:
               RegB        = $F6
               RegA        = $F7
               RegXB       = $F8
               RegXA       = $F9
               RegC        = $FA
               Temp        = $FB
               BankNum     = $FC
               RamAddr     = $FD
               RamVal      = $FE

UART Pointers

UartRxInPt  = $F2           ; Rx head pointer, for chars placed in buf
UartRxOutPt = $F3           ; Rx tail pointer, for chars taken from buf

Uart Queues (after stack)
               UartTxQue   = $200          ; 256 byte output queue
               UartRxQue   = $300          ; 256 byte input queue

          MOS Prompt variables
               PromptLine  = $80           ; Prompt line (entered by user)
               PromptMax   = $50           ; An 80-character line is permitted
                                           ; ($80 to $CF)
               PromptLen   = $D0           ; Location of length variable

          MOS I/O Function variables
               StrPtr      = $E0           ; String pointer for I/O functions

 Other variables:
 Timer64Hz   = $E2            ; 4-byte (32-bit) counter
                              ; incremented 64 times / sec
   ; $E2,$E3,$E4,$E5 (unsigned long,
   ; little endian)

 RamBankNum  = $E6            ; Current Banked RAM bank#.
 DetectedDevices = $E7        ; Flags indicating devices detected  
                              ; by system during startup.

Detected devices flags:
DEVPRESENT_RTC     
 %10000000
DEVPRESENT_NORTC   
%01111111
DEVPRESENT_EXTRAM  
%01000000
DEVPRESENT_NOEXTRAM
%10111111
DEVPRESENT_BANKRAM 
100000
DEVPRESENT_NOBRAM  
%11011111
DEVPRESENT_UART    
010000
DEVPRESENT_NOUART  
%11101111
DEVNOEXTRAM        
DEVPRESENT_NOEXTRAM & DEVPRESENT_NOBRAM

          Customizable jump vectors
          Program loaded and run in RAM can modify these vectors
          to drive custom I/O console hardware and attach/change
          handler to IRQ procedure. Interrupt flag should be
          set before changes are applied and cleared when ready.
          Custom IRQ handler routine should make a jump to standard
          handler at the end. Custom I/O function routine should
          end with RTS.

               StoreAcc  =     $11           ; Temporary Accumulator store.
               IrqVect   =        $0012      ; Customizable IRQ vector
               GetChVect =        $0014      ; Custom GetCh function jump vector
               PutChVect =        $0016      ; Custom PutCh function jump vector
               GetsVect  =        $0018      ; Custom Gets function jump vector
               PutsVect  =        $001a      ; Custom Puts function jump vector

I/O space / address range:

$C000 .. $C7FF, 8 pages (8 x 256 bytes):

Internal (non-buffered) I/O bus:

$C000 .. $C0FF - slot 0 (RAM bank switching register)
$C100 .. $C1FF - slot 1 (RTC registers)
$C200 .. $C2FF - slot 2 (Reserved for Prioritized Interrupt Controller)
$C300 .. $C3FF - slot 3 (Reserved for built in I/O parallel interface PIA or VIA)

External (buffered/expansion) I/O bus:

$C400 .. $C4FF - slot 4 (UART)
$C500 .. $C5FF - slot 5
$C600 .. $C6FF - slot 6
$C700 .. $C7FF - slot 7

RAM bank switching.

NOTE: Because RAM bank switching hardware register is write only, we cannot read from it to determine which bank is selected. The purpose of bank# RAM register at $E6 is just that - remembering last selected bank#.

 Address:
$C000
 Value:
$00 .. $07
 Banked memory:
$8000 .. $BFFF
 Bank number RAM register:
$E6

Memory map:

$0000 - $7FFF: Base RAM, 32 kB. $0000 - $03FF space is used by the system.
$6000 - $7FFF: Optional Video RAM, 8 kB. (takes away from Base RAM, leaving 24 kB for general purpose)
$8000 - $BFFF: Banked RAM, 16 kB space x 8 banks = 128 kB.
$C000 - $C7FF: I/O space, 8 slots x 256 Bytes = 2 kB.
$C800 - $FFFF: EPROM, 14 kB.


System programs:

System programs currently consist of:

enhshell.c - combines rudimentary command line interface with additional functions for RTC, Banked RAM and more.
setdt.c - program that allows to set and show date / time.
date.c  - programs that shows date / time.
d2hexbin.c - conversion tool from decimal to hexadecimal / binary code.

Programs written in C (CC65) or CA65 assembly for MKHBC-8-Rx computer / MKHBC OS use library written in C and assembly languages which implements standard C library (CC65), I/O console and RTC functions and are compiled into library archive mkhbcos.lib.
Corresponding C header files are:


  • mkhbcos_ansi.h - ANSI terminal API
  • mkhbcos_ds1685.h - DS1685 RTC API
  • mkhbcos_lcd.h - standard LCD 16x2 API
  • mkhbcos_ml.h - C header with definitions of MKHBCOS API and internals
  • mkhbcos_ml.inc - assembly header with definitions for MKHBCOS API and internals
  • mkhbcos_serialio.h - serial I/O API

Thank you for visiting my blog.

---
MK
Feb 13 2018

Saturday, February 3, 2018

RTC library moved to EPROM, kernel jump table extended.

Hello 8-bit computing enthusiasts.

A little update on the MKHBC-8-Rx project.
Recently having (mostly) completed my home improvement projects, I have some more time to play with my pet project.
I have been mainly working on code, cleaning it up and moving some essential parts to EPROM. It is still a mess, but I think it is good enough to be published, so I put it on github.

I moved assembly code of RTC DS-1685 driver to EPROM, enabled periodic interrupts from RTC and added interrupt routine for RTC. I also expanded kernel jump table and added some new functions to my so called 'enhanced shell', although calling this program a shell is a bit of an overkill. :-)

Below I present the current documentation of the OS / firmware fir this system:


System programming / Operating System of MKHBC-8-Rx is divided into 2 parts:

  • Firmware, which resides in EPROM.
  • System programs, which are loaded to RAM via serial port.

Firmware consists of hexadecimal machine code monitor, hardware drivers code, MOS-6502 mandatory vectors: Reset, NMI, IRQ and Kernel Jump Table.
Firmware is implemented in MOS 6502 assembly language.
Kernel Jump Table is a series of jump commands that redirect to code performing certain system functions.
These jump commands are guaranteed to be always at the same memory locations, so the system programs using them don't have to be rebuilt each time the implementation of firmware is changed and some internal addresses has moved. The jump table is always in the same place jumping from the same locations to the same functions that they should perform, even if the functions themselves change the location in the EPROM due to code refactoring or relocation of binary code.
The new entries can be added to the Kernel Jump Table though, so it is guaranteed to be backwards compatible.

Theory of operation:

When computer is started, the reset circuit holds the reset line low long enough for CPU to initialize, then the CPU performs startup routine which consists of reading the Reset vector from the EPROM memory (which must be at fixed address) and executes the routine pointed by that vector.
The routine contains initialization code for OS and then goes into the eternal loop which sends the output via serial port and expects input on the serial port. By connecting text serial terminal device configured to be the same speed as computer's UART speed, it is possible to interface with the computer to send commands and receive output.

The command UI is very simple and consists of very rudimentary hexadecimal machine code monitor which allows to read/dump values in computer's memory, write/modify values in Random Access Memory and execute code at provided address.
This UI is rudimentary but sufficient for entering code into computer's RAM and executing it.

Programming API / Kernal Jump Table:

Function
Address (hex)
Parameters
Return
Description
CallDS1685Init
FFD2
RegB, RegA, RegXB, RegXA
RegC in Acc
Initialize RTC chip.
CallDS1685ReadClock
FFD5
n/a
Data is returned via hardware stack. Calling subroutine is responsible for allocating 8 bytes on stack before calling this function. Clock data are stored in following order below the subroutine return address: seconds, minutes, hours, dayofweek, date, month, year, century. Valid return data on stack only if Acc > 0 (Acc = # of bytes on stack). Calling subroutine still should de-allocate stack space by calling PLA x 8 after reading or discarding returned data.
Read RTC clock data.
CallDS1685SetClock
FFD8
Parameters are passed via hardware stack: seconds, minutes, hours, day of week, day of month, month, year, century. Calling subroutine is responsible for allocating 8 bytes on stack and filling the space with valid input data before calling this function. Calling subroutine is also responsible for freeing stack space (PLA x 8).
n/a
Set date/time of RTC chip.
CallDS1685SetTime
FFDB
Parameters are passed via hardware stack: seconds, minutes, hour. Calling subroutine is responsible for allocating space on stack and filling it with valid input data before calling this function.  Calling subroutine is also responsible for freeing stack space (PLA x 3).
n/a
Set time of RTC chip.
CallDS1685StoreRam
FFDE
BankNum, RamAddr, RamVal
n/a
Store a value in non-volatile RTC memory bank.
CallDS1685ReadRam
FFE1
BankNum, RamAddr
value in Acc
Read value from non-volatile RTC memory bank.
CallReadMem
FFE4
PromptLine (contains hexadecimal address range)
n/a (output)
Machine code monitor function - read memory.
CallWriteMem
FFE7
PromptLine (contains hexadecimal address and values)
n/a (memory is modified)
Machine code monitor function - write memory.
CallExecute
FFEA
PromptLine (contains hexadecimal address)
n/a (code is executed)
Machine code monitor function - execute code in memory.
CallGetCh
FFED
n/a
Character code in Acc
Standard I/O function - get character.
CallPutCh
FFF0
Character code in Acc.
n/a (output)
Standard I/O function - put/print character.
CallGets
FFF3
n/a (input)
PromptLine, PromptLen
Standard I/O function - get string.
CallPuts
FFF6
StrPtr
n/a (output)
Standard I/O function - put/print string.
CallBankRamSel
FFCF
Banked RAM bank # in Acc. (0..7)
n/a (selects RAM bank, updates shadow register in RAM)
Banked RAM bank selection.

WARNING:
Disable interrupts before calling any RTC function:
SEI
<call to RTC API>
CLI

Registers, buffers, memory:

RTC RAM shadow:
               RegB        = $F6
               RegA        = $F7
               RegXB       = $F8
               RegXA       = $F9
               RegC        = $FA
               Temp        = $FB
               BankNum     = $FC
               RamAddr     = $FD
               RamVal      = $FE

Uart Queues (after stack)
               UartTxQue   = $200          ; 256 byte output queue
               UartRxQue   = $300          ; 256 byte input queue

          MOS Prompt variables
               PromptLine  = $80           ; Prompt line (entered by user)
               PromptMax   = $50           ; An 80-character line is permitted ($80 to $CF)
               PromptLen   = $D0           ; Location of length variable

          MOS I/O Function variables
               StrPtr      = $E0           ; String pointer for I/O functions

 Other variables:
 Timer64Hz   = $E2            ; 4-byte (32-bit) counter incremented 64 times / sec
   ; $E2,$E3,$E4,$E5 (unsigned long, little endian)

          Customizable jump vectors
          Program loaded and run in RAM can modify these vectors
          to drive custom I/O console hardware and attach/change
          handler to IRQ procedure. Interrupt flag should be
          set before changes are applied and cleared when ready.
          Custom IRQ handler routine should make a jump to standard
          handler at the end. Custom I/O function routine should

          end with RTS.

               StoreAcc  =  $11          ; Temporary Accumulator store.
               IrqVect   =  $0012        ; Customizable IRQ vector
               GetChVect =  $0014        ; Custom GetCh function jump vector
               PutChVect =  $0016        ; Custom PutCh function jump vector
               GetsVect  =  $0018        ; Custom Gets function jump vector
               PutsVect  =  $001a        ; Custom Puts function jump vector

I/O space / address range:

$C000 .. $C7FF, 8 pages (8 x 256 bytes):

Internal (non-buffered) I/O bus:

$C000 .. $C0FF - slot 0 (RAM bank switching register)
$C100 .. $C1FF - slot 1 (RTC registers)
$C200 .. $C2FF - slot 2 (Reserved for Prioritized Interrupt Controller)
$C300 .. $C3FF - slot 3 (Reserved for built in I/O parallel interface PIA or VIA)

External (buffered/expansion) I/O bus:

$C400 .. $C4FF - slot 4 (UART)
$C500 .. $C5FF - slot 5
$C600 .. $C6FF - slot 6
$C700 .. $C7FF - slot 7

RAM bank switching.

 Address:
$C000
 Value:
$00 .. $07
 Banked memory:
$8000 .. $BFFF
 Bank number RAM register:
$E6

Memory map:

$0000 - $7FFF: Base RAM, 32 kB.
$6000 - $7FFF: Optional Video RAM, 8 kB.
$8000 - $BFFF: Banked RAM, 16 kB space x 8 banks = 128 kB.
$C000 - $C7FF: I/O space, 8 slots x 256 Bytes = 2 kB.
$C800 - $FFFF: EPROM, 14 kB.


System programs:

System programs currently consist only one - enhanced shell.
It is written in C and compiled with CC65.
Programs written in C (CC65) or CA65 assembly for MKHBC-8-Rx computer / MKHBC OS use library written in C and assembly languages which implements standard C library (CC65), I/O console and RTC functions and are compiled into library archive mkhbcos.lib.
Corresponding C header files are:


  • mkhbcos_ansi.h - ANSI terminal API
  • mkhbcos_ds1685.h - DS1685 RTC API
  • mkhbcos_lcd.h - standard LCD 16x2 API
  • mkhbcos_ml.h - machine code monitor API
  • mkhbcos_serialio.h - serial I/O API

That's all for today.
Thank you for visiting my blog.

Marek K. 2/4/2018.

Monday, June 19, 2017

Update on MKHBC-8-Rx : NV RAM access added to DS1685 driver.

DS1685 chip has non-volatile (battery sustained) RAM in two banks. Total of 242 bytes are available for user to store data or code that will be persistent between system power cycles (assuming external 3V battery is used).
I have plans regarding this memory for future iterations of the operating system, so right after I finished the RTC / BRAM card I got to working on enhancing the driver to have read / write access to this memory.

The new API:

/*
 * File: mkhbcos_ds1685.h
 * Purpose: Declarations and definitions for
 *          DS1685 RTC (Real Time Clock) chip API.
 * Author: Marek Karcz
 * Created: 02/05/2012
 *
 * Revision history:
 *
 *
 */

#ifndef MKHBCOS_DS1685
#define MKHBCOS_DS1685

// DS RTC registers mask bits

// reg. A

#define DSC_REGA_UIP 0x80 // %10000000
#define DSC_REGA_DV2 0x40 // %01000000
#define DSC_REGA_DV1 0x20 // 100000
#define DSC_REGA_DV0 0x10 // 010000
#define DSC_REGA_RS3 0x08 // 001000
#define DSC_REGA_RS2 0x04 // 000100
#define DSC_REGA_RS1 0x02 // 000010
#define DSC_REGA_RS0 0x01 // 000001

// aliases

#define DSC_REGA_CTDWN DSC_REGA_DV2
#define DSC_REGA_OSCEN DSC_REGA_DV1
#define DSC_REGA_BSEL DSC_REGA_DV0
#define DSC_REGA_BANK0 0xEF
#define DSC_REGA_BANK1 0x10

// reg. B

#define DSC_REGB_SET 0x80 // %10000000
#define DSC_REGB_PIE 0x40 // %01000000
#define DSC_REGB_AIE 0x20 // 100000
#define DSC_REGB_UIE 0x10 // 010000
#define DSC_REGB_SQWE 0x08 // 001000
#define DSC_REGB_DM 0x04 // 000100
#define DSC_REGB_24o12 0x02 // 000010
#define DSC_REGB_DSE 0x01 // 000001

// aliases

#define DSC_REGB_UNSET 0x7F // %01111111


struct ds1685_clkdata
{
unsigned char seconds;
unsigned char minutes;
unsigned char hours;
unsigned char dayofweek;
unsigned char date; // day
unsigned char month;
unsigned char year;
unsigned char century;
};

unsigned char __fastcall__ ds1685_init (unsigned char regb,
                                        unsigned char rega,
                                        unsigned char regextb,
                                        unsigned char regexta);

struct ds1685_clkdata *ds1685_rdclock (struct ds1685_clkdata *buf);
void ds1685_setclock (struct ds1685_clkdata *buf);
void ds1685_settime (struct ds1685_clkdata *buf);
/* bank - 0 or 1, addr - $00 - $7f ($0e - $7f for Bank 0), data - 0..255 */
void ds1685_storeram (unsigned char bank,
              unsigned char addr,
      unsigned char data);  
unsigned char __fastcall__ ds1685_readram(unsigned char bank,
                         unsigned char addr);

#endif

and the code:

;------------------------------------------------------------------
;
; File: mkhbcos_ds1685.s
; Author: Marek Karcz
; Purpose: Implements initialization routines and API for
; Real Time Clock chip DS1685 with multiplexed
;           address bus connected to buffered I/O bus as an I/O
;           device.
;
; Revision history:
; 2012-01-31:
; Initial revision.
;       (NOTE: These routines will eventually make their way
;              to EPROM as a part of firmware. At that time,
;              this file will be revised to call up the API
;              functions in the kernal table, instead of
;              being full implementation.)
;
; 2012-02-06:
; Implementation.
;
; 2015-11-29
;   I/O slot assignment changed.
;
; 2015-12-5
;   Modification due to hardware changes (Chris Ward variant):
;   DSCALADDR and DSCALDATA order changed.
;
; 2017-06-15:
;    Added functions to store and read non-volatile RAM.
;
;------------------------------------------------------------------

; M.O.S. API defines (kernal)

.define mos_StrPtr $E0
.define tmp_zpgPt $F6
.define IOBase $C000
.define RTC IOBase+256
;.define RTC IOBase+7*256

.define DSCALADDR RTC
.define DSCALDATA RTC+1

.setcpu "6502"
.import ldaxysp,pushax,popax,pusha,popa,staspidx
.import incsp2,incsp3,incsp4,ldauidx

.define sp $20

;RegB = tmp_zpgPt
;RegA = tmp_zpgPt+1
;RegXB = tmp_zpgPt+2
;RegXA = tmp_zpgPt+3
;RegC = tmp_zpgPt+4
;Temp = tmp_zpgPt+5

;BankNum = tmp_zpgPt
;RamAddr = tmp_zpgPt+1
;RamVal  = tmp_zpgPt+2 

.segment "DATA"

RegB:    .byte $00
RegA:    .byte $00
RegXB:   .byte $00
RegXA:   .byte $00
RegC:    .byte $00
Temp:    .byte $00
BankNum: .byte $00
RamAddr: .byte $00
RamVal:  .byte $00

ExtRamAddr = $50
ExtRamPort = $53

; DS RTC registers mask bits

; reg. A
DSC_REGA_UIP = %10000000
DSC_REGA_DV2 = %01000000
DSC_REGA_DV1 = 100000
DSC_REGA_DV0 = 010000
DSC_REGA_RS3 = 001000
DSC_REGA_RS2 = 000100
DSC_REGA_RS1 = 000010
DSC_REGA_RS0 = 000001
; aliases
DSC_REGA_CTDWN = DSC_REGA_DV2
DSC_REGA_OSCEN = DSC_REGA_DV1
DSC_REGA_BSEL = DSC_REGA_DV0
DSC_REGA_BANK0 = $EF
DSC_REGA_BANK1 = $10

; reg. B
DSC_REGB_SET = %10000000
DSC_REGB_PIE = %01000000
DSC_REGB_AIE = 100000
DSC_REGB_UIE = 010000
DSC_REGB_SQWE = 001000
DSC_REGB_DM = 000100
DSC_REGB_24o12 = 000010
DSC_REGB_DSE = 000001

; aliases

DSC_REGB_UNSET = %01111111


; code

.export _ds1685_init,_ds1685_rdclock,_ds1685_setclock,_ds1685_settime
.export _ds1685_readram,_ds1685_storeram
;,_read

.segment "CODE"

; Initialize DS1685 RTC chip.
; unsigned char __fastcall__ ds1685_init (unsigned char regb,
;                                         unsigned char rega,
;                                         unsigned char regextb,
;                                         unsigned char regexta)

.proc _ds1685_init: near

.segment "CODE"
; get parameters, put them in temp. buffer
jsr pusha
ldy #$03
;ldx #$00
lda (sp),y
sta RegB
ldy #$02
;ldx #$00
lda (sp),y
sta RegA
ldy #$01
;ldx #$00
lda (sp),y
sta RegXB
ldy #$00
;ldx #$00
lda (sp),y
sta RegXA
; initialize control register B
ldx RegB
lda #$0b
jsr WrRTC
; read status register C
lda #$0c
jsr RdRTC
sta RegC
; initialize control register A, switch to bank 1
lda RegA
ora #DSC_REGA_BANK1 ; switch to bank 1
tax
lda #$0a
jsr WrRTC
; initialize extended control register B
ldx RegXB
lda #$4b
jsr WrRTC
; initialize extended control register A
ldx RegXA
lda #$4a
jsr WrRTC
; switch to bank 0
  jsr Switch2Bank0
ldx #$00
lda RegC
  jsr incsp4
rts

.endproc

.segment "CODE"

; read clock data
; struct ds1685_clkdata *ds1685_rdclock(struct ds1685_clkdata *buf);
; struct ds1685_clkdata
; {
; unsigned char seconds;
; unsigned char minutes;
; unsigned char hours;
; unsigned char dayofweek;
; unsigned char date; // day
; unsigned char month;
; unsigned char year;
; unsigned char century;
; };

.proc _ds1685_rdclock:near

.segment "CODE"
; disable update transfers

lda #$0b
jsr RdRTC
sta RegB ; save register B for later
ora #DSC_REGB_SET
tax
lda #$0b
jsr WrRTC


; determine mode (BCD or BIN)

lda #DSC_REGB_DM
sta Temp
lda RegB
bit Temp
bne binmoderead

; can't do BCD mode yet, return
ldy #$01
jsr ldaxysp
jsr incsp2
rts

binmoderead:
; binary mode read

lda #$00 ; load register address of seconds
jsr RdRTC ; read register value to Acc
and #111111 ; mask 2 upper bits
ldy #$00
jsr Tfer2RetBuf ; transfer to return buffer at index 0 (seconds)

lda #$02 ; load register address of minutes
jsr RdRTC ; read register value to Acc
and #111111 ; mask 2 upper bits
ldy #$01
jsr Tfer2RetBuf ; transfer to return buffer at index 1 (minutes)

lda #$04 ; load register address of hours
jsr RdRTC ; read register value to Acc
pha
lda #DSC_REGB_24o12
sta Temp
lda RegB ; determine which hours mode (12/24 hours)
bit Temp
beq mode12hbin
pla
and #011111 ; mask 3 upper bits for 24H mode read
clc
bcc storehours
mode12hbin:
pla
and #001111 ; mask 4 upper bits for 12H mode read
storehours:
ldy #$02
jsr Tfer2RetBuf ; transfer to return buffer at index 2 (hours)

lda #$06 ; load register address of day (of week)
jsr RdRTC ; read register value to Acc
and #000111 ; mask 5 upper bits
ldy #$03
jsr Tfer2RetBuf ; transfer to return buffer at index 3 (dayofweek)

lda #$07 ; load register address of date (day of month)
jsr RdRTC ; read register value to Acc
and #011111 ; mask 3 upper bits
ldy #$04
jsr Tfer2RetBuf ; transfer to return buffer at index 4 (date)

lda #$08 ; load register address of month
jsr RdRTC ; read register value to Acc
and #001111 ; mask 4 upper bits
ldy #$05
jsr Tfer2RetBuf ; transfer to return buffer at index 5 (month)

lda #$09 ; load register address of year
jsr RdRTC ; read register value to Acc
and #%011111111 ; mask the highest bit
ldy #$06
jsr Tfer2RetBuf ; transfer to return buffer at index 6 (year)

  jsr Switch2Bank1

lda #$48 ; load register address of century
jsr RdRTC ; read register value to Acc
ldy #$07
jsr Tfer2RetBuf ; transfer to return buffer at index 7 (century)

  jsr Switch2Bank0

; enable update transfers

lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC

ldy #$01
jsr ldaxysp
jsr incsp2
rts

.endproc


.segment "CODE"

; set clock data
; void ds1685_setclock (struct ds1685_clkdata *buf);

.proc _ds1685_setclock:near

.segment "CODE"
; disable update transfers
; set binary mode
lda #$0b
jsr RdRTC
ora #DSC_REGB_SET
ora #DSC_REGB_DM
tax
lda #$0b
jsr WrRTC

ldy #$00 ; get argument 0 (seconds)
jsr GetParFromSpIdx
tax
lda #$00 ; write to DS1685 seconds register
jsr WrRTC

ldy #$01 ; get argument 1 (minutes)
jsr GetParFromSpIdx
tax
lda #$02 ; write to DS1685 minutes register
jsr WrRTC

ldy #$02 ; hours
jsr GetParFromSpIdx 
tax
lda #$04
jsr WrRTC

ldy #$03 ; day of week
jsr GetParFromSpIdx
tax
lda #$06
jsr WrRTC

ldy #$04 ; date (day of month)
jsr GetParFromSpIdx
tax
lda #$07
jsr WrRTC

ldy #$05 ; month
jsr GetParFromSpIdx
tax
lda #$08
jsr WrRTC

ldy #$06 ; year
jsr GetParFromSpIdx
tax
lda #$09
jsr WrRTC

; enable update transfers

lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC
; disable update transfers
lda #$0b
jsr RdRTC
ora #DSC_REGB_SET
tax
lda #$0b
jsr WrRTC

lda #$0a ; get reg. A
jsr RdRTC
ora #DSC_REGA_BANK1 ; switch to bank 1
tax
lda #$0a
jsr WrRTC

ldy #$07 ; century
jsr GetParFromSpIdx
tax
lda #$48
jsr WrRTC

  jsr Switch2Bank0

; enable update transfers

lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC

jsr incsp2

rts

.endproc

.segment "CODE"

; set clock data
; void ds1685_settime (struct ds1685_clkdata *buf);

.proc _ds1685_settime:near

.segment "CODE"
  jsr Switch2Bank0

; disable update transfers
; set binary mode
lda #$0b
jsr RdRTC
ora #DSC_REGB_SET
ora #DSC_REGB_DM
tax
lda #$0b
jsr WrRTC

ldy #$00 ; get argument 0 (seconds)
jsr GetParFromSpIdx
tax
lda #$00 ; write to DS1685 seconds register
jsr WrRTC

ldy #$01 ; get argument 1 (minutes)
jsr GetParFromSpIdx
tax
lda #$02 ; write to DS1685 minutes register
jsr WrRTC

ldy #$02 ; hours
jsr GetParFromSpIdx 
tax
lda #$04
jsr WrRTC

; enable update transfers

lda #$0b
jsr RdRTC
and #DSC_REGB_UNSET
tax
lda #$0b
jsr WrRTC

jsr incsp2

rts

.endproc

.segment "CODE"

; store value in non-volatile RAM
; void ds1685_storeram (unsigned char bank,
;                 unsigned char addr,
;         unsigned char data);

.proc _ds1685_storeram: near

.segment "CODE"
; get parameters, put them in temp. buffer
;jsr pusha
ldy #$02
;ldx #$00
lda (sp),y
sta BankNum
ldy #$01
;ldx #$00
lda (sp),y
sta RamAddr
ldy #$00
;ldx #$00
lda (sp),y
sta RamVal

; if Bank #1, jump to lwrextram
  lda BankNum
  bne lwrextram

; switch to Bank 0
  jsr Switch2Bank0

; A = address
; X = value
; write RAM
  lda RamAddr
  ldx RamVal
  jsr WrRTC
; exit
  jsr incsp3
  rts

; Write RAM in Bank 1
lwrextram:

  jsr Switch2Bank1
; load RAM addr. into extended ram address register
  ldx RamAddr
  lda #ExtRamAddr
  jsr WrRTC
; load RAM value into extended RAM port
  ldx RamVal
  lda #ExtRamPort
  jsr WrRTC

; switch to Bank 0
  jsr Switch2Bank0

; exit
  jsr incsp3
  rts

.endproc

.segment "CODE"

; read value from non-volatile RAM
; unsigned char __fastcall__ ds1685_readram (unsigned char bank,
;                                    unsigned char addr);

.proc _ds1685_readram: near

.segment "CODE"
; get parameters, put them in temp. buffer
jsr pusha
ldy #$01
;ldx #$00
lda (sp),y
sta BankNum
ldy #$00
;ldx #$00
lda (sp),y
sta RamAddr

; if Bank #1, jump to lwrextram
  lda BankNum
  bne lrdextram

; switch to Bank 0
  jsr Switch2Bank0

; A = address
; read RAM
  lda RamAddr
  jsr RdRTC
  sta Temp
; exit
  lda Temp
  ldx #00
  jsr incsp2
  rts

; read RAM from Bank 1
lrdextram:

  jsr Switch2Bank1
; load RAM addr. into extended ram address register
  ldx RamAddr
  lda #ExtRamAddr
  jsr WrRTC
; load RAM value into extended RAM port
  lda #ExtRamPort
  jsr RdRTC
  sta Temp

; switch to Bank 0
  jsr Switch2Bank0

; exit
  lda Temp
  ldx #00
  jsr incsp2
  rts

.endproc

.segment "CODE"

; helper procedures

;-------------------------------------------------------------------------------
; Write DS1685 address (Acc).
;-------------------------------------------------------------------------------

WrRTCAddr:
sta DSCALADDR
rts

;-------------------------------------------------------------------------------
; Write DS1685 data (Acc).
;-------------------------------------------------------------------------------

WrRTCData:
sta DSCALDATA
rts

;-------------------------------------------------------------------------------
; Read DS1685 data (-> Acc).
;-------------------------------------------------------------------------------

RdRTCData:
lda DSCALDATA
rts

;-------------------------------------------------------------------------------
; Write DS1685 Acc = Addr, X = Data
;-------------------------------------------------------------------------------

WrRTC:
jsr WrRTCAddr
txa
jsr WrRTCData
rts

;-------------------------------------------------------------------------------
; Read DS1685 Acc = Addr -> A = Data
;-------------------------------------------------------------------------------

RdRTC:
jsr WrRTCAddr
jsr RdRTCData
rts

;-------------------------------------------------------------------------------
; Transfer A to return buffer at index Y.
;-------------------------------------------------------------------------------

Tfer2RetBuf:

pha
tya
pha
  ldy     #$01 ; transfer to return buffer
  jsr     ldaxysp
  jsr     pushax
  ldx     #$00
pla
tay
pla
  jsr     staspidx
rts

;-------------------------------------------------------------------------------
; Load to A from arguments buffer/stack at index Y.
;-------------------------------------------------------------------------------

GetParFromSpIdx:

tya
pha
  ldy   #$01 ; get buffer
  jsr   ldaxysp
sta Temp
pla
tay
lda Temp
  jsr   ldauidx
rts

;-------------------------------------------------------------------------------
; Swicth to RTC registers in bank 0.
;-------------------------------------------------------------------------------

Switch2Bank0:

  lda #$0a        ; get register A
jsr RdRTC
and #DSC_REGA_BANK0  ; switch to bank 0
tax
lda #$0a        ; write register A
jsr WrRTC
  rts

;-------------------------------------------------------------------------------
; Swicth to RTC registers in bank 0.
;-------------------------------------------------------------------------------

Switch2Bank1:

lda #$0a ; get reg. A
jsr RdRTC
ora #DSC_REGA_BANK1 ; switch to bank 1
tax
lda #$0a      ; write reg. A
jsr WrRTC
  rts


;------------------------------ END OF FILE -------------------------------------

Presented below is the computer now in full glory with PropTermMK device, PC keyboard and small VGA display.
I also added few screenshots of the PropTermMK features and NV RAM access functions in action.










This is all I have time for today. I know I promised to finally publish all firmware code and blueprints to GitHub, but I need to clean up the code some more before I do that and some pressing home improvement projects are now pulling me away from this one.

Thank you for visiting my blog.

MK 6/19/2017