Converting quaternary to octal. ASM 8086 - algorithm

I have to prepare program for 8086 processor which converts quaternary to octal number.
My idea:
Multiplying every digit by exponents of 4 and add to register. Later check the highest exponent of 8 not higher than sum from first step.
Divide by exponents of 8 until remainder equals 0. Every result of dividing is one digit in octal.
But for 16-digits number last exponent of 4 is 4^15. I suppose It isn't optimal algorithm.
Is there any other way? Maybe to binary and group by 3 digits.

Turns out you can indeed process values 3 digits at a time. Done this way, you can process strings of arbitrary length, without being limited by the size of a register. Not sure why you might need to, unless aliens try to communicate with us using ascii strings of quaternary digits with huge lengths. Might happen.
It's possible to do the translation either way (Right to Left or Left to Right). However, there's a bit of a challenge to either:
If you are processing RtL, you need to know the length of the output string before you start (so that you know where to write the digits as you compute them). This is do-able, but a bit tricky. In simplest terms, the length is ((strlen(Q) + 2) / 3) * 2. That almost gets it. However, you can end up with a blank space at the beginning for a number of cases. "1" as well as "10" will give the blank space. "20" won't. The correct value can be computed, but it's annoying.
Likewise, processing LtR has a similar problem. You don't have the problem of figuring out where to write digits, but consider: If the string to convert it "123", then the conversion is simple (33 octal). But what if you start processing, and the complete string is "1231" (155 octal)? In that case what you need to process it like "001231" (01 55). IOW, digits can be processed in groups of 3, but you need to handle the initial case where the number of digits doesn't evenly divide by 3.
Posting solutions to homework is usually something I avoid. However I doubt you are going to turn this in as your 'solution,' and it's (barely) possible that google might send someone here who needs something similar.
A few things to note:
This code is intended to be called from C using Microsoft's fastcall (it made testing easier) and compiled with masm.
While it is written in 32bit (my environment), there's nothing that particularly requires 32bit in it. Since you said you were targeting 8086, I've tried to avoid any 'advanced' instructions. Converting to 16bit or even 64bit should not present much of a challenge.
It processes from left to right.
As with any well-written routine, it validates its parameters. It outputs a zero length string on error, such as invalid digits in the input string.
It will crash if the output buffer is NULL. I suppose I could return a bool on error (currently returns void), but, well, I didn't.
I'm sure the code could be tighter (couldn't it always?), but for "homework project quality," it seems reasonable.
Other than that, that comments should explain the code.
.386
.model flat
.code
; Call from C via:
; extern "C" void __fastcall PrintOct(const char *pQuat, char *pOct);
; On Entry:
; ecx: pQuat
; edx: pOct
; On Exit:
; eax, ecx, edx clobbered
; all others preserved
; If pOct is zero bytes long, an error occurred (probably invalid digits)
#PrintOct#8 PROC
; -----------------------
; If pOct is NULL, there's nothing we can do
test edx, edx
jz Failed
; -----------------------
; Save the registers we modify (except for
; eax, edx and ecx which we treat as scratch).
push esi
push ebx
push edi
mov esi, ecx
mov edi, edx
xor ebx, ebx
; -----------------------
; esi: pQuat
; edi: pOct
; ebx: zero (because we use lea)
; ecx: temp pointer to pQuat
; Reject NULL pQuat
test esi, esi
jz WriteNull
; -----------------------
; Reject 0 length pQuat
mov bl, BYTE PTR [esi]
test bl, bl
jz WriteNull
; -----------------------
; How many chars in pQuat?
mov dl, bl ; bl is first digit as ascii. Preserve it.
CountLoop:
inc ecx ; One more valid char
; While we're counting, check for invalid digits
cmp dl, '0'
jl WriteNull
cmp dl, '3'
jg WriteNull
mov dl, BYTE PTR [ecx] ; Read the next char
test dl, dl ; End of string?
jnz CountLoop
sub ecx, esi
; -----------------------
; At this point, there is at least 1 valid digit, and
; ecx contains # digits
; bl still contains first digit as ascii
; Normally we process 3 digits at a time. But the number of
; digits to process might not be an even multiple of 3.
; This code finds the 'remainder' when dividing ecx by 3.
; It might seem like you could just use 'div' (and you can),
; but 'div' is so insanely expensive, that doing all these
; lines is *still* cheaper than a single div.
mov eax, ecx
mov edx, 0AAAAAAABh
mul edx
shr edx, 1
lea edx, [edx+edx*2]
sub ecx, edx ; This gives us the remainder (0-2).
; If the remainder is zero, use the normal 3 digit load
jz LoadTriplet
; -----------------------
; Build a triplet from however many leading 'odd' digits
; there are (1 or 2). Result is in al.
lea eax, DWORD PTR [ebx-48] ; This get us the first digit
; If there was only 1 digit, don't try to load 2
cmp cl, 1
je OneDigit
; Load the other digit
shl al, 2
mov bl, BYTE PTR [esi+1]
sub bl, 48
or al, bl
OneDigit:
add esi, ecx ; Update our pQuat pointer
jmp ProcessDigits
; -----------------------
; Build a triplet from the next 3 digits.
; Result is in al.
; bl contains the first digit as ascii
LoadTriplet:
lea eax, DWORD PTR [ebx-48]
shl al, 4 ; Make room for the other 2 digits.
; Second digit
mov cl, BYTE PTR [esi+1]
sub cl, '0'
shl cl, 2
or al, cl
; Third digit
mov bl, BYTE PTR [esi+2]
sub bl, '0'
or al, bl
add esi, 3 ; Update our pQuat pointer
; -----------------------
; At this point
; al: Triplet
; ch: DigitWritten (initially zeroed when computing remainder)
ProcessDigits:
mov dl, al
shr al, 3 ; left digit
and dl, 7 ; right digit
; If we haven't written any digits, and we are
; about to write a zero, skip it. This deals
; with both "000123" and "2" (due to OneDigit,
; the 'left digit' might be zero).
; If we haven't written any digits yet (ch == 0), and the
; value we are are about to write is zero (al == 0), skip
; the write.
or ch, al
jz Skip1
add al, '0' ; Convert to ascii
mov BYTE PTR [edi], al ; Write a digit
inc edi ; Update pointer to output buffer
jmp Skip1a ; No need to check again
Skip1:
or ch, dl ; Both check and update DigitWritten
jz Skip2
Skip1a:
add dl, '0' ; Convert to ascii
mov BYTE PTR [edi], dl ; Write a digit
inc edi ; Update pointer to output buffer
Skip2:
; Load the next digit.
mov bl, BYTE PTR [esi]
test bl, bl
jnz LoadTriplet
; -----------------------
; All digits processed. We know there is at least 1 valid digit
; (checked on entry), so if we never wrote anything, the value
; must have been zero. Since we skipped it to avoid
; unnecessary preceding zeros, deal with it now.
test ch, ch
jne WriteNull
mov BYTE PTR [edi], '0'
inc edi
; -----------------------
; Write the trailing NULL. Note that if the returned string is
; 0 bytes long, an error occurred (probably invalid digits).
WriteNull:
mov BYTE PTR [edi], 0
; -----------------------
; Cleanup
pop edi
pop ebx
pop esi
Failed:
ret
#PrintOct#8 ENDP
end
I've run a string with 1,000,000,000 quaternary digit thru it as well as all the values from 0-4,294,967,295. Seems to work.
I for one welcome our new 4-digited alien overlords.

