Мир в 1982 году

Общие даты, по категориям, наука, спорт, музыка, кино, театр, литература, живопись, игры (англ.), авиация, метро, железные дороги.

Положение на игровом и компьютерно-железных рынках

По моим впечатлениям, этот год не был чем-то примечателен - выходили новые игры и системы (компьютеры, приставки, портативные устройства) и ничто не предвещало бурь в следующем году.

Из выпущенных систем стоит отметить:

11.1981 - PC-88. Несмотря на выпуск в прошлом году, игры начали выходить только с 1982 года (во всяком сл��чае достоверной информации о играх за 1981 год нет, на mobygames с высокой долей вероятности может быть ошибка с платформой).

23.04.1982 - ZX Spectrum. Только в Британии, выпуск в США будет только в ноябре 1983 года. 06.1982 - MPC 1600 (Columbia Data Products). Первый IBM PC совместимый клон.

08.1982 - Commodore 64. Легенда и важный гвоздь кризиса игровой индустрии США в 1983 году. 10.1982 - PC-98. Самый известный японский компьютер. В последние годы, за счет основы на процессорах Intel, возможна эмуляция в DosBox-X.

11.1982 - Sharp X1. Самый продвинутый из основной тройки японцев на момент выхода (PC-88, X1, FM-7).

11.1982 - FM-7.

Для обычного человека все тоже шло своим чередом - потребности покупать еще один компьютер нет, в отличие от приобретения софта и потенциального расширения памяти. Последнее можно было приобрести у IBM или же купить у Microsoft подобие-аналог RamCard (помимо опции расширения памяти её можно было использовать и как RAM-диск, что могло быть полезным). Другое направление - программирование. BASIC показался привлекательным по своей простоте и интересно посмотреть еще какой-нибудь язык. Таковым становится ассемблер, благо IBM и Microsoft продают компиляторы под него (но с нюансом).

Эмуляционный конфиг

RamCard выпускался с начала 1982 года (пример раннего экземпляра - у некоторых чипов стоит дата 8204 (четвертая неделя 1982 года), а на самой плате указан 1981 год) и был доступным решением (125 долларов за вариант на 64КБ при покупке напрямую у Microsoft согласно официальной брошюре, у реселлеров цена могла быть до двух с небольшим раз выше). Память же нужна для работы компилятора из .ASM в .OBJ - нужно минимум 128КБ, на 64КБ работать не будет.

Софт

Насколько понимаю, новая версия BASICA поставлялась с новыми партиями IBM PC, потому на текущем этапе будут использоваться изначальные PC-DOS и BASICA версий 1.00. BASIC-86 5.22 также не стал смотреть из-за наличия знакомства с BASIC и несовместимости (исполняемый файл в формате CMD, работающий под CP\M).

Однако, как раз с этого года доступны компиляторы под ассемблер - IBM Macro Assembler и Microsoft Macro Assembler. По факту это один и тот же компилятор, просто у IBM самая ранняя версия за декабрь 1981 года. Вариант Microsoft вышел в феврале-марте 1982 года, но обновленная версия указано только у линковщика (LINK.EXE, 1.08) - у компилятора 1.00 и 1981 год. Нюанс в том, что у IBM на дискете только компилятор, а LINK.EXE изначально вложен в состав PC-DOS 1.00. Я об этом подумал уже в ходе компиляции кода под вариант Microsoft, но я решил этим пренебречь ввиду не сильно большой разницы во времени выхода.

Так как писать под ассемблер придется много, я позволил себе допустить послабление и использовать текстовый редактор для IBM PC, который стал доступен на этой платформе немного позже 1982 года. Настолько сильно погружаться в эпоху интереса у меня не было, а встроенный в PC-DOS 1.00 EDLIN меня ужаснул за 10 секунд. После просмотра ряда самых маленьких текстовых редакторов на old-dos.ru выбор пал на WordStar 3.20 (остальное не заработало) - исполняемый файл датируется маем 1983 года, хотя в самой программе указан 1982 год. Несмотря на неказистый дизайн у него есть базовый набор нужных функций по набору текста. Вкупе с моноширинным шрифтом я, не побоюсь сказать, кайфанул от процесса печати кода - просто и минималистично, но в то же время приятно и комфортно. Есть, однако, нюанс с размером файлов - максимальный размер при сохранении составляет, примерно, около 14КБ (хотя при этом WordStar может открывать файлы большего размера). Дополнительная причина писать код модульно (в идеале - каждая функция в своем файле, а в условном MAIN.ASM только ссылки на эти файлы), а не в один файл.

