From The Mana World

This is a spec for some internal stuff that's better than the current stuff.

It is not expected that ordinary people will ever need to do this, but it *is* more powerful than the existing language.

I've given up on the lambda stack method - despite its purity, it simply consumes too much memory without specialized tools.

A script consists of a sequence of one-byte opcode and one-byte data, as well as ancillary tables used to assign meaning to the data byte.

There are tables for: integer constant, string constant, label or user function, builtin function, and variable name

These tables are per-function, so there should be no problem hitting the limit of 256 of each.

At runtime, there are 3 stacks: integer, string, and location (label/user function). Should there be another one for opaque pointers, such as items?

Note that the builtin functions obviously do NOT correspond directly to the ones in the scripting page.

Opcodes

op arg description
add N ≥ 2 pop top N integers, push their sum
cat N ≥ 2 pop top N strings, push their concatenation.
sub N ≥ 2 pop top N-1 integers, pop another, subtract the others from it, push the result
mul N ≥ 2 pop top N integers, push their product
div N ≥ 2 pop top N-1 integers, pop another, divide it by all the others
mod 0 pop top two integers, push their remainder
band N ≥ 2 pop top N integers, push their bitwise-and.
bor N ≥ 2 pop top N integers, push their bitwise-or
bxor N ≥ 2 pop top N integers, bitwise-xor repeatedly, push the final result.
neg 0 pop an integer, push its negation
inv 0 pop an integer, push its bitwise complement
not 0 pop an integer, push its boolean complement
shr 0 or N pop one or two integers, push unsigned right shift
shl 0 or N pop one or two integers, push left shift.
lt,le,gt,ge,eq,ne 0 (still squishy) pop two integers, push boolean comparison result
  (still need a string version)
  (should I completely remove this and instead add more jumps?)
s2i 0 pop top string, push converted integer
i2s 0 pop top integer, push converted string
dupi 0 pop top integer, push it twice

