Kłopot z assemblerem

0

Witam, czy ktoś mógłby wyjaśnić w czym tkwi błąd?
polecenie: Napisać program, króty pobiera łańcuch znakowy z wejścia, dodaje kody ASCII wszystkich znakoów wchodziących w jego skład oraz wypisuje tą sume na ekranie. Program powinien być w stanie powtarzac operacje dopóki użytkownik nie naciśnie klawisza "+". Wykorzystać procedury i stos. Przewidzieć sytuacje wyjątkowe.

org 100h

	start:	
	
	mov cx,0 ;czyszczenie licznika
	petla:
		mov ah,1
		int 21h
		xor ah,ah
		
		cmp al,43    			;wyjscie jesli '+'
		je koniec
		
		cmp al,13				;koniec pobierania dla 'entera'
		je	koniec_czytania
		jne na_stos
			
	na_stos:
			push ax
			add cx,2
			call na_asci
			xor bx,bx
			jmp petla
			
	na_asci:
		mov bx,10
		p3:
			div bx
			add dx,48
			mov [napis+di],dx
			inc di
			xor dx,dx
			cmp ax,0
			jz powrot
			jnz p3
			
	koniec_czytania:
		inc di
		mov byte [napis+di],"$"
		call wyswietlanie
		call powrot_bp
		call wyswietlnie_asci
		mov bx,cx
		call czyszczenie_stosu
		xor cx,cx
		xor di,di
		jmp petla
		
	czyszczenie_stosu:
		p2:
			cmp bx,0
			je powrot
		
			pop dx
			sub bx,2
			jmp p2
	
	wyswietlanie:
		
		pop dx  ;sciagamy powrot karetki zostajacy po enterze
		push bp
		mov bp,sp
		add bp,cx
		p1:
			cmp cx,0
			je powrot
			
			mov ah,2
			mov dx,[bp]
			int 21h
			sub bp,2
			sub cx,2
			jmp p1
		
	wyswietlnie_asci:
		p4:
			mov ah,2
			mov dx,[napis+di]
			dec di
			int 21h
			cmp di,0
			je powrot
			jne p4
			
			
	powrot_bp:
		;mov ah,9
		;mov dx,napis
		;int 21h
		;xor dx,dx
		mov ah,9
		mov dx,nowa_linia
		int 21h
		pop bp
		ret	
	
	powrot:
		ret
	
	koniec:
		mov	ax, 4C00h
		int	21h
		
	
	section .data:
	nowa_linia db 10,13,"$"
	napis times 512 db 0
1

Napisz czym kompilujesz, jakie dane wejściowe podajesz i jaki masz efekt końcowy.
Napisz też jakiego debugera próbowałeś używać.

0
vpiotr napisał(a):

Napisz czym kompilujesz, jakie dane wejściowe podajesz i jaki masz efekt końcowy.
Napisz też jakiego debugera próbowałeś używać.

Program do kompilacji to NASM, dane wejściowe z klawiatury, program zatrzymuje się po pierwszym znaku (nie można nic wpisać, wcisnąć itp). Podejrzewam, że problem może leżeć w procedurach, ale nie jestem pewien.

Co do debugera to http://nevar.pl/asm/debugger

1

Skoro to 16 bit to odpal to sobie w jakimś emu8086 i przeleć krok po kroku przez ten kod i zobacz gdzie się dzieje coś dziwnego.

0

Stąd moje podejrzenia, po wejściu do debugera znaki pobierane są prawidłowo, problem pojawia się na poziomie "call wyswietl" (mianowicie nie wraca z procedury, tylko zaczyna zapętlać się w krokach których nie ma w programie). Problem w tym iż nie widzę błędu w kodzie.

1

aaa no ja sie nie dziwie że to nie działa. Robisz tam wszędzie call i jakoś lewo łączysz je z ret! Przecież to się musi parować! call cośtam oznacza mniej więcej tyle co:

push ip
jmp cośtam

Ale żeby potem z tego wrócić należy zrobić ret czyli mniej więcej

pop ip
jmp ip

bo chcesz wrócić w miejsce z którego wywołałeś daną funkcje. A ty robisz jakieś cuda na kiju, i skoki do jakiegoś generycznego ret. WTF? Nie dziwota że "nie widzisz" błędu, bo kod jest napisany nieczytelnie.

