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
//! RSMON — rsemu's own 256-byte Apple 1 monitor, in 6502 machine code.
//!
//! # Why this exists
//!
//! The Apple 1's own monitor is Steve Wozniak's, and its copyright status is
//! not clear: it has been passed around freely for decades, which is not a
//! licence. rsemu therefore treats it exactly like `nestest` and blargg's
//! ROMs — **fetch-only, never vendored** (`docs/testing/conformance-suites.md`)
//! — and ships this instead, so that `rsemu run apple1` demonstrates itself
//! with nothing to argue about. Point `--rom` at a Woz Monitor image and you
//! get the real thing; give no `--rom` and you get this.
//!
//! Written for rsemu, © Karpelès Lab Inc., MIT like the rest of the crate. It
//! is not a port, a translation or an abridgement of anything: the parts that
//! *do* match the Apple 1's own monitor are the ones the hardware dictates —
//! the register addresses, and the shape of a poll loop on a status bit that
//! only has one shape.
//!
//! # What it does
//!
//! A hexadecimal examine/deposit monitor, one keystroke at a time, echoing as
//! it goes:
//!
//! ```text
//! RSMON
//! >FF00 four hex digits and Return
//! FF00: D8 A2 FF 9A A9 7F 8D 12
//! > Return again walks on eight bytes
//! FF08: D0 A9 A7 8D 11 D0 8D 13
//! >0300:AA an address, a colon, a byte, Return
//! >0300 and it is there
//! 0300: 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, so the monitor can examine
//! and deposit anywhere above `$04`.
//!
//! # The listing
//!
//! Assembled at `$FF00`, and exactly 250 bytes long, so the 6502's vectors sit
//! immediately after it at `$FFFA` with nothing left over. 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
//! KBD = $D010 PIA port A: the keyboard, bit 7 always set
//! KBDCR = $D011 PIA control A: bit 7 set when a key is waiting
//! DSP = $D012 PIA port B: the display, bit 7 set while busy
//! DSPCR = $D013 PIA control B
//!
//! FF00 D8 RESET: CLD
//! FF01 A2 FF LDX #$FF
//! FF03 9A TXS
//! FF04 A9 7F LDA #$7F ; DDRB: PB0-PB6 out, PB7 in
//! FF06 8D 12 D0 STA DSP
//! FF09 A9 A7 LDA #$A7 ; CRA/CRB: select the data
//! FF0B 8D 11 D0 STA KBDCR ; registers, CA1/CB1 on a
//! FF0E 8D 13 D0 STA DSPCR ; rising edge
//! FF11 A2 00 LDX #$00
//! FF13 8A TXA
//! FF14 85 00 STA $00 ; address low
//! FF16 85 01 STA $01 ; address high
//! FF18 85 03 STA $03 ; mode: 0 examine, 1 deposit
//! FF1A BD F4 FF BANNER: LDA MSG,X
//! FF1D F0 06 BEQ NEWCMD
//! FF1F 20 C1 FF JSR PUTC
//! FF22 E8 INX
//! FF23 D0 F5 BNE BANNER
//! FF25 20 CC FF NEWCMD: JSR CRLF
//! FF28 A9 3E PROMPT: LDA #'>'
//! FF2A 20 C1 FF JSR PUTC
//! FF2D 20 D1 FF MAIN: JSR GETC ; blocks, and echoes
//! FF30 29 7F AND #$7F
//! FF32 C9 0D CMP #$0D
//! FF34 F0 35 BEQ ENTER
//! FF36 C9 3A CMP #':'
//! FF38 F0 27 BEQ SETDEP
//! FF3A 20 DC FF JSR HEXVAL ; C=1 and A=nibble if hex
//! FF3D 90 EE BCC MAIN
//! FF3F A6 03 LDX $03
//! FF41 D0 10 BNE DIGDAT
//! FF43 A2 04 LDX #$04 ; shift the nibble into the
//! FF45 06 00 SHLA: ASL $00 ; 16-bit address
//! FF47 26 01 ROL $01
//! FF49 CA DEX
//! FF4A D0 F9 BNE SHLA
//! FF4C 05 00 ORA $00
//! FF4E 85 00 STA $00
//! FF50 4C 2D FF JMP MAIN
//! FF53 A2 04 DIGDAT: LDX #$04 ; or into the byte
//! FF55 06 02 SHLD: ASL $02
//! FF57 CA DEX
//! FF58 D0 FB BNE SHLD
//! FF5A 05 02 ORA $02
//! FF5C 85 02 STA $02
//! FF5E 4C 2D FF JMP MAIN
//! FF61 A9 00 SETDEP: LDA #$00
//! FF63 85 02 STA $02
//! FF65 A9 01 LDA #$01
//! FF67 85 03 STA $03
//! FF69 D0 C2 BNE MAIN ; always: A is 1
//! FF6B A5 03 ENTER: LDA $03
//! FF6D F0 11 BEQ DUMP
//! FF6F A5 02 LDA $02 ; deposit and advance
//! FF71 A0 00 LDY #$00
//! FF73 91 00 STA ($00),Y
//! FF75 84 03 STY $03
//! FF77 E6 00 INC $00
//! FF79 D0 02 BNE ENT1
//! FF7B E6 01 INC $01
//! FF7D 4C 28 FF ENT1: JMP PROMPT ; the echoed Return ended the line
//! FF80 A5 01 DUMP: LDA $01 ; "AAAA:" then eight bytes
//! FF82 20 AE FF JSR PRBYTE
//! FF85 A5 00 LDA $00
//! FF87 20 AE FF JSR PRBYTE
//! FF8A A9 3A LDA #':'
//! FF8C 20 C1 FF JSR PUTC
//! FF8F A0 00 LDY #$00
//! FF91 A9 20 DUMPL: LDA #' '
//! FF93 20 C1 FF JSR PUTC
//! FF96 B1 00 LDA ($00),Y
//! FF98 20 AE FF JSR PRBYTE
//! FF9B C8 INY
//! FF9C C0 08 CPY #$08
//! FF9E D0 F1 BNE DUMPL
//! FFA0 A5 00 LDA $00 ; address += 8
//! FFA2 18 CLC
//! FFA3 69 08 ADC #$08
//! FFA5 85 00 STA $00
//! FFA7 90 02 BCC DMP1
//! FFA9 E6 01 INC $01
//! FFAB 4C 25 FF DMP1: JMP NEWCMD
//! FFAE 48 PRBYTE: PHA
//! FFAF 4A LSR
//! FFB0 4A LSR
//! FFB1 4A LSR
//! FFB2 4A LSR
//! FFB3 20 B7 FF JSR PRHEX
//! FFB6 68 PLA
//! FFB7 29 0F PRHEX: AND #$0F
//! FFB9 09 30 ORA #$30
//! FFBB C9 3A CMP #$3A
//! FFBD 90 02 BCC PUTC
//! FFBF 69 06 ADC #$06 ; C=1 here, so +7: '9'+1 -> 'A'
//! FFC1 48 PUTC: PHA
//! FFC2 2C 12 D0 PUTC1: BIT DSP ; the display's bit 7 is DA
//! FFC5 30 FB BMI PUTC1
//! FFC7 68 PLA
//! FFC8 8D 12 D0 STA DSP
//! FFCB 60 RTS
//! FFCC A9 0D CRLF: LDA #$0D
//! FFCE 4C C1 FF JMP PUTC
//! FFD1 AD 11 D0 GETC: LDA KBDCR
//! FFD4 10 FB BPL GETC
//! FFD6 AD 10 D0 LDA KBD
//! FFD9 4C C1 FF JMP PUTC ; tail call: echoes and returns A
//! FFDC C9 30 HEXVAL: CMP #'0'
//! FFDE 90 12 BCC HVBAD
//! FFE0 C9 3A CMP #'9'+1
//! FFE2 90 0A BCC HVDIG
//! FFE4 C9 41 CMP #'A'
//! FFE6 90 0A BCC HVBAD
//! FFE8 C9 47 CMP #'F'+1
//! FFEA B0 06 BCS HVBAD
//! FFEC E9 06 SBC #$06 ; C=0 here, so -7
//! FFEE 29 0F HVDIG: AND #$0F
//! FFF0 38 SEC
//! FFF1 60 RTS
//! FFF2 18 HVBAD: CLC
//! FFF3 60 RTS
//! FFF4 52 53 4D MSG: .byte "RSMON", 0
//! 4F 4E 00
//! FFFA 00 FF .word RESET ; NMI
//! FFFC 00 FF .word RESET ; RESET
//! FFFE 00 FF .word RESET ; IRQ
//! ```
/// Where [`RSMON`] is assembled, and where the Apple 1 decodes its monitor ROM.
pub const RSMON_BASE: u64 = 0xff00;
/// RSMON, ready to bind to the `rom` media slot of `machines/apple1.machine`.
///
/// 256 bytes: 250 of code and message at `$FF00`, then the 6502's NMI, RESET
/// and IRQ vectors, all three pointing at `$FF00`. See the module docs for the
/// listing these bytes came from.
pub static RSMON: & = &;