User:O11c/asm
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[edit]
| 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[edit]
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