;---------------------------------------------------------------------------------------------------------------------------------- ;LEGAL NOTICE, DO NOT REMOVE ; ;Annotated Busicom 141-PF software based on binaries recovered by Tim McNerney and Fred Huettig in collaboration with the Computer ;History Museum (November 2005). Original disassembly, reverse-engineering, initial analysis and documentation by Barry Silverman, ;Brian Silverman, and Tim McNerney (November 2006). Detailed analysis, commenting, documentation by Lajos Kintli (July 2007). ; ;The original Busicom binary code is not a copyrighted work and may be freely distributed without restriction. The commented code ;and related documentation (the "work") are subject to the terms of this license. ; ;This is version 1.0.0 of the "work" (reconstructed "source code") released on November 15, 2007. ;It corresponds to the preliminary version of the file named "BusicomCalculator_071026.asm" ;as submitted for editorial review by Lajos Kintli on October 26, 2007 ; ;Notice: This software and documentation is provided "AS IS." There is no warranty, claims of accuracy or fitness for any purpose other than for education. The authoritative version of this file can be located at http://www.4004.com ; ;You are free: ; ;* to copy, distribute, display, and perform this work ;* to make derivative works ; ;Under the following conditions: ; ; Attribution. You must attribute the work in the manner specified by the author or licensor. ; ; Noncommercial. You may not use this work for commercial purposes. ; ; Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under a license ; identical to this one. ; ;* For any reuse or distribution, you must make clear to others the license terms of this work. ;* Any of these conditions can be waived if you get permission from the copyright holder. ; ;Your fair use and other rights are in no way affected by the above. ; ;This is a human-readable summary of the Legal Code (the full license) available at: ; http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;Table of contents ; ;Chapter 1 Introduction ; ;Chapter 2 Abbreviations ; ;Chapter 3 HARDWARE ENVIRONMENT ; 3.1 MCS-4 family components ; 3.2 Keyboard Matrix ; 3.3 Printer ; ;Chapter 4 SOFTWARE ENVIRONMENT ; 4.1 I4004 instruction set summary ; 4.2 Using of ROM areas ; 4.3 Using of RAM areas ; 4.4 Basics of operation and implementation of the calculator ; 4.5 Square root implementation ; 4.6 Unusual results ; 4.7 Basic pseudo instruction codes ; 4.8 Square root pseudo instruction codes ; ;Chapter 5 Detailed analysis of the assembly code ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;Chapter 1 INTRODUCTION ; ;This document is an analysis of Busicom 141-PF calculator built with Intel 4004, the world's first microprocessor. The software of ;the calculator can be considered the first program has ever made for microprocessors. ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;Chapter 2 ABBREVIATIONS ; ;ACC i4004 accumulator ;BPC basic pseudo code ;CY i4004 carry flag ;CR constant register ;DP digit point ;DR dividend register ;IR indirect register ;KR keyboard buffer register ;M0..M15 main memory cells 0..15 ;MR memory register ;NR number register ;R0..R15 i4004 register 0..15 ;RR result register ;QPC square root pseudo code ;S0..S3 status character 0..3 ;SQRT square root ;SR sub total register ;TR main total register ;WR working register ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;Chapter 3 HARDWARE ENVIRONMENT: ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;3.1 MCS-4 family components ; ;The Busicom calculator has been built with the Intel's MCS-4 family set using the following main components: ; 5 * i4001 256 * 8 bit ROM with 4 bit input port (5th is optional for square root function) ; 2 * i4002 320 bit RAM with 4 bit input/output port ; 3 * i4003 10 bit shift register ; 1 * i4004 central processor unit (CPU) ; ;The port bits of ROMs, RAMs and TEST pin of CPU are used for: ; ;TEST: printer drum sector signal ; ;ROM0: shifter output ; bit0 = keyboard matrix column shifter clock (for a i4003 shifter) ; bit1 = shifter data (shared for printer and keyboard matrix shifter) ; bit2 = printer shifter clock (for two cascaded i4003 shifter) ; bit3 = not used ; ;ROM1: keyboard matrix rows input ; ;ROM2: bit0 = printer drum index signal ; bit1 = not used ; bit2 = not used ; bit3 = printer paper advancing button ; ;ROM3: not used ; ;ROM4: not used ; ;RAM0: printer control (used bits are outputs) ; bit0 = printing color (0=black, 1=red) ; bit1 = fire print hammers ; bit2 = not used ; bit3 = advance the printer paper ; ;RAM1: status lights (used bits are outputs) ; bit0 = memory lamp ; bit1 = overflow lamp ; bit2 = minus sign lamp ; bit3 = not used ; ;The CPU is driven with 750 kHz clock signal, which gives 1.333 microsec machine cycle time and 10.67 microsec instruction cycle ;time (10.67 or 21.33 microsec for individual instructions). ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;3.2 Keyboard matrix ; ;The buttons of the keyboard are organized in a matrix. The columns are driven by an i4003 shifter, and the status of selected rows ;can be fetched from the ROM1 input port. There is an 8 state digit point switch and a 3 state rounding switch which are mapped to ;the keyboard matrix through diodes, connecting the 9th and 10th bit of the shifter into the 4 bits of the ROM1 port. This way one ;shortcut on the 8 state digit point switch may active 0, 1 or 2 inputs on the input port of ROM1, and the switched position is ;directly binary decoded (value 7 can not be set). The 3 state rounding switch can activate 0 or 1 line on the ROM1 port. ; ;The following table summarizes, how the buttons, switches are logically mapped into the 10 columns x 4 rows matrix. This is in ;fact a "mirrored" version of the physical arrangement, as the last column comes first. Behind the name of the buttons there are ;hexadecimal values in parenthesis, which are the assigned scan codes. Only the first 8 columns are scanned for the buttons. The ;state of 9th and 10th column is fetched and just stored into RAM0 as status characters for later processing. ; ;shifter ROM1 bit0 ROM1 bit1 ROM1 bit2 ROM1 bit3 ;---------------------------------------------------------------------------------------------------------------------------------- ;bit0 CM (81) RM (82) M- (83) M+ (84) ;bit1 SQRT (85) % (86) M=- (87) M=+ (88) ;bit2 diamond (89) / (8a) * (8b) = (8c) ;bit3 - (8d) + (8e) diamond2 (8f) 000 (90) ;bit4 9 (91) 6 (92) 3 (93) . (94) ;bit5 8 (95) 5 (96) 2 (97) 00 (98) ;bit6 7 (99) 4 (9a) 1 (9b) 0 (9c) ;bit7 Sign (9d) EX (9e) CE (9f) C (a0) ;bit8 dp0 dp1 dp2 dp3 (digit point switch, value 0,1,2,3,4,5,6,8 can be switched) ;bit9 sw1 (unused) (unused) sw2 (rounding switch, value 0,1,8 can be switched) ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;3.3 Printer ; ;The printer contains a continuously rotating cylinder shaped printer drum equipped with 18 columns having 13 characters on each, ;except column 16, which is empty. The first 15 columns form the numbers, the last two columns give the special characters in the ;following order: ; ; sector column 1-15 column 17 column 18 ; 0 0 diamond # ; 1 1 + * ; 2 2 - I ; 3 3 X II ; 4 4 / III ; 5 5 M+ M+ ; 6 6 M- M- ; 7 7 ^ T ; 8 8 = K ; 9 9 SQRT E ; 10 . % Ex ; 11 . C C ; 12 - R M ; ;Rotating of the drum is followed through two input signals. The sector signal becomes active for each row of characters (sensed ;at the TEST pin of i4004), while the index signal informs the controlling unit, when the first row is in the printing position ;(led to bit 0 of ROM2 input port). In reality the period of sector signals is around 28ms (35.7Hz), and the index signal is ;13*28=364ms (2.74Hz). Each column has a separate hammer, which can be fired towards the drum, when the desired character just ;passes on the corresponding column. The shape of character is printed through an inked ribbon to the paper. The upper and lower ;half of the ribbon is inked into different colors (black and red), rising of the ribbon is controlled by bit0 of RAM0 port. The ;hammers are selected through two cascaded i4003 shifter registers, which have the following connection towards the columns: ; ;bit00 column 17 special characters ;bit01 column 18 special characters ;bit02 - not used ;bit03 column 1 digit or digit point ;bit04 column 2 digit or digit point ;bit05 column 3 digit or digit point ;bit06 column 4 digit or digit point ;bit07 column 5 digit or digit point ;bit08 column 6 digit or digit point ;bit09 column 7 digit or digit point ;bit10 column 8 digit or digit point ;bit11 column 9 digit or digit point ;bit12 column 10 digit or digit point ;bit13 column 11 digit or digit point ;bit14 column 12 digit or digit point ;bit15 column 13 digit or digit point ;bit16 column 14 digit or digit point ;bit17 column 15 digit or digit point ;bit18 - not used ;bit19 - not used ; ;When the shifter is filled with the correct pattern, and the desired row is in the right position, the hammers can be fired by ;bit1 of RAM0 port. E.g. when SQRT(2)=1.4142135623730 is printed, the following list of data should be sent to the shifter: ; ; Sector Hex binary (msb-lsb) meaning (1.4142135623730 SQ) ; --------------------------------------------------------------------------------------------------------------------------- ; 0 20000 00100000000000000000 0 ;one digit 0 is used ; 1 00248 00000000001001001000 1 1 1 ;three digit 1 are used ; 2 02100 00000010000100000000 2 2 ;two digit 2 are used ; 3 14400 00010100010000000000 3 3 3 ;three digit 3 are used ; 4 000A0 00000000000010100000 4 4 ;two digit 4 are used ; 5 00800 00000000100000000000 5 ;one digit 5 is used ; 6 01000 00000001000000000000 6 ;one digit 6 is used ; 7 08000 00001000000000000000 7 ;one digit 7 is used ; 8 00000 00000000000000000000 ;digit 8 is not used ; 9 00001 00000000000000000001 SQ ;digit 9 is not used, square root character is used ; 10 00010 00000000000000010000 . ;digit point ; 11 00000 00000000000000000000 ;no character is used from this row ; 12 00000 00000000000000000000 ;no character is used from this row ; ;The print operation is started with whatever print row happens to be under the hammers at the time the print function is called. ;The data list shown in the table above may be sent exactly as in that order, or it may be started e.g. with sector 4 and continue ;through 12, 0, and back to 3. If this had not been designed, there would have been a pause of up to a full rotation of the drum ;(in worst case approximately 364ms) before printing would start. ; ;When all columns are printed, the paper can be advanced, which is activated by bit3 of RAM0 port. ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;;Chapter 4 SOFTWARE ENVIRONMENT ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.1 i4004 instruction set summary ; ;Opcode 2nd byte Mnemonic CY description ;---------------------------------------------------------------------------------------------------------------------------------- ;00000000 NOP - No operation ;0001CCCC AAAAAAAA JCN - Jump conditional ;0010RRR0 DDDDDDDD FIM - Fetch indirect from ROM into register pair ;0010RRR1 SRC - Send Register Control ;0011RRR0 FIN - Fetch indirect from ROM (register pair = indirect from location R0R1 of the same page) ;0011RRR1 JIN - Jump indirect (8 bit of program counter = register pair) ;0100AAAA AAAAAAAA JUN - Jump unconditional ;0101AAAA AAAAAAAA JMS - Jump to subroutine ;0110RRRR INC - Increment register ;0111RRRR AAAAAAAA ISZ - Increment register, and jump at nonzero result ;1000RRRR ADD CY Add register and carry to accumulator (ACC=ACC+reg+CY) ;1001RRRR SUB CY Subtract register and borrow from accumulator (ACC=ACC+(15-reg)+(1-CY)) ;1010RRRR LD - Load register into accumulator ;1011RRRR XCH - Exchange register with accumulator ;1100DDDD BBL - Branch back (return) and load data into accumulator ;1101DDDD LDM - Load data into accumulator ; ;11100000 WRM - Write accumulator into main memory ;11100001 WMP - Output accumulator to RAM port ;11100010 WRR - Output accumulator to ROM port ;11100011 WPM - Write accumulator to 4008/4009 read/write program memory (not used in this application) ;11100100 WR0 - Write accumulator into status character 0 ;11100101 WR1 - Write accumulator into status character 1 ;11100110 WR2 - Write accumulator into status character 2 ;11100111 WR3 - Write accumulator into status character 3 ;11101000 SBM CY Subtract main memory and borrow from accumulator (ACC=ACC+(15-mem)+(1-CY)) ;11101001 RDM - Read main memory into accumulator ;11101010 RDR - Input ROM port into accumulator ;11101011 ADM CY Add main memory and carry to accumulator (ACC=ACC+mem+CY) ;11101100 RD0 - Read accumulator from status character 0 ;11101101 RD1 - Read accumulator from status character 1 ;11101110 RD2 - Read accumulator from status character 2 ;11101111 RD3 - Read accumulator from status character 3 ; ;11110000 CLB 0 Clear both (accumulator and carry) ;11110001 CLC 0 Clear carry ;11110010 IAC CY Increment accumulator ;11110011 CMC CY Complement carry (CY=1-CY) ;11110100 CMA - Complement accumulator (ACC=15-ACC) ;11110101 RAL CY Rotate accumulator left through carry ;11110110 RAR CY Rotate accumulator right through carry ;11110111 TCC 0 Transmit carry to accumulator and clear carry (ACC=CY) ;11111000 DAC CY Decrement accumulator ;11111001 TCS 0 Transmit carry subtract and clear carry (ACC=9+CY) ;11111010 STC 1 Set carry ;11111011 DAA CY Decimal adjust accumulator (ACC=ACC+6, if CY=1 or ACC>9) ;11111100 KBP - Keyboard process (0->0, 1->1, 2->2, 4->3, 8->4, rest->15) ;11111101 DCL - Designate command line (not used in this application) ;---------------------------------------------------------------------------------------------------------------------------------- ; ;Meaning of CCCC bits in the JCN instructions: ; ; CCCC hex abbreviation jump, when ; -------------------------------------------- ; 0001 1 TZ test=0 ; 0010 2 C1 cy=1 ; 0100 4 AZ accumulator=0 ; 1001 9 TN test=1 ; 1010 a C0 cy=0 ; 1100 c AN accumulator!=0 ; ;Combination of last 3 bits of CCCC would result logic "or" function with the individual conditions, however these are not used in ;this application. ;---------------------------------------------------------------------------------------------------------------------------------- ; ;The instruction set does not contain the very basic logical functions ("OR" and "AND"), which may be necessary in the application. ;This function can be implemented in a bit level through using the carry bit and the accumulator functions. It is advantageous, if ;the questioned bit(s) are placed to the lowest or highest bit position(s) (bit 0 and bit 3 in the 4 bit wide registers). ; ;E.g. if an "AND 1" would be needed for testing bit 0, the bit can be tested from the carry after the "RAR" instruction. ;"OR 1" can be replaced with the sequence of "RAR", "STC" and "RAL" instructions. ; ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.2 Using of ROM areas. ; ;There are 5 i4001 ROMs which implement the program address range of $000-$4ff (divided into 5 pages). The 5th ROM at address $400 ;is optional, and contains the implementation of the SQRT function. This ROM is not included, if the end user have not paid for the ;square root key. Even though the relevant "JUN $400" instruction is still in the remaining ROMs, however it would be never called ;due to the missing SQRT physical button. ; ;The memory ranges store the different code parts, which are summarized in the following table. Detailed description of the blocks ;can be found at the referenced memory address. ; ;$000-$027: main loop ;$029-$04a: processing of a pressed button ;$04b-$05f: basic pseudo code engine ;$061-$062: piece of code for the keyboard matrix handling ;$063-$069: keyboard shifter handling ;$06a-$076: printer drum synchronization ;$077-$080: piece of code for the keyboard matrix handling ;$081-$0a0: table for translating the keyboard scan codes into function code and parameter ;$0a1-$0af: table for translating the function code into pseudo code entry address ;$0b0-$0ff: main part of keyboard matrix handling ;$100-$1f8: implementation of pseudo instructions ;$1f9-$293: implementation of pseudo instructions for printer handling ;$294-$2ff: implementation of pseudo instructions ;$300-$304: basic pseudo code fetch routines ;$305-$3ff: pseudo code implementation of the calculator ;$400-$418: SQRT pseudo code engine ;$419-$427: unused memory area (NOPs) ;$428-$446: pseudo code implementation of the SQRT function ;$447-$44f: unused memory area (NOPs) ;$450-$450: SQRT pseudo code jump ;$451-$4aa: implementation of SQRT pseudo instructions ;$4ab-$4ff: unused memory area (NOPs) ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.3 Using of RAM areas: ; ;There are two i4002 RAMs are used, both have 4 registers with 16 cells main memory and 4 cells status characters. In this ;document the altogether eight RAM registers originally designed for storing numbers are generally referred to NR(0) to NR(7), ;(NR(0)..NR(3) in RAM0 and NR(4)..NR(7) in RAM1), the main memory cells are referred to M0..M15 and status characters as S0..S3. ;E.g. NR(7).S2 means status character 2 of register 3 in RAM1. ; ;Generally these registers are places for storing numbers. Usually status character 0 is the plus / minus sign (value 0 ;means positive, 1 or 15 means negative number), status character 1 is the place of digit point, and the main memory cells contain ;the number in BCD form (one cell is for one digit). E.g. NR(1) register containing -75.43 is represented with NR(1).S0=15 ;(negative number), NR(1).S1=2 (number has 2 digits behind the digit point), and NR(1).M4..M15=0, NR(1).M3=7, NR(1).M2=5, ;NR(1).M1=4 and NR(1).M0=3 (digits are adjusted to main memory location 0 starting with lowest digit value). In this calculator ;implementation the numbers are handled at 14 digit length, therefore M14 and M15 are 0, however temporarily it may contain valid ;data, e.g. during multiplication the two 14 digit numbers can produce 28 digit result. Status character 2 and 3 of the registers ;are used for special purposes. ; ;The number registers has an additional abbreviations as those are typically used for predefined purposes in the implementation of ;this calculator: ; ;NR(0): KR keyboard register keyboard buffer ;NR(1): WR working register input register (usually 2nd operand in add/sub/mul/div operation) ;NR(2): DR dividend register multiplicand/dividend (1st operand of mul/div operation) ;NR(3): RR result register temporary register (result of mul/div/sqrt operation, copied finally into WR) ;NR(4): CR constant register constant multiplicand/dividend register ;NR(5): SR sub total register sub total accumulator (1st operand of add/sub function) ;NR(6): TR main total register main total accumulator (1st operand of add/sub function) ;NR(7): MR memory register memory register (1st operand of memory add/sub function) ; ;Register 0 in RAM0 (KR) is not for storing numbers instead it is used for the keyboard buffer. It may happen during a time ;consuming operation that the user already presses new button(s) on the keyboard. These are stored temporarily into this buffer ;waiting for later processing. ; ;Special meaning of the RAM status characters: ; ;NR(1..7).S0: sign (bit0=0: positive, bit0=1: negative) ;NR(1..7).S1: place of digit point ; ;KR.S0: the keyboard buffer pointer ;KR.S3: keyboard pressing status (0=no button is held down, 15=a button is held down) ; ;WR.S2: rounding switch (10th column of the keyboard matrix) (0=floating, 1=rounding, 8=truncating) ;WR.S3: digit point switch (9th column of the keyboard matrix), values 0,1,2,3,4,5,6 and 8 are used ; ;DR.S2: multiply/divide status ;RR.S2: last operation ;CR.S2: digit entry mode status ;SR.S2: overflow status ; ;TR.S2: regularly cleared, but never read ; ;unused status characters (remains 0 after reset): KR.S1, KR.S2, DR.S3, RR.S3, CR.S3, SR.S3, TR.S3, MR.S2, MR.S3 ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.4 Basics of operation and implementation of the calculator ; ;The software of the calculator is written in i4004 assembly code and it uses the "interpretive mode" concept, where another ;instruction set is simulated (called pseudo instruction codes) by fetching the simulated codes from the memory and executing the ;associated routines, which implement the required subfunctions. ; ;In this calculator implementation the "hardware device drivers" for the keyboard and printer are implemented in native ;assembly code, but the main part of calculator logic is coded as a sequence of a pseudo codes in the following way: ; ;The calculator regularly scans the keyboard matrix in the main loop. When any of the buttons is pressed, a function code (in R5), ;a parameter (in R4) and a pseudo code entry address (in R0R1) are assigned to it, and the pseudo code interpreter is called (at ;overflow situation only buttons "CE" and "C" start the engine). In the pseudo code a "state machine" is implemented, where the ;calculator is modeled with internal states and registers. When the pseudo code engine starts to execute the pseudo instructions ;from the defined address, the functions modify these internal states and the number registers heavily depending on the function ;code, parameter and earlier states of state machine. At the end of this operation the pseudo code engine is terminated, and the ;keyboard scanning is continued in the main loop. ; ;In the implemented pseudo code interpreter there is no "routine or function" call possibility, but there is a need to execute the ;same subfunctions from different places. This problem is solved in a way, that the common code is started, and at the end of the ;execution the pseudo code branches to the relevant places, typically based on the function code or parameter. These are more or ;less constant values during the pseudo code execution, however their values may be ruined at the very last checks or as a loop ;counting. Additionally the function code at processing the "M=+" and "M=-" buttons are changing from 8 to 3, which start to work ;as an "=" function and later these are switched to "M+" and "M-" function. ; ;The main states of the state machine are stored in RR.S2, DR.S2, CR.S2 and SR.S2. These have only limited values, which represent ;the following situations: ; ;RR.S2=0 - new number is entered (or some operation is ended, and the result can be used as a new number) ;RR.S2=1 - last operation was multiply or divide ;RR.S2=8 - last operation was addition or subtraction ; ;DR.S2=0 - no started multiplication or division (may mean DR is divided by default) ;DR.S2=3 - started multiplication (DR is multiplied) ;DR.S2=4 - started division (DR is divided) ;DR.S2=8 - default division with constant value in CR ;DR.S2=B - started multiplication with constant value in CR ;DR.S2=C - started division with constant value in CR ; ;CR.S2=0 - digit entry is not started yet ;CR.S2=1 - started digit entry with digit point ;CR.S2=8 - started digit entry without digit point ; ;SR.S2=0 - no overflow ;SR.S2=1 - overflow occurred ; ;Below is a summarization, which inputs are assigned to the pressed buttons and how the registers and the main states are modified ;based on their previous values. Following the "instructions" defined by this table the operation of the calculators can be ;understood. It does not contain the printing and digit point adjustment instructions, just the high level handling of the main ;states and the number registers. "-" in the in columns means not checked state, while "-" in the out columns means not changed ;value. Short remark is also added ("M=+" and "M=-" are not commented, there the same comments are valid like at "=" and at the ;memory add/sub function). ; ;---------------------------------------------------------------------------------------------------------------------------------- ; entry in in in in out out ;button address fcode fpar RR.S2 DR.S2 RR.S2 DR.S2 operation (remark) ;---------------------------------------------------------------------------------------------------------------------------------- ;* $306 1 3 0 0 1 3 DR=WR, CR=WR (save the first operand) ; 0 3 1 3 RR=DR*WR, WR=RR, DR=WR, CR=WR (chain mul/div operation) ; 0 4 1 3 RR=DR/WR, WR=RR, DR=WR, CR=WR (chain mul/div operation) ; 0 8,B,C 1 3 DR=WR, CR=WR (new number after const mul/div) ; 1 - 1 3 DR=WR, CR=WR (operation correction) ; 8 - 1 3 WR=TR, DR=WR, CR=WR, SR=0, TR=0 (main total is the first operand) ; (clear totals) ;/ $306 1 4 0 0 1 4 DR=WR, CR=WR (save the first operand) ; 0 3 1 4 RR=DR*WR, WR=RR, DR=WR, CR=WR (chain mul/div operation) ; 0 4 1 4 RR=DR/WR, WR=RR, DR=WR, CR=WR (chain mul/div operation) ; 0 8,B,C 1 4 DR=WR, CR=WR (new number after const mul/div) ; 1 - 1 4 DR=WR, CR=WR (operation correction) ; 8 - 1 4 WR=TR, DR=WR, CR=WR, SR=0, TR=0 (main total is the first operand) ; (clear totals) ;+ $391 2 1 0,8 - 8 - RR=WR, TR=TR+WR, SR=SR+WR (add number to totals) ; 1 - 8 0 RR=WR, TR=TR+WR, SR=SR+WR (add number to totals) ; ;- $391 2 2 0,8 - 8 - RR=WR, TR=TR-WR, SR=SR-WR (sub number from totals) ; 1 - 8 0 RR=WR, TR=TR-WR, SR=SR-WR (sub number from totals) ; ;M+ $398 3 5 - - 0 - RR=WR, MR=MR+WR (add number to memory) ; ;M- $398 3 6 - - 0 - RR=WR, MR=MR-WR (sub number from memory) ; ;EX $3f1 4 a - - 0 - CR=WR, RR=DR, DR=WR, WR=RR (exchange) ; ;diamond $3cd 5 0 0 - - - (print only) ; 1 - - - (print only) ; 8 - - - WR=SR, SR=0 (recall subtotal) ; ;00 $3d7 6 0 - - 0 - WR=add two new zeros to the number (number entry) ; ;RM $3fd 7 c - - 0 - WR=MR (recall memory) ; ;= $38a 8 1 0,1 0,4 0 +8 CR=WR, RR=DR/WR, DR=?, WR=RR (divide, divisor to const) ; 0,1 3 0 +8 RR=DR*WR, DR=0, WR=RR (multiply) ; 0,1 8,C 0 - DR=WR, WR=CR, RR=DR/WR, WR=RR (const divide) ; 0,1 B 0 - DR=WR, WR=CR, RR=DR*WR, WR=RR (const multiply) ; 8 - 0 - WR=TR, SR=0, TR=0 (recall main total, clear totals) ; ;M=+ $38a 8->3 5 0,1 0,4 0 +8 CR=WR, RR=DR/WR, DR=?, WR=RR, RR=WR, MR=MR+WR ; 0,1 3 0 +8 RR=DR*WR, DR=0, WR=RR, RR=WR, MR=MR+WR ; 0,1 8,C 0 - DR=WR, WR=CR, RR=DR/WR, DR=?, WR=RR, RR=WR, MR=MR+WR ; 0,1 B 0 - DR=WR, WR=CR, RR=DR*WR, DR=0, WR=RR, RR=WR, MR=MR+WR ; 8 - 0 - WR=TR, SR=0, TR=0, RR=WR, MR=MR+WR ; ;M=- $38a 8->3 6 0,1 0,4 0 +8 CR=WR, RR=DR/WR, DR=?, WR=RR, RR=WR, MR=MR-WR ; 0,1 3 0 +8 RR=DR*WR, DR=0, WR=RR, RR=WR, MR=MR-WR ; 0,1 8,C 0 - DR=WR, WR=CR, RR=DR/WR, DR=?, WR=RR, RR=WR, MR=MR-WR ; 0,1 B 0 - DR=WR, WR=CR, RR=DR*WR, DR=0, WR=RR, RR=WR, MR=MR-WR ; 8 - 0 - WR=TR, SR=0, TR=0, RR=WR, MR=MR-WR ; ;SQRT $305 9 1 - - 0 0 CR=WR, RR=SQRT(WR), DR=?, WR=RR SQRT ; ;% $361 a 1 - 0,4 0 +8 CR=WR, RR=DR/WR, DR=?, WR=RR (divide, divisor to const) ; - 3 0 +8 RR=DR*WR, DR=0, WR=RR (multiply) ; - 8,C 0 - DR=WR, WR=CR, RR=DR/WR, DR=?, WR=RR (const divide) ; - B 0 - DR=WR, WR=CR, RR=DR*WR, DR=0, WR=RR (const multiply) ; ;CM $3f9 b b - - 0 - WR=MR, MR=0 (recall memory and clear) ; ;000 $3d7 c 0 - - 0 - WR=add three new zeros to the number (number entry) ; ;digit $3d7 d digit - - 0 - WR=add new digit to the number (number entry) ; ;sign $3d7 d 10 - - 0 - WR=change the sign of WR (number entry) ; ;dp $3d7 d 11 - - 0 - WR=mark the digit point (number entry) ; ;CE $3ca e 0 - - 0 - WR=0 (entry clear) ; ;C $3c5 f b - - 0 0 WR=0, DR=0, SR=0, TR=0 (clear operands and totals) ;---------------------------------------------------------------------------------------------------------------------------------- ; ;As an example let us follow how the "24 * 3 =" is executed. Assumed, that at the beginning all the internal states and registers ;are cleared. ; ;---------------------------------------------------------------------------------------------------------------------------------- ; in in in in out out ;button fcode fpar RR.S2 DR.S2 RR.S2 DR.S2 operation remark ;---------------------------------------------------------------------------------------------------------------------------------- ;2 d 2 0 0 0 0 WR=2 digit is placed to the working register ;4 d 4 0 0 0 0 WR=24 digit is added to the end of the working register ;* 2 1 0 0 1 3 DR=24 number is copied to dividend/multiplicand register ; CR=24 number is copied to constant register ;3 d 3 1 3 0 3 WR=3 digit is placed to the working register ;= 8 1 0 3 0 B RR=24*3=72 multiplication is calculated ; DR=0 multiplicand is cleared ; WR=72 result is copied to the working register ;---------------------------------------------------------------------------------------------------------------------------------- ;Note: CR still contains the original multiplicand, new numbers can be multiplied with it. DR.S2=B indicates this. ; ;Note: The calculator can be reset by pressing the "CM", "C" and "Ex" buttons in this order. "CM" is required to clear MR, "C" ;clears WR, DR, SR and TR, and "Ex" copies the cleared WR, DR into RR and CR. These clear the internal flags too. Pressing just "C" ;may be also enough (if clearing the memory is not needed), but it leaves numbers in RR and CR, which are not disturbing, as those ;can not be referred in any way (those will be overwritten with new values before use). ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.5 Square root implementation ; ;Mathematical background: ; ;At first, let us think in the set of integer numbers only. The task is to find a suitable "p" to certain "N", where ; ; (p+1)*(p+1) > N >= p*p. We can say then, that SQRT(N)=p. ; ;E.g. SQRT(5936123) would lead to 2436, as 2437*2437=5938969 > 5936123 >= 2436*2436=5934096. ; ;It is easy to see, that stealing 2 digits from the end of "N" results stealing one digit from the end of SQRT(N): ; ; E.g. as SQRT(5936123)=2436, then SQRT(59361)=243, SQRT(593)=24 and SQRT(5)=2. ; ;The square root is guessed digit by digit from left to right in the reverse logic to the previous rule. For calculating the sqrt ;for a big number, at first digits are "removed" pair by pair from the end, e.g. 5936123 leads to 5, then the calculation is ;started from the "simplified" number (from 5). When the square root is already known for it ("half result"), then the next ;two digits of "N" are taken (93) and the next digit of the square root of 593 is "guessed" based on the already known "half ;result". This algorithm is repeated till the end of the "N" by taking the removed digit pairs (61 and 23). (Bigger accuracy can ;be reached, if "N" is extended with "0"-s, and the same algorithm is continued.) ; ;For demonstrating how the next digit is "guessed", let us assume, that the square root is already calculated to certain number ;of digits. E.g. we already calculated SQRT(593)=24, and we would like to find the next digit ("d") in SQRT(59361), which must ;be in the form of 240 + d, where d=0..9. ; ;The base of square root algorithm is implemented by using the following equation: ; ; N >= (a+b)*(a+b) = a*a + 2*a*b + b*b, which is in other form: ; ; (N - a*a) - (2*a*b + b*b) >= 0. ; ;In our example "N"=59361, "a" is 240, and "b" is the next digit "d", or with the actual numbers: ; ; (59361 - 240*240) - (2*240*d + d * d) >= 0, which is in shorted way ; ; 1761 - (480*d + d*d) >= 0. ; ;In this equation "d" is tried from 1 to 10, and the loop is stopped, when the left side becomes negative. If we are doing it ;one by one, we can save the multiplication in the "480*d", if we subtract 480 at every step from 1761. Calculation of d*d ;can be simplified too. Let us notice, that the difference between the neighboring square numbers are an odd number, ;d*d-(d-1)*(d-1)=d*d-(d*d-2*d+1)=2*d-1, thus by adding the odd numbers from 1,3,5 ... serially we get the series of square ;numbers (1=1*1, 1+3=2*2, 1+3+5=3*3, ... 1+3+5+7+9+11+13+15+17+19=10*10). If the odd numbers are serially added to 480, the ;result of subtraction will contain also sum of the odd numbers, that is the d*d square. Alternatively after starting the ;subtrahend value with 480+1, it can be incremented by 2 at every step. ; ;In our example the next digit would be determined in the following way: ; ;step remainder try subtrahend altogether with previous subtrahends new remainder ;1 1761 d=1 480+1 480 + 1 = 480*1 + 1*1 1280 ;not negative ;2 1280 d=2 480+3 480+480 + 1+3 = 480*2 + 2*2 797 ;still not negative ;3 797 d=3 480+5 480+480+480 + 1+3+5 = 480*3 + 3*3 312 ;still not negative ;4 312 d=4 480+7 480+480+480+480 + 1+3+5+7 = 480*4 + 4*4 -175 ;negative, stop ; ;This means, that the next digit should be 3, as 4 already produces negative remainder, but 3 does not, so SQRT(59361)=243. ;Note, that the "new remainder" (312) gives the hundreds of the initial remainder for the next round (59361-243*243=312 ;or after taking the next two digits 5936123-2430*2430=31200+23). See also, that the subtrahend at step 4 is bigger with 1, ;than the tens of subtrahend for the next round (2*243*10=4860), an additional decrement is needed before the next round. ; ;For starting the algorithm the first one or two digits of the number are taken as the initial remainder, the initial half result ;is cleared. In our example, this would look like with the following numbers (round 5 and 6 is not necessarily needed, it just ;demonstrates, how further accuracy can be reached): ; ;round number digit pair remainder half result guessed digit subtracted numbers new remainder ;1 5 5 5 0 2 1,3 1 ;2 593 93 193 2 4 41,43,45,47 17 ;3 59361 61 1761 24 3 481,483,485 312 ;4 5936123 23 31223 243 6 4861,4863,4865,4867,4869,4871 2027 ;5 593612300 00 202700 2436 4 48721,48723,48725,48727 7804 ;6 59361230000 00 780400 24364 1 487281 293119 ; ;Thus SQRT(5936123)=2436 (or 2436.41, if two more digits are needed). If we are thinking in real numbers instead of integers, ;similar algorithm can be used. Now the digit pairing at the first simplification should be started from the digit point to left ;and right direction too. E.g. in "593.6123" the digit pairing would look like this "5 93 . 61 23". Then the ;algorithm can be started similarly, just when the digit point is reached between the digit pairs, a digit point has to be placed ;into the result too, but apart from this, the calculation should be done as integer numbers would be handled. When SQRT(593)=24 ;is determined, the digit point is added right after the interim result "24.", and the next guessed digits are placed behind it, ;that is sqrt(593.6123)=24.3641. ; ;In the real implementation square root of WR is calculated. WR is left shifted into the leftmost position, then it is copied to ;DR. Due to the digit pairing the shift may be needed one more time again, which is done in DR in this case. The new place of ;digit point is calculated based on the original place of digit point and the number of shifts, which is counted in R10 and R11. ;WR is the place of subtrahend, which is cleared for starting the algorithm. Multiplication with 10 is implemented with shifting ;the number (as BCD arithmetic is used) or it is not needed at all, as R15 dynamically points to the part of the number, which is ;already used in the calculation. DR contains the remainder. The "guessed" digit is counted in R13 and is shifted into RR. The ;final result is copied from RR to WR after returning back to the basic pseudo code interpreter to address $340. Place of the digit ;point is set from R11 there too. ; ;Note: this implementation does not care about the sign, SQRT(X) is handled generally by SQRT(ABS(X)). ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.6 Unusual results ; ;Strange behaviors: The following list contains few sequences, which produces unusal results. Some of these can be planned ;or this kind of malfunctions are intentionally left in the program due to the memory size limitation (correct handling would have ;required more code), or maybe the target of this analysis was a slightly damaged assembly code (bits of the original ROMs may be ;damaged during the long years). Below there are some cases, which can be reproduced from the just powered up calculator (assumed, ;that the calculator is switched to floating and 0 digit point state). ; ;1, The default operation is the division. If "10 Ex 3 =" is entered, the result is "3.3333333333333", which is 10/3. ; ;2, If "=" is entered, then an overflow occurs due to the default division operation and the 0/0 calculation. ; ;3, DR internal register is not initialized correctly at the end of a division, when "=", "M=+", "M=-" or "%" is pressed. This ; time DR contains the left adjusted remainder, which may use the 15th digit position too, while the place of digit point is ; inherited from the previous dividend. This number can be exchanged into WR, and can be the source for other operations. ; Entering "10 / 3 = Ex Diamond" results "100000000000000" (15 digit length number!). "10.01 / 3 = Ex Diamond" places ; "2000000000000.00" into WR which is printed as "000000000000.00", as there is no column for the leading "2" (digit point ; already uses an extra column), but "10.01 / 3 = Ex / 3 =" gives "666666666666.6". ; ;4, Square root function leaves in DR the double of root result with cleared digit point. This also can be 15 digit number, it can ; be exchanged into WR, or can be the source of the default division. E.g. "2 SQRT Ex Diamond" gives "28284271247460", or ; "2 SQRT 3 =" and repeatedly pressing new "="-s produces series of very strange numbers. (This might be due to one bit damage at ; address $4aa, in this code there is $04, but originally $0c is very probable. If the assumption is correct, DR would get the ; original value of WR.) ; ;5. Digit point adjustment problem. Adding and subtracting does not check the place of digit point, it assumes, that both operands ; are adjusted to the same digit point place (determined by the digit point switch). However changing the state of a digit point ; during an operation will cause incorrectly added or subtracted numbers, as the changed state of digit point switch modifies ; the later entered WR, but the earlier accumulated results in SR, TR or MR are not adjusted, just the digit point place will be ; refreshed by the next operation. E.g. "(DP=0) 1 + (DP=3) 2 + =" gives "2.001", where "(DP=n)" means digit point switch is at ; position "n". ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.7 Basic pseudo instruction codes ; ;The following table summarizes the instructions implemented by the pseudo codes. The first column is a count, how many times the ;instruction is used from page $300, 2nd column is the hexadecimal value of the pseudo instruction code, 3rd is an 'artificial' ;mnemonic (or a list of mnemonics), and finally a short description is about the function of the instruction. In the instructions ;new abbreviations are introduced for some i4004 registers, status characters and their values. ; ;WR.S2: 0=NTRUNC, bit0=ROUND ;DR.S2=MOP: 0=MOPN, bit0=MOPMUL, bit3=MOPCONST ;RR.S2=MODE: 0=MODENN, bit0=MODEMD, bit3=MODEAS ;CR.S2=MENT: bit0:MENTDP ;SR.S2=OVFL ;R13=DIGIT ;R10R11=DPCNT ; ;---------------------------------------------------------------------------------------------------------------------------------- ;1 $01 MOV IR,WR Move working register into indirect register (IR=WR) ; ;3 $02 MOV CR,WR Move working register into constant register (CR=WR) ; ;3 $03 MOV RR,WR Move working register into result register (RR=WR) ; ;3 $04 MOV DR,WR Move working register into dividend/multiplicand register (DR=WR) ; ;2 $09 MOV WR,MR Move memory register into working register (WR=MR) ; ;0 $0A MOV WR,TR Move main total register into working register (WR=TR) ; ;1 $0B MOV WR,SR Move sub total register into working register (WR=SR) ; ;2 $0C MOV WR,CR Move constant register into working register (WR=CR) ; ;3 $0D MOV WR,RR Move result register into working register (WR=RR) ; ;2 $0E MOV WR,DR Move dividend/multiplicand register into working register (WR=DR) ; ;2 $1E ADD IR,WR Add working register to indirect register (IR=IR+WR) ; ;2 $21 ADD DR,WR Add working register to dividend/multiplicand register (DR=DR+WR) ; ;1 $2C SUB WR,IR + JPC NNEG + INC DIGIT ; Subtract indirect from working register (WR=WR-IR) ; jump at non negative with increment the digit ; ;2 $31 SUB IR,WR + JPC NNEG + INC DIGIT ; Subtract working register from indirect register (IR=IR-WR) ; jump at non negative with increment the digit ; ;1 $34 SUB DR,WR + JPC NNEG + INC DIGIT ; Subtract working register from dividend/multiplicand register (DR=DR-WR) ; jump at non negative with increment the digit ; ;1 $44 CLR MR Clear memory register (MR=0) ; ;0 $45 CLR TR Clear main total register (TR=0) ; ;1 $46 CLR SR Clear sub total register (SR=0) ; ;0 $47 CLR CR Clear constant dividend/multiplicand register (CR=0) ; ;1 $48 CLR RR Clear result register (RR=0) ; ;2 $49 CLR DR Clear dividend/multiplicand register (DR=0) ; ;2 $4A CLR WR Clear working register (WR=0) ; ;3 $51 SHL RR Left shift of result register with R13 ; ;3 $52 SHL DR Left shift of dividend/multiplicand register with R13 ; ;3 $53 SHL WR Left shift of working register with R13 ; ;2 $5A SSR RR Right shorted shift of result register ; (one digit right shift of 14 digit length RR with R13 (R13 is shifted into digit 14)) ; ;2 $5D SHR RR Right shift of result register ; (one digit right shift of RR with R13 (0 is shifted from right)) ; ;2 $5E SHR DR Right shift of dividend/multiplicand register ; (one digit right shift of DR with R13 (0 is shifted from right)) ; ;3 $5F SHR WR Right shift of working register ; (one digit right shift of WR with R13 (0 is shifted from right)) ; ;4 $6C JPC MODENN Jump, if new number is entered and not processed with add/div/mul/div (jump, if RR.S2=0) ; ;1 $6D JPC MOPN Jump, if divide or multiply operation is not specified (jump, if DR.S2=0) ; ;1 $6E JPC NTRUNC Jump, if number is not truncated/rounded (jump, if WR.S2=0) ; ;0 $73 JPC OVFL Jump at overflow (jump, if SR.S2.bit0<>0) ; ;1 $74 JPC MENTDP Jump if number is entered with digit point (jump, if CR.S2.bit0<>0) ; ;4 $75 JPC MODEMD Jump, if number is used for mul/div operation (jump, if RR.S2.bit0<>0) ; ;2 $76 JPC MOPMUL Jump, if multiplication is started (jump, if DR.S2.bit0<>0) ; ;1 $77 JPC ROUND Jump, if rounding is needed (jump, if WR.S2.bit0<>0) ; ;2 $7B JPC MOPCONST Jump, if multiplication/division is done with constant value (jump, if DR.S2.bit3>0) ; ;1 $7F CLR OVFL Clear overflow (SR.S2=0) ; ;2 $82 CLR MOP Clear divide/multiply operation (clear DR.S2=0) ; ;1 $85 SET OVFL Set overflow (SR.S2=1) ; ;1 $86 SET MENTDP Set that number is entered with digit point (set CR.S2=1) ; ;1 $87 SET MODEMD Set that number is used for mul/div operation (set RR.S2=1) ; ;1 $8A SET MODEAS Set that number is used for add/sub operation (set RR.S2=8) ; ;3 $8D SET MOPPAR Set the multiplication/division from function parameter (set DR.S2=function code parameter) ; ;1 $90 SET MOPCONST Set that multiply/divide operation is with constant value (set DR.S2.bit3=1) ; ;1 $97 JPC NBIG_IR Jump if indirect register does not contain big value (upper two digits are empty) ; ;4 $9A JPC NBIG_WR Jump if working register does not contain big value (upper two digits are empty) ; ;1 $9E CLR DIGIT + JPC NBIG_DR ; Clear the digit (R13=0) ; Jump if dividend/multiplicand register does not contain big value (upper two digits are empty) ; ;2 $A0 CLR DIGIT + JPC ZERO_DR ; Clear the digit (R13=0) ; Jump if dividend/multiplicand register is zero ; ;1 $A2 JPC ZERO_WR Jump if working register is zero ; ;16 $A7 JMP Jump always ; ;3 $A9 JPC BIG_DIGIT Jump, if digit is bigger then 9 (jump, if R13>9) ; ;1 $AC JPC ZERO_DIGIT + DEC DIGIT (decrement R13 and jump, if R13 was 0 before the decrement) ; ;1 $AE CLR DIGIT + JMP Clear digit (R13=0) and jump ; ;2 $B1 JPC NEWOP Jump at new add/sub/mul/div operation (jump, if function code<8) ; ;3 $B4 JPC MEMOP Jump at new memory operation (jump, if function parameter>3) ; ;2 $B7 JPC ROTFC Rotate the function code right and jump, if the rotated out bit is zero ; ;2 $BC JPC ODDPAR Jump if function parameter is odd ; ;1 $BF SET DP_IR Set digit point place of indirect register (IR.S1=R11) ; ;2 $C2 SET DP_WR Set digit point place of working register (WR.S1=R11) ; ;1 $C6 GET DP_WR Get the digit point place of working register (R11=WR.S1) ; ;5 $CA INC DPCNT Increment digit point counter (increment R10R11) ; ;3 $CE JPC NBIG_DPCNT Jump, if digit point counter does not exceed the upper limit (jump, if R10R11<0E) ; ;3 $CF JPC ZERO_DPCNT Jump, if digit point counter is zero (jump, if R10R11=0) ; ;1 $D4 JPC DIFF_SIGN Jump, if working register and indirect register have different sign ; (jump, if WR and IR have different sign) ; ;1 $D7 DIGIT Digit functions ; ;3 $D9 MOV WR,TR + CLR TR + CLR SR WR=TR, TR=0, SR=0 ; ;1 $DB SET MRMFUNC + JMP Set function code the memory function (3) and jump ; ;5 $DD DEC DPCNT Decrement digit point counter (decrement R10R11) ; ;1 $DF GET DPDIFF Difference between required an actual digit point is set into digit point counter ; (WR.S1=WR.S3, R10R11=difference between required an actual digit point) ; ;1 $E1 GET DPCNTDIV Digit point counter adjust for division (set R10R11 to DR.S1+(13-R11)-WR.S1) ; ;1 $E3 GET DPCNTMUL Digit point counter adjust for multiplication ; (set R10R11 to the sum of digital places (WR, DR & current in R11) ; ;2 $E5 SET DIVMUL_SIGN + MOV DIGIT,15 ; Sign of result register is set based on the WR and DR for multiplication or division ; DIGIT is set to 15 (R13=15, used for loop counting) ; ;2 $E7 NEG WR Change the sign of working register (complement WR.S0) ; ;1 $E9 ROUNDING Increment working register if DIGIT>4 ; (if R13>4 then increment R14 else increment WR) ; ;1 $EB PRN ADVANCE + CLR DPCNT ; Advancing the printer paper. ; (end of printing with advancing the paper and R10R11=0, R14R15=0) ; ;1 $ED SQRT Square root of working register is calculated into result register. Continues at address $40. ; ;3 $EF CLR MENT + CLR OVFL + RET Return (CR.S2=0, SR.S2=0, TR.S2=0 and exit) ; ;7 $F1 CLR MODE + CLR MENT + RET Return (RR.S2=0, CR.S2=0 and exit) ; ;4 $F3 CLR MODE + RET Return (RR.S2=0, CR.S2=0 and exit) ; ;0 $F9 PRN FPAR,C print number with function parameter and char=11 "C" in last column (not used) ; ;2 $FA PRN FPAR,MEM print number with function parameter and char=12 "M" in last column ; ;1 $FB PRN FPAR,FCODE print number with function parameter and empty character in last column ; ;5 $FC PRN FPAR print number with function parameter and empty character in last column ; ;3 $FD PRN ROUND,FPAR print number with optional rounding char and function parameter in last column ; ;2 $FE PRN FCODE print number with function code and empty character in last column ; ;1 $FF PRN OVFL print unimplemented number (dots with empty extra columns) ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;4.8 Square root pseudo instruction codes ; ;The optional square root ROM implements a second pseudo code engine at address space $400-$4FF, and uses some of the basic pseudo ;instructions just under new code value, plus implements new instructions. These are: ; ;---------------------------------------------------------------------------------------------------------------------------------- ;1 $51 PRN FCODE print number with function code and empty character in last column ; ;1 $53 CLR RR Clear result register (RR=0) ; ;1 $55 CLR WR Clear working register (WR=0) ; ;1 $57 SHL RR Left shift of result register with R13 ; ;1 $58 SHL DR Left shift of dividend/multiplicand register with R13 ; ;1 $59 SHL WR Left shift of working register with R13 ; ;2 $5B MOV DR,WR Move working register into dividend/multiplicand register (DR=WR) ; ;1 $5D SUB DR,WR + JPC NNEG + INC DIGIT ; Subtract working register from dividend/multiplicand register (DR=DR-WR) ; jump at non negative with increment the digit; ; ;1 $5F ADD DR,WR Add working register to dividend/multiplicand register (DR=DR+WR) ; ;1 $61 JPC ZERO_WR Jump if working register is zero ; ;1 $63 JPC NBIG_WR Jump if working register does not contain big value (upper two digits are empty) ; ;1 $65 CLR DIGIT + GET DP_WR Clear digit (R13=0) and get the digit point place of working register (R11=WR.S1) ; ;1 $6A SET LPCSQRT + SET DPCNTSQRT + JPC EVENDP ; Set sqrt loop counter (R15=13) ; Adjust place of digit point for sqrt (R10R11=(R10R11/2+6+((R10R11 mod 2)))) ; Jump, if original place of digit point was even ; ;2 $7A INC WR_POS Increment working register from position in R15 ; ;1 $85 DEC WR_POS Decrement working register from position in R15 ; ;1 $93 INC DPCNT + JMP increment digit point counter (R10R11) and unconditional jump ; ;1 $96 JMP Unconditional jump ; ;1 $98 JPC NZERO_LPCSQRT + DEC LPCSQRT decrement sqrt loop counter (R15), and jump, except when R15 was 0 ; ;1 $9C SHR WR Right shift of working register ; (one digit right shift of WR with R13 (0 is shifted from right)) ; ;1 $9F CLR MOP + RET_BPC Return back to basic pseudo code interpreter to address $40 ; ;1 $A7 MOV CR,WR Move working register into constant register (CR=WR) ; ;1 $A9 MOV DR,WR Move working register into dividend/multiplicand register (DR=WR) ??? ; (MOV WR,CR) It is very probable that this would be move constant register into working register (WR=CR) ; (this way there are two codes for "DR=WR" ($5B and $A9) and here "WR=CR" would be more logical) ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;Chapter 5 Detailed analysis of the assembly code ;---------------------------------------------------------------------------------------------------------------------------------- ;---------------------------------------------------------------------------------------------------------------------------------- ;Main Loop ; ;At power up the PC is set to 000, and also the internal registers and RAM areas are cleared, thus no real initialization is needed ;here. The program can be directly started with the main loop including the keyboard handling and printer drum synchronization. ;When a pressed key is processed, the program will continue the execution in the main loop with the "jump 000" instruction. ;---------------------------------------------------------------------------------------------------------------------------------- 000 f0 junb_000: clb 001 11 01 jcnb_001: jcn TZ $001 ;wait for the inactive printer drum sector signal 003 50 b0 jms $0b0 ;Keyboard handling 005 51 5f jcnb_005: jms $15f ;right shift of keyboard buffer through R13 007 ad ld 13 008 b1 xch 1 ;R1=lower half of the possible scan code 009 f0 clb 00a 51 5f jms $15f ;right shift of keyboard buffer through R13 00c ad ld 13 ;ACC=upper half of the possible scan code 00d 1c 29 jcnb_00d: jcn AN $029 ;jump, if valid data was shifted from the buffer ;Status light handling ;RAM1 port: ; BIT0 = Memory lamp (MR) ; BIT1 = Overflow lamp (SR.S2.bit0) ; BIT2 = Minus sign lamp (WR.S0.bit0) 00f 68 inc 8 ;R4R5 points to WR 010 51 73 jms $173 ;read the overflow bit, CY=SR.S2.bit0 012 27 jcnf_012: src 3< 013 ec rd0 ;read WR.S0 (minus/positive sign, bit 0 is used) 014 f5 ral 015 b3 xch 3 ;R3=WR.S0 << 1 + (overflow bit) 016 68 inc 8 ;R4R5 points TR 017 f0 clb 018 51 a0 jms $1a0 ;check, whether MR contains any number 01a f3 cmc ;after negate CY=1, if MR is not empty 01b b3 xch 3 01c f5 ral ;shift into ACC (8*WR.S0.bit1 + 4*WR.S0.bit0 + 2*(overflow) + (MR ? 1 : 0)) 01d e1 wmp ;output into RAM1 port 01e 66 inc 6 01f 27 src 3< 020 ea rdr ;read ROM2 port 021 f5 ral 022 f7 jcnb_022: tcc 023 14 00 jcn AZ $000 ;jump back, if ROM2.bit3 is low: paper advance button is not held down 025 52 46 jms $246 ;more advancing the printer paper 027 40 00 jun $000 ;jump back to main loop ; ;A pressed button is found ; 029 b0 xch 0 ;R0R1=Keyboard scan code 02a ec rd0 ;decrement the keyboard buffer pointer (KR.S0) by two 02b f8 dac 02c f8 dac 02d e4 wr0 02e 27 src 3< 02f ea rdr ;read the content of ROM1 port (decimal point switch) 030 e7 wr3 ;write it into WR.S3 (number of decimal places) 031 50 64 jms $064 ;shift one high bit into keyboard shifter 033 27 src 3< 034 ea rdr ;read the content of ROM1 port (rounding switch) 035 e6 wr2 ;write it into WR.S2 036 34 fin 2< ;translate the scan code into function code and parameter (into R4R5) 037 20 a0 fim 0< $a0 039 a5 ld 5 03a b1 xch 1 03b 30 fin 0< ;fetch the pseudo code entry address of the function code from table $0a0-$0af (into R0R1) 03c 68 inc 8 03d 51 73 jms $173 ;read the overflow bit, CY=SR.S2.bit0 03f d0 ldm 0 040 e1 wmp ;put RAM1.port=0 (clear status lamps) 041 d1 ldm 1 ;ACC=1 042 f3 cmc ;CY=!(overflow) 043 f5 ral ;ACC=3 (!overflow) or ACC=2 (overflow) 044 fc kbp ;ACC=15 (!overflow) or ACC=2 (overflow) 045 85 add 5 ;adding the function code ;if there is no overflow, all functions set the CY flag ;if there is overflow, only "C" or "CE" functions set the CY flag 046 1a 00 jcn C0 $000 ;jump, if overflow blocks the new function 048 f0 clb 049 00 nop 04a 00 nop ;---------------------------------------------------------------------------------------------------------------------------------- ;Basic pseudo code engine with keyboard handling ; ; usage of registers: ; R0R1 - pseudo code instruction pointer ; R2R3 - pseudo instruction code ; R4 - parameter (defined by the last pressed button) ; R5 - function code (defined by the last pressed button) ; R6R7 - $20 - points to DR ; R8R9 - $10 - points to WR ; R12 - printer drum sector counter ; ACC - 0 ; CY - 0 ; ; R10,R11,R13,R14,R15 - generally usable registers ; (R10R11 - digit point counter) ; (R13 - digit, used for shifters, loop counting) ; (R14 - rounding indicator) ; ;Pseudo code interpreter logic: ; ;Pseudo instruction codes are fetched from the address 300-3ff, based on the R0R1 instruction pointer. The pseudo instruction codes ;are executed as CPU native assembly instructions by calling a subroutine and jumping to address $100+code. At the end of the ;execution of a pseudo instruction, the pseudo code instruction pointer is incremented by 1. If the previous pseudo instruction ;returned ACC with 0 value, the execution is continued from the incremented address, otherwise the data byte on the incremented ;address is understood as a pseudo code jump address, which is conditionally executed. If the previously returned CY was 1, it is ;copied into the instruction pointer, otherwise it is skipped by increasing the instruction pointer again. ;---------------------------------------------------------------------------------------------------------------------------------- 04b 11 4f jcnb_04b: jcn TZ $04f ;wait for the inactive printer drum sector signal 04d 50 b0 iszb_04d: jms $0b0 ;keyboard handling 04f 26 20 jcnf_04f: fim 3< $20 051 28 10 fim 4< $10 053 53 00 jms $300 ;fetch the pseudo instruction code into R2R3 055 51 00 jms $100 ;execute the associated routine 057 71 5a jcnb_047: isz 1 $05a ;inc R0R1, pseudo code instruction pointer 059 60 inc 0 05a 14 4b iszf_05a: jcn AZ $04b ;jump back, if ACC returned by the pseudo instruction was 0 05c f7 tcc 05d 14 57 jcn AZ $057 ;if CY returned by the pseudo instruction was 0, R0R1 is incremented again ;(the jump address is skipped) 05f 43 02 jun $302 ;if CY was set to 1, implement it as a pseudo code jump instruction... 061 d4 jcnb_061: ldm 4 ;piece of code, executed when no row is active in the actual column of keyboard matrix 062 40 d4 jun $0d4 ;4 is the number of buttons in one column ; i4003 shift register handling ; ; bit0=keyboard matrix shifter clock ; bit1=shifter data ; bit2=printer hammer shifter clock 064 d3 subr_064: ldm 3 ;shift high bit into keyboard shifter (Clock=1, Data=1) 065 29 subr_065: src 4< ;R8R9 selects ROM0 066 e2 wrr ;assert shifter 067 d0 ldm 0 ;Clock=0, Data=0 068 e2 jcnb_068: wrr ;assert shifter 069 c0 bbl 0 ; ;Synchronization with the spinning printer drum. Called strictly after the sector signal becomes inactive. Increment R12, the ;printer sector counter. Wait for a short time, and check the state of the index signal. If it is active, clear R12. ; 06a 6c subr_06a: inc 12 ;R12, the printer drum sector counter is incremented 06b 22 20 fim 1< $20 06d 23 iszb_06d: src 1< 06e ea rdr ;read ROM2 input port 06f f6 rar ;index signal is rotated into CY 070 73 6d isz 3 $06d ;jump back 15 times (short wait) 072 1a 76 jcn C0 $076 ;jump, if index signal is inactive 074 f0 clb 075 bc xch 12 ;clear R12, the printer drum sector counter 076 c0 jcnf_076: bbl 0 ; ;piece of code for the keyboard matrix handling, buffer clearing, when two buttons are pressed at the same time ; 077 a9 jcnb_077: ld 9 ;check the status of the current row 078 14 d9 jcn AZ $0d9 ;go back to the next row, if no button is pressed in this column ;continue, if two buttons are simultaneously pressed in different columns 07a 28 00 jxnb_07a: fim 4< $00 ;clear the keyboard buffer 07c f0 clb 07d 51 4a jms $14a ;initialize the keyboard buffer (clear KR.M0-F, KR.S0-1) 07f 40 f7 jun $0f7 ;jump to exit from keyboard handling ; ; Keyboard decode table for translating the keyboard scan code into function code and parameter ; ; upper half byte=parameter ; lower half byte=function code ; 081 bb ;CM 082 c7 ;RM 083 63 ;M- 084 53 ;M+ 085 19 ;SQRT 086 1a ;% 087 68 ;M=- 088 58 ;M=+ 089 05 ;diamond 08a 41 ;/ 08b 31 ;* 08c 18 ;= 08d 22 ;- 08e 12 ;+ 08f 05 ;another diamond 090 0c ;000 091 9d ;9 092 6d ;6 093 3d ;3 094 bd ;. 095 8d ;8 096 5d ;5 097 2d ;2 098 06 ;00 099 7d ;7 09a 4d ;4 09b 1d ;1 09c 0d ;0 09d ad ;S 09e a4 ;EX 09f 0e ;CE 0a0 bf ;C ; ; table for translating the function code into pseudo code entry address ; ;Note: table theoretically is started at address 0a0, but the first entry is not used 0a1 06 ;div/mul 0a2 91 ;+/- 0a3 98 ;M+/M- 0a4 f1 ;Ex 0a5 cd ;diamond 0a6 d7 ;00 0a7 fd ;RM 0a8 8a ;=,M=+/M=- 0a9 05 ;Sqrt 0aa 61 ;% 0ab f9 ;CM 0ac d7 ;000 0ad d7 ;digit 0ae ca ;CE 0af c5 ;C ;---------------------------------------------------------------------------------------------------------------------------------- ;Keyboard handling ; ;This part checks the first 8 columns of the keyboard matrix, and calculates the scan code based on the position of the button in ;the matrix. When a button is pressed, the scan code is placed into the keyboard buffer stored in KR. When two buttons are pressed ;or held down simultaneously, the buffer is cleared. ; ;This is synchronized to the printer drum rotation and is called strictly after checking the sector signal (TEST pin of CPU). ;Typically after a "lback1: jcn TZ lback1" loop, so the sector signal just became inactive, and terminated after a ;lback2: jcn TN lback2 loop, when the sector signal becomes active. ;---------------------------------------------------------------------------------------------------------------------------------- 0b0 50 6a subr_0b0: jms $06a ;R12 synchronization with the printer drum sectors 0b2 28 07 fim 4< $07 0b4 50 64 iszb_0b4: jms $064 ;shift one high bit into keyboard shifter 0b6 79 b4 isz 9 $0b4 ;loop back, (gives 9 pulses, deactivates the entire keyboard shifter except last column) 0b8 26 18 fim 3< $18 ;R6=1 for selecting ROM1, R7=loop counter (16-8=8 columns are checked) 0ba 22 00 fim 1< $00 ;Clear R2 and R3, scan code counter 0bc d1 ldm 1 0bd 50 65 jms $065 ;shift one low bit into keyboard shifter (select the first column, other columns are high) 0bf 27 iszb_0bf: src 3< 0c0 ea rdr ;Read ROM1 port, rows of the selected keyboard column 0c1 fc kbp ;Decode the lines (0->0, 1->1, 2->2, 4->3, 8->4, rest->15) 0c2 b9 xch 9 ;place the code into R9 0c3 a2 ld 2 0c4 f5 ral ;R2 bit3 is shifted into CY, highest bit of possible scan code 0c5 f7 tcc 0c6 1c 77 jcn AN $077 ;jump, if a pressed button has already been collected (and may continue at $0d9) 0c8 a9 ld 9 0c9 79 cd isz 9 $0cd ;inc R9, and jump, if maximum one column is active 0cb 40 7a jun $07a ;jump to clear the buffer and exit from the keyboard processing ;(two buttons are pressed in the same column) 0cd 14 61 iszf_0cd: jcn AZ $061 ;jump, if none of the lines are active (ACC=4, and continue at $0d4) 0cf b2 xch 2 0d0 f5 ral 0d1 fa stc 0d2 f6 rar 0d3 b2 xch 2 ;R2.bit3 is set to high (indicating, that a button is pressed) 0d4 83 junf_0d4: add 3 ;ACC=1..4, if line is decoded, or 4, if no line is active 0d5 b3 xch 3 ;adding ACC to scan code counter, R3=lower half 0d6 d0 ldm 0 0d7 82 add 2 ;adding carry to the upper half 0d8 b2 xch 2 0d9 50 64 jcnf_0d9: jms $064 ;shift one high bit into keyboard shifter (select the next column in the matrix) 0db 77 bf isz 7 $0bf ;loop back, check the next columns of the matrix 0dd 29 src 4< ;select the keyboard buffer 0de a2 ld 2 ;R2.bit3 indicates, if a button is pressed 0df f5 ral 0e0 f7 tcc 0e1 14 f8 jcn AZ $0f8 ;jump, if no button is pressed (clear the keyboard pressing status) 0e3 ef rd3 ;check KR.S3, the keyboard pressing status 0e4 f2 iac ;ACC=1,CY=0 (when KR.S3=15) or ACC=0,CY=1 (when KR.S3=0) 0e5 f7 tcc ;ACC=0 or ACC=1 0e6 1c f7 jcn AN $0f7 ;jump, if the keyboard pressing status is 15 (a button is held down) 0e8 ec rd0 ;a button is pressed right now, it should be placed into the keyboard buffer 0e9 b9 xch 9 ;R9=KR.S0, the keyboard buffer pointer 0ea 29 src 4< 0eb a3 ld 3 0ec e0 wrm ;write R3 (lower half of the scan code) into the buffer 0ed 69 inc 9 0ee 29 src 4< 0ef e9 rdm ;read next byte, and if it is not 0, then 0f0 1c 7a jcn AN $07a ;jump to clear the buffer and exit from the keyboard processing (overrun case) 0f2 a2 ld 2 0f3 e0 wrm ;write R2 (upper half of the scan code) into the buffer 0f4 69 inc 9 0f5 a9 ld 9 0f6 e4 wr0 ;KR.S0=R9 -> store the incremented buffer pointer 0f7 df jxnf_0f7: ldm 15 ;KR.S3=15 -> a button is held down 0f8 e7 jcnf_0f8: wr3 ;write the keyboard pressing status 0f9 28 00 fim 4< $00 ;exit from the keyboard check, initialize R6R7 -> WR, R8R9 -> KR 0fb 26 10 fim 3< $10 0fd 19 fd jcnb_0fd: jcn TN $0fd ;wait for the active printer drum sector signal 0ff c0 bbl 0 100 33 subr_100: jin 1< ;jump to the pseudo instruction code associated routine ; ; Store the working register into another register. ; ;BPC_01: MOV IR,WR ;BPC_02: MOV CR,WR ;BPC_03: MOV RR,WR ;BPC_04: MOV DR,WR 101 a5 vmbc_101: ld 5 ;target=IR (function code+4), load function code into ACC 102 f2 vmbc_102: iac ;target=CR 103 f2 vmbc_103: iac ;target=RR 104 86 vmbc_104: add 6 ;target=DR 105 b8 xch 8 ;source and destination is exchanged 106 b6 xch 6 107 41 0e jun $10e ;jump to copy numbers ; ; Load the content of a register into the working register ; ;BPC_09: MOV WR,MR ;BPC_0A: MOV WR,TR ;BPC_0B: MOV WR,SR ;BPC_0C: MOV WR,CR ;BPC_0D: MOV WR,RR ;BPC_0E: MOV WR,DR 109 66 vmbc_109: inc 6 ;source=MR 10a 66 subr_10a: inc 6 ;source=TR 10b 66 vmbc_10b: inc 6 ;source=SR 10c 66 vmbc_10c: inc 6 ;source=CR 10d 66 vmbc_10d: inc 6 ;source=RR 10e 27 junf_10e: src 3< ;source=DR, move number into another number, NR(R8)=NR(R6) 10f e9 vmbc_10f: rdm 110 29 vmbc_110: src 4< 111 e0 wrm ;number is moved digit by digit 112 69 inc 9 113 77 0e isz 7 $10e ;loop for all digits 115 27 src 3< ;copy status character 0-1 116 ec rd0 ;plus/minus sign 117 b3 xch 3 118 ed rd1 ;place of digit point 119 29 src 4< 11a e5 wr1 11b b3 xch 3 ;R3=place of plus/minus sign 11c e4 vmbc_11c: wr0 11d c0 bbl 0 ; ; Adding two numbers ; ;BPC_1E: ADD IR,WR ;BPC_21: ADD DR,WR 11e d4 vmbc_11e: ldm 4 ;target=IR (function code + 4) 11f 85 add 5 120 b6 xch 6 121 29 iszb_121: src 4< ;NR(R6)=NR(R6)+NR(R8), two numbers are added digit by digit 122 e9 rdm 123 27 src 3< 124 eb adm ;adding and daa correcting one digit 125 fb daa 126 e0 wrm 127 69 inc 9 128 77 21 isz 7 $121 ;loop for all digits 12a f1 clc 12b c0 bbl 0 ; ; Subtracting two numbers ; ;BPC_2C: SUB WR,IR, jump, if result is not negative (R13 is incremented at jump) ;BPC_31: SUB IR,WR, jump, if result is not negative (R13 is incremented at jump) ;BPC_34: SUB DR,WR, jump, if result is not negative (R13 is incremented at jump) 12c d4 vmbc_12c: ldm 4 12d 85 add 5 12e b8 xch 8 ;source is set to function code + 4 12f 41 33 jun $133 ;target is set to 1 131 d4 junf_131: ldm 4 132 85 add 5 133 b6 junf_133: xch 6 ;target is set to function code + 4 134 fa vmbc_134: stc ;NR(R6)=NR(R6)-NR(R8), two numbers are subtracted digit by digit 135 f9 iszb_135: tcs ;ACC=9+CY (10 or 9), CY=0 136 29 vmbc_136: src 4< 137 e8 sbm ;ACC=10(9)-NR(R8).M(R9) 138 f1 clc 139 27 src 3< 13a eb adm ;ACC=NR(R6).M(R7)+(10(9)-NR(R8).M(R9)) 13b fb daa 13c e0 vmbc_13c: wrm ;NR(R6).M(R7)=daa adjusted result 13d 69 inc 9 13e 77 35 isz 7 $135 ;loop for all digits 140 1a 43 vmbc_140: jcn C0 $143 ;skip R13 incrementing, if last digit does not generate carry 142 6d inc 13 143 c1 jcnf_143: bbl 1 ;prepare pseudo code jump ; ; clear a register including status character 0 and 1 ; ;BPC_44: CLR MR (MR=0) ;BPC_45: CLR TR (TR=0) ;BPC_46: CLR SR (SR=0) ;BPC_47: CLR CR (CR=0) ;BPC_48: CLR RR (RR=0) ;BPC_49: CLR DR (DR=0) ;BPC_4A: CLR WR (WR=0) 144 68 vmbc_144: inc 8 ;target=MR 145 68 inc 8 ;target=TR 146 68 subr_146: inc 8 ;target=SR 147 68 vmbc_147: inc 8 ;target=CR 148 68 vmbc_148: inc 8 ;target=RR 149 68 subr_149: inc 8 ;target=DR 14a 29 subr_14a: src 4< ;NR(R8).M(R9)=ACC (=0) 14b e0 wrm ;clearing the number digit by digit 14c 79 4a isz 9 $14a ;loop for all digits 14e e4 wr0 ;clear sign 14f e5 wr1 ;clear place of digit point 150 c0 bbl 0 ; ; On digit left shift. The number is shifted through R13 ; ;BPC_51: SHL RR one digit left shift of RR with R13 ;BPC_52: SHL DR one digit left shift of DR with R13 ;BPC_53: SHL WR one digit left shift of WR with R13 151 68 vmbc_151: inc 8 ;target=RR 152 68 vmbc_152: inc 8 ;target=DR 153 29 iszb_153: src 4< 154 e9 rdm ;load current digit into ACC 155 bd xch 13 ;previous and current digit is exchanged between ACC and R13 156 e0 wrm ;save the previous digit 157 79 53 isz 9 $153 loop for next digits 159 c0 bbl 0 ; ; On digit right shift. The number is shifted through R13 ; ; ;BPC_5A: SSR RR one digit right shift of 14 digit length RR with R13 (R13 is shifted into digit 14) ;BPC_5D: SHR RR one digit right shift of RR with R13 (0 is shifted from right) ;BPC_5E: SHR DR one digit right shift of DR with R13 (0 is shifted from right) ;BPC_5F: SHR WR one digit right shift of WR with R13 (0 is shifted from right) 15a de vmbc_15a: ldm 14 ;only 14 digits are shifted 15b b9 xch 9 15c ad ld 13 15d 68 vmbc_15d: inc 8 ;target=RR 15e 68 vmbc_15e: inc 8 ;target=DR 15f bd subr_15f: xch 13 ;one digit right shift of NR(R8).M(R9) with R13 160 a9 ld 9 161 f8 jcnb_161: dac ;decrement R9, loop counter 162 f1 clc 163 b9 xch 9 164 29 src 4< 165 e9 rdm ;load current digit into ACC 166 bd xch 13 ;previous and current digit is exchanged between ACC and R13 167 e0 wrm ;save the previous digit 168 a9 ld 9 169 1c 61 jcn AN $161 ;loop for next digits 16b c0 bbl 0 ; ; checking, whether status character 2 of certain RAM register is 0. CY=1, if it is 0 ; ;BPC_6C: JPC MODENN jump, if RR.S2=0 ;BPC_6D: JPC MOPN jump, if DR.S2=0 ;BPC_6E: JPC NTRUNC jump, if WR.S2=0 16c 68 vmbc_16c: inc 8 ;source=RR 16d 68 vmbc_16d: inc 8 ;source=DR 16e 29 vmbc_16e: src 4< ;source=WR 16f ee rd2 ;read status character 2 170 f8 dac ;decrement, only 0->15 leaves CY=0 171 f3 cmc ;complement carry, the pseudo jump condition 172 c1 bbl 1 ;prepare pseudo code jump ; ; read bit 0 of status character 2 of certain RAM into CY ; ;BPC_73: JPC OVFL jump, if SR.S2.bit0>0 ;BPC_74: JPC MENTDP jump, if CR.S2.bit0>0 ;BPC_75: JPC MODEMD jump, if RR.S2.bit0>0 ;BPC_76: JPC MOPMUL jump, if DR.S2.bit0>0 ;BPC_77: JPC ROUND jump, if WR.S2.bit0>0 173 68 subr_173: inc 8 ;source=SR 174 68 vmbc_174: inc 8 ;source=CR 175 68 vmbc_175: inc 8 ;source=RR 176 68 vmbc_176: inc 8 ;source=DR 177 29 vmbc_177: src 4< 178 ee rd2 ;read status character 2 179 f6 rar ;rotate bit 0 into carry, the pseudo jump condition 17a c1 bbl 1 ;prepare pseudo code jump ; ; read bit 3 of status character 2 of certain RAM into CY ; ;BPC_7B: JPC MOPCONST jump, if DR.S2.bit3>0 17b 27 vmbc_17b src 3< 17c ee vmbc_17c: rd2 ;read status character 2 17d f5 ral ;rotate bit 3 into CY, the pseudo jump condition 17e c1 bbl 1 ;prepare pseudo code jump ; ; clear status character 2 of certain number ; ;BPC_7F: CLR OVFL clear SR.S2 ;BPC_82: CLR MOP clear DR.S2 17f 66 vmbc_17f: inc 6 ;target=SR 180 66 subr_180: inc 6 ;target=CR 181 66 junb_181: inc 6 ;target=RR 182 27 junb_182: src 3< ;target=DR 183 e6 wr2 ;write status character 2 of target (in fact it is cleared as ACC=0) 184 c0 bbl 0 ; ; set status character 2 to a value ; ;BPC_85: SET OVFL SR.S2=1, set overflow ;BPC_86: SET MENTDP CR.S2=1, set that number is entered with digit point ;BPC_87: SET MODEMD RR.S2=1, set that number is used for mul/div operation ;BPC_8A: SET MODEAS RR.S2=8, set that number is used for add/sub operation ;BPC_8D: SET MOPPAR DR.S2=function parameter, set the multiplication/division from function parameter ;BPC_90: SET MOPCONST DR.S2.bit3=1, set that multiply/divide operation is with constant value 185 66 vmbc_185: inc 6 ;target=SR 186 66 vmbc_186 inc 6 ;target=CR 187 d1 vmbc_187: ldm 1 ;target=RR 188 41 81 jun $181 ;set NR(R6+1).S2=1 18a d8 vmbc_18a: ldm 8 18b 41 81 jun $181 ;set NR(R6+1).S2=8 18d a4 vmbc_18d: ld 4 ;ACC = parameter 18e 41 82 jun $182 ;set NR(R6).S2=parameter 190 27 vmbc_190: src 3< 191 ee rd2 ;set high bit of NR(R6).S2 to 1 192 f5 ral 193 fa stc 194 f6 rar 195 e6 wr2 196 c0 bbl 0 ; ; checking, whether the number contains any nonzero digit ; ;BPC_97: JPC NBIG_IR jump, if digits 14-15 of IR does not contain any value ;BPC_9A: JPC NBIG_WR jump, if digits 14-15 of WR does not contain any value ;BPC_9E: CLR DIGIT + JPC NBIG_DR jump, if digits 14-15 of DR does not contain any value. R13=0 ;BPC_A0: CLR DIGIT + JPC ZERO_DR clear R13 and jump, if DR does not contain any value ;BPC_A2: JPC ZERO_WR jump, if WR does not contain any value 197 d4 vmbc_197: ldm 4 198 85 add 5 ;ACC=function code or function code + 4 199 b8 xch 8 ;R8 points to IR 19a de vmbc_19a: ldm 14 19b b9 xch 9 ;R9=14 19c 41 a2 jun $1a2 19e de vmbc_19e: ldm 14 19f b9 xch 9 ;R9=14 and ACC=previous R9 (=0) 1a0 bd subr_1a0: xch 13 ;save ACC=0 into R13 1a1 68 inc 8 1a2 29 subr_1a2: src 4< ;check whether the number contains any digit. Return jump with CY=1, if the number is empty 1a3 df vmbc_1a3: ldm 15 1a4 eb adm ;number is added in binary mode to the maximum value digit by digit 1a5 79 a2 isz 9 $1a2 ;loop for the rest of digits ;BPC_A7: JMP Unconditional jump 1a7 f3 vmbc_1a7: cmc ;negate the pseudo jump condition 1a8 c1 bbl 1 ;prepare pseudo code jump ;BPC_A9: JPC BIG_DIGIT Jump, if R13>9 1a9 ad vmbc_1a9: ld 13 ;load R13 1aa fb vmbc_1aa: daa ;set CY=1, the pseudo jump condition, if R13>9 1ab c1 vmbc_1ab: bbl 1 ;prepare pseudo code jump ;BPC_AC: JPC ZERO_DIGIT + DEC DIGIT decrement R13 and jump, if R13 was 0 before the decrement 1ac ad vmbc_1ac: ld 13 1ad f8 dac ;ACC=decremented R13, will be placed back to R13 ;BPC_AE: CLR DIGIT + JMP clear R13 and jump 1ae bd vmbc_1ae: xch 13 1af f3 cmc ;negate the pseudo jump condition 1b0 c1 bbl 1 ;prepare pseudo code jump ;BPC_B1: JPC NEWOP jump, if function code < 8 (new add/sub/mul/div operation) 1b1 d7 vmbc_1b1: ldm 7 1b2 95 sub 5 ;R5=function code; set CY=1, the pseudo jump condition, if R5<8 1b3 c1 bbl 1 ;prepare pseudo code jump ;BPC_B4: JPC MEMOP jump, if function parameter > 3 (new memory operation) 1b4 dc vmbc_1b4: ldm 12 1b5 84 add 4 ;R4=function parameter; set CY=1, the pseudo jump condition, if R4>3 1b6 c1 bbl 1 ;prepare pseudo code jump ;BPC_B7: JPC ROTFC rotate the function code one bit right, jump if the next bit is 0 1b7 a5 vmbc_1b7 ld 5 ;rotate R5=function code with 1 bit right 1b8 f6 rar ;bit 0 is rotated to CY 1b9 b5 xch 5 ;rotated value is saved back 1ba f3 cmc ;complement CY, the pseudo jump condition 1bb c1 bbl 1 ;prepare pseudo code jump ;BPC_BC: JPC ODDPAR jump, if bit0 of parameter>0 1bc a4 vmbc_1bc: ld 4 ;load R4=parameter into ACC 1bd f6 vmbc_1bd: rar ;rotate bit 0 into CY, the pseudo jump condition 1be c1 bbl 1 ;prepare pseudo code jump ;BPC_BF: SET DP_IR set digit point place of indirect register (IR.S1=R11) 1bf d4 vmbc_1bf: ldm 4 1c0 85 add 5 ;ACC=function code + 4 1c1 b8 xch 8 ;set it to target register ;BPC_C2: SET DP_WR set digit point place of working register (WR.S1=R11) 1c2 29 vmbc_1c2: src 4< 1c3 ab ld 11 1c4 e5 wr1 ;write place of digit point 1c5 c0 bbl 0 ;BPC_C6: GET DP_WR get digit point place of working register (R11=WR.S1) 1c6 29 vmbc_1c6: src 4< 1c7 ed rd1 ;read place of digit point 1c8 bb xch 11 1c9 c0 bbl 0 ;BPC_CA: INC DPCNT increment digit point counter (increment R10R11) 1ca 7b cd vmbc_1ca: isz 11 $1cd ;increment lower part, jump, if not zero 1cc 6a inc 10 ;increment upper part 1cd c0 iszf_1cd: bbl 0 ;BPC_CE: JPC NBIG_DPCNT jump, if R10R11<14 ;BPC_CF: JPC ZERO_DPCNT jump, if R10R11=0 1ce dd vmbc_1ce: ldm 13 1cf 9b vmbc_1cf: sub 11 ;subtract the lower part from 13 1d0 f3 cmc 1d1 d0 ldm 0 ;subtract the upper part from 0 1d2 9a sub 10 ;pseudo jump condition is set at no borrow 1d3 c1 vmbc_1d3: bbl 1 ;prepare pseudo code jump ;Pseudo instruction code jump table. Normally pseudo instruction code execution can be directly started on address range $100-$1ff. ;This is a jump table to functions, which are implemented on other pages 1d4 42 d3 vmbc_1d4: jun $2d3 ;BPC_D4: jump, if WR and IR have different sign 1d6 00 nop 1d7 42 94 vmbc_1d7: jun $294 ;BPC_D7: digit functions 1d9 42 a3 vmbc_1d9: jun $2a3 ;BPC_D9: WR=TR, clear SR & TR; recall main total 1db 42 aa vmbc_1db: jun $2aa ;BPC_DB: Set function code=3, and jump 1dd 42 ae vmbc_1dd: jun $2ae ;BPC_DD: decrement R10R11 1df 42 b3 vmbc_1df: jun $2b3 ;BPC_DF: WR.S1=WR.S3, R10R11=difference between required an actual digit point 1e1 42 b9 vmbc_1e1: jun $2b9 ;BPC_E1: digit point counter adjust for division 1e3 42 ca vmbc_1e3: jun $2ca ;BPC_E3: digit point counter adjust for multiplication 1e5 42 de vmbc_1e5: jun $2de ;BPC_E5: Sign of result register for multiplication or division + R13=15 1e7 42 e7 vmbc_1e7: jun $2e7 ;BPC_E7: complement WR.S0 (change the sign of WR) 1e9 42 ec vmbc_1e9: jun $2ec ;BPC_E9: rounding, if R13>4 then increment WR (and R14 too) 1eb 42 46 vmbc_1eb: jun $246 ;BPC_EB: end of printing with advancing the paper and R10R11=0, R14R15=0 1ed 44 00 vmbc_1ed: jun $400 ;BPC_ED: square root (optional) ;BPC_EF: CLR MENT + CLR OVFL + RET clear CR.S2, SR.S2, TR.S2 and exit ;BPC_F1: CLR MODE + CLR MENT + RET clear RR.S2, CR.S2 and exit ;BPC_F3: CLR MODE + RET clear RR.S2 and exit 1ef 51 80 vmbc_1ef: jms $180 ; R6=R6+2, clear status character 2 of NR(R6) 1f1 51 81 vmbc_1f1: jms $181 ; R6=R6+1, clear status character 2 of NR(R6) 1f3 51 81 vmbc_1f3: jms $181 ; R6=R6+1, clear status character 2 of NR(R6) 1f5 2a 00 fim 5< $00 1f7 40 00 jun $000 ;exit from the pseudo code interpreter ;BPC_F9..FF: Printing functions: ;BPC_F9: PRN FPAR,C print number with function parameter and char=11 "C" in last column (not used) ;BPC_FA: PRN FPAR,MEM print number with function parameter and char=12 "M" in last column ;BPC_FB: PRN FPAR,FCODE print number with function parameter and empty character in last column ;BPC_FC: PRN FPAR print number with function parameter and empty character in last column ;BPC_FD: PRN ROUND,FPAR print number with optional rounding char and function parameter in last column ; (determined by R14.bit0: 0=empty, 1=code 7 (rounding up char)) ;BPC_FE: PRN FCODE print number with function code and empty character in last column ;BPC_FF: PRN OVFL print unimplemented number (dots with empty extra columns) 1f9 6f inc 15 ; (R15 will be 9) 1fa 6f vmbc_1fa: inc 15 ; (R15 will be 10) 1fb 6f vmbc_1fb: inc 15 ; (R15 will be 11) 1fc 6f vmbc_1fc: inc 15 ; (R15 will be 12) 1fd 6f vmbc_1fd: inc 15 ; (R15 will be 13) 1fe 6f vmbc_1fe: inc 15 ; (R15 will be 14) 1ff bf vmbc_1ff: xch 15 ; (R15 will be 15) 200 f4 cma 201 bf xch 15 ; R15 is complemented ;setting the printing method, determined by the value in R15 (and R14=rounding) 202 7f 10 isz 15 $210 ;R15 was 15: unimplemented number (overflow/divide by 0) 204 da ldm 10 ;load 10 (code of digit point) 205 51 4a jms $14a ;fill WR with 10s (WR.S0 too: positive number, WR.S1 too: not used) 207 2e ff fim 7< $ff ;R14R15=$FF: last two columns will be empty 209 ba xch 10 ;R10=10: "place of digit point" would generate a point too 20a df ldm 15 20b b9 xch 9 ;R9=15: 14 valid character 20c 29 src 4< 20d e0 wrm ;WR.M15=0 20e 42 2c jun $22c ;jump to start the printing 210 7f 17 iszf_s10: isz 15 $217 ;R15 was 14: number with function code and empty character in last column 212 df ldm 15 213 bf xch 15 ;R15=15 (empty column) 214 a5 ld 5 ;function code 215 42 26 jun $226 ;jump to save ACC into R14 217 d1 iszf_217: ldm 1 218 8f add 15 219 f7 tcc 21a 14 25 jcn AZ $225 ;jump, if R15<13 ; number with function parameter and a character (can be empty) in the last column ;R15 was 13: number with optional rounding char and function parameter in the last column 21c a4 ld 4 21d bf xch 15 ;R15=function parameter 21e be xch 14 ;ACC=R14, set previously by the rounding (0=truncating, 1=rounding up) 21f f6 rar ;CY=R14.bit0 220 f3 cmc ;CY=complement of R14.bit0 221 de ldm 14 222 f6 rar ;ACC=8*CY+7 (7=rounding up char, 15=empty char) 223 42 26 jun $226 ;jump to save ACC into R14 225 a4 jcnf_225: ld 4 ;load parameter into R14 226 be junf_226: xch 14 ;save ACC into R14, code of character in last column 227 29 src 4< 228 ed rd1 229 ba xch 10 ;R10=place of digit point 22a ed rd1 22b bb xch 11 ;R11=place of digit point 22c 11 2c jcnb_22c: jcn TZ $22c ;wait for the inactive printer drum sector signal 22e d2 ldm 2 22f bd xch 13 ;R13=2 230 ec rd0 ;read WR.S0 (sign) 231 f6 rar 232 f7 tcc ;ACC=0 (WR positive) or 1 (WR negative) 233 e1 wmp ;switch the printing color into red in case of WR has minus sign, output to RAM0 port 234 50 b0 jms $0b0 ;keyboard handling 236 68 inc 8 ;R8R9 points to WR again (keyboard handling puts it to KR) ;R6R7 points to WR too 237 6b jcnb_237: inc 11 ;search for the place of the first digit before the digit point, result in R11 238 ab ld 11 239 b9 xch 9 ;R9=points to part, to be checked (started from place of digit point + 1) 23a 51 a2 jms $1a2 ;check, whether the remaining part of the number contains any digit 23c f7 tcc 23d 14 37 jcn AZ $237 ;jump back, if the remaining part of the number is not empty ;by this point: ; R6=1 (select WR) ; R7=0 (used as a digit loop counter) ; R8=1 (select WR) ; R9=0 ; R10=place of digit point ; R11=place of first nonzero digit before the digit point+1 ; R13=2 (used as a printer sector loop counter) ; R14=character code on column before the last column (or 13..15, if that is empty) ; R15=character code on the last column (or 13..15, if that is empty) ;printing: R13 loop counter for the printer sectors 23f 11 3f junb_23f: jcn TZ $23f ;wait for the inactive printer drum sector signal 241 f0 clb 242 e1 wmp ;printer control signals are set to inactive 243 e2 wrr 244 7d 53 isz 13 $253 ;jump to next sector, if there is ;BPC_EB: PRN ADVANCE + CLR DPCNT end of printing with advancing the paper and R10R11=0, R14R15=0 246 2a 0c subr_246: fim 5< $0c ;R10R11=$0C 248 2e 00 fim 7< $00 ;R14R15=$00 24a d8 ldm 8 24b 11 4b jcnb_24b: jcn TZ $24b ;wait for the inactive printer drum sector signal 24d e1 wmp ;Write RAM0 port, first 8, later 3 times 0 (advance the printer paper with a line) 24e 50 b0 jms $0b0 ;Keyboard handling 250 7b 4b isz 11 $24b ;loop back 252 c0 bbl 0 253 50 6a iszf_253: jms $06a ;R12 synchronization with the printer drum sectors 255 b8 xch 8 ;clear R8 ;printing: R7 loop for the digits - filling the printer shifter for one sector 256 dd ldm 13 ;(if R15=13, then the number is empty) 257 9f sub 15 ;ACC=13-R15 258 f1 clc 259 1c 5f jcn AN $25f ;jump, if R15<>13 25b ba xch 10 ;R10=0 (if R15=13, empty columns are printed) 25c df ldm 15 ;(handling of empty columns) 25d 42 61 jun $261 25f 27 jcnf_25f: src 3< ;(handling of valid digits) 260 e9 rdm ;read one digit into ACC 261 77 77 isz 7 $277 ;jump to next digit, if there is still 263 aa ld 10 ;pattern of extra two columns are fetched from R14 and R15 264 1c 68 jcn AN $268 ;jump, if R10<>0 (digit point is already shifted) 266 52 8f jms $28f ;shift one inactive column into printer shifter (CY=0) 268 af jcnf_268: ld 15 269 52 8a jms $28a ;if R15=R12, shift 1 into printer shifter else shift 0 26b ae ld 14 26c 52 8a jms $28a ;if R14=R12, shift 1 into printer shifter else shift 0 26e 19 6e jcnb_26e: jcn TN $26e ;wait for the active printer drum sector signal 270 d2 ldm 2 271 29 src 4< 272 e1 wmp ;fire printer hammers 273 50 b2 jms $0b2 ;Keyboard handling (R7 is cleared!) 275 42 3f jun $23f ;loop back for the next sectors 277 52 8a iszf_277: jms $28a ;if ACC=R12, shift 1 into printer shifter else shift 0 279 aa ld 10 27a 14 83 jcn AZ $283 ;jump, if R10=0 (there is no digit point) 27c 97 sub 7 27d f1 clc 27e 1c 83 jcn AN $283 ;jump, if R10<>R7 (digit point is not in this position) 280 da ldm 10 ;shift the digit point into the shifter 281 52 8a jms $28a ;if R12=10, shift 1 into printer shifter else shift 0 283 a7 jcnf_283: ld 7 ;check, whether the loop counter exceeded the number of valid digits 284 9b sub 11 285 f7 tcc 286 14 56 jcn AZ $256 ;loop back for the next valid digits 288 42 5c jun $25c ;loop back for the empty columns 28a 9c subr_28a: sub 12 ;if ACC=R12, shift 1 into printer shifter else shift 0 28b f1 clc 28c 1c 8f jcn AN $28f 28e fa stc 28f d1 subr_28f: ldm 1 ;shift CY into printer shifter 290 f5 ral 291 f5 ral ;ACC=4+2*CY 292 40 65 jun $065 ;shift one low bit into printer shifter ;BPC_D7: DIGIT this function is called, when a digit, "00", "000", digit point or minus sign button is pressed 294 a4 junf_294: ld 4 295 bd xch 13 ;R13=digit 296 26 40 fim 3< $40 298 27 src 3< 299 ee rd2 ;read CR.S2, digit entry mode status 29a 1c a1 jcn AN $2a1 ;Jump, if the calculator is already in digit entry mode 29c d8 ldm 8 29d e6 wr2 ;put 8 into the digit entry mode status 29e f0 clb 29f 51 4a jms $14a ;clear WR, WRS0, WRS1 2a1 41 c6 jun $1c6 ;R11=WR.S1, place of digit point ;BPC_D9: MOV WR,TR + CLR TR + CLR SR recall main total (WR=TR, clear SR & TR) 2a3 51 0a junf_2a3: jms $10a ;WR=TR 2a5 51 46 jms $146 ;clear SR (including S0 and S1) 2a7 51 49 jms $149 ;clear TR (including S0 and S1) 2a9 c0 bbl 0 ;BPC_DB: SET MRMFUNC + JMP set function code=3 (memory function), and jump 2aa d3 junf_2aa: ldm 3 2ab b5 xch 5 ;R5=function code is set to 3 2ac fa stc ;set CY=1, the pseudo jump condition 2ad c1 bbl 1 ;prepare pseudo code jump ;BPC_DD: DEC DPCNT decrement R10R11 2ae d1 junf_2ae: ldm 1 2af b3 xch 3 ;R3=1 2b0 bb xch 11 ;ACC=R11 2b1 42 c2 jun $2c2 ;jump to R10R11 adjust ;BPC_DF: GET DPDIFF WR.S1=WR.S3, set R10R11 to the difference between required an actual digit point 2b3 52 f9 junf_2b3: jms $2f9 ;read the decimal places of WR and DR (R2=DR.S1, R3=WR.S1), DR is not used 2b5 ef rd3 ;read the required decimal places defined by the digit point switch 2b6 e5 wr1 ;set it to WR.S1 2b7 42 c2 jun $2c2 ;jump to R10R11 adjust ;BPC_E1: GET DPCNTDIV digit point counter adjust for division (set R10R11 to DR.S1+(13-R11)-WR.S1) 2b9 52 f9 junf_2b9: jms $2f9 ;read the decimal places of WR and DR (R2=DR.S1, R3=WR.S1) 2bb dd ldm 13 2bc 9b sub 11 ;ACC=13-R11 2bd f1 clc 2be 82 add 2 ;ACC=R2+(13-R11) 2bf ba xch 10 2c0 f7 tcc 2c1 ba xch 10 ;R10=carry ; R10R11 adjust: set R10R11 to the difference between required an actual digit point ; input: ACC=required place of digit point ; R3=place of digit point of the actual number 2c2 93 junf_2c2: sub 3 2c3 bb xch 11 ;R11=ACC-R3 2c4 f3 cmc 2c5 ba xch 10 2c6 99 sub 9 ;borrow is subtracted from the upper half (R9=0) 2c7 ba xch 10 ;R10=R10-(CY) 2c8 f1 clc 2c9 c0 bbl 0 ;BPC_E3: GET DPCNTMUL digit point counter adjust for multiplication ; set R10R11 to the sum of digital places (WR, DR and current in R11) 2ca 52 f9 junf_2ca: jms $2f9 ;read the decimal places of WR and DR (R2=DR.S1, R3=WR.S1) 2cc a3 ld 3 2cd 8b add 11 2ce 82 add 2 2cf bb xch 11 ;R11=R11+R3+R2 2d0 f7 tcc 2d1 ba xch 10 ;R10=0 or 1 2d2 c0 bbl 0 ;BPC_D4: JPC DIFF_SIGN jump, if WR and IR have different sign (either is minus, the other is plus) 2d3 d4 junf_2d3: ldm 4 2d4 85 add 5 2d5 b6 xch 6 ;R6=function code + 4 2d6 27 src 3< 2d7 ec rd0 ;read the sign of IR 2d8 b2 xch 2 2d9 29 src 4< 2da ec rd0 ;read the sign of WR 2db 82 add 2 ;bit0 of result is 0, if both number have the same sign 2dc f6 rar ;rotate bit 0 into CY, the pseudo jump condition 2dd c1 bbl 1 ;prepare pseudo code jump ;BPC_E5: SET DIVMUL_SIGN + MOV DIGIT,15 ; Sign of result register is set based on the WR and DR for multiplication or division ; R13 is set to 15 for loop counting 2de 52 d6 junf_2de: jms $2d6 ;compare WR and DR sign 2e0 f7 tcc 2e1 66 inc 6 ;R6 points to RR 2e2 27 src 3< 2e3 e4 wr0 ;set sign of RR 2e4 df ldm 15 2e5 bd xch 13 ;R13=15, used as "loop end" indicator at divide/multiply 2e6 c0 bbl 0 ;BPC_E7: NEG WR complement sign of working register (change the sign of WR) 2e7 29 junf_2e7: src 4< 2e8 ec rd0 ;read the sign 2e9 f4 cma ;complement it 2ea e4 wr0 ;write back the new sign 2eb c0 bbl 0 ;BPC_E9: ROUNDING increment WR (and R14 too), if R13>4 2ec db junf_2ec: ldm 11 2ed 8d add 13 ;R13 is added to 11 2ee 1a f1 jcn C0 $2f1 ;if R13<5, CY=0, jump to add (??? jump to $2f8 would have been better) 2f0 6e inc 14 ;save also the fact of rounding into R14 2f1 d0 jcnf_2f1: ldm 0 ;Add CY to WR 2f2 29 src 4< 2f3 eb adm 2f4 fb daa ;add carry and decimal digit by digit 2f5 e0 wrm 2f6 79 f1 isz 9 $2f1 ;loop for the next digits 2f8 c0 bbl 0 2f9 27 subr_2f9: src 3< ;read the decimal places of WR and DR (R2=DR.S1, R3=WR.S1) 2fa ed rd1 2fb b2 xch 2 ;R2=DR.S1 2fc 29 src 4< 2fd ed rd1 2fe b3 xch 3 ;R3=WR.S1 2ff c0 bbl 0 300 32 subr_300: fin 1< ;fetch the pseudo instruction code into R2R3 and return 301 c0 bbl 0 302 30 fin 0< ;fetch the jump address, as the new value of pseudo code instruction pointer into R0R1 303 40 4b jun $04b ;jump to the WM code interpreter ;---------------------------------------------------------------------------------------------------------------------------------- ;Detailed analysis of basic pseudo code list. ;---------------------------------------------------------------------------------------------------------------------------------- 305 fn_sqrt: ed ;SQRT (+ JMP num_dpadj) ;square root of WR is placed into RR 306 fn_muldiv: 6c 14 ;JPC MODENN,md_prn2 ;jump, if new number is entered 308 75 0e ;JPC MODEMD,md_prn1 ;jump, if mul or div was the last operation 30a d9 ;MOV WR,TR + CLR TR + CLR SR ;if add or sub was the last operation, then main total is recalled 30b fc ;PRN FPAR 30c a7 0f ;JMP md_exitf 30e md_prn1: fb ;PRN FPAR,FCODE 30f md_exitf: 8d ;SET MOPPAR ;keep the operation (from the parameter) for the next round 310 md_exitc: 04 ;MOV DR,WR ;put the number into DR and CR 311 02 ;MOV CR,WR 312 87 ;SET MODEMD 313 ef ;CLR MENT + CLR OVFL + RET 314 md_prn2: fc ;PRN FPAR 315 6d 0f ;JPC MOPN,md_exitf ;jump, if the other operand is not entered yet 317 7b 0f ;JPC MOPCONST,md_exitf ;jump, at constant calculation (new number for calculation) 319 76 46 ;JPC MOPMUL,mul_start ;jump, if previous operation is multiply ;---------------------------------------------------------------------------------------------------------------------------------- ;dividing: WR <- RR = DR / WR ; ;DR and WR is left adjusted into position WR.M14<>0 and DR.M14<>0, DR is decreased by WR till it becomes negative. WR is added back ;to DR for getting back the smallest non negative DR. The count, how many times it could be decreased gives the next digit of ;result, which is shifted into RR. DR is shifted left for doing the subfunction for the next digit. The same process is repeated ;14 times. Place of digit point of the result is calculated separately. Finally the result from RR is copied to WR. ;---------------------------------------------------------------------------------------------------------------------------------- 31b 8d ;SET MOPPAR ;divide is marked into MOP 31c div_chk0: a2 3c ;JPC ZERO_WR,num_overf ;divide by zero would result overflow 31e 48 ;CLR RR 31f a0 73 ;CLR DIGIT + JPC ZERO_DR,num_res ;if dividend is zero, the result will be zero too 321 e1 ;GET DPCNTDIV ;digit point initialization for divide 322 div_chkDR: 9e 32 ;CLR DIGIT + JPC NBIG_DR,div_lshDR ;rotate DR into leftmost position 324 div_chkWR: 9a 36 ;JPC NBIG_WR,div_lshWR ;rotate WR into leftmost position 326 e5 ;SET DIVMUL_SIGN + MOV DIGIT,15 ;sign of result is set 327 51 ;SHL RR ;15 is shifted into the cleared RR, as a mark for loop end 328 51 ;SHL RR 329 div_loop: 34 29 ;SUB DR,WR + JPC NNEG,div_loop + INC DIGIT ;find, how many times the subtraction can be done 32b 21 ;ADD DR,WR ;adding back the last unneeded subtract 32c 51 ;SHL RR ;next digit of result is shifted into RR 32d a9 3f ;JPC BIG_DIGIT,div_finsh ;if shifted out number>9, end of division 32f 52 ;SHL DR ;next digit (shifted out from RR) is shifted into DR 330 a7 29 ;JMP div_loop 332 div_lshDR: 52 ;SHL DR ;one digit rotate left of DR 333 ca ;INC DPCNT 334 a7 22 ;JMP div_chkDR 336 div_lshWR: 53 ;SHL WR ;one digit rotate left of WR 337 cf 3c ;JPC ZERO_DPCNT,num_overf ;jump if rotate would cause overflow 339 dd ;DEC DPCNT 33a a7 24 ;JMP div_chkWR 33c num_overf: ff ;PRN OVFL ;print overflow 33d 85 ;SET OVFL ;set overflow flag 33e f1 ;CLR MODE + CLR MENT + RET ;exit 33f div_finsh: 5d ;SHR RR ;rotate the number right 340 num_dpadj: ce 73 ;JPC NBIG_DPCNT,num_res ;jump, if the result contains acceptable number of digits 342 dd ;DEC DPCNT ;otherwise shift the number to right 343 5d ;SHR RR ;Note: the place of this instruction could have been saved, 344 a7 40 ;JMP num_dpadj ; if the jump would go back to div_finsh ;---------------------------------------------------------------------------------------------------------------------------------- ;multiplication: WR <- RR = DR * WR ; ;As starting WR is copied to RR and DR copied to WR. DR is cleared. ;DR and RR is shifted right. Last digit of RR is placed into R13, WR is added R13 times to DR. The process is repeated 14 times. ;Two 14 digit operand produces maximum 28 digit result. For us the most significant digits are interesting. Therefore the 28 digit ;result is rotated towards the lower digits, till the upper 14 digits contain nonzero digits, the place of digit point is counted ;in R10 and R11. After rotate the result is finally copied to WR. ;---------------------------------------------------------------------------------------------------------------------------------- 346 mul_start: 8d ;SET MOPPAR ;multiplication is marked in MOP 347 mul_st2: 03 ;MOV RR,WR 348 e3 ;GET DPCNTMUL ;digit point initialization for multiply 349 e5 ;SET DIVMUL_SIGN + MOV DIGIT,15 ;sign of result is set 34a 0e ;MOV WR,DR 34b 49 ;CLR DR 34c 52 ;SHL DR ;shift R13=15 into DR, but it is immediately shifted into RR 34d mul_loopn: 5e ;SHR DR ;DR-RR is shifted right 34e 5a ;SSR RR 34f a9 56 ;JPC BIG_DIGIT,mul_shres ;jump if R13=15 was shifted out (exit from the loop) 351 mul_loopd: ac 4d ;JPC ZERO_DIGIT,mul_loopn + DEC DIGIT ;multiply the number with one digit 353 21 ;ADD DR,WR ;finally DR=DR+R13*WR 354 a7 51 ;JMP mul_loopd 356 mul_shres: a0 40 ;CLR DIGIT + JPC ZERO_DR,num_dpadj ;rotate nonzero digits from DR to RR 358 cf 3c ;JPC ZERO_DPCNT,num_overf ;jump if overflow occurred 35a 5e ;SHR DR ;DR-RR is shifted right 35b 5a ;SSR RR 35c dd ;DEC DPCNT 35d a7 56 ;JMP mul_shres 35f dp_mark: 86 ;SET MENTDP ;digit point flag 360 f3 ;CLR MODE + RET 361 fn_percnt: fe ;PRN FCODE 362 ca ;INC DPCNT ;increment the digit point place counter by 2 363 ca ;INC DPCNT 364 a7 67 ;JMP num_md 366 num_prm: fe ;PRN FCODE 367 num_md: 7b 6f ;JPC MOPCONST,num_mul2 ;jump at const divide/multiply 369 90 ;SET MOPCONST 36a num_mul1: 76 47 ;JPC MOPMUL,mul_st2 ;jump to multiply, if previous operation is multiply 36c 02 ;MOV CR,WR ;save the divisor for constant divide 36d a7 1c ;JMP div_chk0 ;jump to divide 36f num_mul2: 04 ;MOV DR,WR ;save the number into DR 370 0c ;MOV WR,CR ;recall previous number from CR 371 a7 6a ;JMP num_mul1 ;jump to divide or multiply 373 num_res: 0d ;MOV WR,RR ;copy the RR result to WR 374 c2 ;SET DP_WR ;set the digit point position from R10R11 375 b1 10 ;JPC NEWOP,md_exitc ;jump to exit at new mul and div operation 377 b4 7b ;JPC MEMOP,num_adj ;jump to adjust at M=+/M=- 379 6e 9e ;JPC NTRUNC,num_pra2 ;jump to result print, if digit point should not be adjusted 37b num_adj: df ;GET DPDIFF ;WR.S1=WR.S3, set R10R11 to the difference between required an actual digit point ;Rotate the number into the required digit point place 37c num_rotl: cf 9a ;JPC ZERO_DPCNT,num_pra1 ;jump, if number is at the right digit point place 37e ce 84 ;JPC NBIG_DPCNT,num_lrot 380 ca ;INC DPCNT ;Rotate right 381 5f ;SHR WR 382 a7 7c ;JMP num_rotl 384 num_lrot: dd ;DEC DPCNT ;Rotate left 385 53 ;SHL WR 386 9a 7c ;JPC NBIG_WR,num_rotl 388 a7 3c ;JMP num_overf ;print overflow 38a fn_memeq: 6c 66 ;JPC MODENN,num_prm ;jump, if new number is entered 38c 75 66 ;JPC MODEMD,num_prm ;jump, if there is started mul/div operation 38e d9 ;MOV WR,TR + CLR TR + CLR SR ;recall main total 38f a7 98 ;JMP fn_memadd ;jump to add functions ;entry address at add or subtract button 391 fn_addsub: 6c 98 ;JPC MODENN,fn_memadd ;jump, if new number is enterer 393 75 97 ;JPC MODEMD,clr_md ;jump, if there is started mul/div operation 395 a7 98 ;JMP fn_memadd ;jump to add functions 397 clr_md: 82 ;CLR MOP ;ignore previous mul/div operation 398 fn_memadd: ae 7b ;CLR DIGIT + JMP num_adj ;jump to adjust the number to the required digits 39a num_pra1: b1 aa ;JPC NEWOP,num_pra3 ;jump at new add/sub operation 39c 77 a3 ;JPC ROUND,num_round ;jump to rounding, if rounding switch is in that position 39e num_pra2: fd ;PRN ROUND,FPAR 39f eb ;PRN ADVANCE + CLR DPCNT 3a0 b4 a8 ;JPC MEMOP,mem_add ;jump to change the function code at M=+/M=-/M+/M- 3a2 f1 ;CLR MODE + CLR MENT + RET 3a3 num_round: e9 ;ROUNDING ;do the rounding based on the last shifted out digit in R13 3a4 9a 9e ;JPC NBIG_WR,num_pra2 ;may generate overflow too 3a6 a7 3c ;JMP num_overf ;print overflow 3a8 mem_add: db ab ;SET MEMFUNC + JMP do_prpadd ;Set M+/M- function code ;---------------------------------------------------------------------------------------------------------------------------------- ;add/subtract functions: ; ;By this point, numbers are shifted into the place determined by the digit point switch, thus no shifting is needed. ; ;!!! Note, if the digit point switch is changed during an operation, the numbers are incorrectly added/subtracted. ; ; function code parameter pre1 operation1 pre2 operation2 ;+ 2 1 RR=WR TR=TR+WR WR=RR SR=SR+WR ;- 2 2 RR=WR TR=TR-WR WR=RR SR=SR-WR ;M+ (M=+) 3 5 RR=WR MR=MR+WR ;M- (M=-) 3 6 RR=WR MR=MR-WR ;---------------------------------------------------------------------------------------------------------------------------------- 3aa num_pra3: fc ;PRN FPAR 3ab do_prpadd: c6 ;GET DP_WR 3ac do_addsub: 03 ;MOV RR,WR 3ad bc b0 ;JPC ODDPAR,skp_neg ;skip negate the number at add 3af e7 ;NEG WR ;negate the number at sub (convert it to add) 3b0 skp_neg: d4 b7 ;JPC DIFF_SIGN,do_sub ;jump, when adding a negative and a positive number 3b2 1e ;ADD IR,WR ;ADD - may generate overflow 3b3 97 bd ;JPC NBIG_IR,do_next ;jump, if there is no overflow 3b5 31 3c ;SUB IR,WR + JPC NNEG,num_overf + INC DIGIT ;correct back IR at overflow and jump always 3b7 do_sub: 31 bd ;SUB IR,WR + JPC NNEG,do_next + INC DIGIT ;SUB - never generates overflow 3b9 1e ;ADD IR,WR 3ba 2c bc ;SUB WR,IR + JPC NNEG,do_cont ;always goes to the next instruction 3bc do_cont: 01 ;MOV IR,WR 3bd do_next: 0d ;MOV WR,RR ;take the original number from RR 3be bf ;SET DP_IR ;set the place of digit point 3bf b4 ff ;JPC MEMOP,do_exit ;exit at memory function 3c1 b7 ac ;JPC ROTFC,do_addsub ;do the addsub for the next number, if there is instruction for it 3c3 8a ;SET MODEAS ;mark, that last operation was add or sub 3c4 ef ;CLR MENT + CLR OVFL + RET ;exit ; ;"C" Clear: clear WR,DR,SR,TR and print. it does not clear RR,CR and RR.S2 ; 3c5 fn_clear: 82 ;CLR MOP 3c6 49 ;CLR DR 3c7 d9 ;MOV WR,TR + CLR TR + CLR SR 3c8 4a ;CLR WR 3c9 fc ;PRN FPAR ; ;"CE" Clear: clear WR, RR.S2, CR.S2 ; 3ca fn_cleare: 4a ;CLR WR 3cb 7f ;CLR OVFL 3cc f1 ;CLR MODE + CLR MENT + RET ; ;"Diamond" - subtotal: print the number or the subtotal ; 3cd fn_diamnd: 6c d5 ;JPC MODENN,dm_prn2 ;jump in entry mode, print the number, and close the entry mode 3cf 75 d3 ;JPC MODEMD,dm_prn1 ;jump in mul/div mode, print the number, and init 3d1 0b ;MOV WR,SR ;in add/sub mode, recall the subtotal number from SR and clear SR 3d2 46 ;CLR SR 3d3 dm_prn1: fc ;PRN FPAR 3d4 ef ;CLR MENT + CLR OVFL + RET 3d5 dm_prn2: fd ;PRN ROUND,FPAR 3d6 f1 ;CLR MODE + CLR MENT + RET ;entry address at digit, digit number, minus sign button ; fuction code parameter ;0..9 13 0..9 ;sign 13 10 ;digit point 13 11 ;00 6 0 ;000 12 0 3d7 fn_digit: d7 ;DIGIT ;save digit into R13, place of digit point (WR.S1) into R11 ;at first entry: WR=0, CR.S2=8 3d8 a9 df ;JPC BIG_DIGIT,dig_dpsgn ;jump at digit point, minus sign 3da dig_numsh: 53 ;SHL WR ;rotate the number into WR 3db 9a e3 ;JPC NBIG_WR,dig_chkdp ; jump, if there is now overflow 3dd 5f ;SHR WR ;at overflow, rotate back the number (additional digits are lost) 3de f3 ;CLR MODE + RET ;mark that new number is entered since the last operation, and exit 3df dig_dpsgn: bc 5f ;JPC ODDPAR,dp_mark ;digit point button is pressed 3e1 e7 ;NEG WR ;minus sign button is pressed 3e2 f3 ;CLR MODE + RET ;mark that new number is entered since the last operation, and exit 3e3 dig_chkdp: 74 e8 ;JPC MENTDP,dig_incdp ;if digit point is already entered, jump to adjust it 3e5 a7 ee ;JMP dig_nextd 3e7 00 ;(unimplemented, never used) 3e8 dig_incdp: ca ;INC DPCNT ;adjust the digit point place with one digit more 3e9 ce ed ;JPC NBIG_DPCNT,dig_savdp 3eb dd ;DEC DPCNT ;if already too much digit entered after the digit point, 3ec 5f ;SHR WR ; ignore the new digit 3ed dig_savdp: c2 ;SET DP_WR ;save the place of digit point 3ee dig_nextd: b7 da ;JPC ROTFC,dig_numsh ;function code contains, how many '0's has to be entered yet ;implementation of button '00' and '000' is here 3f0 f3 ;CLR MODE + RET ;mark that new number is entered since the last operation, and exit ; ;Exchange function: CR=WR, WR <- RR <- DR <- WR ; 3f1 fn_ex: fd ;PRN ROUND,FPAR 3f2 02 ;MOV CR,WR ;CR=WR (WR is saved to CR) 3f3 0e ;MOV WR,DR 3f4 03 ;MOV RR,WR ;RR=DR 3f5 0c ;MOV WR,CR 3f6 04 ;MOV DR,WR ;DR=saved WR 3f7 0d ;MOV WR,RR ;WR=RR 3f8 f1 ;CLR MODE + CLR MENT + RET ; ;Clear memory: recall (WR=MR), print and clear (R7=0) ; 3f9 fn_clrmem: 09 ;MOV WR,MR 3fa fa ;PRN FPAR,MEM 3fb 44 ;CLR MR 3fc f1 ;CLR MODE + CLR MENT + RET ; ;Recall memory: recall (WR=MR) and print ; 3fd fn_rm: 09 ;MOV WR,MR 3fe fa ;PRN FPAR,MEM 3ff do_exit: f1 ;CLR MODE + CLR MENT + RET ;---------------------------------------------------------------------------------------------------------------------------------- ; Optional program for making the SQRT function ;---------------------------------------------------------------------------------------------------------------------------------- 400 20 28 fim 0< $28 ;pseudo code entry address of the SQRT function ; ;Similar pseudo code interpreter implementation, like at $04b-05f, just uses the pseudo instruction codes from address range $400-$4ff ; 402 11 06 jcnb_402: jcn TZ $406 ;wait for the inactive printer drum sector signal 404 50 b0 jms $0b0 ;keyboard handling 406 26 20 jcnf_406: fim 3< $20 408 28 10 fim 4< $10 40a 32 fin 1< ;fetch pseudo instruction code into R2R3 40b f0 clb 40c 54 50 jms $450 ;execute the associated routine 40e 71 11 jcnb_40e: isz 1 $411 ;inc R0R1, pseudo code instruction pointer 410 60 inc 0 411 14 02 iszf_411: jcn AZ $402 ;jump back, if ACC returned by the pseudo instruction was 0 413 f7 tcc 414 14 0e jcn AZ $40e ;if CY returned by the pseudo instruction was 0, R0R1 is incremented again 416 30 fin 0< ;if CY was set to 1, read the pseudo code jump address 417 44 02 jun $402 ;jump to continue the pseudo code from the modified address 419 00 00 00 00 00 00 00 ;unused NOPs 420 00 00 00 00 00 00 00 00 ;---------------------------------------------------------------------------------------------------------------------------------- ;Square root pseudo code implementation ;---------------------------------------------------------------------------------------------------------------------------------- 428 sq_start: 51 ;PRN FCODE ;print number with function code (9: SQRT) 429 a7 ;MOV CR,WR ;save the number to the constant register 42a 53 ;CLR RR ;clear result register 42b 61 3e ;JPC ZERO_WR,sq_exit ;jump, if number is zero (the result will be also zero) 42d 65 ;CLR DIGIT + GET DP_WR ;R10R11=place of digit point 42e sq_bshift: 63 44 ;JPC NBIG_WR,sq_lshift ;number is adjusted to the leftmost position 430 9c ;SHR WR ;one digit overshift is corrected back 431 5b ;MOV DR,WR ;remainder (DR) is initialized to the shifted number 432 55 ;CLR WR ;initial subtrahend (WR) is cleared 433 6a 36 ;SET LPCSQRT + SET DPCNTSQRT + JPC EVENDP,sq_loopns ;R15=13, sqrt digit point calculation ;jump if original digit point position was even 435 sq_loopsh: 58 ;SHL DR ;multiplication by 10 of the remaining part ;(and possible additional shift if it is needed) 436 sq_loopns: 7a ;INC WR_POS ;increment the subtrahend (WR from position in R15) by 1 437 5d 41 ;SUB DR,WR + JPC NNEG,sq_rptinc + INC DIGIT;remainder is decremented by the subtrahend (DR=DR-WR) ;and jump, if the result is not negative ;digit counter (R13) is incremented too 439 5f ;ADD DR,WR ;add the subtrahend to get back the last non negative value 43a 85 ;DEC WR_POS ;decrement the subtrahend by one (prepare it for the next round) 43b 57 ;SHL RR ;shift the new digit into the number, R13 is cleared too 43c 98 35 ;JPC NZERO_LPCSQRT,sq_loopsh + DEC LPCSQRT ;decrement R15, and jump, except when R15 becomes 0 ;(next round calculates with one more digit) 43e sq_exit: a9 ;MOV DR,WR (MOV WR,CR ???) ;??? subtrahend is saved (originally it may be WR=CR) 43f 5b ;MOV DR,WR ;??? duplicated, but not disturbing code 440 9f ;CLR MOP + RET_BPC ;return back to basic pseudo code interpreter to address $40 441 sq_rptinc: 7a ;INC WR_POS ;increment the subtrahend by 1 (WR from position in R15) 442 96 36 ;JMP sq_loopns ;jump back 444 sq_lshift: 59 ;SHL WR ;rotate number into left position 445 93 2e ;INC DPCNT + JMP sq_bshift ;increment R10R11, and jump back ;---------------------------------------------------------------------------------------------------------------------------------- 447 00 ;unused NOPs 448 00 00 00 00 00 00 00 00 450 33 jmsf_450: jin 1< ;jump to the pseudo instruction code associated routine 451 41 fe vmbc_451: jun $1fe ;PRN FCODE 453 41 48 vmbc_453: jun $148 ;CLR RR 455 41 4a vmbc_455: jun $14a ;CLR WR 457 68 vmbc_457: inc 8 ;SHL RR 458 68 vmbc_458: inc 8 ;SHL DR 459 41 53 vmbc_459: jun $153 ;SHL WR 45b 41 04 vmbc_45b: jun $104 ;MOV DR,WR 45d 41 34 vmbc_45d: jun $134 ;SUB DR,WR + JPC NNEG + INC DIGIT 45f 41 21 vmbc_45f: jun $121 ;ADD DR,WR 461 41 a2 vmbc_461: jun $1a2 ;JPC ZERO_WR 463 41 9a vmbc_463: jun $19a ;JPC NBIG_WR ;QPC_65: CLR DIGIT + GET DP_WR 465 bd vmbc_465: xch 13 ;clear digit (R13=0) 466 29 src 4< 467 ed rd1 468 bb xch 11 ;R11=WR.S1, get the digit point place of WR 469 c0 bbl 0 ;QPC_6A: SET LPCSQRT + SET DPCNTSQRT + JPC EVENDP ; R15=13, R10R11=(R10R11/2+6+((R10R11 mod 2))), jump, if original R10R11 was even 46a 2e 6d vmbc_46a: fim 7< $6d ;R14=6, R15=13 46c ab ld 11 46d b7 xch 7 ;R7=R11 (save original R11 into R7) 46e ba xch 10 ;ACC=R10 (R10=0 [previous R7]) 46f f6 rar ;CY=R10.bit0 470 ab ld 11 471 f6 rar ;ACC=8*(R10.bit0)+(R11 div 2), CY=(R11 mod 2) 472 8e add 14 ;ACC=8*(R10.bit0)+(R11 div 2)+(R11 mod 2)+6, CY=overflow 473 bb xch 11 ;store it to R11 474 f7 tcc ;ACC=overflow 475 ba xch 10 ;R10=0 or 1 476 b7 xch 7 ;ACC=original R11 477 f6 rar ;CY=(R11 mod 2), rotate bit 0 into CY 478 f3 cmc ;CY=1-(R11 mod 2), negate the pseudo jump condition 479 c1 bbl 1 ;prepare pseudo code jump ;QPC_7A: INC WR_POS increment WR from position in R15 47a af vmbc_47a: ld 15 47b b9 xch 9 ;R9=R15 47c fa stc 47d d0 ldm 0 ;clear ACC 47e 29 src 4< 47f eb adm 480 fb daa ;add carry to number digit by digit 481 e0 wrm 482 79 7d isz 9 $47d ;loop back for the next digits 484 c0 bbl 0 ;QPC_85: DEC WR_POS Decrement WR from position in R15 ; ;inside the loop when R7 is subtracted from ACC and CY is complemented: ; ; CY=0 CY=1 ;------------------------------------------------------- ;ACC 0 ACC=0, CY=0 ACC=15->9, CY=1 ;ACC 1..9 ACC=ACC, CY=0 ACC=ACC-1, CY=0 485 af vmbc_485: ld 15 486 b9 xch 9 ;R9=R15 487 f3 cmc ;at first: set CY=1, later complement the borrow bit 488 29 src 4< 489 e9 rdm ;read next digit from WR 48a 97 sub 7 ;subtract R7 (=0) from it, ACC=ACC+15+(1-CY) 48b 12 8e jcn C1 $48e ;jump, if there is no borrow 48d d9 ldm 9 ;set the number to 9 (BCD adjust) 48e e0 wrm ;write back the result 48f 79 87 isz 9 $487 ;loop back for the next digits 491 f0 clb 492 c0 bbl 0 ;QPC_93: INC DPCNT + JMP Increment digit point counter (R10R11) and unconditional jump ;QPC_96: unconditional jump 493 7b 96 vmbc_493: isz 11 $496 ;inc R11, and skip if result is nonzero 495 6a inc 10 ;inc R10 496 fa vmbc_496: stc ;set CY=1, the pseudo jump condition 497 c1 bbl 1 ;prepare pseudo code jump ;QPC_98: JPC NZERO_LPCSQRT + DEC LPCSQRT decrement R15, and jump, except when R15 was 0 498 af vmbc_498: ld 15 ;decrement R15, sqrt loop counter 499 f8 dac 49a bf xch 15 ;the pseudo jump condition is set, if R15 was nonzero 49b c1 bbl 1 ;prepare pseudo code jump ;QPC_9C: SHR WR Right shift of working register 49c 41 5f vmbc_49c: jun $15f ;one digit right shift of WR with R13 (0 is shifted from left) 49e 00 nop ;QPC_9F: CLR MOP + RET_BPC Clear divide/multiply operation and return back to basic pseudo code interpreter 49f 27 vmbc_49f: src 3< ;clear DR.S2 4a0 e6 wr2 4a1 20 40 fim 0< $40 ;entry address is $40 4a3 26 00 fim 3< $00 4a5 40 4b jun $04b ;jump back to basic pseudo code interpreter ;QPC_A7: MOV CR,WR Move working register into constant register (CR=WR) 4a7 41 02 vmbc_4a7: jun $102 ;CR=WR ;QPC_A9: MOV DR,WR (or MOV WR,CR) ; Move working register into dividend/multiplicand register (DR=WR), but it is very probable that this would be ; move constant register into working register (WR=CR) 4a9 41 04 vmbc_4a9: jun $104 ;Maybe it is "jun $10c" ;(the difference is only one bit in the code - was the source ROM damaged?) 4ab 00 00 00 00 00 ;Unused NOPs 4b0 00 00 00 00 00 00 00 00 4b8 00 00 00 00 00 00 00 00 4c0 00 00 00 00 00 00 00 00 4c8 00 00 00 00 00 00 00 00 4d0 00 00 00 00 00 00 00 00 4d8 00 00 00 00 00 00 00 00 4e0 00 00 00 00 00 00 00 00 4e8 00 00 00 00 00 00 00 00 4f0 00 00 00 00 00 00 00 00 4f8 00 00 00 00 00 00 00 00 ;----------------------------------------------------------------------------------------------------------------------------------