Assembler - obcinanie 'ogonków' polskim znakom

0

Witam,

Piszę mój pierwszy program w assemblerze, a konkretniej dll'kę z funkcją funkcją szyfrującą przy użyciu szyfru Beaufort'a znak podany w parametrze używając klucza podanego jako drugi parametr. Zasadniczo program działa tak jak powinien, ale muszę go trochę udoskonalić.

Rzecz w tym żeby program sprawdzał czy znak podany w parametrze jest polskim znakiem, a jeśli tak, to ma go zamienić na jego odpowiednik w alfabecie łacińskim (czyli np ą -> a , ę -> e, ł -> l etc.). Próbowałem coś takiego napisać ale mój pomysł nie działa. Oto mój kod (w kodzie jest tylko sprawdzanie znaku 'Ą', jak to ogarnę to z pozostałymi sobie sam poradzę):

.686 
.387
.model flat, stdcall 
.xmm
.data


.code

cipherCharAsm proc uses ebx znak:DWORD, klucz:DWORD
				mov eax, klucz      ;wrzucenie klucza do EAX
				mov ebx, znak       ;wrzucenie znaku do EBX

				cmp eax, 'Ą'          ;sprawdzenie czy klucz w EAX to 'Ą'
				jne szyfruj            ;jeśli nie, to przejdź to szyfrowania
				mov eax, 'A'          ;jeśli tak, to wrzuć 'A' do EAX

szyfruj:			or eax, 20h           ;podnieś klucz do uppercase'a
				or ebx, 20h           ;podnieś znak do uppercase'a
				sub ebx, 41h         ;odejmij od znaku 41h (czyli uzyskaj jego wartość liczbową, A=0, B=1, C=2, .. itd)
				sub eax, ebx         ;odejmij od klucza wartość liczbową znaku

				cmp eax, '@'         ;sprawdź czy otrzymany znak jest mniejszy od 'A'
				jg zwroc               ;jeżeli nie to przejdź do zwracania
				add eax, 1Ah         ;jeżeli tak, to dodaj do niego 1Ah


zwroc:			ret                      ;wartość jest zwracana przez akumulator


cipherCharAsm endp 

end 
0

Przeczytałem, wydaje mi się że zrozumiałem, ale w dalszym ciągu nie wiem jak rozwiązać mój problem. Chodzi o to, że w programie przekazuję do procedury znaki char, które mają inne kodowanie niż znaki w assemblerze, tak? W takim razie jakiego kodowania używa MASM? Ewentualnie jak zmienić te kodowanie?

0
  1. Kodowanie znaków podanych do programu może zależeć od konsoli w której je wpisujesz. Sprawdź jak koduje konsola której używasz.
  2. Kodowanie znaków podanych w ten sposób w kodzie jak dałeś to Ą zależy najpewniej od kodowania znaków w pliku z kodem źródłowym.
  3. Mówisz że robisz z tego dll więc wysyłasz znak do tej procedury z innego języka programowania. To oznacza że musisz sprawdzić jak ten język koduje domyślnie dane w swoich stringach!
0

cmp eax, '@' ;sprawdź czy otrzymany znak jest mniejszy od 'A'

jeśli A to dlaczego @?

jg zwroc ;jeżeli nie to przejdź do zwracania

jg i jl służą do liczb ze znakiem. używaj ja i jb do wartości bez znaku.

a problem z polskimi znakami pewnie polega na innym kodowaniu użytym w DLL i w EXE.

0
Azarien napisał(a):

cmp eax, '@' ;sprawdź czy otrzymany znak jest mniejszy od 'A'

jeśli A to dlaczego @?

Sam nie wiem dlaczego, tak jak pisałem to mój pierwszy kontakt z assembler'em, ale kiedy dam 'A' to program go "nie łapie", tak jakby assemblerowe jg działało jak<=, a @ jest zaraz pod 'A'

A więc tak: zarówno dll'ka jak i główny program (C++, CLR Forms, pisane w VS2013) mają w właściwościach projektu ustawione kodowanie na UNICODE. Postanowiłem obejść ten problem trochę na około i przerobiłem główny program tak, by przed wywołaniem procedury z dll'ki rzutował przesyłane char'y na int'a i wyświetlił je szestnastkowo. I tu się nieco zdziwiłem. O ile łacińskie znaki mają kodowanie zgodne z ASCII, to np dla litery 'Ą' wartość ta wynosi ffffffa5 :O Wzorując się na tej wartości postanowiłem znaleźć kodowanie w którym znak 'Ą' ma taką właśnie wartość. Ale niestety na stronie http://www.fileformat.info/info/unicode/char/104/charset_support.htm nie znalazłem takiego kodowania.

