Assembler wyświetlanie napisu

0

Witam!
Potrzebuję wyświetlić na konsoli pierwszą literkę z ciągu znaków. Mam następujący kod, który nie działa:

.intel_syntax noprefix
.global main
.text
	main:
		mov eax, offset mesg 
                mov bl, [eax]
		push ebx 
		call printf 
		pop ebx
		mov ebx, 0
                mov eax, 0
		ret 


.data
mesg:
.asciz "Hello, world\n"

Całość przy gcc dodaje -m32.
Nie mam pojęcia jak wyswietlic ta literkę.
Liczę na pomoc, Pozdrawiam :)

0

NASM avoids this undesirable situation by having a much simpler syntax for memory references. The rule is simply that any access to the contents of a memory location requires square brackets around the address, and any access to the address of a variable doesn't. So an instruction of the form mov ax,foo will always refer to a compile-time constant, whether it's an EQU or the address of a variable; and to access the contents of the variable bar, you must code mov ax,[bar].

This also means that NASM has no need for MASM's OFFSET keyword, since the MASM code mov ax,offset bar means exactly the same thing as NASM's mov ax,bar. If you're trying to get large amounts of MASM code to assemble sensibly under NASM, you can always code %idefine offset to make the preprocessor treat the OFFSET keyword as a no-op.

https://nasm.us/doc/nasmdoc2.html

NASM nie posiada w swojej składni słowa kluczowego OFFSET. To nie MASM. Nie wspiera również takich instrukcji:

For this reason, NASM doesn't support the LODS, MOVS, STOS, SCAS, CMPS, INS, or OUTS instructions, but only supports the forms such as LODSB, MOVSW, and SCASD, which explicitly specify the size of the components of the strings being manipulated.

global _main
extern _printf

section .data

txt: db "Hello World", 0
format: db "%c", 0

section .text

_main:
xor eax, eax

mov eax, [txt]
push eax
push format
call _printf
add esp, 8


section .bss

Edit: //

global _main
extern _printf


section .data

txt: db "Hello World", 0

section .text

_main:
xor eax, eax

push ebp 
mov ebp, esp
sub esp, 0x28

mov eax, txt
push eax
mov word[esp+0x4], 0x420
call _printf

pop eax
ret

section .bss

Część kodu zaczępnąłem z: http://www.ccis.northeastern.edu/home/kapil/courses/cs3650f15/resources/unix-xv6-source/forktest.asm i wspierałem się: https://www.imada.sdu.dk/~kslarsen/dm546/Material/IntelnATT.htm

Powyższy kod wypisuje cały napis, dostosuj tak, jak chcesz, aby wypisywał jeden znak.

global _main
extern _printf

section .data

txt: db "Hello World", 0

section .text

_main:
xor eax, eax

push ebp 
mov ebp, esp
sub esp, 0x28

mov eax, txt
mov dword[eax+1], 0

push eax
;mov word[esp+0x4], 0x420

call _printf

pop eax
ret

Może dałoby się ten kod lepiej napisać, no ale jest napisany jak jest.
Korzystałem między innymi z tego źródła: https://forum.nasm.us/index.php?topic=2152.0



global _main
extern _printf

section .data
; 0xA
txt: db "Hello,World!", 10, 0
txt_len: equ $-txt

section .text

_main:



mov ecx, txt_len


l22:
	push ecx

	lea esi, [txt]

	add esi, ecx
	
	mov byte[esi+1], 0xA
	mov byte[esi+2], 0x00

	cmp ecx, 4
	je label_here
	
	pop ecx
	dec ecx
	cmp ecx, -1
jne l22

ret

label_here:

push esi
call _printf
add esp, 4

pop ecx

ret

section .bss

Wypisuje odpowiednią literę zależnie od liczby, którą jest przy tym cmp ecx, 4. Zamiast 4 można wpisać inną liczbę i wypisze inną literę.

global _main
extern _printf

section .data
; 0xA
txt: db "Hello,World!", 10, 0
txt_len: equ $-txt

section .text

_main:



mov ecx, txt_len


l22:
	push ecx

	lea esi, [txt]

	add esi, ecx
	
	mov byte[esi+1], 0xA
	mov byte[esi+2], 0x00

	push esi
	call _printf
	add esp, 4

	
	pop ecx
	dec ecx
	cmp ecx, -1
jne l22

ret

section .bss

Wypisuje wszystko odwrotnie.
//

mov eax, [txt+1], wyrzuci "e". Pamiętaj, że istnieje takie coś jak konwencja wywołania parametrów w funkcji. Dla printf to jest "od prawej do lewej", czyli najpierw tekst do wypisania, a później format. Popatrz na składnię instrukcji printf. http://www.cplusplus.com/reference/cstdio/printf/ Char ma 8 bitów, więc musisz napisać db, czyli 1 bajt. int printf ( const char * format, ... );

http://issc.uj.ac.za/assembler/NASM.pdf
http://www.student.chemia.uj.edu.pl/~mrozek/USl/oprogramowanie/dokumentacja/asm_pdf_linux.pdf
https://gynvael.coldwind.pl/?id=445
https://ww2.ii.uj.edu.pl/~kapela/pn/print-lecture-and-sources.php
http://fulmanski.pl/zajecia/x86/zajecia_20142015/book.pdf
<-- Konwencje wywołań.

0

W zasadzie to nadal nie wiem co to jest za składnia. Niby przypomina trochę MASM, trochę GNU Assembly, w każdym razie stworzyłem taki kod co drukuje literę H - nie wiem czy o to chodziło, ale podejrzewam, że chodziło Ci o wydrukowanie którejś z liter. Tak czy inaczej będziesz mógł sobie ten kod przebudować zgodnie z zadaniem, które masz do wykonania.

.intel_syntax noprefix
.global main
.text
    main:
        mov eax, offset mesg 
		mov bl, byte [eax-1]
		mov eax, offset format_char
		push ebx
		push eax
        call printf 
        pop eax
		pop ebx
        mov ebx, 0
        mov eax, 0
        ret 

.data
mesg:
.asciz "Hello, world\n"
format_char:
.asciz "%c\n"

Btw. polecam zapoznać się z debuggerem gdb, bo daje ogromne możliwości. ;) Możesz sobie chociażby wypróbować komendy z tego linku -> https://www.tutorialspoint.com/gnu_debugger/index.htm

1
Shizzer napisał(a):

W zasadzie to nadal nie wiem co to jest za składnia. Niby przypomina trochę MASM, trochę GNU Assembly

NASM ma swoją składnię. Moim zdaniem lepszą od MASM/TASM (bo bardziej spójną) i od GNU as (bo ta to w ogóle jakieś dziwactwo).

Ale to wyżej to GNU z .intel_syntax, czyli taka jakaś hybryda.

0

Tak w sumie to potrzebuję wypisać wszystko po kolei ale nadal nie mogę tutaj licznika ogarnąć co by mi iterował po ciągu znaków

0

Po co Ci tutaj jakikolwiek licznik? Przecież funkcje ze standardowej biblioteki C typu puts lub printf (chociaż nawiasem mówiąc kompilator przy wypisywaniu ciągu znaków bez podanego formatu i tak zamieni printf na puts) wypiszą ten ciąg znaków za Ciebie - wystarczy, że przekażesz do nich jego adres.

Nie wiem czy wiesz, ale ta dyrektywa .asciz jest po to, żeby z literału utworzyć C-String, czyli dany literał dzięki tej dyrektywie jest z automatu uzupełniany na końcu NULL-bytem -> http://bravegnu.org/gnu-eprog/asm-directives.html

0

Pierwszą literkę można wyświetlić korzystając z ciągu formatującego, jeśli musi być printf. Alternatywnie za pomocą write.
Przerobiony przykład podany przez ciebie:

.intel_syntax noprefix
.global main
.text
main:
  push ebp    /*tworzysz ramkę stosu*/
  mov ebp,esp

  push offset mesg    /*odłożenie argumentu na stosie*/
  push offset format    /*format na stos*/
  call printf
  add esp, 8   /*zdjęcie argumentów ze stosu*/

  mov esp,ebp    /*przywracasz poprzednią ramkę stosu*/
  pop ebp

  xor eax,eax    /*zwrócenie bez błedu (zerowanie eax)*/
		
  ret    /*wracasz z funkcji*/

.data
mesg:
.asciz "Hello, world\n"
format:
.asciz "%.1s\n"
0

Okej, troszkę skłamałem. Ostateczny cel to zamiana literek duże na małe a małe na duże, wypisując je kolejno jedna po drugiej, nie jako ciąg w jednej linii. Tak, wiem można by wpisać napis tak:

.intel_syntax noprefix
.global main
.text
	main:
		mov eax, offset mesg 
		push eax 
		call printf 
		add esp, 4 
		xor eax, eax 
		ret 

.data
mesg:
.asciz "Hello, world\n"

Ale to się mija z moim celem. Stąd mowa o liczniku i iterowaniu :P.

0

Tu masz kod zamieniający małe litery na duże i duże na małe. Napisałem go w NASM, bo tego Twojego Assemblera nie znam. Możesz sobie zamienić kod z NASM na swój Assembler. Skoro w jednym z komentarzy pisałeś, że masz to zrobić w ramach zajęć to powinieneś po prostu wiedzieć jakiego tam Assemblera używacie. Gdybyś mi napisał, o którego chodzi byłoby mi łatwiej się dostosować i może czegoś nowego nauczyć, a tak to musisz się męczyć samemu.

global main
extern printf
section .text
    main:
		push ebp
		mov ebp, esp
        mov eax, mesg
		mov ecx, 0
       	
		.loop_through_str:
			cmp byte [eax+ecx], 0			; check if our pointer points to the null-byte
			je .output
			cmp byte [eax+ecx], 65			; if ( *(mesg+idx) >= 'A' )
			jae .to_lower_check
			jmp .not_a_letter

		.to_lower_check:
			cmp byte [eax+ecx], 90			; if ( *(mesg+idx) <= 'Z' )
			jbe .to_lower
			cmp byte [eax+ecx], 97			; if ( *(mesg+idx) >= 'a' )
			jae .to_upper_check

		.to_upper_check:
			cmp byte [eax+ecx], 122			; if ( *(mesg+idx) <= 'z' )
			jbe .to_upper
			jmp .not_a_letter				; else { continue; }
		
		.to_lower:
			xor edx, edx
			mov dl, byte [eax+ecx]
			add dl, 32
			mov byte [eax+ecx], dl
			inc ecx
			jmp .loop_through_str

		.to_upper:
			xor edx, edx
			mov dl, byte [eax+ecx]
			sub dl, 32
			mov byte [eax+ecx], dl
			inc ecx
			jmp .loop_through_str

		.not_a_letter:
			inc ecx						; continue
			jmp .loop_through_str

		.output:
			push eax
			call printf
        xor eax, eax 
		leave
        ret 

section .data
mesg: db "Hello, world", 0xa, 0
0

Sama pętla wypisywania może wyglądać następująco (litery będą oddzielone spacjami - patrz "format"):

print:
  mov esi, offset mesg
  xor ebx,ebx
  print_loop:
    mov bl,[esi]
    cmp bl,0
    jz finish

    push ebx
    push offset format
    call printf
    add esp, 8    /*zdjęcie argumentu ze stosu*/
    inc esi

    jmp print_loop
finish:

...
.data
format:
.asciz "%c "

Natomiast zamiana liter z małych na duże i na odwrót możesz zrobić za pomocą czegoś takiego:

		cld
		mov esi, offset mesg
		mov edi,esi

loop:
		lodsb
		cmp al,0
		jz print
small:	
/*Twój kod sprawdzający i zmieniający z małej na dużą (może mieć 6 linii)
big:
/* Twój kod sprawdzający i zamieniający z dużej na małą (może mieć 5 linii)*/
next:
		stosb
		jmp loop

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