Pull to refresh

Comments 14

Достаточно посмотреть свой код, который ты писал год, два, три назад. :)

И что это даст, пролистывание простыней свичей и ифов?

Ну, как же.. void main() работы самого Торвальдса! Напоминает void setup() в моём скетче для ардуины ;)

А где исходный код MSDOS 1.1 и MSDOS 2.0? Тоже интересная часть истории:)

Мне кажется, или в приведенном коде CERN httpd переполнение буфера при попытке раздать файл размером 100 десятичных гигабайт? Такой исторический код надо закопать и никогда на него не смотреть - это совсем другие языки (даже если это C), совсем другие требования, другая экспертиза. По современным меркам это один сплошной антипаттерн.

А где исходники Doom Engine? Безусловный шедевр

Шедевральная вещь – это исходный код программы-монитора Apple II, написанный Возняком. Я многое в своё время из него почерпнул, разбирая построчно, благо, он был неоднократно опубликован. Например, чтобы пропустить следующую команду размером 2 байта, не тратя ещё 2 байта на минимальную команду перехода, Возняк использовал 1 байт с подходящим кодом команды, который вместе со следующей 2-байтовой командой образовывал безвредную 3-байтовую команду (бывшая команда становилась полем данных новой команды). Так он каждый раз экономил 1 байт за счёт отказа от уровня абстракции ассемблера и работы на уровне голого машинного кода. И весь монитор умещался в ПЗУ размером 2 килобайта (а этот монитор включал набор процедур, как сейчас сказали бы, bios, плюс дизассемблер и собственный командный язык).

1 байт с подходящим кодом команды, который вместе со следующей 2-байтовой командой образовывал безвредную 3-байтовую команду

А можно немного более развернуто? НЯП в эту конструкцию должно быть 2 точки входа, где-то должны быть jmp (ненулевого размера) для перехода?

Мне попадалась интересная конструкция передачи параметров в функцию через PC

call function
.data Hello, World\0
// continue execution here
.label function
pull ax // вынимаем из стека указатель на Hello, world
// обрабатываем строку до \0
// AX теперь указывает на \0, который заодно NOP в 8080
jmp ax 

Экономятся инструкции push перед вызовом функции, pull внутри функции, и jmp вместо ret. Но код вперемешку с данными, никаких early return, никакой кроссплатформенности. Удобно передавать только константные zero-terminated строки. Вряд ли это можно ещё где-то использовать.

Например, так вот (это, правда, из соседнего кода, AppleSoft BASIC):

db57: a9 20        OUTSP           lda     #‘ ’              ;print a space
db59: 2c                           db      #$2C             ;skip over next line
db5a: a9 3f        OUTQUES         lda     #‘?’              ;print question mark
                   ; Print char from A-reg
db5c: 09 80        OUTDO           ora     #$80              ;print A-reg
db5e: c9 a0                        cmp     #$a0              ;control chr?
db60: 90 02                        bcc     LDB64             ;skip if so
db62: 05 f3                        ora     FLASH_BIT         ;=$40 for FLASH, else $00
db64: 20 ed fd     LDB64           jsr     MON_COUT          ;ANDs with $3F (INVERSE), $7F (FLASH)
db67: 29 7f                        and     #$7f
db69: 48                           pha
db6a: a5 f1                        lda     SPEEDZ            ;complement of speed #
db6c: 20 a8 fc                     jsr     MON_WAIT          ;so SPEED=255 becomes A=1
db6f: 68                           pla
db70: 60                           rts

Если мы вызываем подпрограмму OUTQUES по адресу $db5a, то загружаем в аккумулятор код вопросительного знака и уходим дальше в подпрограмму печати произвольного символа из аккумулятора OUTDO, которая находится ниже. А если мы вызываем подпрограмму OUTSP по адресу $db59, то мы загружаем в аккумулятор код пробела, выполняем машинную команду $2c, которая вместе со следующей командой складывается в бесполезную инструкцию тестирования битов bit $3fa9, и опять-таки уходим на код OUTDO. Таким образом, на три точки входа с различными параметрами в печать символа мы потратили всего 5 байтов. Хотя в мнемонике ассемблера мы должны были бы вместо db #$2c поставить двухбайтовый условный переход, например, bne *+2, или даже, рассуждая тупо, трёхбайтовый безусловный – jmp $db5c.

Сам код OUTDO здесь использует подпрограмму монитора MON_COUT для печати символа, но перед этим применяет к коду символа маску, установленному режимами вывода INVERSE и FLASH, а также делает задержку печати, установленную режимом SPEED= (это всё фишки Бейсика).

Интересный пример байтоложества ;) Сэкономлен 1 (один!) байт в одном месте программы. Конечно, когда в ПЗУ всего 2048 байт, это не лишнее, есть даже шедевральный рассказ про последний байт. Но это не масштабитруется; на ПЗУ в 1МБ 1024 байта экономить будет очень дорого.

А как же легендарный алгоритм обратного корня из Quake III Arena?

float Q_rsqrt( float number )
{
	long i;
	float x2, y;
	const float threehalfs = 1.5F;

	x2 = number * 0.5F;
	y  = number;
	i  = * ( long * ) &y;                       // evil floating point bit level hacking
	i  = 0x5f3759df - ( i >> 1 );               // what the fuck? 
	y  = * ( float * ) &i;
	y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//	y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

	return y;
}

В разные времена были разные приоритеты. То на чем экономили 30 лет назад не имеет смысла сейчас.

Sign up to leave a comment.