Примеры ассемблерного кода для ZX Spectrum

Я не буду делать днинное вступление. Один знакомый хакер однажды сказал, что 10 строк кода могут быть понятнее и интереснее, чем 1000 слов объяснений. Все эти примеры написаны на ассемблере для архитектуры Z80 и запускаются на ретро-компьютере ZX Spectrum 48k.

Книги, ссылки, разные полезности и все такое прочее

http://zxnext.narod.ru/manuals/Basic_Programming.pdf — книга по бейсику для спектрума
http://www.retro8bitcomputers.co.uk/Content/downloads/books/SpectrumMachineLanguageForTheAbsoluteBeginner.pdf — и по ассемблеру
http://zxpress.ru/book.php?id=18 — Издательство Инфорком, Программирование в машинных кодах и на языке ассемблера
https://spectrumforeveryone.com/technical/zx-spectrum-pcb-schematics-layout/ — схемотехника. Что и куда подключается
http://datasheets.chipdb.org/Mostek/3880.pdf http://www.zilog.com/docs/z80/um0080.pdf — даташиты на процессор z80
https://clrhome.org/table/ — удобная и практичная таблица доступных опкодов
https://www.chibiakumas.com/z80/ — самоучитель

Выводим текст на экран. Сами символы будут рисоваться с помощью кода, который уже есть в ROM памяти.

 DEVICE ZXSPECTRUM48 ; даем компилятору знать, для какого компьютера мы пишем. Это влияет на экспорт .TAP файла org $8000 ; пусть все наши байты попадут в память начиная с адреса 0x8000 ld a, 2 ; загружаем двойку в регистр A call $1601 ; вызываем функцию, которая лежит где-то в прошивке (переключение потока печати) ld bc, string_end - string ; просим компилятор вычесть одно из другого, это такой хитрый способ вычисления длины строки ld de, string ; кладем адрес начала строки в регистровую пару de call $203c ; и вызываем что-то, что находится в прошивке (печать строки) ; когда оно отработает, выполнение продолжится с этого же места ret ; программа закончилась, пора возвращаться в интерпретатор string: db "zx spectrum rulez",$0d ; строка текста будет лежать по этому адресу
string_end:
code_end: ; это просто метки, когда компилятор примется за дело, он расставит нужные адреса вместо меток SAVEBIN "noise.bin",$8000,code_end - $8000 ; встроенная в компилятор функция. ; можно экспортировать произвольный промежуток как бинарник EMPTYTAP "noise.tap" ; а еще можно сохранить tap прямо из ассемблера, если ваш компилятор так умеет SAVETAP "noise.tap",CODE,"run32768",$8000,code_end - $8000,$8000

https://skoolkid.github.io/rom/index.html — список интересных адресов в прошивке, с комментариями.
https://skoolkid.github.io/rom/asm/1FFC.html — вот тут спрятана $203c процедура.

Сохраняем программу как текстовый файл noise.asm. Запускаем компилятор:

wine sjasmplus.exe noise.asm --lst=noise.lst --sym=noise.sym

Смотрим листинг:

А вот так выгдядит список меток. Теперь каждая метка получила свой адрес:

code_end: EQU 0x00008021
string: EQU 0x0000800F
string_end: EQU 0x00008021

Включаем эмулятор спектрума, вставляем кассету с нашей программой, отдаем команду LOAD "" CODE, включаем кассету на воспроизведение. Программа засасывается в память по адресу, который указан в заголовке блока, то есть, 32768 ($8000).

Запускаем то, что лежит в памяти: RANDOMIZE USR 32768

Добавим пробелы по сторонам и цветовые коды. Пусть надпись будет в центре.

 db $16,11,4,$11,$01,$10,$06," zx spectrum rulez " ; немного поменяем эту строку

Байты можно указывать в 16-ричном виде ($00 — $ff), а можно в десятичном (0 — 255), а можно в виде строкового литерала.

Байт $16 — это команда 'AT', и после нее должны идти еще два байта, задающие новые координаты курсора.

