;============================================================================= ; ; exprcom.asm Expression calculator 'exprcom 1 + 2' ; ; Written: 95/03/13 Pieter Hintjens ; Revised: 95/05/24 ; ; Usage: Assemble and link as a .COM file: ; masm exprcom; ; link exprcom; ; exe2bin exprcom.exe exprcom.com ; del exprcom.exe ; ; Model: Code, data, and stack are in same segment: CS, DS, ES, and SS ; point to the one segment, and never change. ; ; Skeleton generated by LIBERO 2.10 on 24 May, 1995, 12:52. ;============================================================================= include exprcom.d ; Include dialog definitions DATA_SEG ; Data definitions end_mark_pty equ 1 ; Relative priority of tokens left_par_pty equ 2 ; which may occur in exression right_par_pty equ 3 ; - higher number means higher term_op_pty equ 4 ; priority, ie. executed first. factor_op_pty equ 5 lowest_op_pty equ 4 end_mark_token equ 'E' ; Indicates end of operator stack ; Each entry in operator stack contains operator and priority in 2 bytes operator_max equ 20 ; Max entries in operator stack operator_ptr dw 0 ; Current size of operator stack operator_stack dw operator_max dup (0) ; Each entry in operand stack contains signed nunber in 2 bytes operand_max equ 20 ; Max entries in operand stack operand_ptr dw 0 ; Current size of operand stack operand_stack dw operand_max dup (0) arg_ptr dw 0 result dw 0 ; Calculated result of expression the_pty db 0 ; Current priority the_token db 0 ; Current token the_number dw 0 ; Current number msg_helptext label byte db 'expr v1.0 (c) 1991-95 by Pieter A. Hintjens.\n' db 'Expression calculator, returns errorlevel result\n\n' db 'expr [/?] expression\n\n' db ' /? - show this help\n' db ' expression - numbers, +/-*, (), spaces\n' db 0 ; End of text msg_program_id db 'expr: ',0 msg_num_overflow db 'number in expression is too large\n',0 msg_operand_over db 'operand stack overflowed\n',0 msg_operator_over db 'operator stack overflowed\n',0 msg_add_over db 'addition overflow\n',0 msg_div_zero db 'divide-by-zero error\n',0 msg_bad_token db 'invalid token on stack: ',0 msg_operator_under db 'operator stack underflowed\n',0 msg_operand_under db 'operand stack underflowed\n',0 msg_no_left_par db 'too many )''s in expression\n',0 msg_no_right_par db 'too few )''s in expression\n',0 msg_inv_token db 'invalid token in expression: ',0 msg_no_token db 'unexpected end of expression - use /? for help\n',0 ;************************* INITIALISE THE PROGRAM ************************ MODULE initialise_the_program cmp arg_help,1 ; Was /? specified on command line? jne initialise_okay lea si,msg_helptext call echo ; yes - display help message mov the_next_event,terminate_event ret ; and terminate dialog initialise_okay label near ; Normal initialisation mov arg_ptr,0 ; Move to start of expression mov result,0 ; Assume result is zero mov al,end_mark_token mov ah,end_mark_pty mov operator_stack[0],ax mov the_next_event,ok_event ret ; Return to state machine ENDMODULE ;***************************** GET NEXT TOKEN ***************************** MODULE get_next_token mov bp,arg_ptr ; Get address into argument text get_next_loop: mov al,arg_text[bp] inc bp ; cmp al,' ' ; Skip leading spaces je get_next_loop ; mov the_token,al ; Store al in token mov arg_ptr,bp ; and update argument pointer cmp al,'+' ; '+' -> term operator je have_term ; cmp al,'-' ; '-' -> term operator je have_term ; cmp al,'*' ; '*' -> factor operator je have_factor ; cmp al,'/' ; '/' -> factor operator je have_factor ; cmp al,'(' ; '(' -> left parenthesis je have_left_par ; cmp al,')' ; ')' -> right parenthesis je have_right_par; cmp al,'0' ; '0'..'9' -> number jb get_next_end ; cmp al,'9' ; jbe have_number ; get_next_end: cmp al,0 ; 0 -> end of expression je have_end_mark ; ; Anything else is an error mov the_next_event,other_event ret ; Return to state machine have_term: mov the_next_event,term_op_event mov the_pty,term_op_pty ret ; Return to state machine have_factor: mov the_next_event,factor_op_event mov the_pty,factor_op_pty ret ; Return to state machine have_left_par: mov the_next_event,left_par_event mov the_pty,left_par_pty ret ; Return to state machine have_right_par: mov the_next_event,right_par_event mov the_pty,right_par_pty ret ; Return to state machine have_end_mark: mov the_next_event,end_mark_event mov the_pty,end_mark_pty ret ; Return to state machine have_number: mov the_next_event,number_event mov bx,10 ; BX = 10 for multiply xor cx,cx ; CX = next digit in low byte sub al,'0' ; AX = value of first digit xor ah,ah ; next_digit: mov cl,arg_text[bp] sub cl,'0' ; cmp cl,9 ; Is result in range 0 to 9? ja have_number_x ; no - we're done inc bp ; mul bx ; Multiply value by 10 add ax,cx ; Add-in to result jo num_overflow ; If overflow, signal error jmps next_digit ; Go do next digit until done have_number_x: mov the_number,ax ; Store number value mov arg_ptr,bp ; and update argument pointer ret ; Return to state machine num_overflow: lea si,msg_num_overflow jmps echo_exception ENDMODULE echo_exception proc near ; Echo message at SI and raise exception push si ; Start message with name of program lea si,msg_program_id call echo pop si mov the_exception_event,exception_event mov exception_raised,1 jmp echo echo_exception endp ;************************** ALLOW SIGNED NUMBER *************************** MODULE allow_signed_number mov bp,arg_ptr ; Get address into argument text mov al,arg_text[bp] cmp al,'0' ; '0'..'9' -> number jb allow_exit ; else quit cmp al,'9' ; ja allow_exit ; inc bp ; Bump past first digit call have_number ; and collect number cmp the_token,'-' ; jne allow_number ; If negative sign, xor ax,ax ; get inverse of number sub ax,the_number ; and put back in the_number mov the_number,ax ; allow_number: mov the_exception_event,number_event mov exception_raised,1 allow_exit label near ret ; Return to state machine ENDMODULE ;**************************** STACK THE NUMBER **************************** MODULE stack_the_number mov ax,the_number ; AX = current number value call push_operand ; Push onto operand stack ret ; Return to state machine ENDMODULE push_operand proc near ; Push operand in AX: pushr ; save work registers mov bx,operand_ptr; inc bx ; bump stack pointer inc bx ; cmp bx,operand_max * 2 ja operand_over mov operand_stack[bx],ax mov operand_ptr,bx push_operand_1: popr ; restore work registers ret ; Return to caller operand_over label near lea si,msg_operand_over call echo_exception jmps push_operand_1 push_operand endp ;*************************** STACK THE OPERATOR *************************** MODULE stack_the_operator mov al,the_token ; AL = operator mov ah,the_pty ; AH = priority call push_operator ; Push AX onto operator stack ret ; Return to state machine ENDMODULE push_operator proc near ; Push operator in AX: pushr ; save work registers mov bx,operator_ptr; inc bx ; bump stack pointer inc bx ; cmp bx,operator_max * 2 ja operator_over mov operator_stack[bx],ax mov operator_ptr,bx push_operator_1: popr ; restore work registers ret ; Return to caller operator_over label near lea si,msg_operator_over call echo_exception jmps push_operator_1 push_operator endp ;************************** UNSTACK GE OPERATORS ************************** MODULE unstack_ge_operators unstack_ge_next label near ; while operator [ptr] >= the_pty mov bx,operator_ptr mov ax,operator_stack[bx] cmp ah,the_pty ; jl unstack_ge_exit call unstack_operator jmps unstack_ge_next unstack_ge_exit label near ret ; Return to state machine ENDMODULE unstack_operator proc near ; Unstack and evaluate operator call pop_operator ; Get next operator from stack mov dx,ax ; DX = operator and priority call pop_operand ; BX = operand 2 mov bx,ax ; cmp dl,'+' ; jne unstack_1 call pop_operand ; AX = operand 1 add ax,bx ; + operand 2 jno unstack_okay ; and re-stack result lea si,msg_add_over call echo_exception; jmps unstack_okay ; unstack_1: cmp dl,'-' jne unstack_2 call pop_operand ; AX = operand 1 sub ax,bx ; - operand 2 jmps unstack_okay ; and re-stack result unstack_2: cmp dl,'*' jne unstack_3 xor dx,dx ; High word is zero call pop_operand ; AX = operand 1 mul bx ; * operand 2 jmps unstack_okay ; and re-stack result unstack_3: cmp dl,'/' jne unstack_4 call pop_operand ; AX = operand 1 cmp bx,0 ; If operand 2 = zero, je divide_zero ; signal divide-by-zero error div bx ; AX = operand 1 / operand 2 jmps unstack_okay ; and re-stack result divide_zero: lea si,msg_div_zero call echo_exception xor ax,ax ; Assumed result is zero jmps unstack_okay ; and re-stack result unstack_4: cmp dl,end_mark_token jne unstack_error ; At end: mov feedback,al ; result of expression = AX call echonum ; lea si,newline ; call echo ; jmps unstack_okay ; Return to caller unstack_error: lea si,msg_bad_token call echo_exception mov ax,dx ; call echonum ; echo DX, as decimal number lea si,newline ; call echo ; echo '\n' unstack_okay: call push_operand ; Push result ret ; Return to caller unstack_operator endp pop_operator proc near ; Pop operator off stack into AX: pushr ; save work registers mov bx,operator_ptr cmp bx,0 jl operator_under mov ax,operator_stack[bx] dec bx ; bump stack pointer dec bx ; mov operator_ptr,bx pop_operator_1: popr ; restore work registers ret ; Return to caller operator_under label near lea si,msg_operator_under call echo_exception jmps pop_operator_1 pop_operator endp pop_operand proc near ; Pop operand off stack into AX: pushr ; save work registers mov bx,operand_ptr cmp bx,0 jl operand_under mov ax,operand_stack[bx] dec bx ; bump stack pointer dec bx ; mov operand_ptr,bx pop_operand_1: popr ; restore work registers ret ; Return to caller operand_under label near lea si,msg_operand_under call echo_exception jmps pop_operand_1 pop_operand endp ;************************* UNSTACK ALL OPERATORS ************************** MODULE unstack_all_operators unstack_all_next label near ; while operator [ptr] >= lowest_op_pty mov bx,operator_ptr mov ax,operator_stack[bx] cmp ah,lowest_op_pty jl unstack_all_exit call unstack_operator jmps unstack_all_next unstack_all_exit label near ret ; Return to state machine ENDMODULE ;************************** UNSTACK IF LEFT PAR *************************** MODULE unstack_if_left_par mov bx,operator_ptr mov ax,operator_stack[bx] cmp al,'(' ; Expect ( on stack, else error je unstack_left lea si,msg_no_left_par call echo_exception ret ; Return to state machine unstack_left: call pop_operator ; if (, pop off stack ret ; Return to state machine ENDMODULE ;************************** UNSTACK IF END MARK *************************** MODULE unstack_if_end_mark mov bx,operator_ptr mov ax,operator_stack[bx] cmp al,end_mark_token je unstack_end ; lea si,msg_no_right_par call echo_exception ret ; Return to state machine unstack_end: call unstack_operator ret ; Return to state machine ENDMODULE ;************************** SIGNAL INVALID TOKEN ************************** MODULE signal_invalid_token lea si,msg_inv_token call echo_exception mov al,the_token ; call echoch ; echo the_token lea si,newline ; call echo ; echo '\n' ret ; Return to state machine ENDMODULE ;************************** SIGNAL TOKEN MISSING ************************** MODULE signal_token_missing lea si,msg_no_token call echo_exception ret ; Return to state machine ENDMODULE ;*************************** GET EXTERNAL EVENT *************************** MODULE get_external_event ret ; Return to state machine ENDMODULE ;************************* TERMINATE THE PROGRAM ************************* MODULE terminate_the_program mov the_next_event,terminate_event ret ; Return to state machine ENDMODULE ;%END MODULES ;--------------- End of source file ------------------------------------------ end program ; This block is generated code