Zadanie "Hello world" z książki "Zrozumieć programowanie"

0

Cześć,

Mam problem z programem "Hello world" rozdział trzeci z książki Gynvaela.
Wyskakuje mi błąd jak w załączniku.

Ktoś może wie o co chodzi?

0

Mam książkę na półce w domu, więc mogę zajrzeć wieczorem. Chyba, że wrzucisz kod.

0

@Fausto: Podrzuć plz swój kod + kod VM (ten który @szweszwe wrzucił używasz?).
Ew. można też rzucić okiem na erratę - https://zrozumiecprogramowanie.pl/#/erratumPage - a nuż to coś z tego.

0

@Gynvael Coldwind:
Kody tylko z Twojego Github nic swojego. Chodziło o przetestowanie przykładu "hello world" z książki. Wieczorem sprawdzę erratę.

Edit.
Niestety nic nie znalazłem, nadal to samo.

0

@Fausto
Wrzuć plz swój kod hello world. Przetestowałem ten co leży na githubie i działa OK:

Switching to: Python 2.7.14 (64 bits)

gynvael:haven-windows>nasm hello.nasm

gynvael:haven-windows>python vm.py hello
Hello World
0

Może chodzi o to, że @Fausto wywołał python vm.py z plikiem .nasm!

0

A to nie chodziło właśnie o wywołanie tego hello.nasm ?

1

Nie, na maszynie wirtualnej uruchamia się kod bajtowy (bytecode). Żeby takowy uzyskać trzeba skompilować plik z pseudoasemblerem za pomocą nasma (tak jak pokazałem w swoim poprzednim poście).

Cytując książkę:
kompilacja
uruchomienie

0

Podepnę się do wątku może mi wybaczycie, chodzi konkretnie o fragment wyniku w formie heksadecymalnej:

000010  02 04 20 02 00 21 09 00

konkretnie: 21 09 00
i makro:

%macro vjz 1
db 0x21
dw (%1 - ($ + 2))
%endmacro

rozumie że db emituje bajt numeru instrukcji 21
nie rozumie tej drugiej linijki a dokładnie zapisu:
(%1 - ($ + 2))
co odejmowane jest od przekazanego przez nas argumentu ? Skąd wzięła się wyemitowana 16bitowa wartość: 09 00

0

Fajny przekrój tematów porusza ta ksiazka.

1
addjar1701 napisał(a):

Podepnę się do wątku może mi wybaczycie, chodzi konkretnie o fragment wyniku w formie heksadecymalnej:

000010  02 04 20 02 00 21 09 00

konkretnie: 21 09 00
i makro:

%macro vjz 1
db 0x21
dw (%1 - ($ + 2))
%endmacro

rozumie że db emituje bajt numeru instrukcji 21
nie rozumie tej drugiej linijki a dokładnie zapisu:
(%1 - ($ + 2))
co odejmowane jest od przekazanego przez nas argumentu ? Skąd wzięła się wyemitowana 16bitowa wartość: 09 00

W równaniu, o które pytasz, są dwie ciekawe rzeczy:

  • Przekazany argument %1. Do tego skoku przekazuje się zazwyczaj "label", czyli oznaczenie miejsca, do którego skok ma się odbyć. Czyli de facto adres w pamięci (co czasem jest tożsame z offsetem w pliku).
  • Oraz $, czyli adres w pamięci tej konkretnej przetwarzanej instrukcji/dyrektywy (tj. dw).

Generalnie nasm (jak i sporo innych assemblerów) działa w ten sposób, że on po prostu emituje strumień bajtów, i każda instrukcja bądź dyrektywa mówi "wyemituj kolejne kilka bajtów w ten sposób". Co za tym idzie każda dyrektywa wie jak daleko (w bajtach) od początku kodu jest. A żeby się dostać do tej informacji, trzeba własnie użyć $.

Czyli, to równanie (dość typowe do skoków relatywnych) można przełożyć na:

dw (adres_docelowy_skoku - (adres_tych_bajtów_które_teraz_wygeneruje_to_dw - wielkość_tej_instrukcji_czyli_dw_czyli_2_bajty))

Samo dw oczywiście powoduje wyemitowanie 16-bitowej wartości (data word, gdzie word w tym kontekście oznacza 16-bitów little endian).

Celem tego równania jest wyliczenie ile bajtów trzeba dodać do licznika instrukcji żeby wykonać skok licząc od NASTĘPNEJ instrukcji (stąd to +2 tam).

Przykład:

[org 0]  ; zaczynamy liczyć od 0 (co jest domyślne)

vnop     ; instrukcja na adresie 0
vnop     ; instrukcja na adresie 1
vjnz  xyz  ; instrukcja na adresie 2
vnop     ; instrukcja na adresie 5 (bo ta poprzednia ma 3 bajty)
xyz:     ; etykieta równa adresowi 6
vnop     ; instrukcja na adresie 6

W tym przypadku dla vjnz naszym %1 jest 6, a naszym $ jest 3 (czemu 3 a nie 2? bo przed dw w makrze jest jeszcze db, które sprawia, że samo dw będzie o bajt dalej).
Jak zaczniemy liczyć wychodzi nam (6 - (3 + 2)), czyli po prostu 1. A więc możemy to interpretować jako "przeskocz jeden bajt".
I patrząc na kod faktycznie tak jest, przeskakujemy tym skokiem jeden vnop (zakładając ofc że ten skok by się wykonał).

Ostateczny bajtkod tego vjnz tam to byłby: 21 01 00

0
Gynvael Coldwind napisał(a):
addjar1701 napisał(a):

Podepnę się do wątku może mi wybaczycie, chodzi konkretnie o fragment wyniku w formie heksadecymalnej:

000010  02 04 20 02 00 21 09 00

konkretnie: 21 09 00
i makro:

%macro vjz 1
db 0x21
dw (%1 - ($ + 2))
%endmacro

rozumie że db emituje bajt numeru instrukcji 21
nie rozumie tej drugiej linijki a dokładnie zapisu:
(%1 - ($ + 2))
co odejmowane jest od przekazanego przez nas argumentu ? Skąd wzięła się wyemitowana 16bitowa wartość: 09 00

W równaniu, o które pytasz, są dwie ciekawe rzeczy:

  • Przekazany argument %1. Do tego skoku przekazuje się zazwyczaj "label", czyli oznaczenie miejsca, do którego skok ma się odbyć. Czyli de facto adres w pamięci (co czasem jest tożsame z offsetem w pliku).
  • Oraz $, czyli adres w pamięci tej konkretnej przetwarzanej instrukcji/dyrektywy (tj. dw).

Generalnie nasm (jak i sporo innych assemblerów) działa w ten sposób, że on po prostu emituje strumień bajtów, i każda instrukcja bądź dyrektywa mówi "wyemituj kolejne kilka bajtów w ten sposób". Co za tym idzie każda dyrektywa wie jak daleko (w bajtach) od początku kodu jest. A żeby się dostać do tej informacji, trzeba własnie użyć $.

Czyli, to równanie (dość typowe do skoków relatywnych) można przełożyć na:

dw (adres_docelowy_skoku - (adres_tych_bajtów_które_teraz_wygeneruje_to_dw - wielkość_tej_instrukcji_czyli_dw_czyli_2_bajty))

Samo dw oczywiście powoduje wyemitowanie 16-bitowej wartości (data word, gdzie word w tym kontekście oznacza 16-bitów little endian).

Celem tego równania jest wyliczenie ile bajtów trzeba dodać do licznika instrukcji żeby wykonać skok licząc od NASTĘPNEJ instrukcji (stąd to +2 tam).

Przykład:

[org 0]  ; zaczynamy liczyć od 0 (co jest domyślne)

vnop     ; instrukcja na adresie 0
vnop     ; instrukcja na adresie 1
vjnz  xyz  ; instrukcja na adresie 2
vnop     ; instrukcja na adresie 5 (bo ta poprzednia ma 3 bajty)
xyz:     ; etykieta równa adresowi 6
vnop     ; instrukcja na adresie 6

W tym przypadku dla vjnz naszym %1 jest 6, a naszym $ jest 3 (czemu 3 a nie 2? bo przed dw w makrze jest jeszcze db, które sprawia, że samo dw będzie o bajt dalej).
Jak zaczniemy liczyć wychodzi nam (6 - (3 + 2)), czyli po prostu 1. A więc możemy to interpretować jako "przeskocz jeden bajt".
I patrząc na kod faktycznie tak jest, przeskakujemy tym skokiem jeden vnop (zakładając ofc że ten skok by się wykonał).

Ostateczny bajtkod tego vjnz tam to byłby: 21 01 00

Dziękuję ślicznie za bardzo przystępne i obszerne wyjaśnienie zrozumieć_programowanie1.png przesyłam screena - tak dla upewnienia się czy dobrze zrozumiełem

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.