'*******************************************************************************
'*                                                                             *
'* ZOG      A ZPU Virtual machine running on the Parallax Propeller            *
'*          micro-controller.                                                  *
'*                                                                             *
'* What?    The ZPU is a 32 bit processor core architecture specified by ZyLin *
'*          consultants for which there a number of HDL implementations        *
'*          for FPGA's.                                                        *
'*                                                                             *
'* Why?     ZyLin have targeted the GNU C Compiler to the ZPU instruction set. *
'*          ZOG makes it possible to use GCC for Propeller development.        *
'*          All ZPU instructions are a single byte, which should work out      *
'*          nicely when the code is placed in external RAM with an 8 bit bus.  *
'*                                                                             *
'*          Based on an original idea by Toby Seckshund.                       *
'*          Maths routines courtesy of Cluso and Parallax.                     *
'*                                                                             *
'*          Encouragement courtesy of Bill Henning and all on the Parallax     *
'*          Propeller discussion forum.                                        *
'*                                                                             *
'* Author:  Michael Rychlik                                                    *
'*                                                                             *
'*******************************************************************************
'
' v0.1      09-02-2010 First draft, totally untested.
'
' v0.2      18-02-2010 Tested and fixed up initial bugs.
'                      Runs first test program compiled with GCC.
'                      Has no I/O yet.
'
' v0.3      19-02-2010 Fixed mysterious "inversion of bit four of offset" problem
'                      with LOADSP and STORESP.
'
' v0.4      20-02-2010 Added primitive UART output such that the C run time
'                      routine outbyte() works. Zog now says hello!
'
' v0.5      28-02-2010 The CONFIG instruction is now always emulated,
'                      see emulate code in libgloss crt0.S for why.
'                      Added handling of the SYSCALL instruction via the io_command trap mechainism.
'                      Added fsrwFemto object (inititialization calls commented out for now).
'
' v0.6      xx-03-2010 Changed UART object to FullDuplexSerialPlus.
'
' v0.8      18-04-2010 Rewrote all ops with optimizations as suggested by Bill Henning.
'
' v0.9      25-04-2010 Optimized some most used ops in the FIBO test (wink, wink:)
'                      Fixed PUSHSP.
'
' v0.11     09-05-2010 Changes endianess of bytecodes in zpu_memory
'                      Adjusted read/write byte/word and instruction fetch inline with endianness
'                      Fixed ADDSP.
'
' v0.12     11-05-2010 The ZPU_EMULATE op now works. If it's ever needed.
'                      Fixed MOD and DIV instructions.
'                      Inlined read/write_byte
'                      read/write_word are EMULLATED for now as their endiannes is not right yet.
'                      ZPU memory is 20K bytes (Until we put SD and VM COG back in).

' v0.13     14-05-2010 Fixed endiannes of strings output by SYS_write syscall.
'                      Return a zero result from SYS_fstat syscall.
'
' v0.14     08-06-2010 Take Bill Henning's VMCog into use for external RAM access.
'                      Added #ifdef USE_VIRTUAL_MEMORY
'
' v0.15     15-06-2010 Fixed zpu_addsp to work correctly with virtual mem read_long.
'                      Added reading of a ZPU image from SD card file.
'                      Runs dhrystone by default (dstone.bin)
'
' v0.16     ??-06-2010 Fixed zpu_emulate for virtual memory usage.
'                      Take VMCog v0.975 into use.
'
' v0.17     20-06-2010 Ensure still builds for HUB memory use.
'
' v0.18     23-06-2010 Test for IM instruction in execute loop and halve the dispatch table size.
'                      Moved clearing of decode_mask into execute loop, saves many LONGs
'                      Change testing of decode_mask to use tjz
'                      Removed FIBO optimization code. We don't like to cheat.
'                      Changed dispatch table entries to BYTES, halving it's size again
'                      and saving one instruction in the exececute loop.
'                      Simplified checking for I/O in read/write_long
'                      These changes bring a 13% boost to fibo performance! (Running from HUB).
'
' v0.19     23-06-2010 read/write long now have access to HUB memory at virtual address $10000
'
' v0.20     01-07-2010 Now Zog C can start a cog via SYS_cognew syscall.
'
' v0.21     03-07-2010 Now runs new "pasm_objects_test" that tests C++ versions
'                      of FDS and VMCOG.
'
' v0.22     04-07-2010 Now handle SYS_cognew in PASM rather than Spin.
'
' v1.0      08-07-2010 Zog is now split out into it's own Spin file with minimal Spin
'                      startup code.
'                      Can now totally replace Spin and take over the entire Prop.
'                      N.B. Virtual Memory opertion is broken at this point.
'
'
'#define SINGLE_STEP
'#define CYCLE_COUNT
'#define USE_VIRTUAL_MEMORY
#define USE_HUB_MEMORY

