From d8fa6a36ffba80be398c90641b3e7e7a79d56d43 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 11 Feb 2013 21:34:04 +0000 Subject: [PATCH] * I2C Support added - Generic I2C driver - PCF8574 8-bit I/O support - PCF8583 RTC support * cpm/utils/AVRCLOCK.MAC - ZSDOS clock driver added * cpm/utils/TIMER.MAC - renamed to ACT.MAC * cpm/utils/RTCDEMO.PAS - Turbo Pascal program to demonstrate the I2C interface added. git-svn-id: svn://cu.loc/avr-cpm/avrcpm/trunk@195 57430480-672e-4586-8877-bcf8adbbf3b7 --- avr/Makefile | 8 +- avr/avrcpm.asm | 5 +- avr/config.inc | 47 ++- avr/i2c.asm | 477 +++++++++++++++++++++++++++++++ avr/init.asm | 6 +- avr/timer.asm | 379 ++++++++++++++++++++---- avr/virt_ports.asm | 38 ++- cpm/BIOS.MAC | 111 +++++-- cpm/CFGACPM.LIB | 73 ++++- cpm/Makefile | 10 + cpm/utils/{TIMER.MAC => ACT.MAC} | 0 cpm/utils/AVRCLOCK.MAC | 195 +++++++++++++ cpm/utils/RTCDEMO.PAS | 294 +++++++++++++++++++ 13 files changed, 1532 insertions(+), 111 deletions(-) create mode 100644 avr/i2c.asm rename cpm/utils/{TIMER.MAC => ACT.MAC} (100%) create mode 100644 cpm/utils/AVRCLOCK.MAC create mode 100644 cpm/utils/RTCDEMO.PAS diff --git a/avr/Makefile b/avr/Makefile index 664425f..efc5b1c 100644 --- a/avr/Makefile +++ b/avr/Makefile @@ -10,6 +10,7 @@ BAUD = 57600 #BAUD = 115200 DRAM_8BIT = 1 +#I2C = 1 TARGET = avrcpm ASRC0 = avrcpm.asm @@ -20,7 +21,7 @@ ASRC0 += dsk_cpm.asm dsk_fat16.asm dsk_fsys.asm dsk_mgr.asm dsk_ram.asm ASRC0 += 8080int-orig.asm 8080int.asm 8080int-jmp.asm 8080int-t3.asm 8080int-t3-jmp.asm Z80int-jmp.asm ifneq ($(DRAM_8BIT),0) - ASRC0 += dram-8bit.inc dram-8bit.asm sw-uart.asm + ASRC0 += dram-8bit.inc dram-8bit.asm sw-uart.asm i2c.asm else ASRC0 += dram-4bit.inc dram-4bit.asm hw-uart.asm endif @@ -29,7 +30,10 @@ endif ASRC := $(ASRC0) svnrev.inc # Place -D or -U options here -CDEFS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -D$(MCU) -DDRAM_8BIT=$(DRAM_8BIT) +CDEFS = -DF_CPU=$(F_CPU) -DBAUD=$(BAUD) -D$(MCU) -DDRAM_8BIT=$(DRAM_8BIT) +ifdef I2C + CDEFS += -DI2C=$(I2C) +endif ASPATH = C:/Programme/Atmel/AVR\ Tools/AvrAssembler2 DEFS = $(ASPATH)/Appnotes diff --git a/avr/avrcpm.asm b/avr/avrcpm.asm index 5103f77..ff278dd 100644 --- a/avr/avrcpm.asm +++ b/avr/avrcpm.asm @@ -50,10 +50,11 @@ .org INT_VECTORS_SIZE .include "init.asm" -#if DRAM_8BIT /* Implies software uart */ +#if DRAM_8BIT /* Implies software uart */ .include "sw-uart.asm" .include "dram-8bit.asm" -#else /* 4 bit RAM, hardware uart */ + .include "i2c.asm" +#else /* 4 bit RAM, hardware uart */ .include "hw-uart.asm" .include "dram-4bit.asm" #endif diff --git a/avr/config.inc b/avr/config.inc index f6d16c9..3d63410 100644 --- a/avr/config.inc +++ b/avr/config.inc @@ -34,14 +34,14 @@ #ifndef BAUD #define BAUD 38400 /* console baud rate */ #endif - -#define K 1024 -#define M 1024*K - -;#define RAMSIZE 256*K*4 /* 1 chip 256Kx4 */ -#define RAMSIZE 4*M*4 * 2 /* 2 chips 4Mx4 */ +#ifndef I2C + #define I2C DRAM_8BIT /* I2C requires 8 bit DRAM */ +#endif +#if I2C && !DRAM_8BIT + #error "I2C requires 8 bit DRAM (DRAM_8BIT=1)!" +#endif -#define EM_Z80 1 /* Emulate Z80 if true */ +#define EM_Z80 1 /* Emulate Z80 if true, else 8080 */ #ifndef FAT16_SUPPORT #define FAT16_SUPPORT 1 /* Include Support for FAT16 Partitions */ @@ -59,12 +59,9 @@ #define RXBUFSIZE 128 /* USART recieve buffer size. Must be power of 2 */ #define TXBUFSIZE 128 /* USART transmit buffer size. Must be power of 2 */ +#define I2C_CLOCK 100000 /* 100kHz */ +#define I2C_BUFSIZE 17 /* largest message size including address byte (SLA) */ -#if EM_Z80 - #define CPUSTR "Z80" -#else - #define CPUSTR "8080" -#endif .equ BOOTWAIT = 1 .equ MEMTEST = 1 @@ -87,6 +84,12 @@ #define DBG_TRACE_BOTTOM 0x01 /* Page boundaries for INS_DEBUG and PRINT_PC */ #define DBG_TRACE_TOP 0xdc /* Trace is off, below bottom page and above top page. */ +#if EM_Z80 + #define CPUSTR "Z80" +#else + #define CPUSTR "8080" +#endif + ;----------------------------------------------------------------------- ; Port declarations @@ -276,6 +279,25 @@ #define startTraceCmd 1 #define stopTraceCmd 0 +; Virtual I2C Interface +#define I2CSTAT 0x05 +#define I2CCTRL 0x05 +#define I2CBLEN 0x06 +#define I2CADR 0x07 +#define I2CADRL 0x07 +#define I2CADRH 0x08 + +; Port-Expander PCF8574 +#define PORT 0x80 +#define PORT0 0x80 +#define PORT1 0x81 +#define PORT2 0x82 +#define PORT3 0x83 +#define PORT4 0x84 +#define PORT5 0x85 +#define PORT6 0x86 +#define PORT7 0x87 + #if defined __ATmega8__ .equ RXTXDR0 = UDR @@ -306,4 +328,3 @@ ; vim:set ts=8 noet nowrap - diff --git a/avr/i2c.asm b/avr/i2c.asm new file mode 100644 index 0000000..4932173 --- /dev/null +++ b/avr/i2c.asm @@ -0,0 +1,477 @@ +; I2C (TWI) master interface. +; This is part of the Z80-CP/M emulator written by Sprite_tm. +; +; Copyright (C) 2010 Leo C. +; +; This file is part of avrcpm. +; +; avrcpm 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 3 of the License, or +; (at your option) any later version. +; +; avrcpm 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 avrcpm. If not, see . +; +; $Id$ +; + +#if I2C + +/* General TWI Master status codes */ +#define TWI_START 0x08 /* START has been transmitted */ +#define TWI_REP_START 0x10 /* Repeated START has been transmitted */ +#define TWI_ARB_LOST 0x38 /* Arbitration lost */ + +/* TWI Master Transmitter status codes */ +#define TWI_MTX_ADR_ACK 0x18 /* SLA+W has been transmitted and ACK received */ +#define TWI_MTX_ADR_NACK 0x20 /* SLA+W has been transmitted and NACK received */ +#define TWI_MTX_DATA_ACK 0x28 /* Data byte has been transmitted and ACK received */ +#define TWI_MTX_DATA_NACK 0x30 /* Data byte has been transmitted and NACK received */ + +/* TWI Master Receiver status codes */ +#define TWI_MRX_ADR_ACK 0x40 /* SLA+R has been transmitted and ACK received */ +#define TWI_MRX_ADR_NACK 0x48 /* SLA+R has been transmitted and NACK received */ +#define TWI_MRX_DATA_ACK 0x50 /* Data byte has been received and ACK transmitted */ +#define TWI_MRX_DATA_NACK 0x58 /* Data byte has been received and NACK transmitted */ + +/* TWI Miscellaneous status codes */ +#define TWI_NO_STATE 0xF8 /* No relevant state information available */ +#define TWI_BUS_ERROR 0x00 /* Bus error due to an illegal START or STOP condition */ + + +#define I2C_BR ((F_CPU / (2 * I2C_CLOCK)) - 8) /* I2C Bit Rate */ + +;---------------------------------------------------------------------- +; +; TWINT: TWI Interrupt Flag +; TWEA: TWI Enable Acknowledge Bit +; TWSTA: TWI START Condition Bit +; TWSTO: TWI STOP Condition Bit +; TWEN: TWI Enable Bit +; TWIE: TWI Interrupt Enable +; +; (1< Bad Sector) ; +; +; +;------------------------ Wall Clock and Timers -------------------------- ;40 64-71 in/out - Timer/Clock control. ;41-46 ; -;47-4C clock - Binary format: y, m, d, h, m, s +;47-4D clock - BCD format: ss, mm, hh, DD, MM, YYl, YYh +; +;------------------------ Ports ------------------------------------------ +;80-87 in/out - Port-Expander PCF8574 (max. 8 Chips) +;88-8F in/out - Port-Expander PCF8574A (not implemented yet!) ; ---------------------------------------------- Start of Code Segment @@ -89,13 +104,28 @@ vport_tbl: .dw utimeget .dw utimeput - .db CLOCKPORT,6 ;Clock format (bin): y, m, d, h, m, s + .db CLOCKPORT,7 ;Clock format (bcd): ss, mm, hh, DD, MM, YYl, YYh .dw clockget .dw clockput .db DEBUGPORT,1 .dw dbg_stat .dw dbg_ctrl + +#if I2C + .db I2CCTRL,1 + .dw vi2c_stat_get + .dw vi2c_ctrl + + .db I2CBLEN,3 ; + .dw vi2c_param_get + .dw vi2c_param_set + + .db PORT,8 + .dw pcf8574_in + .dw pcf8574_out +#endif + .db 0,0 ; Stop mark ;--------------------------------------------------------------------- @@ -147,8 +177,8 @@ vprw_next: ;port # not in range, test next block. adiw z,4 rjmp vprw_loop vprw_found: - brtc PC+2 - adiw z,2 + brtc PC+2 ;read or write? + adiw z,2 ;skip read function pointer lpm _tmp0,z+ lpm _tmp1,z+ movw z,_tmp0 diff --git a/cpm/BIOS.MAC b/cpm/BIOS.MAC index 2b3842f..a260ec8 100644 --- a/cpm/BIOS.MAC +++ b/cpm/BIOS.MAC @@ -19,31 +19,39 @@ maclib CFGACPM.LIB +cr equ 0dh +lf equ 0ah + aseg org 100h .phase bios .z80 -nsects equ ($-ccp)/128 ;warm start sector count +nsects equ ($-ccp)/128 ;warm start sector count - jp boot + jp boot wboote: - jp wboot - jp const - jp conin - jp conout - jp list - jp punch - jp reader - jp home - jp seldsk - jp settrk - jp setsec - jp setdma - jp read - jp write - jp listst - jp sectran + jp wboot + jp const + jp conin + jp conout + jp list + jp punch + jp reader + jp home + jp seldsk + jp settrk + jp setsec + jp setdma + jp read + jp write + jp listst + jp sectran + jp 0 ;zsdos (?) + jp 0 ;zsdos (?) + jp 0 ;zsdos (?) + jp clock ;zsdos compatible clock set/get + .8080 maclib AVRCPM.LIB @@ -92,34 +100,34 @@ msgnodisk: db cr,lf,0 const: - in a,(0) + in a,(0) ret conin: - in a,(0) - cp 0ffh - jp nz,conin + in a,(0) + cp 0ffh + jp nz,conin - in a,(1) + in a,(1) ret conout: - ld a,c - out (1),a + ld a,c + out (1),a ret list: ret listst: - ld a,0 + ld a,0 ret punch: ret reader: - ld a,01Fh + ld a,01Fh ret prmsg: @@ -405,6 +413,55 @@ sectran: ld h,0 ret +;------------------------------------------------------------------------ +; ZSDOS clock drivers may use registers BC and D without restoring them, +; but must preserve the Z80's alternate and index registers. +; Other registers must be used exactly as follows: +; +; Enter: C = 00H to Read the Clock, 01H to Set the Clock +; DE = Address of a 6-byte field to Receive or from which +; to Set time in DateStamper format (BCD digits as: +; YY MM DD HH MM SS). 24-hour operation is assumed. +; +; Exit : A = 01H for a successful operation, +; 0FFH for a failure of any sort (Can't set, etc.) +; +; When Reading the Clock: +; E = Original contents of Entry value of DE plus 5 +; HL = Entry value of DE plus 5 (Seconds field) + +clock: + dec c + jr z,clk_set + inc c + ret nz + +clk_read: + ex de,hl + ld bc,5*256 + CLOCKPORT-1 +clkg_l: + inc c + ini + jr nz,clkg_l + ld e,(hl) + in a,(CLOCKPORT+5) + ld (hl),a + jr clk_e + +clk_set: + ld hl,5 + add hl,de + ld bc,6*256 + CLOCKPORT+6 +clks_l: + dec c + outd + jr nz,clks_l +clk_e: + ld a,1 + ret + +;------------------------------------------------------------------------ + bcb: dw drvtbl dw dirbuf dw enddat diff --git a/cpm/CFGACPM.LIB b/cpm/CFGACPM.LIB index 6f30879..7c2f7d0 100644 --- a/cpm/CFGACPM.LIB +++ b/cpm/CFGACPM.LIB @@ -29,11 +29,80 @@ iobyte equ 0003h ;intel iobyte buff equ 0080h ;default buffer address retry equ 3 ;max retries on disk i/o before error + +;copy from avr/virt_ports.asm: +; Port Direction Function +;hex dez +;------------------------------------------------------------------------- +;00 0 in - Con status. +; Returns 0xFF if the UART has a byte, 0 otherwise. +;01 1 in/out - Console input, aka UDR. / Console Output +;02 2 out - Console Output (deprecated) +;03 3 in - "UART" status: bit 0 = rx, bit 1 = tx +;04 4 in - "UART" data register, no wait +; +;------------------------ Virtual I2C interface -------------------------- +;05 5 out - Control Port: 1 = Start read operation +; 2 = Start write operation +;05 5 in - Status of last Transfer: 0 = ok, else fail +;06 6 in/out - Number of bytes to transfer, including Slave address +;07,08 7,8 in/out - Read/Write address low/high +; +;------------------------ Disk I/O --------------------------------------- +;0D,0E 13,14 in/out - Set address of Bios Controll Block +;0F 15 in/out - Disk select +;10,11 16,17 in/out - Track select +;12,13 18,19 in/out - Sector select +;14,15 20,21 in/out - Write addr +; +;16 22 out - Trigger disk i/o operations +; Bit 7 = 1: Read sector +; Bit 6 = 1: Write sector +; Bit 5 = 1: BIOS WBOOT +; Bit 4 = 1: BIOS Home +; Only one of bits 4..7 may be set. +; If Write function (bit 6=1): +; Bits 0..2: 0 - write to allocated +; 1 - write to directory +; 2 - write unallocated +; 3 - write to directory +; +;16 22 in - Result of last read/write operation. +; 0x00 = ok, 0xff = error (--> Bad Sector) +; +; +; +;------------------------ Wall Clock and Timers -------------------------- +;40 64-71 in/out - Timer/Clock control. +;41-46 +; +;47-4D clock - BCD format: ss, mm, hh, DD, MM, YYl, YYh +; +;------------------------ Ports ------------------------------------------ +;80-87 in/out - Port-Expander PCF8574 (max. 8 Chips) +;88-8F in/out - Port-Expander PCF8574A (not implemented yet!) + + READ_FUNC equ 7 WRITE_FUNC equ 6 BOOT_FUNC equ 5 HOME_FUNC equ 4 -cr equ 13 -lf equ 10 +TIMERCTL equ 040h +TIMER_MSECS equ TIMERCTL+1 +TIMER_SECS equ TIMER_MSECS+2 +starttimercmd equ 1 +quitTimerCmd equ 2 +printTimerCmd equ 15 +uptimeCmd equ 16 + +CLOCKPORT equ TIMERCTL+7 + +DEBUGPORT equ 04FH +StartTraceCmd equ 1 +StopTraceCmd equ 0 + + + + diff --git a/cpm/Makefile b/cpm/Makefile index e12e618..e60ea57 100644 --- a/cpm/Makefile +++ b/cpm/Makefile @@ -37,6 +37,14 @@ CPM.BIN: IPL.BIN BIOS.BIN $(CPMSYS) dd conv=sync bs=128 count=7 if=BIOS.BIN >> tmpCPM.BIN &&\ mv tmpCPM.BIN CPM.BIN +zsdossys: IPL.BIN CCP.BIN ZSDOS.BIN BIOS.BIN + dd conv=sync bs=118 count=1 if=IPL.BIN > tmpCPM.BIN &&\ + echo -n "" >> tmpCPM.BIN &&\ + dd conv=sync bs=128 count=16 if=CCP.BIN >> tmpCPM.BIN &&\ + dd conv=sync bs=128 count=28 if=ZSDOS.BIN >> tmpCPM.BIN &&\ + dd conv=sync bs=128 count=7 if=BIOS.BIN >> tmpCPM.BIN &&\ + mv tmpCPM.BIN zsdossys + BIOS.PRN BIOS.REL : AVRCPM.LIB CFGACPM.LIB IPL.PRN IPL.REL : CFGACPM.LIB @@ -94,6 +102,7 @@ help: @echo -e "The following make targets are supported:\n"\ " diskimage - Build a complete CP/M image. (default)\n"\ " CPM.BIN - Build CP/M system (IPL+CCP+BDOS+BIOS).\n"\ + " zsdossys - Build ZSDOS system (IPL+CCP+ZSDOS+BIOS).\n"\ "\n"\ " BIOS.PRN - Make a listing file from bios.asm\n"\ " IPL.PRN - Dito for ipl\n"\ @@ -106,3 +115,4 @@ help: "\n"\ " clean - Remove intermediate and output files.\n"\ " help - Print this message and exit.\n" + diff --git a/cpm/utils/TIMER.MAC b/cpm/utils/ACT.MAC similarity index 100% rename from cpm/utils/TIMER.MAC rename to cpm/utils/ACT.MAC diff --git a/cpm/utils/AVRCLOCK.MAC b/cpm/utils/AVRCLOCK.MAC new file mode 100644 index 0000000..dc506de --- /dev/null +++ b/cpm/utils/AVRCLOCK.MAC @@ -0,0 +1,195 @@ +VERS EQU 02 + .Z80 +; NAME AVRCLK ; Change this to no more than 6-char + ; name for the REL driver module + +;--------------------------------------------------------------------- +; Time format (6 bytes packed BCD) +; +; TIME+0 last 2 digits of year (prefix 19 assumed for 78 to 99, else 20 assumed) +; TIME+1 month [1..12] +; TIME+2 day [1..31] +; TIME+3 hour [0..23] +; TIME+4 minute [0..59] +; TIME+5 second [0..59] +; + +; This first section contains identification information for the driver +; The information is not placed in the clock driver code section, but are +; located in a different area located by the _CLKID Named Common directive. + + COMMON /_CLKID/ + +DESCST: DEFW 0000 ; Add label here if a static year byte + ; is used by your clock driver. The + ; label should point to the year byte + + ;123456789012345678901234 +CLKNAM: DEFB 'AVRCPM Clock ' ; Exactly 24 chars in name + DEFB VERS/10+'0','.',VERS MOD 10 +'0',0 + +DESCR: DEFB 'This is the AVRCPM clock',0 + + +;--------------------------------------------------------------------- +; This section contains any configurable parameters needed for the +; clock driver. They must be structured in the manner shown in order +; for the loader to properly match and set the values. +; The values in this section are not loaded in the same code section +; as the actual driver code, but are located in another base referenced +; by the _PARM_ Named Common directive. + + COMMON /_PARM_/ + +PARBAS: DEFW 0 ; # of parameters (Set to 00 if none) + DEFW 0 ; Pointer to STRS (Set to 00 if none) + +;------------------------------------------------------------------ +; This section should contain the actual Clock Driver code, and all +; entries here are located in the CSEG, or Code Segment. + + CSEG + +MHZ equ 2 ; Base Processor speed +CLOCKPORT equ 47h + +;----------------------------------------------------------- +; Z S D O S C L O C K H E A D E R +;----------------------------------------------------------- +; Enter: HL points to a 6-byte buffer to Get/Set time +; Exit : A=1 on Success, A=FFH if error +; HL points to last char in buffer +; E contains original seconds (HL+5) +; NOTE: If clock Set is not included, comment these two jumps +; out to save a few bytes. The loader, SETUPZST, uses +; these two jumps to recognize a full ZSDOS clock and +; modify the interface code. + +PRGBAS: JP GETTIM ; Jump to Read Clock + JP WRCLK ; Jump to Set Clock + +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; R e a d T h e C l o c k +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +GETTIM: ; The work of reading the clock + ; goes here. Values needing to be set + ; during installation are referenced as: + ld bc,5 + add hl,bc + push hl + ld b,6 + ld c,CLOCKPORT + ld e,(hl) +GETT_l: + in a,(c) + ld (hl),a + inc c + dec hl + djnz GETT_l + pop hl + ld a,1 + ret + + +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - +; S e t T h e C l o c k +;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +WRCLK: + ld a,(hl) + cp 78h + ld a,19h + jr nc,WRC_1 + ld a,20h +WRC_1: + out (CLOCKPORT+6),a + ld b,6 + ld c,CLOCKPORT+5 +WRC_l: + ld a,(hl) + out (c),a + dec c + inc hl + djnz WRC_l + dec hl + ld a,1 + ret + + +;------------------------------------------------------------- +; This code installs configurable items into the clock module +; Enter with DE pointing to the physical base address of the +; relocatable module. DE MUST BE USED TO SET VALUES IN +; THE CSEG PORTION OF CODE! +; NOTE: Code in this section is not added to the actual clock +; driver, but placed in a different area referenced to +; the common base _POST_. + + COMMON /_POST_/ + +; Values in the _PARM_, _POST_ and _PRE_ sections may be loaded +; and saved directly, since their addresses are constant from +; linkage through execution. Setting or reading values in the +; CSEG must be indirect based on the value in the DE register +; pair. The following examples show how to access the various +; sections. +; +; LD A,(XYR) ; EXAMPLE - Get byte from _PARM_ directly +; LD HL,YYR ; " - Begin offset into CSEG indirectly +; ADD HL,DE ; " - HL now addresses relocated loc'n +; LD (HL),A ; " - ..so value can be stored +; +; Likewise, 16-bit values must be accessed indirectly, and may use +; the BC register pair as transfer storage. +; +; LD BC,(XPORT) ; EXAMPLE - Get word from _PARM_ directly +; LD HL,YPORT1 ; " - Begin offset into CSEG indirectly +; ADD HL,DE ; " - HL now addresses relocated loc'n +; LD (HL),C ; " - ..so value can be saved.. +; INC HL ; " - ...a byte.. +; LD (HL),B ; " - ....at a time.. +; +; LD (YPORT2),BC ; EXAMPLE - Values can be stored directly into +; " - other sections such as _PRE_ + + RET ; This RETURN MUST be present even if no other + ; code is included in this section + + +;---------------------------------------------------------------- +; This module is executed just prior to installing the module to +; insure that a valid clock is present +; Enter with DE pointing to base of relocated clock code segment +;--------------------------------------------------------------- +; Read clock and wait for seconds to roll - watchdog protected +; Enter with: DE pointing to relocated clock read routine +; HL pointing to base of high module + + COMMON /_PRE_/ + +; Optional final setup of the clock module may go here. Examples of such +; code would be installation-dependant items such as physical RAM location +; for the driver module. If any code is added here, the DE register pair +; MUST be preserved to properly inter PRECLOCK code (If included). + +;YPORT2 EQU $+1 ; EXAMPLE - just to show accessing method +; LD BC,0000 ; " - ..from _POST_ code. + + INCLUDE PRECLOCK.LIB ; This section of code merely calls the + ; clock and waits an arbitrary period of + ; time (>> 1 second) to see if the time + ; changes. It returns an error if not. + if 0 +TSTRD: JR TSTRD0 ; Jump around address store + + DEFW TSTRD ; Org location of the code +TSTRD0: SCF + LD A,1 + RET + endif + + + + END + diff --git a/cpm/utils/RTCDEMO.PAS b/cpm/utils/RTCDEMO.PAS new file mode 100644 index 0000000..dfe332d --- /dev/null +++ b/cpm/utils/RTCDEMO.PAS @@ -0,0 +1,294 @@ +program RTCDemo; +{$C- } {Sonst funktioniert 'keypressed' nicht} + +{--------------------------------------------------------------------- + AVRCPM + Test und Demonstration des I2C-Interface + I2C Uhrenchip PCF8583 + Zeit und Datum lesen und schreiben + + Moegliche Erweiterung (TODO:): + - Control/Status-Register und Alarmregister lesen/schreiben + - RAM lesen/schreiben + + $Id$ +----------------------------------------------------------------------} + +const + I2CCMD = 5; {adr of I2C Command Port (1=read, 2=write)} + I2CMSGLEN = 6; {Transferpuffergroesse} + I2CADRL = 7; {Transferpufferadresse low/high} + I2CADRH = 8; + + I2C_CMD_Read = 1; {I2C Read Command} + I2C_CMD_Write = 2; {I2C Write Command} + + { TP Delay Loop } + T100ms = 100; {20MHz AVR + Config fuer 3 MHz Z80 (TINST)} + {T100ms = 75;} {20MHz AVR + Config fuer 4 MHz Z80 (TINST default)} + +type + I2CBufLen = 0..16; + + CalTime = record + sec: 0..59; + min: 0..59; + hrs: 0..23; + day: 1..31; + month: 1..12; + year: integer; + end; + + DTinput = array [1..3] of integer; + +var + msgbuf : array[0..16] of Byte; {TODO: Transferbuffer nicht global} + + Time : CalTime; + Done: boolean; + +{--------------------------------------------------------------------- + Debugging: Print 16 byte RAM ab adr +----------------------------------------------------------------------} +procedure hexdump(adr: integer); +var + i: integer; + c: byte; + + function hexdigit(c: byte): char; + begin + if c < 10 then + hexdigit := char(c + $30) + else + hexdigit := char(c - 10 + $41); + end; + +{TODO: print adr } +begin + for i := 0 to 15 do + begin + c := Mem[adr+i]; + write(hexdigit(c shr 4), hexdigit(c and $f), ' '); + end; + writeln; +end; + +{--------------------------------------------------------------------- + I2C - Routinen +----------------------------------------------------------------------} +procedure i2c_init; +var + i: integer; +begin + msgbuf[0] := $A0; {I2C-Adresse des RTC-Chips} + + Port[I2CADRH] := Hi(Addr(msgbuf)); + Port[I2CADRL] := Lo(Addr(msgbuf)); +end; + +procedure i2c_write(len:Byte); +begin + Port[I2CMSGLEN] := len; + Port[I2CCMD] := I2C_CMD_Write; +end; + +procedure i2c_read(len:Byte); +begin + Port[I2CMSGLEN] := len; + Port[I2CCMD] := I2C_CMD_Read; +end; + + +function BCDtoBINbyte(i: byte): byte; +begin + BCDtoBINbyte := (i div 16) * 10 + (i and $F); +end; + +function BINtoBCDbyte(i: byte): byte; +begin + BINtoBCDbyte := (i div 10) * 16 + (i mod 10); +end; + +procedure ReadRTC(var t: CalTime); +begin + msgbuf[1] := 2; + i2c_write(2); + i2c_read(6); + with t do + begin + sec := BCDtoBINbyte(msgbuf[1]); + min := BCDtoBINbyte(msgbuf[2]); + hrs := BCDtoBINbyte(msgbuf[3] and $3F); + end; + msgbuf[1] := $10; + i2c_write(2); + i2c_read(3); + with t do + begin + day := BCDtoBINbyte(msgbuf[4] and $3F); + month:= BCDtoBINbyte(msgbuf[5] and $1F); + year := msgbuf[1] + 256*msgbuf[2]; + while Lo(year) and $3 <> (msgbuf[4] shr 6) do + year := year + 1; + end; +end; + + +procedure WriteRTC(t: CalTime); +begin + with t do begin + msgbuf[1] := 1; {register address} + msgbuf[2] := 0; {hundredth of sec} + msgbuf[3] := BINtoBCDbyte(sec); + msgbuf[4] := BINtoBCDbyte(min); + msgbuf[5] := BINtoBCDbyte(hrs); + msgbuf[6] := BINtoBCDbyte(day) + (Lo(year) shl 6); + msgbuf[7] := BINtoBCDbyte(month); {TODO: weekdays} + end; + i2c_write(8); + + msgbuf[1] := $10; {register address} + with t do begin + msgbuf[2] := Lo(year); + msgbuf[3] := Hi(year); + end; + i2c_write(4); +end; + +{--------------------------------------------------------------------- + Dialog Routinen +----------------------------------------------------------------------} + +procedure PrintTime(t: CalTime); +begin + with t do {TODO: Fuehrende '0' statt ' '} + begin + write('Zeit: ', hrs:2, ':',min:2, ':',sec:2, ' ', + 'Datum: ', day, '.', month, '.', year); + end; +end; + +procedure PrintMenu; +begin + ClrScr; + writeln('Menu:'); + writeln(' T) Zeit setzen'); + writeln(' D) Datum setzen'); + writeln(' Q) Quit'); + writeln('> '); +end; + +function GetKey: char; +var + c: char; +begin + Read(Kbd, c); + GetKey := c; +end; + +{Daemliche input routine ohne Fehlerpruefung (TODO:)} +procedure GetDateTime(var a: DTinput; sep: char); +var + line: String[80]; + s: String[4]; + i,j,k: integer; + rc: integer; + +begin + readln(line); + line := line + sep; + j := 1; + for i := 1 to 3 do + begin + k := 1; + while line[k+j] in ['0'..'9'] do + k := k + 1; + s := Copy(line, j, k); + val(s, a[i], rc); + j := j + k + 1; + end; +end; + +procedure SetTime; +var + a: DTinput; + +begin + write('Zeit (hh:mm:ss): '); + a[1] := -1; + GetDateTime(a, ':'); +{ writeln('intime:', a[1], ':', a[2], ':', a[3]); +} + if (a[1] in [0..24]) and (a[2] in [0..59]) and (a[3] in [0..59]) then + begin + ReadRTC(Time); + Time.hrs := a[1]; + Time.min := a[2]; + Time.sec := a[3]; + WriteRTC(Time); + end else + begin + writeln('Fehler in Eingabe.'); + end; +end; + +procedure SetDate; +var + a: DTinput; + +begin + write('Datum (TT.MM.JJJJ): '); + a[1] := -1; + GetDateTime(a, '.'); +{ writeln('indate:', a[1], '.', a[2], '.', a[3]); +} + if (a[1] in [1..31]) and (a[2] in [1..12]) and (a[3] > 0) and (a[3] <= 2076) then + begin + ReadRTC(Time); + Time.day := a[1]; + Time.month:= a[2]; + Time.year := a[3]; + WriteRTC(Time); + end else + begin + writeln('Fehler in Eingabe.'); + end; +end; + +procedure CleanConsolebuffer; +var + c: char; +begin + while keypressed do c := GetKey; +end; + +{--------------------------------------------------------------------- + main +----------------------------------------------------------------------} + +begin + PrintMenu; + i2c_init; + + writeln; + Done := False; + repeat + Delay(T100ms); + ReadRTC(Time); + GotoXY(35,1); PrintTime(Time); ClrEol; + gotoXY(2,5); ClrEol; + + if keypressed then + begin + gotoXY(1,7); ClrEol; + gotoXY(1,6); ClrEol; + case UpCase(GetKey) of + 'T': SetTime; + 'D': SetDate; + 'Q': Done := True; + end; + end; + until Done; + CleanConsolebuffer; +end. + \ No newline at end of file -- 2.39.2