Related

Passing string parameter to a PROC

I want to call a function that will perform upper to lower case conversion to a user typed string, preserving the especial characters. This part works, but only for the first 4 characters, everything after that just gets truncated. I believe it is because I have defined the parameters as DWORD:
I have tried using PAGE, PARA and BYTE. The first two don't work and with byte says type missmatch.
upperToLower proc, source:dword, auxtarget:dword
mov eax, source ;Point to string
mov ebx, auxtarget ; point to destination
L1:
mov dl, [eax] ; Get a character from buffer
cmp byte ptr [eax], 0 ; End of string? (not counters)
je printString ; if true, jump to printString
cmp dl, 65 ; 65 == 'A'
jl notUpper ; if less, it's not uppercase
cmp dl, 90 ; 90 == 'Z'
jg notUpper ; if greater, it's not uppercase
xor dl, 100000b ; XOR to change upper to lower
mov [ebx], dl ; add char to target
inc eax ; Move counter up
inc ebx ; move counter up
jmp L1 ; loop
notUpper: ; not uppercase
mov [ebx], dl ; copy the letter
inc eax ;next letter
inc ebx
jmp L1
printString:
invoke WriteConsoleA, consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten, 0
ret
upperToLower endp
The PROTO:
upperToLower PROTO,
source: dword,
auxtarget: dword
Invoke:
invoke upperToLower, offset buffer, offset target
The buffer parameter is: buffer db 128 DUP(?)
How can I get printed the whole string, and not just the first 4 characters?
Why are only 4 characters being printed? You write the string to the console with:
invoke WriteConsoleA, consoleOutHandle, auxtarget, sizeof auxtarget, bytesWritten, 0
The sizeof auxtarget parameter is the size of auxtarget which is a DWORD (4 bytes) thus you are asking to only print 4 bytes. You need to pass the length of the string. You can easily do so by taking the ending address in EAX and subtracting the source pointer from it. The result would be the length of the string you traversed.
Modify the code to be:
printString:
sub eax, source
invoke WriteConsoleA, consoleOutHandle, auxtarget, eax, bytesWritten, 0
A version of your code that follows the C call convention, uses both a source and destination buffer, tests for the pointers to make sure they aren't NULL, does the conversion using a similar method described by Peter Cordes is as follows:
upperToLower proc uses edi esi, source:dword, dest:dword
; uses ESI EDI is used to tell assembler we are clobbering two of
; the cdecl calling convetions non-volatile registers. See:
; https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl
mov esi, source ; ESI = Pointer to string
test esi, esi ; Is source a NULL pointer?
jz done ; If it is then we are done
mov edi, dest ; EDI = Pointer to string
test edi, edi ; Is dest a NULL pointer?
jz done ; If it is then we are done
xor edx, edx ; EDX = 0 = current character index into the strings
jmp getnextchar ; Jump into loop at point of getting next character
charloop:
lea ecx, [eax - 'A'] ; cl = al-'A', and we do not care about the rest
; of the register
cmp cl, 25 ; if(c >= 'A' && c <= 'Z') c += 0x20;
lea ecx, [eax + 20h] ; without affecting flags
cmovna eax, ecx ; take the +0x20 version if it was in the
; uppercase range to start with
mov [edi + edx], al ; Update character in destination string
inc edx ; Go to next character
getnextchar:
movzx eax, byte ptr [esi + edx]
; mov al, [esi + edx] leaving high garbage in EAX is ok
; too, but this avoids a partial-register stall
; when doing the mov+sub
; in one instruction with LEA
test eax, eax ; Is the character NUL(0) terminator?
jnz charloop ; If not go back and process character
printString:
; EDI = source, EDX = length of string
invoke WriteConsoleA, consoleOutHandle, edi, edx, bytesWritten, 0
mov edx, sizeof buffer
done:
ret
upperToLower endp
A version that takes one parameter and changes the source string to upper case could be done this way:
upperToLower proc, source:dword
mov edx, source ; EDX = Pointer to string
test edx, edx ; Is it a NULL pointer?
jz done ; If it is then we are done
jmp getnextchar ; Jump into loop at point of getting next character
charloop:
lea ecx, [eax - 'A'] ; cl = al-'A', and we do not care about the rest
; of the register
cmp cl, 25 ; if(c >= 'A' && c <= 'Z') c += 0x20;
lea ecx, [eax + 20h] ; without affecting flags
cmovna eax, ecx ; take the +0x20 version if it was in the
; uppercase range to start with
mov [edx], al ; Update character in string
inc edx ; Go to next character
getnextchar:
movzx eax, byte ptr [edx] ; mov al, [edx] leaving high garbage in EAX is ok, too,
; but this avoids a partial-register stall
; when doing the mov+sub in one instruction with LEA
test eax, eax ; Is the character NUL(0) terminator?
jnz charloop ; If not go back and process character
printString:
sub edx, source ; EDX-source=length
invoke WriteConsoleA, consoleOutHandle, source, edx, bytesWritten, 0
done:
ret
upperToLower endp
Observations
A generic upperToLower function that does the string conversion would normally not do the printing itself. You'd normally call upperToLower to do the conversion only, then you'd output the string to the display in a separate call.

How to print signed integer in x86 assembly (NASM) on Mac

I found an implementation of unsigned integer conversion in x86 assembly, and I tried plugging it in but being new to assembly and not having a debugging env there yet, it's difficult to understand why it's not working. I would also like it to work with signed integers so it can capture error messages from syscalls.
Wondering if one could show how to fix this code to get the signed integer to print, without using printf but using strprn provided by this answer.
%define a rdi
%define b rsi
%define c rdx
%define d r10
%define e r8
%define f r9
%define i rax
%define EXIT 0x2000001
%define EXIT_STATUS 0
%define READ 0x2000003 ; read
%define WRITE 0x2000004 ; write
%define OPEN 0x2000005 ; open(path, oflag)
%define CLOSE 0x2000006 ; CLOSE
%define MMAP 0x2000197 ; mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t offset)
; szstr computes the lenght of a string.
; rdi - string address
; rdx - contains string length (returned)
strsz:
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1 (uses bitwise id: ~x = -x-1)
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative -> absolute value
dec rcx ; -1 to skip the null-term, rcx contains length
mov rdx, rcx ; size returned in rdx, ready to call write
ret
; strprn writes a string to the file descriptor.
; rdi - string address
; rdx - contains string length
strprn:
push rdi ; push string address onto stack
call strsz ; call strsz to get length
pop rsi ; pop string to rsi (source index)
mov rax, WRITE ; put write/stdout number in rax (both 1)
mov rdi, 1 ; set destination index to rax (stdout)
syscall ; call kernel
ret
; mov ebx, 0xCCCCCCCD
itoa:
xor rdi, rdi
call itoal
ret
; itoa loop
itoal:
mov ecx, eax ; save original number
mul ebx ; divide by 10 using agner fog's 'magic number'
shr edx, 3 ;
mov eax, edx ; store quotient for next loop
lea edx, [edx*4 + edx] ; multiply by 10
shl rdi, 8 ; make room for byte
lea edx, [edx*2 - '0'] ; finish *10 and convert to ascii
sub ecx, edx ; subtract from original number to get remainder
lea rdi, [rdi + rcx] ; store next byte
test eax, eax
jnz itoal
exit:
mov a, EXIT_STATUS ; exit status
mov i, EXIT ; exit
syscall
_main:
mov rdi, msg
call strprn
mov ebx, -0xCCCCCCCD
call itoa
call strprn
jmp exit
section .text
msg: db 0xa, " Hello StackOverflow!!!", 0xa, 0xa, 0
With this working it will be possible to properly print signed integers to STDOUT, so you can log the registers values.
https://codereview.stackexchange.com/questions/142842/integer-to-ascii-algorithm-x86-assembly
How to print a string to the terminal in x86-64 assembly (NASM) without syscall?
How do I print an integer in Assembly Level Programming without printf from the c library?
https://baptiste-wicht.com/posts/2011/11/print-strings-integers-intel-assembly.html
How to get length of long strings in x86 assembly to print on assertion
My answer on How do I print an integer in Assembly Level Programming without printf from the c library? which you already linked shows that serializing an integer into memory as ASCII decimal gives you a length, so you have no use for (a custom version of) strlen here.
(Your msg has an assemble-time constant length, so it's silly not to use that.)
To print a signed integer, implement this logic:
if (x < 0) {
print('-'); // or just was_negative = 1
x = -x;
}
unsigned_intprint(x);
Unsigned covers the abs(most_negative_integer) case, e.g. in 8-bit - (-128) overflows to -128 signed. But if you treat the result of that conditional neg as unsigned, it's correct with no overflow for all inputs.
Instead of actually printing a - by itself, just save the fact that the starting number was negative and stick the - in front of the other digits after generating the last one. For bases that aren't powers of 2, the normal algorithm can only generate digits in reverse order of printing,
My x86-64 print integer with syscall answer treats the input as unsigned, so you should simply use that with some sign-handling code around it. It was written for Linux, but replacing the write system call number will make it work on Mac. They have the same calling convention and ABI.
And BTW, xor al,al is strictly worse than xor eax,eax unless you specifically want to preserve the upper 7 bytes of RAX. Only xor-zeroing of full registers is handled efficiently as a zeroing idiom.
Also, repnz scasb is not fast; about 1 compare per clock for large strings.
For strings up to 16 bytes, you can use a single XMM vector with pcmpeqb / pmovmskb / bsf to find the first zero byte, with no loop. (SSE2 is baseline for x86-64).

assembly: sorting numbers using only conditional statments

I am new to assembly and I am trying to write a program that gets five user inputed numbers, stores them in variables num1-num5, sorts them(without using arrays) with num5 having the greatest value and num1 having the lowest value, and then displays the sorted numbers. I am having trouble figuring out how to approach this. I got the 5 numbers and stored them in the variables but I am confused as to how to start with sorting. I have tried a few things but I keep getting errors. This is my code that I can actually get running but it isn't working the way I want it to.
TITLE MASM Template (main.asm)
INCLUDE Irvine32.inc
.data
getnumber byte "Please enter a number between 0 and 20",0ah,0dh,0
num1 byte 0
num2 byte 0
num3 byte 0
num4 byte 0
num5 byte 0
.code
main PROC
call Clrscr
;************* get the information from the user*******************
mov edx, offset getnumber ;ask to input number
call writestring
call readint
mov bl, al
mov num1, bl ;get the number and move to num1 variable
mov edx, offset getnumber ;ask to input number
call writestring
call readint
mov bl, al
mov num2, bl ;get the number and move to num2 variable
mov edx, offset getnumber ;ask to input number
call writestring
call readint
mov bl, al
mov num3, bl ;get the number and move to num3 variable
mov edx, offset getnumber ;ask to input number
call writestring
call readint
mov bl,al
mov num4, bl ;get the number and move to num4 variable
mov edx, offset getnumber ;ask to input number
call writestring
call readint
mov bl, al
mov num5, bl ;get the number and move to num5 variable
;***show the user inputed numbers****
mov al, num1
call writeint
mov al, num2
call writeint
mov al,num3
call writeint
mov al, num4
call writeint
mov al,num5
call writeint
;*****start comparing***
cmp bl,num5
jl jumptoisless
jg jumptoisgreater
jumptoisless:
call writeint
jumptoisgreater:
mov bl, num5
mov dl, num4
mov num5, dl
mov num4, bl
call writeint
jmp imdone
imdone:
call dumpregs
exit
main ENDP
END main
Some notes to your code:
call readint
mov bl, al
mov num2, bl
Why don't you simply store al directly to memory, as: mov [num2],al? You don't use the bl anyway.
Except here:
;*****start comparing***
cmp bl,num5
jl jumptoisless
jg jumptoisgreater
Where I would be afraid what call writeint does to ebx (or you did your homework, and you know from head that call writeint preserves ebx content?).
And if the ebx is preserved, then bl contains still num5 from the input, so it will be equal.
Funnily enough, when equal, you will continue with jumptoisless: part of code, which will output some leftover in al, and then it will continue to jumptoisgreater: part of code, so effectively executing all of them.
Can you watch the CPU for a while in debugger, while single stepping over the instructions, to understand a bit better how it works? It's a state machine, ie. based on the current values in registers, and content of memory, it will change the state of registers and memory in the deterministic way.
So unless you jump away, next instructions is executed after the current one, and jl + jg doesn't cover "equal" state (at least you do cmp only once, so hopefully you understand the jcc instructions don't change flags and both jl/jg operate on the same result of cmp in flags). The Assembler doesn't care about name of your labels, and it will not warn you the "isgreater" code is executed even when "isless" was executed first.
About how to solve your task:
Can't think of anything reasonably short, unless you start to work with num1-num5 memory as array, so you can address it in generic pointer way with index. So I will gladly let you try on your own, just a reminder you need at least n*log_n compares to sort n values, so if you would write very effective sort code, you would need at least 5*3 = 15 cmp instructions (log2(5) = 3, as 23 = 8).
On the other hand an ineffective (but simple to write and understand) bubble sort over array can be done with single cmp inside two loops.
rcgldr made me curious, so I have been trying few things...
With insertion sort it's possible to use only 8x (at most) cmp (I hope the pseudo-syntax is understandable for him):
Insert(0, num1)
// ^ no cmp
Insert((num2 <= [0] ? 0 : 1), num2)
// ^ 1x cmp executed
Insert((num3 <= [0] ? 0 : (num3 <= [1] ? 1 : 2)), num3)
// ^ at most 2 cmp executed
Insert((num4 <= [1] ? (num4 <= [0] ? 0 : 1) : (num4 <= [2] ? 2 : 3)), num4)
// ^ always 2 of 3 cmp executed
Insert((num5 <= [1] ? (num5 <= [0] ? 0 : 1) : (num5 <= [2] ? 2 : (num5 <= [3] ? 3 : 4))), num5)
// ^ at most 3 of 4 cmp executed
=> total at most 8 cmp executed.
Of course doing the "insert" with "position" over fixed variables would be total PITA... ;) So this is half-joke proposal just to see if 8x cmp is enough.
("6 compares" turned out to be my brain-fart, not possible AFAIK)

Assembler (TASM x64) arrays and elements

I have an array of nine names:
.model tiny
.data
vardas1 db "Rokas",0ah,'$'
vardas2 db "Tomas",0ah,'$'
vardas3 db "Matas",0ah,'$'
vardas4 db "Domas",0ah,'$'
vardas5 db "Augis",0ah,'$'
vardas6 db "Vofka",0ah,'$'
vardas7 db "Marka",0ah,'$'
vardas8 db "Auris",0ah,'$'
vardas9 db "Edvis",0ah,'$'
vardai dw offset vardas1, offset vardas2, offset vardas3, offset vardas4, offset vardas5, offset vardas6, offset vardas7, offset vardas8, offset vardas9
.code
org 100h
I need to read a digit from keyboard, and then I need to print that name. For example I will push 5, and console should write "Augis". BTW, second code block aren't all code, just loop that doesn't work
paieska:
mov dx, offset _comment1 ; Just string name asking user to input digit
mov ah, 9
int 21h
mov j, 00h ; Trying to input the digit from keyboard
mov ah, 01h
mov dl, 0ah
int 21h
mov bx, offset vardai ; Add array "names" to bx register
add bx, cx ; Add cx for indexing
mov dx, [bx] ; Add first array element to dx register
add cx, 2 ; Increasing cx by 2, because I'm using data word not data byte
mov ah, 9 ; Try to print it
int 21h
cmp cx, j ; Try to compare cx (index of array) to mine inputed digit "j"
jne paieska
je end
mov ah, 01h
mov dl, 0ah ;NO NEED FOR THIS - INT21/01 DOES NOT USE DL
int 21h
MOV AH, '1' ; MIN INPUT CHAR
mov bx, offset vardai ; Add array "names" to bx register WELL, ASSIGN ACTUALLY
MOV CX,2 ;NUMBER OF BYTES TO ADD (WORDS, NOT BYTES)
LOOPN:
mov dx, [bx] ; name-pointer array element to dx register
CMP AH,AL ; MATCHING char?
JE PNAME ; YES, PRINT NAME
add bx, cx ; Add cx=2 for next name
inc AH ; next possible character input
CMP AH,'9'+1 ; allowed is '1'..'9'
jne loopn ; in allowed range
; input not 1..9
mov dx, offset errormessage
PNAME:
mov ah, 9 ; Try to print it
int 21h
jmp end
Well, I tried to edit your approach with CAPS, but it became too complicated.
Essentially, you are reading a character from the keyboard using function 01. This character arrives in AL. If all goes well, it should be '1'..'9'. Notice these are the ASCII characters '1'..'9', that is hex 31..39
Next step is to set BX to the start of the table, AH to the minimum character you anticipate and CX to 2 because the table contains words, not bytes.
Now we have a loop. Load X from the table, and check whether AL is equal to AH. If the user input 1, these will be equal, so go print the string.
Otherwise, add 2 to BX to point to the next entry in the table (this could have been done by ADD BX,2 or INC BX INC BX which would mean the MOV CX,2 would be unnecessary - just the way I wrote it...) and increment the '1' in AH to '2'.
The end-condition for the loop is when AH gets incremented from '9' to - well, ':' or '9'+1. If it hasn't reached that end-condition, then run around the loop until all of the values '1'..'9' have been tested. If you haven't got to PNAME yet, then there's an error because the character input wasn't allowed, so point to an error message and then print it.
Now jumping to the end - probably you'd want to terminate the program, so you'd execute
MOV AH,4CH
INT 21H

Outputting registers to the console with MASM

I'm one day into learning ASM and I've done a few tutorials, and even successfully modified the tutorial content to use jmp and cmp, etc instead of the MASM .if and .while macros.
I've decided to try and write something very, very simple to begin with before I continue with more advanced tutorials. I'm writing a Fibonacci number generator. Here is the source I have so far:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
.code
start:
mov eax, 1
mov ecx, 1
_a:
push eax
add eax, ecx
pop ecx
; Jump to _b if there is an overflow on eax
; Print Values Here
jmp _a
_b:
push 0
call ExitProcess
end start
I intend to check for overflows on eax/ecx but right now I'm just interested in displaying the values of eax/ecx on the screen.
I know how to push the address of a constant string from .data and call StdOut which was the first example in the hello world tutorial, but this appears to be quite different (?).
There is this code provided by Microsoft itself
http://support.microsoft.com/kb/85068
Note that this code outputs AX register on 16 bit systems. But you can get the idea, you just need to convert AX value into ASCII characters by looping through each character. Skip the interrupts part and use your StdOut function.
mov dx, 4 ; Loop will print out 4 hex characters.
nexthex:
push dx ; Save the loop counter.
mov cl, 4 ; Rotate register 4 bits.
rol ax, cl
push ax ; Save current value in AX.
and al, 0Fh ; Mask off all but 4 lowest bits.
cmp al, 10 ; Check to see if digit is 0-9.
jl decimal ; Digit is 0-9.
add al, 7 ; Add 7 for Digits A-F.
decimal:
add al, 30h ; Add 30h to get ASCII character.
mov dl, al
;Use StdOut to print value of dl
;mov ah, 02h ; Prepare for interrupt.
;int 21h ; Do MS-DOS call to print out value.
pop ax ; Restore value to AX.
pop dx ; Restore the loop counter.
dec dx ; Decrement loop counter.
jnz nexthex ; Loop back if there is another character
; to print.
See here as well:
http://www.masm32.com/board/index.php?PHPSESSID=fa4590ba57dbaad4bc44088172af0b49&action=printpage;topic=14410.0

Resources