' These are the SPIN byte codes for mul and div
SPIN_MUL_OP     = $F4  '(multiply, return lower 32 bits)
SPIN_DIV_OP     = $F6  '(divide, return quotient 32 bits)
SPIN_REM_OP     = $F7  '(divide, return remainder 32 bits)

'I/O control block commands
io_cmd_out      = $01
io_cmd_in       = $02
io_cmd_break    = $03
io_cmd_syscall  = $04

'System  call ID numbers. Taken from libgloss syscall.h
'These are required by the ANSI C part of newlib (excluding system() of course).
SYS_exit        =  1
SYS_open        =  2
SYS_close       =  3
SYS_read        =  4
SYS_write       =  5
SYS_lseek       =  6
SYS_unlink      =  7
SYS_getpid      =  8
SYS_kill        =  9
SYS_fstat       = 10
'SYS_sbrk       = 11 - not currently a system call, but reserved.
'ARGV support.
SYS_argvlen     = 12
SYS_argv        = 13
'These are extras added for one reason or another.
SYS_chdir       = 14
SYS_stat        = 15
SYS_chmod       = 16
SYS_utime       = 17
SYS_time        = 18
'Propeller specific actions
SYS_cognew      = 8000
SYS_coginit     = 8001
SYS_cogstop     = 8002

VAR
  long cog

PUB start (params) | okay
  okay := cog := cognew(@enter, params) + 1   'Start emulator in a new COG

PUB stop
'FIXME we need this

PUB getdispatch_table
  return @dispatch_table

PUB getzog
  return @enter

DAT
                        org     0
enter                   jmp     #init
'------------------------------------------------------------------------------
'Opcode handlers. This section MUST fit in 256 LONGS.

zpu_breakpoint          call    #break
                        jmp     #done_and_inc_pc

zpu_addsp               and     address, #$0F wz
              if_z      jmp     #no_offset             'Handle case where offset = 0, this DOES happen.
                        shl     address, #2
                        add     address, sp
                        call    #read_long
                        add     tos, data
                        jmp     #done_and_inc_pc
no_offset               add     tos, tos
                        jmp     #done_and_inc_pc

zpu_loadsp              and     address, #$1F
                        xor     address, #$10           'Trust me, you need this.
                        shl     address, #2
                        add     address, sp
                        call    #push_tos
                        call    #read_long
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_storesp             and     address, #$1F
                        xor     address, #$10           'Trust me, you need this.
                        shl     address, #2
                        add     address, sp
                        mov     data, tos
                        call    #write_long
                        call    #pop
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_config              mov     cpu, tos
                        call    #pop
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_pushpc              call    #push_tos
                        mov     tos, pc
                        jmp     #done_and_inc_pc

zpu_or                  call    #pop
                        mov     nos, data
                        or      tos, nos
                        jmp     #done_and_inc_pc

zpu_not                 xor     tos, minus_one
                        jmp     #done_and_inc_pc

zpu_load                mov     address, tos
                        call    #read_long
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_pushspadd           shl     tos, #2
                        add     tos, sp
                        jmp     #done_and_inc_pc

zpu_store               call    #pop
                        mov     nos, data
                        mov     address, tos
                        call    #write_long
                        call    #pop
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_poppc               mov     pc, tos
                        call    #pop
                        mov     tos, data
                        jmp     #done

zpu_poppcrel            add     pc, tos
                        call    #pop
                        mov     tos, data
                        jmp     #done

zpu_flip                rev     tos, #32
                        jmp     #done_and_inc_pc

zpu_add                 call    #pop
                        mov     nos, data
                        add     tos, nos
                        jmp     #done_and_inc_pc

