Здравствуйте! Есть задача - написать оптимизированную по размеру исполняемого файла без ассемблерных вставок(можно использовать прерывания) программу для заполнения матрицы по спирали для случаев от 1 до 9. Задачу решил и написал самый оптимизированный алгоритм, который я смог из себя выдавить. Скажите, что ещё здесь можно оптимизировать или уже стоит приступать к замене ввода на безэховый и вывода через прерывания? Может от каких переменных избавиться? Или циклы оптимизировать можно?
program spiral; var m: array [1..9, 1..9] of word; //Сама матрица n: word; //Размерность матрицы i: word; //Индексы матрицы j: word; c: word; //Вводимое число l: word; //Длина стороны s: integer; //Флаг меняющийся между значениями -1 и 1 t: word; //Координата поворота p: ^word; //Указатель на координаты begin readln(n); l := n; t := n; s := 1; i := 1; p := @j; repeat p^ := p^ + s; c := c + 1; //Если достигнута точка поворота if p^ = t then begin //Меняем изменяемую координату if p = @j then begin p := @i; l := l - 1; end else begin p := @j; s := -s; end; //Узнаём координату следующего поворота t := p^ + s*(l); end; m[i,j] := c; until l = 0; //Обычный вывод for i:=1 to n do begin for j:=1 to n do write(m[i,j]:3); writeln; end; end.
Моя после компиляции без всяких особых натроек компилятора весит 3104 байта
А чем компилировали? Поделитесь секретом
BPC Старый добрый BPC. Это собственно и есть компилятор для соревнований. Всё в досбоксе собирается.
Добавлено через 3 мин. Счёт идёт на байты. Вообще задание полностью так звучит - Нужно написать две программы спиральной матрицы для размерность от 2 до 9. Одну на ассемблере, другую на паскале и в суме они должны быть меньше 3000 байт, и тот чья прога меньше остальных, тот и получает самоэкз. В программе на паскале использовать asm end. Запрещено.
Понятно, Ассемблер всё объясняет. А то задача выглядит как злобный троллинг препода.
Я бы начал думать как сэкономить на WriteLn. Теоретически, у вас два вызова с разным набором параметров. На Си можно было бы сделать подобие printf("..формат..", Элемент_Массива, i==n?"\n":" "); хотя само вычисление может оказаться большим.
Понятно, Ассемблер всё объясняет. А то задача выглядит как злобный троллинг препода.
Я бы начал думать как сэкономить на WriteLn. Теоретически, у вас два вызова с разным набором параметров. На Си можно было бы сделать подобие printf("..формат..", Элемент_Массива, i==n?"\n":" "); хотя само вычисление может оказаться большим.
В том то и дело. Он говорил, что можно использовать в паскале прерывания(безэховый ввод в частности), но нельзя ассемблерные вставки. Да и даже с учётом, что вторая прога на ассемблере, рекорд на паскале всё равно остаётся 1200 байт. Побить я его конечно не стремлюсь(да и вряд ли возможно), но как вообще в принципе можно приблизиться хотя бы 1500-1800 байт на паскале? Пытался использовать модуль dos с его Intr и MsDos, но программа становится больше, что меня не устраивает. Пока, косо смотрю в сторону interrupt-процедур, но толковой документации я не нашёл и я вообще не знаю, могут ли они дать мне уменьшение в размерах?(там вроде как регистры можно использовать, но использует ли их реально компилятор на выходе я не знаю).
Пытался использовать модуль dos с его Intr и MsDos, но программа становится больше, что меня не устраивает. Пока, косо смотрю в сторону interrupt-процедур, но толковой документации я не нашёл и я вообще не знаю, могут ли они дать мне уменьшение в размерах?(там вроде как регистры можно использовать, но использует ли их реально компилятор на выходе я не знаю).
Turbo Pascal позволяет написать свою interrupt-процедуру, но не определить указатель на неё. Когда я пытаюсь написать
… Turbo Pascal ругается на отсутствие = после interrupt. Похоже, у указателей на процедуры вообще не может быть директив.
Ситуация всё же не безнадёжная. Мне удалось вызвать прерывание 16#21# (AH => 16#09#). Вызов прерываний как программных, так и аппаратных, похож на далёкий вызов, но в стек помещается также и регистр флагов. Инструкция iret, которой заканчиваются обработчики прерываний, в отличие от retf, также и возвращает состояние этого регистра. Если в Турбо Паскале указатели на процедуры могут быть только обычными, мне пришла в голову идея объявить тип указателя на прерывание как принимающий один аргумент. И чтоб передавать потом в этот аргумент типичное состояние флагов, которое я вывел на экран ассемблерной процедурой, не попадающей в конечный результат.
Далее, вот конкретно подвызов AH => 16#09# принимает адрес строки в DS:DX. Соответственно, надо как-то подстроить, чтоб там оказались нужные значения. Почти надёжный способ с AX и DX — это устроить возврат LongInt. LongInt возвращается в DX:AX, старшее слово — в DX, младшее — в AX. На DS я повлиять не могу, он спроецирован на общий для всей программы сегмент данных. Так что нужно, чтоб буфер был там. То есть, это обязательно глобальная переменная.
Итак, мне нужна функция, которая сконструирует DX:AX, вернув подходящее значение. Это значение будет сразу же «выброшено», но функция-то об этом не знает. Возвращать лучше бы DWord, но в Turbo Pascal такого типа нет, а есть только знаковый LongInt. Это плохо. Вдруг старший бит DX будет выставлен? Значит, я сначала получаю значение DX как есть (тип Word), потом привожу к Integer, что в Турбо Паскале часто работает как Unchecked_Conversion в языке Ада или reinterpret_cast в C++. Потом надо это значение сдвинуть на 16 бит в верхнее слово, но сначала ещё раз привести тип, на этот раз — к LongInt. А после этого при помощи «or $0900» инициализировать будущий AH.
Я использовал GetIntVec из модуля Dos. Вы можете переделать на прямой доступ к таблице прерываний, но осторожно. Между функцией, которая конструирует DX:AX, и вызовом прерывания по возможности не должно быть никаких вычислений, которые могли бы уничтожить AX. А доступ по прямому адресу чреват чем-то таким. Так что надо сохранить этот адрес заранее поближе. С глобальными переменными, как я вижу, работает хорошо.
--------------------
If you want to get to the top, you have to start at the bottom