--= NES 6502 Assembler =--
NES - это старая добрая приставка Dendy раработанная Nintendo на базе 8-ми
разрядного процессора 6502. Я чё-то заморочился написанием демок под эту
платформу, также была идея сделать номер в виде NES-образа. Тоесть грузишь
его например через эмулятор и получаешь оболочку + стотьи ) Но увы не
получилось сделать нормальную облочку, поэтому отложил идею до след номера.
Ну а это небольшая статья по программированию под NES, некоторое описание
архитектуры и её особенностей.
Для написания на компе вареза под NES понадобится ассемблер (например
nesasm или p65) и какой-нить из эмуляторов (их тьма, jNES например и
проч).
Вот таблица инструкций ассемблера архитектуры 6502 (из доки по nesasm):
+------+----------+-----------------------------+
| | NVTBDIZC | Description |
+------+----------+-----------------------------+
| ADC | XX0---XX | Add with Carry |
| AND | X-0---X- | Logical AND |
| ASL | X-0---XX | Arithmetic Shift left |
| BCC | --0----- | Branch if Carry Clear |
| BCS | --0----- | Branch if Carry Set |
| BEQ | --0----- | Branch if Equal |
| BIT | XX0---X- | Bit Test |
| BMI | --0----- | Branch if Minus |
| BNE | --0----- | Branch if Not Equal |
| BPL | --0----- | Branch if Plus |
| BRA | --0----- | Branch Always |
| BRK | --0----- | Break |
| BVC | --0----- | Branch if Overflow Clear |
| BVS | --0----- | Branch if Overflow Set |
| CLC | --0----0 | Clear Carry flag |
| CLD | --0-0--- | Clear Decimal flag |
| CLI | --0--0-- | Clear Interrupt disable |
| CLV | -00----- | Clear Overflow flag |
| CMP | X-0---XX | Compare A with source |
| CPX | X-0---XX | Compare X with source |
| CPY | X-0---XX | Compare Y with source |
| DEC | X-0---X- | Decrement |
| DEX | X-0---X- | Decrement X |
| DEY | X-0---X- | Decrement Y |
| EOR | X-0---X- | Logical Exclusive OR |
| INC | X-0---X- | Increment |
| INX | X-0---X- | Increment X |
| INY | X-0---X- | Increment Y |
| JMP | --0----- | Jump |
| JSR | --0----- | Jump to Sub Routine |
| LDA | X-0---X- | Load A |
| LDX | X-0---X- | Load X |
| LDY | X-0---X- | Load Y |
| LSR | 0-0---XX | Logical Shift Right |
| NOP | --0----- | No Operation |
| ORA | X-0---X- | Logical inclusive OR |
| PHA | --0----- | Push A |
| PHP | --0----- | Push P |
| PLA | X-0---X- | Pull A |
| PLP | XXXXXXXX | Pull P |
| ROL | X-0---XX | Rotate Left |
| ROR | X-0---XX | Rotate Right |
| RTI | XXXXXXXX | Return from Interrupt |
| RTS | --0----- | Return from Sub Routine |
| SBC | XX0---XX | Substract with Carry |
| SEC | --0----1 | Set Carry flag |
| SED | --0-1--- | Set Decimal flag |
| SEI | --0--1-- | Set Interrupt disable |
| STA | --0----- | Store A |
| STX | --0----- | Store X |
| STY | --0----- | Store Y |
| TAX | X-0---X- | Transfer A to X |
| TAY | X-0---X- | Transfer A to Y |
| TSX | X-0---X- | Transfer S to X |
| TXA | X-0---X- | Transfer X to A |
| TXS | --0----- | Transfer X to S |
| TYA | X-0---X- | Transfer Y to A |
+------+----------+-----------------------------+
А теперь по порядку: сам процессор восьмиразрядный, имеет 5 регистров. Это
X, Y - обычные регистры
A - аккумулятор, вся арифметика и логика выполняется тут
S - старый добрый stack pointer ) куда ж без него
P - хранит статус процессора, всякие флаги и проч. о нём позднее.
О том какие инструкции как меняют флаги статус процессора - см. 2й
столбец таблицы.
Статусы процессора.
0 1 2 3 4 5 6 7 (8bit)
N V B D I Z C
N - Negative. Означает что 7й бит аккумулятора, отвечающий за знак, равен 1.
V - Overflow. Выставляется когда сложение или вычитаение двух чисел выходит
за пределы от -128 до +127.
B - Break. Выставляется если вызывается инструкция BRK, сбрасывается при
внешнем прерывании IRQ.
D - Decimal mode. Вкл/выкл десятичный режим.
I - Interrupt disable вкл/выкл )
Z - Zero. Выставляется если какая-то из арифметических инструкций вернула 0.
C - Carry. При сложении или вычитании.
__ Прерывания. __
На 6502 существует всего 3 типа прерываний:
- RESET - хардварное, юзается при загрузке и ребуте
- IRQ/BRK - хардварное IRQ или софтварная инструкция BRK, этот тип
прерываний называется maskable interrupts и их обработку можно отключать с
помощью флага Interrupt disable процессора.
- NMI - не отключаемое прерывание, non maskable interrupt. Генерируется
каждым новым кадром (Vblank - обновление экрана), 50 раз в секунду на PAL
и примерно 60 раз в секунду на NTSC.
Адреса обработчиков прерывания в памяти NES:
$FFFA, $FFFB = NMI
$FFFC, $FFFD = RESET
$FFFE, $FFFF = IRQ/BRK
По приоритетам: RESET, NMI, IRQ/BRK (низший).
__ Адресация. __
Адресное пространство NES адресуется 16-ю битами ($0000 - $ffff), тоесть
составляет 64 кбайта.
Первые 256 байт памяти называются zero page, следующие 256 байт - 1
страница. Всего 256 страниц стало быть.
$0000 .. $00ff - zero page
$0100 .. $01ff - stack. первая страница это стек.
...
Вообще принято юзать полный адрес, тоесть $xxxx но для zero page можно
сокращать - $xx.
Вот примерные типы адресаций:
$xxxx - абсолютный адрес
$xx - zero page адрес
$xxxx,X/Y - адрес + содержимое регистра X или Y в качестве смещения.
($xx, X) - indexed inderect. значение регистра X в качестве смещения. В
итоге адрес берётся из $(xx + X) и (xx + X + 1)
($xx),Y - inderect indexed. Адрес = $(xx)$(xx+1) + Y
$xx - в инструкциях ветвления (branch) если xx < 127 то прибавляется
к текущему значению prog counter. в противном случае из prog
counter вычитается значение $ff-$xx
__ Инструкции. __
LDA $xxxx - загрузить значение в аккумулятор
LDX $xxxx - загрузить значение в регистр Х
LDY $xxxx - загрузить значение в регистр Y
STA $xxxx - сохранить значение аккумулятора
STX $xxxx - сохранить значение регистра Х
STY $xxxx - срхранить значение регистра Y
примеры:
LDA #$ad - загружает константу 0xad в аккумулятор
LDA $003f - загружает значение по адресу $003f в аккумулятор
LDA $3f - тоже самое, но т.к. адрес с zero-page можно сократить запись
STA $003d - сохраняем знакчение аккумулятора по адресу $003d
STA $3d - тоже самое, но используем краткий адрес zero-page
ADC $xxxx - прибавить к аккумулятору с учётом carry flag
SBC $xxxx - вычесть из аккумулятора с учётом carry flag
INC $xxxx - увеличить память на единицу
INX - увеличить на единицу регистр X
INY - увеличить на единицу регистр Y
DEC $xxxx - уменьшить память на единицу
DEX - уменьшить на единицу регистр X
DEY - уменьшить на единицу регистр Y
AND $xxxx - логическое И: значение по адресу $xxxx и аккумулятор
ORA $xxxx - логическое ИЛИ: значение по адресу $xxxx и аккумулятор
EOR $xxxx - исключающее ИЛИ: значение по адресу $xxxx и аккумулятор
JMP $xxxx - джамп ага
особая адерсация (см.выше branch)
BCC $xx - локальный джамп если Carry flag=0
BCS $xx - локальный джамп если Carry flag=1
BEQ $xx - локальный джамп если Zero flag=1
BNZ $xx - локельный джамп если Zero flag=0
BMI $xx - локальный джамп если Negative flag=1
BPL $xx - локальный джамп если Negative flag=0
BVS $xx - локальный джамп если Overflow flag=1
BVC $xx - локальный джамп если Overflow flag=0
CMP $xxxx - сравнение аккумулятора со значением по адресу $xxxx
CPX $xxxx - сравнение регистра X со значением по адресу $xxxx
CPY $xxxx - сравнение регичсра Y со значением по адресу $xxxx
BIT $xxxx - сравнивает биты по адресу $xxxx и аккумулятор, не меняет значения
операндов.
ASL - сдвиг влево значения аккумулятора
LSR - логический сдвиг направо
ROL - поворот налево
ROR - поворот направо
TAX - перенос значения аккумулятора в регистр X
TAY - перенос значения аккумулятора в регистр Y
TXA - перенос значения регистра X в аккумулятор
TYA - перенос значения регистра Y в аккумулятор
TSX - перенос указателя стека в регистр X
TXS - перенос значения регистра Х в указатель стека
PHA - кладём значение аккумулятора в стек
PHP - кладём значения флагов процессора в стек
PLA - берём значение аккумулятора из стека
PLP - берём значение флагов процессора из стека
JSR - прыжок в подфункцию
RTS - возвращение из подфункции
RTI - возвращение из обработчика прерывания
CLC - сбросить Carry flag
CLD - сбросить Decimal flag
CLI - сбросить Interrupt disable flag
CLV - сбросить oVerflow flag
SEC - установить Carry flag
SED - установить Decimal flag
SEI - установить Interrupt disable flag
NOP - вездесущий ноп ))
BRK - прерывание, проц вызывает обработчик, адрес которого лежит в
$fffe,$ffff
__ Picture Processing Unit (PPU). __
В NES за графику отвечает отдельный модуль, PPU (аля видеокарта )). Память
PPU - VRAM - состоит из областей:
$0000 - pattern table 0 (размер $1000) PT0
$1000 - pattern table 1 (размер $1000) PT1
$2000 - таблица имён 0 (размер $3с0) NT0
$23c0 - таблица атрибутов 0 (размер $40) AT0
$2400 - таблица имён 1 (размер $3с0) NT1
$27c0 - таблица атрибутов 1 (размер $40) AT1
$2800 - таблица имён 2 (размер $3с0) NT2
$2bc0 - таблица атрибутов 2 (размер $40) AT2
$2c00 - таблица имён 3 (размер $3с0) NT3
$2fc0 - таблица атрибутов 3 (размер $40) AT3
$3000 - free space (размер $f00)
$3f00 - палитра (размер $10)
$3f10 - палитра спрайтов (размер $10)
таблицы Pattern Table содержат кусочки изгображения 8х8 пикселей (tiles),
из которых формируется изображение.
Пример тайла:
$0000 00010000
.. 00000000
.. 01000100
.. 00000000 bit 0
.. 11111110
.. 00000000
.. 10000010
$0007 00000000
$0008 00000000
.. 00101000
.. 01000100
.. 10000010 bit 1
.. 00000000
.. 10000010
.. 10000010
$000f 00000000
рисунок 8х8 бит разбит на две части, складывая которые также индексируется
цвет (максимум на одном экране возможно одновременно отобразить 16 цветов).
Дикий гемморой на самом деле так рисовать, поэтому есть редакторы тайлов. В
них рисуешь картинку, а прога разбивает её на тайлы + кодирует цветами и
выдаёт дамп, который можно сразу загнать в Pattern table.
таблицы Name Table - матрицы 32x30 из индексов тайлов, описанных в pattern
table. Тоесть картинка получается 256х240 пикселей.
таблицы Atribute Tables - таблица состоящая из таблиц тайлов 4х4 с
прописаными для них цветами.
таблица цветов (палитра) - максимум 16 цветов. Нулевой элемент палитры
задаёт "прозрачный" цвет.
Зеркалирование между Name tables. Эта фишка означает что когда идёт запись
в одну из зеркалированных таблиц, изменения автоматически вносятся и в
другую. По умолчанию в NES используется вертикальое или горизонтальное
зеркалирование.
NT0 NT1 NT2 NT3
заркалирование +---------------------------
горизонтальное I $2000 $2000 $2400 $2400
вертикальное I $2000 $2400 $2000 $2400
Как рисуется картинка на NES (сверху вниз):
1. Color intensity (общий параметр графы, задаёт общий оттенок экрана)
2. спрайты с BGPRI=0 (спрайты, которые поверх бекграунда)
3. бэкграунд
4. спрайты с BGPRI=1 (спрайты, которые позади бэкграунда)
__ Спрайты. __
В PPU есть отдельная область памяти - SPR-RAM, тоесть память для спрайтов.
Всего влазит 64 спрайта размерами 8х8 и 8х16, тайлы спрайтов тоже храняться в
Pattern Table-ах VRAM. SPR-RAM это по сути та же таблица, заполненная
структурами спрайтов. Описание структуры примерно такое:
- y координата из левого верхнего угла (1 байт)
- индекс тайла в pattern table VRAM (1 байт)
- атрибуты спрайта vhp000cc (1 байт)
v - vertical flip 0/1
вертикально перевачивать спрайт
h - horizontal flip 0/1
горизонтально переворачивать спрайт
p - background priority 0/1
если 0 то силой рисует поверх бэкграунда спрайт
далее 3 нулевых бита..
cc - два старших бита цвета
Также спрайты в зависимости от расположения структуры в SPR-RAM имеют свои
приоритеты. Приоритет спрайта с индексом 0 выше чем спрайта с индексом 1, и
так далее.
__ Взаимодействие. __
Всё пиздато, но мы рассмотрели всё пока лишь по отдельности. Несколько
слов о том, как всё это работает вместе. А работает через отображения в
память NES регистров. Регистры содержат 16 битные значения. кароч пример:
LDA #$20
STA $2006
LDA #$00
STA $2006 ; пишем в $2006 адрес указателя VRAM = $2000
; далее читаем из $2007 данные VRAM, указатель++
LDA $2007 ; A=?? VRAM Buffer=$AA
LDA $2007 ; A=$AA VRAM Buffer=$BB
LDA $2007 ; A=$BB VRAM Buffer=$CC
Ну кароч такое чтение происходит, прикол в том что буффер VRAM PPU при
первом чтении ничего не возвращает, эдакая бага =)
А теперь подробнее список отображений регистров в памяти:
$2000 - PPU Control Register 1 (только запись)
флаги:
D7 - выполнять ли прерывание NMI при VBlank
D6 - не используется
D5 - размер спрайта 8х8 или 8х16
0 - 8х8
1 - 8х16
D4 - Pattern table для бэкграунда (0 или 1)
D3 - Pattern table для спрайтов (0 или 1)
D2 - PPU address increment - увеличение адреса указателя при чтении
0 - на единицу
1 - на 32
D1-D0 - адрес name table в VRAM
00 - $2000 (NT0)
01 - $2400 (NT1)
10 - $2800 (NT2)
11 - $2c00 (NT3)
$2001 - PPU Control Register 2 (только запись)
флаги:
D7-D5 - цвет фона (если D0=1)
000 - none
001 - green
010 - blue
100 - red
D7-D5 - интенсивность цвета в цветном режиме (D0=0)
000 - none
001 - монохромный зелёный
010 - монохромный синий
100 - мнохромный красный
D4 - показывать ли спрайты
D3 - показывать ли бэкграунд
D2 - sprite clipping (хз чё такое ))
D1 - background clipping (тоже хз ))
D0 - тип дисплея
0 - цветной
1 - монохромный
$2002 - PPU Status Register (только чтение)
флаги:
D7 - находмся в VBlank прерывании?
D6 - отрисован ли спрайт с индексом 0 (первый)
D5 - скока спрайтов отображается, меньше и равно (0) или больше(1) 8.
D4 - VRAM write flag
0 - в VRAM можно записывать данные
1 - VRAM игнорирует запись
Злоебучий регистр, после чтения его он сбрасывает D7, $2005 и $2006
регистры.
$2003 - PPU регистр адреса в SPR-RAM (8 бит), задаём смещение указателя в
SPR-RAM (только запись)
$2004 - PPU регистр записи 8бит данных в SPR-RAM (только запись), после
записи указатель++.
$2005 - VRAM Address register1, записываются 2 байта. Хардварный скролл,
первый байт это на скока прокручивать по горизонтали, второй - по
вертикали.
$2006 - VRAM Address register 2, записываются 2 байта адреса в VRAM,
с которого идёт чтение и запись данных через рагистр $2007
$2007 - чтение/запись 8бит данных VRAM
Звуковые регистры:
$4000 - pAPU Pulse #1 Control Register (W)
$4001 - pAPU Pulse #1 Ramp Control Register (W)
$4002 - pAPU Pulse #1 Fine Tune (FT) Register (W)
$4003 - pAPU Pulse #1 Coarse Tune (CT) Register (W)
$4004 - pAPU Pulse #2 Control Register (W)
$4005 - pAPU Pulse #2 Ramp Control Register (W)
$4006 - pAPU Pulse #2 Fine Tune Register (W)
$4007 - pAPU Pulse #2 Coarse Tune Register (W)
$4008 - pAPU Triangle Control Register #1 (W)
$4009 - pAPU Triangle Control Register #2 (?)
$400A - pAPU Triangle Frequency Register #1 (W)
$400B - pAPU Triangle Frequency Register #2 (W)
$400C - pAPU Noise Control Register #1 (W) |
$400E - pAPU Noise Frequency Register #1 (W)
$400F - pAPU Noise Frequency Register #2 (W)
$4010 - pAPU Delta Modulation Control Register (W)
$4011 - pAPU Delta Modulation D/A Register (W)
$4012 - pAPU Delta Modulation Address Register (W)
$4013 - pAPU Delta Modulation Data Length Register (W)
(со звуком не заморачивался)
$4014 - SPR-RAM DMA, пишем сюда один байт $x. После чего в SPR-RAM
копируется 256 байт из адреса $100 * $x.
Дальше идут регистры по джойстикам и вяским лазерным пистолетам. Это уже
найдёте сами при желании.
__ Формат iNES файлов __
iNES - формат образов NES картриджей.
+--------+------+--------------------------------------------+
| Offset | Size | Content(s) |
+--------+------+--------------------------------------------+
| 0 | 3 | 'NES' |
| 3 | 1 | $1A |
| 4 | 1 | 16K PRG-ROM page count |
| 5 | 1 | 8K CHR-ROM page count |
| 6 | 1 | ROM Control Byte #1 (флаг тобишь) |
| | | %####vTsM |
| | | | ||||+- 0=гориз. зеркалирование |
| | | | |||| 1=верт. зеркалирование |
| | | | |||+-- 1=юзать дополнит. память SRAM |
| | | | ||+--- 1=512-byte trainer present |
| | | | |+---- 1=зеркалирование всех NT 0..3 |
| | | | | |
| | | +--+----- Mapper # (lower 4-bits) |
| 7 | 1 | ROM Control Byte #2 |
| | | %####0000 |
| | | | | |
| | | +--+----- Mapper # (upper 4-bits) |
| 8-15 | 8 | $00 |
| 16-.. | | Actual 16K PRG-ROM pages (in linear |
| ... | | order). If a trainer exists, it precedes |
| ... | | the first PRG-ROM page. |
| ..-EOF | | CHR-ROM pages (in ascending order). |
+--------+------+--------------------------------------------+
CHR-ROM - данные, которые при загрузке картриджа сразу заносятся в Pattern
table.
__ Кодинг. __
Здесь я юзал open-source перловый NES-ассемблер p65 (смотри в инклудах).
; пример NES 6502 проги
; iNES header
.ascii "NES"
.byte $1a
; число PRG-ROM секций (секции с кодом):
.byte $01
; число CHR-ROM секций:
.byte $01
; ROM control bytes: горизонтальное зеркалирование, no SRAM, no trainer,
; Mapper #0
.byte $00, $00
; остальные параметры забиваем нулями
.byte $00,$00,$00,$00,$00,$00,$00,$00
; PRG-ROM
; на второй странице бум хранить спрайты, создаём алиас
.alias sprite $200
; кое какие переменные на zero page:
.segment zp ;
.org $0000
.space dx 1
.space scroll 1
.text
.org $C000
; init stuff
reset: sei
cld
; Wait two VBLANKs.
* lda $2002
bpl -
* lda $2002
bpl -
; Reset the stack pointer.
ldx #$FF
txs
; Disable all graphics.
lda #$00
sta $2000
sta $2001
jsr init'sprites
jsr load'palette
jsr load'name'tables
jsr init'scrolling
; Set basic PPU registers. Load background from $0000,
; sprites from $1000, and the name table from $2000.
lda #%10001000
sta $2000
lda #%00011110
sta $2001
cli
; Transfer control to the VBLANK routines.
loop: jmp loop
init'sprites:
; Clear page #2, which we'll use to hold sprite data
lda #$00
ldx #$00
* sta sprite, x
inx
bne -
; initialize Sprite 0
lda #$20
sta sprite ; Y coordinate
lda #$01
sta sprite+1 ; Pattern number
sta sprite+3 ; X coordinate
; sprite+2, color, stays 0.
; Set initial value of dx
lda #$01
sta dx
rts
; Load palette into $3F00
load'palette:
lda #$3F
ldx #$00
sta $2006
stx $2006
* lda palette,x
sta $2007
inx
cpx #$20
bne -
rts
load'name'tables:
; Jam some text into the first name table (at $2400, thanks to mirroring)
ldy #$00
ldx #$04
lda #<bg
sta $10
lda #>bg
sta $11
lda #$24
sta $2006
lda #$00
sta $2006
* lda ($10),y
sta $2007
iny
bne -
inc $11
dex
bne -
; Clear out the Name Table at $2800 (where we already are. Yay.)
ldy #$00
ldx #$04
lda #$00
* sta $2007
iny
bne -
dex
bne -
rts
init'scrolling:
lda #240
sta scroll
rts
update'sprite:
lda #>sprite
sta $4014 ; Jam page $200-$2FF into SPR-RAM
lda sprite+3
beq hit'left
cmp #255-8
bne edge'done
; Hit right
ldx #$FF
stx dx
jmp edge'done
hit'left:
ldx #$01
stx dx
edge'done: ; update X and store it.
clc
adc dx
sta sprite+3
rts
reverse'dx:
lda #$FF
eor dx
clc
adc #$01
sta dx
rts
scroll'screen:
ldx #$00 ; Reset VRAM
stx $2006
stx $2006
ldx scroll ; Do we need to scroll at all?
beq no'scroll
dex
stx scroll
lda #$00
sta $2005 ; Write 0 for Horiz. Scroll value
stx $2005 ; Write the value of 'scroll' for Vert. Scroll value
no'scroll:
rts
vblank: jsr scroll'screen
jsr update'sprite
irq: rti
; palette data
palette:
.byte $0E,$00,$0E,$19,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$01,$21
.byte $0E,$20,$22,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
; Background data
bg:
.ascii " "
.ascii " 010 "
.ascii " 0110100 "
.ascii " 100100100 "
.ascii " "
.ascii " "
.ascii " DEFACED STAFF "
.ascii " PROUDLY PRESENTS "
.ascii " "
.ascii " DEFACED E'ZINE ISSUE 10 "
.ascii " "
.ascii " "
.ascii " V NOMERE "
.ascii " "
.ascii " 1) MAILMANZ BAG "
.ascii " 2) NES 6502 ASSEMBLER "
.ascii " 3) ANTIFA "
.ascii " 4) ART "
.ascii " 5) POTREBLYAD "
.ascii " 6) SOCIAL SILENCE "
.ascii " 7) SOMETHERE "
.ascii " 8) OUTRO "
.ascii " "
.ascii " "
.ascii " HAVE PHUN AND HAPPY "
.ascii " NEW YEAR 2007 "
.ascii " "
.ascii " "
.ascii " RUSSIAN UNDERGROUND COMMUNITY "
.ascii " "
; Attribute table
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
.byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
.advance $FFFA
.word vblank
.word reset
.word irq
; CHR-ROM
.org $0000 ; указываем смещение VRAM, куда загружать эти данные
; первые 20 символов - пустые
.advance $0200
; далее идут выдранные из Commodore картриджа (их тайлы похожи по формату))
; символы
.byte $00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 32
.byte $18,$18,$18,$18,$00,$00,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 33
.byte $66,$66,$66,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 34
.byte $66,$66,$FF,$66,$FF,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 35
.byte $18,$3E,$60,$3C,$06,$7C,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 36
.byte $62,$66,$0C,$18,$30,$66,$46,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 37
.byte $3C,$66,$3C,$38,$67,$66,$3F,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 38
.byte $06,$0C,$18,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 39
.byte $0C,$18,$30,$30,$30,$18,$0C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 40
.byte $30,$18,$0C,$0C,$0C,$18,$30,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 41
.byte $00,$66,$3C,$FF,$3C,$66,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 42
.byte $00,$18,$18,$7E,$18,$18,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 43
.byte $00,$00,$00,$00,$00,$18,$18,$30,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 44
.byte $00,$00,$00,$7E,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 45
.byte $00,$00,$00,$00,$00,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 46
.byte $00,$03,$06,$0C,$18,$30,$60,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 47
.byte $3C,$66,$6E,$76,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 48
.byte $18,$18,$38,$18,$18,$18,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 49
.byte $3C,$66,$06,$0C,$30,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 50
.byte $3C,$66,$06,$1C,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 51
.byte $06,$0E,$1E,$66,$7F,$06,$06,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 52
.byte $7E,$60,$7C,$06,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 53
.byte $3C,$66,$60,$7C,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 54
.byte $7E,$66,$0C,$18,$18,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 55
.byte $3C,$66,$66,$3C,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 56
.byte $3C,$66,$66,$3E,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 57
.byte $00,$00,$18,$00,$00,$18,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 58
.byte $00,$00,$18,$00,$00,$18,$18,$30,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 59
.byte $0E,$18,$30,$60,$30,$18,$0E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 60
.byte $00,$00,$7E,$00,$7E,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 61
.byte $70,$18,$0C,$06,$0C,$18,$70,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 62
.byte $3C,$66,$06,$0C,$18,$00,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 63
.byte $3C,$66,$6E,$6E,$60,$62,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 64
.byte $18,$3C,$66,$7E,$66,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 65
.byte $7C,$66,$66,$7C,$66,$66,$7C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 66
.byte $3C,$66,$60,$60,$60,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 67
.byte $78,$6C,$66,$66,$66,$6C,$78,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 68
.byte $7E,$60,$60,$78,$60,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 69
.byte $7E,$60,$60,$78,$60,$60,$60,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 70
.byte $3C,$66,$60,$6E,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 71
.byte $66,$66,$66,$7E,$66,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 72
.byte $3C,$18,$18,$18,$18,$18,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 73
.byte $1E,$0C,$0C,$0C,$0C,$6C,$38,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 74
.byte $66,$6C,$78,$70,$78,$6C,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 75
.byte $60,$60,$60,$60,$60,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 76
.byte $63,$77,$7F,$6B,$63,$63,$63,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 77
.byte $66,$76,$7E,$7E,$6E,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 78
.byte $3C,$66,$66,$66,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 79
.byte $7C,$66,$66,$7C,$60,$60,$60,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 80
.byte $3C,$66,$66,$66,$66,$3C,$0E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 81
.byte $7C,$66,$66,$7C,$78,$6C,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 82
.byte $3C,$66,$60,$3C,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 83
.byte $7E,$18,$18,$18,$18,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 84
.byte $66,$66,$66,$66,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 85
.byte $66,$66,$66,$66,$66,$3C,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 86
.byte $63,$63,$63,$6B,$7F,$77,$63,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 87
.byte $66,$66,$3C,$18,$3C,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 88
.byte $66,$66,$66,$3C,$18,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 89
.byte $7E,$06,$0C,$18,$30,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 90
.byte $3C,$30,$30,$30,$30,$30,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 91
.byte $0C,$12,$30,$7C,$30,$62,$FC,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 92
.byte $3C,$0C,$0C,$0C,$0C,$0C,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 93
.byte $00,$18,$3C,$7E,$18,$18,$18,$18,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 94
.byte $00,$10,$30,$7F,$7F,$30,$10,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 95
; остальное забиваем нулями
.advance $1000
; далее описывается вторая Pattern Page (1), все спрайты тут:
.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Character 0: Blank
.byte $18,$24,$66,$99,$99,$66,$24,$18,$00,$18,$18,$66,$66,$18,$18,$00 ; Character 1: Diamond sprite
; остальное заполняем нулями
.advance $2000
сборка:
$ p65.pl ./awe.p65 awe.nes
далее полученный awe.nes образ можно открыть в любом эмуляторе.
__ Eof. __
На этом всё, всем спасибо, все свободны )