zpu_sub                 call    #pop
                        sub     data, tos
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_pushsp              call    #push_tos
                        mov     tos, sp
                        add     tos, #4
                        jmp     #done_and_inc_pc

zpu_popsp               mov     sp, tos
                        mov     address, sp
                        call    #read_long
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_nop                 jmp     #done_and_inc_pc

zpu_and                 call    #pop
                        mov     nos, data
                        and     tos, nos
                        jmp     #done_and_inc_pc

zpu_xor                 call    #pop
                        mov     nos, data
                        xor     tos, nos
                        jmp     #done_and_inc_pc

zpu_loadb
#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        mov     addr, tos
                        xor     addr, #%11              'XOR here is an endianess fix.
                        shl     addr, #9
                        movs    addr, #READVMB
                        wrlong  addr, mboxcmd
:waitres                rdlong  data, mboxcmd wz
              if_nz     jmp     #:waitres
                        rdbyte  tos, mboxdat
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, tos
'                        and     memp, zpu_memory_mask
                        xor     memp, #%11              'XOR here is an endianess fix.
                        add     memp, zpu_memory_addr
                        rdbyte  tos, memp
#endif
                        jmp     #done_and_inc_pc

zpu_storeb              call    #pop
#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        wrbyte  data, mboxdat
                        mov     addr, tos
                        xor     addr, #%11              'XOR here is an endianess fix.
                        shl     addr, #9
                        movs    addr, #WRITEVMB
                        wrlong  addr, mboxcmd

:waitdone               rdlong  data, mboxcmd wz
                  if_nz jmp     #:waitdone
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, tos
'                        and     memp, zpu_memory_mask
                        xor     memp, #%11              'XOR here is an endianess fix.
                        add     memp, zpu_memory_addr
                        wrbyte  data, memp
#endif
                        call    #pop
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_loadh               mov     address, tos
                        call    #read_word
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_storeh              call    #pop
                        mov     address, tos
                        call    #write_word
                        jmp     #done_and_inc_pc

zpu_lessthan            call    #pop
                        cmps    tos, data wz,wc
                        mov     tos, #0
              if_b      mov     tos, #1
                        jmp     #done_and_inc_pc

zpu_lessthanorequal     call    #pop
                        cmps    tos, data wz,wc
                        mov     tos, #0
              if_be     mov     tos, #1
                        jmp     #done_and_inc_pc

zpu_ulessthan           call    #pop
                        cmp     tos, data wz, wc
                        mov     tos, #0
              if_b      mov     tos, #1
                        jmp     #done_and_inc_pc

zpu_ulessthanorequal    call    #pop
                        cmp     tos, data wz, wc
                        mov     tos, #0
              if_be     mov     tos, #1
                        jmp     #done_and_inc_pc

zpu_swap                mov     data, tos
                        shr     data, #16
                        shl     tos, #16
                        or      data, tos
                        jmp     #done_and_inc_pc

zpu_mult16x16           call    #pop
                        mov     x, data
                        and     x, word_mask
                        mov     y, tos
                        and     y, word_mask
                        mov     a, #SPIN_MUL_OP
                        jmp     #math_F4

zpu_eqbranch            call    #pop
              if_z      add     pc, tos
              if_nz     add     pc, #1
                        call    #pop
                        mov     tos, data
                        jmp     #done

zpu_neqbranch           call    #pop
              if_nz     add     pc, tos
              if_z      add     pc, #1
                        call    #pop
                        mov     tos, data
                        jmp     #done

zpu_mult                call    #pop
                        mov     x, data
                        mov     y, tos
                        mov     a, #SPIN_MUL_OP
                        jmp     #math_F4

zpu_div                 call    #pop
                        mov     y, data wz
              if_z      jmp     #div_zero_error
                        mov     x, tos
                        mov     a, #SPIN_DIV_OP
                        jmp     #math_F4

zpu_mod                 call    #pop
                        mov     y, data wz
              if_z      jmp     #div_zero_error
                        mov     x, tos         wz
                        mov     a, #SPIN_REM_OP
                        jmp     #math_F4

zpu_lshiftright         call    #pop
                        and     tos, #$3F
                        shr     data, tos
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_ashiftleft          call    #pop
                        and     tos, #$3F
                        shl     data, tos
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_ashiftright         call    #pop
                        and     tos, #$3F
                        sar     data, tos
                        mov     tos, data
                        jmp     #done_and_inc_pc

