Task

Open a text file and count the occurrences of each letter.

Some of these programs count all characters (including punctuation),
but some only count letters A to Z.




Other tasks related to string operations:

Metrics
 Array length
 String length
 Copy a string
 Empty string  (assignment)
Counting
 Word frequency
 Letter frequency
 Jewels and stones
 I before E except after C
 Bioinformatics/base count
 Count occurrences of a substring
 Count how many vowels and consonants occur in a string
Remove/replace
 XXXX redacted
 Conjugate a Latin verb
 Remove vowels from a string
 String interpolation (included)
 Strip block comments
 Strip comments from a string
 Strip a set of characters from a string
 Strip whitespace from a string -- top and tail
 Strip control codes and extended characters from a string
Anagrams/Derangements/shuffling
 Word wheel
 ABC problem
 Sattolo cycle
 Knuth shuffle
 Ordered words
 Superpermutation minimisation
 Textonyms (using a phone text pad)
 Anagrams
 Anagrams/Deranged anagrams
 Permutations/Derangements
Find/Search/Determine
 ABC words
 Odd words
 Word ladder
 Semordnilap
 Word search
 Wordiff  (game)
 String matching
 Tea cup rim text
 Alternade words
 Changeable words
 State name puzzle
 String comparison
 Unique characters
 Unique characters in each string
 Extract file extension
 Levenshtein distance
 Palindrome detection
 Common list elements
 Longest common suffix
 Longest common prefix
 Compare a list of strings 
 Longest common substring
 Find common directory path
 Words from neighbour ones
 Change e letters to i in words
 Non-continuous subsequences
 Longest common subsequence
 Longest palindromic substrings
 Longest increasing subsequence
 Words containing "the" substring
 Sum of the digits of n is substring of n
 Determine if a string is numeric
 Determine if a string is collapsible
 Determine if a string is squeezable
 Determine if a string has all unique characters
 Determine if a string has all the same characters
 Longest substrings without repeating characters
 Find words which contains all the vowels
 Find words which contains most consonants
 Find words which contains more than 3 vowels
 Find words which first and last three letters are equals
 Find words which odd letters are consonants and even letters are vowels or vice_versa
Formatting
 Substring
 Rep-string
 Word wrap
 String case
 Align columns
 Literals/String
 Repeat a string
 Brace expansion
 Brace expansion using ranges
 Reverse a string
 Phrase reversals
 Comma quibbling
 Special characters
 String concatenation
 Substring/Top and tail
 Commatizing numbers
 Reverse words in a string
 Suffixation of decimal numbers
 Long literals, with continuations 
 Numerical and alphabetical suffixes
 Abbreviations, easy
 Abbreviations, simple
 Abbreviations, automatic
Song lyrics/poems/Mad Libs/phrases
 Mad Libs
 Magic 8-ball
 99 Bottles of Beer
 The Name Game (a song)
 The Old lady swallowed a fly
 The Twelve Days of Christmas
Tokenize
 Text between
 Tokenize a string
 Word break problem
 Tokenize a string with escaping
 Split a character string based on change of character
Sequences
 Show ASCII table
 De Bruijn sequences
 Self-referential sequences
 Generate lower case ASCII alphabet




bdos:		equ	5	; CP/M syscalls 
putch:		equ	2	; Print a character
puts:		equ	9	; Print a string
fopen:		equ	15	; Open a file
fread:		equ	20	; Read a file
fcb:		equ	5ch	; FCB for file given on command line
dma:		equ	80h	; Default DMA
		org	100h	; CP/M loads the program starting at page 1
		;; Zero out pages two and three (to keep a 16-bit counter
		;; for each possible byte in the file).
		;; We can do this because this program is small enough to
		;; fit in page 1 in its entirety.
		xra	a	; Zero A.
		mov	b,a	; Zero B too (make it loop 256 times)
		lxi	d,200h	; Start of page two