Пример работы в WordStar 3.20
Пример работы в WordStar 3.20

Пара нюансов по Macro Assembler - расширение файла не имеет значения (можно не только .ASM, но и любое другое, главное чтобы внутри был код ассемблера), внутри файла должен быть особый символ для определения конца файла (в моем случае это был знак с hex-кодом 1A: стрелка).

Также - dosbox-x с некоторых пор поддерживает монтирование дискет форматированных под DOS 1.x, потому его можно использовать для копирования файлов туда-сюда.

Программы

Из-за размера .ASM файлов, полные листинги приводится не будут.

Скачать - github.

Memory Check (MEM.EXE)

Классика поздних версий MS-DOS в реализации для ранних.

Запросы: Qwen (ранняя версия запроса), Grok (ранняя версия запроса).

PC-DOS 1.00 занимает 12КБ в памяти. Интересно, про какую версию информация, что она занимает всего 8КБ?
PC-DOS 1.00 занимает 12КБ в памяти. Интересно, про какую версию информация, что она занимает всего 8КБ?

Также - рабочий пример работы с файлами через FCB (File Control Block): в ходе работы данные записываются в файл MEM.TXT объемом 64 байта, который можно посмотреть через команду TYPE. Интересно, что размер показывается честно - BASIC, WordStar и, возможно, Macro Assembler округляют размер файла до кратного 128 байтам (насколько понимаю, следствие наследия\совместимости CP\M). FCB является единственным способом работы с файлами в DOS 1.x, handle-метод появился только в DOS 2.x.

Ниже - отрывок из кода (плюс код с разными процедурами выхода из программы в зависимости от версии DOS - 1.x не знает про 4Ch; выход из программы работает через RET, а перед этим нужно в самой первой процедуре кода набирать команды PUSH DS ; SUB AX, AX ; PUSH AX для обозначения PSP-сегмента, от которого выход и происходит):

    ; Set up FCB to MEM.TXT
    LEA DX, FILE_FCB
    MOV AH, 0Fh ; Open file
    INT 21h
    CMP AL, 0FFh ; FFh="not found/error"
    JNE SHORT FILE_OPENED

    ; If not found, create it
    LEA DX, FILE_FCB
    MOV AH, 16h ; Create file
    INT 21h
    CMP AL, 0FFh ; Error?
    JE SHORT SKIP_SAVE ; Skip if create failed

FILE_OPENED:
    ; Set record size to 1 byte (for seq write)
    MOV WORD PTR [FILE_FCB+0Eh], 64 ; Record size = 64

    ; Set DTA to output buffer (for write)
    MOV DX, OFFSET OUTPUT_BUFFER
    MOV AH, 1Ah ; Set DTA
    INT 21h

    ; Write to file (AH=15, number of records=OUTPUT_LEN)
    MOV CX, OUTPUT_LEN
    LEA DX, FILE_FCB
    MOV AH, 15h ; Seq write
    INT 21h
    CMP AL, 0 ; AL=0 success
    JNE SHORT SKIP_SAVE ; Skip close if write failed

    ; Close file
    LEA DX, FILE_FCB
    MOV AH, 10h ; Close file
    INT 21h

SKIP_SAVE:
    ; Exit based on DOS version
    CMP DOS_MAJOR, 2
    JAE SHORT EXIT_DOS2
    RET

EXIT_DOS2:
    MOV AH, 4Ch
    INT 21h
START ENDP

Draw Tool (Draw160)

Рисовалка в нестандартном режиме 160x100x16 RGBI (технически обозначается как 160x200, но чем это обусловлено я не выяснял, специалисты смогут лучше пояснить). По факту - модификация текстового режима 80x25 с поддержкой цвета.

Под DOS 1.x глючит (курсор появляется в любой точке экрана и в том числе за его пределами) и работает только один из Shift'ов клавиатуры, такое поведение происходит и в MartyPC. Под DOS 2.x+ работает корректно. Также - запускается и на конфиге с 48КБ памяти.