zpu_call                mov     nos, tos
                        mov     tos, pc
                        add     tos, #1
                        mov     pc, nos
                        jmp     #done

zpu_callpcrel           mov     nos, tos
                        mov     tos, pc
                        add     tos, #1
                        add     pc, nos
                        jmp     #done

zpu_eq                  call    #pop
                        cmp     tos, data wz
              if_z      mov     tos, #1
              if_nz     mov     tos, #0
                        jmp     #done_and_inc_pc

zpu_neq                 call    #pop
                        cmp     tos, data wz
              if_nz     mov     tos, #1
              if_z      mov     tos, #0
                        jmp     #done_and_inc_pc

zpu_neg                 neg     tos, tos
                        jmp     #done_and_inc_pc

zpu_syscall             jmp     #syscall

zpu_emulate             call    #push_tos
                        mov     tos, pc
                        add     tos, #1

#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        mov     addr, pc
                        xor     addr, #%11              'XOR here is an endianess fix.
                        shl     addr, #9
                        movs    addr, #READVMB
                        wrlong  addr, mboxcmd
:waitres                rdlong  data, mboxcmd wz
              if_nz     jmp     #:waitres
                        rdbyte  pc, mboxdat
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, pc
'                        and     memp, zpu_memory_mask
                        xor     memp, #%11              'XOR here is an endianess fix.
                        add     memp, zpu_memory_addr
                        rdbyte  pc, memp
#endif
                        sub     pc, #32
                        shl     pc, #5
                        jmp     #done

zpu_illegal
div_zero_error          call    #break
                        jmp     #done
'------------------------------------------------------------------------------
                        fit $FF                         'Opcode handlers must fit in 256 LONGS
'------------------------------------------------------------------------------
'ZPU memory space access routines

'Push a LONG onto the stack from "tos"
push_tos

#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong    temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp       #:waitmbox              'instructions are not necc
                        mov       addr, sp
                        wrlong    tos, mboxdat
                        shl       addr, #9
                        movs      addr, #WRITEVML
                        wrlong    addr, mboxcmd

:waitdone               rdlong    addr, mboxcmd wz
                  if_nz jmp       #:waitdone
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, sp
'                        and     memp, zpu_memory_mask
                        add     memp, zpu_memory_addr
                        wrlong  tos, memp
#endif
                        sub     sp, #4
push_tos_ret            ret

'Pop a LONG from the stack into "data", set Z according to data.
pop                     add     sp, #4
#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        mov     addr, sp
                        shl     addr, #9
                        movs    addr, #READVML
                        wrlong  addr, mboxcmd
:waitres                rdlong  data, mboxcmd wz
              if_nz     jmp     #:waitres
                        rdlong  data, mboxdat wz
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, sp
'                        and     memp, zpu_memory_mask
                        add     memp, zpu_memory_addr
                        rdlong  data, memp wz           'Must set Z for caller
#endif
pop_ret                 ret

'Read a LONG from ZPU memory at "address" into "data"
read_long               cmp     address, word_mask wc   'FIXME this only allows 64K RAM
              if_nc     jmp     #in_long

#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        mov     addr, address
                        shl     addr, #9
                        movs    addr, #READVML
                        wrlong  addr, mboxcmd
:waitres                rdlong  data, mboxcmd wz
              if_nz     jmp     #:waitres
                        rdlong  data, mboxdat
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, address
'                        and     memp, zpu_memory_mask
                        add     memp, zpu_memory_addr
                        rdlong  data, memp
#endif
read_long_ret           ret

'Write a LONG from "data" to ZPU memory at "address"
write_long              cmp     address, word_mask wc   'FIXME this only allows 64K RAM
              if_nc     jmp     #out_long
#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        wrlong  data, mboxdat
                        mov     addr, address
                        shl     addr, #9
                        movs    addr, #WRITEVML
                        wrlong  addr, mboxcmd

:waitdone               rdlong  addr, mboxcmd wz
                  if_nz jmp     #:waitdone
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, address
'                        and     memp, zpu_memory_mask
                        add     memp, zpu_memory_addr
                        wrlong  data, memp