(maybe use N to take an element that's not at the top?)

dups 0 pop top string, push it twice
popi 0 pop top integer, discard
pops 0 pop top string, discard
jump N unconditional jump to "location" table entry
jz N pop top integer; if zero, jump to location
jnz N pop top integer; if not zero, jump to location
  (maybe add jl/jg or jp/jn ?)
bcall function call a entry N from the "builtin function" table. The function must determine its own arity.
   Most functions take a fixed number of arguments - default arguments are added by the compiler.
   A few take a variable number, such as menu, and have their own way of indicating this - such as
   pushing the number of arguments onto the integer stack before the call.
ret 0 Computed jump to top location on location stack.
op arg description
li integer push an integer from the integer constant table
ls string push a string from the string constant table
ll location push a location from the label/user function table
lparam name load a pc special parameter, as given in the table
sparam name store a pc special parameter, as given in the table
lpgr name load a persistent player variable
spgr name store a persistent player variable
lpr name load a temporary player integer variable
spr name store a temporary player integer variable
lprs name load a temporary player string variable
sprs name store a temporary player string variable
lpa1r name load an account variable (#)
spa1r name store an account variable (#)
lpa2r name load an account variable (##)
spa2r name store an account variable (##)
lgr name load a global variable (whether persistent or temporary - may change)
sgr name store a global variable (whether persistent or temporary - may change)
lgrs name load a global string variable (whether persistent or temporary - may change)
sgrs name store a global string variable (whether persistent or temporary - may change)

Example

function DailyQuestPoints
    # lb gettimetick
    li 2
    bcall gettimetick # 1
    li 86400
    sub
    spr @dq_earliest

    lpgr DailyQuestTime
    lpr @dq_earliest
    lt
    jnz .L0
        lpr @dq_earliest
        spgr DailyQuestTime

label .L0
    # lb gettimetick
    li 2
    bcall gettimetick # 1
    lpgr DailyQuestTime
    sub
    lpgr BaseLevel
    mul
    li 86400
    div
    spr @dq_increments

    lpgr DailyQuestTime
    lpr @dq_increments
    li 86400
    mul
    lparam BaseLevel
    div
    add
    spgr DailyQuestTime

    lpgr DailyQuestPoints
    lparam BaseLevel
    ge
    jnz L_Bonus

    lpgr DailyQuestPoints
    lpr @dq_increments
    add
    spgr DailyQuestPoints

    lpgr DailyQuestPoints
    lparam BaseLevel
    gt
    jz .L1
        lparam BaseLevel
        spgr DailyQuestPoints

label .L1
label L_Bonus
    lpgr DailyQuestPoints
    lpgr DailyQuestBonus
    add
    spgr DailyQuestPoints

    li 0
    spgr DailyQuestBonus

    ret



function DailyQuest
    ucall DailyQuestPoints

    lparam BaseLevel
    lpr @dq_level
    lt
    jnz L_Low_Level

    lpgr DailyQuestPoints
    lpr @dq_cost
    lt
    jnz L_Not_Enough_Points

    # lb mes
    ls "\"If you bring me "
    lpr @dq_count
    i2s
    ls " "
    lprs @dq_friendly_name$
    ls ", I will give you a reward.\""
    cat 5
    # or maybe drop cat and instead: li 5
    bcall mes # 1

    # lb menu
    ls "I have what you want."
    ll L_Trade
    ls "Ok, I'll get to work."
    ll .L0
    ls "Nah, I'm not going to help you."
    ll .L0
    li 3
    bcall menu


.L0
    li 1
    spr @dq_return

    j L_Exit


label L_Trade
    # lb countitem
    lprs @dq_name$
    bcall countitem # 1
    lpr @dq_count
    lt
    jnz L_Not_Enough

    # lb delitem
    lprs @dq_name$
    lpr @dq_count
    bcall delitem # 2

    lparam Zeny
    lpr @dq_money
    add
    sparam Zeny

    lb getexp
    lpr @dq_exp
    li 0
    bcall getexp # 2

    lpgr DailyQuestPoints
    lpr @dq_cost
    sub
    spr DailyQuestPoints

    lpr @dq_handle_return
    jnz L_Exit_Good

    # lb mes
    ls "\"Thank you!\""
    # maybe: li 1
    # or bcall mes1 ?
    bcall mes #

    ucallsub S_SayPhrase

    # lb mes
    ls ""
    # li 1
    bcall mes # 1

    # lb mes
    ls "["
    lpr @dq_money
    i2s
    ls " money]"
    cat 3
    # li 1
    bcall mes # 1

    # lb mes
    ls "["
    lpr @dq_exp
    i2s
    ls " experience points]"
    cat 3
    # li 3
    bcall mes # 1

label L_Exit_Good
    li 4
    spr @dq_return
    j L_Exit


label L_Not_Enough
    lpr @dq_handle_return
    jnz .L2
        lb mes
        ls "\"I said "
        lpr @dq_count
        i2s
        ls " "
        lprs @dq_friendly_name$
        ls "; you should learn to count.\""
        cat 5
        bcall mes 1

label .L2
    li 3
    spr @dq_return

    j L_Exit


label L_Low_Level
    lpr @dq_handle_return
    jnz .L3
        # lb mes
        ls "\"Hey, you should go kill some things to get stronger first.\""
        # li 1
        bcall mes # 1

label .L3
    li 0
    spr @dq_return

    j L_Exit


label L_Not_Enough_Points
    # lb mes
    ls "\"You look exhausted, maybe you should rest a bit.\""
    # li 1
    bcall mes # 1

    li 2
    spr @dq_return

    j L_Exit


label L_Exit
    li 0
    spr @dq_handle_return

    ret


function DailyQuest.S_SayPhrase
    lpr @dq_handle_return
    jz .L4

    ret


label .L4
    lpgr DailyQuestPoints
    lpr @dq_cost
    lt
    jnz L_Exhausted

    lpgr DailyQuestPoints
    lparam BaseLevel
    gt
    jnz L_Over

    lpgr DailyQuestPoints
    lparam BaseLevel
    li 9
    mul
    li 10
    div
    gt
    jnz L_P90

    lpgr DailyQuestPoints
    lparam BaseLevel
    li 7
    mul
    li 10
    div
    gt
    jnz L_P70

    lpgr DailyQuestPoints
    lparam BaseLevel
    li 5
    mul
    li 10
    div
    gt
    jnz L_P50

    j L_Low


label L_Over
    # lb mes
    ls "\"Woah, you're bursting with power.\""
    # li 1
    bcall mes # 1

    ret

label L_P90
    # lb mes
    ls "\"You're in a very good shape.\""
    # li 1
    bcall mes # 1

    ret

label L_P70
    # lb mes
    ls "\"You don't seem very exhausted by my tasks.\""
    # li 1
    bcall mes # 1

    ret

label L_P50
    # lb mes
    ls "\"Aren't you getting weary yet?\""
    # li 1
    bcall mes # 1

    ret

label L_Low
    # lb mes
    ls "\"You look a little tired.\""
    # li 1
    bcall mes # 1

    ret

label L_Exhausted
    # lb mes
    ls "\"You look exhausted, maybe you should rest a bit.\""
    # li 1
    bcall mes # 1

    ret