更新说明

介绍

此仓库来源于 nesasm , 并对其功能进行完善

功能

描述

相对路径逻辑修改

使用include incbin等命令时相对路径基于使用命令的文件, 不再是基于程序当前工作目录

绝对寻址优化

增加绝对寻址可转变为零页寻址时使用零页寻址(不用显示指定零页寻址)

增加STR指令

STR指令增加长度限制(可选范围0-255)

标签长度优化

标签长度从32扩展到256

命令行增加 -fill

指定填充内存

命令行增加 -fns

指定生成fns函数地址列表文件(原来默认生成且不能控制)

命令行增加 -out

指定输出文件名

命令行增加 -bank 指定bank大小

预处理指令增加 .HEX

用于指定十六进制编写的字节数据

预处理指令增加 .RDW

用于指定大头序双字节数据

预处理指令增加 .BASE

用于指定编译结果放置位置

支持中文标签

兼容65s指令语法

兼容65s

() 间接寻址

<取低位

>取高位

文件头增加 电池备份指令

INESBAT

文件头增加 INES2.0 支持

INESTIM

INESSUBMAP

INESPRGRAM

INESPRGNVRAM

INESCHRRAM

INESCHRNVRAM

NESASM v3.2 使用手册

命令行用法

nesasm_3_2.exe [-选项] [-? (帮助)] 输入文件, 例如

nesasm_3_2.exe demo.asm
nesasm_3_2.exe demo.asm -out demo.nes

汇编程序只接受一个输入文件 “输入文件”,该文件将是组装成ROM文件(.NES扩展)可由模拟器直接使用。

还可以生成列表文件 (.LST 扩展名)如果在输入文件中遇到 LIST 指令。

以下是不同选项的说明:

选项

说明

-s

显示区段使用情况。

如果指定了其中一个选项,汇编程序将显示有关 ROM 库使用情况的信息。

使用 “-s” 显示基本信息,“-s” 显示更详细的信息

-l #

控制列表文件的输出:

0 - 完全禁用列表文件,即使LIST 指令用于输入文件

1 - 最低水平;DB、DW 和 DEFCHR 生成的代码不会被倾倒

2 - 正常水平;只有 DEFCHR 生成的代码不会被倾倒

3 - 最大水平;所有代码都转储在列表文件中

默认级别为 2 级.

-m

在列表文件中强制展开宏,即使在输入文件中看不到 MLIST 指令.

-raw

不会生成 ROM 标头

-bank

指定编译时bank大小

0: 8KB

1: 16KB

2: 32KB

默认为0

-fill

内存填充 (00-FF)

-out

指定输出文件名

-fns

输出函数地址列表文件

包含路径

默认情况下,汇编程序在以下情况下查看当前目录加载包含文件,但当它找不到该文件时, 然后使用环境变量“NES_INCLUDE”获取列表包含路径。理想情况下,您需要将此变量设置为您的 'AUTOEXEC.BAT“文件,并让它指向”NES”MagicKit 的目录。

例如:set NES_INCLUDE=c:\magickit\nes

符号

支持两种类型的符号:全局符号和局部符号。局部符号前面有一个点“.”,并且仅在两个全局符号之间有效。符号后面可以跟冒号“:”,但这不是必需的。

表达式

汇编程序支持非常复杂的表达式。您可以根据需要使用任意数量的括号级别,并且可以在运算符和数字之间使用空格。

数字可以用三个基数写成:十六进制($7F)、二进制(x0101)和十进制(48)。还支持(‘A’)字符值。

所有常用的运算符都存在:

+, -, *, /, %, ^, &, |, ~, <<, >>

以及比较运算符:

=, !=, !, <, >, <=, >=

对于优先级,适用与 C 相同的规则。

您还可以在表达式中使用预定义或用户定义的函数。

预定义函数

描述

HIGH()

返回值的高字节。

LOW()

返回值的低字节。

BANK()

返回符号的bank索引。如果没有给出符号,或者给出了多个符号,函数将返回错误。

PAGE()

返回标签的页面索引。请参见上方的错误。

SIZEOF()

返回数据元素的大小。

用户定义的函数