#endif
write_long_ret          ret

'Read a WORD from ZPU memory at "address into "data"
read_word               mov     memp, address
'                        and     memp, zpu_memory_mask
                        xor     memp, #%10              'XOR here is an endianess fix.
                        add     memp, zpu_memory_addr
                        rdword  data, memp
read_word_ret           ret

'Write a WORD from "data" to ZPU memory at "address"
write_word              mov     memp, address
'                        and     memp, zpu_memory_mask
                        xor     memp, #%10              'XOR here is an endianess fix.
                        add     memp, zpu_memory_addr
                        wrword  data, memp
write_word_ret          ret
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
in_long                 cmp     address, vhub_end wz, wc
              if_be     jmp     #read_hub_long

                        cmp     address, timer_address wz
              if_z      mov     data, cnt
              if_z      jmp     #read_long_ret
                        mov     data, #$100
                        jmp     #read_long_ret

out_long                cmp     address, vhub_end wz, wc
              if_be     jmp     #write_hub_long

                        wrlong  data, io_data_addr
                        mov     temp, #io_cmd_out     'Set I/O command to OUT
                        wrlong  temp, io_command_addr
:wait                   rdlong  temp, io_command_addr wz
              if_nz     jmp     #:wait
                        jmp     #write_long_ret
'------------------------------------------------------------------------------
read_hub_long           sub     address, word_mask    'FIXME this only allows 64K RAM
                        rdlong  data, address
                        jmp     #read_long_ret

write_hub_long          sub     address, word_mask    'FIXME this only allows 64K RAM
                        wrlong  data, address
                        jmp     #write_long_ret
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
'Maths routines borrowed from the Cluso Spin interpreter.
'In turn orrowed from the Parallax Spin interpreter.
'$F4..F7 = MUL/DIV lower/upper result
'
' "a" holds the opcode (lower 5 bits as shown in the first block of code... e.g. MPY=$F4=%10100)
' "x" and "y" are the two numbers to be multiplied (or divided) x * y or x / y, result is in "x" (pushed).
'
' So, if you use this routine with a set to...
'  a = $F4 (multiply, return lower 32 bits)
'  a = $F5 (multiply, return upper 32 bits)
'  a = $F6 (divide, return quotient 32 bits)
'  a = $F7 (divide, return remainder 32 bits)

math_F4                 and     a,#%11111               '<== and mask (need in a)
                        mov     t1,#0
                        mov     t2,#32                  'multiply/divide
                        abs     x,x             wc
                        muxc    a,#%01100
                        abs     y,y             wc,wz
        if_c            xor     a,#%00100
                        test    a,#%00010       wc      'set c if divide (DIV/MOD)
        if_c_and_nz     jmp     #mdiv                   'if divide and y=0, do multiply so result=0
                        shr     x,#1            wc      'multiply
mmul    if_c            add     t1,y            wc
                        rcr     t1,#1           wc
                        rcr     x,#1            wc
                        djnz    t2,#mmul
                        test    a,#%00100       wz
        if_nz           neg     t1,t1
        if_nz           neg     x,x             wz
        if_nz           sub     t1,#1
                        test    a,#%00001       wz
        if_nz           mov     x,t1
                        mov     tos, x
                        jmp     #done_and_inc_pc
mdiv                    shr     y,#1            wc,wz   'divide
                        rcr     t1,#1
        if_nz           djnz    t2,#mdiv
mdiv2                   cmpsub  x,t1            wc
                        rcl     y,#1
                        shr     t1,#1
                        djnz    t2,#mdiv2
                        test    a,#%01000       wc
                        negc    x,x
                        test    a,#%00100       wc
                        test    a,#%00001       wz
        if_z            negc    x,y
                        mov     tos, x
                        jmp     #done_and_inc_pc

a                       long    0
x                       long    0
y                       long    0
t1                      long    0
t2                      long    0
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
zpu_im                  tjz    decode_mask, #:first

:next                   shl     tos, #7
                        and     data, #$7F
                        or      tos, data
                        jmp     #done_and_inc_pc