Запросы: Qwen (1, 2, 3), Google Gemini, Grok (1, 2, 3).

Скриншот из dosbox-x
Скриншот из dosbox-x
Скриншот из PCBox 5.4
Скриншот из PCBox 5.4

Запись в DosBox Pure (RetroArch) с шейдером crt-royale-ntsc-320px-composite.glslp (сделать скриншот не удалось - портится изображение).

Ниже два отрывка кода - в первом набор значений для перепрограммирования CRTC, а во втором сама процедура перенастройки режима:

    ; --- CRTC Register Data for 160x100 Mode ---
    crtcvals DB 71h, 50h, 5Ah, 0Ah ; R0-R3 (Horizontal)
             DB 7Fh ; R4: Vertical Total (127)
             DB 06h ; R5: Vertical Adjust
             DB 64h ; R6: Vertical Displayed (100 rows)
             DB 70h ; R7: Vertical Sync
             DB 02h ; R8 Interlace (Mode 2)
             DB 01h ; R9 Max Scan Line (1=2 lines high)
             DB 20h, 00h ; R10-11 (Cursor - hidden)
; ===============================================
; SET_MODE_160X100
; ===============================================
SET_MODE_160X100 PROC NEAR
    ; 1. Set standard 80x25 text mode first (Mode 3)
    MOV AX, 0003h
    INT 10h

    ; 2. Disable Blink (Port 03D8h)
    MOV DX, 03D8h
    MOV AL, 09h
    OUT DX, AL

    ; 3. Reprogram CRTC (6845)
    MOV SI, OFFSET DGROUP:crtcvals
    MOV CX, 12 ; Update registers 0 to 11
    MOV DX, 03D4h ; CRTC Index Register
    XOR AH, AH ; Start at Register 0

CRTC_LOOP:
    MOV AL, AH ; Select Register Index
    OUT DX, AL
    INC DX ; Select Data Register (3D5h)
    MOV AL, CS:[SI] ; Get Value (CS override needed)
    OUT DX, AL
    DEC DX ; Back to Index
    INC SI
    INC AH
    LOOP CRTC_LOOP
    RET
SET_MODE_160X100 ENDP

Основной функционал и выход из программы под DOS 1.x работает. Единственное что не удалось сделать рабочим - сохранение\загрузка файла через FCB. Найти или подобрать рабочие процедуры мне не удалось, информации про это в документации к Macro Assembler нет. Обидненько.

Обновление (16.02.2026): используя документацию по самому DOS (IBM PC-DOS 1.0 (Aug. 1981), доступен минимум на WinWorld) удалось сделать рабочими FCB и функции сохранения\загрузки файла (запросы - Qwen, DeepSeek, Grok):

    ; --- FCB for IMG.PIC (Standard 37 bytes) ---
    fcb DB 0 ; Drive=0 (default)
        DB 'IMG', 5 DUP(' ') ; Filename (8 chars, space-padded)
        DB 'PIC' ; Extension (3 chars, space-padded)
        DW 0 ; Current block (DW 0)
        DW 80h ; Record size=128 bytes
        DB 16 DUP(0) ; File size + reserved fields (16 bytes)
        DB 0 ; Current record within block
        DD 0 ; Relative record number (4 bytes)
; ===============================================
; SAVE_IMAGE
; ===============================================
SAVE_IMAGE PROC NEAR
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI
    PUSH DI
    PUSH DS
    PUSH ES

    ; Set DTA to our buffer
    MOV DX, OFFSET dta
    MOV AH, 1Ah
    INT 21h

    ; First try to delete existing file (FCB delete)
    LEA DX, fcb
    MOV AH, 13h ; Delete file via FCB
    INT 21h ; Ignore errors (file may not exist)

    ; Create file IMG.PIC
    LEA DX, fcb
    MOV AH, 16h ; Create file via FCB
    INT 21h
    OR AL, AL ; AL=0, success
    JNZ SAVE_ERROR

    ; FCB block record
    XOR AX, AX
    MOV WORD PTR fcb+12, AX ; current block = 0
    MOV BYTE PTR fcb+32, AL ; current record = 0

    ; Prepare to write 125 records (128 bytes each, total 16000 bytes)
    MOV BX, 125 ; Number of records
    MOV AX, 0B800h
    MOV ES, AX ; ES = VRAM segment
    XOR SI, SI ; Source offset in VRAM