Zacznijmy od tego że robisz: call wyswietlanie a pierwsza linijka tej funkcji to pop dx i wbrew twojemu komentarzowi ta instrukcja zabierze ze stosu adres powrotu z funkcji, bo oczywiście oznacza że jak juz trafimy na jakieś ret to jako adres powrotu wczytane zostaną jakieś losowe bajty ze stosu...

0

Dzięki, rzeczywiście mój błąd. Lecz problem nie jest do końca rozwiązany, mianowicie w debugerze program przechodzi i zapętla się tak jak powinien, ale po odpaleniu bez debugera nadal zatrzymuje się na pierwszym znaku, bez możliwości wpisania kolejnego znaku :(

0

Cudów nie ma, coś robisz tam źle, ale w takim spaghetti trudno stwierdzić co dokładnie. Z dziwnych rzeczy to ja w ogóle nie rozumiem tego twojego "na ascii", bo nijak sie ma do treści zadania. Przecież wczytując znaki wczytujesz właśnie numeryczne wartosci ich kodów ascii. Dodatkowo nie bardzo rozumiem po co robisz push ax skoro nigdy z tego nie korzystasz.

0

To moj pierwszy projekt w assemblerze, wiec nie jest niestety zbyt czytelny. Czy mógłbyś pomóc nanieść odpowiednie poprawki (projekt na dziś na 17 :P)

Na ascii mial dodawac dziesietny numer ascii do napisu

0

Moje rozwiązanie miało polegać na tym że:

  1. pobieramy znak z klawiatury i sprawdzamy czy to enter albo +
  2. jeśli nie dodajemy na stos (push ax z wcześniej wyczyszczonym ah, wrzuci sam znak z al)
  3. wywołujemy na_ascii, która dodaje do naszego napisu po kolei znaki z kodu ascii danego znaku
  4. powtarzamy aż do entera lub +
  5. po enterze dodajemy do napisu "$"
    5.1. pop dx zdejmujemy powrót karetki
    5.2. wyświetlamy ze stosu od tylu
    5.3. wyświetlamy napis od tylu
    5.5. czyścimy stos i powtarzamy cały program
  6. po + kończymy program
0

Problemem jest iż, przez debuger program przechodzi i się ładnie zapętla, a odpalając normalnie zacina się po pierwszym znaku.

0

Ale ty nie masz tego odpalić z debugerem i cieszyć się ze działa, tylko przeklikać to krok po kroku, analizujac zawartość stosu i rejestrów i sprawdzić kiedy dzieje sie coś dziwnego. Debugger ma to do siebie że np. zeruje pewne wartości i dzięki temu "przypadkiem" coś moze działać.

0

Od 3 dni próbuje, ale niestety nic z tego nie wychodzi (dlatego proszę o pomoc tutaj) :D

0

Tutaj próbowałem używając mniej stosów. Może znajdziecie błąd, ponieważ po wciśnięciu entera program zamraża się.

org 100h

	start:
	xor di,di
	petla:
		mov ah,1
		int 21h
		xor ah,ah
		
		cmp al,43    			;wyjscie jesli '+'
		je koniec
		
		cmp al,13				;koniec pobierania dla 'entera'
		je	koniec_czytania
		jne do_napisu
	
	do_napisu:
		mov [napis+di],ax
		inc di
		jmp petla
		
	koniec_czytania:
		push di
		mov cx,di
		xor di,di
		p1:
			cmp cx,0
			je	wyswietlanie
			
			call na_asci
			jmp p1
			
			
	na_asci:
		mov bx,10
		p4:
			cmp cx,0
			je powrot
			jne	kazdy_jeden

	kazdy_jeden:
		mov al,[napis+di]
		dec cx
		inc di
		p6:
			div bx
			add dx,48
			mov [napis_ascii+si],dx
			xor dx,dx
			inc si
			cmp ax,0
			jz p4
			jnz p6
				
	wyswietlanie:
		pop cx
		push si
		xor di,di
		p2:
			mov ah,2
			mov dl,[napis+di]
			xor dh,dh
			int 21h
			inc di
			cmp cx,di
			je wyswietlanie_asci
			jne p2
	
	wyswietlanie_asci:
		xor cx,cx
		pop cx
		xor si,si
		p3:
			mov ah,2
			mov dl,[napis_ascii+si]
			xor dh,dh
			int 21h
			inc si
			cmp cx,si
			je petla
			jne p3
			
			
		

	
	powrot:
		ret
	
	koniec:
		mov ax,4c00h
		int 21h
	
	napis times 512 db 0
	napis_ascii times 1024 db 0

czy to, że używam di i si jako liczników może powodować błędy?

0

Na moje oko to czarujesz tam ze stosem i zdejmujesz gdzieś za dużo/ za mało. Analizowanie takiego stosowego kodu to zawsze problem. Moja rada:

  1. Pozbądź się tego czarowania stosem!
  2. Napisz do każdej funkcji jasny komentrarz w którym napiszesz w jakich rejestrach funkcja przyjmuje parametry.
  3. Przed wywołaniem danej funkcji ustaw parametry w rejestrach a potem wywołaj. Jeśli trzymasz akurat coś ważnego w rejestrze to zrób push przez wywołaniem funkcji i pop po wywołaniu. Czyli np.
; input: ax, dx
; output: cx
funkcja:
    xor cx, cx
    add cx, bx
    add cx, ax
    ret

main:
    ; pamiętamy rejestry przed wywołaniem funkcji
    push ax
    push bx
    push cx
    mov ax, 1
    mov bx, 2
    call funkcja
    ; teraz w cx mamy 1+2, cośtam z nim robimy
    ; przywracamy poprzednie wartości rejestrów
    pop cx
    pop bx,
    pop ax

0

Przystosuj do własnych potrzeb:

STATIC_DOS_INTERRUPT		equ	0x21

STATIC_DOS_KEY_READ		equ	0x0100
STATIC_DOS_PRINT_CHAR		equ	0x0200
STATIC_DOS_EXIT			equ	0x4C00

STATIC_ASCII_ENTER		equ	0x0D
STATIC_ASCII_PLUS		equ "+" 

STATIC_DIGIT_TO_ASCII		equ	0x30

STATIC_EMPTY			equ	0x2B

STATIC_BYTE_MASK		equ	0xFF

STATIC_NUMBER_SYSTEM_DECIMAL	equ	0x0A

;===============================================================================
main:
	; suma
	xor	ecx,	ecx

.loop:
	; pobierz klawisz w klawiatury
	mov	ax,	STATIC_DOS_KEY_READ
	int	STATIC_DOS_INTERRUPT

	; usuń śmieci
	and	eax,	STATIC_BYTE_MASK

	; anulowanie operacji?
	cmp	al,	STATIC_ASCII_PLUS
	je	.end	; tak

	; wyświetl wynik?
	cmp	al,	STATIC_ASCII_ENTER
	je	.print	; tak

	; dodaj do sumy
	add	ecx,	eax

	; kontynuuj
	jmp	.loop

.print:
	; suma istnieje?
	test	ecx,	ecx
	jz	.end	; nie, koniec programu

	; załaduj sumę do akumulatora
	mov	eax,	ecx

	; baza - system dziesiętny
	mov	ebx,	STATIC_NUMBER_SYSTEM_DECIMAL

	; brak reszty, starszej części
	xor	edx,	edx

	; wskaźnik rozmiaru liczby na stosie
	mov	ebp,	esp

.prepare:
	; wylicz pierwszą cyfrę
	div	ebx

	; zachowaj
	push	edx

	; usuń informacje o cyfrze
	xor	dl,	dl

	; przeliczać dalej?
	test	eax,	eax
	jnz	.prepare	; tak

	; wyświetl wszystkie cyfry wchodzące w skład sumy
	mov	ax,	STATIC_DOS_PRINT_CHAR

.digit:
	; koniec stosu?
	test	bp,	sp
	je	.end	; tak

	; pobierz cyfrę do wyświetlenia
	pop	edx

	; zamień cyfrę na kod ASCII i wyświetl
	add	dl,	STATIC_DIGIT_TO_ASCII
	int	STATIC_DOS_INTERRUPT

	; kontynuuj z pozostałymi cyframi sumy
	jmp	.digit

.end:
	; zakończ proces
	mov	ax,	STATIC_DOS_EXIT
	int	STATIC_DOS_INTERRUPT

1 użytkowników online, w tym zalogowanych: 0, gości: 1