:first                  call    #push_tos
                        mov     tos, data
                        shl     tos, #(32 - 7)          'Sign extend
                        sar     tos, #(32 - 7)
                        mov     decode_mask, #1         'BEWARE falls thought to done_and_inc_pc
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
' Main ZPU fetch and execute loop
done_and_inc_pc         add     pc, #1
done
execute
#ifdef USE_VIRTUAL_MEMORY
':waitmbox              rdlong  temp, mboxcmd wz        'If only one client to VMCOG, these first two
'              if_nz    jmp     #:waitmbox              'instructions are not necc
                        mov     addr, pc
                        xor     addr, #%11              'XOR here is an endianess fix.
                        shl     addr, #9
                        movs    addr, #READVMB
                        wrlong  addr, mboxcmd
:waitres                rdlong  data, mboxcmd wz
              if_nz     jmp     #:waitres
                        rdbyte  data, mboxdat
#endif
#ifdef USE_HUB_MEMORY
                        mov     memp, pc
'                        and     memp, zpu_memory_mask
                        xor     memp, #%11               'XOR here is an endianess fix.
                        add     memp, zpu_memory_addr
                        rdbyte  data, memp
#endif
#ifdef SINGLE_STEP
                        call    #break
#endif
                        test     data, #$80 wz          'Check for IM instruction. This saves table lookup
              if_nz     jmp     #zpu_im                 'for the most common op. 7% fibo speed gain!

                        mov     address, data           'Some opcodes contains address offsets.
                        mov     decode_mask, #0         'All opcodes, bar IM, clear decode_mask

                        add     data, dispatch_tab_addr
                        rdbyte  temp, data
                        jmp     temp                    'No # here we are jumping through temp.
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
init                    mov     address, par
                        rdlong  zpu_memory_addr, address
                        add     address, #4
                        rdlong  zpu_memory_sz, address
                        add     address, #4
                        rdlong  pc, address
                        add     address, #4
                        rdlong  sp, address
                        add     address, #4
                        rdlong  dispatch_tab_addr, address
                        jmp     #execute
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
break                   wrlong  pc, pc_addr             'Dump registers to HUB.
                        wrlong  sp, sp_addr
                        wrlong  tos, tos_addr
                        wrlong  debug, debug_addr
                        wrlong  decode_mask, dm_addr
                        mov     temp, #io_cmd_break     'Set I/O command to BREAK
                        wrlong  temp, io_command_addr
:wait                   rdlong  temp, io_command_addr wz
              if_nz     jmp     #:wait
break_ret               ret
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
syscall                 mov     address, sp             'Get syscall ID from stack
                        add     address, #8
                        call    #read_long
                        cmp     data, sys_cognew_ wz    'Is it SYS_cognew?
              if_nz     jmp     #syscall_external_handler

'On SYS_cognew syscall:
'ZPU [SP +  8] is syscall ID
'    [SP + 16] is address of PASM code to load
'    [SP + 20] is address of PAR for Cog
'Cog number or error code is returned in ZPU R0 which is memory address 0.
handle_sys_cognew
                        mov     coginit_dest, #%1000    'Set cognew "any cog will do" bit
                        add     address, #8             'Get HUB address of PASM code to run
                        call    #read_long
                        add     data, zpu_memory_addr   'Map to HUB address

                        and     data, word_mask         'Only 16 bit address
                        shl     data, #2                'Shift into bits 17:4 (only high 14 bits required)
                        or      coginit_dest, data      'Place in dest for coginit

                        add     address, #4             'Now offset 20 from SP
                        call    #read_long              'Get address of PAR data
                        add     data, zpu_memory_addr   'Map to HUB address

                        and     data, word_mask         'Only 16 bit address
                        shl     data, #16               'Move to bits 31:18 (only high 14 bits required)

                        or      coginit_dest, data      'Combine PASM addr and PAR addr
                        coginit coginit_dest wc,wz,wr
              if_c      mov     coginit_dest, minus_one 'Return -1 if no free cog
                        mov     address, #0             'Error is returned in ZPU reg 0, which is address 0
                        mov     data, coginit_dest
                        call    #write_long
                        jmp     #done_and_inc_pc

syscall_external_handler
               '         wrlong  sp, sp_addr
               '         wrlong  decode_mask, dm_addr
                        mov     temp, #io_cmd_syscall   'Set I/O command to SYSCALL
                        wrlong  temp, io_command_addr
