Link to home
Start Free TrialLog in
Avatar of Dmon443
Dmon443

asked on

NASM assembly program

Hi,

I am trying to code in assembly with NASM with great frustration...Basically what I'm stuck on is changing a number that is entered into a larger number drawn with $ signs. So what the program does among other things is reads a number between 1 and 9 and converts it to a numeric value and displays a large $ number. I have defined a bit pattern for each number for each line to display the pattern of $ signs to draw the number with. All numbers are 7 lines in size and now I need to step through the bit pattern, this is where I am stuck. Here is my program so far.
			bits	16
			org		0x100
			jmp		main	;Jump to main program
message:	db		'Please enter a number(1-9)',0ah,0dh,'$'
str_to_num:
			xor 	ax,ax	;initial value of ax = 0
			xor		bh,bh	;bh = 0
			mov 	cx,10	
			mov		si,al	;put number in si
			mov		bl,[si]	;move memory contents pointed to by si to bl
			cmp		bl,39h	;ASCII for 9
			jg		error	; > 9 is invalid
			sub		bl,30h	;convert to numeric
			jl		error	; < 0 also invalid
			imul	cx		; 32 bit result
			ret				;return
error:		
			db		'**','$'
			mov		dx,db	;adding ** to number that is not 1-9
			mov		ah,09	;to display string
			int		21h		;DOS interrupt
			jmp		message
			ret				;return

enl_num1:
			db		06h,06h,06h,06h,06h,06h,06h ; enlarged 1
			mov		dx,db
enl_num2:
			db		04h,0Ah,11h,02h,04h,08h,1Fh	; enlarged 2
			mov 	dx,db
enl_num3:
			db		0Eh,11h,06h,18h,06h,01h,1Eh ; enlarged 3
			mov		dx,db
enl_num4:
			db		01h,02h,04h,0Ah,1Fh,02h,02h ; enlarged 4
			mov		dx,db
enl_num5:
			db		1Fh,10h,1Ch,02h,01h,02h,1Ch ; enlarged 4
			mov		dx,db
enl_num6:
			db		01h,02h,04h,08h,14h,14h,08h ; enlarged 5
			mov		dx,db
enl_num7:
			db		1Fh,01h,02h,04h,08h,10h,10h ; enlarged 7
			mov		dx,db
enl_num8:
			db		04h,0Ah,0Ah,04h,0Ah,0Ah,04h ; enlarged 8
			mov		dx,db
enl_num9:
			db		0Eh,11h,11h,0Fh,01h,01h,1Fh ; enlarged 9
			mov		dx,db
			
main:		mov		al,00	;clear the screen
			mov		ah,06
			mov		bh,17h	;white on blue
			mov 	dh,05	;set row to 5
			mov		dl,10	;set column to 10
			int		10h		;screen handling
			mov		dx,db
			mov		ah,09	;to display string
			int		21h		;DOS interrupt to display string
			mov		ah,07	;single char keyboard input
			int 	21h		;DOS interrupt
			int 	20h

Open in new window

Avatar of Bill Bach
Bill Bach
Flag of United States of America image

Is this a homework assignment?  Not really proper to provide answers, but I'll gladly help you do the task on your own.

Can you describe what part of the app is working, and what problem you have?  I see a number of issues:
- First, you seem to be reading the character, but never calling the output routine.
- Second, you seem to indicate in the output routine comments that the input value is in AL, but you start by wiping AX.  This leads me to believe that you doi not understand the concepts of registers.  I would start by re-reading this section first, as registers are critical to understanding assembler.
- Third, you are trying to pull the data from the si pointer, but it appears that you never initialize this pointer properly.  Again, reading up on the use of the SI register, along with indirection, is likely in order.
- Finally, you need to convert the bit pattern to the output string, and you seem to have no code for this yet at all.  Here's a hint -- you are using a simple bit pattern to indicate which "dots" should be printed and which are blank.  So, how do you get this data out?  It's pretty simple, actually -- try reading each "row byte", then process each bit, displaying either "$" or " ".  How can you get the bits out of the byte?  This is an exercise for the reader, of course.  The straightforward solution would be a series of 256 "IF" statements, or possibly an indexed read into an array of strings.  However, think about trying the bit shift functions  and see if this is any easier....