用户定义的函数使用FUNC指令声明,例如:

SCR_ADDR .func (\1) + ((\2) << 5)

最多可以使用 9 个参数,即 1 到 9。

要调用函数,只需将参数括在括号内并用逗号分隔即可:

stw #SCR_ADDR(10,4)+$2000,<$20

用户定义的函数可能非常有用,人们经常需要在表达式中一次又一次地使用相同的计算。定义函数将为您节省大量工作,并减少拼写错误。😃

请注意,函数调用可以嵌套,您可以毫无问题地从一个函数调用另一个函数,但是,递归调用将产生错误。

虽然函数对于仅通过函数调用替换常用表达式非常有用,但宏用于用单行代码替换常用指令组。

使用以下命令启动宏定义:

label  .macro

或者您也可以将标签放在“.macro”关键字之后,如下所示:

.macro label

之后跟随宏的主体,该主体由“.endm”指令终止。

例如,让我们定义一个“neg”宏来否定累加器。

neg .macro
    eor   #$FF
    inc   A
    .endm

宏也可以有参数。在宏体中,您可以通过使用反斜杠字符(‘’)后跟一个数字来引用参数。可以使用9个参数,从1到9。

这是另一个例子:

add    .macro    ; 添加一个值到寄存器A
    clc          ; (处理进位标志)
    adc
    .endm

可以使用其他“特殊”参数,以下是您可以在宏中使用的所有可能参数的列表:

参数

描述

\1 - \9

输入参数 - 最多可以在宏调用中使用9个

#

输入参数的数量

?1 - ?9

输入参数的“类型”:

ARG_NONE (= 0) = 无参数

ARG_REG (= 1) = 寄存器 -> A, X, Y

ARG_IMMEDIATE (= 2) = 立即数类型 -> #xx

ARG_ABSOLUTE (= 3) = 绝对寻址 -> 标签, $xxxx

ARG_INDIRECT (= 4) = 间接寻址 -> [标签]

ARG_STRING (= 5) = 字符串参数 -> “…”

ARG_LABEL (= 6) = 标签参数 -> 标签

@

特殊参数,每个宏返回不同的数字;可用于在宏内定义局部符号。

abs .macro

lda \1

bpl .x@

eor #$FF

inc A

sta \1

.x@:

.endm

指令

指令

描述

LIST

启用列表文件生成。您可以稍后使用NOLIST指令暂时停止输出,然后再用LIST重新启动。

NOLIST

停止列出输出。

MLIST

允许在清单文件中进行宏展开。

OPT

请停止在清单文件中扩展宏。

如果您使用“-m”命令行选项,此指令将不会产生任何效果。

EQU

给一个符号赋值。字符 ‘=’ 也有相同的功能。

BANK

选择一个8KB / 16KB / 32KB 的ROM bank,并将位置计数器重置为该bank中最新已知的位置。

当bank大小为 8KB时, 有效值范围为0-7663

当bank大小为 16KB时, 有效值范围为0-3831

当bank大小为 32KB时, 有效值范围为0-1915

ORG

设置程序计数器的位置。

地址的最低13位通知汇编器ROM bank中的偏移量,而最高的3位表示页面索引。

BASE

将程序输出的位置设置在银行尺寸范围内。

DB

BYTE

在当前位置存储一个或多个数据字节。

DW

WORD

存储数据字(小端)。

例如,DW $6502 编译为字节 02 65。

RDW

RWORD

存储数据字(大端)。

例如,RDW $6502 编译为字节 65 02。

STR

存储字符串,第一个字节是字符串的长度。

例如 STR “FlameCyclone” 编译为 0C 46 6C 61 6D 65 43 79 63 6C 6F 6E 65

HEX

存储以十六进制格式编写的二进制文件。

DS

在当前位置保留空间。如果在CODE或DATA组中使用此指令,该空间将填充为零。

RSSET

将RS指令的内部计数器设置为指定值。

RS

给一个符号分配一个值;有点像EQU,但这里分配的值来自内部计数器,分配后,该计数器会增加RS指令中指定的数量。这是一种非常方便的定义结构成员偏移量的方法,以下是一个小例子:

; C:

; –

