input
6(7)
print <змінна>
lea bx, [si+<змінна>]
call print
6(7)
<змінна1>+<змінна2>
mov ax, <змінна1>
add ax, [si+<змінна2>]
6(7)
<змінна>+<число>
mov ax, <змінна>
add ax, <число>
6
<число>+<змінна>
mov ax, <число>
add ax, [si+<змінна>]
6(7)
<змінна>+@ або @+<змінна>
add ax, [si+<змінна>]
3(4)
<число>+@ або @+<число>
add ax, <число>
3
<змінна1>-<змінна2>
mov ax, <змінна1>
sub ax, [si+<змінна2>]
6(7)
<змінна>-<число>
mov ax, <змінна>
sub ax, <число>
6
<число>-<змінна>
mov ax, <число>
sub ax, [si+<змінна>]
6(7)
<змінна>-@
sub ax, [si+<змінна>]
neg ax
5(6)
@-<змінна>
sub ax, [si+<змінна>]
3(4)
<число>-@
sub ax, <число>
neg ax
5
@-<число>
sub ax, <число>
3
<змінна1>*<змінна2>
mov ax,<змінна1>
imul [si+<змінна2>]
6(7)
<змінна>*<число> або <число>*<змінна>
imul ax, [si+<змінна>],<число>
5(6)
<змінна>*@ або @*<змінна>
imul [si+<змінна>]
3(4)
<число>*@ або @*<число>
imul ax,<число>
4
<змінна1>/<змінна2>
xor dx,dx
mov ax,<змінна1>
idiv [si+<змінна2>]
9(10)
<змінна>/<число>
xor dx,dx
mov ax,<змінна>
mov bx, <число>
idiv bx
10
<число>/<змінна>
xor dx,dx
mov ax,<число>
idiv [si+<змінна>]
8(9)
КП.КС-05.00.00.000 ПЗ | Арк.
12
Зм. | Арк. | № докум | Підпис | Дата
Продовження таблиці 1
Конструкція
Асемблер
Довжина коду
(байт)
<змінна>/@
xor dx,dx
mov bx, [si+<змінна>]
xchg ax,bx
idiv bx
8(9)
@/<змінна>
xor dx,dx
idiv [si+<змінна>]
5(6)
<число>/@
xor dx,dx
mov bx, <число>
xchg ax,bx
idiv bx
8
@/<число>
xor dx,dx
mov bx, <число>
idiv bx
7
Для зменшення розміру програми використовується індексна адресація, базою якої є регістр SI з нульовим значенням. Звертання до перших 59 змінних дозволяє зменшити довжину багатьох конструкцій на 1 байт (використовується однобайтове зміщення, що розширюється знаком до 16 біт). В дужках вказана довжина конструкції в гіршому випадку.
Програма формує вихідний файл з текстом програми на мові Асемблер для компілятора Borland Turbo Assembler з використанням повних директив сегментації. Текст програми компілюється в *.exe файл шляхом виклику програм tasm.exe та tlink.exe через функцію system().
Нижче наведений шаблон для формування тексту програми:
.386 ; дозволити використання
; команд процесора 80386
.387 ; дозволити використання
; команд співпроцесора 80387
stack segment use16 ; сегмент стеку розміром 256
db 256 dup(?) ; байт з використанням
stack ends ; 16-розрядної адресації
data segment use16 ; початок сегменту даних
КП.КС-05.00.00.000 ПЗ | Арк.
13
Зм. | Арк. | № докум | Підпис | Дата
conv dt 0 ; 10 байт для роботи з 80387
<назва_змінної_1> DW <значення_змінної_1> ; ініціалізація змінних
;--------------------------------------------------------
<назва_змінної_N> DW <значення_змінної_N> ;
data ends ; кінець сегменту даних
code segment use16 ; початок сегменту коду
assume cs:code, ds:data, ss:stack ; об’явлення сегментів
main: mov ax,data ; ініціалізація сегменту
mov ds,ax ; даних
mov ax,stack ; ініціалізація сегменту
mov ss,ax ; стеку
mov sp,256 ; ініціалізація покажчика стеку
xor si,si ; SI=0
; текст основної програми
mov ah,1 ; виклик переривання DOS
int 21h ; для очікування натиснення
; кнопки на клавіатурі
mov ah,4Ch ; виклик переривання DOS
int 21h ; для завершення програми
; текст підпрограм вводу/виводу
code ends ; кінець сегменту коду
end main ; кінець модуля
Робота з програмою виглядає наступним чином. Для запуску програми слід ввести її назву в командний рядок разом з параметром (шлях до вхідного файлу, що транслюється). Після роботи програми в директорії, в якій вона знаходиться, з’являться вихідний файл res.asm, виконавчий файл скомпільованої програми res.exe та допоміжні файли компілятора. Після цього залишається лише запустити res.exe. В директорії програми обов’язково мають знаходитися файли input_r.txt та print_r.txt!
КП.КС-05.00.00.000 ПЗ | Арк.
14
Зм. | Арк. | № докум | Підпис | Дата
4 ТЕКСТ ПРОГРАМИ
#include <stdio.h> // Функції стандартних вводу/виводу
#include <string.h> // Функції роботи з рядками
#include <ctype.h> // Функції визначення типу байту
#include <stdlib.h> // Бібліотека стандартних функцій
char in[512], // Масив вхідного файлу
id[30][32], // Таблиця ідентифікаторів
var[32], // Назва зчитаного ідентифікатора
ex[90], // Масив арифметичного виразу
op1[30], op2[30], // Операнди арифметичної операції
pr[90], // Масив пріоритетів у виразі
d, // Прапорець присвоєння значення числа
found, // Прапорець повтору ідентифікатора
new_entry, // Прапорець нового ідентифікатора
oper, // Символ арифметичної операції
imm1, imm2, // Прапорці типу операндів (1-число)
k, e, // Покажчики масивів
v, // Прапорець присвоєння значення змінної
l, // Довжина виразу
current, // Поточний пріоритет елементу виразу
number, // Прапорець послідовності цифр
L1, L2, // Довжини операндів
shift, // Значення для зсуву виразу
max; // Максимальний пріоритет у виразі
int i, // Покажчик масиву вхідного файлу
filesize, // Розмір вхідного файлу
max_id; // Число ідентифікаторів у таблиці
FILE *fin,*fout; // Покажчики дескрипторів файлів
int main(int argc,char**argv)
{
КП.КС-05.00.00.000 ПЗ | Арк.
15
Зм. | Арк. | № докум | Підпис | Дата
if((fin=fopen(argv[1],"r"))==NULL) // Відкриття файлу з перевіркою на
{ // помилки
printf("Cannot open %s\n",argv[1]); // Повідомлення про помилку
return 0; // Завершення програми
}
if((fout=fopen("res.asm","w+"))==NULL) // Створення файлу з перевіркою на
{ // помилки
printf("Cannot create res.asm\n"); // Повідомлення про помилку
return 0; // Завершення програми
}
for(i=0;!feof(fin);i++)
in[i]=fgetc(fin); // Читання вхідного файлу в масив
filesize=i; // Визначення розміру масиву
fprintf(fout,".386 \n.387 \nstack segment use16 \ndb 256 dup(?) \nstack ends \n");
fprintf(fout,"data segment use16 \nconv\t dt 0 \n"); // Початок програми
for(i=0;i<filesize;i++) // Ініціалізація змінних
{
e=found=0;
while(isalpha(in[i])) // Читання ідентифікатора
{
found=1;
var[e++]=in[i++];
}
if(found) // Обробка зчитаного ідентифікатора
{
var[e]=0;
new_entry=1;
for(k=0;k<max_id;k++) // Пошук в таблиці ідентифікаторів
if(strcmp(id[k],var)==0)
{
КП.КС-05.00.00.000 ПЗ | Арк.
16
Зм. | Арк. | № докум | Підпис | Дата
new_entry=0; // Ідентифікатор знайдений в таблиці
break;
}
if(new_entry) // Якщо є новий ідентифікатор
{
fprintf(fout,"%s\t dw ",var); // Запис директиви у вихідний файл
for(e=0;var[e]!=0;e++)
id[max_id][e]=var[e]; // Запис ідентифікатора в таблицю
id[max_id++][e]=0;
if(in[i]=='=') // Якщо є присвоєння
{
d=1;
i++;
if(in[i]=='-')
fprintf(fout,"%c",in[i++]); // Вставка мінуса
for(e=0;in[i]!=0x0A;i++) // Перевірка присвоєння значення числа
{
if(!isdigit(in[i]))
{
d=0; // Це присвоєння значення змінної
break;
}
var[e++]=in[i]; // Читання значення числа
}
var[e]=0;
if(d)
fprintf(fout,"%s",var); // Присвоєння значення числа
else
fprintf(fout,"0"); // Ініціалізація змінної нулем
}
КП.КС-05.00.00.000 ПЗ | Арк.
17
Зм. | Арк. | № докум | Підпис | Дата
else
fprintf(fout,"0"); // Ініціалізація змінної нулем
}
}
}
fprintf(fout,"data ends\n code segment use16\n assume cs:code, ds:data, ss:stack\n main:\tmov ax,data \n\tmov ds,ax \n\tmov ax,stack \n\tmov ss,ax \n\tmov sp,256 \n\txor si,si \n"); // Початок сегменту програми
for(i=0;i<filesize;i++) // Трансляція тексту
{
if(strcmp(in+i,"input")==0) // Якщо знайдена конструкція input
{
i+=6;