Mniejsza z tym, uznałem że w takim razie zamiast porównywać wprowadzony do dll'ki znak z 'Ą' porównam go właśnie z ffffffa5.I tu pojawił się kolejny problem. W linii

cmp eax, 0xffffffa5h

wywala błąd error A2206: missing operator in expression Co robię źle?

EDIT: Teraz jest jeszcze ciekawiej. Pomyślałem że to może kwestia zapisu pliku w różnym kodowaniu jest przyczyną, więc w File->Advanced Save Options... zmieniłem kodowanie pliku na "Unicode (UTF-8 with signature) - Codepage 65001" i od tego momentu program kompletnie się nie kompiluje. Już w pierwszej linii

.686

wywala error A2044: invalid character in file Dodam że przedtem nie było wybrane "żadne" kodowanie, tj. na rozwijanej liście nie był wybrany żaden element, a teraz już nie da się wrócić do poprzedniego stanu.

0

pokaż jak ten kod wywołujesz. bez kombinowania z rzutowaniem.

UNICODE nie ma tu raczej nic do rzeczy, w C++ char to char niezależnie od tego czy jest #define UNICODE czy nie. to makro jest używane w WinAPI.

1

@RedMajkel

  1. Dodałeś BOM do pliku więc na pewno sie nie skompiluje :P Notepad++ ma opcje "konwertowania" na inny format i możesz tam zrobić sobie "UTF-8 bez BOM".
  2. Nie bardzo rozumiem czemu się zdziwiłeś skoro twierdzisz że przeczytałeś artykuł podlinkowany przeze mnie wyżej. Gdybyś go przeczytał ze zrozumieniem to powinno być dla ciebie oczywiste czemu znaki ASCII są "takie same" także UTF-8 a jednocześnie czemu inne symbole zajmują znacznie wiecej niż 1 bajt... Przeczytaj może jeszcze raz?
  3. A skąd u ciebie to 0x? Asembler zakłada że jak kończysz liczbę na h to jest hexem, nie trzeba żadego 0x. Ale wychodzi tutaj że nie bardzo rozumiesz co robisz bo w kodzie wyżej masz np. add eax, 1Ah i tutaj jakoś wiedziałeś że nie trzeba żadnego prefixu 0x przed liczbą.

Moja rada:

  • przeczytaj jeszcze raz, ze zrozumieniem o kodowaniach
  • nie pisz kodu którego nie rozumiesz! jeśli nie wiesz czemu jakaś linijka wygląda tak jak wygląda to nie kopiuj jej na pałę "bo działa", tylko doczytaj żeby zrozumieć.
2

A jakim kodowaniem w takim razie obslugiwane są znaki spoza ASCII?

Pod Windows? Aktualnie ustawioną w systemie „stroną kodową”. Przy czym w konsoli to może być inne kodowanie niż w okienkach.
Dla języka polskiego domyślnie są to kodowania 852 i 1250.

No i po coś jest też przecież wchar_t?

wchar_t pod Windows jest w UTF-16.

Ale to czy we właściwościach projektu ustawiono UNICODE czy nie, nie zmienia czym jest char i czym jest wchar_t.
Ma wpływ na makro TCHAR, które jest z#definiowane jako char jeśli nie ma UNICODE, a wchar_t jeśli jest.

0
Azarien napisał(a):

pokaż jak ten kod wywołujesz. bez kombinowania z rzutowaniem.

typedef char (__stdcall *BC)(char, char);

BC beaufort;

//... 
HINSTANCE beaufortAsm = LoadLibrary(L"AsmDll.dll");
//...
beaufort = (BC)GetProcAddress((HMODULE)beaufortAsm, "cipherCharAsm");
std::string zaszyfrowany;

typedef struct{
	char znakTekstu;
	char znakKlucza;
	int indexZnaku;
} doZaszyfrowania;

void __cdecl ThreadProc(void* args) // funkcja otwierana w nowym watku wywolujaca funkcje szyfrujaca
{

	doZaszyfrowania *argumenty = (doZaszyfrowania*) args;
	char zaszyfrowanyZnak = beaufort(argumenty->znakTekstu, argumenty->znakKlucza); // wywolanie funkcji szyfrujacej
	zaszyfrowany[argumenty->indexZnaku] = zaszyfrowanyZnak;



	_endthread();
}
Shalom napisał(a):