Байты $10, $11 — это команды 'INK' и 'PAPER', после каждой из них должен идти один байт, задающий цвет ($00 — $07).

Надо добавить бейсиковый загрузчик, чтобы программа могла сама запускаться после загрузки с кассеты.

Формат .tap описан здесь: https://documentation.help/BASin/format_tape.html https://sinclair.wiki.zxnet.co.uk/wiki/TAP_format

Спектрум хранит бейсиковые программы в особом формате (https://habr.com/ru/post/103127/). И если набрать программу на эмуляторе спектрума, потом сделать дамп памяти, то можно найти то место, где все это хранится:

Затем эти байты можно вставить прямо в асм и попросить sjasmplus собрать нам tap файл, который содержит загрузчик. Это немного дурацкий метод, но он работает.

 org $4000 ; выбираем какой-нибудь адрес, и ничего, что он попадает в дисплей. Это временно
basic_loader: db $00,$0a,$0e,$00,$20,$fd,"32767",$0e,$00,$00,$ff,$7f,$00,$0d ; 10 CLEAR 32767 db $00,$14,$07,$00,$20,$ef,$22,$22,$20,$af,$0d ; 20 LOAD "" CODE db $00,$1e,$0f,$00,$20,$f9,$c0,"32768",$0e,$00,$00,$00,$80,$00,$0d ; 30 RANDOMIZE USR 32768
basic_loader_end: SAVEBIN "noise.bin",$8000,code_end - $8000 EMPTYTAP "noise.tap" SAVETAP "noise.tap",BASIC,"loader",basic_loader,basic_loader_end - basic_loader, 10 SAVETAP "noise.tap",CODE,"run32768",$8000,code_end - $8000,$8000

Чтобы загрузить такую кассету, нужна команда LOAD ""

Получается, на кассете будет записан сначала заголовок BASIC блока, потом сама программа на бейсике, которая сразу же самозапустится (номер строки, с которой начнется выполнение, хранится в заголовке). А она уже загрузит наш код и запустит его.

Добавим движения. Пусть надпись будет ползать по экрану по диагонали, менять цвет и оставлять следы. Еще сделаем клавишу для выхода.

Длиннотекст и анимированная гифка
 DEVICE ZXSPECTRUM48 org $8000 ld a, 2 call $1601 ; select stream ld a, %00000001 out ($fe), a ; переключим цвет бордюра на синий main_loop: halt ; после этой команды процессор ничего не может выполнять, программа виснет halt ; пока не придет прерывание (а оно срабатывает в начале кадра, 50 кадров в секунду) halt ; прерывания же включены, не так ли? halt ; наша строка будет делать один шаг каждые четыре кадра push bc ; у нас в регистре B хранится переменная. Надо ее сохранить в стеке ld bc, string_end - string ld de, string call $203c ; output string pop bc ; и не забыть вынуть ее обратно из стека ld hl, string + 1 ; Будем прямо на ходу менять байты в строке ld a, (hl) ld b, a ; сохраняем регистр A в регистр B, чтобы потом вернуть, если что inc a cp 22 ; если цифра больше 21, то это уже много, нужно поставить ноль jr nz, vertical_check_ok xor a ; обнуляем регистр A vertical_check_ok: ld (hl), a ; записываем получившийся байт обратно в строку inc hl ld a, (hl) or a ; сравниваем регистр A с самим собой. Если он нулевой, то прыгаем jr z, horisontal_need_to_jump dec a ; уменьшаем на единичку ld (hl), a jr horisontal_ok horisontal_need_to_jump: ld a, 31 ; ставим курсор на крайний правый символ ld (hl), a dec hl ld (hl), b ; тут мы снова лезем в вертикальный байт и меняем его обратно inc hl horisontal_ok: inc hl inc hl ld a, (hl) ; меняем цвет фона inc a and %00000111 ; обнуляем биты, чтобы цифра была от 0 до 7 ld (hl), a inc hl inc hl ld a, (hl) ; меняем цвет текста inc a and %00000111 ; то же самое ld (hl), a ld a, %01111111 in a, ($fe) ; читаем клавиатуру and $00000001 ; если нажать пробел, то программа выходит обратно в бэйсик jr nz, main_loop ; это такой цикл, он будет крутиться все время ret ; return to basic string: db $16,11,4,$11,$01,$10,$06," zx spectrum rulez "
string_end:
code_end: org $4000
basic_loader: db $00,$0a,$0e,$00,$20,$fd,"32767",$0e,$00,$00,$ff,$7f,$00,$0d ; 10 CLEAR 32767 db $00,$14,$07,$00,$20,$ef,$22,$22,$20,$af,$0d ; 20 LOAD "" CODE db $00,$1e,$0f,$00,$20,$f9,$c0,"32768",$0e,$00,$00,$00,$80,$00,$0d ; 30 RANDOMIZE USR 32768
basic_loader_end: SAVEBIN "noise.bin",$8000,code_end - $8000 EMPTYTAP "noise.tap" SAVETAP "noise.tap",BASIC,"loader",basic_loader,basic_loader_end - basic_loader, 10 SAVETAP "noise.tap",CODE,"run32768",$8000,code_end - $8000,$8000

К чему это все? Кому это может понадобиться? Дело в том, что у ассемблерщиков сформировалась традиция проводить фестивали демосцены. Демосцена возникла не на пустом месте, у нее есть свои традиции и своя история. И вы читаете эту статью не случайно. Вам ведь надо срочно научиться кодить на Z80, чтобы успеть отправить свою первую работу на competition. Верно?

Готовые работы других демосценеров доступны для всех желающих (можно запустить на эмуляторе самому или посмотреть видео в YouTube)

На момент написания статьи (29 декабря 2021) близжайшие demoparty сначала DI/HALT в Нижнем Новгороде, а потом Chaos Constructions в Петербурге. И, пока еще есть время, я хочу успеть дописать свой долгострой.

Я пересел на Z80 недавно, раньше я писал прошивки для микроконтроллеров на AVR архитектуре (все началось после этой статьи и этой книжки). Я и сейчас думаю, что AVR — самый адекватный ассемблер, подходящий для новичков. Но правила игры там совсем другие.

Если хотите, я могу написать еще примеры кода для звука, цветной графики, портов, клавиатуры. В эту статью они не поместились.

Если я не упомянул вашу любимую книжку про спектрум, то дайте ссылку. Мне интересно.

Читайте так же:

  • SEO-анализ для новичковSEO-анализ для новичков Описание 20 апреля в 18.00 (мск) Admitad Academy вместе с digital-агентством Webit проведём вебинар «SEO-анализ для новичков».О чём расскажут на вебинаре:Какие факторы влияют на ранжирование сайта;Основные инструменты для анализа;Как проверить сайт на наличие технических ошибок;Как найти […]
  • Как SEO-агентства планируют свое развитие. Результаты опросаКак SEO-агентства планируют свое развитие. Результаты опроса В ноябре-декабре этого года CMS Magazine и независимый консультант Константин Заруцкий в закрытом режиме провели обширное  исследование, посвященное теме развития digital-агентств. Всего в нем приняло участие 300+ компаний, 169 из которых оказывают услуги поисковой оптимизации. […]
  • Приглашаем на digital-конференцию Admitad Expert 2021Приглашаем на digital-конференцию Admitad Expert 2021 Пресс-релиз 17 сентября в Loft Hall в Москве пройдет конференция Admitad Expert – ежегодное событие для представителей digital-рынка о современных подходах к онлайн-маркетингу. Чем удивит вас Expert в 2021 году и почему нужно приобретать билеты уже сейчас? Мир постоянно меняется, и […]
  • Наркологическая клиника Verimed отрицает информацию о возможной утечке данных пациентовНаркологическая клиника Verimed отрицает информацию о возможной утечке данных пациентов По информации издания «Коммерсантъ», наркологическая клиника Verimed опровергла факт утечки персональных данных своих пациентов. Также клиника заявила, что информация в СМИ о продаже ее базы является ложной.Представитель Verimed пояснил, что у них нет базы данных клиентов в цифровом […]