Start there & take another shot at the app, and let's see where we go from there...
Avatar of Dmon443
Dmon443

ASKER

thanks for the advice, ok just addressing your first 2 points...Would I be going in the right direction now?
			bits	16
			org		0x100
			jmp		main	;Jump to main program
message:	db		'Please enter a number(1-9)',0ah,0dh,'$'
str_to_num:
			xor		bh,bh	;bh = 0
			mov 	cx,10	
			mov		si,ah	;put number in si
			mov		bl,[si]	;move memory contents pointed to by si to bl
			cmp		bl,39h	;ASCII for 9
			jg		error	; > 9 is invalid
			sub		bl,30h	;convert to numeric
			jl		error	; < 0 also invalid
			imul	cx		; 32 bit result
			ret				;return
error:		
			db		'**','$'
			mov		dx,db	;adding ** to number that is not 1-9
			mov		ah,09	;to display string
			int		21h		;DOS interrupt4a
			jmp		message
			ret				;return

enl_num1:
			db		06h,06h,06h,06h,06h,06h,06h ; enlarged 1
			mov		dx,db
enl_num2:
			db		04h,0Ah,11h,02h,04h,08h,1Fh	; enlarged 2
			mov 	dx,db
enl_num3:
			db		0Eh,11h,06h,18h,06h,01h,1Eh ; enlarged 3
			mov		dx,db
enl_num4:
			db		01h,02h,04h,0Ah,1Fh,02h,02h ; enlarged 4
			mov		dx,db
enl_num5:
			db		1Fh,10h,1Ch,02h,01h,02h,1Ch ; enlarged 4
			mov		dx,db
enl_num6:
			db		01h,02h,04h,08h,14h,14h,08h ; enlarged 5
			mov		dx,db
enl_num7:
			db		1Fh,01h,02h,04h,08h,10h,10h ; enlarged 7
			mov		dx,db
enl_num8:
			db		04h,0Ah,0Ah,04h,0Ah,0Ah,04h ; enlarged 8
			mov		dx,db
enl_num9:
			db		0Eh,11h,11h,0Fh,01h,01h,1Fh ; enlarged 9
			mov		dx,db
			
main:		mov		al,00	;clear the screen
			mov		ah,06
			mov		bh,17h	;white on blue
			mov 	dh,05	;set row to 5
			mov		dl,10	;set column to 10
			int		10h		;screen handling
			mov		dx,db
			mov		ah,09	;to display string
			int		21h		;DOS interrupt to display string
			mov		ah,07	;single char keyboard input
			int 	21h		;DOS interrupt
			jmp		str_to_num; convert str to num
			int 	20h

Open in new window

I see my error there about the ax and al register since al is lower ax so clearing it would be a bad idea.
Why have you opted to use a JMP statement to go to your str_to_num function (line 63)?  Remember that JMP does not return, so any code AFTER the JMP is meaningless, and your code shows another software interrupt immediately following (line 64).

Second, you need to really review your DOS function call documentation, too.  The INT21H, Service 7 returned the typed character in the AL register. So, when you make your call, the AL register is populated.  Yet you then seem to be pulling the character from the AH register in line 8.

Looking at the rest of the code, I am not confident that you have the fundamentals of assembly language down pat, yet.  In fact, there is no coherent thought process running  through this code -- it looks like you have been patching it together from Google searches.  

I would recommend that you start over on this code from the beginning.  If you are this far along into assembly, then you probably have coding experience in another high-level language -- this is good.  Start with the language that you know and write the code there, or start with a simple pseudo-code language, if that is easier.  Write out the entire logic FIRST, so that you know what it is supposed to look like from a logic perspective FIRST.  Then, we'll review that and when it is correct, we can convert to assembler.

I'm an old-school developer, having taught myself Applesoft BASIC first, then I learned 6502 assembler by reading through the Monitor ROM dumps in the Applie ][+ manuals.  I wrote some pretty big applications in 6502, then 65816, and later coded in 8086, 68000, and VAX assembler for various projects.  Understanding assembler has made me a better C programmer, and my code is almost always tight & fast -- rare in today's development world.  Because of that, I don't want you to get discouraged, but I instead want you to find some additional instruction and let's keep going.....
Avatar of Dmon443

ASKER

