Основы машинного кода
Автор: L.A.
Да-да! Всё верно! L.A. из Extacy-3 пишет статью о машинном коде! Погодите-ка! L.A. ведь не кодер?! А вот тут вы ошибаетесь! Я увлекался кодингом с самого основания E-3. Просто Bogie всегда программировал лучше меня, поэтому он и считается главным кодером группы.
Прежде чем начать со всей этой технической болтовни, скажу сразу: чтобы понять эту статью, вам необходимо:
- хорошо знать BASIC;
- иметь базовое представление о том, что такое байт и бит;
- и представлять, как устроена карта памяти вашего «Спекки»!
Всё это уже знаете? Тогда поехали!
Машинный код — это низкоуровневый язык. Что это значит? Это значит, что он максимально близок к прямой записи байтов в память (вы поймёте это лучше, когда дочитаете эту писанину!). А ещё его сложнее понять, чем высокоуровневый язык вроде BASIC.
Как и BASIC, машинный код (сокращённо M/C) состоит из команд, но в машинном коде они называются «мнемониками» (произносится как «не-мо-ни-ки»). Они делятся на две части: опкод (код операции) и операнд. Если вы возьмёте руководство к своему +2, +2A или +3 и откроете главу 8, часть 27 (серая книжка от +2) или часть 28 (чёрная книжка от +2A/+3), то увидите следующую шапку:
CODE CHARACTER HEX Z80 ASSEMBLER -After CBh -After EDh
Это отличное место, чтобы сделать закладку (или загнуть уголок страницы), потому что, набравшись даже небольших знаний по машинному коду Z80, вы будете постоянно возвращаться к этим страницам!
Итак, найдите строку CODE 62. Забудьте пока про колонки символов и HEX, на данный момент они бесполезны. Посмотрите на графу Z80 ASSEMBLER — именно она нас и интересует. Там должно быть написано:
LD A,N
Как вы уже знаете, мнемоники Z80 состоят из двух частей: опкода и операнда. Опкод в команде LD A,N — это часть LD A, а операнд — это N. N — это восьмибитное число, которое определяете ВЫ САМИ! Давайте его зададим. Я выберу 69. Таким образом, инструкция превращается в LD A,69. Проще простого, правда? Итак, теперь мы понимаем, что такое мнемоника Z80 и из чего она состоит.
Но что на самом деле делает LD A,69? Она загружает (LOAD) в регистр A значение 69. Ого-го! Но A — это сокращение от Accumulator (аккумулятор). Это место в вашем Z80, где происходят все вычисления, сравнения и так далее. Подведём итог: LD = LOAD (загрузить регистр или адрес), а A = Accumulator.
Если вы поняли суть, то перейдём к объяснению регистров.
Регистры — это, по сути, области процессора Z80, где он хранит числа и адреса. Вот список основных из них:
H L
D E
B C
A F
IX
IY
Регистры могут хранить либо 8-битное, либо 16-битное число. 16-битные числа хранятся только в регистровых парах, как показано выше: HL, DE, BC, AF, IX и IY. 8-битные числа хранятся в отдельных регистрах: A, B, C, D, E, F, H и L.
Как вы, возможно, знаете (или не знаете), максимальное значение для 8-битного числа — 255, а для 16-битного — 65535. Также вам следует знать, что адрес видеобуфера (display file) — 16384.
Теперь, зная всё это, давайте напишем нашу первую программу:
LD HL,16384 ; Загрузить в HL значение 16384
LD A,69 ; Загрузить в A значение 69
LD (HL),A ; Записать содержимое A по адресу из HL
RET ; Вернуться (RETurn)
Запустите свой ассемблер, задайте ORG на адрес, который не налезет прямо поверх самого ассемблера, наберите этот код, ассемблируйте его, выйдите в BASIC и введите RANDOMIZE USR адрес, где адрес — это адрес, указанный в директиве ORG. Если вы используете ассемблер Tornado, добавьте в самое начало кода следующие строки:
ORG 40000
DUMP 40000
Затем ассемблируйте, вернитесь в BASIC и введите RANDOMIZE USR 40000.
Этот фрагмент кода должен быть вполне понятен новичку. Объясню его подробнее. Первая строка — LD HL,16384 — загружает в регистровую пару HL значение 16384. 16384 — это адрес первого байта экранной области памяти. Вторая строка — LD A,69 — загружает в аккумулятор значение 69. Третья строка — LD (HL),A — та самая магия, которая помещает значение из регистра A по адресу, хранящемуся в HL. В нашем случае она запишет число 69 по адресу 16384 (в видеобуфер). Четвёртая строка — RET — всего лишь возвращает управление в то место, где «Спекки» находился до вызова подпрограммы, то есть в BASIC. Поймите принцип: мы вызвали код из BASIC командой RANDOMIZE USR, поэтому, когда процессор доходит до команды RET, он благополучно возвращается обратно в BASIC. Эта маленькая программка — аналог бейсиковской команды POKE на машинном коде.
Теперь перейдём к новой команде — INC. INC — это сокращение от Increment («инкремент»), что означает «прибавить 1». После INC можно указывать любые регистры, кроме IX и IY. Если мы добавим её в нашу программу, то сможем увидеть эффект в действии:
LD HL,16384
LD A,69
LD (HL),A
INC A
LD (HL),A
RET
Этот код сделает POKE 16384,69, затем увеличит A на 1 (в A станет 70) и сделает POKE 16384,70. Но произойдёт это настолько молниеносно, что вы вряд ли успеете что-то заметить!
Поэтому давайте попробуем что-нибудь чуточку посложнее:
LD HL,16384
LD A,0
POOCAKE LD (HL),A
INC A
HALT
JP POOCAKE
ВНИМАНИЕ... Если вы попытаетесь ассемблировать и запустить это, программа НИКОГДА не вернётся в BASIC. Сейчас объясню, как она работает. Что делают LD HL,16384, LD A,0 и LD (HL),A, вы уже знаете. Обратите внимание на слово POOCAKE слева от LD (HL),A — это метка (LABEL). Команда INC A будет увеличивать A на единицу при каждом выполнении. HALT приостанавливает работу компьютера на 1/50 секунды (до кадрового прерывания), чтобы эффект от инкремента был заметен глазу. Команда JP POOCAKE выполняет безусловный переход обратно на метку POOCAKE, зацикливая выполнение: LD (HL),A -> INC A -> HALT -> JP POOCAKE. Теперь вы понимаете, почему программа не возвращается в BASIC. Наберите её, ассемблируйте по тому же адресу, что и прошлую, и запустите. Ха-ха-ха-ха! Ваш «Спекки» непрерывно делает POKE 16384 увеличивающимся числом!
Вы можете подумать, что значение A будет расти до бесконечности. Но поскольку это 8-битное число, оно дойдёт до 255, а при следующем инкременте превратится в 0 — ведь, как мы помним, максимальное значение 8-битного числа равно 255! Выше 255 оно подняться не может, поэтому переполняется, сбрасывается в 0, и цикл начинается заново!
Если вам всё ещё не совсем понятна логика программы выше, вот её аналог на BASIC:
10 LET HL=16384 : REM LD HL,16384
20 LET A=0 : REM LD A,0
30 POKE HL,A : REM LD (HL),A
40 LET A=A+1 : IF A=256 THEN LET A=0 : REM INC A
50 PAUSE 1 : REM HALT
60 GOTO 30 : REM JP POOCAKE
Вот так это выглядит на BASIC в максимально близком переводе. Заметьте, я добавил проверку на случай, если A превысит 255 — ведь если в BASIC попытаться сделать POKE со значением больше 255, компьютер выдаст ошибку!
На этом пока всё. Надеюсь, вам понравилось это небольшое введение в машинный код. Продолжение читайте в S.E.#2. И помните, что предлог — это худшее слово, которым можно статью завершить с.
Поделитесь вашим мнением о статье