; struct {

; short p_x;

; short p_y;

; byte p_color;

; } pixel;

;

; ASM:

; ----


.rsset $0 ; 设置RS计数器的初始值

P_X .rs 2

P_Y .rs 2

P_COLOR .rs 1

你可以随后在“像素”结构中使用这些符号作为偏移量。

ldy #P_COLOR

lda [pixel_ptr],Y

MACRO

开始一个宏定义。

ENDM

结束宏定义。

PROC

ENDP

PROCGROUP

ENDPROCGROUP

INCBIN

将二进制文件包含在当前位置。如果文件大于ROM存储器,将使用尽可能多的连续存储器。

INCLUDE

请在当前位置包含一个源文件。最多可以有7个级别。

INCCHR

从PCX文件中提取一部分并将其转换为NES 4色8x8图形字符。有三种语法可用:

INCCHR picpcx

不带任何附加参数,该命令将转换整个PCX文件。


INCCHR picpcx,32,4

告诉汇编器仅转换32个字符的4行(一个字符大小为8x8)。


INCCHR picpcx,48,16,32,4

与上述相同,但从坐标48,16(以像素为单位)开始提取字符。

DEFCHR

定义一个字符块(8x8像素)。

该指令接受8个参数(每个参数都存储为8个nybble的32位值),每个参数对应一个像素数据行。

此指令还会重新组织像素数据以符合NES所需的位格式。

请注意,只能使用颜色索引0到3,因为NES瓷砖只有4种颜色。

如果尝试使用更多颜色,将会生成错误。

zero: .defchr $00111110,\

$01000011,\

$01000101,\

$01001001,\

$01010001,\

$01100001,\

$00111110,\

$00000000

ZP

选择零页部分($0000-$00FF)。

BSS

选择RAM部分($0200-$07FF)。

CODE

选择程序代码部分。

DATA

选择程序数据部分

在ZP和BSS部分,您只能分配存储空间,不能存储初始值。

IF

条件汇编指令。

此指令将评估所提供的表达式,然后根据结果打开或关闭条件汇编。

如果结果为空,则关闭条件汇编;如果结果非空,则打开条件汇编。

IFDEF

IFNDEF

这些指令允许条件组装,具体取决于是否定义了标签

ELSE

将条件程序集打开、关闭或反之亦然

ENDIF

终止条件程序集的当前级别。

如果 IF 和 ENDIF 的数量不匹配,则报告错误.

FAIL

当汇编程序遇到此指令时,它将中止编译。

可在宏中用于参数错误检测.

INESPRG

指定 16k prg bank 的数量 (1-3832, 即16-61312 KB).

INESCHR

指定 8k chr bank 的数量 (0-3832, 即0-30656 KB).

INESMAP

指定使用的 NES 映射器 (0-4095).

INESMIR

指定 bank 的 VRAM 镜像。(0: 水平 1: 垂直 2: 四屏)

INESBAT

指定 存在电池备份 (0: 不存在 1: 存在)。

INESTIM

指定 CPU/PPU 时序 (0: NTSC, 1: PAL, 2: 多区域, 3: Dendy)。

INESSUBMAP

指定子映射器号 (0-15)

INESPRGRAM

指定 PRG RAM 大小 (0: 不存在, 1-15: 存在且大小 = 64字节 左移 1-15 次, 即128字节 - 2048KB)

INESPRGNVRAM

指定 PRG NVRAM 大小 (0: 不存在, 1-15: 存在且大小 = 64字节 左移 1-15 次, 即128字节 - 2048KB)

INESCHRRAM

指定 CHR RAM 大小 (0: 不存在, 1-15: 存在且大小 = 64字节 左移 1-15 次, 即128字节 - 2048KB)

INESCHRNVRAM

指定 CHR NVRAM 大小 (0: 不存在, 1-15: 存在且大小 = 64字节 左移 1-15 次, 即128字节 - 2048KB)

例子

