home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
calculat
/
calc32.lbr
/
CALC.ZZ0
/
CALC.Z80
Wrap
Text File
|
1988-03-24
|
14KB
|
921 lines
;32 bit calculator 88-02-02
;
;******* NOTES ON LABELS *******
;
;HATOR and LATOR are used for
;the high 16 and low 16 bytes
;of the operator. Similarly
;for HAND and LAND. The result
;is placed by most functions in
;MOST and LEAST. For
;exponentiation, these values
;are moved back to the operator
;for subsequent passes. In
;the addition and subtraction
;routines, certain parts are
;shared by other routines ...
;this is why they jump around.
;
ORG 100H
LD (OLDSP),SP
LD SP,STACK
LD DE,HELLO
CALL PRTSTR
;
;GET OPERATOR, OPERATION, AND OPERAND
;
START
LD SP,STACK ;Clean up the garbage caused by error traps
LD HL,0
LD (MOST),HL
LD (LEAST),HL ;Clear result
CALL GETINPUT
LD BC,BUFFER+2
CALL DECODE
LD (LATOR),DE
LD (HATOR),HL
LD A,(FUNCTION)
OR A
JR Z,CONVERT
;
;factorial is rather useless
;it overflows at 13
;
CP '!'
JP Z,FACTORIAL
PUSH AF ;Save it
INC BC ;Skip the null
CALL DECODE ;Get second number
LD (LAND),DE
LD (HAND),HL ;Save number
POP AF
CP '&'
JR Z,IAND
CP '%'
JR Z,IOR
CP '#'
JR Z,IXOR
CP '('
JR Z,RLEFT
CP ')'
JR Z,RRIGHT
CP '<'
JR Z,SLEFT
CP '>'
JP Z,SRIGHT
CP '+'
JP Z,ADD
CP '-'
JP Z,SUBTRACT
CP '*'
JP Z,MULTIPLY
CP '/'
JP Z,DIVIDE
CP '^'
JP Z,EXPONENT
;
;BAD OPERATION
;
BADOP
LD DE,BADOPER
JP ER1
;
;*****HEX<->DEC CONVERSION*****
;
CONVERT
LD HL,(HATOR)
LD (MOST),HL
LD HL,(LATOR)
LD (LEAST),HL
JP DISPLAY
;
;*****AND FUNCTION*****
;
IAND
LD HL,LATOR
LD A,(LAND)
AND (HL)
IAND1
LD (LEAST),A
JP DISPLAY
;
;*****OR FUNCTION
;
IOR
LD HL,LATOR
LD A,(LAND)
OR (HL)
JR IAND1
;
;*****XOR FUNCTION*****
;
IXOR
LD HL,LATOR
LD A,(LAND)
XOR (HL)
JR IAND1
;
;*****ROTATE LEFT FUNCTION*****
;
RLEFT
LD A,(LAND)
LD B,A
LD A,(LATOR)
RLEFT1
RLCA
DJNZ RLEFT1
JR IAND1
;
;*****ROTATE RIGHT FUNCTION*****
;
RRIGHT
LD A,(LAND)
LD B,A
LD A,(LATOR)
RR1
RRCA
DJNZ RR1
JR IAND1
;
;*****SHIFT LEFT FUNCTION*****
;
SLEFT
LD DE,(LATOR)
LD HL,(HATOR)
LD A,(LAND)
LD B,A
SLEFT1
SLA E
RL D
RL L
RL H
DJNZ SLEFT1
SLEFT2
LD (LEAST),DE
LD (MOST),HL
JP DISPLAY
;
;*****SHIFT RIGHT FUNCTION*****
;
SRIGHT
LD DE,(LATOR)
LD HL,(HATOR)
LD A,(LAND)
LD B,A
SR1
OR A
RR H
RR L
RR D
RR E
DJNZ SR1
JR SLEFT2
;
;*****ADD FUNCTION*****
;
ADD:
CALL ADD1
LD (LEAST),HL
LD HL,(HATOR)
LD DE,(HAND)
ADC HL,DE
JP C,OVER1 ;Too big
LD (MOST),HL
JP DISPLAY
ADD1
LD HL,(LATOR)
LD DE,(LAND)
ADD HL,DE
RET
;
;*****SUBTRACT FUNCTION*****
;
SUBTRACT
CALL SUB1
LD (LEAST),HL
CALL SUB2
JP C,OVER1
LD (MOST),HL
JP DISPLAY
SUB1
LD HL,(LATOR)
LD DE,(LAND)
XOR A
SBC HL,DE
RET
SUB2
LD HL,(HATOR)
LD DE,(HAND)
SBC HL,DE
RET
;
;*****MULTIPLICATION FUNCTION*****
;
MULTIPLY
CALL MULT0
JP DISPLAY
;
;SHARED WITH EXPONENT
;
MULTX
PUSH BC
CALL MULT0 ;Do one power
POP BC
DEC BC
LD A,B
OR C
RET NZ
POP BC ;Clean up stack
JP DISPLAY
MULT0
XOR A
LD HL,0
LD (LEAST),HL
LD (MOST),HL
LD (CARRY),A ;No carry in operator yet
LD B,32 ;Number of bits to test
;
;SHIFT OPERAND
;
MULT1
LD HL,(HAND)
LD DE,(LAND)
SRL H ;Move a zero in
RR L
RR D
RR E
LD (HAND),HL
LD (LAND),DE ;Save it
JR NC,MULT2 ;Nothing to multiply
;
;CHECK OVERFLOW CONDITION
;
LD A,(CARRY)
OR A
JP NZ,OVERFLOW
LD HL,(LEAST)
LD DE,(LATOR)
ADD HL,DE
LD (LEAST),HL
LD HL,(MOST)
LD DE,(HATOR)
ADC HL,DE
JP C,OVERFLOW
LD (MOST),HL
;
;SHIFT OPERATOR AND SET OVERFLOW
;
MULT2
LD HL,(HATOR)
LD DE,(LATOR)
XOR A ;Clear carry
RL E
RL D
RL L
RL H
LD (LATOR),DE
LD (HATOR),HL
JR NC,MULT3
;
;WE HAVE A CARRY
;SET TO ABORT IF ANOTHER ADDITION IS TRIED
;
INC A ;Becomes non-zero
LD (CARRY),A ;Remember
MULT3
DJNZ MULT1 ;Do 32 times
RET
;
;*****DIVISION FUNCTION*****
;
DIVIDE
LD HL,(HAND)
LD A,H
OR L
LD HL,(LAND)
OR H
OR L
LD DE,DIVZERO
JP Z,ER1
;
;NO DIVIDE BY ZERO
;LET'S GO FOR IT
;
LD B,32
LD HL,(HATOR)
LD (EXPH),HL
LD HL,(LATOR)
LD (EXPL),HL
LD HL,0
LD (HATOR),HL
LD (LATOR),HL
LD (LEAST),HL
LD (MOST),HL
;
;DOUBLE OPERATOR
;
DIV1
LD HL,(EXPL)
RL L
RL H
LD (EXPL),HL
LD HL,(EXPH)
RL L
RL H
LD (EXPH),HL
LD HL,(LATOR)
RL L
RL H
LD (LATOR),HL
LD HL,(HATOR)
RL L
RL H
LD (HATOR),HL
;
;TRY A SUBTRACT
;
CALL SUB1
PUSH HL
CALL SUB2
POP DE
JR C,DIV2
;
;GOOD DIVISION SAVE RESULT
;
LD (LATOR),DE
LD (HATOR),HL
XOR A
DIV2
PUSH AF
LD HL,(LEAST)
ADD HL,HL
LD (LEAST),HL
LD HL,(MOST)
ADC HL,HL
LD (MOST),HL
LD HL,(LEAST)
POP AF
JR C,DIV3
INC HL
LD (LEAST),HL
DIV3
DJNZ DIV1
;
;
;DISPLAY REMAINDER IN DECIMAL ONLY
;
LD DE,REMAIN
CALL PRTSTR
LD HL,(MOST)
PUSH HL
LD HL,(LEAST)
PUSH HL
LD HL,(HATOR)
LD (MOST),HL
LD HL,(LATOR)
LD (LEAST),HL
CALL DPHEX
LD DE,UP ;Reverse line feed
CALL PRTSTR
POP HL
LD (LEAST),HL
POP HL
LD (MOST),HL
JP DISPLAY
;
;*****EXPONENTIATION FUNCTION*****
;
EXPONENT
LD HL,(HATOR)
LD (EXPH),HL ;Save multiplier
LD HL,(LATOR)
LD (EXPL),HL
LD HL,(HAND)
LD A,H
OR L
JP NZ,OVER1 ;Are you crazy?
LD HL,1
LD (LEAST),HL
LD BC,(LAND)
LD A,B
OR C
JP Z,DISPLAY ;The answer is 1
LD HL,1
LD (LAND),HL ;First cycle with a 1
DEC HL
LD (HAND),HL
;
;HERE WE GO
;
EXP1
CALL MULTX
;
;MOVE ORIGINAL MULTIPLIER BACK
;
LD HL,(EXPH)
LD (HAND),HL
LD HL,(EXPL)
LD (LAND),HL ;Move original back
;
;MOVE RESULT BACK INTO OPERAND
;
LD HL,(MOST)
LD (HATOR),HL
LD HL,(LEAST)
LD (LATOR),HL
JR EXP1 ;More more more
;
;*****FACTORIAL FUNCTION*****
;
FACTORIAL
LD HL,1
LD (LEAST),HL
LD BC,(LATOR)
LD A,C
OR A
JP Z,DISPLAY ;0!=1
LD (LEAST),BC
DEC A
JP Z,DISPLAY ;1!=1
DEC BC ;Do X*(X-1) on first pass
FACT1
LD (LAND),BC
CALL MULTX
LD HL,0
LD (HAND),HL
LD HL,(MOST)
LD (HATOR),HL
LD HL,(LEAST)
LD (LATOR),HL
JR FACT1
;
;*****SUBROUTINES*****
;
;
;EXIT TO CCP
;
EXIT:
LD SP,(OLDSP)
LD E,10
JP PRT
;
;GET A NUMBER
;
GETINPUT
LD DE,BUFFER
LD C,10
CALL 5
LD HL,BUFFER+1
LD A,(HL) ;Used length
OR A
JR Z,EXIT
LD B,A
XOR A
LD (FUNCTION),A
PARSE
INC HL
LD A,(HL)
CP '''' ;ASCII prefix
JR NZ,PARSE1
INC HL ;Pass byte as is
DEC B ;Skip the '
RET Z
JR PARSE6 ;Skip the character
PARSE1
CP 'a'
JR C,PARSE2
CP 'z'+1
JR NC,PARSE2
AND 5FH ;Upper case
PARSE2
CP 'H'
JR Z,PARSE5 ;Intro for HEX value
CP 'I'
JR Z,PARSE5 ;Intro for binary (INDIVIDUAL)
CP 'M'
JR Z,PARSE5 ;Memory flag
CP ','
JR Z,PARSE5 ;Ignore separator
CP '^'
JR Z,PARSE3 ;The following are functions
CP '>'
JR Z,PARSE3
CP '<'
JR Z,PARSE3
SUB '0'
JR NC,PARSE4
ADD A,'0' ;Oops it was a function, maybe
PARSE3
LD (FUNCTION),A
LD A,255 ;End marker
JR PARSE5
PARSE4
CP 10
JR C,PARSE5
SUB 7 ;Offset for A-F
PARSE5
LD (HL),A
PARSE6
DJNZ PARSE
INC HL
LD (HL),255 ;End marker
RET
;
;DECODE HEX, DECIMAL, OR ASCII VALUE
;
DECODE
LD HL,0
LD DE,0 ;Start with nothing
LD A,(BC)
CP 'H'
JR Z,GETHEX
CP 'I' ;Binary prefix
JR Z,GETBINARY
CP 'M'
JR Z,GETMEMORY
CP '''' ;ASCII prefix
JR NZ,GETDECIMAL ;Default
INC BC
LD A,(BC)
LD E,A ;Pass ASCII byte to E
INC BC
RET
;
;GET LAST RESULT
;
GETMEMORY
LD DE,(LMEM)
LD HL,(HMEM)
INC BC
RET
;
;GET A HEX NUMBER
;
GETHEX
INC BC ;Skip the 'H' first time around
LD A,(BC)
CP 255
RET Z
CP ','
JR Z,GETHEX ;Ignore comma separators
CP 16
JR NC,ERROR
PUSH BC ;Save position
;
;SLIDE BITS ONE AT A TIME
;TO MULTIPLY 'HLDE' BY 0FH AND ADD NUMBER IN 'A'
;
RLCA
RLCA
RLCA
RLCA ;Move number to high nibble of A
LD B,4 ;4 rotates will do the trick
ROTATE
RL A
RL E
RL D
RL L
RL H
JR C,OVERFLOW ;If top bit of 'H' non-zero
DJNZ ROTATE
POP BC ;Get pointer back
JR GETHEX
;
;GET A BINARY NUMBER
;
GETBINARY
INC BC ;Skip the 'I' first time around
LD A,(BC)
CP 255
RET Z
CP ','
JR Z,GETBINARY ;Ignore comma separators
CP 2
JR NC,ERROR
RRA ;Get bit into carry
RL E ;Bring it into E
RL D
JR GETBINARY
;
;BAD DIGIT ENCOUNTERED
;
ERROR
LD DE,ERMES
JR ER1
;
;OVERFLOW CONDITIONS
;
OVERFLOW
POP BC ;Clean up stack
OVER1
LD DE,OVER
ER1
CALL PRTSTR
JP START
;
;GET A DECIMAL NUMBER
;
GETDECIMAL
DEC BC ;Don't ignore first digit
GETD2
INC BC
LD A,(BC)
CP 255 ;End of buffer?
RET Z
CP ','
JR Z,GETD2 ;Ignore commas
CP 10
JR NC,ERROR ;No hex digits allowed here
PUSH BC ;Save pointer
;
;DE AND HL *10
;
EX DE,HL
ADD HL,HL ;DE*2
PUSH HL
POP BC ;Save DE*2 in BC
EX DE,HL
ADC HL,HL ;HL*2 + overflow
JR C,OVERFLOW ;High bytes not allowed to overflow
PUSH HL ;Save *2
EX DE,HL
ADD HL,HL ;DE*4
EX DE,HL
ADC HL,HL ;HL*4
JR C,OVERFLOW
EX DE,HL
ADD HL,HL ;DE*8
EX DE,HL
ADC HL,HL ;HL*8
JR C,OVERFLOW
EX DE,HL
ADD HL,BC ;DE*10
POP BC ;HL*2
EX DE,HL
ADC HL,BC ;HL*10
JR C,OVERFLOW
LD B,0
LD C,A
EX DE,HL
ADD HL,BC ;Add incoming digit
EX DE,HL
LD C,0
ADC HL,BC ;Add carry to HL
JR C,OVERFLOW
POP BC ;Get pointer back
JR GETD2 ;Whew! get another one
;
;*****OUTPUT ROUTINES
;
;
;DISPLAY LEAST SIGNIFICANT BYTE IN BINARY
;
DISPLAY
LD HL,(MOST)
LD (HMEM),HL ;Save for memory operation
LD HL,(LEAST)
LD (LMEM),HL
LD DE,CRLF
CALL PRTSTR
LD A,(LEAST)
LD B,8
BITS
RLCA
LD E,'0'
JR NC,BIT1
LD E,'1'
BIT1
PUSH AF
PUSH BC
CALL PRT
POP BC
POP AF
DJNZ BITS
;
;DISPLAY IN ASCII TO HELP FOR LOGICALS
;
LD E,' '
CALL PRT
LD A,(LEAST)
AND 7FH ;Strip high bit
CP ' ' ;Control chr?
JR NC,ASC1
PUSH AF ;Save for now
LD E,'^'
CALL PRT
POP AF
ADD A,64 ;ASCII offset
ASC1
LD E,A
CALL PRT
;
;DISPLAY RESULT IN HEX
;
DHEX
LD DE,CRLF
CALL PRTSTR
XOR A
LD (LEAD),A ;Ignore leading zeros
LD HL,(MOST)
CALL DUMPH ;Hexdump high bytes
CALL COMMA
LD HL,(LEAST)
CALL DUMPH ;Hexdump low bytes
LD DE,EQUAL
CALL PRTSTR
;
;DISPLAY IN DECIMAL
;
CALL DPHEX
JP START
;
;DECIMAL CONVERSION ROUTINE
;
DPHEX
XOR A
LD (LEAD),A ;Set leading zero flag
LD BC,3B9AH
LD DE,0CA00H ;1,000,000,000
CALL SDIV
CALL COMMA
LD BC,5F5H
LD DE,0E100H ;100,000,000
CALL SDIV
LD BC,98H
LD DE,9680H ;10,000,000
CALL SDIV
LD BC,0FH
LD DE,4240H ;1,000,000
CALL SDIV
CALL COMMA
LD BC,1
LD DE,86A0H ;100,000
CALL SDIV
LD BC,0 ;Will be zero from here on in
PUSH BC
LD DE,10000
CALL SDIV ;It will do the trick
POP BC
PUSH BC
LD DE,1000
CALL SDIV
CALL COMMA
POP BC
PUSH BC
LD DE,100
CALL SDIV
POP BC
LD DE,10
CALL SDIV
LD A,(LEAST)
ADD A,'0'
LD E,A
CALL PRT
LD DE,CRLFF
CALL PRTSTR
RET
;
;FIND ONE DIGIT
;SUBTRACT BCDE FROM MOSTLEAST
;KEEP COUNT IN A
;
SDIV
LD A,'0' ;ASCII offset for count
SDIV2
OR A ;And clear carry
LD HL,(MOST)
SBC HL,BC
JR C,PRINT ;No more of this digit
LD (MOST),HL
LD HL,(LEAST)
SBC HL,DE ;Subtract rest of number
LD (LEAST),HL
JR NC,SDIV3 ;No borrow needed
LD HL,(MOST)
EX AF,AF' ;Save counter
LD A,H
OR L
JR Z,SDIV4 ;Sorry can't borrow
EX AF,AF' ;Get counter back
DEC HL ;Get a borrow
LD (MOST),HL
SDIV3
INC A ;Clear carry and increment counter
JR SDIV2 ;Do another round
;
;WE HAVE OVERFLOW -- UNDO IT
;
SDIV4
EX AF,AF' ;Get counter back first
ADD HL,BC
LD (MOST),HL
LD HL,(LEAST)
ADD HL,DE
LD (LEAST),HL
PRINT
LD E,A
CP '0'
JR Z,CHECK
LD (LEAD),A ;Yes we've printed a non-zero
CHECK
LD A,(LEAD) ;Have we printed a number?
OR A
RET Z ;Not yet
PRT
LD C,2
CALL 5
RET
;
;PRINT A COMMA IF NON-ZERO DIGIT PRINTED
;
COMMA
LD E,','
JR CHECK ;Print the comma, maybe
;
;DUMP HL IN HEX
;
DUMPH
LD A,H ;Get high byte
PUSH HL
CALL DUMPA
POP HL
LD A,L ;Get low byte
DUMPA
PUSH AF
RRCA ;Get high nibble
RRCA
RRCA
RRCA
CALL DUMP
POP AF ;Get low nibble
DUMP
AND 0FH
ADD A,'0' ;ASCII offest
CP '9'+1
JR C,PRINT ;Not a HEX digit
ADD A,7 ;Offset for A-F
JR PRINT ;And return
;
;PRINT STRING IN (DE)
;
PRTSTR
LD C,9
CALL 5
RET
;
;DATA NEEDED BY ROUTINE
;
LEAST DS 2
MOST DS 2
LEAD DS 1
;
;OTHER DATA
;
;
HELLO DB 13,10,10,'32-BIT CALCULATOR'
DB 13,10,9,' Guy Cousineau',10
DB 13,10,9,'< SHL > SHR ( RL ) RR'
DB 13,10,'& AND % OR # XOR'
DB 13,10,9,'+ - * / ^ !',10
DB 13,10,'SYNTAX num[OPnum]<CR>',10
DB 13,10,'use ''M'' to use last result'
DB 13,10,'precede HEX numbers with H'
DB 13,10,'precede ASC values with '''
DB 13,10,'precede BIN values with I',10
DB 13,10,'Empty <CR> exits'
CRLFF DB 10
CRLF DB 13,10,'$'
ERMES DB 10,7,9,'*BAD DIGIT*',13,10,'$'
OVER DB 10,7,9,'*OVERFLOW*',13,10,'$'
BADOPER DB 10,7,9,'*BAD FUNCTION*',13,10,'$'
DIVZERO DB 10,7,9,'*DIVIDE BY ZERO*',13,10,'$'
EQUAL DB ' <-> $'
REMAIN DB 10,9,' REMAINDER $'
UP DB 6,6,6,'$'
LATOR DS 2
HATOR DS 2 ;OPERATOR
LAND DS 2
HAND DS 2 ;OPEARND
CARRY DS 1
QUOTIENT DS 2
EXPON DS 2
EXPH DS 2
EXPL DS 2
LMEM DS 2
HMEM DS 2
FUNCTION DS 1
OLDSP DS 2
BUFFER DB 28
DS 30 ;INPUT BUFFER
DS 10H
STACK
;
;THIS IS ALL THE STACK WE NEED FOR THIS ROUTINE
;KEEP THAT IN MIND WHEN ADDING AS A SUBROUTINE
;
BUFFER
DS 10H
STACK
;
;THIS IS ALL THE STACK WE NEED FOR THIS ROUTINE
;KEEP THAT IN M