:wait                   rdlong  temp, io_command_addr wz'Wait for command completion
              if_nz     jmp     #:wait
                '        rdlong  decode_mask, dm_addr    'Retrive registers from HUB.
                '        rdlong  sp, sp_addr
                        jmp     #done_and_inc_pc
'------------------------------------------------------------------------------

'------------------------------------------------------------------------------
'ZPU registers and working variables
pc                      long 0      'ZPU Program Counter
sp                      long 0      'ZPU Stack Pointer
tos                     long 0      'Top Of Stack
nos                     long 0      'Next on Stack
decode_mask             long 0      'Flag for the IM instruction
instruction             long 0      'Opcode being executed.
data                    long 0      'Data parameter for read, write etc
address                 long 0      'Address parameter for read write etc
memp                    long 0      'Temporary pointer into ZPU memory space
temp                    long 0      'For temp operands etc
cpu                     long 0      'The CPU type given by the CONGIG op.
op_counter              long 0      'Counts number of executed ops.

zpu_memory_addr         long 0      'HUB address of the ZPU memory area
zpu_memory_sz           long 0      'Size of ZPU memory area in bytes
dispatch_tab_addr       long 0      'HUB address of instruction dispatch table
pc_addr                 long 0      'HUB address of PC
sp_addr                 long 0      'HUB address of SP
tos_addr                long 0      'HUB address of tos
dm_addr                 long 0      'HUB address of decode mask
io_command_addr         long 0      'HUB address of I/O command byte.
io_port_addr            long 0      'HUB address of I/Ö port number
io_data_addr            long 0      'HUB address of I/O data
debug_addr              long 0      'HUB address of debug register
coginit_dest            long 0      'Used for coginit instruction.

#ifdef USE_VIRTUAL_MEMORY
'--------------------------------------------------------------------------------------------------
' Variables used to support VM readbyte/writebyte
'--------------------------------------------------------------------------------------------------
addr                    long 0      'Address we want to read, can be PC or whatever
mboxcmd                 long 0-0    'Pointer to first long of VMCOG mailbox (+0 offset)
mboxdat                 long 0-0    'Pointer to second long of VMCOG mailbox (+4 offset)
'--------------------------------------------------------------------------------------------------
#endif

'------------------------------------------------------------------------------
' Big constants
minus_one               long $FFFFFFFF
minus_two               long $FFFFFFFE
word_mask               long $0000FFFF
'zpu_memory_mask         long zpu_memory_size - 1 'FIXME We may want this back
uart_address            long $80000024
'timer_address           long $080a0014
timer_address           long $80000100
vhub_end                long $0001FFFF
sys_cognew_             long SYS_cognew

'------------------------------------------------------------------------------
                        fit     $1F0
'--------------------------------------------------------------------------------------------------
'IO interface area. Do not change the order of these.
io_cmd_block  long
io_command    long      0
io_port       long      0
io_data       long      0

debug         long      0
'--------------------------------------------------------------------------------------------------

'------------------------------------------------------------------------------
' The instruction dispatch look up table (in HUB)
dispatch_table
{00}    byte  zpu_breakpoint
{01}    byte  zpu_illegal
{02}    byte  zpu_pushsp
{03}    byte  zpu_illegal
{04}    byte  zpu_poppc
{05}    byte  zpu_add
{06}    byte  zpu_and
{07}    byte  zpu_or
{08}    byte  zpu_load
{09}    byte  zpu_not
{0A}    byte  zpu_flip
{0B}    byte  zpu_nop
{0C}    byte  zpu_store
{0D}    byte  zpu_popsp
{0E}    byte  zpu_illegal
{0F}    byte  zpu_illegal

{10}    byte  zpu_addsp
{11}    byte  zpu_addsp
{12}    byte  zpu_addsp
{13}    byte  zpu_addsp
{14}    byte  zpu_addsp
{15}    byte  zpu_addsp
{16}    byte  zpu_addsp
{17}    byte  zpu_addsp
{18}    byte  zpu_addsp
{19}    byte  zpu_addsp
{1A}    byte  zpu_addsp
{1B}    byte  zpu_addsp
{1C}    byte  zpu_addsp
{1D}    byte  zpu_addsp
{1E}    byte  zpu_addsp
{1F}    byte  zpu_addsp

