The structure may seem large but remember we are building a forth
process to deal with the interrupt.
On interrupt: entry LP points to a data structure that describes the state of the system at the start of the interrupt. This describes that structure.
Note we do not save the MAC state, if interrupt code wishes to use the MAC saving and restoreing is the interrupts code problem.
zero
DUP CONSTANT _#int_old_lp CELL+ \
DUP CONSTANT _#int_format 2 +
DUP CONSTANT _#int_status 2 +
DUP CONSTANT _#int_PC CELL+
DUP CONSTANT _#int_D0 CELL+
DUP CONSTANT _#int_D1 CELL+
DUP CONSTANT _#int_D2 CELL+
DUP CONSTANT _#int_D3 CELL+
DUP CONSTANT _#int_D4 CELL+
DUP CONSTANT _#int_D5 CELL+
DUP CONSTANT _#int_D6 CELL+
DUP CONSTANT _#int_D7 CELL+
DUP CONSTANT _#int_A0 CELL+
DUP CONSTANT _#int_A1 CELL+
DUP CONSTANT _#int_A2 CELL+
DUP CONSTANT _#int_A3 CELL+
DUP CONSTANT _#int_A4 CELL+
DUP CONSTANT _#int_A5 CELL+
DUP CONSTANT _#int_A6 CELL+
DUP CONSTANT _#int_A7 CELL+
DROP
The value at _#int_A7 must be used as the base address of interrupt entry structure. See system_entry
Note this word cannot be used with local variables, it assumes LP still points to the interrupt: stack frame
: @int_stack_frame ( offset -- value)
@lp + @
;
When you enter interrupt code you can use assembler if you desire. The following words set up the FORTH environment.
\ INT2
494E5432 CONSTANT _#interrupt_magic
\ DEND
44454E44 CONSTANT #stack_end_magic
\ DSTK
4453544B CONSTANT #stack_start_magic
ram_variable %interrupt_count
: _interrupt_set_user ( --)
#stack_start_magic \ leave on stack
_#interrupt_magic interrupt_magic_number !
@u DUP #activation_task> + !
@u _activation> !
1 %interrupt_count +!
%interrupt_count @ _task_restart_time !
zero 'buffer !
zero 'output_file !
zero 'input_file !
zero 'abort_file !
zero _error_pos? W!
\ last location in user space; if not set at end of int then int stack overflow.
#stack_end_magic data_guard !
\ This sets things up so a THROW will bring us back here.
\ If you want a little extra speed you can get rid of
\ this, but the fault exception routines will work if this
\ is done.
R@
\ skip the branch
4+
\ the catch actually executes the interrupt code.
CATCH DROP
\ code following the code that called this is a branch to the ;interrupt
\ call code
;
The user area and data stack are built on the return stack. You can use the local variable structure to create local data areas on the return stack if you desire. Obviously using it to describe input and output data stack items it a bit of a waste of time. There is no entry and exit data stack.
_interrupt_end_user user_base - CONSTANT _#interrupt_user_size
\ This is the length of the interrupt data stack, increase if
\ required, but also increase _#system_stack_length
80 CONSTANT _#interrupt_data_stack
Describe the return stack after registers saved
On entry A0 points to the entry stack.
forth : interrupt: ( --)
HOST CREATE
zero _%local_use !
\ switch to the interrupt stack
system_entry
\ push all registers
4FEF tw,
FFC4 tw, \ ##code 7 8 + 4 * NEGATE R) R LEA
48D7 tw, \ ##code \\ D0 A6 \\ R ) MMOV
7FFF tw, \
518F tw, \ 08 # R SUB \ room for format and return
\ this pushes a value onto the return stack
4E55 tw, \ ##code \\ LP nnn # LINK
_#interrupt_user_size
_#interrupt_data_stack + NEGATE tw,
\ get info from user stack into registers
4CD0 tw, \ A0 ) \\ D0 D2 \\ MMOV
0007 tw,
\ copy data from user registers to system stack
\ this is done so systemstack contains relevent info
\ should a fault occure
2B40 tw, _#int_A0 tw, \ D0 _#int_A0 LP) MOV
2B41 tw, _#int_format tw, \ D1 _#int_format LP) MOV
2B42 tw, _#int_PC tw, \ D2 _#int_pc LP) MOV
\ set up user stack
2C4D tw, \ ##code LP S MOV
\ set up user space
264F tw, \ ##code R U MOV
\ jsr to first forth word this sets up the user area and
\ executes following code.
4EB9 tw, \ ##code _interrupt_set_user AB JSR
['] _interrupt_set_user t,
\ for the abort
6000 tw, \ ##code BRA
\ reserve space for address resolution and put values on stack that will
\ see the resolution done by code in ;intrerrupt
HERE 0 tw,
_#comp_code_if
\ start the forth compilation
]T
forth
;
HOST
: interrupt: ( --)
CREATE
zero _%local_use !
system_entry
\ push all registers
4FEF W,
FFC4 W, \ ##code 7 8 + 4 * NEGATE R) R LEA
48D7 W, \ ##code \\ D0 A6 \\ R ) MMOV
7FFF W,
518F W, \ _#int_D0 _#int_format - # R SUB \ room for format and return
4E55 W, \ ##code \\ LP nnn # LINK
_#interrupt_user_size
_#interrupt_data_stack + NEGATE W,
\ get info from user stack into registers
4CD0 W, \ A0 ) \\ D0 D2 \\ MMOV
0007 W,
\ copy data from user registers to system stack
\ this is done so systemstack contains relevent info
\ should a fault occure
2B40 W, _#int_A0 W, \ D0 _#int_A0 LP) MOV
2B41 W, _#int_format W, \ D1 _#int_format LP) MOV
2B42 W, _#int_PC W, \ D2 _#int_pc LP) MOV
\ set up user stack
2C4D W, \ ##code LP S MOV
\ set up user space
264F W, \ ##code R U MOV
\ jsr to first forth word this sets up the user area and
\ executes following code.
4EB9 W, \ ##code _interrupt_set_user JSR
['] _interrupt_set_user ,
\ for the abort
6000 W, \ ##code BRA
HERE 0 W,
_#comp_code_origin
\ start the forth compilation
]
;
forth : ;interrupt
HOST
\ stop the compiler
[COMPILE] [
\ resolve the interrupt: reference
_#comp_code_if
forth ?PAIR
HOST HERE
forth OVER - SWAP
HOST TW!
\ local variable exit code if required.
forth _%local_use @ IF
HOST
4E5D tw, \ ##code LP UNLK
target_previous
_end_xlocal_dictionary
forth THEN
HOST
4E5D tw, \ ##code LP UNLK
508F tw, \ _#int_D0 _#int_format - # R ADD
4CD7 tw, \ ##code R ) \\ D0 A6 \\ MMOV
7FFF tw,
4FEF tw, \ ##code 7 8 + 4 * R) R LEA
003C tw,
system_rte
forth
; TARGET
HOST
: ;interrupt
\ stop the compiler
[COMPILE] [
\ resolve the interrupt: reference
_#comp_code_origin ?pair
!forward
\ word exit code
\ We have to unlink LP if we used local variables as
\ we need th previous value of LP to get out of the interrupt
\ code.
_%local_use @ IF
4E5D W, \ ##code LP UNLK
PREVIOUS
_end_local_dictionary
THEN
4E5D W, \ ##code LP UNLK
508F W, \ _#int_D0 _#int_format - # R ADD
4CD7 W, \ ##code R ) \\ D0 A6 \\ MMOV
7FFF W,
4FEF W, \ ##code 7 8 + 4 * R) R LEA
003C W,
system_rte
; IMMEDIATE