I'm not sure exactly what you're up to, and I won't write finished code, but here are a few suggestions. First, you need to know if your code is in a 16, 32, or 64 bit segment. I will assume a 32 bit segment, in which case all registers and operands will default to 32 bits. For bulletproof code, I would recommend using operand size prefixes where appropriate. Now, since you're setting ES anyway, use one of the string instructions, in this case, LODSB. And use the LOOPNZ instruction for loop control (ECX as a counter). Here's an untested bit to count capitals and lowercase:
Push ESI //You really should save these so you can restore them later
Push ES
Push DS
...
... //Your code to set the new values in the segment regs
... //And define the character string - make sure it's null terminated!
mov esi, Offset string //DS:ESI must point to string for lodsb.
// (ES:DSI & DS:ESI are used for movsb, which you may need later)
//Stringlength is optional. You could use scansb to find it, or just set it to
//a value you know is larger than the string and stop the loop on a null character
//with loopnz (see below). If this is 16 bit segment, use CX instead, ditto for SI.
mov ecx, StringLength //You could use scansb to find this or just set it large
NextChar:
lodsb //Put next character in AL
mov ah, al //Make copy of character in AH
and ah, 0xDF //Remove bit that distinguishes between lower and upper case
cmp ah, 0x41 //Test for lower and upper case A
jb Loopy //Not a letter, loop on next character
cmp ah, 0x5A //Test for lower and upper Z
ja Loopy //Not a letter, loop on next character
bt ax,5 //Test the 0x20 bit in AL
jc isLowerCase //if set, it's lower case
inc UpperCaseCount //Count upper case characters
jmp Loopy //and loop
isLowerCase:
inc UpperCaseCount //Count lower case characters and loop
Loopy:
cmp al,0 //set zero flag if null terminator
loopnz NextChar //loop till count or null terminator
...Any additional code you need
pop ESI //restore them
pop ES
pop DS Loop exits with counts set, either when the string length is reached or a
null byte is found. Strategy depends upon the fact that A-Z is 0x41-0x5A and
a-z is 0x61-0x7A, that is, if the 0x20 bit is set and in the a-z range, its lower case. That's why I test range on AH with the 0x20 bit removed (with the AND 0xDF),
which makes the upper and lower case range the same. Then test the still unchanged
AL to see if the lower case bit is set. This snippet probably has some bugs and I
intentionally left out segment stuff and definitions of the counters, etc. Remember also to POP anything else off the stack that you've saved there. Finally, why are you messing with the stack segment at all? I would just use the stack segment that the OS provides when it enters your code unless you plan to do some really massive stuff with the stack - but then, you've only defined a tiny stack segment. Good luck!