@RedMajkel

  1. Dodałeś BOM do pliku więc na pewno sie nie skompiluje :P Notepad++ ma opcje "konwertowania" na inny format i możesz tam zrobić sobie "UTF-8 bez BOM".
  2. Nie bardzo rozumiem czemu się zdziwiłeś skoro twierdzisz że przeczytałeś artykuł podlinkowany przeze mnie wyżej. Gdybyś go przeczytał ze zrozumieniem to powinno być dla ciebie oczywiste czemu znaki ASCII są "takie same" także UTF-8 a jednocześnie czemu inne symbole zajmują znacznie wiecej niż 1 bajt... Przeczytaj może jeszcze raz?
  3. A skąd u ciebie to 0x? Asembler zakłada że jak kończysz liczbę na h to jest hexem, nie trzeba żadego 0x. Ale wychodzi tutaj że nie bardzo rozumiesz co robisz bo w kodzie wyżej masz np. add eax, 1Ah i tutaj jakoś wiedziałeś że nie trzeba żadnego prefixu 0x przed liczbą.

Moja rada:

  • przeczytaj jeszcze raz, ze zrozumieniem o kodowaniach
  • nie pisz kodu którego nie rozumiesz! jeśli nie wiesz czemu jakaś linijka wygląda tak jak wygląda to nie kopiuj jej na pałę "bo działa", tylko doczytaj żeby zrozumieć.
  1. Z tym sobie zaraz poradziłem, ale problem pozostał taki, że przy kompilowaniu VS i tak zawsze zapisuje plik jako UTF-8, wywala error, wtedy muszę zmienić jego kodowanie w notepad++, i dopiero wtedy mogę kompilować. Trochę to uciążliwe teraz.
  2. Ja wszystko rozumiem, że podstawowe znaki "są takie same", a reszta już zależy od konkretnego kodowania, i mogą zajmować więcej bajtów. Bardziej zdziwiła mnie konkretna jego wartość, bo takiej w żadnym kodowaniu nie potrafiłem znaleźć.
    3.0xwzięło się z tego, że kiedy próbowałem kompilować to z samym 'h' na końcu to wywalało error error A2006: undefined symbol : ffffffa5h
    Dlatego zastosowałem się do rady if the first character of the number is A-F you must have a leading 0. If it is a number, like in your example, you can have it as well, but don't need to którą znalazłem na stackoverflow (http://stackoverflow.com/questions/17504300/probleme-with-error-a2206-missing-operator-in-expression#comment25446925_17504357)

EDIT: Poradziłem sobie, ale po prostu obszedłem to na około, i chyba nie do końca wszystko rozumiem. Zmieniłem char na wchar_t.
Teraz wyświetlając przerzutowaną na int wartość 'Ą' otrzymałem ffa5 (I tu pierwsza niejasność z mojej strony - skoro jak napisał Azarien, wchar_t jest kodowany w UTF-16, to czy nie powinno być feff0104 ?? Czy to ja w dalszym ciągu czegoś tu nie rozumiem? ).

No ale dalej nie działało zarówno:

cmp eax, ffa5h

jak i

cmp eax, 0xffa5

oraz

cmp eax, 0xffa5h

Dopiero kiedy przeliczyłem tą wartość na wartość dziesiętną i ją wstawiłem do programu - zadziałało:

cmp eax, 65445

Możecie mi wytłumaczyć czemu żadna z wpisywanych przeze mnie wersji hex nie zadziałała?

EDIT2: Ehhh, idiota ze mnie :D rada z stackoverflow była dobra, tylko ja ją źle interpretowałem - chodziło o same '0' jako prefiks, nie '0x' jak cały czas wpisywałem :D Anyway, dzięki za pomoc że naprowadziliście mnie na rozwiązanie :D jeśli tylko ktoś mógłby mi wytłumaczyć dlaczego kod dla wchar_t 'Ą' to ffa5, skoro w UTF-16 ma on całkiem inną wartość, to będę bardzo wdzięczny :)

1

UTF-8 to kodowanie, w którym jeden znak zajmuje od jednego do czterech bajtów.
Napisz sobie w pliku tekstowym jedną literę (bez entera), zapisz jako UTF-8 bez BOM i zobacz że plik ma inną długość dla A a inną dla Ą.

W UTF-16 jeden znak to dwa albo cztery bajty. Przy czym na czterech bajtach jest taka egzotyka jak egipskie hieroglify, więc w praktyce można to olać i traktować jako kodowanie dwubajtowe. Wtedy to się nazywa UCS-2.

W Visual Studio w oknie zapisywania pliku jest funkcja “Save with Encoding...”. Nie musisz za każdym razem konwertować pliku w innym edytorze.
Plik raz zapisany w danym kodowaniu powinien już w takim pozostać przy następnym zapisywaniu.

vs.png

0

Dzięki za info, temat do zamknięcia :D

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