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
//! RSMON/serial — rsemu's own monitor for a 6502 with an ACIA on it.
//!
//! # Why this exists
//!
//! The monitor everyone puts on this board is Ben Eater's adaptation of
//! Wozmon, published as a gist with **no licence, no copyright line and no
//! attribution**, and itself derived from Steve Wozniak's Apple 1 monitor,
//! whose status is also unclear. Two layers of unclear provenance is two more
//! than an MIT crate can absorb (`CLAUDE.md`, "Provenance"), so rsemu neither
//! vendors it nor copies from it. Point `--rom` at an image if you have one;
//! give no `--rom` and you get this.
//!
//! This is a rewrite of [`apple1::monitor`](crate::dev::apple1::monitor)'s
//! RSMON — ours, © Karpelès Lab Inc., MIT like the rest of the crate — moved
//! from `$FF00` to `$8000` and re-plumbed onto a
//! [`w65c51n`](super::acia). The serial version is *shorter* than the PIA one:
//! there is no `DA`/`RDA` handshake and no display busy bit, just two status
//! bits to poll.
//!
//! # What it does
//!
//! A hexadecimal examine/deposit monitor, one keystroke at a time, echoing as
//! it goes:
//!
//! ```text
//! RSMON
//! >8000 four hex digits and Return
//! 8000: D8 A2 FF 9A A9 1F 8D 03
//! > Return again walks on eight bytes
//! 8008: 50 A9 0B 8D 02 50 A2 00
//! >0200:AA an address, a colon, a byte, Return
//! >0200 and it is there
//! 0200: AA 00 00 00 00 00 00 00
//! >
//! ```
//!
//! Hexadecimal digits shift into a 16-bit address from the right, so four of
//! them replace it completely and there is no "start over" key to learn. `:`
//! switches to entering a byte; Return stores it and advances, so `:` `AA`
//! Return `:` `BB` Return fills consecutive bytes. Anything else is echoed and
//! ignored.
//!
//! Zero page `$00`/`$01` hold the address, `$02` the byte being entered and
//! `$03` the mode. Nothing else in RAM is touched but the stack, so the monitor
//! can examine and deposit anywhere above `$04`.
//!
//! Line endings are the one place this differs from the Apple 1 version by
//! choice rather than by wiring. The Apple 1's terminal treated a bare CR as a
//! new line; a serial terminal does not, so `CRLF` sends both characters and
//! the echoed CR that ends a typed line is completed with an LF before anything
//! is printed under it.
//!
//! # The listing
//!
//! Assembled at `$8000` and 261 bytes long. The socket is 32 KiB
//! ([`rom`](super::rom)), so everything between the end of this and the vectors
//! at `$FFFA` is unprogrammed `$FF`.
//!
//! It is reproduced here in full because [`RSMON`] is otherwise unreadable, and
//! because a test below walks the same bytes with the crate's own 6502
//! disassembler and checks that the instruction boundaries and the register
//! accesses are the ones written here.
//!
//! ```text
//! ACIA_DATA = $5000 read: the byte that arrived; write: send one
//! ACIA_STATUS = $5001 bit 4 TDRE, bit 3 RDRF
//! ACIA_CMD = $5002
//! ACIA_CTRL = $5003
//! ADDRL = $00 the 16-bit address, low byte
//! ADDRH = $01 and high
//! BYTE = $02 the byte being entered
//! MODE = $03 0 examine, 1 deposit
//!
//! 8000 D8 RESET: CLD
//! 8001 A2 FF LDX #$FF
//! 8003 9A TXS
//! 8004 A9 1F LDA #$1F ; 8-N-1, 19200, baud generator
//! 8006 8D 03 50 STA ACIA_CTRL
//! 8009 A9 0B LDA #$0B ; DTR low, RTS low, no interrupts
//! 800B 8D 02 50 STA ACIA_CMD
//! 800E A2 00 LDX #$00
//! 8010 8A TXA
//! 8011 85 00 STA ADDRL
//! 8013 85 01 STA ADDRH
//! 8015 85 03 STA MODE
//! 8017 BD FF 80 BANNER: LDA MSG,X
//! 801A F0 06 BEQ NEWCMD
//! 801C 20 C3 80 JSR PUTC
//! 801F E8 INX
//! 8020 D0 F5 BNE BANNER
//! 8022 20 D0 80 NEWCMD: JSR CRLF
//! 8025 A9 3E PROMPT: LDA #'>'
//! 8027 20 C3 80 JSR PUTC
//! 802A 20 DA 80 MAIN: JSR GETC ; blocks, and echoes
//! 802D 29 7F AND #$7F
//! 802F C9 0D CMP #$0D
//! 8031 F0 35 BEQ ENTER
//! 8033 C9 3A CMP #':'
//! 8035 F0 27 BEQ SETDEP
//! 8037 20 E7 80 JSR HEXVAL ; C=1 and A=nibble if hex
//! 803A 90 EE BCC MAIN
//! 803C A6 03 LDX MODE
//! 803E D0 10 BNE DIGDAT
//! 8040 A2 04 LDX #$04 ; shift the nibble into the
//! 8042 06 00 SHLA: ASL ADDRL ; 16-bit address
//! 8044 26 01 ROL ADDRH
//! 8046 CA DEX
//! 8047 D0 F9 BNE SHLA
//! 8049 05 00 ORA ADDRL
//! 804B 85 00 STA ADDRL
//! 804D 4C 2A 80 JMP MAIN
//! 8050 A2 04 DIGDAT: LDX #$04 ; or into the byte
//! 8052 06 02 SHLD: ASL BYTE
//! 8054 CA DEX
//! 8055 D0 FB BNE SHLD
//! 8057 05 02 ORA BYTE
//! 8059 85 02 STA BYTE
//! 805B 4C 2A 80 JMP MAIN
//! 805E A9 00 SETDEP: LDA #$00
//! 8060 85 02 STA BYTE
//! 8062 A9 01 LDA #$01
//! 8064 85 03 STA MODE
//! 8066 D0 C2 BNE MAIN ; always: A is 1
//! 8068 A9 0A ENTER: LDA #$0A ; the echoed CR wants its LF
//! 806A 20 C3 80 JSR PUTC
//! 806D A5 03 LDA MODE
//! 806F F0 11 BEQ DUMP
//! 8071 A5 02 LDA BYTE ; deposit and advance
//! 8073 A0 00 LDY #$00
//! 8075 91 00 STA (ADDRL),Y
//! 8077 84 03 STY MODE
//! 8079 E6 00 INC ADDRL
//! 807B D0 02 BNE ENT1
//! 807D E6 01 INC ADDRH
//! 807F 4C 25 80 ENT1: JMP PROMPT
//! 8082 A5 01 DUMP: LDA ADDRH ; "AAAA:" then eight bytes
//! 8084 20 B0 80 JSR PRBYTE
//! 8087 A5 00 LDA ADDRL
//! 8089 20 B0 80 JSR PRBYTE
//! 808C A9 3A LDA #':'
//! 808E 20 C3 80 JSR PUTC
//! 8091 A0 00 LDY #$00
//! 8093 A9 20 DUMPL: LDA #' '
//! 8095 20 C3 80 JSR PUTC
//! 8098 B1 00 LDA (ADDRL),Y
//! 809A 20 B0 80 JSR PRBYTE
//! 809D C8 INY
//! 809E C0 08 CPY #$08
//! 80A0 D0 F1 BNE DUMPL
//! 80A2 A5 00 LDA ADDRL ; address += 8
//! 80A4 18 CLC
//! 80A5 69 08 ADC #$08
//! 80A7 85 00 STA ADDRL
//! 80A9 90 02 BCC DMP1
//! 80AB E6 01 INC ADDRH
//! 80AD 4C 22 80 DMP1: JMP NEWCMD
//! 80B0 48 PRBYTE: PHA
//! 80B1 4A LSR
//! 80B2 4A LSR
//! 80B3 4A LSR
//! 80B4 4A LSR
//! 80B5 20 B9 80 JSR PRHEX
//! 80B8 68 PLA
//! 80B9 29 0F PRHEX: AND #$0F
//! 80BB 09 30 ORA #$30
//! 80BD C9 3A CMP #$3A
//! 80BF 90 02 BCC PUTC
//! 80C1 69 06 ADC #$06 ; C=1 here, so +7: '9'+1 -> 'A'
//! 80C3 48 PUTC: PHA
//! 80C4 AD 01 50 PUTC1: LDA ACIA_STATUS
//! 80C7 29 10 AND #$10 ; TDRE: room for another byte
//! 80C9 F0 F9 BEQ PUTC1
//! 80CB 68 PLA
//! 80CC 8D 00 50 STA ACIA_DATA
//! 80CF 60 RTS
//! 80D0 A9 0D CRLF: LDA #$0D
//! 80D2 20 C3 80 JSR PUTC
//! 80D5 A9 0A LDA #$0A
//! 80D7 4C C3 80 JMP PUTC
//! 80DA AD 01 50 GETC: LDA ACIA_STATUS
//! 80DD 29 08 AND #$08 ; RDRF: a byte is waiting
//! 80DF F0 F9 BEQ GETC
//! 80E1 AD 00 50 LDA ACIA_DATA
//! 80E4 4C C3 80 JMP PUTC ; tail call: echoes and returns A
//! 80E7 C9 30 HEXVAL: CMP #'0'
//! 80E9 90 12 BCC HVBAD
//! 80EB C9 3A CMP #'9'+1
//! 80ED 90 0A BCC HVDIG
//! 80EF C9 41 CMP #'A'
//! 80F1 90 0A BCC HVBAD
//! 80F3 C9 47 CMP #'F'+1
//! 80F5 B0 06 BCS HVBAD
//! 80F7 E9 06 SBC #$06 ; C=0 here, so -7
//! 80F9 29 0F HVDIG: AND #$0F
//! 80FB 38 SEC
//! 80FC 60 RTS
//! 80FD 18 HVBAD: CLC
//! 80FE 60 RTS
//! 80FF 52 53 4D MSG: .byte "RSMON", 0
//! 4F 4E 00
//!
//! FFFA 00 80 .word RESET ; NMI
//! FFFC 00 80 .word RESET ; RESET
//! FFFE 00 80 .word RESET ; IRQ
//! ```
//!
//! # Why nothing here touches the VIA
//!
//! The monitor needs a console and nothing else, and the 65C22 comes up with
//! both ports as inputs and both timers idle, which is harmless. A program
//! that wants the VIA configures it; a monitor that configured it for you would
//! be guessing at what is wired to the port headers.
/// Where [`RSMON`] is assembled, and the address A15 alone decodes the EEPROM
/// at.
pub const RSMON_BASE: u64 = 0x8000;
/// RSMON/serial: 261 bytes of 6502, assembled at `$8000`.
///
/// This is the code alone. [`RSMON_IMAGE`] is what a machine binds: this,
/// padded to the socket, with the vectors at the top.
pub static RSMON: & = &;
/// A 32 KiB EEPROM image: [`RSMON`], unprogrammed cells, and the vectors.
///
/// Built at compile time rather than written out, because 32768 hexadecimal
/// literals of which 32507 are `$FF` would be a worse thing to review than the
/// four lines that produce them.
pub static RSMON_IMAGE: & = &;