SAVE_LOOP:
    PUSH BX

    ; Copy 128 bytes from VRAM to DTA
    MOV DI, OFFSET dta ; Reset DI to start of DTA
    MOV CX, 128 ; 128 bytes
    CLD
COPY_BYTE:
    MOV AL, ES:[SI]            ; Byte from VRAM
    MOV [DI], AL               ; Store in DTA
    INC SI
    INC DI
    LOOP COPY_BYTE

    ; Write one record via FCB
    LEA DX, fcb
    MOV AH, 15h ; Write seq record
    INT 21h
    OR AL, AL ; AL=0, success
    JNZ SAVE_ERROR_POP

    POP BX
    DEC BX
    JNZ SAVE_LOOP

    ; Close file
    LEA DX, fcb
    MOV AH, 10h ; Close file via FCB
    INT 21h

    ; Restore segment registers
    ; MOV AX, DGROUP
    ; MOV DS, AX
    ; MOV ES, AX

    POP ES
    POP DS
    POP DI
    POP SI
    POP DX
    POP CX
    POP BX
    POP AX
    CLC ; Success
    RET

SAVE_ERROR_POP:
    POP BX

SAVE_ERROR:
    ; Close file if open
    LEA DX, fcb
    MOV AH, 10h ; Close file via FCB
    INT 21h
    MOV DL, 07h ; Beep on error
    MOV AH, 02h
    INT 21h
    POP ES
    POP DS
    POP DI
    POP SI
    POP DX
    POP CX
    POP BX
    POP AX
    STC ; Failure
    RET

SAVE_IMAGE ENDP

; ===============================================
; LOAD_IMAGE
; ===============================================
LOAD_IMAGE PROC NEAR
    PUSH AX
    PUSH BX
    PUSH CX
    PUSH DX
    PUSH SI
    PUSH DI
    PUSH DS
    PUSH ES

    ; Set DTA to our buffer
    MOV DX, OFFSET dta
    MOV AH, 1Ah
    INT 21h

    ; Open file using FCB
    LEA DX, fcb
    MOV AH, 0Fh ; Open file via FCB
    INT 21h
    OR AL, AL ; AL=0, success
    JNZ LOAD_ERROR

    ; FCB block record
    XOR AX, AX
    MOV WORD PTR fcb+12, AX ; current block = 0
    MOV BYTE PTR fcb+32, AL ; current record = 0

    ; Read 125 records (128 bytes each)
    MOV BX, 125 ; Number of records
    MOV AX, DGROUP
    PUSH AX
    POP DS ; DS=Data segment
    MOV AX, 0B800h
    PUSH AX
    POP ES ; ES=VRAM segment
    MOV DI, 0 ; Start offset in VRAM

LOAD_LOOP:
    PUSH BX

    ; Read one record into DTA
    LEA DX, fcb
    MOV AH, 14h ; Read seq record
    INT 21h
    OR AL, AL ; AL=:0 - success, 1 - EOF, 3 - error
    JNZ LOAD_ERROR_POP

    ; Copy 128 bytes from DTA to VRAM
    MOV SI, OFFSET dta ; Reset SI to start of DTA
    MOV CX, 64 ; 64 words = 128 bytes
    CLD
    REP MOVSW ; Copy, DI advances by 128 bytes

    POP BX
    DEC BX
    JNZ LOAD_LOOP

    ; Close file
    LEA DX, fcb
    MOV AH, 10h ; Close file via FCB
    INT 21h

    ; Restore segment registers
    ; MOV AX, DGROUP
    ; MOV DS, AX
    ; MOV ES, AX

    POP ES
    POP DS
    POP DI
    POP SI
    POP DX
    POP CX
    POP BX
    POP AX
    CLC ; Success
    RET

LOAD_ERROR_POP:
    POP BX

LOAD_ERROR:
    ; Close file if open
    LEA DX, fcb
    MOV AH, 10h ; Close file via FCB
    INT 21h
    MOV DL, 07h ; Beep on error
    MOV AH, 02h
    INT 21h
    POP ES
    POP DS
    POP DI
    POP SI
    POP DX
    POP CX
    POP BX
    POP AX
    STC ; Failure
    RET

