eris2010

Documentation: http://frombelow.net/projects/eris2010/
Clone: git clone https://git.frombelow.net/eris2010.git
Log | Files | Refs | Submodules | README | LICENSE

commit 37ac774138eceb6c7f25bc7866728d018256ac96
parent 822e76c00ceadee13fd533320aa460a502c57169
Author: Gerd Beuster <gerd@frombelow.net>
Date:   Mon, 30 Nov 2020 18:36:16 +0100

Data stack implemented

Diffstat:
Mroms/boot/boot.asm | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mroms/boot/boot_macros.inc | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Msw/interrupts/interrupts.asm | 30++++++++++++++++++++++++++----
Asw/stack_test/Makefile | 3+++
Asw/stack_test/stack_test.asm | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msw/ttt/ttt.asm | 46++++++++++++++++++++++++++--------------------
6 files changed, 310 insertions(+), 38 deletions(-)

diff --git a/roms/boot/boot.asm b/roms/boot/boot.asm @@ -1,26 +1,42 @@ CLOCK_SPEED = 4 ;4 Mhz Clock + +.if SYMON + rom_start = $c000 +.else + rom_start = $e000 +.endif .include "boot_macros.inc" -;;; Global zero page variables + * = $00 +.dsection zero_page +.cerror * > $ff, "Zero page exhausted" + * = rom_start +.dsection rom +.cerror * > $ffff, "ROM exhausted" + export .namespace -lfsr_state = $20 ; 16 bit +.section zero_page + ;; LFSR +lfsr_state: .word ? + ;; Stack +ds_pointer: .word ? +ds_frame: .word ? + ;; Temporary zero page variables; + ;; only used in subroutine +puts_str: .word ? +gets_len: .byte ? +gets_str: .word ? + ;; Everything beyond these reserved variables available to + ;; programs in RAM. +rom_zero_page_end: +.send zero_page .endn - -;;; Temporary zero page variables; -;;; only used in subroutine + .namespace export -puts_str = $10 ; 16 bit address -gets_len = $10 ; 8 bit -gets_str = $11 ; 16 bit address +stack_end = $7ffb .endn -.if SYMON - * = $c000 -.else - * = $e000 -.endif - ;;; The actual (hardware) interrupt vectors are located in the ROM ;;; at $ fffa/fffb (nmi) and $fffe/$ffff (irq). Since we want to be ;;; able to handle interrupts in RAM, the ROM isr routines jump to the @@ -31,6 +47,7 @@ nmi_vector = $7ffc irq_vector = $7ffe .endn + .section rom ;;; ---------------------------------------------------------- ;;; RESET ;;; @@ -44,6 +61,7 @@ boot: .block #STORE_WORD export.default_nmi_handler, export.nmi_vector cld cli + jsr export.init_stack jsr export.init_acia jsr check_for_program_download bcs no_program_download @@ -497,6 +515,61 @@ derefer_ram_irq: ;; in RAM jmp (export.irq_vector) +;;; ---------------------------------------------------------- +;;; STACK +;;; +;;; ---------------------------------------------------------- + +.namespace export +init_stack: +.endn + .block + #STORE_WORD export.stack_end+1, export.ds_pointer + rts + .bend + +.namespace export +new_stack_frame: +.endn + .block + #PUSH $02 ; Store old frame pointer + #COPY_WORD export.ds_frame, (export.ds_pointer) + #COPY_WORD export.ds_pointer, export.ds_frame ; Set new frame pointer + rts + .bend + +.namespace export +remove_stack_frame: +.endn + .block + #COPY_WORD export.ds_frame, export.ds_pointer ; Reset stack pointer to begin of frame. + #COPY_WORD (export.ds_pointer), export.ds_frame ; Restore old frame pointer. + #PULL $02 ; Remove obsolete frame pointer from stack. + rts + .bend + +.namespace export +push_a: +.endn + .block + pha + #PUSH $01 + pla + sta (export.ds_pointer) + rts + .bend + +.namespace export +pull_a: +.endn + .block + lda (export.ds_pointer) + pha + #PULL $01 + pla + rts + .bend + ;;; Default NMI handler. Unless the user program changes ;;; the nmi_vector, NMIs are handled here. .namespace export @@ -518,3 +591,5 @@ default_program: .word derefer_ram_nmi ; nmi .word boot ; reset .word derefer_ram_irq ; irq + +.send rom diff --git a/roms/boot/boot_macros.inc b/roms/boot/boot_macros.inc @@ -84,6 +84,13 @@ gets_len = gets_len gets = gets putc = putc + init_stack = init_stack + new_stack_frame = new_stack_frame + remove_stack_frame = remove_stack_frame + push_a = push_a + pull_a = pull_a + ds_pointer = ds_pointer + ds_frame = ds_frame .endn BOOT_EMBEDDED = false .else @@ -137,3 +144,49 @@ STORE_WORD .macro lda #>\1 sta \2+1 .endm + +PUSH: .macro ; Reserve bytes on stack (no actual push operation) + #SUB_WORD export.ds_pointer, \1 + .endm + +PULL: .macro ; Remove bytes from stack (no return value) + #ADD_WORD export.ds_pointer, \1 + .endm + +PUSH_WORD: .macro ; Push a word on the stack + #PUSH $02 + lda #<\1 + sta (export.ds_pointer) + lda #>\1 + ldy #$01 + sta (export.ds_pointer),y + .endm + + ;; By using the Y register, we also support indirect addressing modes +COPY_WORD: .macro + lda \1 + sta \2 + ldy #$01 + lda \1,y + sta \2,y + .endm + +SUB_WORD: .macro + lda \1 + sec + sbc #<\2 + sta \1 + lda \1+1 + sbc #>\2 + sta \1+1 + .endm + +ADD_WORD: .macro + lda \1 + clc + adc #<\2 + sta \1 + lda \1+1 + adc #>\2 + sta \1+1 + .endm diff --git a/sw/interrupts/interrupts.asm b/sw/interrupts/interrupts.asm @@ -11,14 +11,36 @@ init: ;; jmp transmitter_interrupt - jmp receiver_interrupt + ;; jmp receiver_interrupt + jmp manual_nmi_irq + +manual_nmi_irq: + ;; Expects NMI or IRQ triggered manually. + ;; Prints a message when an interrupt is received + .block + jsr init_acia + #STORE_WORD isr_print_message_irq, irq_vector + #STORE_WORD isr_print_message_nmi, nmi_vector + jsr getc + #PRINTSNL 'Please trigger an interrupt.' +wait: + jmp wait + +isr_print_message_irq: + #PRINTSNL 'IRQ triggered.' + rti + +isr_print_message_nmi: + #PRINTSNL 'NMI triggered.' + rti + .bend ;;; Looks like interrupt controlled tranmission is buggy. ;;; The only way to transmit seems to be a delay loop. transmitter_interrupt: .block jsr init_acia - #STORE_WORD isr, irq_vector + #STORE_WORD isr_raise_flag, irq_vector #PRINTSNL 'Push key to start.' jsr getc loop: @@ -45,7 +67,7 @@ wait_loop: receiver_interrupt: .block jsr init_acia - #STORE_WORD isr, irq_vector + #STORE_WORD isr_raise_flag, irq_vector ;; Receiver interrupt on lda acia_cmd_reg lda #%11001001 @@ -59,7 +81,7 @@ loop: jmp loop .bend -isr: +isr_raise_flag: lda flag inc a sta flag diff --git a/sw/stack_test/Makefile b/sw/stack_test/Makefile @@ -0,0 +1,3 @@ +TARGET=stack_test + +include ../Makefile.common diff --git a/sw/stack_test/stack_test.asm b/sw/stack_test/stack_test.asm @@ -0,0 +1,113 @@ +;;; A data stack +.if SYMON + .include "boot_symon.l" +.else + .include "boot.l" +.endif + .include "boot_macros.inc" + + * = $300 + .dsection code + .cerror * > $8000, "RAM exhausted" + + .section code +init: + cld + jsr init_acia + jmp stack_test +stack_test: + .block + ;; Testing empty stack + jsr init_stack + jsr new_stack_frame + jsr remove_stack_frame + lda ds_pointer + cmp #<stack_end+1 + #CHECK_ERROR + lda ds_pointer+1 + cmp #>stack_end+1 + #CHECK_ERROR + ;; Push and pull + jsr new_stack_frame + PUSH $01 + lda #$a0 + sta (ds_pointer) + PUSH $01 + lda #$b0 + sta (ds_pointer) + PUSH $01 + lda #$c0 + sta (ds_pointer) + lda (ds_pointer) + cmp #$c0 + #CHECK_ERROR + PULL $01 + lda (ds_pointer) + cmp #$b0 + #CHECK_ERROR + PULL $01 + lda (ds_pointer) + cmp #$a0 + #CHECK_ERROR + ;; Push and pull with new frame + PUSH $01 + lda #$a1 + sta (ds_pointer) + lda ds_pointer ; Save ds_pointer + pha ; for later comparison + jsr new_stack_frame + PUSH $03 + ldy #$00 + lda #$b1 + sta (ds_pointer),y + inc y + sta (ds_pointer),y + inc y + sta (ds_pointer),y + inc y + PULL $01 + lda (ds_pointer) + cmp #$b1 + #CHECK_ERROR + jsr remove_stack_frame + pla ; Still same ds_pointer + cmp ds_pointer ; as before creating and + #CHECK_ERROR ; deleting frame? + lda (ds_pointer) + cmp #$a1 + #CHECK_ERROR + PULL $01 + lda (ds_pointer) + cmp #$a0 + #CHECK_ERROR + jsr remove_stack_frame + lda ds_pointer ; Stack should be + cmp #<stack_end+1 ; empty again. + #CHECK_ERROR + lda ds_pointer+1 + cmp #>stack_end+1 + #CHECK_ERROR + #PUSH_WORD $abcd + jsr pull_a + cmp #$cd + #CHECK_ERROR + jsr pull_a + cmp #$ab + #CHECK_ERROR + PRINTSNL("Stack test completed!") +loop: + jmp loop + .bend + +CHECK_ERROR: .macro + .block + beq error_cont +error: + PRINTSNL("Error!") +error_loop: + jmp error_loop +error_cont: + .bend + .endm + + .send code diff --git a/sw/ttt/ttt.asm b/sw/ttt/ttt.asm @@ -18,44 +18,49 @@ .include "boot.l" .endif .include "boot_macros.inc" + * = rom_zero_page_end + .dsection zero_page * = $0300 .endif - -;;; Variables +.dsection ttt_game + +.section zero_page ;; This temporary variable is used all over the place - tmp = $30 +tmp: .word ? ;; Used by win_first_diagonal and win_first_diagonal - empty_field = $3e +empty_field: .byte ? ;; Used to check for forking opportunities - fork_board_rows = $4006 - fork_board_columns = $4009 - fork_board_diagonals = $400c +fork_board_rows: .byte ?, ?, ? +fork_board_columns: .byte ?, ?, ? +fork_board_diagonals: .byte ?, ?, ? ;; Used to pass location of board ;; to subroutines. - board_ptr = $32 +board_ptr: .word ? ;; Sometimes we copy stuff from one board ;; to the other. For this we have to ;; point to the other board. - other_board_ptr = $34 +other_board_ptr: .word ? ;; Default board location in memory - main_board = $4000 +main_board: .byte ?, ?, ? ;; Mirrored board location in memory - board_mirrored = $4003 +board_mirrored: .byte ?, ?, ? ;; Pointer to init subroutine of - ;; first player (2 bytes) - player_x_init_ptr = $36 + ;; first player +player_x_init_ptr: .word ? ;; Pointer to ply subroutine of - ;; first player (2 bytes) - player_x_ply_ptr = $38 + ;; first player +player_x_ply_ptr: .word ? ;; Pointer to init subroutine of - ;; second player (2 bytes) - player_o_init_ptr = $3a + ;; second player +player_o_init_ptr: .word ? ;; Pointer to ply subroutine of ;; first player (2 bytes) - player_o_ply_ptr = $3c +player_o_ply_ptr: .word ? ;; Bord rendered as ASCII art - board_ascii = $5000 +board_ascii: .byte ?, ?, ?, ?, ?, ?, ?, ?, ? +.send zero_page +.section ttt_game start_ttt: .block @@ -159,5 +164,6 @@ run_tests: jsr getc jmp run_tests .endif - +.send ttt_game +