215 lines
5.7 KiB
NASM
215 lines
5.7 KiB
NASM
; ʕ·ᴥ·ʔ- fuzzy v0 rev 0: parse program text and spit out binary representation @ $4000
|
|
|
|
.include "./macro.inc"
|
|
|
|
n = $05 ; temporary storage for data stack operations
|
|
base = $00
|
|
result_binary_base = base ; pointer to where the next byte of binary data should be stored0
|
|
binary_base_index = result_binary_base + 2 ; offset for that pointer
|
|
binary_subroutine_address = binary_base_index + 1 ; pointer to a subroutine to be written to the binary
|
|
|
|
.org $8000
|
|
.include "./subroutines.inc"
|
|
|
|
program_text:
|
|
.include "./program.inc"
|
|
|
|
reset:
|
|
sei
|
|
lda #0
|
|
ldx #0
|
|
ldy #0
|
|
|
|
main:
|
|
stz binary_base_index
|
|
lda #$40
|
|
sta result_binary_base + 1 ; set where to store resulting binary
|
|
stz binary_subroutine_address
|
|
lda #$80
|
|
sta binary_subroutine_address + 1 ; available subroutines start at $8000
|
|
jsr compile_values
|
|
stp
|
|
|
|
; parser loop, eventually this will be able to handle longer program strings, but indexing by y is fine for now
|
|
compile_values:
|
|
ldy #0
|
|
parser_loop:
|
|
lda program_text, y ; get character at index
|
|
cmp #0 ; is eof?
|
|
beq .end ; yes, exit loop
|
|
cmp #20 ; is space?
|
|
beq parser_loop ; yes, skip this char
|
|
cmp #12 ; is newline?
|
|
beq .newline ; yes, handle newline
|
|
jsr compile_values_op
|
|
jsr compile_values_nat
|
|
.newline: ; we reached a newline, y is program string index
|
|
iny ; WARN: don't accidentally iny in this loop w/out handling a character
|
|
lda program_text, y ; load next char
|
|
cmp #12 ; is newline?
|
|
bne parser_loop ; no, keep parsing tokens
|
|
rts ; yes, no more tokens in body (see syntax.md for info)
|
|
.end:
|
|
rts
|
|
|
|
; a holds character value, y program text index, only iny if you find a matching character & consume it
|
|
compile_values_op:
|
|
cmp #"+" ; i personally think this syntax is really silly but whatever, one of these days i'm gonna write my own assembler and document everything cause vasm documentation is kinda terrible
|
|
bne .next
|
|
.is_plus:
|
|
lda #1
|
|
jsr store_subroutine
|
|
rts
|
|
.next:
|
|
rts
|
|
; cmp #"!" ; commenting these out for now to handle a single simple case
|
|
; cmp #"&"
|
|
; cmp #"|"
|
|
; cmp #"-"
|
|
; cmp #"*"
|
|
; cmp #"/"
|
|
; cmp #"="
|
|
; cmp #">"
|
|
; cmp #"<"
|
|
; cmp #"#"
|
|
|
|
; a holds character value, y program text index, only iny if you find a matching character & consume it
|
|
; TODO:
|
|
; 1-3 digit decimal values
|
|
; 1-2 digit hex values
|
|
compile_values_nat:
|
|
; TODO:
|
|
; cmp #"$" ; is hex?
|
|
; bne .decimal ; no, try decimal
|
|
; cmp
|
|
; rts
|
|
cmp #47 ; less than (before) start of 0-9 georgescii range?
|
|
bcc .not_nat
|
|
cmp #57 ; greater than end of 0-9 georgescii range?
|
|
bcs .not_nat
|
|
pha
|
|
lda #$a9 ; $a9: lda imm
|
|
jsr store_binary
|
|
pla
|
|
jsr georgescii_decimal_to_value
|
|
jsr store_binary
|
|
lda #2 ; push
|
|
jsr store_subroutine
|
|
iny
|
|
rts
|
|
.not_nat:
|
|
rts
|
|
|
|
; georgescii decimal value in a register, return equivalent plain value in a register
|
|
georgescii_decimal_to_value:
|
|
clc
|
|
sbc #$30 ; decimal digits start at georgescii $30
|
|
rts
|
|
|
|
; we have binary in the a register we want to store
|
|
store_binary:
|
|
phy
|
|
ldy binary_base_index
|
|
sta (result_binary_base), y
|
|
inc binary_base_index
|
|
bne .not_overflow ; did we roll over?
|
|
inc result_binary_base + 1 ; yes, roll over base address
|
|
.not_overflow: ; no, carry on as normal
|
|
ply
|
|
rts
|
|
|
|
; binary_subroutine_address is a pointer to a subroutine that we want to store
|
|
; the first byte at the subroutine's address is its length
|
|
store_contiguous_binary:
|
|
pha ; just to be safe
|
|
lda (binary_subroutine_address) ; get the subroutine length
|
|
tax ; loop counter
|
|
ldy #1 ; index into subroutine, offset by one to skip subroutine length
|
|
.loop:
|
|
lda (binary_subroutine_address), y
|
|
jsr store_binary
|
|
iny
|
|
dex
|
|
bne .loop
|
|
.end:
|
|
pla
|
|
rts
|
|
|
|
; this wouldn't be necessary if we could get the
|
|
; address of a label in vasm, but that's for another time
|
|
; (when i feel like writing an assembler lol)
|
|
; for now, pass the index of the subroutine (in subroutines.asm)
|
|
; to a and it will get written to binary_subroutine_address
|
|
get_subroutine_address:
|
|
pha
|
|
tax ; set up counter
|
|
bne .loop ; first subrotine?
|
|
stz binary_subroutine_address ; yes, store its address
|
|
lda #$80
|
|
sta binary_subroutine_address + 1
|
|
rts
|
|
.loop: ; loop through
|
|
lda (binary_subroutine_address) ; no, load length of subroutine
|
|
inc ; distance from next subroutine
|
|
clc
|
|
adc binary_subroutine_address ; add it to the current address
|
|
sta binary_subroutine_address
|
|
bcs .no_carry
|
|
lda binary_subroutine_address + 1 ; add the carry to the high byte of address
|
|
adc #0
|
|
sta binary_subroutine_address + 1
|
|
.no_carry:
|
|
dex ; is this our address?
|
|
bne .loop ; yes, we're done
|
|
pla
|
|
rts
|
|
|
|
|
|
; pass subroutine index to a and it will get written into the binary
|
|
; TODO: stabilize subroutine location & just write a `jsr $subroutine` to the binary
|
|
store_subroutine:
|
|
pha
|
|
phy
|
|
phx
|
|
jsr get_subroutine_address
|
|
jsr store_contiguous_binary
|
|
; reset subroutine address
|
|
stz binary_subroutine_address
|
|
lda #$80
|
|
sta binary_subroutine_address + 1
|
|
plx
|
|
ply
|
|
pla
|
|
rts
|
|
|
|
; write error message and stop execution
|
|
error:
|
|
ldy #0
|
|
.loop:
|
|
lda .message, y
|
|
sta $4000, y
|
|
beq .end
|
|
iny
|
|
bra .loop
|
|
.end:
|
|
stp
|
|
.message:
|
|
.asciiz "ruh roh! fuzzy couldn't compile"
|
|
|
|
|
|
|
|
isr: ; interrupt service routine
|
|
pha
|
|
phx
|
|
phy
|
|
ply
|
|
plx
|
|
pla
|
|
rti
|
|
|
|
|
|
|
|
.org $fffc
|
|
.word reset
|
|
.word isr
|