Ok yes this is my first assembly program I am trying to write so that is why the coding is bad. So I took your advice and wrote it in Qt which I am also learning but am much better with.
#ifndef ENLARGE_H
#define ENLARGE_H
#include <QString>
class Enlarge
{
public:
    Enlarge();
    void getNum();
    void convertToStr();
    void convertToBin();
    void drawLarge();
private:
    int num;
    QString sNum, hexString, binString;
};

#endif // ENLARGE_H

#include "enlarge.h"
#include <QTextStream>
#include <QDebug>
using namespace std;

QTextStream cout(stdout);
QTextStream cin(stdin);

Enlarge::Enlarge()
{
}


void Enlarge::getNum(){
    int n;
    cout << "Please enter a number(1-9): ";
    cin >> n;
    while ((n < 1) || (n > 9))
    {
        cout << "**";
        cout << "Please enter a number(1-9): ";
        cin >> n;
    }
    num = n;
}

void Enlarge::convertToStr() {
    switch (num)
    {
    case '1':
        hexString = "06060606060606";
        break;
    case '2':
        hexString = "040A110204081F";
        break;
    case '3':
        hexString = "0E11061806011E";
        break;
    case '4':
        hexString = "0102040A1F0202";
        break;
    case '5':
        hexString = "1F101C0201021C";
        break;
    case '6':
        hexString = "01020408141408";
        break;
    case '7':
        hexString = "1F010204081010";
        break;
    case '8':
        hexString = "040A0A040A0A04";
        break;
    case '9':
        hexString = "0E11110F01011F";
        break;
    }
}

//QString convertToHex(QString numbers){
//    QString hNum;
//    for (int i = 1;i < 14;i+=2)
//   {
//        hNum[i] = (numbers[i],0,16);
//    }
//    return hNum;
//}

void Enlarge::convertToBin(){
    for (int i = 0; i < hexString.length(); ++i)
    {
        if (hexString[i] == '0')
             binString.append ("0000");
        else if (hexString[i] == '1')
            binString.append ("0001");
        else if (hexString[i] == '2')
             binString.append ("0010");
        else if (hexString[i] == '3')
             binString.append ("0011");
        else if (hexString[i] == '4')
             binString.append ("0100");
        else if (hexString[i] == '5')
             binString.append ("0101");
        else if (hexString[i] == '6')
             binString.append ("0110");
        else if (hexString[i] == '7')
             binString.append ("0111");
        else if (hexString[i] == '8')
             binString.append ("1000");
        else if (hexString[i] == '9')
             binString.append ("1001");
        else if (hexString[i] == 'A')
             binString.append ("1010");
        else if (hexString[i] == 'B')
             binString.append ("1011");
        else if (hexString[i] == 'C')
             binString.append ("1100");
        else if (hexString[i] == 'D')
             binString.append ("1101");
        else if (hexString[i] == 'E')
             binString.append ("1110");
        else if (hexString[i] == 'F')
             binString.append ("1111");
    }
 }

void Enlarge::drawLarge(){
    for (int j = 0; j < 7; j =+ 4)
    {
        for (int i = 0; i < binString.length(); ++i)
        {
            if (binString == 0)
                cout << " ";
            else
                cout << "$";
        }
        cout << endl;
    }
}

#include <QCoreApplication>
#include <QTextStream>
#include <windows.h>
#include "enlarge.h"
#include <QDebug>

using namespace std;


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Enlarge number;
    qDebug() << QString(50, '\n');
    system("color 17");
    COORD p = {05,10};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), p);
    number.getNum();
    number.convertToStr();
    number.convertToBin();
    number.drawLarge();
    return EXIT_SUCCESS;

}

Open in new window

There is something wrong with that also, but it does at least run without any errors and I think the logic of what needs to be done is right?
Great.  Next step is add some debugging.  Add a function to print out your BINARY string, in an 8x7 grid.  Test it with each number to make sure your data is right.  You should get something like this for "1" (06060606060606)
00000110
00000110
00000110
00000110
00000110
00000110
00000110
You need to make sure that this part is working first, right?  I think when you try to output this, you'll find a few other issues, such as:
- forgetting to initialize binString to an empty string
- not figuring out where to break each line

Then, you have only to change the output: 0->" " and 1->"$".