zero:		stax	d	; Zero out a byte (store A, which is zero)
		inx	d	; Next byte
		stax	d	; Zero out another byte
		inx	d	; Next byte
		dcr	b	; Decrement the loop counter.
		jnz	zero	; Continue until B comes back to zero.
		;; Open the file given on the command line. 
		lxi	d,fcb	; CP/M always tries to parse the command line,
		mvi	c,fopen	; and gives us a file "object" in page zero.
		call	bdos	; We can just call fopen on it.
		inr	a	; It sets A=FF on error, so if incrementing A
		jz	error	; rolls back over to 0, that's an error.
		;; Process the file record by record.
		;; In CP/M, each file consists of a number of 128-byte
		;; records. An exact size is not kept.
		;; If a text file is not an exact multiple of 128 bytes
		;; long, the last record will contain a ^Z (26 decimal),
		;; and anything after that byte should be ignored.
read:		lxi	d,fcb	; From the file control block (the "object"),
		mvi	c,fread	; read one record. By default it ends up in
		call	bdos	; the last half of page zero.
		ana	a	; Zero carry flag.
		rar		; Low bit says if end reached
		jc	output	; If so, go print the table
		ana	a	; If any other bits are set, that's a
		jnz	error	; read error.
		;; Count the characters in the current record.
		lxi	d,dma-1	; Set DE to point just before the record
byte:		inr	e	; Go to the next byte.
		jz	read	; If end of record, go get next record.
		ldax	d	; Grab the current byte 
		cpi	26	; If it is EOF, we're done.
		jz	output	; Go print the table 
		mov	l,a	; Otherwise, increment the counter for this
		mvi	h,2	; character: the low byte is kept in page 2.
		inr	m	; 'm' means the value in memory at HL.
		jnz	byte	; If no rollover, we're done; count next byte
		inr	h	; But we're keeping a 16-bit counter, so
		inr	m	; if there is rollover, increment high byte.
		jmp	byte	; The high byte is in page 3 -unorthodox, but
				; it's easy to access here.
		;; We've done the whole file. For each printable
		;; ASCII character (32..126), print the character and
		;; the count. 
output:		mvi	a,32	; Start at 32.
		;; Print a character and its counter
char:		mov	l,a	; Load 16-bit counter into DE. Low byte
		mvi	h,2	; is in page 2 at a;
		mov	e,m
		inr	h	; And the high byte is in page 3.
		mov	d,m
		mov	a,d	; Test if the counter is zero
		ora	e
		mov	a,l	; Put the character back in A
		jz	next	; If zero, don't print anything.
		push	psw	; If not, push the character,
		push	d	; and the counter.
		mvi	c,putch	; Print the current character
		mov	e,a
		call	bdos	
		lxi	d,separator	; Then print ': '
		call 	outs
		;; Then convert the counter to ASCII
		pop	d 	; Retrieve the counter 
		lxi	h,numend	; Get pointer to end of digit string
		push	h	; And put it on the stack
dgtloop:	xchg		; Put counter in HL (16-bit accumulator)
		lxi	b,-10	; Dividend is 10 
		mov	d,b	; Start quotient at -1 (we'll loop once
		mov	e,b	; too many, this corrects for it)
divloop:	inx	d	; Increment the quotient,
		dad	b	; subtract 10 from the dividend,
		jc	divloop	; and keep doing it until it goes negative
		lxi	b,10+'0'	; Add 10 back to get the remainder,
		dad	b		; plus '0' to make it ASCII.
		mov	a,l	
		pop	h	; Retrieve digit pointer
		dcx	h	; Decrement it (to point at current digit)
		mov	m,a	; Store the digit
		push	h	; And store the new pointer
		mov	a,d	; Check if the quotient is now zero
		ora	e
		jnz	dgtloop	; If not, do the next digit.
		pop	d	; Set DE to point at the first digit
		call	outs	; And output it as a string.
		pop	psw	; Restore the character
next:		inr	a	; Increment it
		cpi	127	; Did we just do the last character?
		jnz	char	; If not, go do the next character.
		ret		; If so, we're done.
		;; Print the error message
error:		lxi	d,errmsg
		;; Print string
outs:		mvi	c,puts
		jmp	bdos
		;; Strings
errmsg:		db	'?$'	; "Error message" (if file error)
separator:	db	': $'	; Goes in between character and number
number:		db	'00000'	; Space to keep ASCII representation of 
numend:		db	13,10,'$'	; a 16-bit number, plus newline.
 
 