--= 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. __

     На этом всё, всем спасибо, все свободны )