-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathassembler.htm
551 lines (538 loc) · 34.8 KB
/
assembler.htm
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>assembler</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
<link rel="stylesheet" href="/G-Pascal/G-Pascal.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">G-Pascal Assembler</h1>
</header>
<p><strong>Author</strong>: Nick Gammon</p>
<p><strong>Date written</strong>: February 2022</p>
<div class="quick_link">
<a href="index.htm">Back to main G-Pascal page</a>
</div>
<div class="quick_link">
<a href="pascal_compiler.htm">G-Pascal info</a>
</div>
<div class="quick_link">
<a href="editor.htm">Text editor</a>
</div>
<div class="quick_link">
<a href="file_menu.htm">File menu</a>
</div>
<div class="quick_link">
<a href="i2c.htm">I2C support</a>
</div>
<div class="quick_link">
<a href="spi.htm">SPI support</a>
</div>
<h2 id="contents">Contents</h2>
<ul>
<li><a href="#Features">Features</a></li>
<li><a href="#how_to_assemble">How to assemble a program</a></li>
<li><a href="#syntax">Syntax and notes</a>
<ul>
<li><a href="#labels">Labels</a></li>
<li><a href="#whitespace">Whitespace</a></li>
<li><a href="#opcode">Opcode / directive</a></li>
<li><a href="#operand">Operand and expression evaluation</a></li>
<li><a href="#directives">Directives</a></li>
</ul></li>
<li><a href="#directives">Assembler directives</a></li>
<li><a href="#memory_layout">Memory layout</a></li>
<li><a href="#co_exist">How to co-exist with Pascal code?</a></li>
<li><a href="#eeprom_functions">Access to EEPROM functions and variables</a></li>
<li><a href="#via_adapter">Accessing the VIA (Versatile Interface Adapater) pins</a></li>
<li><a href="#i2c">I<sup>2</sup>C and SPI support</a></li>
<li><a href="#debugging">Debugging</a>
<ul>
<li><a href="#toggle_led">Toggling an LED</a></li>
<li><a href="#breakpoints">Inserting breakpoints</a></li>
<li><a href="#resume">Resume after breakpoint (RES)</a></li>
<li><a href="#debugging_print">Debugging prints</a></li>
<li><a href="#view_memory">View memory (MEM)</a></li>
<li><a href="#change_memory">Change memory (POKE)</a></li>
<li><a href="#jsr">JSR to address</a></li>
<li><a href="#jmp">JMP to address</a></li>
</ul></li>
<li><a href="#credits">Credits</a></li>
</ul>
<p><strong>Author</strong>: Nick Gammon (written in 2022)</p>
<h2 id="introduction">Introduction</h2>
<p>The G-Pascal assembler is a 65C02 assembler, entirely resident in the EEPROM, and available immediately after resetting your board. This is intended to let you try small to medium-scale assembler projects, without having to keep removing the EEPROM chip and programming it externally.</p>
<hr />
<h2 id="Features">Features</h2>
<ul>
<li><p>The documented WD65C02 instruction set, with all operand modes</p></li>
<li><p>Full expression evaluation of operands, with operator precedence, parentheses, bitwise operations and so on.</p></li>
<li><p>Relocation of the output to any memory address (ORG directive)</p></li>
<li><p>Here is “hello world” in assembler:</p>
<pre><code> jmp begin ; skip the message
hello asciiz "Hello, world!"
begin = *
lda #<hello
ldx #>hello
jsr print
rts</code></pre></li>
</ul>
<hr />
<h2 id="how_to_assemble">How to enter and assemble a program</h2>
<ol type="1">
<li><p>Use the Editor to enter your program (type “I” to insert text). Alternatively load it from your PC by using “LOAD”. The Editor is described in a separate page.</p></li>
<li><p>Type “A” to assemble it and produce machine code.</p></li>
<li><p>If it assembles without errors type “R” to run the code. It will automatically start running at the location of the first byte of emitted machine code. It is probably useful to put a JMP there to go to where you want the code to start executing. Put a RTS at the end of your code to return to the G-Pascal system. Alternatively, JMP RESTART to do a “warm start” if you aren’t sure a RTS will take you back to where you started.</p></li>
</ol>
<hr />
<h2 id="syntax">Syntax and notes</h2>
<p>The assembler is intended to be compatible — to a point — with the vasm6502 assembler, in particular when run on your PC like this:</p>
<pre><code>vasm6502_oldstyle MYFILE.asm -wdc02 -esc -Fbin -o MYFILE.bin -L MYFILE.list</code></pre>
<p>Note that there is a limit to how much can be implemented in a few kB of EEPROM. Thus source files written in this assembler should compile in vasm, however if you use extra features of vasm (like conditional compiles) then these are not supported. In other words, file written here should be compatible with vasm, not necessarily the other way around.</p>
<p>Vasm is available from <a href="http://sun.hasenbraten.de/vasm/" class="uri">http://sun.hasenbraten.de/vasm/</a></p>
<p>The assembler is line-based, that is each line is treated separately, and is terminated by the newline character (0x0A).</p>
<hr />
<h2 id="labels">Labels</h2>
<p>A line may optionally contain of a label, which must be located in column 1 (the first column).</p>
<ul>
<li>Labels must start with a letter, and then consist of letters, numbers and/or the underscore character.</li>
<li>Labels are not case-sensitive.</li>
<li>There are no local labels.</li>
<li>It is an error to re-use a label.</li>
<li>Labels may optionally be followed by a colon (“:”).</li>
<li>Except in the case of the EQU (or “=”) directive the value of the label is the value of the current output code address.</li>
<li>A line may consist of a label only.</li>
</ul>
<hr />
<h2 id="whitespace">Whitespace</h2>
<p>Whitespace (spaces) are ignored, except that a label must start in the first column.</p>
<h2 id="comments">Comments</h2>
<p>A comment starts with semicolon (“;”) and continues to the end of the line. Lines may consist entirely of comments, or be entirely blank.</p>
<hr />
<h2 id="opcode">Opcode / directive</h2>
<p>Opcodes (eg. LDA) or directives (eq. ORG) are separated from the label, if present, by one or more spaces. If there is no label then the opcode must be at least on column 2 of the line. Opcodes are not case-sensitive.</p>
<p>A test file is <a href="examples/asm/assembler_opcode_test.asm">here</a> which assembles every opcode/addressing-mode combination. The expected code to be generated is shown in the comments. This should assemble the same under this assembler and vasm.</p>
<hr />
<h2 id="operand">Operand</h2>
<p>Operands are separated from the opcode by one or more spaces.</p>
<ul>
<li><p>The operand "*" evaluates to the current output code address, eg.</p>
<pre><code>foo = * ; foo is assigned to the current address
bra *+4 ; branch to 4 bytes ahead of this instruction</code></pre></li>
<li><p>In the case of opcodes which may or may not use the A register as an operand, the “A” may be omitted. eg.</p>
<pre><code> LSR ; logical shift A right
INC ; increment A</code></pre></li>
<li><p>String operands only apply to the ASC, ASCII, ASCIIZ and STRING directives. In those cases a quoted string of any length (up to 255 characters) may be the operand. In a similar way to the Pascal compiler, strings may start with either single of double quotes, and contain the other type of quote inside. Alternatively you may double the starting quote to include it in the string. eg. ‘Nick’‘s cat’. In other places strings of one to three characters long may be used, and will be treated as numbers. Eg.</p>
<pre><code> lda #'A' ; load A with $41</code></pre>
<p>Strings may also contain “escape” sequences of a backslash followed by a letter, as follows:</p>
<pre><code> \A bell ($07)
\B backspace ($08)
\E escape (0x1B)
\F formfeed ($0C)
\N newline (0x0A)
\R carriage return (0x0D)
\T horizontal tab (0x09)
\V vertical tab (0x0B)
\' single quote
\" double quote
\\ backslash
\Xnn where nn is one or two hex digits (eg. \x0A \X42 \x9 )</code></pre>
<p>The letter following the backslash is not case-dependent. For the \Xnn form, if there is potential confusion if the character following the escape sequence happens to be a hex digit then you should use two digits. Only the first two digits are considered when parsing.</p></li>
<li><p>Other operands go through the “expression evaluator” after taking into account indirect addressing which is noticed by the presence of a leading “(” on the line.</p>
<p>The expression evaluator combines values using operators following the following precedence list, as obtained from the vasm documentation:</p>
<pre><code> 1. + - ! ~ (unary +/- sign, not, complement), also < (low-byte) > (high byte)
2. << >> (shift left, shift right)
3. & (bitwise and)
4. ^ (bitwise exclusive-or)
5. | (bitwise inclusive-or)
6. * / % (multiply, divide, modulo)
7. + - (plus, minus)
8. < > <= >= (less, greater, less or equal, greater or equal)
9. == != <> (equality, inequality)
10. && (logical and)
11. || (logical or)</code></pre>
<p>The high-byte and low-byte operators are unary operators with a high precedence. Depending on their location in the expression they are not considered to be comparison operators. For example :</p>
<pre><code> lda #>foo ; load A with the high-order byte of the address of foo
lda #>(foo + $100) ; load A with the high-order byte of the address of (foo + $100)
lda #<foo ; load A with the low-order byte of the address of foo</code></pre>
<p>There is a <a href="examples/asm/expression_evaluator_test.asm">operator evaluation</a> test file you can try.</p></li>
<li><p>Parentheses may be used to force the order of evaluation of operators.</p></li>
<li><p>Labels, if used in expressions, take on the value of the address of that label.</p></li>
<li><p>Expressions are evaluated internally to 24 bits of precision, and then truncated to 16 bits at the end of the expression evaluation. This means that operations which might overflow 16 bits may still evaluate correctly. eg. lda # ($8000 << 4) >> 12 would generate a9 80 (that is, lda #$80) because the high-order bit was not lost during the initial shift left (it was still in the range of a 24-bit value).</p></li>
<li><p>A quirk of the numeric literal parser means you cannot directly use the numeric literal -8388608 in a program. A workaround it to use $800000 instead as that is the hex equivalent of that value, or to use (-8388607 - 1). This is because the parser allows a number of up to 23 bits (0 to 8388607) and then sets the high-order bit to make it negative if you provided a minus sign.</p></li>
</ul>
<hr />
<h2 id="directives">Directives</h2>
<p>The following assembler directives are supported:</p>
<ul>
<li><p>LIST <em>number</em></p>
<p>This controls the listing of your code during assembly. It consists of three bits which may be OR’ed together as follows:</p>
<ul>
<li>1 : Show the source during assembly (with the generated address on the left)</li>
<li>2 : Show the generated opcodes during assembly</li>
<li>4 : Show the symbol table at the completion of the assembly for user-defined symbols</li>
<li>8 : Show the symbol table at the completion of the assembly for system-defined (library) symbols</li>
</ul>
<p>For example:</p>
<pre><code> LIST 1 ; show source
LIST 1|2 ; show source and generated code (alternatively: LIST 3)
LIST 1|2|4 ; show source, generated code, and user-defined symbols (alternatively: LIST 7)
LIST 1|2|4|8 ; show source, generated code, and ALL symbols (alternatively: LIST 15)
LIST 0 ; stop listing anything</code></pre>
<p>The assembler does two passes, the first to find the address of forward-declared symbols, and the second to output the machine code. The listing is shown during the second pass, unless there is an error, in which case the line in error is shown.</p></li>
<li><p>LIST</p>
<p>Same as “LIST 3”.</p></li>
<li><p>NOLIST</p>
<p>Same as “LIST 0”.</p></li>
<li><p>ASSERT <em>expression</em></p>
<p>Raises an error “Assertion failed” if <em>expression</em> is zero. This can be used to do compile-time checking. For example, you could check if a block of memory exceeded a certain size. You can also use it to check that the assembler is evaluating expressions correctly, for example:</p>
<pre><code> assert 2 + 2 == 4</code></pre></li>
<li><p>ASC <em>string</em></p>
<p>Insert the string into the code, with no terminator.</p>
<pre><code> asc "Hello, world!"</code></pre></li>
<li><p>ASCII <em>string</em></p>
<p>Same as ASC.</p></li>
<li><p>ASCIIZ <em>string</em></p>
<p>Insert the string into the code, with a 0x00 (null) terminator.</p></li>
<li><p>STRING <em>string</em></p>
<p>Same as ASCIIZ.</p></li>
<li><p><em>label</em> EQU <em>address</em></p>
<p>The specified label takes on the address of the evaluated expression.</p>
<pre><code>EEPROM EQU $8000
start_message ASC "Hi there"
end_message EQU * ; end_message becomes the current output address
message_length EQU end_message - start_message ; calculate message length</code></pre></li>
<li><p><em>label</em> = <em>address</em></p>
<p>Same as EQU.</p></li>
<li><p>ORG <em>expression</em></p>
<p>The output generation is relocated to whatever <em>expression</em> evaluates to, from this point on. If this line had a label the label evaluates to the address <em>before</em> the relocation.</p>
<pre><code> org $5000 ; output code at $5000 onwards now</code></pre></li>
<li><p>DFB <em>expression</em> [, <em>expression</em> ]</p>
<p>A byte is emitted to the output, which is the value of the expression. It must evaluate to 0x00 to 0xFF. Multiple bytes may be emitted separated by commas.</p>
<pre><code> dfb $01,$02,$03,$04</code></pre></li>
<li><p>DFW <em>expression</em> [, <em>expression</em> ]</p>
<p>Two bytes (one word) are emitted to the output, which is the value of the expression. It must evaluate to 0x0000 to 0xFFFF. The bytes are emitted in little-endian order, that is the low-order byte first, followed by the high-order byte. Multiple words may be emitted separated by commas.</p>
<pre><code> dfw $1234,$5678,$ABCD</code></pre></li>
<li><p>WORD <em>expression</em> [, <em>expression</em> ]</p>
<p>Same as DFW.</p></li>
<li><p>BLK <em>expression</em></p>
<p>The assembler emits <em>expression</em> zeroes. For example:</p>
<pre><code> BLK 10 ; emit 10 zeroes, advancing the output address by 10</code></pre></li>
<li><p>RESERVE <em>expression</em></p>
<p>The assembler advances the output address by <em>expression</em> bytes <em>without</em> emitting anything. This could be handy for reserving blocks of memory without overwriting them during the assembly process.</p></li>
<li><p>SYM <em>expression</em></p>
<p>Relocate the symbol table, used during assembly, to the address that <em>expression</em> evaluates to. This must be done before any symbols are created (that is, before any labels). The intention of this is to allow you to make a “safe place” for code which could be shared by the Pascal compiler and the assembler, as described below.</p>
<p>For example:</p>
<pre><code> SYM $4800 ; symbols are to be placed at $4800 (growing downwards)
ORG $4800 ; code is to be placed at $4800 (growing updwards)</code></pre>
<p>You will not get an error if you have already created symbols (labels), however once the symbol table has been relocated the library symbols will be reloaded into the new location, and your existing symbols discarded.</p></li>
</ul>
<hr />
<h2 id="memory_layout">Memory layout</h2>
<h3 id="where-is-the-generated-machine-code-placed">Where is the generated machine-code placed?</h3>
<p>In the absence of an ORG directive, the generated code is placed directly after the end of the source code. This is fine for testing out assembly-code ideas.</p>
<p>During assembly the symbol table is placed at the top of RAM and grows downwards, see graphic:</p>
<p><img src="images/Assembler%20memory%20organisation.svg.png" /></p>
<hr />
<h3 id="co_exist">How to co-exist with Pascal code?</h3>
<p>It is possible to generate machine code and call that from a Pascal program (using <strong>call</strong> ( <em>address</em> ) ). However to do this we need to move the assembler code somewhere where it won’t be overwritten by the Pascal source or the Pascal P-codes. One possible spot would be somewhere between the P-codes and the symbol table, however the exact safe location for such code might be hard to determine.</p>
<p>A safer approach is to move the symbol table (with a compiler and assembler directive) and free up a block of memory, like this:</p>
<p><img src="images/Assembler%20memory%20organisation%20with%20symbol%20table%20relocation.svg.png" /></p>
<p>To do this, in the assembler we need two extra lines, right at the start of the source file:</p>
<pre><code> SYM $5800 ; symbols are to be placed at $5800 (growing downwards)
ORG $5800 ; code is to be placed at $5800 (growing upwards)</code></pre>
<p>If you haven’t installed my RAM extending mod, change the address from $5800 to $3800, as RAM ends at $4000 in that case.</p>
<p>Now when you assemble your code the output goes to $5800 and is not affected by the symbol table growing, or the later loading of Pascal source.</p>
<p>Meanwhile, in your Pascal code you need to do a similar thing:</p>
<pre><code>{%S $5800 }</code></pre>
<p>That ensures that the Pascal compiler symbol table also starts lower in memory, thus not clobbering the generated machine code. That directive also relocates the run-time stack to the same address.</p>
<hr />
<h3 id="example-of-co-existence">Example of co-existence</h3>
<pre><code> SYM $5800 ; symbols are to be placed at $5800 (growing downwards)
ORG $5800 ; code is to be placed at $5800 (growing upwards)
; interface routines in the EEPROM
jmp begin ; skip the message
hello asciiz "Hello, world!\n"
begin = *
lda #<hello
ldx #>hello
jsr print
rts</code></pre>
<p>Load and assemble (LOAD then ASS) the above assembler code, which relocates itself to $5800 and with the symbol table moved to $5800 to avoid a clash with the symbols.</p>
<p>You can test that on its own now by typing RUN. The runtime system automatically starts running at the first <em>emitted</em> object code. In this case, that is the “jmp start” instruction.</p>
<pre><code> { Relocate symbol table and runtime stack: } {%s $5800}
{ Note: hello_world_relocated.asm should be loaded and compiled before running this }
begin
writeln ("About to call machine code ...");
call ($5800);
writeln ("Machine code done.")
end .</code></pre>
<p>Now load, compile and run the above Pascal code. It will display a message, then call the assembler code at $5800, and then return to display its final message.</p>
<p>If you don’t have the extended RAM mod then change $5800 in the above examples to $3800.</p>
<hr />
<h2 id="eeprom_functions">Access to EEPROM functions and variables</h2>
<p>Some useful “exposed” function addresses, plus some zero-page addresses, are pre-loaded into the symbol table (there are around 80 of them, taking around 1630 bytes of RAM in the process). This lets you call various subroutines, like PRINT for outputting to the terminal, DIGITALREAD, DIGITALWRITE, PINMODE and so on, without having to work out where they are in the EEPROM.</p>
<p>If you are desperately short of RAM, and want to avoid these being loaded into the symbol table by the assembler, you could recompile the source, omitting or commenting-out some or all of those in assembler.inc. Look for assembler_library_functions_table. Leave the table there, but omit some or all of the contents.</p>
<p>A list of them is with explanations <a href="doc/on_board_functions_and_variables.txt">here</a>.</p>
<p>Example of using the inbuilt functions to multiply two numbers and display the result:</p>
<pre><code>;
; put 47302 into value
;
lda #<47302
sta value
lda #>47302
sta value+1
stz value+2
;
; put 55 into value2
;
lda #<55
sta value2
lda #>55
sta value2+1
stz value2+2
;
; multiply them to get the result 2601610
; exp_multiply multiplies value by value2 and puts the result in value
;
jsr exp_multiply
;
; display the result (displays value)
;
jsr display_in_decimal
; done!
rts</code></pre>
<hr />
<h3 id="via_adapter">Accessing the VIA (Versatile Interface Adapater) pins</h3>
<p>There are three utility functions in the EEPROM code to control the 16 pins on the VIA chip, which operate similarly to the ones on the Arduino.</p>
<p>The functions are in the <a href="doc/on_board_functions_and_variables.txt">exposed functions list</a>.</p>
<p>The VIA ports are PA0 to PA7 (pins 2 to 9 on the chip) and PB0 to PB7 (pins 10 to 17 on the chip). For the purposes of these functions they are numbered 0 to 15, where 0 is PA0, 1 is PA1, 8 is PB0 and 15 is PB7, and so on for the ones in-between.</p>
<p>Pins PA0 and PA1 on the VIA are used for the serial interface, as well as CB2 to detect the start bit for incoming serial data.</p>
<p>Pins PA5, PA6, PA7, PB4, PB5, PB6, PB7 are used for the LCD interface.</p>
<p>That leaves 7 pins for your own use (PA2, PA3, PA4, PB0, PB1, PB2, PB3).</p>
<ul>
<li><p><strong>pinmode</strong> (port, mode). This sets the mode of the port, where 0 is input and 1 is output.</p>
<p>Port in A, mode in X, then <strong>jsr pinmode</strong></p></li>
<li><p><strong>digitalWrite</strong> (port, value). Writes to the specified port. The value can be 0 or 1.</p>
<p>Port in A, value in X, then <strong>jsr digitalwrite</strong></p></li>
<li><p><strong>digitalRead</strong> (port). Reads a value from the specified port. Returns 0 or 1.</p>
<p>Port in A, then <strong>jsr digitalread</strong>. Returns the value in A.</p></li>
</ul>
<p><img src="images/VIA%20pin%20mappings.png" /></p>
<p>Example:</p>
<pre><code>
;
; digitalWrite example
;
jmp begin ; skip the variable declarations
counter dfb 0 ; how many toggles we did
pin_state dfb 0 ; current pin state
ITERATIONS = 20 ; how many times to loop (this will be 10 flashes)
begin:
stz pin_state
stz counter
;
; set PA2 to output
;
lda #2 ; Port PA2
ldx #1 ; write mode
jsr pinmode
write_loop:
;
; write to PA2
;
lda pin_state
eor #1 ; toggle state
sta pin_state
tax
lda #2 ; Port PA2
jsr digitalwrite
;
; delay 500 ms
;
ldx #<500
ldy #>500
jsr delay
;
; do it ITERATIONS times
;
inc counter
lda counter
cmp #ITERATIONS
bcc write_loop
;
; all done
;
rts</code></pre>
<hr />
<h2 id="i2c">I<sup>2</sup>C and SPI support</h2>
<p>There are support functions for interfacing with I<sup>2</sup>C devices, such as real-time clocks, port expanders, and many other peripherals, with the board acting as an I<sup>2</sup>C master. See <a href="i2c.htm">I2C support</a> for more details.</p>
<p>There are also support functions for interfacing with SPI devices, such as 7-segment displays, 8x8 matrix displays, port expanders, and many other peripherals, with the board acting as an SPI master. See <a href="spi.htm">SPI support</a> for more details.</p>
<hr />
<h2 id="debugging">Debugging</h2>
<p>Debugging assembler code can be a pain — I know from experience. Below are a few suggestions.</p>
<h2 id="stack-pointer-changed-when-running-assembler-code">Stack pointer changed when running assembler code</h2>
<p>When running assembler code the stack pointer is changed to $CF rather than $FF. In other words, the top of the stack is address $1CF. This is so that, if you hit a breakpoint and use various commands like MEM, POKE and so on, the use of the stack in the Editor does not corrupt your user stack. This will reduce your available stack size by 48 bytes (out of 256). That still allows 208 nested subroutine calls, assuming you don’t also push stuff onto the stack, which should be plenty. If it isn’t, in your code you can change the stack pointer, however then the stack “backtrace” shown when you hit a BRK will not be correct.</p>
<hr />
<h3 id="toggle_led">Toggling an LED</h3>
<p>You can use a handful of instructions to toggle one of the VIA pins, like this:</p>
<pre><code>VIA_PORTA = $7FF1
DEBUG_MASK = %00010000
pha
lda #DEBUG_MASK ; toggle debug flag
tsb VIA_PORTA ; turn it on
trb VIA_PORTA ; turn it off
pla</code></pre>
<p>The VIA pins are set to output by default in the hardware initialisation routines (to avoid floating inputs to the chip).</p>
<p>This toggling is very fast, you would not see it with the naked eye. An oscilloscope or logic analyser could show if that port was toggled. Alternatively leave it on (omit the “trb” instruction) and then you could see if a particular code path had been traversed.</p>
<hr />
<h3 id="breakpoints">Inserting breakpoints</h3>
<p>Another technique is to put BRK instructions into your code. The processor actually advances two bytes after a breakpoint, so the byte after BRK is reserved for a breakpoint number. Thus, if you are uncertain if your code has reached a particular spot you can put a BRK there, followed by a breakpoint number, e.g.</p>
<pre><code> BRK
DFB 1 ; breakpoint #1</code></pre>
<p>Now if that gets executed you will see something like this:</p>
<pre><code>BRK executed at address $9969, A = $00, X = $01, Y = $01, P = $32, S = $f4, id = $01
Stack: 8c 9b 42 9b 5f 97</code></pre>
<p>The breakpoint processing displays the address of the BRK, followed by the A, X, Y registers, followed by the processor flags (P) and the stack pointer (S). Also the byte after the break is shown as the break “ID”.</p>
<p>Then it displays the stack from S+6 onwards. This is because the processor takes 3 bytes of stack to handle the BRK, plus the breakpoint handler pushes A and X onto the stack before saving the stack register. Finally, the stack register points at the first unused spot, not the last used spot. Thus the bytes shown should be what was on the stack when the break occurred. Assuming for a moment that you haven’t (in your code) pushed registers onto the stack, then you should see a stack “traceback” being the last few JSR addresses.</p>
<p>Be aware that the JSR pushes the program counter + 2 onto the stack (not + 3). Thus you need to add one to each address to see what the address after the JSR was (or subtract 2 to find the address of the JSR itself). So in the example above, the stack traceback was (from earliest to latest):</p>
<ul>
<li>$9760 — for the JSR at $975D</li>
<li>$9B43 — for the JSR at $9B40</li>
<li>$9B8D — for the JSR at $9B8A</li>
</ul>
<p>Remember that the high-order byte is pushed first by the JSR so you have to read the bytes from right to left. Thus “5F 97” becomes $975F, and then adding one gives you $9760.</p>
<p>From the above we conclude that, in this case, a JSR at $975D called something (you can find what by looking at your listing), and then a JSR at $9B40 called something, and finally a JSR at $9B8A called something. Then the BRK was reached.</p>
<p>You could sprinkle multiple BRK instructions through your code to confirm or deny that certain parts of code are executed each with their own unique breakpoint “ID” so you can see which one was reached (without having to match the breakpoint address to the generated code address).</p>
<p>If you do not put an “ID” byte after the BRK, then the code shown will simply be the opcode of the next instruction in the source. If you are planning to do a RESUME then you must put an “ID” byte after each BRK. You could insert breakpoints in places where you want to check the contents of various registers, or other memory locations, and then type RESUME afterwards to move onto the next breakpoint.</p>
<p>You can resume execution after hitting a breakpoint, see below.</p>
<hr />
<h3 id="resume">Resume after breakpoint (RES)</h3>
<p>After hitting a breakpoint, you can type RESUME to continue at the next instruction. In this case you <strong>must</strong> put a breakpoint identifier after the breakpoint, or the code will be resumed one byte from the correct place. This means you can put various breakpoints in your code and check that registers (and other memory addresses) are what you expect before resuming execution.</p>
<p>The processor registers are loaded with the values saved when the BRK was executed. These values are saved in the following memory locations:</p>
<ul>
<li>$10 - A register</li>
<li>$11 - X register</li>
<li>$12 - Y register</li>
<li>$13 - status register</li>
<li>$14 - stack register</li>
<li>$15/$16 - address of breakpoint (placed into the program counter)</li>
</ul>
<p><img src="images/Breakpoint%20register%20saves.png" /></p>
<hr />
<h3 id="debugging_print">Debugging prints</h3>
<p>Another technique is to put “debugging prints” inside your code. This could be used instead of inserting BRK instructions if you just want a breadcrumb trail of what is being executed and in what order. For example:</p>
<pre><code>;
; Debug point: A is point code
;
debug_message asciiz "Debug point "
debug_point:
phx
phy
pha
lda #<debug_message
ldx #>debug_message
jsr print
pla
jsr COUT ; output the point code
jsr CROUT ; newline
ply
plx
rts</code></pre>
<p>Now in your code you could do something like this:</p>
<pre><code> pha ; save A
lda #'A' ; debug point A
jsr debug_point ; display message
pla ; retrieve A</code></pre>
<hr />
<h3 id="view_memory">View memory (MEM)</h3>
<p>You can use the MEMORY command to view any range of memory. For example, to look at the zero-page registers:</p>
<pre><code>: mem $0 $1f
$0000: d3 c7 00 da c9 00 00 00 00 ff ff ff ff 00 00 00 . . . . . . . . . . . . . . . .
$0010: 00 00 00 00 00 00 00 37 c6 00 00 00 00 00 00 00 . . . . . . . 7 . . . . . . . .</code></pre>
<p>Press Ctrl+C to abort a long listing. Values in the range 0x20 to 0x7F are shown in ASCII on the right of the hex numbers. Other values are shown as a period.</p>
<hr />
<h3 id="change_memory">Change memory (POKE)</h3>
<p>For debugging purposes you can use “POKE” to alter any memory address. There is no checking if you are altering a sane memory location.</p>
<p>For example:</p>
<pre><code>POKE $1000 $12 $34 'A'</code></pre>
<p>That would put $12 into address $1000, $34 into address $1001, and $41 into address $1002.</p>
<p>You will get a message, and poking will cease, if the value read back from that location is not the value you are attempting to place there. This would happen if you were writing to read-only memory.</p>
<p>You can poke one or more locations (sequentially). The maximum number depends on what will fit into the editor’s command-line buffer which is 256 bytes long including the trailing newline and 0x00 byte.</p>
<p>A possible use of POKE would be to insert extra breakpoints “on the fly” while you are debugging. For example, once you hit one breakpoint (which you put in the code), you may be curious to know what the registers have in them a few instructions later. Referring to your listing for guidance about where to insert your breakpoints you could:</p>
<ul>
<li>Find a suitable address / place of interest in your code</li>
<li>Note the machine-code instructions at that address by (using MEM), plus the next one (write them down)</li>
<li>Replace those two bytes (using POKE) with a $00 (BRK) followed by a breakpoint number. eg. POKE $1234 $00 $42</li>
<li>Resume your code so the new breakpoint is eventually reached (using RES)</li>
<li>After noting the contents of registers, stack, and memory, replace those two bytes with the ones you previously noted (using POKE)</li>
<li>Back up the address of the breakpoint by two bytes, so the replaced instruction is executed. To do that, find the contents of locations $15/$16, subtract two from them, and poke them back into $15/$16. Remember $15 is the low-order byte and $16 is the high-order byte.</li>
<li>Possibly insert another breakpoint further on in the code for the next point-of-interest stopping point</li>
<li>Resume your code so it continues from the instruction you just replaced (RESUME)</li>
</ul>
<p>You could also use POKE to remove instructions by replacing them with a NOP instruction ($EA).</p>
<p><img src="images/Breakpoint%20register%20saves.png" /></p>
<hr />
<h3 id="jsr">JSR to address</h3>
<p>You can call any subroutine by using “JSR” in the shell. eg.</p>
<pre><code>JSR $1234</code></pre>
<p>Prior to executing the JSR, the shell loads the processor registers from zero-page memory as follows:</p>
<ul>
<li>$10 - A register</li>
<li>$11 - X register</li>
<li>$12 - Y register</li>
<li>$13 - status register</li>
</ul>
<p>You could use POKE to seed those registers.</p>
<p>After the subroutine returns, those four locations will have the new values of those registers placed in them.</p>
<hr />
<h3 id="jmp">JMP to address</h3>
<p>You can jump to any address by using “JMP” in the shell. eg.</p>
<pre><code>JMP $1234</code></pre>
<p>Prior to executing the JMP, the shell loads the processor registers from zero-page memory as follows:</p>
<ul>
<li>$10 - A register</li>
<li>$11 - X register</li>
<li>$12 - Y register</li>
<li>$13 - status register</li>
</ul>
<p>You could use POKE to seed those registers.</p>
<hr />
<h2 id="credits">Credits and references</h2>
<ul>
<li><strong>Vasm</strong> portable and retargetable assembler was used to assemble the G-Pascal EEPROM binary file. It is available from <a href="http://sun.hasenbraten.de/vasm/" class="uri">http://sun.hasenbraten.de/vasm/</a></li>
<li>Expression evaluation ideas from <a href="https://www.geeksforgeeks.org/expression-evaluation/">Expression Evaluation</a> on the GeeksForGeeks website. It is based on the <a href="https://en.wikipedia.org/wiki/Shunting-yard_algorithm">Shunting-yard algorithm by Edsger Dijkstra</a></li>
<li>CRC code from <a href="http://www.6502.org/source/integers/crc-more.html">More CRC Calculations by Greg Cook</a></li>
<li><a href="https://jacksum.net/en/index.html">Jacksum</a> was used to confirm CRC calculations</li>
<li><a href="https://en.wikipedia.org/wiki/Endianness">Endianness - Wikipedia</a></li>
<li>Multiplication and division algorithms from <a href="https://llx.com/Neil/a2/mult.html">Multiplying and Dividing on the 6502 by Neil Parker</a></li>
<li><a href="i2c.htm">I2C support</a></li>
<li><a href="spi.htm">SPI support</a></li>
</ul>
<hr />
<div class="quick_link">
<a href="index.htm">Back to main G-Pascal page</a>
</div>
<div class="quick_link">
<a href="i2c.htm">I<sup>2</sup>C support</a>
</div>
<div class="quick_link">
<a href="spi.htm">SPI support</a>
</div>
<hr />
<h2 id="license">License</h2>
<p>Information and images on this site are licensed under the <a href="https://creativecommons.org/licenses/by/3.0/au/">Creative Commons Attribution 3.0 Australia License</a> unless stated otherwise.</p>
</body>
</html>