📄 calc.asm
字号:
;4 FUNCTION CALCULATOR PROGRAM
;Assumes 1.2MHz Clock for scan timing.
; TODO : Custom Character for the 'M' sign
; Check instances of multiple decimal point presses ( all covered ?)
;Reset vector
org 0000h
jmp start
;Start of the program
org 0100h
start: mov A,#030h ;1 line, 8 bits
call wrcmd
mov A,#LCD_SETVISIBLE + 4
call wrcmd
mov A,#LCD_SETDDADDR+15 ; Start at right hand side of the display
call wrcmd
mov A,#LCD_SETMODE + 3 ; Automatic Increment - Display shift left.
call wrcmd
mov 025h,#00h ; Set output mode (floating point).
call boundsbuffer ; Initialise the bounds buffer - used for error checking.
mov mode,#4 ; Initialise the constant buffer to 100. Primarily used for % ops.
mov digitcode,#031h
call storedigit
mov digitcode,#030h
call storedigit
mov digitcode,#030h
call storedigit
mov status,#00h ; variable used to determine the first key press after an operation.
mov bufferctr,#00h
mov opcounter,#00h
mov decimalcnt,#00h
call waitkey
halt: mov PCON,#1 ;Halt
;***********************************************************
;**** Floating Point Package ****
;********************************
$INCLUDE (FP52.ASM)
;Routine to peek arg at DPTR
argout: mov R0,#FP_NUMBER_SIZE
aoloop: movx A,@DPTR
anl A,#0F0h
rr a
rr a
rr a
rr a
add A,#aodata-$-3
movc A,@A+PC
call sndchr
movx A,@DPTR
anl A,#0Fh
add A,#aodata-$-3
movc A,@A+PC
call sndchr
inc DPTR
djnz R0, aoloop
ret
aodata: db '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
;Routine to output character in A, preserving all but A.
sndchr: push R0B0
push R1B0
call getmode
mov digitcode,A
call storedigit
pop R1B0
pop R0B0
ret
;Routine to print error message at DPTR.
prterr: jmp wrstr
;Routine to handle input parameter error.
badprm: mov DPTR,#bpmsg
jmp wrstr
bpmsg: db 'Bad Parameter',0
;***********************************************************
;**** LCD Display Routines ****
;******************************
;LCD Registers addresses
LCD_CMD_WR equ 00h
LCD_DATA_WR equ 01h
LCD_BUSY_RD equ 02h
LCD_DATA_RD equ 03h
LCD_PAGE equ 80h
;LCD Commands
LCD_CLS equ 1
LCD_HOME equ 2
LCD_SETMODE equ 4
LCD_SETVISIBLE equ 8
LCD_SHIFT equ 16
LCD_SETFUNCTION equ 32
LCD_SETCGADDR equ 64
LCD_SETDDADDR equ 128
;Sub routine to write null terminated string at DPTR in program ram.
wrstr: mov P2,#LCD_PAGE
mov R0,#LCD_DATA_WR
wrstr1: clr A
movc A,@A+DPTR
jz wrstr2
movx @R0,A
call wtbusy
inc DPTR
jmp wrstr1
wrstr2: ret
;Sub routine to write null terminated string at DPTR in program ram.
wrstrslow: mov P2,#LCD_PAGE
mov R0,#LCD_DATA_WR
wrstr1s: clr A
movc A,@A+DPTR
jz wrstr2s
movx @R0,A
call wtbusy
inc DPTR
push DPL
push DPH
mov DPTR,#20
call wtms
pop DPH
pop DPL
jmp wrstr1s
wrstr2s: ret
;Sub routine to write custom character cell A
;with data at DPTR
wrcgc: mov P2,#LCD_PAGE
rl A
rl A
rl A
add A,#LCD_SETCGADDR
call wrcmd
mov R0,#LCD_DATA_WR
mov R2,#8
wrcgc1: clr A
movc A,@A+DPTR
movx @R0,A
call wtbusy
inc DPTR
djnz R2, wrcgc1
ret
;Sub routine to write command:
wrcmd: mov P2,#LCD_PAGE
mov R0,#LCD_CMD_WR
movx @R0,A
jmp wtbusy
;Sub routine to write character:
wrdata: mov P2,#LCD_PAGE
mov R0,#LCD_DATA_WR
movx @R0,A
;Subroutine to wait for busy clear
wtbusy: mov R1,#LCD_BUSY_RD
movx A,@r1
jb ACC.7,wtbusy
ret
;Wait for number of seconds in A
wtsec: push ACC
call wtms
pop ACC
dec A
jnz wtsec
ret
;Wait for number of milliseconds in DPTR
wtms: xrl DPL,#0FFh ;Can't do DEC DPTR, so do the loop by forming 2's complement
xrl DPH,#0FFh ;and incrementing instead.
inc DPTR
wtms1: mov TL0,#09Ch ;100 ticks before overflow
mov TH0,#0FFh
mov TMOD,#1 ;Timer 0 mode 1
setb TCON.4 ;Timer 0 runs
wtms2: jnb TCON.5,wtms2
clr TCON.4 ;Timer 0 stops
clr TCON.5
inc DPTR
mov A,DPL
orl A,DPH
jnz wtms1
ret
;Subroutine to Center a String on one line of the Display ( 16 character Display )
CentreString: mov R6,#0
mov R4,strlength
mov A,#16 ; R4 holds the string length.
subb A,R4 ; A holds the total *spare* character spaces.
jc ExitSub ; Exit Routine if string is longer than display width.
rrc A ; Shift right (Divide by 2)
mov R6,A ; R6 now holds the CentreOffset.
clr A
CharBuff: mov A,#CHAR_SPACE ; Write the *padding* to center the string.
call wrdata
cjne R6,#0,CharBuff
ExitSub: ret
;Subroutine to determine the length of a null terminated string.
StringLength: mov R4,#00
loop: clr A
movc A,@A+DPTR
inc dptr
inc R4
jnz loop
mov strlength ,R4
ret ; strlength includes the terminating NULL.
;Subroutine to write a null terminated string *wrapped* around an offset.
WriteString: mov R0,#LCD_DATA_WR
clr A
mov A,stroffset
mov R5,A ; R5 is the counter.
mov B,A ; B is the counter for the djnz
;mov R7,#5
mov R6,#16
loop1: movc A,@A+DPTR ; Loop1 goes from the offset to the terminator or to 20 chars -> .
jz eos_found
movx @R0,A
call wtbusy
inc R5
mov A,R5
djnz R6,loop1 ; R6 *holds* the string to 20 characters or on 1 line.
eos_found: mov R5,#0
mov R2,stroffset
cjne R2,#0,loop2 ; Check for case with no wrap.
ret
loop2: mov A,R5 ; Loop2 wraps from the first character to the offset.
movc A,@A+DPTR
movx @R0,A
call wtbusy
inc R5
djnz B,loop2
push DPL ; Pause after writing the string.
push DPH
mov DPTR,#2
call wtms
pop DPH
pop DPL
ret
;***********************************************************
;**** Keypad Routines ****
;*************************
XSEG ; External Data Memeory - Access through DPTR.
;**** BUFFERS *****
; Buffers are set up with byte 0 as the sign - the relevent number of digits - and the final bit for the terminator.
; The exception to this is the Hundredbuffer which is *hardcoded* at 100. In practice this means that a ten byte
; buffer holds an 8 digit number (sign&number&terminator = 10 ).
KEYBUFFER: ds 10 ; General I/O buffer.
OLDNUMBUFFER: ds 10 ; Holds the previous number ( used for repeat operations)
MEMORYBUFFER: ds 10 ; Holds the number in memory
HUNDREDBUFF : ds 5 ; Holds the constant number 100
BOUNDBUFFER: ds 10 ; Holds 99999999 and is signed so both upper and lower bounds can be checked.
TEMPBUFFER: ds 25 ; Holds the operation result until compared with boundsbuffer.
DSEG AT 060h ; Data memory.
;***** FLAGS *****
; Flags are used mainly because most of the operators have different functionality when consecutively pressed more than once.
; Status returns 1 after an operator press and 0 after a digit key press.
equalsflag: ds 1 ; Flag for the equals operator.
memopflag: ds 1 ; Flag for memory operations.
arithopflag: ds 1 ; Flag for arithmetic operations.
pctopflag: ds 1 ; Flag for the percentage operator.
memocc: ds 1 ; Flag whether there is a value in the memory buffer
errorflag: ds 1 ; Flag an error.
signflag: ds 1 ; Boolean for the sign of the number ( default to +ve )
status: ds 1 ; Flag the type of key pressed ( operator or digit ).
;***** VARIABLES *****
opcodehex: ds 1 ; Store the operation type.
oldopcode : ds 1 ; As above - must be able to store the last operation as well as the current
; one for cancel command and consecutive operator presses.
opcodeflag: ds 1
bufferctr: ds 1 ; A counter ( incremented along the buffer on storing a digit ).
opcounter: ds 1 ; Count the number of operations since a ( total ) Cancel.
digitcode: ds 1 ; Holds the ascii value of the key pressed.
mode: ds 1 ; Determines at which buffer the DTPR addresses.
memcounter: ds 1 ; Stores the length of the number currently in the memorybuffer
copyfrom: ds 1 ; Used to copy the contents of one buffer into another buffer
copyto: ds 1 ; As above.
local: ds 1 ; Local variable
decimalcnt: ds 1 ; Counter for decimal points - don't allow more than 1 to be inputed per number.
stroffset: ds 1 ; Holds the offset of a string ( for centering purposes ).
strlength: ds 1 ; Holds the length of the string.
CSEG ; Return to code segment.
CHAR_SPACE equ 0FEh
errorstr: db 'Error!'
db 0
string1: db "YOU'RE JOKING!"
db 0
CGC1: db 010001b ; Memory only.
db 011011b
db 010101b
db 000000b
db 000000b
db 000000b
db 000000b
db 000000b
CGC2: db 010001b ; Memory and error.
db 011011b
db 010101b
db 011111b
db 010000b
db 011110b
db 010000b
db 011111b
CGC3: db 000000b ; Error only.
db 000000b
db 000000b
db 011111b
db 010000b
db 011111b
db 010000b
db 011111b
;Keycodes returned for function keys:
ON equ 1
SGN equ 2
PCT equ 3
SQR equ 4
MRC equ 5
MADD equ 6
MSUB equ 7
KEY_ROW1 equ 0EFh
KEY_ROW2 equ 0DFh
KEY_ROW3 equ 0BFh
KEY_ROW4 equ 07Fh
keyflags equ 040h
;Data tables for returned row bits
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -