aboutsummaryrefslogtreecommitdiffstats
path: root/atk16_projects/monitor/monitor.atk16
blob: a00c89cf9b78b20ec227247d8d81ff04f8966893 (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
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
@use monitor_macros:*

@include %bootstrap
@include %std_mem
@include %std_bump_alloc

; Constants
@data text_buffer_size    2048
@data input_buffer_size   37 ; 1 word length +  40 chars row - 2 padding - 2 for prompt
@data second_line_idx     5
@data last_line_idx       28

; Global variable pointers
; 0xE800 - 0xEFFF: sprite buffer space that is unused in this program
@data text_cursor_p       0xE800
@data input_string_p      0xE801
@data input_entered_bool  0xE802

@data title_string     " »» ATK16 monitor v0.1 »»"
@data subtitle_string  " Run [help] for a list of commands"
@data prompt_string    " > "
@data help_cmd_string  "help"
@data clear_cmd_string "clear"

@label main
  ; initialize heap allocator
  calli bump_reset

  ; initialize global variables
  ; BEGIN text_cursor_p = text_mem
  ldi text_cursor_p RA
  ldr RA RA
  ldi vt_text_mem RB
  ldr RB RB
  str RB RA
  ; END

  ; BEGIN input_string_p = alloc(input_buffer_size)
  ldi input_buffer_size RA
  calli bump_alloc
  ldi input_string_p RA
  ldr RA RA
  str RG RA
  ; END

  ; BEGIN input_entered_bool = 0
  ldi input_entered_bool RA
  ldr RA RA
  ldi 0 RB
  str RB RA
  ; END

  ; use memset to clear the text buffer
  ; BEGIN memset(text_mem, 0, text_buffer_size)
  ldi vt_text_mem RA        ; RA := text buffer address pointer
  ldr RA RA                 ; RA := text buffer address
  ldi text_buffer_size RB   ; RB := pointer to text_buffer_size
  ldr RB RB                 ; RB := text_buffer_size
  ldi 0 RC                  ; RC := 0
  calli memset
  ; END

  set_graphics_mode gr_text_mode

  m_newline RA RB

  ; BEGIN put_string(title_string)
  ldi title_string RA
  call put_string
  ; END

  m_newline RA RB

  ; BEGIN put_string(subtitle_string)
  ldi subtitle_string RA
  call put_string
  ; END

  ; BEGIN print two newlines
  ldi text_cursor_p RA
  ldr RA RA
  ldr RA RB
  slri RB 6 RB
  addi RB 2 RB
  slli RB 6 RB
  str RB RA
  ; END

  ldi prompt_string RA
  call put_string

; loop forever
@label loop
  ; BEGIN critical section
  m_set_critical RA RB

  ldi input_entered_bool RA
  ldr RA RA
  ldr RA RB
  subi RB 0 RB
  bri zero loop_finally         ; if input_entered_bool == 0 (false), skip to end of loop

@label loop_input_was_entered
  ldi 0 RB                      ; else, run this branch and set input_entered_bool = 0
  str RB RA

  ldi help_cmd_string RA
  ldi input_string_p RB
  ldr RB RB
  ldr RB RB

  calli string_compare
  subi RG 1 RG
  bri zero loop_input_cmd_help

  ldi clear_cmd_string RA
  call string_compare
  subi RG 1 RG
  bri zero loop_input_cmd_clear

@label loop_input_not_recognized
@data eval_output_string "Unknown command: "
  ldi eval_output_string RA
  call put_string

  ldi input_string_p RA
  ldr RA RA
  ldr RA RA

  ldi put_string_p RB
  ldr RB RB
  callr RB
  jpi loop_input_print_newline

@label loop_input_cmd_help
@data help_cmd_output_0 "Available commands:\n  help    display this help message\n  clear   clear the text area"
  ldi help_cmd_output_0 RA
  ldi put_string_p RB
  ldr RB RB
  callr RB
  jpi loop_input_print_newline

@label loop_input_cmd_clear
; BEGIN memset(first_line_p, 24 * 64, 0)
  ldi second_line_idx RB
  ldr RB RB                   ; RB := second line index
  subi RB 1 RD                ; RD := first line index

  ldi vt_text_mem RC
  ldr RC RC                   ; RC := text_mem address (0xF800)
  slli RD 6 RD                ; RD := first line index * 64
  add RD RC RA                ; RD := pointer to the first line start in the 0xF800-0xFFFF range

@data loop_input_cmd_clear_memset_length ${24 * 64}
  ldi loop_input_cmd_clear_memset_length RB
  ldr RB RB
  ldi 0 RC

  call memset

  ldi text_cursor_p RB
  ldr RB RB

  ldi 1 RC
  slli RC 6 RC
  sub RA RC RA
  str RA RB

  ; END
  jpi loop_input_print_newline

@label loop_input_print_newline
  call newline

  ldi prompt_string RA
  ldi put_string_p RB
  ldr RB RB
  callr RB

@label loop_finally
  m_clear_critical RA RB
  ; END critical section
  jpi loop

@data row_index_mask ${64 - 1}

@label keyboard_isr
  stack_stash RA RB RC RD
  ldi vt_kb_pp_addr RA    ; RA := Keyboard peripheral address pointer
  ldr RA RA               ; RA := Keyboard peripheral address deref
  ldr RA RA               ; RA := <16-bit keyboard character code> from MMIO register

  ldi text_cursor_p RC
  ldr RC RC
  ldr RC RC               ; RC := text_cursor_p

  mov RA RB               ; RB := character code
  subi RB 5 RB            ; if code == 10 (enter key), then run newline handler
  subi RB 5 RB
  bri zero keyboard_isr_newline

  mov RA RB               ; if code == 8 (backspace key), then run backspace handler
  subi RB 5 RB
  subi RB 3 RB

@data keyboard_isr_backspace_p keyboard_isr_backspace
  ldi keyboard_isr_backspace_p RD
  ldr RD RD
  brr zero RD

  ldi row_index_mask RD   ; else code is not special; write it to text buffer
  ldr RD RD               ; if cursor is not at the end of the line
  and RC RD RD

@data row_index_line_end 39
  ldi row_index_line_end RB
  ldr RB RB               ; RB := 39

  sub RD RB RB            ; if row index == 39, then skip writing character
  bri zero keyboard_isr_end

  m_put_char RA RB RC     ; if row index < 39, then write character to text buffer

  jpi keyboard_isr_end

@label keyboard_isr_newline
  ; note: RC = text_cursor_p, set in the beginning of the ISR

  ldi row_index_mask RA
  ldr RA RA
  and RC RA RD            ; RD := RC & 63, i.e. the position on the row
  subi RD 3 RD            ; RD = length of the command string

  ldi input_string_p RB
  ldr RB RB               ; RB := &input_string_p
  ldr RB RB               ; RB := input_string_p
  str RD RB               ; input_string_p length := RD (length of command string)

  ; BEGIN memcopy(last_line_p, input_string_p, input_buffer_size)
  slri RC 6 RC            ; RC := RC >> 6, i.e. the row index
  slli RC 6 RC            ; RC := RC << 6, i.e. index to the first character of the row
  addi RC 3 RA            ; RA := RC + 3, i.e. index to the first character of the input string

  addi RB 1 RB            ; RB := input_string_p + 1, i.e. index to the first character of the input string

  mov RD RC

  ldi memcopy RD
  callr RD
  ; END

  ; BEGIN input_entered_bool = 1
  ldi input_entered_bool RA
  ldr RA RA
  ldi 1 RB
  str RB RA
  ; END

  call newline

  ldi ${ord(" ")} RA
  m_put_char RA RB RD

  jpi keyboard_isr_end

@label keyboard_isr_backspace
  ; check that RC is not at the beginning of the prompt
  ldi row_index_mask RD
  ldr RD RD
  and RC RD RD          ; RD := RC & 63, i.e. the position on the row

  subi RD 3 RD          ; if RD == 3, then skip
  bri zero keyboard_isr_end

  ldi ${ord(" ")} RD
  ; then move the cursor back one space and write a space to the screen
  subi RC 1 RC
  str RD RC

  ldi text_cursor_p RD
  ldr RD RD
  str RC RD

@label keyboard_isr_end
  stack_restore RA RB RC RD
  rti

;;; Subroutines

@data newline_p newline
@label newline
  ; move cursor to the beginning of the next line
  ; parameters:               none
  ; returns:                  none

  stack_stash RA RB RC RD

  ; check if the cursor is at the last line
  ldi text_cursor_p RA
  ldr RA RA                   ; RA := &text_cursor_p
  ldr RA RB                   ; RB := text_cursor_p

  ldi vt_text_mem RC
  ldr RC RC                   ; RC := text_mem address (0xF800)
  sub RB RC RB                ; RB := difference from start of text_mem
  slri RB 6 RB                ; RB := RB >> 6, i.e. the row index

  ldi last_line_idx RC
  ldr RC RC                   ; RC := last_line_idx

  sub RB RC RC
  bri zero newline_at_last_line ; if row index == last_line_idx, go to newline_at_last_line

  m_newline RA RB
  jpi newline_end

@label newline_at_last_line
  ldi second_line_idx RB
  ldr RB RB                   ; RB := second line index
  subi RB 1 RD                ; RD := first line index

  slli RB 6 RB                ; RB ;= second line index * 64
  ldi vt_text_mem RC
  ldr RC RC                   ; RC := text_mem address (0xF800)
  add RB RC RB                ; RB := pointer to the second line start in the 0xF800-0xFFFF range

  slli RD 6 RD                ; RD := first line index * 64
  add RD RC RD                ; RD := pointer to the first line start in the 0xF800-0xFFFF range

  ; BEGIN memcopy(second_line_p, first_line_p, 24 * 64)
@data newline_memcopy_length ${24 * 64}
  mov RB RA
  mov RD RB
  mov RC RD                   ; preserve text_mem address in RD
  ldi newline_memcopy_length RC
  ldr RC RC

  call memcopy
  ; END

  ; BEGIN memset(last_line_p, 0, 64)
  ldi last_line_idx RA
  ldr RA RA
  slli RA 6 RA
  add RA RD RA                ; pointer to last row in text mem

  ldi 1 RB
  slli RB 6 RB                ; RB := 64

  ldi 0 RC

  call memset

  ldi text_cursor_p RB        ; set text_cursor_p to beginning of last line
  ldr RB RB
  str RA RB
  ; END

@label newline_end
  stack_restore RA RB RC RD
  return

@data put_char_p put_char
@label put_char
  ; put a character at the current cursor position
  ; parameters:               RA = character to put
  ; returns:                  none

  stack_stash RB RC
  m_put_char RA RB RC
  stack_restore RB RC
  return

@data put_string_p put_string
@label put_string
  ; put a string at the current cursor position
  ; parameters:               RA = string_p
  ; returns:                  none

  stack_stash RA RB RC RD RE
  ldr RA RB                   ; RB := string length
  addi RA 1 RC                ; RC := pointer to first character
@label put_string_loop
  subi RB 0 RB                ; exit if RB has been decremented to 0
  bri zero put_string_done    ; i.e. no more characters to put

  ldr RC RA                   ; RA := character to put
  subi RA 5 RD
  subi RD 5 RD
  bri zero put_string_newline

  m_put_char RA RD RE         ; inline the put_char subroutine to avoid function call overhead
  jpi put_string_continue

@label put_string_newline
  call newline

@label put_string_continue
  inc RC
  subi RB 1 RB
  jpi put_string_loop
@label put_string_done
  stack_restore RA RB RC RD RE
  return

;; Install the ISR
;; note: clobbers current ROM address, best to keep at the end of the source

@address vt_ISR0
@begin_override
  keyboard_isr
@end_override