1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-02-27 03:24:45 +00:00
oot/include/audio/aseq.h
Dragorn421 70fde8ac3b
Macroify MML opcodes (#2485)
* add defines for sequence opcodes

* name aseq_cmd_id defines from the corresponding macro names

* ASEQ_CMD_ID_ -> ASEQ_OPC_

* remove previous opcode defines

* ASEQ_OPC_LAYER_STEP3_ -> ASEQ_OPC_LAYER_

* sync with MM and fixups

* format

* ASEQ_OPC_ -> ASEQ_OP_

* ASEQ_OP_CTRLFLOW_ -> ASEQ_OP_

* tab-align

* ASEQ_OP_SEQUENCE_ -> ASEQ_OP_SEQ_

* ASEQ_OP_CHANNEL_ -> ASEQ_OP_CHAN_

* define MML_VERSION in the Makefile for seqplayer.c
2025-02-23 06:43:15 -05:00

2517 lines
63 KiB
GAS

/**
* @file aseq.h
* Zelda64 Music Macro Language (MML) Assembler Definitions
*
* This file implements the assembler for Zelda64 Music Macro Language. MML is an interpreted language that combines
* general MIDI with arithmetic and control flow instructions.
*
* There are multiple distinct sections:
* - .sequence: The top level of the program, this is unique and executes sequentially.
* - .channel: Spawned by the top-level sequence, there can be up to 16 channels active at any time that execute in
* parallel. Channels map directly to MIDI channels, control changes made in channels affect the notes
* in the layers spawned by the channel.
* - .layer: Spawned by channels, up to 4 layers per channel. These execute in parallel. The purpose of layers is
* to allow overlapping notes.
* - .table: Contains dyntable labels.
* - .array: Contains array-like data that can be read using sequence IO instructions.
* - .filter: Contains filter structures.
* - .envelope: Contains envelope scripts.
* - .buffer: Contains arbitrary data.
*
* Execution begins in the sequence section at position 0 of the sequence with no channels or layers initialized.
*
* Execution flows until it blocks on certain commands that induce delays for a fixed number of ticks (it is to be
* understood that ticks in this context refers to seqTicks in code), such as the delay instructions or note
* instructions. If this happens in a channel or layer, the other channels/layers will continue execution until they
* hit their own delays.
*
* Sequences can self-modify. The ldseq, stseq and related instructions can perform loads and stores to any location
* in a sequence, including the executable sections. This can be used to make up for the lack of registers and
* arithmetic instructions by replacing immediate values in the instructions themselves.
*
* The maximum call depth is 4. Call depth applies to both loops and subroutines. Loops are implemented like
* subroutines in that starting a loop pushes a return address onto the call stack and reaching the end of the loop
* decrements the loop counter. If the loop has iterations left, it jumps to the return address. When a loop completes
* all the iterations, or when the break instruction is executed, the return address is popped from the call stack and
* no jump occurs.
*
* In the instruction descriptions, we use a number of conventions for referring to various internal state:
* - PC : The location of the current instruction, within the respective sequence/channel/layer
* - SEQ : The sequence data, viewed as an array of s8/u8
* - TR : Temporary s8 register
* - TP : Temporary u16 register
* - CIO[15..0][7..0] : Channel IO
* - SIO[7..0] : Sequence IO
* - DYNTBL : Current dyntable, DYNTBL16 and DYNTBL8 are different views into the same memory
* - DYNTBL16 : Current dyntable, viewed as an array of s16/u16
* - DYNTBL8 : Current dyntable, viewed as an array of s8/u8
* - CALLDEPTH : The current subroutine nesting level
* - SHORTVELTBL : Current short notes velocity table
* - SHORTGATETBL : Current short notes gate time table
*/
#ifndef ASEQ_H
#define ASEQ_H
/**
* MML Version
*/
#ifndef MML_VERSION
#error "MML version not defined, define MML_VERSION in the cpp invocation"
#endif
#define MML_VERSION_OOT 0
#define MML_VERSION_MM 1
/**
* IO Ports
*/
#define IO_PORT_0 0
#define IO_PORT_1 1
#define IO_PORT_2 2
#define IO_PORT_3 3
#define IO_PORT_4 4
#define IO_PORT_5 5
#define IO_PORT_6 6
#define IO_PORT_7 7
/**
* Parameter Constants
*/
#define TRUE 1
#define FALSE 0
#define PITCH_A0 0
#define PITCH_BF0 1
#define PITCH_B0 2
#define PITCH_C1 3
#define PITCH_DF1 4
#define PITCH_D1 5
#define PITCH_EF1 6
#define PITCH_E1 7
#define PITCH_F1 8
#define PITCH_GF1 9
#define PITCH_G1 10
#define PITCH_AF1 11
#define PITCH_A1 12
#define PITCH_BF1 13
#define PITCH_B1 14
#define PITCH_C2 15
#define PITCH_DF2 16
#define PITCH_D2 17
#define PITCH_EF2 18
#define PITCH_E2 19
#define PITCH_F2 20
#define PITCH_GF2 21
#define PITCH_G2 22
#define PITCH_AF2 23
#define PITCH_A2 24
#define PITCH_BF2 25
#define PITCH_B2 26
#define PITCH_C3 27
#define PITCH_DF3 28
#define PITCH_D3 29
#define PITCH_EF3 30
#define PITCH_E3 31
#define PITCH_F3 32
#define PITCH_GF3 33
#define PITCH_G3 34
#define PITCH_AF3 35
#define PITCH_A3 36
#define PITCH_BF3 37
#define PITCH_B3 38
#define PITCH_C4 39
#define PITCH_DF4 40
#define PITCH_D4 41
#define PITCH_EF4 42
#define PITCH_E4 43
#define PITCH_F4 44
#define PITCH_GF4 45
#define PITCH_G4 46
#define PITCH_AF4 47
#define PITCH_A4 48
#define PITCH_BF4 49
#define PITCH_B4 50
#define PITCH_C5 51
#define PITCH_DF5 52
#define PITCH_D5 53
#define PITCH_EF5 54
#define PITCH_E5 55
#define PITCH_F5 56
#define PITCH_GF5 57
#define PITCH_G5 58
#define PITCH_AF5 59
#define PITCH_A5 60
#define PITCH_BF5 61
#define PITCH_B5 62
#define PITCH_C6 63
#define PITCH_DF6 64
#define PITCH_D6 65
#define PITCH_EF6 66
#define PITCH_E6 67
#define PITCH_F6 68
#define PITCH_GF6 69
#define PITCH_G6 70
#define PITCH_AF6 71
#define PITCH_A6 72
#define PITCH_BF6 73
#define PITCH_B6 74
#define PITCH_C7 75
#define PITCH_DF7 76
#define PITCH_D7 77
#define PITCH_EF7 78
#define PITCH_E7 79
#define PITCH_F7 80
#define PITCH_GF7 81
#define PITCH_G7 82
#define PITCH_AF7 83
#define PITCH_A7 84
#define PITCH_BF7 85
#define PITCH_B7 86
#define PITCH_C8 87
#define PITCH_DF8 88
#define PITCH_D8 89
#define PITCH_EF8 90
#define PITCH_E8 91
#define PITCH_F8 92
#define PITCH_GF8 93
#define PITCH_G8 94
#define PITCH_AF8 95
#define PITCH_A8 96
#define PITCH_BF8 97
#define PITCH_B8 98
#define PITCH_C9 99
#define PITCH_DF9 100
#define PITCH_D9 101
#define PITCH_EF9 102
#define PITCH_E9 103
#define PITCH_F9 104
#define PITCH_GF9 105
#define PITCH_G9 106
#define PITCH_AF9 107
#define PITCH_A9 108
#define PITCH_BF9 109
#define PITCH_B9 110
#define PITCH_C10 111
#define PITCH_DF10 112
#define PITCH_D10 113
#define PITCH_EF10 114
#define PITCH_E10 115
#define PITCH_F10 116
#define PITCH_BFNEG1 117
#define PITCH_BNEG1 118
#define PITCH_C0 119
#define PITCH_DF0 120
#define PITCH_D0 121
#define PITCH_EF0 122
#define PITCH_E0 123
#define PITCH_F0 124
#define PITCH_GF0 125
#define PITCH_G0 126
#define PITCH_AF0 127
// Hardcoded Instruments
#define FONTANY_INSTR_SFX 126
#define FONTANY_INSTR_DRUM 127
// Instruments implemented in gWaveSamples
#define FONTANY_INSTR_SAWTOOTH 128
#define FONTANY_INSTR_TRIANGLE 129
#define FONTANY_INSTR_SINE 130
#define FONTANY_INSTR_SQUARE 131
#define FONTANY_INSTR_NOISE 132
#define FONTANY_INSTR_BELL 133
#define FONTANY_INSTR_8PULSE 134
#define FONTANY_INSTR_4PULSE 135
#define FONTANY_INSTR_ASM_NOISE 136
/**
* Command Opcode IDs
*/
// control flow commands
#define ASEQ_OP_CONTROL_FLOW_FIRST 0xF2
#define ASEQ_OP_RBLTZ 0xF2
#define ASEQ_OP_RBEQZ 0xF3
#define ASEQ_OP_RJUMP 0xF4
#define ASEQ_OP_BGEZ 0xF5
#define ASEQ_OP_BREAK 0xF6
#define ASEQ_OP_LOOPEND 0xF7
#define ASEQ_OP_LOOP 0xF8
#define ASEQ_OP_BLTZ 0xF9
#define ASEQ_OP_BEQZ 0xFA
#define ASEQ_OP_JUMP 0xFB
#define ASEQ_OP_CALL 0xFC
#define ASEQ_OP_DELAY 0xFD
#define ASEQ_OP_DELAY1 0xFE
#define ASEQ_OP_END 0xFF
// sequence commands
#define ASEQ_OP_SEQ_TESTCHAN 0x00 // low nibble used as argument
#define ASEQ_OP_SEQ_STOPCHAN 0x40 // low nibble used as argument
#define ASEQ_OP_SEQ_SUBIO 0x50 // low nibble used as argument
#define ASEQ_OP_SEQ_LDRES 0x60 // low nibble used as argument
#define ASEQ_OP_SEQ_STIO 0x70 // low nibble used as argument
#define ASEQ_OP_SEQ_LDIO 0x80 // low nibble used as argument
#define ASEQ_OP_SEQ_LDCHAN 0x90 // low nibble used as argument
#define ASEQ_OP_SEQ_RLDCHAN 0xA0 // low nibble used as argument
#define ASEQ_OP_SEQ_LDSEQ 0xB0 // low nibble used as argument
#if (MML_VERSION == MML_VERSION_MM)
#define ASEQ_OP_SEQ_C2 0xC2
#define ASEQ_OP_SEQ_C3 0xC3
#endif
#define ASEQ_OP_SEQ_RUNSEQ 0xC4
#define ASEQ_OP_SEQ_SCRIPTCTR 0xC5
#define ASEQ_OP_SEQ_STOP 0xC6
#define ASEQ_OP_SEQ_STSEQ 0xC7
#define ASEQ_OP_SEQ_SUB 0xC8
#define ASEQ_OP_SEQ_AND 0xC9
#define ASEQ_OP_SEQ_LDI 0xCC
#define ASEQ_OP_SEQ_DYNCALL 0xCD
#define ASEQ_OP_SEQ_RAND 0xCE
#define ASEQ_OP_SEQ_NOTEALLOC 0xD0
#define ASEQ_OP_SEQ_LDSHORTGATEARR 0xD1
#define ASEQ_OP_SEQ_LDSHORTVELARR 0xD2
#define ASEQ_OP_SEQ_MUTEBHV 0xD3
#define ASEQ_OP_SEQ_MUTE 0xD4
#define ASEQ_OP_SEQ_MUTESCALE 0xD5
#define ASEQ_OP_SEQ_FREECHAN 0xD6
#define ASEQ_OP_SEQ_INITCHAN 0xD7
#define ASEQ_OP_SEQ_VOLSCALE 0xD9
#define ASEQ_OP_SEQ_VOLMODE 0xDA
#define ASEQ_OP_SEQ_VOL 0xDB
#define ASEQ_OP_SEQ_TEMPOCHG 0xDC
#define ASEQ_OP_SEQ_TEMPO 0xDD
#define ASEQ_OP_SEQ_RTRANSPOSE 0xDE
#define ASEQ_OP_SEQ_TRANSPOSE 0xDF
#define ASEQ_OP_SEQ_EF 0xEF
#define ASEQ_OP_SEQ_FREENOTELIST 0xF0
#define ASEQ_OP_SEQ_ALLOCNOTELIST 0xF1
// channel commands
#define ASEQ_OP_CHAN_CDELAY 0x00 // low nibble used as argument
#define ASEQ_OP_CHAN_LDSAMPLE 0x10 // low nibble used as argument
#define ASEQ_OP_CHAN_LDCHAN 0x20 // low nibble used as argument
#define ASEQ_OP_CHAN_STCIO 0x30 // low nibble used as argument
#define ASEQ_OP_CHAN_LDCIO 0x40 // low nibble used as argument
#define ASEQ_OP_CHAN_SUBIO 0x50 // low nibble used as argument
#define ASEQ_OP_CHAN_LDIO 0x60 // low nibble used as argument
#define ASEQ_OP_CHAN_STIO 0x70 // lower 3 bits used as argument
#define ASEQ_OP_CHAN_RLDLAYER 0x78 // lower 3 bits used as argument
#define ASEQ_OP_CHAN_TESTLAYER 0x80 // lower 3 bits used as argument
#define ASEQ_OP_CHAN_LDLAYER 0x88 // lower 3 bits used as argument
#define ASEQ_OP_CHAN_DELLAYER 0x90 // lower 3 bits used as argument
#define ASEQ_OP_CHAN_DYNLDLAYER 0x98 // lower 3 bits used as argument
#if (MML_VERSION == MML_VERSION_MM)
#define ASEQ_OP_CHAN_A0 0xA0
#define ASEQ_OP_CHAN_A1 0xA1
#define ASEQ_OP_CHAN_A2 0xA2
#define ASEQ_OP_CHAN_A3 0xA3
#define ASEQ_OP_CHAN_A4 0xA4
#define ASEQ_OP_CHAN_A5 0xA5
#define ASEQ_OP_CHAN_A6 0xA6
#define ASEQ_OP_CHAN_A7 0xA7
#define ASEQ_OP_CHAN_RANDPTR 0xA8
#endif
#define ASEQ_OP_CHAN_LDFILTER 0xB0
#define ASEQ_OP_CHAN_FREEFILTER 0xB1
#define ASEQ_OP_CHAN_LDSEQTOPTR 0xB2
#define ASEQ_OP_CHAN_FILTER 0xB3
#define ASEQ_OP_CHAN_PTRTODYNTBL 0xB4
#define ASEQ_OP_CHAN_DYNTBLTOPTR 0xB5
#define ASEQ_OP_CHAN_DYNTBLV 0xB6
#define ASEQ_OP_CHAN_RANDTOPTR 0xB7
#define ASEQ_OP_CHAN_RAND 0xB8
#define ASEQ_OP_CHAN_RANDVEL 0xB9
#define ASEQ_OP_CHAN_RANDGATE 0xBA
#define ASEQ_OP_CHAN_COMBFILTER 0xBB
#define ASEQ_OP_CHAN_PTRADD 0xBC
#if (MML_VERSION == MML_VERSION_OOT)
#define ASEQ_OP_CHAN_RANDPTR 0xBD
#endif
#if (MML_VERSION == MML_VERSION_MM)
#define ASEQ_OP_CHAN_SAMPLESTART 0xBD
#define ASEQ_OP_CHAN_UNK_BE 0xBE
#endif
#define ASEQ_OP_CHAN_INSTR 0xC1
#define ASEQ_OP_CHAN_DYNTBL 0xC2
#define ASEQ_OP_CHAN_SHORT 0xC3
#define ASEQ_OP_CHAN_NOSHORT 0xC4
#define ASEQ_OP_CHAN_DYNTBLLOOKUP 0xC5
#define ASEQ_OP_CHAN_FONT 0xC6
#define ASEQ_OP_CHAN_STSEQ 0xC7
#define ASEQ_OP_CHAN_SUB 0xC8
#define ASEQ_OP_CHAN_AND 0xC9
#define ASEQ_OP_CHAN_MUTEBHV 0xCA
#define ASEQ_OP_CHAN_LDSEQ 0xCB
#define ASEQ_OP_CHAN_LDI 0xCC
#define ASEQ_OP_CHAN_STOPCHAN 0xCD
#define ASEQ_OP_CHAN_LDPTR 0xCE
#define ASEQ_OP_CHAN_STPTRTOSEQ 0xCF
#define ASEQ_OP_CHAN_EFFECTS 0xD0
#define ASEQ_OP_CHAN_NOTEALLOC 0xD1
#define ASEQ_OP_CHAN_SUSTAIN 0xD2
#define ASEQ_OP_CHAN_BEND 0xD3
#define ASEQ_OP_CHAN_REVERB 0xD4
#define ASEQ_OP_CHAN_VIBFREQ 0xD7
#define ASEQ_OP_CHAN_VIBDEPTH 0xD8
#define ASEQ_OP_CHAN_RELEASERATE 0xD9
#define ASEQ_OP_CHAN_ENV 0xDA
#define ASEQ_OP_CHAN_TRANSPOSE 0xDB
#define ASEQ_OP_CHAN_PANWEIGHT 0xDC
#define ASEQ_OP_CHAN_PAN 0xDD
#define ASEQ_OP_CHAN_FREQSCALE 0xDE
#define ASEQ_OP_CHAN_VOL 0xDF
#define ASEQ_OP_CHAN_VOLEXP 0xE0
#define ASEQ_OP_CHAN_VIBFREQGRAD 0xE1
#define ASEQ_OP_CHAN_VIBDEPTHGRAD 0xE2
#define ASEQ_OP_CHAN_VIBDELAY 0xE3
#define ASEQ_OP_CHAN_DYNCALL 0xE4
#define ASEQ_OP_CHAN_REVERBIDX 0xE5
#define ASEQ_OP_CHAN_SAMPLEBOOK 0xE6
#define ASEQ_OP_CHAN_LDPARAMS 0xE7
#define ASEQ_OP_CHAN_PARAMS 0xE8
#define ASEQ_OP_CHAN_NOTEPRI 0xE9
#define ASEQ_OP_CHAN_STOP 0xEA
#define ASEQ_OP_CHAN_FONTINSTR 0xEB
#define ASEQ_OP_CHAN_VIBRESET 0xEC
#define ASEQ_OP_CHAN_GAIN 0xED
#define ASEQ_OP_CHAN_BENDFINE 0xEE
#define ASEQ_OP_CHAN_FREENOTELIST 0xF0
#define ASEQ_OP_CHAN_ALLOCNOTELIST 0xF1
// layer commands
#define ASEQ_OP_LAYER_NOTEDVG 0x00
#define ASEQ_OP_LAYER_NOTEDV 0x40
#define ASEQ_OP_LAYER_NOTEVG 0x80
#define ASEQ_OP_LAYER_LDELAY 0xC0
#define ASEQ_OP_LAYER_SHORTVEL 0xC1
#define ASEQ_OP_LAYER_TRANSPOSE 0xC2
#define ASEQ_OP_LAYER_SHORTDELAY 0xC3
#define ASEQ_OP_LAYER_LEGATO 0xC4
#define ASEQ_OP_LAYER_NOLEGATO 0xC5
#define ASEQ_OP_LAYER_INSTR 0xC6
#define ASEQ_OP_LAYER_PORTAMENTO 0xC7
#define ASEQ_OP_LAYER_NOPORTAMENTO 0xC8
#define ASEQ_OP_LAYER_SHORTGATE 0xC9
#define ASEQ_OP_LAYER_NOTEPAN 0xCA
#define ASEQ_OP_LAYER_ENV 0xCB
#define ASEQ_OP_LAYER_NODRUMPAN 0xCC
#define ASEQ_OP_LAYER_STEREO 0xCD
#define ASEQ_OP_LAYER_BENDFINE 0xCE
#define ASEQ_OP_LAYER_RELEASERATE 0xCF
#define ASEQ_OP_LAYER_LDSHORTVEL 0xD0 // low nibble used as an argument
#define ASEQ_OP_LAYER_LDSHORTGATE 0xE0 // low nibble used as an argument
#if (MML_VERSION == MML_VERSION_MM)
#define ASEQ_OP_LAYER_F0 0xF0
#define ASEQ_OP_LAYER_F1 0xF1
#endif
#ifdef _LANGUAGE_ASEQ
/**
* IDENT
*/
.ident "Zelda64 Audio Sequence compiled with GNU AS + aseq.h"
#if (MML_VERSION == MML_VERSION_OOT)
.ident "MML VERSION OOT"
#else
.ident "MML VERSION MM"
#endif
/**
* Sequence IDs
*/
// Some sequence instructions such as runseq would like sequence names for certain arguments.
// This facilitates making the sequence enum names available in sequence assembly files.
.set __SEQ_ID_CTR, 0
#define DEFINE_SEQUENCE(name, seqId, storageMedium, cachePolicy, seqFlags) \
.internal seqId; \
.set seqId, __SEQ_ID_CTR; \
.set __SEQ_ID_CTR, __SEQ_ID_CTR + 1
#define DEFINE_SEQUENCE_PTR(seqIdReal, seqId, storageMediumReal, cachePolicyReal, seqFlags) \
.internal seqId; \
.set seqId, __SEQ_ID_CTR; \
.set __SEQ_ID_CTR, __SEQ_ID_CTR + 1
#include "tables/sequence_table.h"
#undef DEFINE_SEQUENCE
#undef DEFINE_SEQUENCE_PTR
// Mark the counter as internal for the symbol table
.internal __SEQ_ID_CTR
/**
* STSEQ offsets
*/
#define STSEQ_HERE (. + 1)
// stores 1 byte (stseq)
#define STSEQ_LOOP_COUNT 1
#define STSEQ_VOL 1
#define STSEQ_PAN 1
#define STSEQ_REVERB 1
#define STSEQ_UNK_A4 1
#define STSEQ_FILTER_IDX 1
#define STSEQ_COMBFILTER_ARG1_HI 2
#define STSEQ_NOTEDV_OPCODE_PITCH 0
#define STSEQ_NOTEDV_DELAY 1
#define STSEQ_NOTEDV_DELAY_HI 1
#define STSEQ_NOTEDV_DELAY_LO 2
#define STSEQ_NOTEDV_VELOCITY 3
#define STSEQ_NOTEDV_VELOCITY_2 2
#define STSEQ_LDI_IMM 1
#define STSEQ_TRANSPOSITION 1
#define STSEQ_PORTAMENTO_MODE 1
#define STSEQ_PORTAMENTO_PITCH 2
#define STSEQ_PORTAMENTO_TIME 3
#define STSEQ_NOTEPAN 1
#define STSEQ_LDELAY 1
#define STSEQ_STSEQ_IMM 1
#define STSEQ_RAND 1
#define STSEQ_SURROUNDEFFECT 1
#define STSEQ_GENERAL_OPCODE 0
#define STSEQ_STEREO 1
#define STSEQ_BENDFINE 1
#define STSEQ_NOTEDVG_OPCODE_PITCH 0
#define STSEQ_NOTEDVG_DELAY_HI 1
#define STSEQ_NOTEDVG_DELAY_LO 2
#define STSEQ_NOTEDVG_VELOCITY_LONGDELAY 3
#define STSEQ_NOTEDVG_DELAY_SHORT 1
#define STSEQ_NOTEDVG_VELOCITY_SHORTDELAY 2
#define STSEQ_GAIN 1
#define STSEQ_INSTR 1
#define STSEQ_VIBDEPTH 1
#define STSEQ_SUB_IMM 1
#define STSEQ_INITCHAN_HI 1
#define STSEQ_INITCHAN_LO 2
#define STSEQ_VOLEXP 1
#define STSEQ_BEND 1
#define STSEQ_LDSEQ_SEQ_ID 1
// stores 2 bytes (stptrtoseq)
#define STSEQ_PTR_DYNTBL 1
#define STSEQ_PTR_LDLAYER 1
#define STSEQ_PTR_LDSEQ 1
#define STSEQ_PTR_STSEQ 2
// stores 2 bytes (so should be used with stptrtoseq)
#define STSEQ_ENVELOPE_POINT(n) (2 * (n))
/**
* Sequence Sections
*/
.internal ASEQ_MODE_NONE; .set ASEQ_MODE_NONE, 0
.internal ASEQ_MODE_SEQUENCE; .set ASEQ_MODE_SEQUENCE, 1
.internal ASEQ_MODE_CHANNEL; .set ASEQ_MODE_CHANNEL, 2
.internal ASEQ_MODE_LAYER; .set ASEQ_MODE_LAYER, 3
.internal ASEQ_MODE_TABLE; .set ASEQ_MODE_TABLE, 4
.internal ASEQ_MODE_ARRAY; .set ASEQ_MODE_ARRAY, 5
.internal ASEQ_MODE_FILTER; .set ASEQ_MODE_FILTER, 6
.internal ASEQ_MODE_ENVELOPE; .set ASEQ_MODE_ENVELOPE, 7
.internal ASEQ_MODE_BUFFER; .set ASEQ_MODE_BUFFER, 8
/* Macros that change structure based on current section */
.macro _RESET_SECTION
.purgem ldseq
.macro ldseq
.error "Invalid section for `ldseq`"
.endm
.purgem filter
.macro filter
.error "Invalid section for `filter`"
.endm
.purgem env
.macro env
.error "Invalid section for `env`"
.endm
.endm
/* Dummy macro definitions for above so purgem doesn't error on first run */
.macro ldseq
.endm
.macro filter
.endm
.macro env
.endm
/* Instantiate above macros */
_RESET_SECTION
/**
* Begin a sequence code section.
*/
.macro .sequence name
_RESET_SECTION
/* `ldseq` changes structure based on current section. */
.purgem ldseq
.macro ldseq ioPortNum, seqId, label
_wr_cmd_id ldseq, ASEQ_OP_SEQ_LDSEQ,,,,,,,, \ioPortNum, 4
_wr_u8 \seqId
_wr_lbl \label
.endm
.set ASEQ_MODE, ASEQ_MODE_SEQUENCE
\name:
.endm
/**
* Begin a channel code section.
*/
.macro .channel name
_RESET_SECTION
/* `ldseq` changes structure based on current section. */
.purgem ldseq
.macro ldseq label
_wr_cmd_id ldseq, ,ASEQ_OP_CHAN_LDSEQ,,,,,,, 0, 0
_wr_lbl \label
.endm
/* `filter` changes structure based on current section. */
.purgem filter
.macro filter lowpassCutoff, highpassCutoff
_check_arg_bitwidth_u \lowpassCutoff, 4
_check_arg_bitwidth_u \highpassCutoff, 4
_wr_cmd_id filter, ,ASEQ_OP_CHAN_FILTER,,,,,,, 0, 0
_wr_u8 (\lowpassCutoff << 4) | (\highpassCutoff)
.endm
/* `env` changes structure based on current section. */
.purgem env
.macro env label
_wr_cmd_id env, ,ASEQ_OP_CHAN_ENV,,,,,,, 0, 0
_wr_lbl \label
.endm
.set ASEQ_MODE, ASEQ_MODE_CHANNEL
\name:
.endm
/**
* Begin a layer code section.
*/
.macro .layer name
_RESET_SECTION
/* `env` changes structure based on current section. */
.purgem env
.macro env label, arg
_wr_cmd_id env, ,,ASEQ_OP_LAYER_ENV,,,,,, 0, 0
_wr_lbl \label
_wr_u8 \arg
.endm
.set ASEQ_MODE, ASEQ_MODE_LAYER
\name:
.endm
/**
* Begin a table section.
*/
.macro .table name
.balign 2
.table_unaligned \name
.endm
/**
* Begin a table section at an unaligned offset.
*/
.macro .table_unaligned name
_RESET_SECTION
.set ASEQ_MODE, ASEQ_MODE_TABLE
\name:
.endm
/**
* Begin an array section.
*/
.macro .array name
_RESET_SECTION
.set ASEQ_MODE, ASEQ_MODE_ARRAY
\name:
.endm
/**
* Begin a filter section.
*/
.macro .filter name
_RESET_SECTION
/* `filter` changes structure based on current section. */
.purgem filter
.macro filter a0, a1, a2, a3, a4, a5, a6, a7
_wr_s16 \a0
_wr_s16 \a1
_wr_s16 \a2
_wr_s16 \a3
_wr_s16 \a4
_wr_s16 \a5
_wr_s16 \a6
_wr_s16 \a7
.endm
.set ASEQ_MODE, ASEQ_MODE_FILTER
.balign 16
\name:
.endm
/**
* Begin an envelope section.
*/
.macro .envelope name
_RESET_SECTION
.set ASEQ_MODE, ASEQ_MODE_ENVELOPE
.balign 2
\name:
.endm
/**
* Begin a buffer section.
*/
.macro .buffer name
_RESET_SECTION
.set ASEQ_MODE, ASEQ_MODE_BUFFER
.balign 16
\name:
.endm
.macro _section_invalid macro_name
.if ASEQ_MODE == ASEQ_MODE_SEQUENCE
.error "Invalid section for `\macro_name`: Sequence"
.elseif ASEQ_MODE == ASEQ_MODE_CHANNEL
.error "Invalid section for `\macro_name`: Channel"
.elseif ASEQ_MODE == ASEQ_MODE_LAYER
.error "Invalid section for `\macro_name`: Layer"
.elseif ASEQ_MODE == ASEQ_MODE_TABLE
.error "Invalid section for `\macro_name`: Table"
.elseif ASEQ_MODE == ASEQ_MODE_ARRAY
.error "Invalid section for `\macro_name`: Array"
.elseif ASEQ_MODE == ASEQ_MODE_FILTER
.error "Invalid section for `\macro_name`: Filter"
.elseif ASEQ_MODE == ASEQ_MODE_ENVELOPE
.error "Invalid section for `\macro_name`: Envelope"
.elseif ASEQ_MODE == ASEQ_MODE_BUFFER
.error "Invalid section for `\macro_name`: Buffer"
.else
.error "Invalid section"
.endif
.endm
/**
* Sequence Start/End
*/
.macro .startseq name
/* Begin a sequence. */
/* Write the sequence name into a special .note.name section */
.pushsection .note.name, "", @note
.asciz "\name"
.popsection
/* Reset section and write start symbol. */
.section .data, "wa", @progbits
.set ASEQ_MODE, ASEQ_MODE_NONE
.balign 16
.global \name\()_Start
\name\()_Start:
_seq_start:
.endm
.macro .endseq name
/* End a sequence. Align to 16 bytes, write end and size symbols. */
.balign 16
_seq_end:
.global \name\()_End
\name\()_End:
.global \name\()_Size
.set \name\()_Size, _seq_end - _seq_start
/* Mark ASEQ_MODE as an internal symbol at the very end since it is redefined many times */
.internal ASEQ_MODE
.endm
/**
* Parameters
*/
.macro _check_arg_bitwidth_u value, num
/* Checks if the unsigned integer `value` fits within `num` bits. */
.if (\value & ((1 << \num) - 1)) != \value
.error "value \value out of range for unsigned \num\()-bit argument"
.endif
.endm
.macro _check_arg_bitwidth_s value, num
/* Checks if the signed integer `value` fits within `num` bits. */
.if (\value & ((1 << (\num - 1)) - 1)) - (\value & (1 << (\num - 1))) != \value
.error "value \value out of range for signed \num\()-bit argument"
.endif
.endm
/* Long encoded numbers a la MIDI */
.macro _var_long x
_check_arg_bitwidth_u \x, 15
.byte (0x80 | (\x & 0x7f00) >> 8), (\x & 0xff)
.endm
/* Potentially long encoded numbers, but may not be. */
.macro _var x
.if (\x >= 0x80)
_var_long \x
.else
_check_arg_bitwidth_u \x, 8
.byte \x
.endif
.endm
.macro _wr8 value
/* Write 8 bits */
.byte (\value) & 0xFF
.endm
.macro _wr16 value
/* Write 16 bits (big-endian) */
.byte (\value) >> 8, (\value) & 0xFF
.endm
.macro _wr_s8 value
/* Ensure the provided arg value fits in 8 bits (signed) */
_check_arg_bitwidth_s \value, 8
_wr8 \value
.endm
.macro _wr_u8 value
/* Ensure the provided arg value fits in 8 bits (unsigned) */
_check_arg_bitwidth_u \value, 8
_wr8 \value
.endm
.macro _wr_s16 value
/* Ensure the provided arg value fits in 16 bits (signed) */
_check_arg_bitwidth_s \value, 16
_wr16 \value
.endm
.macro _wr_u16 value
/* Ensure the provided arg value fits in 16 bits (unsigned) */
_check_arg_bitwidth_u \value, 16
_wr16 \value
.endm
.macro _wr_lbl value
/* Subtract sequence start label to get a numeric offset that can be written at assembling time */
_wr16 (\value - _seq_start)
.endm
.macro _wr_16_rel value
/* Turn label into relative offset.
* Can't check encoding, complains about non-constant value.. */
_wr16 (\value - $reladdr\@)
$reladdr\@:
.endm
.macro _wr_8_rel value
/* Turn label into relative offset.
* Can't check encoding, complains about non-constant value.. */
_wr8 (\value - $reladdr\@)
$reladdr\@:
.endm
.macro _wr_cmd_id cmd_name, seq_id=-1, chan_id=-1, layer_id=-1, tbl_id=-1, arr_id=-1, filt_id=-1, env_id=-1, buf_id=-1, lo_bits, n_lo_bits
/* Helper for writing command ids. Will write the command id appropriate for the current section, optionally
* packing in low-order bits if a command uses them for an argument. */
_check_arg_bitwidth_u \lo_bits, \n_lo_bits
.if ASEQ_MODE == ASEQ_MODE_SEQUENCE && \seq_id != -1
_check_arg_bitwidth_u \seq_id, 8
.byte (\seq_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_CHANNEL && \chan_id != -1
_check_arg_bitwidth_u \chan_id, 8
.byte (\chan_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_LAYER && \layer_id != -1
_check_arg_bitwidth_u \layer_id, 8
.byte (\layer_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_TABLE && \tbl_id != -1
_check_arg_bitwidth_u \tbl_id, 8
.byte (\tbl_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_ARRAY && \arr_id != -1
_check_arg_bitwidth_u \arr_id, 8
.byte (\arr_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_FILTER && \filt_id != -1
_check_arg_bitwidth_u \filt_id, 8
.byte (\filt_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_ENVELOPE && \env_id != -1
_check_arg_bitwidth_u \env_id, 8
.byte (\env_id & 0xFF) | (\lo_bits)
.elseif ASEQ_MODE == ASEQ_MODE_BUFFER && \buf_id != -1
_check_arg_bitwidth_u \buf_id, 8
.byte (\buf_id & 0xFF) | (\lo_bits)
.else
_section_invalid \cmd_name
.endif
.endm
.macro .ptr value
_wr_lbl \value
.endm
.macro .ptr_raw value
_wr16 \value
.endm
/**
* Instructions
*/
/**
* end
*
* Marks the end of an executable section.
*
* If CALLDEPTH is not 0 (that is, execution is currently in a subroutine)
* the current subroutine is exited and execution continues at the saved
* return address. If CALLDEPTH is 0, the current layer/channel/sequence is
* closed. If the sequence is closed, all execution ends. If a channel is
* closed, so are its layers.
*/
.macro end
_wr_cmd_id end, ASEQ_OP_END,ASEQ_OP_END,ASEQ_OP_END,,,,,, 0, 0
.endm
/**
* delay1
*
* Delays for one tick.
*/
.macro delay1
_wr_cmd_id delay1, ASEQ_OP_DELAY1,ASEQ_OP_DELAY1,,,,,,, 0, 0
.endm
/**
* delay <delay:var>
*
* Delays for `delay` ticks.
*/
.macro delay delay
_wr_cmd_id delay, ASEQ_OP_DELAY,ASEQ_OP_DELAY,,,,,,, 0, 0
_var \delay
.endm
/**
* call <label:lbl>
*
* Unconditionally enters a subroutine at `label`. Execution will resume following this instruction once the
* subroutine encounters an `end` instruction.
*/
.macro call label
_wr_cmd_id call, ASEQ_OP_CALL,ASEQ_OP_CALL,ASEQ_OP_CALL,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* jump <label:lbl>
*
* Branches to `label` unconditionally.
*/
.macro jump label
_wr_cmd_id jump, ASEQ_OP_JUMP,ASEQ_OP_JUMP,ASEQ_OP_JUMP,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* beqz <label:lbl>
*
* Branches to `label` if TR == 0.
*/
.macro beqz label
_wr_cmd_id beqz, ASEQ_OP_BEQZ,ASEQ_OP_BEQZ,ASEQ_OP_BEQZ,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* bltz <label:lbl>
*
* Branches to `label` if TR < 0.
*/
.macro bltz label
_wr_cmd_id beqz, ASEQ_OP_BLTZ,ASEQ_OP_BLTZ,ASEQ_OP_BLTZ,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* loop <num:u8>
*
* Begin a loop for `num` iterations, pushing the address of the next
* instruction to the call stack.
*
* Maximum loop nesting level is 4 - CALLDEPTH, after this the call stack
* becomes full.
*/
.macro loop num
_wr_cmd_id loop, ASEQ_OP_LOOP,ASEQ_OP_LOOP,ASEQ_OP_LOOP,,,,,, 0, 0
_wr_u8 \num
.endm
/**
* loopend
*
* Indicates the end of a loop body. Whenever the instruction is executed
* the loop counter decrements and either branches back to the loop start
* if there are remaining iterations or exits the loop if all iterations
* have been completed. When the loop is to be exited, the top of the call
* stack is popped.
*/
.macro loopend
_wr_cmd_id loopend, ASEQ_OP_LOOPEND,ASEQ_OP_LOOPEND,ASEQ_OP_LOOPEND,,,,,, 0, 0
.endm
/**
* break
*
* Immediately ends the current loop, popping it from the call stack, but
* does not branch to the end of the loop.
*
* Programming Note: Do not allow execution to later flow into loopend, as
* the call stack would be popped twice.
*/
.macro break
_wr_cmd_id break, ASEQ_OP_BREAK,ASEQ_OP_BREAK,ASEQ_OP_BREAK,,,,,, 0, 0
.endm
/**
* bgez <label:lbl>
*
* Branches to `label` if TR >= 0.
*/
.macro bgez label
_wr_cmd_id bgez, ASEQ_OP_BGEZ,ASEQ_OP_BGEZ,ASEQ_OP_BGEZ,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* rjump <label:lbl>
*
* Branches to `label` unconditionally.
* `label` is a relative offset rather than an absolute offset, making it appropriate
* for use in position-independent code.
* Note that the range is reduced compared to absolute jumps, only labels within a
* signed 8-bit (+/-128) range are reachable.
*/
.macro rjump label
_wr_cmd_id rjump, ASEQ_OP_RJUMP,ASEQ_OP_RJUMP,ASEQ_OP_RJUMP,,,,,, 0, 0
_wr_8_rel \label
.endm
/**
* rbeqz <label:lbl>
*
* Branches to `label` if TR == 0.
* `label` is a relative offset rather than an absolute offset, making it appropriate
* for use in position-independent code.
* Note that the range is reduced compared to absolute branches, only labels within a
* signed 8-bit (+/-128) range are reachable.
*/
.macro rbeqz label
_wr_cmd_id rbeqz, ASEQ_OP_RBEQZ,ASEQ_OP_RBEQZ,ASEQ_OP_RBEQZ,,,,,, 0, 0
_wr_8_rel \label
.endm
/**
* rbltz <label:lbl>
*
* Branches to `label` if TR < 0.
* `label` is a relative offset rather than an absolute offset, making it appropriate
* for use in position-independent code.
* Note that the range is reduced compared to absolute branches, only labels within a
* signed 8-bit (+/-128) range are reachable.
*/
.macro rbltz label
_wr_cmd_id rbltz, ASEQ_OP_RBLTZ,ASEQ_OP_RBLTZ,ASEQ_OP_RBLTZ,,,,,, 0, 0
_wr_8_rel \label
.endm
/**
* allocnotelist <num:u8>
*
* Clears the channel note pool and reallocates it with space for `num` notes.
*/
.macro allocnotelist num
_wr_cmd_id allocnotelist, ASEQ_OP_SEQ_ALLOCNOTELIST,ASEQ_OP_CHAN_ALLOCNOTELIST,,,,,,, 0, 0
_wr_u8 \num
.endm
/**
* freenotelist
*
* Clears the channel note pool.
*/
.macro freenotelist
_wr_cmd_id freenotelist, ASEQ_OP_SEQ_FREENOTELIST,ASEQ_OP_CHAN_FREENOTELIST,,,,,,, 0, 0
.endm
/**
* unk_EF
*
* Has no function.
*/
.macro unk_EF arg1, arg2
_wr_cmd_id unk_EF, ASEQ_OP_SEQ_EF,,,,,,,, 0, 0
_wr_s16 \arg1
_w_u8 \arg2
.endm
/**
* bendfine <amt:s8>
*
* Fine-tunes the pitch bend amount for the channel or layer.
*/
.macro bendfine amt
_wr_cmd_id bendfine, ,ASEQ_OP_CHAN_BENDFINE,ASEQ_OP_LAYER_BENDFINE,,,,,, 0, 0
_wr_s8 \amt
.endm
/**
* gain <value:u8>
*
* Sets the channel gain (multiplicative volume scale factor) to the provided qu4.4 fixed-point value.
*/
.macro gain value
_wr_cmd_id gain, ,ASEQ_OP_CHAN_GAIN,,,,,,, 0, 0
_wr_u8 \value
.endm
/**
* vibreset
*
* Resets channel vibrato, filter, gain, sustain, etc. state.
*/
.macro vibreset
_wr_cmd_id vibreset, ,ASEQ_OP_CHAN_VIBRESET,,,,,,, 0, 0
.endm
/**
* fontinstr <fontId:u8> <instId:u8>
*
* Updates the soundfont and instrument for the channel simultaneously.
*/
.macro fontinstr fontId, instId
_wr_cmd_id fontinstr, ,ASEQ_OP_CHAN_FONTINSTR,,,,,,, 0, 0
_wr_u8 \fontId
_wr_u8 \instId
.endm
/**
* notepri <priority1:u4> <priority2:u4>
*
* Set note priorities.
*/
.macro notepri priority1, priority2
_check_arg_bitwidth_u \priority1, 4
_check_arg_bitwidth_u \priority2, 4
_wr_cmd_id notepri, ,ASEQ_OP_CHAN_NOTEPRI,,,,,,, 0, 0
_wr_u8 (\priority1 << 4) | \priority2
.endm
/**
* params <muteBhv:u8> <noteAllocPolicy:u8> <channelPriority:u8> <transposition:u8>
* <pan:u8> <panWeight:u8> <reverb:u8> <reverbIndex:u8>
*
* Sets various channel parameters.
*/
.macro params muteBhv, noteAllocPolicy, channelPriority, transposition, pan, panWeight, reverb, reverbIndex
_wr_cmd_id params, ,ASEQ_OP_CHAN_PARAMS,,,,,,, 0, 0
_wr_u8 \muteBhv
_wr_u8 \noteAllocPolicy
_wr_u8 \channelPriority
_wr_u8 \transposition
_wr_u8 \pan
_wr_u8 \panWeight
_wr_u8 \reverb
_wr_u8 \reverbIndex
.endm
/**
* ldparams <label:lbl>
*
* Sets various channel parameters by loading them from `label`. The data
* is ordered in the same way as the arguments in `params`.
*/
.macro ldparams label
_wr_cmd_id ldparams, ,ASEQ_OP_CHAN_LDPARAMS,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* samplebook <value:u8>
*
* Sets the sample book mode.
*/
.macro samplebook value
_wr_cmd_id samplebook, ,ASEQ_OP_CHAN_SAMPLEBOOK,,,,,,, 0, 0
_wr_u8 \value
.endm
/**
* reverbidx <arg:u8>
*
* Sets the channel reverb.
*/
.macro reverbidx arg
_wr_cmd_id reverbidx, ,ASEQ_OP_CHAN_REVERBIDX,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* reverbidx <arg:u8>
*
* Sets the channel vibrato delay.
*/
.macro vibdelay arg
_wr_cmd_id vibdelay, ,ASEQ_OP_CHAN_VIBDELAY,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* vibdepthgrad <extentStart:u8> <extentTarget:u8> <extentChangeDelay:u8>
*
* Sets the vibrato extent.
*/
.macro vibdepthgrad arg0, arg1, arg2
_wr_cmd_id vibdepthgrad, ,ASEQ_OP_CHAN_VIBDEPTHGRAD,,,,,,, 0, 0
_wr_u8 \arg0
_wr_u8 \arg1
_wr_u8 \arg2
.endm
/**
* vibfreqgrad <rateStart:u8> <rateTarget:u8> <rateChangeDelay:u8>
*
* Sets the vibrato rate.
*/
.macro vibfreqgrad arg0, arg1, arg2
_wr_cmd_id vibfreqgrad, ,ASEQ_OP_CHAN_VIBFREQGRAD,,,,,,, 0, 0
_wr_u8 \arg0
_wr_u8 \arg1
_wr_u8 \arg2
.endm
/**
* volexp <amt:u8>
*
* Changes the expression amount for the channel.
*/
.macro volexp amt
_wr_cmd_id volexp, ,ASEQ_OP_CHAN_VOLEXP,,,,,,, 0, 0
_wr_u8 \amt
.endm
/**
* transpose <semitones:s8>
*
* Sets the transposition amount. Transposition shifts all note pitches by the
* provided number of semitones.
*/
.macro transpose semitones
_wr_cmd_id transpose, ASEQ_OP_SEQ_TRANSPOSE,ASEQ_OP_CHAN_TRANSPOSE,ASEQ_OP_LAYER_TRANSPOSE,,,,,, 0, 0
_wr_s8 \semitones
.endm
/**
* rtranspose <semitones:s8>
*
* Adjusts the transposition amount. This is only available at the top sequence level.
*/
.macro rtranspose semitones
_wr_cmd_id rtranspose, ASEQ_OP_SEQ_RTRANSPOSE,,,,,,,, 0, 0
_wr_s8 \semitones
.endm
/**
* freqscale <arg:s16>
*
* Sets the freqScale for the current channel.
*/
.macro freqscale arg
_wr_cmd_id freqscale, ,ASEQ_OP_CHAN_FREQSCALE,,,,,,, 0, 0
_wr_s16 \arg
.endm
/**
* tempo <bpm:u8>
*
* Changes the tempo of the sequence.
*/
.macro tempo bpm
_wr_cmd_id tempo, ASEQ_OP_SEQ_TEMPO,,,,,,,, 0, 0
_wr_u8 \bpm
.endm
/**
* tempochg <arg:s8>
*
* Sets the tempoChange for the sequence.
*/
.macro tempochg arg
_wr_cmd_id tempochg, ASEQ_OP_SEQ_TEMPOCHG,,,,,,,, 0, 0
_wr_s8 \arg
.endm
/**
* pan <amt:u8>
*
* Changes the pan amount for a channel.
*/
.macro pan pan
/* pan can only take values in 0..127 */
_check_arg_bitwidth_u \pan, 7
_wr_cmd_id pan, ,ASEQ_OP_CHAN_PAN,,,,,,, 0, 0
_wr_u8 \pan
.endm
/**
* panweight <weight:u8>
*
* Controls how much the final pan value is influenced by the channel pan and layer pan.
* The layer pan is set by the drum instrument pan.
* A value of 0 ignores channel pan, while a value of 127 ignores layer pan.
*
* finalPan = (weight * channelPan + (128 - weight) * layerPan) / 128
*/
.macro panweight weight
/* weight can only take values in 0..127 */
_check_arg_bitwidth_u \weight, 7
_wr_cmd_id panweight, ,ASEQ_OP_CHAN_PANWEIGHT,,,,,,, 0, 0
_wr_u8 \weight
.endm
/**
* vol <amt:u8>
*
* Sets the volume amount for this sequence or channel.
*/
.macro vol amt
_wr_cmd_id vol, ASEQ_OP_SEQ_VOL,ASEQ_OP_CHAN_VOL,,,,,,, 0, 0
_wr_u8 \amt
.endm
/**
* volmode <mode:u8> <fadeTimer:s16>
*
* TODO DESCRIPTION
*/
.macro volmode mode, fadeTimer
_wr_cmd_id volmode, ASEQ_OP_SEQ_VOLMODE,,,,,,,, 0, 0
_wr_u8 \mode
_wr_u16 \fadeTimer
.endm
/**
* volscale <arg:u8>
*
* Sets the fadeVolumeScale for the sequence.
*/
.macro volscale arg
_wr_cmd_id volscale, ASEQ_OP_SEQ_VOLSCALE,,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* releaserate <release:u8>
*
* Sets the envelope release rate for this channel or layer.
*/
.macro releaserate release
_wr_cmd_id releaserate, ,ASEQ_OP_CHAN_RELEASERATE,ASEQ_OP_LAYER_RELEASERATE,,,,,, 0, 0
_wr_u8 \release
.endm
/**
* vibdepth <arg:u8>
*
* Sets the vibrato depth for the channel.
*/
.macro vibdepth arg
_wr_cmd_id vibdepth, ,ASEQ_OP_CHAN_VIBDEPTH,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* vibfreq <arg:u8>
*
* Sets the vibrato rate for the channel.
*/
.macro vibfreq arg
_wr_cmd_id vibfreq, ,ASEQ_OP_CHAN_VIBFREQ,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* initchan <bitmask:u16>
*
* Initializes the channels marked in the provided bitmask.
*
* e.g. initchan 0b1 initializes channel 0.
* initchan 0b101 initializes channels 0 and 2.
*/
.macro initchan bitmask
_wr_cmd_id initchan, ASEQ_OP_SEQ_INITCHAN,,,,,,,, 0, 0
_wr_u16 \bitmask
.endm
/**
* freechan <bitmask:u16>
*
* Frees the channels marked in the provided bitmask.
*/
.macro freechan bitmask
_wr_cmd_id freechan, ASEQ_OP_SEQ_FREECHAN,,,,,,,, 0, 0
_wr_u16 \bitmask
.endm
/**
* mutescale <arg:s8>
*
* Sets the muteVolumeScale for the sequence.
*/
.macro mutescale arg
_wr_cmd_id mutescale, ASEQ_OP_SEQ_MUTESCALE,,,,,,,, 0, 0
_wr_s8 \arg
.endm
/**
* mute
*
* Mutes the sequence player.
*/
.macro mute
_wr_cmd_id mute, ASEQ_OP_SEQ_MUTE,,,,,,,, 0, 0
.endm
/**
* reverb <amt:u8>
*
* Sets the reverb amount for this channel.
*/
.macro reverb amt
_wr_cmd_id reverb, ,ASEQ_OP_CHAN_REVERB,,,,,,, 0, 0
_wr_u8 \amt
.endm
/**
* mutebhv <flags:u8>
*
* Sets mute behavior for this sequence or channel.
*/
.macro mutebhv flags
_wr_cmd_id mutebhv, ASEQ_OP_SEQ_MUTEBHV,ASEQ_OP_CHAN_MUTEBHV,,,,,,, 0, 0
_wr_u8 \flags
.endm
/**
* bend <amt:s8>
*
* Sets the pitch bend amount for this channel.
*/
.macro bend amt
_wr_cmd_id bend, ,ASEQ_OP_CHAN_BEND,,,,,,, 0, 0
_wr_s8 \amt
.endm
/**
* ldshortvelarr <label:lbl>
*
* Sets the location of SHORTVELTBL.
*/
.macro ldshortvelarr label
_wr_cmd_id ldshortvelarr, ASEQ_OP_SEQ_LDSHORTVELARR,,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* sustain <value:u8>
*
* Sets the adsr sustain value for this channel.
*/
.macro sustain value
_wr_cmd_id sustain, ,ASEQ_OP_CHAN_SUSTAIN,,,,,,, 0, 0
_wr_u8 \value
.endm
/**
* ldshortgatearr <label:lbl>
*
* Sets the location of SHORTGATETBL.
*/
.macro ldshortgatearr label
_wr_cmd_id ldshortgatearr, ASEQ_OP_SEQ_LDSHORTGATEARR,,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* notealloc <arg:u8>
*
* Sets the noteAllocPolicy for either the sequence or the current channel.
*/
.macro notealloc arg
_wr_cmd_id notealloc, ASEQ_OP_SEQ_NOTEALLOC,ASEQ_OP_CHAN_NOTEALLOC,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* effects <headset:bool> <type:b2> <strongR:b1> <strongL:b1> <strongRvrbR:b1> <strongRvrbL:b1>
*
* Sets stereo effects.
*/
.macro effects headset, type, strongR, strongL, strongRvrbR, strongRvrbL
_check_arg_bitwidth_u \headset, 1
_check_arg_bitwidth_u \type, 2
_check_arg_bitwidth_u \strongR, 1
_check_arg_bitwidth_u \strongL, 1
_check_arg_bitwidth_u \strongRvrbR, 1
_check_arg_bitwidth_u \strongRvrbL, 1
_wr_cmd_id effects, ,ASEQ_OP_CHAN_EFFECTS,,,,,,, 0, 0
_wr_u8 (\headset << 7) | (\type << 4) | (\strongR << 3) | (\strongL << 2) | (\strongRvrbR << 1) | (\strongRvrbL << 0)
.endm
/**
* stptrtoseq <label:lbl>
*
* Stores TP -> label
*/
.macro stptrtoseq label
_wr_cmd_id stptrtoseq, ,ASEQ_OP_CHAN_STPTRTOSEQ,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* ldptr <label:lbl>
*
* Loads label -> TP
*/
.macro ldptr label
_wr_cmd_id ldptr, ,ASEQ_OP_CHAN_LDPTR,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* ldptr <imm:u16>
*
* Loads imm -> TP
*/
.macro ldptri imm
_wr_cmd_id ldptr, ,ASEQ_OP_CHAN_LDPTR,,,,,,, 0, 0
_wr_u16 \imm
.endm
/**
* rand <max:u8>
*
* Stores a random number in the range [0, max) into TR. If max is 0 the range is [0, 255]
*/
.macro rand max
_wr_cmd_id rand, ASEQ_OP_SEQ_RAND,ASEQ_OP_CHAN_RAND,,,,,,, 0, 0
_wr_u8 \max
.endm
/**
* [sequence] dyncall <table:lbl>
*
* Jumps to table[TR], treating table as a u16 array, pushing the next PC to the call stack
*
* [channel] dyncall
*
* Jumps to DYNTBL16[TR], pushing the next PC to the call stack
*/
.macro dyncall table=-1
.if \table == -1
_wr_cmd_id dyncall, ,ASEQ_OP_CHAN_DYNCALL,,,,,,, 0, 0
.else
_wr_cmd_id dyncall, ASEQ_OP_SEQ_DYNCALL,,,,,,,, 0, 0
_wr_lbl \table
.endif
.endm
/**
* ldi <imm:u8>
*
* Loads the immediate value `imm` into TR.
*/
.macro ldi imm
_wr_cmd_id ldi, ASEQ_OP_SEQ_LDI,ASEQ_OP_CHAN_LDI,,,,,,, 0, 0
_wr_u8 \imm
.endm
/**
* and <imm:u8>
*
* Computes TR = TR & imm
*/
.macro and imm
_wr_cmd_id and, ASEQ_OP_SEQ_AND,ASEQ_OP_CHAN_AND,,,,,,, 0, 0
_wr_u8 \imm
.endm
/**
* sub <imm:u8>
*
* Computes TR = TR - imm
*/
.macro sub imm
_wr_cmd_id sub, ASEQ_OP_SEQ_SUB,ASEQ_OP_CHAN_SUB,,,,,,, 0, 0
_wr_u8 \imm
.endm
/**
* stseq <imm:u8> <label:lbl>
*
* Stores the u8 value `TR + imm` to the location specified by `label`.
*/
.macro stseq imm, label
_wr_cmd_id stseq, ASEQ_OP_SEQ_STSEQ,ASEQ_OP_CHAN_STSEQ,,,,,,, 0, 0
_wr_u8 \imm
_wr_lbl \label
.endm
/**
* stop
*
* Immediately stops the sequence or channel.
*/
.macro stop
_wr_cmd_id stop, ASEQ_OP_SEQ_STOP,ASEQ_OP_CHAN_STOP,,,,,,, 0, 0
.endm
/**
* font <fontId:u8>
*
* Set the current soundfont for this channel to `fontId`.
*/
.macro font fontId
_wr_cmd_id font, ,ASEQ_OP_CHAN_FONT,,,,,,, 0, 0
_wr_u8 \fontId
.endm
/**
* scriptctr <arg:u16>
*
* Sets scriptCounter to arg.
*
* scriptCounter usually increments every time the sequence is updated but is otherwise
* never used, so changing it with this instruction has no useful effects.
*/
.macro scriptctr arg
_wr_cmd_id scriptctr, ASEQ_OP_SEQ_SCRIPTCTR,,,,,,,, 0, 0
_wr_u16 \arg
.endm
/**
* dyntbllookup
*
* Loads DYNTBL16[TR] -> DYNTBL
* unless TR is -1, in which case nothing happens.
*/
.macro dyntbllookup
_wr_cmd_id dyntbllookup, ,ASEQ_OP_CHAN_DYNTBLLOOKUP,,,,,,, 0, 0
.endm
/**
* runseq <seqPlayer:u8> <seqId:u8>
*
* Plays the sequence seqId on seqPlayer.
*/
.macro runseq seqPlayer, seqId
_wr_cmd_id runseq, ASEQ_OP_SEQ_RUNSEQ,,,,,,,, 0, 0
_wr_u8 \seqPlayer
_wr_u8 \seqId
.endm
#if (MML_VERSION == MML_VERSION_MM)
/**
* mutechan <arg0:s16>
*
* TODO DESCRIPTION
*/
.macro mutechan arg0
_wr_cmd_id mutechan, ASEQ_OP_SEQ_C3,,,,,,,, 0, 0
_wr_s16 \arg0
.endm
#endif
/**
* noshort
*
* Disable short notes encoding.
*/
.macro noshort
_wr_cmd_id noshort, ,ASEQ_OP_CHAN_NOSHORT,,,,,,, 0, 0
.endm
/**
* short
*
* Enable short notes encoding.
*/
.macro short
_wr_cmd_id short, ,ASEQ_OP_CHAN_SHORT,,,,,,, 0, 0
.endm
/**
* dyntbl <label:lbl>
*
* Loads label -> DYNTBL
*/
.macro dyntbl label
_wr_cmd_id dyntbl, ,ASEQ_OP_CHAN_DYNTBL,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* instr <instNum:u8>
*
* Set instrument `instNum` from the current soundfont as the active instrument for this channel or layer.
*/
.macro instr instNum
_wr_cmd_id instr, ,ASEQ_OP_CHAN_INSTR,ASEQ_OP_LAYER_INSTR,,,,,, 0, 0
_wr_u8 \instNum
.endm
#if (MML_VERSION == MML_VERSION_MM)
/**
* unk_BE <arg0:u8>
*
* TODO DESCRIPTION
*/
.macro unk_BE arg0
_wr_cmd_id unk_BE, ,ASEQ_OP_CHAN_UNK_BE,,,,,,, 0, 0
_wr_u8 \arg0
.endm
#endif
/**
* randptr <range:u16> <offset:u16>
*
* Assigns a random number sampled from [offset,offset+range) -> TP
*
* If range is 0, it is treated as 65536.
*/
.macro randptr range, offset
_wr_cmd_id randptr, ,ASEQ_OP_CHAN_RANDPTR,,,,,,, 0, 0
_wr_u16 \range
_wr_u16 \offset
.endm
#if (MML_VERSION == MML_VERSION_MM)
/**
* samplestart <arg0:u8>
*
* TODO DESCRIPTION
*/
.macro samplestart arg
_wr_cmd_id samplestart, ,ASEQ_OP_CHAN_SAMPLESTART,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* unk_A7 <arg:u8>
*
* TODO DESCRIPTION
*/
.macro unk_A7 arg
_wr_cmd_id unk_A7, ,ASEQ_OP_CHAN_A7,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* unk_A6 <arg0:u8> <arg1:s16>
*
* TODO DESCRIPTION
*/
.macro unk_A6 arg0, arg1
_wr_cmd_id unk_A6, ,ASEQ_OP_CHAN_A6,,,,,,, 0, 0
_wr_u8 \arg0
_wr_s16 \arg1
.endm
/**
* unk_A5
*
* TODO DESCRIPTION
*/
.macro unk_A5
_wr_cmd_id unk_A5, ,ASEQ_OP_CHAN_A5,,,,,,, 0, 0
.endm
/**
* unk_A4 <arg:u8>
*
* TODO DESCRIPTION
*/
.macro unk_A4 arg
_wr_cmd_id unk_A4, ,ASEQ_OP_CHAN_A4,,,,,,, 0, 0
_wr_u8 \arg
.endm
/**
* unk_A3
*
* TODO DESCRIPTION
*/
.macro unk_A3
_wr_cmd_id unk_A3, ,ASEQ_OP_CHAN_A3,,,,,,, 0, 0
.endm
/**
* unk_A2 <arg:s16>
*
* TODO DESCRIPTION
*/
.macro unk_A2 arg
_wr_cmd_id unk_A2, ,ASEQ_OP_CHAN_A2,,,,,,, 0, 0
_wr_s16 \arg
.endm
/**
* unk_A1
*
* TODO DESCRIPTION
*/
.macro unk_A1
_wr_cmd_id unk_A1, ,ASEQ_OP_CHAN_A1,,,,,,, 0, 0
.endm
/**
* unk_A0 <arg:s16>
*
* TODO DESCRIPTION
*/
.macro unk_A0 arg
_wr_cmd_id unk_A0, ,ASEQ_OP_CHAN_A0,,,,,,, 0, 0
_wr_s16 \arg
.endm
#endif
/**
* ptradd <value:lbl>
*
* Computes TP += value
*/
.macro ptradd value
_wr_cmd_id ptradd, ,ASEQ_OP_CHAN_PTRADD,,,,,,, 0, 0
_wr_lbl \value
.endm
/**
* ptraddi <value:u16>
*
* Like ptradd but for immediates instead of labels
*
* Computes TP += value
*/
.macro ptraddi value
_wr_cmd_id ptradd, ,ASEQ_OP_CHAN_PTRADD,,,,,,, 0, 0
_wr_u16 \value
.endm
/**
* combfilter <arg0:u8> <arg1:u16>
*
* Enable comb filter.
* TODO args? arg0=16,arg1=val<<8 maps well to midi chorus
*/
.macro combfilter arg0, arg1
_wr_cmd_id combfilter, ,ASEQ_OP_CHAN_COMBFILTER,,,,,,, 0, 0
_wr_u8 \arg0
_wr_u16 \arg1
.endm
/**
* randgate <range:u8>
*
* Sets the range of random note gateTime fluctuations.
*
* NOTE: This feature is bugged. If this is non-zero it will actually use the range set by randvel.
*/
.macro randgate range
_wr_cmd_id randgate, ,ASEQ_OP_CHAN_RANDGATE,,,,,,, 0, 0
_wr_u8 \range
.endm
/**
* randvel <range:u8>
*
* Sets the range for random note velocity fluctuations.
*/
.macro randvel range
_wr_cmd_id randvel, ,ASEQ_OP_CHAN_RANDVEL,,,,,,, 0, 0
_wr_u8 \range
.endm
/**
* rand <max:u16>
*
* Stores a random number in the range [0, max) into TP. If max is 0 the range is [0, 65535]
*/
.macro randtoptr max
_wr_cmd_id randtoptr, ,ASEQ_OP_CHAN_RANDTOPTR,,,,,,, 0, 0
_wr_u16 \max
.endm
/**
* dyntblv
*
* Loads DYNTBL8[TR] -> TR
*/
.macro dyntblv
_wr_cmd_id dyntblv, ,ASEQ_OP_CHAN_DYNTBLV,,,,,,, 0, 0
.endm
/**
* dyntbltoptr
*
* Loads DYNTBL16[TR] -> TP
*/
.macro dyntbltoptr
_wr_cmd_id dyntbltoptr, ,ASEQ_OP_CHAN_DYNTBLTOPTR,,,,,,, 0, 0
.endm
/**
* ptrtodyntbl
*
* Transfers TP -> DYNTBL
*/
.macro ptrtodyntbl
_wr_cmd_id ptrtodyntbl, ,ASEQ_OP_CHAN_PTRTODYNTBL,,,,,,, 0, 0
.endm
/**
* ldseqtoptr <label:lbl>
*
* Loads SEQ[label + TR * 2] -> TP
*
* Note that TR acts as an index into an array of u16 starting at label.
*/
.macro ldseqtoptr label
_wr_cmd_id ldseqtoptr, ,ASEQ_OP_CHAN_LDSEQTOPTR,,,,,,, 0, 0
_wr_lbl \label
.endm
/**
* freefilter
*
* Invalidates the current active filter buffer.
*/
.macro freefilter
_wr_cmd_id freefilter, ,ASEQ_OP_CHAN_FREEFILTER,,,,,,, 0, 0
.endm
/**
* ldfilter <filter:lbl>
*
* Sets the active filter buffer to the location specified by `filter`.
*/
.macro ldfilter filter
_wr_cmd_id ldfilter, ,ASEQ_OP_CHAN_LDFILTER,,,,,,, 0, 0
_wr_lbl \filter
.endm
/**
* cdelay <delay:b4>
*
* Short delay encoding for channel sections.
* Delays by `delay` ticks.
*/
.macro cdelay delay
_wr_cmd_id cdelay, ,ASEQ_OP_CHAN_CDELAY,,,,,,, \delay, 4
.endm
/**
* ldsample <portNum:b3>
*
* Triggers an async load of a sample belonging to either:
* - The Instrument ID in TR, if type is LDSAMPLE_INST.
* - The Sound Effect ID in TP, if type is LDSAMPLE_SFX.
*
* Load status is made available in CIO[CUR_CHANNEL][portNum].
*/
.macro ldsample type, portNum
.if \type == LDSAMPLE_INST
_wr_cmd_id ldsample, ,ASEQ_OP_CHAN_LDSAMPLE,,,,,,, \portNum, 3
.elif \type == LDSAMPLE_SFX
_wr_cmd_id ldsample, ,ASEQ_OP_CHAN_LDSAMPLE | 8,,,,,,, \portNum, 3
.else
.error "ldsample: invalid type"
.endif
.endm
#define LDSAMPLE_INST 0
#define LDSAMPLE_SFX 1
/**
* stcio <channelNum:b4> <portNum:u8>
*
* Stores the contents of TR into CIO[channelNum][portNum]
*/
.macro stcio channelNum, portNum
_wr_cmd_id stcio, ,ASEQ_OP_CHAN_STCIO,,,,,,, \channelNum, 4
_wr_u8 \portNum
.endm
/**
* ldcio <channelNum:b4> <portNum:u8>
*
* Loads the contents of CIO[channelNum][portNum] into TR.
*/
.macro ldcio channelNum, portNum
_wr_cmd_id ldcio, ,ASEQ_OP_CHAN_LDCIO,,,,,,, \channelNum, 4
_wr_u8 \portNum
.endm
/**
* rldlayer <layerNum:b3> <label:rel_lbl>
*
* Opens the note layer at `label` for index `layerNum`.
* `label` is a relative offset rather than an absolute offset, making it appropriate
* for use in position-independent code.
*/
.macro rldlayer layerNum, label
_wr_cmd_id rldlayer, ,ASEQ_OP_CHAN_RLDLAYER,,,,,,, \layerNum, 3
_wr_16_rel \label
.endm
/**
* testlayer <layerNum:b3>
*
* Tests if the layer specified by `layerNum` is finished.
* Stores result to TR:
* - 1 if layer is finished.
* - 0 if layer is not finished.
* - -1 if layer does not exist.
*/
.macro testlayer layerNum
_wr_cmd_id testlayer, ,ASEQ_OP_CHAN_TESTLAYER,,,,,,, \layerNum, 3
.endm
/**
* ldlayer <layerNum:b3> <label:lbl>
*
* Opens the note layer at `label` for index `layerNum`.
*/
.macro ldlayer layerNum, label
_wr_cmd_id ldlayer, ,ASEQ_OP_CHAN_LDLAYER,,,,,,, \layerNum, 3
_wr_lbl \label
.endm
/**
* dellayer <layerNum:b3>
*
* Deletes the layer specified by index `layerNum`.
*/
.macro dellayer arg
_wr_cmd_id dellayer, ,ASEQ_OP_CHAN_DELLAYER,,,,,,, \arg, 3
.endm
/**
* dynldlayer <arg:b3>
*
* Allocates a new layer starting at the pointer read from DYNTBL16[TR]
*/
.macro dynldlayer arg
_wr_cmd_id dynldlayer, ,ASEQ_OP_CHAN_DYNLDLAYER,,,,,,, \arg, 3
.endm
/**
* testchan <channelNum:b4>
*
* Tests if the channel specified by index `channelNum` is enabled.
* Stores result to TR:
* - 0 if enabled
* - 1 if disabled
*/
.macro testchan channelNum
_wr_cmd_id testchan, ASEQ_OP_SEQ_TESTCHAN,,,,,,,, \channelNum, 4
.endm
/**
* stopchan <channelNum:b4>
*
* Disables the channel specified by channel `channelNum`.
*/
.macro stopchan channelNum
.if ASEQ_MODE == ASEQ_MODE_SEQUENCE
_wr_cmd_id stopchan, ASEQ_OP_SEQ_STOPCHAN,,,,,,,, \channelNum, 4
.else
_wr_cmd_id stopchan, ,ASEQ_OP_CHAN_STOPCHAN,,,,,,, 0, 0
_wr_u8 \channelNum
.endif
.endm
/**
* subio <portNum:b4>
*
* In sequence section:
* Computes TR = TR - SIO[portNum]
*
* In channel section:
* Computes TR = TR - CIO[CUR_CHANNEL][portNum]
*/
.macro subio portNum
_wr_cmd_id subio, ASEQ_OP_SEQ_SUBIO,ASEQ_OP_CHAN_SUBIO,,,,,,, \portNum, 4
.endm
/**
* ldres <portNum:b4> <resType:u8> <resId:u8>
*
* Load Resource.
* resType (sync with SampleBankTableType)
* - 0 = Sequence
* - 1 = Font
* - 2 = Sample
*
* resId is either a sequence id, soundfont id or samplebank id.
*
* Load status is made available in SIO[portNum].
*/
.macro ldres portNum, resType, resId
_wr_cmd_id ldres, ASEQ_OP_SEQ_LDRES,,,,,,,, \portNum, 4
_wr_u8 \resType
_wr_u8 \resId
.endm
/**
* Sequence Section: stio <portNum:b4>
* Channel Section: stio <portNum:b3>
*
* Moves the contents of TR to either SIO[portNum] or CIO[CUR_CHANNEL][portNum]
* depending on current section.
*/
.macro stio portNum
.if ASEQ_MODE == ASEQ_MODE_CHANNEL
_wr_cmd_id stio, ,ASEQ_OP_CHAN_STIO,,,,,,, \portNum, 3
.else
_wr_cmd_id stio, ASEQ_OP_SEQ_STIO,,,,,,,, \portNum, 4
.endif
.endm
/**
* ldio <portNum:b4>
*
* Moves the contents of either SIO[portNum] or CIO[CUR_CHANNEL][portNum] to TR
* depending on current section.
*/
.macro ldio portNum
_wr_cmd_id ldio, ASEQ_OP_SEQ_LDIO,ASEQ_OP_CHAN_LDIO,,,,,,, \portNum, 4
.endm
/**
* ldchan <channelNum:b4> <label:lbl>
*
* Opens the sequence channel for index `channelNum` with data beginning at `label`.
*/
.macro ldchan channelNum, label
_wr_cmd_id ldchan, ASEQ_OP_SEQ_LDCHAN,ASEQ_OP_CHAN_LDCHAN,,,,,,, \channelNum, 4
_wr_lbl \label
.endm
/**
* rldchan <channelNum:b4> <label:rel_lbl>
*
* Opens the sequence channel for index `channelNum` with data beginning at `label`.
* `label` is a relative offset rather than an absolute offset, making it appropriate
* for use in position-independent code.
*/
.macro rldchan channelNum, label
_wr_cmd_id rldchan, ASEQ_OP_SEQ_RLDCHAN,,,,,,,, \channelNum, 4
_wr_16_rel \label
.endm
/**
* ldelay <delay:var>
*
* Delay for `delay` ticks.
*/
.macro ldelay delay
_wr_cmd_id ldelay, ,,ASEQ_OP_LAYER_LDELAY,,,,,, 0, 0
_var \delay
.endm
/**
* Workaround for bugged delays using a larger encoding than is needed.
* Acts like ldelay but forces a long var encoding even if the arg can fit
* in a smaller encoding.
* Should never be used when not required for matching purposes.
*/
.macro lldelay delay
_wr_cmd_id lldelay, ,ASEQ_OP_DELAY,ASEQ_OP_LAYER_LDELAY,,,,,, 0, 0
_var_long \delay
.endm
/**
* shortvel <velocity:u8>
*
* Set velocity used by short notes.
*/
.macro shortvel velocity
_wr_cmd_id shortvel, ,,ASEQ_OP_LAYER_SHORTVEL,,,,,, 0, 0
_wr_u8 \velocity
.endm
/**
* shortdelay <delay:var>
*
* Set delay used by short notes.
*/
.macro shortdelay delay
_wr_cmd_id shortdelay, ,,ASEQ_OP_LAYER_SHORTDELAY,,,,,, 0, 0
_var \delay
.endm
/**
* legato
*
* Enables legato on the current layer.
*/
.macro legato
_wr_cmd_id legato, ,,ASEQ_OP_LAYER_LEGATO,,,,,, 0, 0
.endm
/**
* nolegato
*
* Disables legato on the current layer.
*/
.macro nolegato
_wr_cmd_id nolegato, ,,ASEQ_OP_LAYER_NOLEGATO,,,,,, 0, 0
.endm
/**
* portamento <mode> <target:u8> <time:u8/var>
*
* The time argument is either a var or a u8 depending on mode
*/
.macro portamento mode, target, time
_wr_cmd_id portamento, ,,ASEQ_OP_LAYER_PORTAMENTO,,,,,, 0, 0
_wr_u8 \mode
_wr_u8 \target
.if (\mode & 0x80) != 0
_wr_u8 \time
.else
_var \time
.endif
.endm
/**
* noportamento
*
* Disables portamento on the current layer.
*/
.macro noportamento
_wr_cmd_id noportamento, ,,ASEQ_OP_LAYER_NOPORTAMENTO,,,,,, 0, 0
.endm
/**
* shortgate <gateTime:u8>
*
* Sets gate time for short notes.
*/
.macro shortgate gateTime
_wr_cmd_id shortgate, ,,ASEQ_OP_LAYER_SHORTGATE,,,,,, 0, 0
_wr_u8 \gateTime
.endm
/**
* notepan <pan:u8>
*
* Sets the pan for this layer. A value of 64 is center.
*/
.macro notepan pan
/* pan can only take values in 0..127 */
_check_arg_bitwidth_u \pan, 7
_wr_cmd_id notepan, ,,ASEQ_OP_LAYER_NOTEPAN,,,,,, 0, 0
_wr_u8 \pan
.endm
/**
* nodrumpan
*
* Instructs the layer to ignore the pan value in drum instruments and only
* use pan set in the layer.
*/
.macro nodrumpan
_wr_cmd_id nodrumpan, ,,ASEQ_OP_LAYER_NODRUMPAN,,,,,, 0, 0
.endm
/**
* stereo <type:b2> <strongR:b1> <strongL:b1> <strongRvrbR:b1> <strongRvrbL:b1>
*
* TODO DESCRIPTION
*/
.macro stereo type, strongR, strongL, strongRvrbR, strongRvrbL
_check_arg_bitwidth_u \type, 2
_check_arg_bitwidth_u \strongR, 1
_check_arg_bitwidth_u \strongL, 1
_check_arg_bitwidth_u \strongRvrbR, 1
_check_arg_bitwidth_u \strongRvrbL, 1
_wr_cmd_id stereo, ,,ASEQ_OP_LAYER_STEREO,,,,,, 0, 0
_wr_u8 (\type << 4) | (\strongR << 3) | (\strongL << 2) | (\strongRvrbR << 1) | (\strongRvrbL << 0)
.endm
/**
* ldshortvel <velocity:b4>
*
* Sets the velocity used in short notes by reading from SHORTVELTBL[velocity]
*/
.macro ldshortvel velocity
_wr_cmd_id ldshortvel, ,,ASEQ_OP_LAYER_LDSHORTVEL,,,,,, \velocity, 4
.endm
/**
* ldshortgate <gateTime:b4>
*
* Sets the gate time used in short notes by reading from SHORTGATETBL[gateTime]
*/
.macro ldshortgate gateTime
_wr_cmd_id ldshortgate, ,,ASEQ_OP_LAYER_LDSHORTGATE,,,,,, \gateTime, 4
.endm
#if (MML_VERSION == MML_VERSION_MM)
/**
* unk_F0 <arg:s16>
*
* TODO DESCRIPTION
*/
.macro unk_F0 arg
_wr_cmd_id unk_F0, ,,ASEQ_OP_LAYER_F0,,,,,, 0, 0
_wr_s16 \arg
.endm
/**
* surroundeffect <arg:u8>
*
* TODO DESCRIPTION
*/
.macro surroundeffect arg
_wr_cmd_id surroundeffect, ,,ASEQ_OP_LAYER_F1,,,,,, 0, 0
_wr_u8 \arg
.endm
#endif
/**
* notedvg <pitch:b6> <delay:var> <velocity:u8> <gateTime:u8>
*
* Plays a note with the current instrument settings for a specified length of time.
*
* pitch : The note to play. Since pitch is only 6 bits it can only hold values 0..63, for access to more values
* use `transpose` first to shift the base pitch.
* delay : The time to play the note for, plus the delay before executing the next instruction.
* velocity : The relative volume.
* gateTime : The proportion of the delay for which the sound does not play.
*
* This instruction must only be used when long notes are enabled with the noshort instruction.
*/
.macro notedvg pitch, delay, velocity, gateTime
_wr_cmd_id notedvg, ,,ASEQ_OP_LAYER_NOTEDVG,,,,,, \pitch, 6
_var \delay
_wr_u8 \velocity
_wr_u8 \gateTime
.endm
/**
* notedv <pitch:b6> <delay:var> <velocity:u8>
*
* Like notedvg, but gateTime is fixed to 0 so there is no delay between when the sound stops and the next instruction.
*
* This instruction must only be used when long notes are enabled with the noshort instruction.
*/
.macro notedv pitch, delay, velocity
_wr_cmd_id notedv, ,,ASEQ_OP_LAYER_NOTEDV,,,,,, \pitch, 6
_var \delay
_wr_u8 \velocity
.endm
/* Workaround for bugs in vanilla sequences, force long encoding for delay. This should not typically be used. */
.macro noteldv pitch, delay, velocity
_wr_cmd_id noteldv, ,,ASEQ_OP_LAYER_NOTEDV,,,,,, \pitch, 6
_var_long \delay
_wr_u8 \velocity
.endm
/**
* notevg <pitch:b6> <velocity:u8> <gateTime:u8>
*
* Like notedvg, but delay is assumed to be the same as the delay for a previous note instruction.
*
* This instruction must only be used when long notes are enabled with the noshort instruction.
*/
.macro notevg pitch, velocity, gateTime
_wr_cmd_id notevg, ,,ASEQ_OP_LAYER_NOTEVG,,,,,, \pitch, 6
_wr_u8 \velocity
_wr_u8 \gateTime
.endm
/**
* shortdvg <pitch:b6> <delay:var>
*
* Like notedvg, but encodes shorter by requiring that the velocity and gateTime be specified in advance using the
* shortvel and shortgate instructions.
*
* This instruction must only be used when short notes are enabled with the short instruction.
*/
.macro shortdvg pitch, delay
_wr_cmd_id shortdvg, ,,ASEQ_OP_LAYER_NOTEDVG,,,,,, \pitch, 6
_var \delay
.endm
/**
* shortdv <pitch:b6>
*
* Like shortdvg, but gateTime is fixed to 0 so there is no delay between when the sound stops and the next
* instruction. Uses the delay set by shortdelay, and the velocity set by shortvel.
*
* This instruction must only be used when short notes are enabled with the short instruction.
*/
.macro shortdv pitch
_wr_cmd_id shortdv, ,,ASEQ_OP_LAYER_NOTEDV,,,,,, \pitch, 6
.endm
/**
* shortvg <pitch:b6>
*
* Like shortdvg, but delay is assumed to be the same as the delay for a previous note instruction. Uses the velocity
* set by shortvel.
*
* This instruction must only be used when short notes are enabled with the short instruction.
*/
.macro shortvg pitch
_wr_cmd_id shortvg, ,,ASEQ_OP_LAYER_NOTEVG,,,,,, \pitch, 6
.endm
/**
* entry <label:lbl>
*
* For dyntable entries.
*/
.macro entry label
_wr_lbl \label
.endm
/**
* point <delay:s16> <arg:s16>
*
* Envelope point
*/
.macro point delay, arg
.if ASEQ_MODE != ASEQ_MODE_ENVELOPE
_section_invalid point
.endif
_wr_s16 \delay
_wr_s16 \arg
.endm
/**
* disable
*
* Envelope disable
*/
.macro disable
.if ASEQ_MODE != ASEQ_MODE_ENVELOPE
_section_invalid disable
.endif
_wr_u16 0
_wr_s16 0
.endm
/**
* hang
*
* Envelope hang
*/
.macro hang
.if ASEQ_MODE != ASEQ_MODE_ENVELOPE
_section_invalid hang
.endif
_wr_u16 0xFFFF // -1
_wr_s16 0
.endm
/**
* goto <index:s16>
*
* Envelope goto
*/
.macro goto index
.if ASEQ_MODE != ASEQ_MODE_ENVELOPE
_section_invalid goto
.endif
_wr_u16 0xFFFE // -2
_wr_s16 \index
.endm
/**
* restart
*
* Envelope restart
*/
.macro restart
.if ASEQ_MODE != ASEQ_MODE_ENVELOPE
_section_invalid restart
.endif
_wr_u16 0xFFFD // -3
_wr_s16 0
.endm
#endif /* _LANGUAGE_ASEQ */
#endif /* ASEQ_H */