LOAD_IMAGE ENDP

К сожалению, возникла другая проблема - после сохранения\загрузки перестает работать курсор. Выявить проблему пока не удалось. Впрочем, исправление этого момента не является принципиально важным - есть возможности компенсировать.

Текущая альтернатива это dosbox-x из-за поддержки сэйв-стейтов (и DosBox Pure в Retroarch).

Super Mario Bros., почти что скриншот
Super Mario Bros., почти что скриншот
Скриншот-мокап возможной игры (Mars Caves). Спрайты персонажей и объектов 16x16, окружения - 8x8.
Скриншот-мокап возможной игры (Mars Caves). Спрайты персонажей и объектов 16x16, окружения - 8x8.
Скриншот из dosbox-x
Скриншот из dosbox-x
Подобие spritesheet
Подобие spritesheet
Скриншот-мокап возможной игры (Cavexplore). Спрайты 8x8. Сильно упрощенная стилизация под Atari 2600.
Скриншот-мокап возможной игры (Cavexplore). Спрайты 8x8. Сильно упрощенная стилизация под Atari 2600.
Недорисованное - Contra (NES/Famicom, 1988).
Недорисованное - Contra (NES/Famicom, 1988).
Superbrothers: Sword & Sorcery EP (2011, iPhone)
Superbrothers: Sword & Sorcery EP (2011, iPhone)
Исходник (Contra)
Исходник (Contra)
Исходник (Superbrothers: Sword & Sorcery EP)
Исходник (Superbrothers: Sword & Sorcery EP)

Помощь приветствуется: DOS 1.x, FCB (решено), DOS 2.x+, handle (github).

PC Speaker Music Tool (PCSMUS)

По замыслу - программа для написания музыки под PC Speaker с возможностью сохранять\загружать результат в файл для использов��ния\прослушивания.

Запросы: Qwen, DeepSeek, Grok (1, 2).

Сделать рабочий код не старался, так как не видел смысла тратить на это время из-за фиаско с FCB для предыдущей программы. Первый вариант после конверсии из MASM 5.0+ в MASM 1.0 компилится без ошибок, но не работает (а еще из .OBJ получается большой по размеру исполняемый файл - 68480 байт). Второй вариант тоже компилится без ошибок и запускается, но зависает (возможно, неправильная последовательность операнд в одной из функций\процедур).

Помощь\предложения по написанию приветствуются - тема на github.

Концепт подобия движка

Идея простая - используя Draw160 и PCSMUS с написанием игровой логики под ассемблер писать игры, которые можно запускать на самых ранних IBM PC как в эмуляции, так и на реальном железе. При этом они - работают в нестандартном графическом режиме (160x100x16 RGBI), с музыкой на PC Speaker (пример красивой реализации - System Beeps, статьи на тему от автора - 1, 2).

Вспомогательные средства

CP437CHK - скрипт на python, проверяющий текстовые файлы на соответствие кодовой странице 437. Полезно, если в коде от нейронки неправильные символы из-за которых он не запускается под эмуляцией.

Возможные выводы

В целом, работа с ассемблером - это интересно. Правда, для понимания кода без справочника по командам и операндам процессора разобраться затруднительно. Сложностей при этом хватает и без этого - программа может скомпилироваться без ошибок, но не работать (для дополнительных проверок и дебага предусмотрены NUL.LST и NUL.CRF, но для этого требуются дополнительные знания) или же глючить в какой-то среде (Draw160 подглючивает в DOS 1.x, но при этом корректно работает под DOS 2.x).

Структура кода и синтаксис мне отчасти напомнили python (в частности, проставление пробелов в строках, хотя это, возможно, было не обязательно делать). Понравился и формат его организации (сначала идут сегменты данных DATASEG, потом STACKSEG, а затем до конца CODESEG; все операции желательно выполнять внутри процедур и самая первая должна быть PROC FAR, последующие — PROC NEAR; START PROC FAR и END START обертывают код внутри CODESEG). Разобрать работу с файлами и можно было бы попробовать сделать графический текстовый редактор с поддержкой нескольких языков минуя ограничения ранних IBM PC за счет использования графики из файла. И игры попробовать поделать само собой.