Wydział Informatyki Politechniki Białostockiej Data:
Laboratorium Informatyki Technicznej,
Przedmiot: Architektura komputerów
11.06.2013
Ćwiczenie nr 9
Temat:
Liczby losowe, tablice, sortowanie, pliki Prowadzący:
Autor:
Dariusz Mikołajczuk
dr inż. Mirosław Omieljanowicz
Plik: ZAD9.ASM
; Napisać program, który:
; a/ wylosuje zawartość tablicy 2048 16-bitowych liczb dodatnich,
; b/ przeniesie do drugiej tablicy pierwsze 1024 liczb,
; c/ Posortuje drugą tablicę w porządku rosnącym,
; d/ sprawdzi, czy w tablicy jest liczba podana przez użytkownika wprowadzona w linii komendy przy
;
wywołaniu programu ,
; e/ zapisze posortowane liczby do pliku tekstowego oddzielone spacjami (nazwa pliku Fi512.txt),
; f/ wypisze w sposób uporządkowany zawartość pliku na ekranie.
org 100h
section .data
table_in
TIMES 2048 dw 0000h
table_out
TIMES 1024 dw 0000h
mes_no_arg
db 'Brak argumentu',0Dh,0Ah,'$'
mes_bad_format db 'Bledna liczba',0Dh,0Ah,'$'
mes_ujemna
db 'Ujemna liczba',0Dh,0Ah,'$'
number_of
db 'Liczba przekracza zakres',0Dh,0Ah,'$'
mes_found
db ' - znaleziono',0Dh,0Ah,'$'
mes_nfound
db ' - nie znaleziono',0Dh,0Ah,'$'
mnoz
dw 1, 10, 100, 1000, 10000
ah_backup
db
0 ; używane przez MACRO_parse_number i MACRO_wypisz_wyr cx_backup
dw
0 ; używane przez MACRO_parse_number i MACRO_wypisz_wyr ch_backup
db
0 ; używane przez MACRO_parse_number
si_backup
dw
0 ; używane przez MACRO_parse_number
dx_backup
dw 0 ; używane przez MACRO_parse_number i MACRO_wypisz_wyr pathOut
db
'Fi512.txt',0
mes_write
db 'Nie udalo sie zapisac do pliku docelowego.',0Dh,0Ah,'$'
mes_open
db 'Nie udalo sie otworzyc pliku docelowego:',0Dh,0Ah,'$'
mes_open2
db 'Nie udalo sie otworzyc stworzonego pliku.',0Dh,0Ah,'$'
mes_read
db 'Nie udalo sie odczytac stworzonego pliku.',0Dh,0Ah,'$'
fd_out
dw
0000h
; uchwyt tworzonego pliku
buforOut
TIMES 1024 db 00h
; bufor zapisu
; ------------------------------ MACRO_printdec ------------------------------
; Wypisuje liczbę 16-bitową bez znaku zapisaną w CX,
; przywraca wartości AX, BX, CX, DX, SI sprzed wywołania makra
; w sekcji zmiennych musi być zadeklarowana następująca tablica:
; mnoz
dw 1, 10, 100, 1000, 10000
%macro MACRO_printdec 0
push
AX
push
BX
push
CX
push
DX
push
SI
xor
DX, DX
; przedłużenie dzielnej od strony MSB równe zero mov
AX, CX
; dzielna - liczba do wypisania
xor
CH, CH
; w CH będą dwie zmienne (CH = XXXXYYYY)
; * od strony MSB 4 bity stanowią wartość logiczną:
; XXXX=0000 - fałsz (nie wypisano jeszcze żadnych cyfr)
; XXXX>0000 - prawda (wypisano XXXX cyfr)
; * od strony LSB 4 bity stanowią licznik kroków pętli,
; zatrzymanie pętli następuje gdy YYYY == 4
mov
BX, 8
; indeks pierwszego dzielnika (10000)
; znajduje się on w tablicy liczb 2-bajtowych (dw)
%%write_loop:
; pętla
; pobierz dzielnik
mov
CL, BL
; kopia indeksu tablicy dzielników
mov
BX, [mnoz+BX] ; pobranie dzielnika
sub
CL, 2
; przygotowanie indeksu kolejnego dzielnika
; wykonanie dzielenia
div
BX
; AX = DX:AX / BX ; DX = DX:AX % BX
xor
BH, BH
; w celu uniknięcia późniejszych błędów przy użyciu BX
; w celu adresowania
mov
BL, CL
; przywrócenie indeksu kolejnego dzielnika mov
SI, DX
; kopia reszty z dzielenia (DX wykorzystywany przy wypisywaniu)
; sprawdzenie czy należy wypisać cyfrę liczby cmp
AL, 0
; czy cyfra jest równa zero
jne
%%wypisz_cyfre; jeśli różna od zera to ją wypisz
; cyfra równa zero:
cmp
CH, 10h
; czy wypisano już jakieś cyfry?
jae
%%wypisz_cyfre; tak wypisano (CH >= 10h) -> wypisz cyfrę
; nie wypisano jeszcze żadnych cyfr, a cyfra równa zero jmp
%%prepare_next_loop
; nie wypisuj cyfry, przygotuj się do kolejnego kroku pętli
mov
AH, 02h ; funkcja systemowa - wypisz znak z DL
mov
DL, AL
; skopiuj cyfrę do wypisania
add
DL, 30h
; konwertuj cyfrę na kod ASCII
int
21h ;
add
CH, 10h
; w następnym kroku CH >= 10h co oznacza że wypisano już jakieś cyfry
%%prepare_next_loop:
mov
CL, CH
; kopia CH
and
CH, 0Fh ; wydzielenie dolnego półbajtu CH
cmp
CH, 4
; czy to czwarty krok pętli (licząc od zera) je
%%koniec_wyp; przerwanie pętli
mov
CH, CL
; przywrócenie CH
inc
CH
; zwiększenie dolnego półbajtu CH o 1, jest to licznik kroków pętli mov
AX, SI
; reszta z ostatniego dzielenia (SI) będzie dzielną
; kolejnego dzielenia (AX)
xor
DX, DX ; wyzerowanie górnej części dzielnej (tu niewykorzystywanej) jmp
%%write_loop
; koniec pętli
%%koniec_wyp:
mov
CH, CL
; przywrócenie CH
mov
AH, 02h
; nie wypisano żadnej cyfry, wypisuje zero cmp
CH, 10h
; czy wypisano co najmniej jedną cyfrę
jae
%%write_newline_end
; jeśli tak pomiń poniższe
mov
DL, 30h
; wypisanie zera, jeśli liczba do wypisania równa zero int
21h
%%write_newline_end:
;~ mov
DL, 0Dh
;~ int
21h
;~ mov
DL, 0Ah
;~ int 21h
pop
SI
pop
DX
pop
CX
pop
BX
pop
AX
%endmacro
; ------------------------------ MACRO_printdec ------------------------------
; ------------------------------ MACRO_savebuf ------------------------------
; Zapisuje bufor buforOut do pliku o uchwycie fd_out
%macro MACRO_savebuf
0
push
CX
push
DX
push
AX
mov
CX, BX
; rozmiar do zapisu
mov
BX, [fd_out]
; uchwyt pliku do zapisu
mov
AH, 40h
; funkcja - zapis do pliku
mov
DX, buforOut
; bufor źródłowy
int
21h
; zapisz
jc
%%blad_zapisu
jmp
%%end_macro
%%blad_zapisu:
pop
AX
pop
DX
pop
CX
mov
AH, 09h
mov
DX, mes_write
jmp
koniec
%%end_macro:
pop
AX
pop
DX
pop
CX
xor
BX, BX
; rozmiar bufora 0
mov
DI, buforOut
%endmacro
; ------------------------------ MACRO_savebuf ------------------------------
; ------------------------------ MACRO_writedec ------------------------------
; Zapisuje do DS:DI liczbę 16-bitową bez znaku zapisaną w CX,
; przywraca wartości AX, BX, CX, DX, SI sprzed wywołania makra
; w sekcji zmiennych musi być zadeklarowana następująca tablica:
; mnoz
dw 1, 10, 100, 1000, 10000
; W ES znajduje się licznik zapisanych bajtów (nie liczb; początkowo równy zero)
%macro MACRO_writedec 0
push
AX
push
BX
push
CX
push
DX
push
SI
xor
DX, DX
; przedłużenie dzielnej od strony MSB równe zero mov
AX, CX
; dzielna - liczba do wypisania
xor
CH, CH
; w CH będą dwie zmienne (CH = XXXXYYYY)
; * od strony MSB 4 bity stanowią wartość logiczną:
; XXXX=0000 - fałsz (nie wypisano jeszcze żadnych cyfr)
; XXXX>0000 - prawda (wypisano XXXX cyfr)
; * od strony LSB 4 bity stanowią licznik kroków pętli,
; zatrzymanie pętli następuje gdy YYYY == 4
mov
BX, 8
; indeks pierwszego dzielnika (10000)
; znajduje się on w tablicy liczb 2-bajtowych (dw)
%%write_loop:
; pętla
; pobierz dzielnik
mov
CL, BL
; kopia indeksu tablicy dzielników
mov
BX, [mnoz+BX] ; pobranie dzielnika
sub
CL, 2
; przygotowanie indeksu kolejnego dzielnika
; wykonanie dzielenia
div
BX
; AX = DX:AX / BX ; DX = DX:AX % BX
xor
BH, BH
; w celu uniknięcia późniejszych błędów przy użyciu BX w celu adresowania mov
BL, CL
; przywrócenie indeksu kolejnego dzielnika mov
SI, DX
; kopia reszty z dzielenia (DX wykorzystywany przy wypisywaniu)
; sprawdzenie czy należy wypisać cyfrę liczby cmp
AL, 0
; czy cyfra jest równa zero
jne
%%wypisz_cyfre; jeśli różna od zera to ją wypisz
; cyfra równa zero:
cmp
CH, 10h
; czy wypisano już jakieś cyfry?
jae
%%wypisz_cyfre; tak wypisano (CH >= 10h) -> wypisz cyfrę
; nie wypisano jeszcze żadnych cyfr, a cyfra równa zero jmp
%%prepare_next_loop
; nie wypisuj cyfry, przygotuj się do kolejnego kroku pętli
%%wypisz_cyfre:
; cyfra w AL
; ES - licznik bajtów w buforze
push
BX
mov
BX, ES
cmp
BX, 1024
je
%%save_buf
jmp
%%write_byte
%%save_buf:
MACRO_savebuf
add
AL, 30h
mov
[DS:DI], AL
inc
DI
inc
BX
mov
ES, BX
pop
BX
add
CH, 10h ; w następnym kroku CH >= 10h co oznacza że wypisano już jakieś cyfry
%%prepare_next_loop:
mov
CL, CH
; kopia CH
and
CH, 0Fh ; wydzielenie dolnego półbajtu CH
cmp
CH, 4
; czy to czwarty krok pętli (licząc od zera) je
%%koniec_wyp; przerwanie pętli
mov
CH, CL
; przywrócenie CH
inc
CH
; zwiększenie dolnego półbajtu CH o 1, jest to licznik kroków pętli mov
AX, SI
; reszta z ostatniego dzielenia (SI) będzie dzielną
; kolejnego dzielenia (AX)
xor
DX, DX ; wyzerowanie górnej części dzielnej (tu niewykorzystywanej) jmp
%%write_loop
; koniec pętli
%%koniec_wyp:
mov
CH, CL
; przywrócenie CH
mov
AH, 02h
; nie wypisano żadnej cyfry, wypisuje zero cmp
CH, 10h
; czy wypisano co najmniej jedną cyfrę
jae
%%write_newline_end
; jeśli tak pomiń poniższe
mov
DL, 30h
; wypisanie zera, jeśli liczba do wypisania równa zero int
21h
%%write_newline_end:
mov
BX, ES
cmp
BX, 1024
je
%%save_buf2
jmp
%%write_byte2
%%save_buf2:
MACRO_savebuf
%%write_byte2:
mov
byte [DS:DI], ' '
inc
DI
inc
BX
mov
ES, BX
SI
pop
DX
pop
CX
pop
BX
pop
AX
%endmacro
; ------------------------------ MACRO_writedec ------------------------------
; Makro przesuwa wskaźnik ES:SI (poprzez inkrementacje SI)
; tak długo, aż napotka znak [ES:SI] != ' ' && [ES:SI] != '\t'
; jedynym modyfikowanym rejestrem jest tu SI
%macro MACRO_rem_spaces 0
push AX
%%rem_spaces_loop:
mov
AL, [ES:SI]
; kolejny bajt
cmp
AL, 20h
; spacja
je
%%rem_spaces_next
; kontynuuj usuwanie
cmp
AL, 09h
; tabulator
je
%%rem_spaces_next
; kontynuuj usuwanie
; (else) znak różny od spacji i tabulatora jmp
%%rem_spaces_break
; zakończ usuwanie
%%rem_spaces_next:
; przygotowanie do kolejnego kroku pętli inc
SI
; zwiększ przesunięcie w segmencie
jmp
%%rem_spaces_loop
; (koniec) rem_spaces_loop
%%rem_spaces_break:
pop
AX
%endmacro
; ------------------------------ MACRO_rem_spaces ------------------------------
; Makro odczytuje liczbę 2 bajtową -65535...65535 zapisaną jako tablica ASCII
; począwszy od adresu [ES:SI], liczba może być poprzedzona dowolną ilością
; zer. Wczytywanie kończy się na pierwszym znaku za liczbą różnym od '0'...'9'
;
; Liczba może być poprzedzona minusem, pomiędzy minusem a liczbą może być
; dowolna ilość białych znaków (spacji i tabulatorów)
;
; Jeśli w pamięci nie ma poprawnej liczby w AL zapisywana jest wartość 0
; (AH i CX pozostają niezmienione)
;
; Jeśli przekroczono zakres, w AL zapisywana jest wartość FFh,
; (AH i CX pozostają niezmienione)
;
; Przy poprawnej konwersji:
; w AL zapisywana jest rzeczywista długość liczby(w znakach znaczących, bez znaku minus)
; w AH zapisywany znak liczby:
;
AH = 00h - liczba w CX jest dodatnia (>=0)
;
AH = 2Dh='-' - liczba w CX jest ujemna ( <0)
; w CX zapisywana jest wartość bezwzględna liczby
; w SI indeks znaku nie należącego do liczby i nie będącego cyfrą,
;
umieszczonego bezpośrednio po liczbie
%macro MACRO_parse_number 0
mov
[cx_backup], CX
; kopia CX (być może do przywrócenia)
mov
[ah_backup], AH
; kopia AH (być może do przywrócenia)
push
BX
; 1. kopia BX (do przywrócenia)
push
DX
; 2. kopia DX (do przywrócenia)
xor
DX, DX
; DH - tymczasowy znak liczby (początkowo 00h == (kod plusa)) xor
CX, CX
; CL - liczba wiodących zer, CH - długość znacząca liczby xor
AX, AX
xor
BH, BH
; znak liczby wczytywany do BL (BH zerowany)
; sprawdzenie czy liczba jest ujemna
mov
BL, [ES:SI]
; pobranie pierwszego znaku liczby
cmp
BL, '-'
; sprawdzenie czy jest to minus
jne
%%rem_zeros_loop
; jeśli pierwszy znak nie jest minusem skocz do
; pętli usuwającej zera
; BL=='-' (liczba ujemna)
mov
DH, BL
; ustaw DH == '-' == (kod minusa-1) - wskazuje że
; liczba jest ujemna
inc
SI
; SI wskazuje teraz na pierwszy znak za minusem MACRO_rem_spaces
; usuń spacje pomiędzy minusem, a liczbą
; pętla usuwająca zera z początku liczby
%%rem_zeros_loop:
mov
BL, [ES:SI]
; pobierz kolejny znak liczby
inc
SI
; zwiększ indeks źródłowy
cmp
BL, '0'
; czy pobrany znak == '0'
jne
%%read_num_loop ; jeśli znak != '0' przejdź do pętli pobierającej liczbę
; pobrano kolejne zero BL=='0'
inc
CL
; zwiększ liczbę zer poprzedzających liczbę jmp
%%rem_zeros_loop; kontynuuj pętle
; pętla odczytująca znaki cyfr znaczących liczby (pierwszy znak pobrany w poprzedniej pętli)
%%read_num_loop:
cmp
BL, '0'
; jeśli znak < '0'
jb
%%read_num_loop_break ;
zakończ pętle
cmp
BL, '9'
; jeśli znak > '1'
ja
%%read_num_loop_break ;
zakończ pętle
cmp
CH, 5
; czy to próba zapisu znaku o indeksie 5 (licząc od zera) je
%%overflow_detected
; jeśli tak --> liczba przekracza dopuszczalny zakres
; (else) CH<5 --> długość liczby nie przekracza dopuszczalnej push BX
; umieść cyfrę (ASCII) na stosie
inc
CH
; zwiększ liczbę wczytanych cyfr znaczących (ilość na stosie) mov
BL, [ES:SI]
; pobierz kolejny znak (cyfrę lub separator) inc
SI
; zwiększ indeks źródłowy liczby
jmp
%%read_num_loop
; przejdź do kolejnego kroku pętli
; Sprawdzenie czy liczba jest poprawna (CH - liczba cyfr znaczących,
; CL - liczba zer poprzedzających cyfrę)
%%read_num_loop_break:
dec
SI
; zmniejszenie SI aby wskazywał znak bezpośrednio po liczbie cmp
CH, 0
jne
%%got_valid_number
; liczba wczytanych cyfr znaczących > 0
; CH==0- nie wczytano żadnych cyfr znaczących (brak cyfr na stosie) cmp
CL, 0
je
%%got_not_valid_number ; brak zer poprzedzających liczbę (CL==0 && CH==0)
; (else) CH==0 && CL !=0 - co najmniej jedno zero poprzedzające cyfrę
; (wczytano: 0 lub 000...000)
mov
AL, 1
; długość wczytanej liczby (wczytano zero) xor
CX, CX
; liczba w CX równa zero
xor
AH, AH
; znak liczby równy długości, równy 00h (plus) jmp
%%parse_number_return
; przejdź do końca funkcji
; pod adresem początkowym [ES:SI] brak poprawnej liczby
%%got_not_valid_number:
xor
AL, AL
; kod wyjścia = 0 - błędna liczba
jmp
%%restore_AH_CX_end
; przywróć rejestr AH, CX i zakończ procedurę
; wczytano poprawną liczbę, ciąg znaków umieszczony wcześniej na stosie
; zostanie teraz przekonwertowany na liczbę
%%got_valid_number:
mov
[ch_backup], CH
; kopia liczby znaczących cyfr liczby
mov
[si_backup], SI
; kopia poprzedniej wartości SI
mov
[dx_backup], DX
; kopia DX (w DH - kod znaku liczby+1)
xor
CL, CL
; wyzerowanie indeksera mnożnika
xor
SI, SI
; wyzerowanie tymczasowej sumy
; Pętla przetwarzająca cyfry ASCII liczby na postać binarną
%%parse_loop:
; pobierz kolejną cyfrę liczby (od najmniej znaczącej strony)
; i konwertuj z ASCII na wartość binarną pop
AX
sub
AL, 30h
; zmniejsz ilość cyfr na stosie
dec
CH
; pobierz kolejny mnożnik do BX
mov
BL, CL
; kopia indeksera mnożnika
xor
BH, BH
; wyczyść górny bajt !
mov
BX, [mnoz+BX]
; pobierz mnożnik
; przeprowadź mnożenie
mul
BX
; DX:AX=AX*BX
cmp
DX, 0
; nadmiar gdy liczba równa np. 75535, 85535 ...
jne
%%overflow2_detected
; Dodaj AX (iloczyn) do wyniku
mov
BX, SI
; kopia sumy
adc
AX, BX
; AX += suma
; Sprawdź czy nie wystąpił nadmiar
jc
%%overflow2_detected ; jeśli wystąpił nadmiar (np 65835) mov
SI, AX
; zapisz nową sumę
; zwiększ indekser mnożnika
add
CL, 02h
; kontynuuj jeśli są liczby na stosie (CH != 0) cmp
CH, 0
jne
%%parse_loop
; koniec pętli parse_loop
; poprawnie wczytano liczbę do SI
mov
AL, [ch_backup]
; liczba cyfr znaczących liczby
mov
CX, SI
; kopia liczby do CX
mov
DX, [dx_backup]
mov
SI, [si_backup]
AH, DH
; ustawienie kodu znaku liczby w AH
jmp
%%parse_number_return
; AL>0 && AL<FFh; valid number in CX
%%overflow2_detected:
; wykryto nadmiar w części parse_number
mov
DX, [dx_backup]
mov
SI, [si_backup]
%%overflow_detected:
mov
AL, 0FFh
; kod wyjścia - overflow
cmp
CH, 0
; czy wczytano jakieś cyfry znaczące
je
%%restore_AH_CX_end
; jeśli nie kontynuuj
%%rem_digits_from_stack:
; jeśli tak usuń je ze stosu
pop
BX
dec
CH
cmp
CH, 0
jne
%%rem_digits_from_stack
; przywrócenie AH i CX sprzed wykonania funkcji (wczytanie liczby nie powiodło się)
%%restore_AH_CX_end:
mov
AH, [ah_backup]
mov
CX, [cx_backup]
%%parse_number_return:
pop
DX
; 2.
pop
BX
; 1.
%endmacro
; ------------------------------ MACRO_parse_number ------------------------------
; ------------------------------ MACRO_putbyte ------------------------------
; Liczba jednobajtowa w AL wypisuje ją na ekran szesnastkowo (modyfikuje AX i DX)
%macro MACRO_putbyte
0 ; AX=xxxx|xxxx|HHHH|LLLL
xor
AH, AH
; AX=0000|0000|HHHH|LLLL
shl
AX, 4
; AX=0000|HHHH|LLLL|0000
shr
AL, 4
; AX=0000|HHHH|0000|LLLL
add
AX, 3030h
; jeśli AH<10,AL<10 w AX => AH,AL in {'0','9'}
cmp
AH, '9'
; czy AH <= '9'
jbe
%%check_AL
; jeśli tak AH gotowy
add
AH, 07h
; AH > '9', konwertuje do {'A'...'F'}
%%check_AL:
cmp
AL, '9'
; czy AL <= '9'
jbe
%%ascii_ok
; AH i AL gotowe
add
AL, 07h
; AL > '9', konwertuje do {'A'...'F'}
%%ascii_ok:
; w AH górny półbajt, AL - dolny liczby w kodzie ASCII mov
DX, AX
mov
AH, 02h
xchg
DH, DL
; zamiana miejscami, bo pierwszy wypisywany DL
int
21h
shr
DX, 8
int
21h
; ------------------------------ MACRO_putbyte ------------------------------
; ------------------------------ MACRO_putword ------------------------------
; Liczba dwubajtowa w CX, wypisuje ją na ekran szesnastkowo
; modyfikuje AX, DX
%macro MACRO_putword
0
mov
AL, CH
MACRO_putbyte
mov
AL, CL
MACRO_putbyte
%endmacro
; ------------------------------ MACRO_putword ------------------------------
; ------------------------------ MACRO_keypress ------------------------------
; czeka na wciśnięcie klawisza po zapełnieniu określonej liczby linii
; spacja - kontynuuje, q/ESC - kończy program
; modyfikuje AX
%macro MACRO_keypress
0
%%wait_loop:
;
mov
AH, 01h
; preview keyboard buffer
int
16h
jz
%%wait_loop
; no key in buffer
cmp
AL, ' '
; if key==' '
je
%%break_wait_loop
cmp
AL, 'q'
; if key=='q'
je
parse_end
cmp
AL, 27
; if key==ESC
je
parse_end
xor
AH, AH
; other key - clear buffer
int
16h
jmp
%%wait_loop
%%break_wait_loop:
xor
AH, AH
; clear buffer (' ')
int
16h
%endmacro
; ------------------------------ MACRO_keypress ------------------------------
; LICZBY LOSOWE (generator LCG)
; Xn= (a * Xn-1 + b ) mod m
%define A_rand
69111
%define
B_rand
11
%define M_rand
65536
; ------------------------------ MACRO_rand2 ------------------------------
; Losuje liczbę pseudolosową do EDX
; Parametry:
; EDX - poprzednia liczba pseudolosowa/ziarno
; ESI - parametr A generatora
; EBX - parametr B generatora
; EDI - parametr M generatora
; Wykonywana operacja:
; EDX = (ESI*EDX + EBX) % EDI <--> Xn = (A*Xn-1 + B) % M
; dodatkowo zmieniany EAX
%macro MACRO_rand2
0
push
ESI
mov
ESI, A_rand
; parametr A generatora (na stałe w ESI) mov
EAX, EDX
; mnożna - poprzednia l. losowa lub ziarno mul
ESI
; EDX:EAX=EAX*ESI <- EAX = Xn-1 * a
add
EAX, EBX
; EAX += b
xor
EDX, EDX
div
EDI
; EAX = (EDX:EAX) div EDI
; EDX = (EDX:EAX) mod EDI
pop
ESI
%endmacro
; ------------------------------ MACRO_rand2 ------------------------------
; ------------------------------ MACRO_rand3 ------------------------------
; EDX - poprzednia/kolejna liczba losowa
; modyfikuje wszystkie rejestry, przywraca SI i CX
; Funkcja powstała na podstawie kodu źródłowego funkcji C rand()
%macro MACRO_rand3
0
push
SI
; zachowaj SI (współdzielony z fill_table_in) push
CX
cmp
EDX, 0
jne
%%skip1
; poprzednia wartość nie może być równa zero mov
EDX, 12983477h; na wejściu tego algorytmu
%%skip1:
push
EDX
; zachowaj poprzednią wylosowaną wartość mov
EAX, EDX
xor
EDX, EDX
mov
EDI, 127773
div
EDI
; hi = EAX = EAX:EDX / EDI
; lo = EDX = EDX:EAX % EDI
mov
EBX, EDX
; EBX := lo
mov
ESI, 2836
mul
ESI
; b = (EDX:)EAX = EAX*ESI = hi * 2836
mov
ECX, EAX
; ECX := b
mov
EAX, EBX
; EAX := lo
mov
ESI, 16807
ESI
; a = (EDX:)EAX = EAX*ESI = lo * 16807
cmp
EAX, ECX
; cmp a & b
jb
%%skip2
; a < b
; (else) a >= b
pop
EDI
; bez znaczenia, poprzednia wartość niepotrzebna sub
EAX, ECX
; a -= b
xor
EDX, EDX
mov
EDI, 65536
div
EDI
; EDX = EDX:EAX % EDI -- zwracana wartość 0...65535
jmp
%%skip3
%%skip2:
; a < b
pop
EAX
; przywróć poprzednią wartość z EDX
add
EAX, 7FFFFFFFh
%%skip3:
pop
CX
pop
SI
%endmacro
; ------------------------------ MACRO_rand3 ------------------------------
section .text
;;; GŁÓWNA CZĘŚĆ PROGRAMU ;;;
start:
xor
EDX, EDX
; górna połowa EDX zawsze pusta
;; Utwórz ziarno dla generatora liczb losowych mov
AH, 2Ch ; pobierz aktualny czas int
21h ; CH = godzina
; CL = minuta
; DH = sekunda
; DL = 1/100 sekundy
add
DX, CX
; ziarno generatora w EDX
;; Wypełnienie tablicy liczb losowych
mov
CX, 2048
; licznik kroków pętli (liczba losowanych liczb) mov
SI, table_in
; offset tablicy do zapisu danych
mov
EDI, M_rand
; parametr M (modulo) na stałe w EDI
mov
EBX, B_rand
; parametr B generatora (na stałe w ES<65536) fill_table_in:
; pętla wypełniająca tablicę
MACRO_rand2
; oblicz kolejną liczbę losową z zakresu 0...65535
mov
[DS:SI], DX ; zapisz ją w kolejnej komórce tablicy add
SI, 2
; zwiększ przesunięcie ( +2, bo tablica liczb 2-bajtowych !!) loop
fill_table_in ; kontynuuj pętlę
;; Skopiuj pierwsze 1024 liczby z table_in do table_out mov
SI, table_in
; źródło
mov
DI, table_out
; cel
mov
CX, 1024
; liczba liczb do skopiowania
copy_1024:
mov
AX, [DS:SI]
mov
[DS:DI], AX
add
SI, 2
add
DI, 2
loop
copy_1024
;; Posortuj drugą tablicę (sortowanie bąbelkowe) xor
DX, DX
; i = 0
sort_loop_zewn:
xor
BX, BX
; j = 0
mov
AX, 2046
; 2(size-1)
sub
AX, DX
; j < size-1-i
mov
SI, AX
; warunek końcowy pętli wewnętrznej
sort_loop_wewn:
mov
AX, [table_out+BX]
; pobierz table_out[j]
mov
CX, [table_out+BX+2]
; pobierz table_out[j+1]
cmp
AX, CX
jbe
sort_loop_wewn_next
; table_out[j] <= table_out[j+1]
; table_out[j] > table_out[j+1]
mov
[table_out+BX], CX
; table_out[j] = table_out[j+1]
mov
[table_out+BX+2], AX
; table_out[j+1] = table_out[j]
sort_loop_wewn_next:
add
BX, 2
; j+=2 - liczby 2-bajtowe
cmp
BX, SI
; czy j == size-1-i
jb
sort_loop_wewn
; jeśli j < size-1-i kontynuuj pętlę
; koniec sort_loop_wewn
add
DX, 2
; i+=2 - liczby 2-bajtowe
cmp
DX, 2048
; czy i == 2048 (1024 * sizeof(dw))
jb
sort_loop_zewn
; jeśli i < 1024 kontynuuj pętlę zewnętrzną
;; Sprawdź czy podano argument
mov
SI, 80h
mov
AL, [ES:SI]
cmp
AL, 00h
jne
parse_arg
; brak argumentu
mov
DX, mes_no_arg
mov
AH, 09h
int
21h
jmp
file_opers
inc
SI
MACRO_rem_spaces
; usuń wszystkie białe znaki przed pierwszą liczbą MACRO_parse_number
; pobierz pierwszą liczbę do CX
; (począwszy od aktualnego przesunięcia SI)
; Sprawdź poprawność pierwszej liczby
cmp
AL, 0
; pod [ES:SI] nie znaleziono poprawnej liczby (AL bez zmian) je
bad_syntax
;
błędna liczba
cmp
AL, 0FFh
; pierwsza liczba przekracza zakres
je
number_of2
;
cmp
AH, 00h
jne
minus_sign2
; pierwsza liczba poprawnie wczytana do CX, w AH jej znak MACRO_printdec
mov
BX, CX
mov
CX, 1024
mov
DI, table_out
find_loop:
mov
AX, [DS:DI]
cmp
AX, BX
je
found_num
add
DI, 2
loop
find_loop
not_found:
mov
AH, 09h
mov
DX, mes_nfound
int
21h
jmp
file_opers
found_num:
mov
AH, 09h
mov
DX, mes_found
int
21h
jmp
file_opers
bad_syntax:
mov
AH, 09h
mov
DX, mes_bad_format
int
21h
jmp
file_opers
minus_sign2:
mov
AH, 09h
mov
DX, mes_ujemna
int
21h
jmp
file_opers
mov
AH, 09h
mov
DX, mes_ujemna
int
21h
jmp
file_opers
file_opers:
;; Utwórz plik docelowy
mov
AH, 3Ch
; utwórz plik
xor
CX, CX
; atrybuty
mov
DX, pathOut
; ścieżka pliku
int
21h
; utwórz, w AX uchwyt do pliku docelowego jc
blad_otwarcia_cel
mov
[fd_out], AX
mov
DI, buforOut
xor
AX, AX
mov
ES, AX
; pozycja w buforze (licznik
mov
SI, table_out
; znaków zapisanych do bufora)
xor
DX, DX
; licznik zapisanych liczb
plik_loop:
; pętla zapisująca tablicę 1024
mov
CX, [DS:SI]
; liczb do bufora buforOut
MACRO_writedec
add
SI, 2
inc
DX
; licznik zapisanych liczb
cmp
DX, 1024
; czy zapisano 1024 liczby
jb
plik_loop
mov
BX, ES
; przenieś do BX liczbę bajtów
MACRO_savebuf
; które są w buforze, a nie są
; zapisane do pliku i zapisz je
;; Zamknij plik docelowy (uchwyt do pliku w BX) mov
BX, [fd_out]
mov
AH, 3Eh
int
21h
;; Otwarcie pliku źródłowego
mov
AX, 3D00h
; AL=00h plik otwierany w trybie tylko do odczytu, tryb kompatybilności mov
DX, pathOut
; ścieżka pliku
int
21h
; otwarcie, w AX umieszczany uchwyt do pliku jc
blad_otwarcia2
mov
[fd_out], AX
; kopia uchwytu do zmiennej
mov
ES, AX
; kopia uchwytu do rejestru
BX, BX
; BL - licznik długości cyfry
; BH - licznik liczb w linii
xor
DX, DX
; DH - dodatkowy licznik linii, co 10 linii należy wcisnąć spacje read_loop:
; Pętla wypisujący plik w sposób sfromatowany mov
SI, BX
; skopiuj liczniki (BX zmieniany poniżej) mov
DI, DX
;
;; Odczytaj dane z pliku źródłowego do bufora (zm. buforOut, 1024B)
;; w AX - aktualnie odczytana ilość bajtów
;; Odczytaj dane z pliku źródłowego
mov
BX, ES
; uchwyt do pliku źródłowego
mov
AH, 3Fh
; funkcja - czytaj z pliku
mov
CX, 1024
; liczba bajtów do odczytania (rozmiar bufora) mov
DX, buforOut
; DS:DX - adres bufora danych
int
21h
; odczytaj
jc
blad_odczytu
; jeśli błąd odczytu wyświetl komunikat zamykając oba pliki
;; jeśli odczytano zero bajtów (EOF), zakończ z sukcesem zamykając oba pliki cmp
AX, 0
je
parse_end
mov
BX, SI
; przywróć liczniki
mov
DX, DI
;
mov
CX, AX
; odczytana liczba bajtów
mov
DI, buforOut
; początek bufora
mov
AH, 02h
wypisuj_bufor:
mov
DL, [DS:DI] ; kolejny bajt
inc
DI
; zwiększ przesunięcie w odczytanym buforze cmp
DL, 20h
; spacja, koniec pojedynczej liczby
je
end_liczby
cmp
DL, 30h
; dowolny znak poniżej '0' lub powyżej '9'
jb
parse_end
; kończy przetwarzanie
cmp
DL, 39h
ja
parse_end
; cyfra
int
21h
; wypisz cyfrę
inc
BL
; zwiększ długość aktualnej cyfry
jmp
kontynuuj
; kontynuuj pętlę wewnętrzna
; NIE MOŻNA TU UŻYĆ loop
; loop może wystąpić TYLKO RAZ w jednej pętli
; koniec liczby
spaces:
; dopisz spacje aby uzupełnić liczbę
int
21h
; do sześciu znaków (co najmniej jedna spacja) inc
BL
; zwiększ liczbę wypisanych znaków
cmp
BL, 6
; kontynuuj pętlę aż zostanie wypisane 6
jb
spaces
; znaków razem z liczbą
next_num:
; przejście do kolejnej liczby
xor
BL, BL
; wyzerowanie licznika długości liczby
inc
BH
; zwiększenie licznika wypisanych liczb
cmp
BH, 10
; czy wypisano 12 liczb
jb
kontynuuj ; jeśli nie to kontynuuj
mov
DL, 0Dh
; jeśli tak wypisz '\n'
int
21h
mov
DL, 0Ah
int
21h
xor
BH, BH
; i wyzeruj licznik liczb
inc
DH
; zwiększ liczbę linii przed keypress
cmp
DH, 10
jb
kontynuuj
MACRO_keypress
; czekaj na wciśnięcie klawisza
xor
DH, DH
mov
AH, 02h
; przywróć funkcję (AH zmieniany w MACRO_keypress) kontynuuj:
; kontynuuj pętle główną zmniejszając
loop
wypisuj_bufor; liczbę wypisanych bajtów CX o 1
jmp
read_loop
; kiedy wewnętrzna pętla for się zakończy
; wraca do głównej pętli
parse_end:
mov
AH, 02h
mov
DX, 0A0Dh
int
21h
shr
DX, 8
int
21h
jmp
close_file_end
blad_odczytu:
; błąd odczytu stworzonego pliku
mov
AH, 09h
mov
DX, mes_read
int
21h
jmp
close_file_end
blad_otwarcia_cel:
; błąd otwarcia pliku do zapisu
mov
AH, 09h
mov
DX, mes_open
int
21h
jmp
koniec
; błąd otwarcia pliku do odczytu (po utworzeniu) mov
AH, 09h
mov
DX, mes_open2
int
21h
jmp
koniec
close_file_end:
;; Zamknij plik
mov
BX, [fd_out]
mov
AH, 3Eh
int
21h
jmp
koniec
koniec:
; zakończenie
mov
AX, 4C00h
int
21h