home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
kaypro
/
kp4time2.lbr
/
KP4TIME2.AQM
/
KP4TIME2.ASM
Wrap
Assembly Source File
|
1985-02-09
|
22KB
|
491 lines
; TIME.COM (8080 version) Version 2.0.
; CP/M program to display and set the real-time clock on Kaypro 4-84.
; Written 04/22/84 by Bob Snider, Columbus Ohio. Greatly expanded 6/30/84.
; Dispaly is MM/DD HH:MM:SS and can be 24-hour format or 12-hour with
; AM/PM. Date is optional, and the ability to set the clock is optional
; for those who want a minimum length module. Customize this assembly
; by setting the options in the "Customization section".
; The clock is just displayed by the command "TIME". The clock is displayed
; and set by anything extra on the command line, ie. "TIME SET". The
; program will prompt for date and time value which must be entered as in the
; display format. Each of the following items can be set independently:
; Date (month/day)
; Day of the week
; Time of day (seconds may be omitted)
; If the 12-hour format is on, AM or PM must always follow the time.
; Any omitted items are not changed in the clock.
; The clock is set at the carriage return. The input line is parsed for
; proper format, but not checked for valid dates or times. If nothing is
; entered the time is unchanged. If an error is detected, the position of
; the error in the string is flagged with a '^' and nothing is set.
; The clock is checked to see if an update has occurred while being read,
; and it is re-read if so, ensuring valid time displays.
; Problems or enhancements should be directed to the Kaypro User's Group on
; CompuServe, page PCS-25.
;
; CONDITIONAL ASSEMBLY CONSTANTS FOR CUSTOMIZATION.
NO EQU 0 ;VALUE FOR 'NO' IN OPTIONS.
YES EQU 1 ;VALUE FOR 'YES' IN OPTIONS.
;
; CUSTOMIZATION SECTION. EACH OPTION MUST BE CODED 'YES' OR 'NO'.
;
AMPM EQU YES ;12-HOUR CLOCK FORMAT WITH AM/PM. NO=24 HOUR.
DATE EQU YES ;DATE IS DISPLAYED.
WEEKDAY EQU YES ;DAY OF WEEK IS DISPLAYED.
ZEROSUP EQU YES ;SUPPRESS LEADING 0'S IN NUMBERS.
SETTIME EQU YES ;ALLOW TIME TO BE SET. NO=DISPLAY ONLY.
;
; COMPILE-TIME CONSTANTS.
;
BOOT EQU 0000H ;SYSTEM BOOT ADDRESS
BDOS EQU 0005H ;BDOS ENTRY POINT
COMTAIL EQU 0080H ;COMMAND TAIL FROM COMMAND LINE (COUNT+CHARS)
CONOUT EQU 2 ;CODE FOR CONSOLE OUTPUT REQUEST
RTCA EQU 20H ;CLOCK ADDRESS SELECT REG
RTCD EQU 24H ;CLOCK DATA REGISTER
RTCS EQU 22H ;CLOCK STATUS REGISTER
REGEND EQU 5+(DATE*3 OR WEEKDAY) ;ENDING REGISTER COUNT FOR TIME LOOP
CR EQU 0DH ;CARRIAGE RETURN CHAR
LF EQU 0AH ;LINE FEED CHAR
;
; WE BEGIN.
;
ORG 0100H
JMP START ;SKIP ID
DB 'TIME V2.0 RAS 6/30/84'
START:
; FIRST SAVE CP/M STACK AND SET UP OURS.
LXI H,0 ;CLEAR HL
DAD SP ;ADD SP+0 IN HL
SHLD SAVESP ;SAVE SP OF CPM
LXI SP,STACK ;POINT TO MY STACK AREA
;
; READ THE CLOCK TIME IN ONE BURST.
;
READTIME:
MVI A,0CFH ;INITIAL STATUS SETUP BYTE
OUT RTCS ;SET PIO FOR MODE 3 IN/OUTPUT
MVI A,0E0H ;LOW 5 BITS OUTPUT, TOP 3 INPUT
OUT RTCS ;SET PIO IN/OUT BITS
MVI A,03H ;DISABLE INTERRUPTS
OUT RTCS ;DO IT
MVI A,14H ;STATUS REG ADDR
OUT RTCA ;SELECT IT
IN RTCD ;RESET STATUS BIT
DOREAD:
LXI H,VALUE ;POINT TO TIME SAVE AREA
MVI B,2 ;START WITH SECONDS
BURST:
MOV A,B ;A IS REGISTER WE WANT TO READ
CPI REGEND ;GOTTEN ALL WE WANT?
JNC CHECK ;YES, DONE GETTING TIME
OUT RTCA ;SELECT THAT REGISTER OF CLOCK
IN RTCD ;READ THE CLOCK DATA
MOV M,A ;SAVE IN CORE
INX H ;NEXT MEMORY LOCATION
INR B ;NEXT REG ADDR
JMP BURST ;GO GET MORE DATA
CHECK:
; SEE IF THE CLOCK ROLLED OVER DURING THE READS.
MVI A,14H ;STATUS REG ADDR
OUT RTCA ;SELECT IT
IN RTCD ;GET STATUS
ORA A ;WAS CLOCK ROLL?
JNZ DOREAD ;YES, GO READ AGAIN
;
; FORMAT THE DATE AND TIME AND SEND TO CONSOLE.
;
GOTIT:
IF DATE ;INCLUDE ONLY IF DATE DISPLAY OPTION
LXI H,MONTH ;POINT TO MONTH VALUE
CALL HEXOUTL ;PRINT IT (MAYBE ZERO SUPPRESSED)
MVI A,'/' ;SEPARATOR
CALL CHAROUT ;SEND A CHAR
DCX H ;POINT TO DAY OF MONTH
CALL HEXOUTL ;PRINT IT (ALSO ZERO-SUP)
CALL BLANK ;PRINT A BLANK
ENDIF ;(DATE)
IF WEEKDAY ;INCLUDE IF DAY OF WEEK OPTION
DCX H ;POINT TO DAY OF WEEK
MOV L,M ;GET VALUE
DCR L ;ADJUST FOR 0-6
MVI H,0 ;CLEAR TOP
LXI D,DAYTAB ;GET ADDRESS OF TABLE
XCHG ;FLIP HL,DE
DAD D ;TABLE+3*(HL)=NAME TO USE
DAD D
DAD D
MVI B,3 ;3 CHARS TO SHOW
WKOUT: MOV A,M ;GET ONE
CALL CHAROUT ;SEND IT
INX H ;NEXT
DCR B ;COUNT IT
JNZ WKOUT ;LOOP
CALL BLANK ;PUT SPACER
ENDIF ;(WEEKDAY)
LXI H,HOURS ;POINT TO HOURS
IF AMPM ;INCLUDE ONLY IF 12 HOUR AM/PM DISPLAY OPT.
MOV A,M ;GET HOURS
MVI B,'A' ;ASSUME AM
CPI 12H ;IS AFTERNOON?
JC NOPMADJ ;NO, NO MORE ADJUSTMENTS
MVI B,'P' ;YES, IS PM
JZ NOPMADJ ;IF STILL IN HOUR 12, NO NUMBERIC ADJUST
SBI 12H ;SUBTRACT 12 HOURS
DAA ;DECIMAL ADJUST
NOPMADJ:
CPI 00H ;IS MIDNIGHT HOUR?
JNZ NOMIDAM ;NO
MVI A,12H ;YES, SET 12 AM
NOMIDAM:
MOV M,A ;RESET HOURS
MOV A,B ;GET AM/PM FLAG
STA AMPMFLAG ;SAVE IT FOR LATER
ENDIF ;(AMPM)
CALL HEXOUTL ;PRINT HOURS (MAYBE ZERO SUPPRESSED)
CALL COLON ;PUT OUT A ":" FOLLOWED BY MINUTES
CALL COLON ;':' AND SECONDS
IF AMPM ;IF AM/PM DISPLAY OPTION
CALL BLANK ;PRINT A BLANK
LDA AMPMFLAG ;GET A OR P
CALL CHAROUT ;PRINT IT
MVI A,'M' ;GET AN M
CALL CHAROUT ;PRINT IT
ENDIF ;(AMPM)
CALL CRLF ;PUT OUT CR LF
;
; SEE IF CLOCK SET REQUESTED BY ANYTHING IN THE COMMAND TAIL.
; PROMPT FOR THE DATE AND TIME TO SET THE CLOCK IF REQUESTED.
;
IF SETTIME ;INCLUDE ONLY IF TIME SET OPTION
LDA COMTAIL ;GET THE COUNT
CPI 2 ;IS AT LEAST 2 CHARS?
JC DONE ;NO, RETURN NOW
; SET THE TIME REQUESTED.
DOSET:
LXI H,VALUE ;POINT TO DATE/TIME VALUE AREA
MVI B,6 ;COUNT TO CLEAR
CLEAR: MVI M,0FFH ;SET NO VALUE
INX H ;NEXT
DCR B ;COUNT
JNZ CLEAR ;LOOP FOR ALL
LXI D,PROMPT ;POINT TO PROMPT MESSAGE
MVI C,09H ;PRINT STRING FUNCTION
CALL BDOS ;PUT TEXT ON SCREEN.
MVI A,30 ;GET BUFFER LENGTH
STA INMAX ;SET LENGTH
LXI D,INMAX ;POINT TO START OF BUFFER PARMS
MVI C,0AH ;READ BUFFER FUNCTION
CALL BDOS ;READ CONSOLE INPUT
CALL CRLF ;FEED A LINE
LXI H,INLEN ;POINT TO INPUT BUFFER LENGTH
MOV A,M ;GET RETURNED LENGTH
ORA A ;WAS IT ZERO?
JZ NOTSET ;YES, NOTHING TO SET
MOV E,A ;SAVE COUNT
MVI D,0 ;CLEAR D
INX H ;POINT TO 1ST CHAR
DAD D ;POINT BEYOND LAST CHAR
MVI M,' ' ;SET A SCAN DELIMITER
INX H ;POINT ONE MORE
MVI M,0 ;SET FINAL PARSE DELIMITER
LXI H,INBUF ;POINT TO FIRST CHAR
ENDIF ;(SETTIME ALONE)
IF SETTIME AND DATE ;INCLUDE IF DATE OPTION IS ON
SHLD WKPTR ;SAVE SCAN POINTER
CALL NUMBER ;DECODE A NUMBER AND DELIMITER
JC NODATE ;INVALID NUM, NO DATE THERE
CPI '/' ;WAS DELIM A SLASH?
JZ OKDATE ;YES, WE HAVE A DATE
LHLD WKPTR ;NO, RESTORE SCAN TO START
JMP NODATE ;CONTINUE NEXT ITEM
OKDATE: MOV A,C ;GET DECODED VALUE
STA MONTH ;SET MONTH
INX H ;NEXT CHAR
CALL NUMBER ;TRY FOR DAY
JC ERROR
CPI ' ' ;WAS DELIM A BLANK?
JNZ ERROR ;NO, BAD
MOV A,C ;GET NUM
STA DAY ;SET DAY OF MONTH
INX H ;NEXT CHAR
ENDIF ;(SETTIME AND DATE)
NODATE:
IF SETTIME AND WEEKDAY ;INCLUDE WITH DAY OF WEEK OPTION
LXI D,DAYTAB ;POINT TO TABLE
MVI C,1 ;INIT VALUE
SHLD WKPTR ;SAVE START POINTER
LOOKUP: LHLD WKPTR ;GET START ADDR
MVI B,3 ;LENGTH
LOOKUPLOOP:
LDAX D ;GET TABLE CHAR
CPI 0 ;END OF TABLE?
JZ NOWEEKDAY ;YES, NOT FOUND, NOT ERROR YET
XRA M ;BASIC COMPARE
ANI 0DFH ;ELIMINATE CASE DIFFERENCE
JZ LOOKMATCH ;MATCHES IF ZERO
ADJUST: INX D ;NO MATCH, BUMP TABLE PTR
DCR B ;FOR REST OF LENGTH
JNZ ADJUST ;LOOP
INR C ;BUMP VALUE
JMP LOOKUP ;TRY NEXT
LOOKMATCH:
INX D ;NEXT IN TABLE
INX H ;NEXT IN INPUT
DCR B ;COUNT
JNZ LOOKUPLOOP ;TRY NEXT CHAR
; GOOD WEEK DAY NAME FOUND.
MOV A,C ;GET DECODED VALUE
STA DAYOFWEEK ;SAVE
MOV A,M ;GET NEXT CHAR
CPI ' ' ;IS GOOD DELIM?
JNZ ERROR ;NO, CRUSH IT
INX H ;POINT TO NEXT CHAR
ENDIF ;(WEEKDAY)
NOWEEKDAY:
IF SETTIME ;INCLUDE IF SET TIME OPTION
CALL NUMBER ;GET NEXT NUMBER
JC NOTIME ;NO TIME THERE
CPI ':' ;WAS DELIM ':'?
JNZ ERROR ;NO, BAD INPUT
MOV A,C ;GET VALUE
STA HOURS ;SAVE
INX H ;NEXT
CALL NUMBER ;TRY FOR MINUTES
JC ERROR
MOV A,C
STA MINUTES ;SAVE MINUTES
MOV A,M ;GET DELIM AGAIN
CPI ':' ;CHECK DELIM
JNZ NOSECS ;ALLOW SECONDS TO BE OMITTED
INX H ;NEXT
CALL NUMBER ;TRY FOR SECONDS
JC ERROR ;BAD
MOV A,C
STA SECONDS ;SAVE SECONDS
NOSECS: MOV A,M ;GET DELIM AGAIN
CPI ' ' ;VALID FINAL DELIM?
JNZ ERROR ;NO, GARBAGE
INX H ;NEXT CHAR
ENDIF ;(SETTIME)
IF SETTIME AND AMPM ;INCLUDE DECODING FOR 12-HOUR FORMAT OPTION
MOV A,M ;GET NEXT CHAR
ANI 0DFH ;MAKE UPPER CASE
CPI 'A' ;IS A?
JZ AOK ;YES
CPI 'P' ;IS P?
JNZ ERROR ;NO, ERROR
LDA HOURS ;FOR PM, GET HOURS BACK
CPI 12H ;WAS IT NOON HOUR?
JZ APOK ;YES, NO ADJUST
ADI 12H ;+12 HOURS FOR PM
DAA ;DECIMAL ADJUST
STA HOURS ;UPDATE VALUE
JMP APOK ;CONTINUE
AOK: LDA HOURS ;GET HOURS
CPI 12H ;WAS MIDNIGHT HOUR?
JNZ APOK ;NO, NO ADJUST
MVI A,00H ;YES, SET TO 00 HOURS
STA HOURS ;UPDATE HOURS
APOK: INX H ;POINT TO 'M'
MOV A,M ;GET IT
ANI 0DFH ;UPPER CASE
CPI 'M' ;IS IT REALLY?
JNZ ERROR ;AW, SHUCKS
INX H ;LAST CHECK
MOV A,M ;GET DELIM
CPI ' ' ;VALID?
JNZ ERROR ;NO
INX H ;NEXT
ENDIF ;(SETTIME AND AMPM)
NOTIME:
IF SETTIME ;NOW FOR ALL SETS
MOV A,M ;LOOK AT LAST DELIM
ORA A ;WAS END OF STRING?
JNZ ERROR ;FOOEY
; NOW LOAD THE TIME VALUES INTO THE CLOCK DEVICE.
LDA SECONDS ;FIRST CHECK FOR SECONDS GIVEN
CPI 0FFH ;WAS OMITTED?
JZ NOGO ;YES, NO GO COMMAND
MVI A,15H ;GET ADDRESS FOR 'GO' COMMAND
OUT RTCA ;RESET SECONDS AND BELOW
MVI A,00H ;JUST CLEAR REG
OUT RTCD ;CAUSE LOW REGS TO CLEAR
NOGO: MVI B,2 ;START AT SECONDS REGISTER
LXI H,SECONDS ;POINT TO SAVED SECONDS VALUE
OUTSET:
MOV A,B ;GET REG ADDR
CPI REGEND ;DONE ALL WE WANT?
JNC SETOK ;YES, SET IS DONE
OUT RTCA ;SELECT THAT REG
MOV A,M ;GET SAVED VALUE
CPI 0FFH ;IS IT 'NO CHANGE'?
JZ SKIPIT ;YES, SKIP THIS ONE
OUT RTCD ;SET THE REGISTER
SKIPIT: INX H ;POINT TO NEXT VALUE
INR B ;NEXT REG ADDR
JMP OUTSET ;LOOP FOR ALL REGS
SETOK:
LXI D,OKMSG ;POINT TO TIME SET OK
ECHOEXIT:
MVI C,09H ;PRINT STRING FUNCTION
CALL BDOS ;PUT TEXT ON SCREEN.
MVI A,0 ;GET A ZERO
STA COMTAIL ;CLEAR COMMAND TAIL LENGTH
JMP READTIME ;DISPLAY NEW TIME AND EXIT
ERROR:
PUSH H ;SAVE ADDR OF ERROR
MVI B,30 ;SET UP TO CLEAR 30 CHARS
LXI H,INBUF ;POINT TO BUFFER
FLAGLOOP:
MVI M,' ' ;CLEAR A CHAR
INX H ;NEXT
DCR B ;COUNT
JNZ FLAGLOOP ;LOOP
POP H ;GET ERROR ADDR BACK
MVI M,'^' ;PUT A POINTER TO ERROR
INX H ;NEXT
MVI M,'$' ;PUT ENDING STRING
LXI D,INBUF ;POINT TO ERROR FLAG LINE
MVI C,09H ;PRINT STRING FUNCTION
CALL BDOS ;DISPLAY FLAG LINE
LXI D,ERRMSG ;POINT TO ERROR MESSAGE
MVI C,09H ;PRINT STRING
CALL BDOS ;DISPLAY ERROR MSG
JMP READTIME ;RE-TRY INPUT
; NOTHING ENTERED IN RESPONSE TO PROMPT. SAY NOT SETTING AND DONE.
NOTSET:
LXI D,NOSETMSG ;POINT TO MSG
JMP ECHOEXIT ;EXIT WITH TIME DISPLAY
;
; ROUTINE TO DECODE A NUMBER UP TO 2 DIGITS AND A DELIMITER.
; INPUT IS HL POINTING TO NEXT IN BUFFER. 0 BYTE IS END OF STRING.
; NUMBER VALUE (IN BCD) IS RETURNED IN C. DELIMITER IS IN A.
; ANY ERROR DETECTED IN NUMBER CAUSES CARRY FLAG SET AND HL RESTORED.
;
NUMBER:
MVI B,2 ;INIT MAX DIGIT COUNTER
MVI C,0 ;CLEAR ANSWER WORK AREA
SHLD WKPTR ;SAVE STARTING SCAN
GETDIG:
MOV A,M ;GET CHAR FROM BUFFER
CPI '0' ;IS >= '0'?
JC NONDIGIT ;NO, NOT A DIGIT
CPI '9'+1 ;IS <= '9'?
JNC NONDIGIT ;NO, NOT A DIGIT
ANI 0FH ;ISOLATE DIGIT VALUE
MOV D,A ;SAVE IT
MOV A,C ;GET PREVIOUS VALUE
RLC ! RLC ! RLC ! RLC ;SHIFT TO HIGH NYBBLE
ORA D ;ADD IN NEW DIGIT
MOV C,A ;SAVE NEW VALUE
INX H ;POINT TO NEXT CHAR
DCR B ;COUNT DIGIT
JNZ GETDIG ;TRY FOR ANOTHER IF LENGTH LEFT
NONDIGIT:
MOV A,B ;LOOK AT COUNT
CPI 2 ;WERE THERE ANY DIGITS?
JZ BADNUM ;NO, ERROR
MOV A,M ;GET DELIMITER BACK
ORA A ;TURN OFF CARRY
RET ;RETURN GOOD
BADNUM:
LHLD WKPTR ;RESTORE SCAN PTR
STC ;SET WE HAD AN ERROR
RET ;RETURN ERROR
; TIME SET MESSAGES.
PROMPT:
DB 'Enter values in above format. Omitted values are unchanged.'
DB CR,LF,'$'
OKMSG DB 'Set OK.',CR,LF,'$'
ERRMSG DB CR,LF,'Invalid format.',CR,LF,'$'
NOSETMSG:
DB 'Nothing set.',CR,LF,'$'
;
ENDIF ;(SETTIME)
;
; DONE. RESTORE PREVIOUS STACK AND RETURN TO CP/M.
;
DONE:
LHLD SAVESP ;GET SAVED SP
SPHL ;SP=HL, RESTORE PREV SP
RET ;RETURN TO CP/M
;
; SUBROUTINE TO PUT A CHARACTER TO CONSOLE. CHAR IN A. SAVES HL.
;
CHAROUT:
PUSH H ;SAVE NEEDED REG
PUSH B
MVI C,CONOUT ;SET BDOS REQUEST CODE
MOV E,A ;PUT CHAR IN E
CALL BDOS ;SYSTEM CALL
POP B
POP H ;RESTORE
RET ;DONE
; ROUTINE ENTRY TO PUT A BLANK ON THE SCREEN.
BLANK:
MVI A,' ' ;GET A SPACE
JMP CHAROUT ;CONTINUE IN CHAROUT SUBROUTINE
; ROUTINE ENTRY TO PUT CR AND LF ON THE SCREEN.
CRLF:
MVI A,0DH ;CARRIAGE RETURN
CALL CHAROUT
MVI A,0AH ;LINE FEED
JMP CHAROUT
;
; SUBROUTINE TO OUTPUT BYTE AS TWO HEX ASCII DIGITS ON CONSOLE.
; INPUT AT (HL).
;
HEXOUTL: ;LEADING DIGIT OUTPUT ROUTINE
IF ZEROSUP ;IF LEADING ZERO SUPPRESSION OPTION
MOV A,M ;GET BYTE
ANI 0F0H ;ISOLATE HIGH NYBBLE
JZ HEXLOW ;IF ZERO, DONT PRINT IT
ENDIF ;(ZEROSUP)
HEXOUT:
MOV A,M ;GET BYTE
RRC ;MOVE TO LOW NYBBLE
RRC
RRC
RRC
ANI 0FH ;ISOLATE LOW NYBBLE
ORI 30H ;MAKE ASCII DIGIT
CALL CHAROUT ;PRINT IT
HEXLOW: MOV A,M ;GET BYTE AGAIN
ANI 0FH ;ISOLATE LOW NYBBLE
ORI 30H ;ASCII AGAIN
CALL CHAROUT ;PRINT 2ND
RET ;DONE
; ROUTINE ENTRY TO PUT OUT A COLON FOLLOWED BY NEXT VALUE FROM TIME.
COLON: MVI A,':' ;GET A COLON
CALL CHAROUT ;PUT IT OUT IN CHAROUT
DCX H ;POINT TO NEXT VALUE TO GO
JMP HEXOUT ;PRINT IT AND RETURN
;
; CONSTANTS.
;
IF WEEKDAY ;FOR DAY OF WEEK OPTION
DAYTAB:
DB 'SUNMONTUEWEDTHUFRISAT',0 ;DAY OF WEEK TABLE
ENDIF
;
; WORK AREA.
;
WKPTR DS 2 ;INPUT SCAN POINTER SAVE AREA
AMPMFLAG DS 1 ;'A' OR 'M' FOR AM/PM OPTION
SAVESP DS 2 ;SAVE AREA FOR CP/M STACK POINTER
VALUE EQU $ ;SAVED TIME VALUE WORK AREA
SECONDS DS 1 ;SECONDS
MINUTES DS 1 ;MINUTES
HOURS DS 1 ;HOURS
DAYOFWEEK DS 1 ;DAY OF WEEK
DAY DS 1 ;DAY
MONTH DS 1 ;MONTH IS LAST THING
DS 32 ;16-POSITION STACK
STACK: EQU $ ;STACK POINTER STARTS HERE
INMAX DS 1 ;MAXIMUM INPUT SIZE PUT HERE
INLEN DS 1 ;RETURNED INPUT CHAR COUNT
INBUF DS 32 ;INPUT BUFFER + ENDING DELIMS
;
END