;======================================================================
;文件头
NES_16KB_PRG_SIZE           =   1   ;16KB PRG大小数量
NES_8KB_CHR_SIZE            =   0   ;8KB CHR大小数量
BANK_DATA_MASK              =   NES_16KB_PRG_SIZE * 2 - 1     ;bank号掩码
;======================================================================
PRG_DATA_BANK_C000          =   NES_16KB_PRG_SIZE * 2 - 2
PRG_DATA_BANK_E000          =   NES_16KB_PRG_SIZE * 2 - 1
MAPPER_MIRRORING            =   1       ;命名表镜像 0水平 1垂直
;======================================================================
RESET_BANK                  =   NES_16KB_PRG_SIZE * 2 - 1
RESET_ADDR                  =   $E000   ;主程序起始地址
;======================================================================
;======================================================================
    .INESPRG NES_16KB_PRG_SIZE      ;16KB PRG 数量, $01-$EF8(1-3832),即16-61,312 KB
    .INESCHR NES_8KB_CHR_SIZE       ;8KB CHR 数量,$01-$EF8(1-3832),即0-30,656 KB
    .INESMAP 4                      ;Mapper号 (0-4095)
    .INESSUBMAP 0                   ;子Mapper号 (0-15)
    .INESMIR 0                      ;命名表镜像 (0: 水平 1: 垂直 2: 四屏)
    .INESBAT 0                      ;指定是否存在电池备份 (0: 不存在 1: 存在)
    .INESPRGRAM 0                   ;指定 PRG RAM 大小 (大小 = 64字节 << 计数)
    .INESPRGNVRAM 0                 ;指定 PRG NVRAM 大小(大小 = 64字节 << 计数)
    .INESCHRRAM 0                   ;指定 CHR RAM 大小(大小 = 64字节 << 计数)
    .INESCHRNVRAM 0                 ;指定 CHR NVRAM 大小(大小 = 64字节 << 计数)
    .INESTIM 0                      ;指定时序 (0: NTSC, 1: PAL, 2: 多区域, 3: Dendy)
 
    .RSSET $40;内部计数器设置为指定值
FC_Data_L   .RS 1;此符号分配后内部计数器 + 1
FC_Data_H   .RS 1;此符号分配后内部计数器 + 1
FC_Palette  .RS $20;此符号分配后内部计数器 + $20

;======================================================================
    .BANK 0;设置程序所在Bank (限制: 8KB模式 0-7663 16KB模式 3831 32KB模式: 1915)
    .ORG $8200;设置程序编译地址位置
    .BASE $0000;设置程序在中Bank中输出的位置
    
    .INCLUDE "sub.asm" ;引用其他源文件
    .INCBIN "sub.bin",$10,$40 ;引用其他文件, 从$10开始, 共$20字节
    .STR "FlameCyclone" ;定义一个字符串, 编译结果: 0C 46 6C 61 6D 65 43 79 63 6C 6F 6E 65
    .HEX 4E 45 53 1A ;定义HEX数据, 编译结果: 4E 45 53 1A
    .DW $6502 ;定义双字节数据(小头序), 编译结果: 02 65
    .RDW $6502 ;定义双字节数据(大头序), 编译结果: 65 02
    .DB $65,$02 ;定义双字节数据编译结果: 65 02
    .DS $32;保留32字节数据(用零填充)
    .RDW $6502 ;定义双字节数据(大头序), 编译结果: 65 02
    
;======================================================================
;测试程序
Test_Proc
    LDA #$65
    STA FC_Data_L
    LDA #$02
    STA FC_Data_H
    RTS
    
;======================================================================
    .BANK PRG_DATA_BANK_E000;设置程序所在Bank
    .ORG RESET_ADDR
;不可屏蔽中断处理
Nmi_Program
    PHA
    TXA
    PHA
    TYA
    PHA
    
    
    PLA
    TAY
    PLA
    TAX
    PLA

    RTI

;======================================================================
;重启处理
Reset_Program
    SEI
    CLD
    STA $00
    LDA #$00
    STA $2000
    STA $2001
    
    
    LDA #$80
    STA $2000
    LDA #$1E
    STA $2001
    
.Loop
    JMP .Loop

;======================================================================
;请求中断处理
Irq_Program
    RTI

;======================================================================
;中断向量表
    .ORG $FFFA
    .DW Nmi_Program
    .DW Reset_Program
    .DW Irq_Program