aboutsummaryrefslogtreecommitdiff
path: root/crates/nasm/README.md
blob: 5e2fa69f49d4d3cbfc3417f6345ba845a5e1cdf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
## Usage

The most basic way to use this assembler is by running:

```
$ nasm awesome.s
```

This will produce an `out.nes` file placed under the same working directory. You
can change the name of the file with the `-o/--output` flag. Hence, you can call
it like so:

```
$ nasm -o awesome.nes awesome.s
```

Moreover, you can actually tell `nasm` to redirect the output to stdout instead
with the `--stdout` flag. This is useful when debugging the binary format with
another CLI tool. For example:

```
$ nasm --stdout awesome.s | hexdump -C
```

The syntax for this assembler is virtually the same as the one for
[ca65](https://cc65.github.io/doc/ca65.html), even if some functions might be
missing. One difference that you will find in contrast with `ca65` is that
`nasm` is a bit more pedantic. Let's consider the following example:

```asm
.scope Scope
  .macro MACRO
    lda #0
  .endmacro
.endscope
```

Here `ca65` will place `MACRO` on the global scope. `nasm` will do the same but
it will also print a warning telling the programmer about this, as this might be
unexpected at first. Hence, as a general rule `nasm` will be more noisy than
`ca65`, in the hope that the programmer is more aware about the end result.

## Mappers

By default `nasm` will assume the configuration for an `NROM` mapper, but this
can be changed with the `-c/--configuration` flag, with which you can pass the
path for the linker configuration file you'd like to use. This file can either
follow the same [ld65 syntax](https://www.cc65.org/doc/ld65-5.html), or a
simplified one (check out some samples for the "simplified" syntax
[here](../../lib/xixanta/src/mappings)).

All of that being said, and out of convenience, this flag also accepts this set
of **values**: `empty`, `nrom`, `nrom65`, `unrom`, `uxrom` and `mmc1`. These
values correspond to the configurations [already
bundled](../../lib/xixanta/src/mappings) on this application.

## Defining global values from the command line

You can define global values with the `-D` flag which follows a `NAME=VALUE`
syntax. Note that the value is expected to be in decimal format and it has to
fit in a byte. Hence, you could have a code like follows:

```asm
.ifdef PAL
  lda #1
.else
  lda #0
.endif
```

If you compile the code with `-D PAL=1`, then the first branch will be taken
instead of the second one.

## Address sanitizer

This assembler comes with a set of tools that builds up an "address
sanitizer". Some of its functionality is already included, while some other is
opt-in via a special command line option.

### Reserved memory

This assembler can detect the memory regions being used and make decisions out
of it. This comes with a few gotchas that the programmer has to be aware in
order for the assembler to be useful. Because of this, the tooling around this
detection is behind the `-a/--asan` flag.

The address sanitizer will blindly follow the naming conventions from
[style.nes](https://github.com/mssola/style.nes), and assume at first that each
variable takes 1 byte exactly. Hence, in order to reserve one byte, you can
simply:

```asm
zp_variable = $20
```

Then the address sanitizer will assume that you are reserving a byte at address
`$20` which will be used throughout the code. If you want to reserve more than a
byte, then:

```asm
zp_buffer = $20   ; asan:reserve $0F
```

Then the address sanitizer will assume that `zp_buffer = $20..$2F
(included)`. Any access to this reserved range will be considered a
conflict. For example:

```asm
zp_buffer = $20   ; asan:reserve $0F
zp_bad    = $22   ; NOTE: The address sanitizer will mark it as a *conflict*.
```

If you want to ignore this (e.g. you are using a variable that shadows other
ones), then you can explicitely tell the address sanitizer to ignore a given
assignment:

```asm
zp_buffer = $20   ; asan:reserve $0F
zp_good   = $22   ; asan:ignore
```

TODO: NOTE asan:ignore also takes care of lda $200 ; asan:ignore



In order for the address sanitizer to be successful, it will warn programmers
whenever an access to memory is being done without using variables. This allows
for the address sanitizer to be more thorough, even if there is never the
guarantee that memory accesses will be safe. For example:

```asm
zp_variable = $20

ldx #20
lda zp_variable, x
```

This will access memory far beyond to `zp_variable` which was only reserving a
single byte. This is beyond the scope of this tool and other tools should be
used instead (e.g. an emulator with breakpoints on accesses to unexpected memory
regions, or `vnf` from this project).

On another note, the address sanitizer is also able to do some basic bound
checks. For example:

```asm
zp_variable = $20     ; asan:reserve $02

lda zp_variable       ; good
lda zp_variable + 1   ; good: arithmetics within the reserved limits.
lda zp_variable + 2   ; bad: arithmetics that would point to out of bounds.
lda zp_variable - 1   ; bad: same as before but wrapping around.
```

With all of this, `nasm` will be able to tell how much memory you've been using
so far, and if you add the `--write-info` option, you will also know how this
memory is laid out by reading the `.nasm/memory.txt` file. Couple that with the
`--stats` option, and for a given project you will be able to know:

1. How much do you have left in memory and in your ROM segments.
2. You have no conflicting variables or variables that shadow others in
   unexpected ways.
3. Where exactly you are reserving RAM and ROM space.
4. Basic arithmetics don't make you fall out of bounds.

### Working RAM

In NES/Famicom programs you have to advertise on the header whether Working RAM
is being used or not (see [byte 6 on the iNES
format](https://www.nesdev.org/wiki/INES)). The programmer is expected to set
this flag on if PRG RAM is available. If there are memory accesses to PRG RAM
but the programmer did not advertise that on the header flag, then the `nasm`
will error out as it cannot assume that these accesses are valid. This check
will happen regardless of the `-a/--asan` flag.