The following program is a tiny program displaying digits of Pi. The decimal version displays PI's 3, no decimal, then is followed by 9303 digits of PI. It is a rewrite of the 125-byte tinypi.com found on Jason Papadopoulos's webpage (see below). I re-wrote it to 113 bytes, and sent him a copy. The enclosed 110-byte program (see archive) is basically the same program, except saves 3 bytes by using INT 29h for screen display.
The enclosed 80-byte hex program is based on a rough version of a C program Jason sent me after I was having trouble converting the decimal version to output PI in hexadecimal. This program outputs 0003, no decimal, followed by another 8184 hexadecimal digits of PI. Notice that the very last digit is wrong. The last 4 digits should be 209C, but are displayed as 209F. There appears to be some sort of rounding error in the algorithm, so in actual usage, you should probably attempt to calculate to calculate two more words than you need to display or use.
Warning: While the decimal program displays digits as it runs, the hexadecimal version will have a long pause, followed by the digits being displayed all at once.
Here is what have been optimized since the 92-byte program, by Mark Andreas
Also, this version is slightly faster, taking 20 seconds on my P166 - compared to 22 seconds for the previous version. I believe the main reasons for the speed difference is that the array is now aligned on even-word boundaries, and that I am now getting a WORD from memory and multiplying it by a register, instead of multiplying by a memory variable. Other changes were:
BX
and DI
registers, allowing STOSW
instead of MOV [BX],AX
following by a couple of INC BX
's.
Unfortunately, the changes made here-in would have no effect on the decimal program, except for the no-size-change where you change:
mov ax,bp ; ax = 10,000 add bx,bx ; align for 2-byte array elements mul [word ptr bx+offset Buffer-2]into:
mov ax,[bx+offset Buffer-2] add bx,bx ; align for 2-byte array elements mul bpThis was one of the things which sped up the new hexadecimal version, so it's probably worth changing the existing decimal-version to use this code change. An archive containg both the decimal, the 92-byte hex and 80-byte hex versions are available for download (6965 bytes, sources, object files and compiled files are included).
.radix 10 syze=2047 ; size is a reserved word in ASM ; 2047 the is largest value which allows ; denom to be less than 65536 ; 2047 words is 8188 digits, of which the 1st 4 digits ; are the 0003 to the left of the decimal, and the last ; digit is probably wrong ; Note: This program waits until after calculating all ; values. This means that there may be a long pause before ; any display. The delay can be over a minute on 486's ; or slower Pentiums. ; Note: It appears that a rounding error creeps into ; the last 1 or 2 words of display. In actual usage, it's ; probably best to calculate 2 more words than displaying. ; In this case, the last displayed WORD should be 209C. terms=syze*16 denom=2*terms+1 .model tiny .code org 100h start: array=terms ; note, am defining array=terms, which ; permits copying values from other registers ; "terms" was used instead of "syze", because ; terms is always even, allowing faster ; access of memory variables mov bx,terms ; terms=syze*16 mov bp,(terms*2)+1 ; denom=2*terms+1 mov si,bx ; si=base of array[], start of printing ; should be "mov si,offset array", but ; am reducing size to allow "mov si,bx" mov di,si ; di=array[0], or same as si register ; sub ax,ax ; assume program begins with AX=0000 ; push di ; it's not required to clear the array[] ; rep stosw ; only effect is that the last digits ; pop di ; error might be slightly different. L1: ; mov di,offset array ; di=array[0] from above & below ; sub cx,cx ; remainder=0, zero from below, ; from above doesn't matter L2: ; dx:ax = dividend ; cx = remainder ; bp = denom ; bx = terms mov dx,cx ;dividend+= (remainder <<16) mov ax,[di] ; ax = array[i] div bp ; dividend = dividend/denom stosw ; array[i] = dividend/denom ; and goes to next array[i] element mov cx,dx ; remainder = dividend mod denom cmp di,offset array+(syze*2) ; bx increases by 2 per array[i] jb L2 L3: ; dx:ax = dividend ; cx = remainder ; bp = denom ; di = terms dec di dec di ; point to beginning of array[i] mov ax,[di] ; array[i] -> ax mul bx ; dx:ax dividend = terms*array[i] add ax,cx ; dividend += remainder adc dx,+00 mov [di],ax ; array[i] = dividend / 65536 ; same as = dividend & 0x0000ffff; mov cx,dx ; remainder = dividend mod 65536 ; same as = dividend >> 16; cmp di,si ja L3 ;------------------------------------ ; this section displays a progress meter ; since program can get slow with high syze value. ; Is safe to alter the AX value when branching up to L1. ; test bl,0ffh ; prints a "*" every 256 loops ; jnz past ; mov al,'*' ; int 29h ; this section adds 9 bytes to the size. past: ;------------------------------------ add word ptr[si],+02 ;array[0]+=2 dec bp dec bp ;denom -=2 dec bx ;terms-- jnz L1 mov bp,syze ; use "syze-1" to avoid printing error ; in last word. mov cl,4 ; display in base 16 hexadecimal Num1: lodsw mov bl,4 Num2: ; Note: you can save 2 byte by changing this routine to ; generate the digit with code valid only for 286's or ; better. replace the "rol ax,cl" with "rol ax,4", ; which allows using CX as the loop instead of ; decrementing through BX. rol ax,cl push ax and al,0fh cmp al,0ah sbb al,69h das ; or al,20h ; add this "or al,20h" for lowercase output. int 29h ; if you need to be able to redirect ; the data to disk, rem out this "int 29h" ; and un-REM the following 3 lines. ; xchg dx,ax ; mov ah,02 ; int 21h pop ax dec bx jnz Num2 dec bp jnz Num1 ret ; Array: ; saving space by setting Array[] to same value as syze ; see top of program. end start