{20}    byte  zpu_emulate
{21}    byte  zpu_emulate
{22}    byte  zpu_emulate 'zpu_loadh                         'FIXME Not tested.
{23}    byte  zpu_emulate 'zpu_storeh                        'FIXME Not tested.
{24}    byte  zpu_lessthan
{25}    byte  zpu_lessthanorequal
{26}    byte  zpu_ulessthan
{27}    byte  zpu_ulessthanorequal
{28}    byte  zpu_swap
{29}    byte  zpu_mult
{2A}    byte  zpu_lshiftright
{2B}    byte  zpu_ashiftleft
{2C}    byte  zpu_ashiftright
{2D}    byte  zpu_call
{2E}    byte  zpu_eq
{2F}    byte  zpu_neq

{30}    byte  zpu_neg
{31}    byte  zpu_sub
{32}    byte  zpu_xor
{33}    byte  zpu_loadb
{34}    byte  zpu_storeb
{35}    byte  zpu_div
{36}    byte  zpu_mod
{37}    byte  zpu_eqbranch
{38}    byte  zpu_neqbranch
{39}    byte  zpu_poppcrel
{3A}    byte  zpu_config
{3B}    byte  zpu_pushpc
{3C}    byte  zpu_syscall
{3D}    byte  zpu_pushspadd
{3E}    byte  zpu_mult16x16
{3F}    byte  zpu_callpcrel

{40}    byte  zpu_storesp
{41}    byte  zpu_storesp
{42}    byte  zpu_storesp
{43}    byte  zpu_storesp
{44}    byte  zpu_storesp
{45}    byte  zpu_storesp
{46}    byte  zpu_storesp
{47}    byte  zpu_storesp
{48}    byte  zpu_storesp
{49}    byte  zpu_storesp
{4A}    byte  zpu_storesp
{4B}    byte  zpu_storesp
{4C}    byte  zpu_storesp
{4D}    byte  zpu_storesp
{4E}    byte  zpu_storesp
{4F}    byte  zpu_storesp

{50}    byte  zpu_storesp
{51}    byte  zpu_storesp
{52}    byte  zpu_storesp
{53}    byte  zpu_storesp
{54}    byte  zpu_storesp
{55}    byte  zpu_storesp
{56}    byte  zpu_storesp
{57}    byte  zpu_storesp
{58}    byte  zpu_storesp
{59}    byte  zpu_storesp
{5A}    byte  zpu_storesp
{5B}    byte  zpu_storesp
{5C}    byte  zpu_storesp
{5D}    byte  zpu_storesp
{5E}    byte  zpu_storesp
{5F}    byte  zpu_storesp

{60}    byte  zpu_loadsp
{61}    byte  zpu_loadsp
{62}    byte  zpu_loadsp
{63}    byte  zpu_loadsp
{64}    byte  zpu_loadsp
{65}    byte  zpu_loadsp
{66}    byte  zpu_loadsp
{67}    byte  zpu_loadsp
{68}    byte  zpu_loadsp
{69}    byte  zpu_loadsp
{6A}    byte  zpu_loadsp
{6B}    byte  zpu_loadsp
{6C}    byte  zpu_loadsp
{6D}    byte  zpu_loadsp
{6E}    byte  zpu_loadsp
{6F}    byte  zpu_loadsp

{70}    byte  zpu_loadsp
{71}    byte  zpu_loadsp
{72}    byte  zpu_loadsp
{73}    byte  zpu_loadsp
{74}    byte  zpu_loadsp
{75}    byte  zpu_loadsp
{76}    byte  zpu_loadsp
{77}    byte  zpu_loadsp
{78}    byte  zpu_loadsp
{79}    byte  zpu_loadsp
{7A}    byte  zpu_loadsp
{7B}    byte  zpu_loadsp
{7C}    byte  zpu_loadsp
{7D}    byte  zpu_loadsp
{7E}    byte  zpu_loadsp
{7F}    byte  zpu_loadsp
'---------------------------------------------------------------------------------------------------------
'N.B. Do not add any more after the dispatch table.
'     It must be at the end such that it can be found and/or extracted by build
'     toolsfor use with C.
'---------------------------------------------------------------------------------------------------------
'The End.

