; Assessment, Excersise 3
;
; Program to count the frequenct of occurance
; of each character in a file
;
; By Andy Bennett (andyjpb@ashurst.eu.org), 2002
AREA charFreq, CODE, READONLY ; declare code area
SWI_Exit EQU 0x11 ; finish program
SWI_Open EQU 0x66 ; open a file or device
SWI_Close EQU 0x68 ; close an open file or device
SWI_Read EQU 0x6a ; read from an open file or device
SWI_Flen EQU 0x6c ; returns the current length of an open file or device
SWI_Write EQU 0x69 ; write to an open file or device
SWI_WriteC EQU 0x0 ; write a character to the console
SWI_Write0 EQU 0x2 ; write a null terminated string to the console
readonly EQU 0 ; open a file or device for reading
writeonly EQU 4 ; open a file or device for writing
ENTRY ; code entry point
START ADR r0, INFILE ; set r0 to point to INFILE
MOV r1, #readonly ; set access mode to file as readonly
SWI SWI_Open ; open the file pointed at by r0
CMP r0, #0 ; check for success on opening file
SWIEQ SWI_Exit ; if file could not be opened then exit now
MOV r5, r0 ; save the file handle in r5 for later
SWI SWI_Flen ; store the length of the file in r0
CMP r0, #-1 ; check that the call was successful
SWIEQ SWI_Exit ; if the call failed then exit now
MOV r6, r0 ; move the length of the file into r6
MOV r2, #1 ; read the file byte by byte
MOV r8, #4 ; store constant in register to keep MUL happy
ADR r0, FREQARRAY ; point r1 at the beginning of FREQARRAY
MOV r1, #0 ; initialise loop counter
MOV r3, #0 ; prepare to initialise FREQARRAY to 0
INITARRAY STR r3, [r0, r1]
CMP r1, #112 ; check to see if we are at the end of FREQARRAY
BGT COUNT ; once memory is initialised, begin counting
ADD r1, r1, #4 ; move along FREQARRAY by one word
B INITARRAY ; continue looping
COUNT CMP r6, #0 ; are we at the end of the file
BEQ ENDCOUNT ; if so, go to the next bit of code
ADR r1, FILECONTENTS ; if not, point r1 at the temp storage space
MOV r0, r5 ; retrive the file handle from r5
SWI SWI_Read ; read the byte in
CMP r0, #0 ; check that the call was sucessful
BNE ENDCOUNT ; if anything was left unread, then end of file encountered prematurely so wrap up
LDRB r4, [r1] ; load byte into the register
CMP r4, #9 ; is it a tab (white space)
MOVEQ r3, #104 ; move along FREQARRAY 26 words (26x4) to the "whitespace" word
BEQ UPDATEFREQ ; "subroutine" to update FREQARRAY
CMP r4, #10 ; is it a line feed (white space)
MOVEQ r3, #26 ; move along FREQARRAY 26 words to the "whitespace" word
BEQ UPDATEFREQ ; "subroutine" to update FREQARRAY
CMP r4, #13 ; is it a carriage return (white space)
MOVEQ r3, #26 ; move along FREQARRAY 26 words to the "whitespace" word
BEQ UPDATEFREQ ; "subroutine" to update FREQARRAY
CMP r4, #32 ; is it a space (white space)
MOVEQ r3, #26 ; move along FREQARRAY 26 words to the "whitespace" word
BEQ UPDATEFREQ ; "subroutine" to update FREQARRAY
; deal with upper case letters
CMP r4, #65 ; is it >= 65
BLT LITTLECMP
CMP r4, #90 ; or <= 90
SUBLE r3, r4, #65 ; move along FREQARRAY (r4 - #65) words to the correct letter
BLE UPDATEFREQ ; "subroutine" to update FREQARRAY
; deal with lower case letters
LITTLECMP CMP r4, #97 ; is it >= 97
BLT OTHERS
CMPGE r4, #122 ; or <= 122
SUBLE r3, r4, #97 ; move along FREQARRAY (r4 - #97) words to the correct letter
BLE UPDATEFREQ ; "subroutine" to update FREQARRAY
; deal with other characters
OTHERS MOV r3, #27 ; move along FREQARRAY 27 words to the "others" word
UPDATEFREQ ADR r4, FREQARRAY ; point r4 at the start of FREQARRAY
MUL r7, r3, r8 ; multiply offset in words by 4 so that value is in bytes
ADD r4, r4, r7 ; move along FREQARRAY to the correct word
LDR r3, [r4] ; load the correct word into r3
ADD r3, r3, #1 ; add one to the count
STR r3, [r4] ; write r3 back to memory again
SUB r6, r6, #1 ; move along to the next byte in the file
B COUNT ; resume processing with the next character in the file
ENDCOUNT MOV r0, r5 ; move the file handle into r0
SWI SWI_Close ; close the file
ADR r1, FREQARRAY ; place the start of FREQARRAY into r1
ADR r2, FILECONTENTS
MOV r6, #0 ; initialise counter, r6, to 0
REPORT CMP r6, #27 ; check loop counter
BGT ENDREPORT ; if >28 go to ENDREPORT
MUL r7, r6, r8 ; multiply offset in words by 4 so that value is in bytes
LDR r3, [r1, r7] ; load the current value into r3
CMP r6, #25 ; see if we are still on the alpha characters
ADDLE r0, r6, #65 ; if we are, put the current ascii value into r0
SWILE SWI_WriteC ; if we are, write out the character we are on
CMP r6, #26 ; see if we are on "White Spaces"
ADREQ r0, TXTWHITE ; if we are, point r0 at white spaces string
SWIEQ SWI_Write0 ; if we are, write out the white spaces string
CMP r6, #27 ; see if we are on "Others"
ADREQ r0, TXTOTHERS ; if we are, point r0 at others string
SWIEQ SWI_Write0 ; if we are, write out the others string
MOV r0, #58 ; then we want a colon, so set it up
SWI SWI_WriteC ; and write it out
MOV r0, #32 ; then we want a space, so set it up
SWI SWI_WriteC ; and write it out
BL HEXOUT ; write value to console in hexadecimal
MOV r0, #10 ; then we want a carriage return, so set it up
SWI SWI_WriteC ; and write it out
ADD r6, r6, #1 ; increment loop counter
B REPORT ; continue round loop with next item
ENDREPORT SWI SWI_Exit ; end program
; subroutines
; subroutine to output values in hex to the console
HEXOUT STMED r13!, {r0-r8, r14} ; save working registers on stack
MOV r2, #8 ; r2 has nibble (4-bit digit) count = 8
MOV r4, #0 ; initialise flag
HEXLOOP MOV r0, r3, LSR #28 ; get top nibble
CMP r0, #9 ; if nibble <= 9, then
ADDLE r0, r0, #"0" ; convert to ASCII numeric char
ADDGT r0, r0, #"A"-10 ; else convert to ASCII alphabet char
CMP r0, #"0" ; do we have a zero...
MOVNE r4, #1 ; if not, set the flag
BNE HEXNOTZERO ; if not, get the digit written out immediately
CMP r4, #0 ; ... and the flag is not set
BEQ HEXLEADZERO ; do not write out leading zeros
HEXNOTZERO SWI SWI_WriteC ; print character
HEXLEADZERO MOV r3, r3, LSL #4 ; shift left 4 bits to get to next nibble
SUBS r2, r2, #1 ; decrement nibble count
BNE HEXLOOP ; if more, do next nibble
LDMED r13!, {r0-r8, pc} ; retrieve working registers from stack
; ... and return to calling program
INFILE = "turing.txt", 0 ; input filename + null
ALIGN ; align variables on 32bit boundries
TXTWHITE = "White Spaces", 0 ; Text for report + null
TXTOTHERS = "Others", 0 ; Text for report + null
FILECONTENTS = "a" ; one byte to store one character from the file in whilst we process it
ALIGN ; align variables on 32bit boundries
FREQARRAY = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ; 28 words for storing the frequency of each character. Init to "0"
END