summaryrefslogtreecommitdiff
path: root/programs/subasm.s
blob: 50f2e8d8badb28d71672c50d5d9d27e012df5f62 (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
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
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
; SuBAsm
; The Sux Bootstrapped Assembler.
;
; by mr b0nk 500 <b0nk@b0nk.xyz>

; Variables
.org $1000
prg_name:
	.byte "SuBAsm"
ver_txt:
	.byte ", version "
ver_num:
	.byte "0.1"

; Directives.
dir:
	.byte "org"
	.byte "byte"
	.byte "word"
	.byte "dword"
	.byte "qword"

; Instruction mnemonics.
mne:
	.byte "CPS"
	.byte "ADC"
	.byte "AAB"
	.byte "PHB"
	.byte "PHP"
	.byte "PHA"
	.byte "PHY"
	.byte "TAY"
	.byte "PHX"
	.byte "TAX"
	.byte "TYX"
	.byte "JMP"
	.byte "SBC"
	.byte "SAB"
	.byte "PLB"
	.byte "PLP"
	.byte "PLA"
	.byte "PLY"
	.byte "TYA"
	.byte "PLX"
	.byte "TXA"
	.byte "TXY"
	.byte "JSR"
	.byte "AND"
	.byte "ABA"
	.byte "STT"
	.byte "TAB"
	.byte "TSX"
	.byte "BPO"
	.byte "ORA"
	.byte "OAB"
	.byte "TBA"
	.byte "SEI"
	.byte "TXS"
	.byte "BNG"
	.byte "XOR"
	.byte "XAB"
	.byte "CLI"
	.byte "BCS"
	.byte "LSL"
	.byte "LLB"
	.byte "STB"
	.byte "SEC"
	.byte "STA"
	.byte "STY"
	.byte "STX"
	.byte "BCC"
	.byte "LSR"
	.byte "LRB"
	.byte "LDB"
	.byte "CLC"
	.byte "LDA"
	.byte "LDY"
	.byte "LDX"
	.byte "BEQ"
	.byte "ROL"
	.byte "RLB"
	.byte "SSP"
	.byte "BNE"
	.byte "ROR"
	.byte "RRB"
	.byte "CSP"
	.byte "BVS"
	.byte "MUL"
	.byte "MAB"
	.byte "SEV"
	.byte "BVC"
	.byte "DIV"
	.byte "DAB"
	.byte "CLV"
	.byte "RTS"
	.byte "CMP"
	.byte "CAB"
	.byte "CPY"
	.byte "CPX"
	.byte "CPB"
	.byte "ENT"
	.byte "RTI"
	.byte "INC"
	.byte "IAB"
	.byte "INY"
	.byte "INX"
	.byte "DEC"
	.byte "DBA"
	.byte "DEY"
	.byte "DEX"
	.byte "WAI"
	.byte "JSL"
	.byte "ASR"
	.byte "ARB"
	.byte "NOP"
	.byte "RTL"
	.byte "BRK"


scr_row:
	.byte $0
scr_col:
	.byte $0
a:
	.word $0
b:
	.word $0
c:
	.word $0
d:
	.word $0

str_buf:

; Input buffer.
.org $2000
buf:

.org $2400
ptr1:
	.qword $2500

ptr2:
	.qword $2900

; Control Register.
.org $C000
ctrl_reg:

; Screen.
.org $C001
scr:

; Keyboard.
.org $C002
kbd:

; Main program.
.org $0
reset:
	cps
	ldx.w #$FFFF
	txs
	ldy #0
	jsr clr_buf
	ldx.w #0	; Reset x.
	ldy #0		; Reset y.
	jmp print_title

read:
	lda ctrl_reg	; Is the keyboard ready?
	beq read	; Loop until the keyboard is ready.
	lda #0		; Start resetting the control register.
	sta ctrl_reg	; Reset the control register.
	jmp getchar	; We got a key.

rset_x:

	ldx #0		; Reset x.
	stx.w x
	rts

print_title:
	lda prg_name, x
	beq print_ver	; Did we find a null terminator?
	sta		; Print character.
	inx		; Increment offset.
	inc x
	jmp print_title	; Keep printing more characters.

print_ver
	jsr rset_x
	lda ver_txt, x
	beq print_num	; Did we find a null terminator?
	sta scr		; Print character.
	inx		; Increment offset.
	jmp print_ver	; Keep printing more characters.

print_num:
	lda ver_num, x
	beq getline	; Did we find a null terminator?
	sta scr		; Print character.
	inx		; Increment offset.
	jmp print_num	; Keep printing more characters.
getline:
	lda #$A
	sta scr		; Print newline
	inc scr_row
	lda #0
	sta scr_col
	inc y
	jsr rset_x
	jmp read

getchar:
	lda kbd		; Get typed character.
	cmp #$1B	; Did the user type an escape?
	beq esc		; Yes, so start getting the escape code.
	cmp #$A		; Did the user type a newline?
	beq nl		; Yes, so start parsing the input.
	cmp #8		; Did the user type a backspace?
	beq bs		; Yes, so start checking the buffer.
	jsr echo	; Print character to screen.

store_char:
	sta buf, y	; Store typed character into the input buffer.
	iny		; Increment buffer offset.
	jmp read	; Get another character.

esc:
	lda ctrl_reg	; Skip the '['.
	lda ctrl_reg	; Get the next character.
	beq read	; We have an error, so discard it, and go back to getting user input.
	lda kbd		; Get the escape code.
	sta c		; Store the escape code, until we need it.
	jsr isup	; Check if the user pressed up.
	lda d
	cmp #0
	bne esc_end
	jsr isdown	; Check if the user pressed down.
	lda d
	cmp #0
	bne esc_end
	lda #0
	jsr isleft	; Check if the user pressed left.
	lda d
	cmp #0
	bne esc_end
	jsr isright	; Check if the user pressed right.
esc_end:
	lda #$0
	sta d
	jmp rset_a	; Go back to getting user input.

isup:
	lda scr_row	; Is the cursor at the top of the screen?
	beq isup_done	; Yes, so return.
	lda c		; No, so load the escape code back into the accumulator.
	cmp #$41	; Did the user press the up arrow key?
	beq up		; Yes, so move the cursor up.
isup_done:
	rts		; End of isup.

isdown:
	lda scr_row	; Start checking the y coordinate of the cursor.
	cmp #$17	; Is the cursor at the bottom of the screen?
	beq isdown_done	; Yes, so return.
	lda c		; No, so load the escape code back into the accumulator.
	cmp #$42	; Did the user press the down arrow key?
	beq down	; Yes, so move the cursor down.
isdown_done:
	rts		; End of isdown.

isright:
	lda scr_col	; Start checking the x coordinate of the cursor.
	cmp #$4F	; Is the cursor at the far right of the screen?
	beq isright_end	; Yes, so return.
	lda c		; No, so load the escape code back into the accumulator.
	cmp #$43	; Did the user press the right arrow key?
	beq right	; Yes, so move the cursor right.
isright_end:
	rts		; End of isright.

isleft:
	lda scr_col	; Is the cursor at the far left of the screen?
	beq isleft_done	; Yes, so return.
	lda c		; No, so load the escape code back into the accumulator.
	cmp #$44	; Did the user press the left arrow key?
	beq left	; Yes, so move the cursor left.
isleft_done:
	rts		; End of isleft.

up:
	dec scr_row
	jsr update_pos
	lda #1
	sta d
	jmp isup_done
down:
	inc scr_row
	jsr update_pos
	lda #1
	sta d
	jmp isdown_done
right:
	inc scr_col
	jsr update_pos
	jmp isright_end
left:
	dec scr_col
	jsr update_pos
	lda #1
	sta d
	jmp isleft_done

update_pos:
	lda #$1B	; Print an escape character
	sta scr		; to the screen.
	lda #$5B	; Print '['
	sta scr		; to the screen, and start the escape sequence.
	jsr getrow	; Start printing the row number to the screen.
	jsr getcol	; Start printing the column number to the screen.
	lda #$48	; Print 'H'
	sta scr		; to the screen.
	rts		; End of update_pos.
getrow:
	lda scr_row	; Get the cursor's y coordinate.
	div #$A		; Divide A by 10.
	adc #$30	; Convert it to ascii, and
	sta scr		; print to the screen.
	tba		; Get the remainder.
	adc #$30	; Convert it to ascii, and
	sta scr		; print to the screen.
	rts		; End of getrow.
getcol:
	lda #$3B	; Print ';'
	sta scr		; to the screen.
	lda scr_col	; Get the cursor's x coordinate.
	div #$A		; Divide A by 10.
	adc #$30	; Convert it to ascii, and
	sta scr		; print to the screen.
	tba		; Get the remainder.
	adc #$30	; Convert it to ascii, and
	sta scr		; print to the screen.
	rts		; End of getrow.



nl:
	sta scr		; Print newline.
	inc scr_row	; Move the cursor down one line.
	lda #0		; Put a null terminator, in place of the newline.
	sta scr_col	; Move the cursor back to column 0.
	sta buf, y	; Place it into the input buffer.
	ldy.w #0	; Reset y, to parse the input.
	jsr parse	; Start parsing input.

back:
	sta scr		; Print backspace.
	lda #0		; Put a null terminator, in place of the backspace.
	sta buf, y	; Place it into the input buffer.
	dey		; Decrement buffer offset.
	dec scr_col
	jmp read	; Get next character.

bs:
	cpy #0		; Are we at the start of the buffer?
	beq read	; We are, so do not store the backspace into the buffer.
	jmp back	; We are not, so add the backspace to the buffer.

parse:
	jsr getdir
	jsr
	rts

getdir:
	iny		; Increment offset.
	lda buf, y
	jsr iswhite
	bcs getdir	; Reset y, if we hit the null terminator.
	cmp #$2E	; Is this character a '.'?
	bne getdir_exit	; No, so return.
	jsr clr_ptr

getdir_end:
	iny		; Increment offset.
	lda buf, y
	jsr iswhite
	bcs getdir_cmp
	ora #%00100000	; Make character lower case.
	sta (ptr1), y
	jmp getdir_end
gettok:
	ply #1
	iny
gettok2:
	lda buf, y
	jsr istoken
	bcs gettok3
	jmp gettok2
gettok3:
	cmp #$
	cmp #$24	; Is this character, a '$'?
	beq getaddr	; Yes, so start getting the address.



getdir_cmp:
	phy #1
	ldy.w #0
	tyx
getdir_cpl:
	ldb dir, x	; Start checking if the directive we're using, is "org".
	stb (ptr2), y
	beq getdir_scmp
	inx
	iny
	jmp getdir_cpl
getdir_scmp:
	ldy #0
	jsr strcmp
	cpx #0
	beq getdir_tok
getdir_exit:
	rts		; End of getdir.


istoken:
	cmp #$20	; Is this character a space?
	beq istoken_f	; Yes, so return false.
	cmp #$09	; Is this character a tab?
	beq istoken_f	; Yes, so return false.
	cmp #$A		; Is this character a newline?
	beq istoken_f	; Yes, so return false.
	cmp #$3B	; Is this character a ';'?
	beq istoken_f	; Yes, so return false.
	cmp #0		; Is this character a null terminator?
	beq istoken_f	; Yes, so return false.
	sec		; Return true.
	rts		; End of istoken.
istoken_f
	clc		; Return false.
	rts		; End of istoken_f.

iswhite:
	cmp #$20	; Is this character a space?
	beq iswhite_t	; Yes, so return true.
	cmp #9		; Is this character a tab?
	beq iswhite_t	; Yes, so return true.
	clc		; No, so return false.
	rts		; End of iswhite.
iswhite_t:
	sec		; Return true.
	rts		; End of iswhite_t.

rset_y:
	ldy.w #0
	jmp print_buf	; Print the input buffer.

print_buf:
	lda buf, y	; Get a character from the input buffer.
	beq fin		; Are we done with printing the buffer?
	sta scr		; Print said character.
	iny
	jmp print_buf	; Keep printing the buffer.

spin:
	nop
	nop
	nop
	jmp spin

clr_buf:
	lda #0
	cpy.w #$3FF
	beq clr_end
	sta buf, y
	iny
	jmp clr_buf
clr_sbuf:
	lda #0
	cpy.w #$1FF
	beq clr_end
	sta str_buf, y
	iny
	jmp clr_sbuf

clr_end:
	rts

echo:
	tab
	ldx scr_col
	cpx #$4F
	bne echo_print
	cmp #$A
	beq linewrap
linewrap:
	inc scr_row
	ldx #0
	stx scr_col
	jsr update_pos
echo_print:
	tba
	sta scr		; Echo typed character.
	inc scr_col	; Increment the cursor's x coordinate.
	sta buf, y	; Store typed character into the input buffer.
	iny		; Increment the buffer offset.
	rts		; Return.

clr_ptr:
	lda #0
	cpy.w #$3FF
	beq ptr_end
	sta (ptr1), y
	sta (ptr2), y
	iny
	jmp clr_ptr
ptr_end:
	rts

strcmp:
	lda (ptr1), y
	cmp (ptr2), y
	bne strcmp_l1
	tax
	beq strcmp_l3
	iny
	bne strcmp
	jmp strcmp


strcmp_l1:
	bcs strcmp_l2
	ldx #0
	dex
	rts

strcmp_l2:
	ldx #1
strcmp_l3:
	rts


.org $FFC0
.qword reset

.org $FF50
.qword spin
.qword spin
.qword spin
.qword spin
.qword spin
.qword spin
.qword spin

.org $FFA0
.qword irq_routine
.org $0
v
.org $100
v
.org $200
v
.org $300
v
.org $1000
v
.org $1100
v
.org $1200
v
.org $1300
v
.org $1400
v
q