As you're working on this, let's also look at the logic for converting your character to a binary.  In Assembler, your values won't be strings -- they will be raw bytes, right?  Bytes are already stored in binary, right?

A fairly basic solution would be to read each byte, go into a series of 256 IF statements, comparing for each value and displaying the right output pattern.  However, you see how painful that will be, right?  

This about this as a solution:
- Check the highest bit of the byte
- If it is 0, print a space, else print $
- Check the next highest bit of the byte
- If it is 0, print a space, else print $
- Check the next highest bit of the byte
- If it is 0, print a space, else print $
... Continue until all 8 bits are processed.  

Note how this solution is a bit more elegant -- you only have 8 IF statements instead of 256.  Now, what would be REALLY elegant would be to have some way to generate a loop to rotate all of the bits through the byte and print based on a single bit -- basically turning this into a nice tight loop.  The actual implementation is left as an exercise for the reader.

Keep calm and Carry on...
Avatar of Dmon443

ASKER

ok I have worked on it more now, wanted to get it to run so I could see what it does but its coming up with syntax errors in all the instances where I try to mov db to dx?!? Why am I not allowed to do that?
			bits	16
			org		0x100
			jmp		main	;Jump to main program
message:	db		'Please enter a number(1-9)',0ah,0dh,'$'
str_to_num:
			xor		bh,bh	;bh = 0
			mov 	cx,10	
			mov		si,ah	;put number in si
			mov		bl,[si]	;move memory contents pointed to by si to bl
			cmp		bl,39h	;ASCII for 9
			jg		error	; > 9 is invalid
			sub		bl,30h	;convert to numeric
			jl		error	; < 0 also invalid
			imul	cx		; 32 bit result
			ret				;return
error:		
			db		'**','$'
			mov		dx,db	;adding ** to number that is not 1-9
			mov		ah,09	;to display string
			int		21h		;DOS interrupt
			jmp		message
			ret				;return

enl_num1:
			db		06h,06h,06h,06h,06h,06h,06h ; enlarged 1
			mov		dx,db
enl_num2:
			db		04h,0Ah,11h,02h,04h,08h,1Fh	; enlarged 2
			mov 	dx,db
enl_num3:
			db		0Eh,11h,06h,18h,06h,01h,1Eh ; enlarged 3
			mov		dx,db
enl_num4:
			db		01h,02h,04h,0Ah,1Fh,02h,02h ; enlarged 4
			mov		dx,db
enl_num5:
			db		1Fh,10h,1Ch,02h,01h,02h,1Ch ; enlarged 4
			mov		dx,db
enl_num6:
			db		01h,02h,04h,08h,14h,14h,08h ; enlarged 5
			mov		dx,db
enl_num7:
			db		1Fh,01h,02h,04h,08h,10h,10h ; enlarged 7
			mov		dx,db
enl_num8:
			db		04h,0Ah,0Ah,04h,0Ah,0Ah,04h ; enlarged 8
			mov		dx,db
enl_num9:
			db		0Eh,11h,11h,0Fh,01h,01h,1Fh ; enlarged 9
			mov		dx,db
			
print_num:	
			mov		cx,8	; cx=8 loop counter
    		mov		al,[si]	;move one byte into al
			push 	ax		;push onto stack
			inc		si		;point to next byte
			cmp		dh,0	;test for 0
			jne		not_0
			je		is_0
			loop	print_num	;decrement cx, repeat loop if cx <>0
			db		' ',0ah,0dh,'$'
			mov		ah,09	;to display string
			int		21h		;DOS interrupt
			
			ret
not_0:		mov		dx,output_d  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret
is_0		mov		dx,output_0  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret

output_0:	db		' ' 
output_d:	db		'$'			

main:		mov		al,00	;clear the screen
			mov		ah,06
			mov		bh,17h	;white on blue
			mov 	dh,05	;set row to 5
			mov		dl,10	;set column to 10
			int		10h		;screen handling
			mov		dx,db
			mov		ah,09	;to display string
			int		21h		;DOS interrupt to display string
			mov		ah,07	;single char keyboard input
			int 	21h		;DOS interrupt
			jmp		str_to_num ;convert str to num
			mov		si,dx	;si contains address of number string
line_loop:	mov		cx,7	;once for each line
			jmp		print_num ;print the number
			loop	line_loop ;decrement cx, repeat if cx<>0
			int 	20h

Open in new window

That is because you don't move DB to DX.  I was wondering what you were trying to do here, but figured that there were so many more issues to tackle first...

The DB pseudo-operative is not a true 8086 command, but rather a compiler directive that is used to Define Bytes.  The DB directives should be in a separate part of your code (one that will NOT execute), because this is non-executable code.  Further, each DB should also be named -- so that you can reference it from the compiler.  I've never used labels on their own line like this, but have always put my labels on the exact line of data -- more like this:
en1_num1    db   06h,06h,06h,06h,06h,06h,06h
en1_num2    db   04h,0Ah,11h,02h,04h,08h,1Fh
etc.  This is closer to what you've done in Line 4.  I would put all the other DB definitions together here.

Then, to use the byte array, you simply refer to the LABEL of the byte array.  For example, in Lines 84-86, you are building up to do an INT call.  Here is the "proper" way to set it up:
    mov dx,message
    mov ah,9
    int 21h
Note that "message" in this case is the label of the byte array that you want to print.  

Your idea in lines 8/9 is close to being right for finding the byte array.  Currently, you put ah into si.  However, this is not quite right.  What should go into si is the ADDRESS of the byte array for this digit.  Thiink about how you can get this.  You'll probably want to take the digit you want to print, multiply that by the number of bytes in each digit (7), and then ADD that value to the first byte array location.  I'll let you code it from there to avoid giving away any answers....
Avatar of Dmon443

ASKER

I think I may be more confused now...
			bits	16
			org		0x100
			jmp		main	;Jump to main program
message:	db		'Please enter a number(1-9)',0ah,0dh,'$'
enl_num1:	db		06h,06h,06h,06h,06h,06h,06h ; enlarged 1
enl_num2:	db		04h,0Ah,11h,02h,04h,08h,1Fh	; enlarged 2
enl_num3:	db		0Eh,11h,06h,18h,06h,01h,1Eh ; enlarged 3
enl_num4:	db		01h,02h,04h,0Ah,1Fh,02h,02h ; enlarged 4
enl_num5:	db		1Fh,10h,1Ch,02h,01h,02h,1Ch ; enlarged 4
enl_num6:	db		01h,02h,04h,08h,14h,14h,08h ; enlarged 5
enl_num7:	db		1Fh,01h,02h,04h,08h,10h,10h ; enlarged 7
enl_num8:	db		04h,0Ah,0Ah,04h,0Ah,0Ah,04h ; enlarged 8
enl_num9:	db		0Eh,11h,11h,0Fh,01h,01h,1Fh ; enlarged 9
error_mess: db		'**','$'
output_0:	db		' ' 
output_d:	db		'$'		

str_to_num:
			xor		bh,bh	;bh = 0
			mov 	cx,10	
			mov		si,ah	;put number in si
			mov		bl,[si]	;move memory contents pointed to by si to bl
			cmp		bl,39h	;ASCII for 9
			jg		error	; > 9 is invalid
			sub		bl,30h	;convert to numeric
			jl		error	; < 0 also invalid
			imul	cx		; 32 bit result
			ret				;return
error:		
			mov		dx,error_mess ;adding ** to number that is not 1-9
			mov		ah,09	      ;to display string
			int		21h		      ;DOS interrupt
			jmp		message
			ret				      ;return

print_num:	
			mov		cx,8	; cx=8 loop counter
    		mov		al,[si]	;move one byte into al
			push 	ax		;push onto stack
			inc		si		;point to next byte
			cmp		dh,0	;test for 0
			jne		not_0
			je		is_0
			loop	print_num	;decrement cx, repeat loop if cx <>0
			db		' ',0ah,0dh,'$'
			mov		ah,09	;to display string
			int		21h		;DOS interrupt
			
			ret
not_0:		mov		dx,output_d  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret
is_0		mov		dx,output_0  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret

main:		mov		al,00	;clear the screen
			mov		ah,06
			mov		bh,17h	;white on blue
			mov 	dh,05	;set row to 5
			mov		dl,10	;set column to 10
			int		10h		;screen handling
			mov		dx,message
			mov		ah,09	;to display string
			int		21h		;DOS interrupt to display string
			mov		ah,07	;single char keyboard input
			int 	21h		;DOS interrupt
			jmp		str_to_num ;convert str to num
			mov		si,dx	;si contains address of number string
line_loop:	mov		cx,7	;once for each line
			jmp		print_num ;print the number
			loop	line_loop ;decrement cx, repeat if cx<>0
			int 	20h

Open in new window

I'm not sure what you are confused about now.  However, perhaps I can use this time to address some of the more basic issues:
1) Line 70 should not be a JMP.
2) Line 69 is a INT call to get the input character.  As I indicated above, it comes back in AL.  As such, when you make the CALL (ok, big hint here) to str_to_num, you need to get the character value from AL, not from AH.  Further, there is no need for SI here.  

Free advice: Whenever you are coding, always use nice, simple subroutines that are very easy to debug.  You should also document them, much the same way that the INT calls are documented -- showing the input and output values.  Here's how I would handle this first routine.  Note how I first document the exact inputs and outputs -- this lets me remember later what the code is expecting.

;str_to_num takes the ASCII character provided in AL and verifies that it is a valid numeric digit.  
;  If the value is a valid digit, the numeric value is left in BL and BH = 1.
;  If the character is invalid, BH is set to 0 and BL should be ignored.
str_to_num:    
                  xor            bh,bh      ;bh = 0, sets error condition for easier exit
                  cmp            al,39h      ;ASCII "9"
                  jg            str_to_num_error      ; > 9 is invalid
                  sub            al,30h      ;ASCII "0"
                  jl            str_to_num_error      ; < 0 also invalid
                  mov            bl,al      ;put number in bl
                  xor            bl,F0h      ;clear high nybble
                  mov            bh,1h      ;Successful completion!
str_to_num_error:    
                  ret                        ;return

See how this break the task down into a nice, easy function?  Now, you can handle the error where you are calling this from by simply checking BH.  This will work just like your Qt "If" statement.

Another useful tidbit -- start at the beginning of a program and code and test each function, one at a time.  If you need to, write a simple test function.  Once the keyboard input is working, then get the str_to_num function working by simply calling str_to_num and then outputting the BH and BL values.  Once you are confident that THIS is working, then go on to the next step.  

Let's get the main part of the program working first, so that you can at least run the code for testing.  The NET result, when you are done with this round, will be to have the data confirmed as valid, and the BL register should be the numeric value of the digit to be displayed.

The NEXT step is to get SI loaded with the offset of the correct digit.  Once you know that BL is correct, doing this should be as simple as calculating the offset into the byte array.  If you are familiar with array storage, you'll know it to be thus:
    OffsetToArrayElementX = ArrayStart + (ElementSize * X)
I'll let you compute this function, too.
Avatar of Dmon443

ASKER

ok so now if I run it it crashes as soon as I enter a number. Also how do you know that the number is in the al register before the str_to_num executes? Because in the main to me it looks like it should be going into the ah register?!

			bits	16
			org		0x100
			jmp		main	;Jump to main program
message:	db		'Please enter a number(1-9)',0ah,0dh,'$'
enl_num1:	db		06h,06h,06h,06h,06h,06h,06h ; enlarged 1
enl_num2:	db		04h,0Ah,11h,02h,04h,08h,1Fh	; enlarged 2
enl_num3:	db		0Eh,11h,06h,18h,06h,01h,1Eh ; enlarged 3
enl_num4:	db		01h,02h,04h,0Ah,1Fh,02h,02h ; enlarged 4
enl_num5:	db		1Fh,10h,1Ch,02h,01h,02h,1Ch ; enlarged 4
enl_num6:	db		01h,02h,04h,08h,14h,14h,08h ; enlarged 5
enl_num7:	db		1Fh,01h,02h,04h,08h,10h,10h ; enlarged 7
enl_num8:	db		04h,0Ah,0Ah,04h,0Ah,0Ah,04h ; enlarged 8
enl_num9:	db		0Eh,11h,11h,0Fh,01h,01h,1Fh ; enlarged 9
error_mess: db		'**','$'
output_0:	db		' ' 
output_d:	db		'$'		

str_to_num:
			xor		bh,bh	;bh = 0
			;mov 	cx,10	
			;mov	si,al	;put number in si
			;mov	bl,[si]	;move memory contents pointed to by si to bl
			cmp		al,39h	;ASCII for 9
			jg		str_to_num_error	; > 9 is invalid
			sub		bl,30h	;convert to numeric
			jl		str_to_num_error	; < 0 also invalid
			mov		bl,al	;put number in bl
			mov		bh,1h	;succesful completion
			imul	cx		; 32 bit result
			ret				;return
str_to_num_error:
			ret				;return
error:		
			mov		dx,error_mess ;adding ** to number that is not 1-9
			mov		ah,09	      ;to display string
			int		21h		      ;DOS interrupt
			jmp		message
			ret				      ;return

print_num:	
			mov		cx,8	; cx=8 loop counter
    		mov		al,[si]	;move one byte into al
			push 	ax		;push onto stack
			inc		si		;point to next byte
			cmp		dh,0	;test for 0
			jne		not_0
			je		is_0
			loop	print_num	;decrement cx, repeat loop if cx <>0
			db		' ',0ah,0dh,'$'
			mov		ah,09	;to display string
			int		21h		;DOS interrupt
			
			ret
not_0:		mov		dx,output_d  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret
is_0		mov		dx,output_0  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret

main:		mov		al,00	;clear the screen
			mov		ah,06
			mov		bh,17h	;white on blue
			mov 	dh,05	;set row to 5
			mov		dl,10	;set column to 10
			int		10h		;screen handling
			mov		dx,message
			mov		ah,09	;to display string
			int		21h		;DOS interrupt to display string
			mov		ah,07	;single char keyboard input
			int 	21h		;DOS interrupt
			call	str_to_num ;convert str to num
			mov		si,dx	;si contains address of number string
line_loop:	mov		cx,7	;once for each line
			jmp		print_num ;print the number
			loop	line_loop ;decrement cx, repeat if cx<>0
			int 	20h

Open in new window

Can you single-step with the debugger to find the point of the crash?

I know that the character is in AL because I pulled out my old documentation on the INT 21h function call.  You are using "Direct Character Input Without Echo", which is documented as:  
Registers on Entry: AH: 7
Registers on Return: AL: Character

Your str_to_num is almost done, but line 29 has to go, as it serves no purpose.  Also, AFTER the call on Line 74, you need to check the value in BH.  If this is 1, print the error message and loop around to get a new character.  You can only continue if BH=0 on the return from str_to_num. If you included my comments, this would be MUCH more clear.

Once you have error checking, remember that the value of the digit is now in BL.  I have NO idea why you are transferring DX into SI in Line 75....

Get this all working FIRST.  Then, you should create a new routine to calculate the starting offset of the printable digit data.  Here's the comment for the routine:
; calc_digit_offset takes the valid digit (in BL) and determines the location of the
;   byte array for the indicated digit.  It returns the memory location of the digit
;   data in SI.  Because BL has already been validated, no error checking is done.

Then, create this function -- it should do exactly what the comment indicates (the mathematical calculation was in the last post).  You should then be able to call this routine as a replacement Line 75.
Avatar of Dmon443

ASKER

So I have done some more on the program...Here it is:
			bits	16
			org		0x100
			jmp		main	;Jump to main program
message:	db		'Please enter a number(1-9)',0ah,0dh,'$'
enl_num1:	db		06h,06h,06h,06h,06h,06h,06h ; enlarged 1
enl_num2:	db		04h,0Ah,11h,02h,04h,08h,1Fh	; enlarged 2
enl_num3:	db		0Eh,11h,06h,18h,06h,01h,1Eh ; enlarged 3
enl_num4:	db		01h,02h,04h,0Ah,1Fh,02h,02h ; enlarged 4
enl_num5:	db		1Fh,10h,1Ch,02h,01h,02h,1Ch ; enlarged 4
enl_num6:	db		01h,02h,04h,08h,14h,14h,08h ; enlarged 5
enl_num7:	db		1Fh,01h,02h,04h,08h,10h,10h ; enlarged 7
enl_num8:	db		04h,0Ah,0Ah,04h,0Ah,0Ah,04h ; enlarged 8
enl_num9:	db		0Eh,11h,11h,0Fh,01h,01h,1Fh ; enlarged 9
error_mess: db		'**','$'
output_0:	db		' ' 
output_d:	db		'$'		

str_to_num:
			xor		bh,bh	;bh = 0
			;mov 	cx,10	
			;mov	si,al	;put number in si
			;mov	bl,[si]	;move memory contents pointed to by si to bl
			cmp		al,39h	;ASCII for 9
			jg		str_to_num_error	; > 9 is invalid
			sub		bl,30h	;convert to numeric
			jl		str_to_num_error	; < 0 also invalid
			mov		bl,al	;put number in bl
			mov		bh,1h	;succesful completion
			ret				;return
str_to_num_error:
			ret				;return
error:		
			mov		dx,error_mess ;adding ** to number that is not 1-9
			mov		ah,09	      ;to display string
			int		21h		      ;DOS interrupt
			jmp		message
			ret				      ;return

print_num:	
			mov		cx,8	; cx=8 loop counter
    		mov		al,[si]	;move one byte into al
			push 	ax		;push onto stack
			inc		si		;point to next byte
			cmp		dh,0	;test for 0
			jne		not_0
			je		is_0
			loop	print_num	;decrement cx, repeat loop if cx <>0
			db		' ',0ah,0dh,'$'
			mov		ah,09	;to display string
			int		21h		;DOS interrupt
			
			ret
not_0:		mov		dx,output_d  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret
is_0		mov		dx,output_0  ;address of output
			mov		ah,09		 ;service - display message
			int		21h			 ;DOS system call
			ret

main:		mov		al,00	;clear the screen
			mov		ah,06
			mov		bh,17h	;white on blue
			mov 	dh,05	;set row to 5
			mov		dl,10	;set column to 10
			int		10h		;screen handling
			mov		dx,message
			mov		ah,09	;to display string
			int		21h		;DOS interrupt to display string
			mov		ah,07	;single char keyboard input
			int 	21h		;DOS interrupt
			call	str_to_num ;convert str to num
			cmp		bh,1h	;test if bh contains 1
			je		error	;print error
			cmp		bh,0h	;test if bh contains 0
			mov		si,bl	;si contains address of number string
line_loop:	mov		cx,7	;once for each line
			jmp		print_num ;print the number
			loop	line_loop ;decrement cx, repeat if cx<>0
			int 	20h

Open in new window


I also tried to run it in debug but I can't get it to work, maybe I am using the wrong commands? After I run debug in a dos prompt I enter:
-n pro1.com
-l
File not found

So I get file not found error and I am running debug from the directory that the pro1.com file is situated with all the other files NASM has created .asm and .lst.
I also tried
-l pro1.com
which came back with an error
I seem to be doing all of the heavy lifting here.  You have received some instruction on assembler, right?

We are still fixing up str_to_num, and you haven't even started Calc_Digit_Offset yet.  Again, this is not the idea of EE -- if you want someone else to write the code, then outsourcing it makes the most sense.  This is something that I would charge $250 for within my own business.

A few more suggestions, then re-read my previous post and get to work:
1) Delete Lines 20,21,22, as they are useless.
2) Delete Line 29, as it is redundant. (Not that without it, we just fall into the RET anyway.)
3) Delete Line 27.  When you do the SUB, the result is in BL, so moving AL to BL is senseless here.
4) You handling of the error at Line 75 is incorrect.  Remember that assembler is VERY simplistic.  If the number is invalid, you are JUMPing to the error, so when you return, the program exits.  This is probably not what you want.  Instead, put in a CALL to the error printout routine, then JUMP back to earlier in the process for the loop.  Then, add another label and skip these two instructions if the number is valid.
Avatar of Dmon443

ASKER

No never received any instruction on assembler before, you know it's weird I just wrote random letters and numbers and it happened to turn into assembly code without receiving any instruction what so ever. Lines 20, 21 and 22 are already commented out but I shall delete them so as to make you happier. I see that this program is a little too ambitious for my first try so I will study more and when I am a little more proficient, try again. I do understand the logic of how to do it since I can write it in another language, I just don't know assembly well enough to convert it.
ASKER CERTIFIED SOLUTION
Avatar of Bill Bach
Bill Bach
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Dmon443

ASKER

wow that's a really complicated way to make a for loop. Thanks for all your help, I learned a lot.