Developing wAx Plug Ins - Chysn/VIC20-wAx2 GitHub Wiki
In addition to the wAx user plug-ins included with the system, wAx has a documented API so that programmers can develop their own plug-ins and use them as they would use native wAx tools.
There are two types of plug-in, "Normal" and "List." A Normal-type plug-in does something and then exits. An example of a Normal-type native tool is the Transfer tool. It performs and operation, and then ends. The Assemble tool is also a Normal-type tool. It performs its task, generates a prompt, and ends. Normal-type tools have quite a bit of diversity in their parameters, even though they usually start with a working address.
Examples of List-type native tools include the Disassemble tool and the Memory Display tool. List-type tools always take the same parameters ([from] [to]), and behave in a consistent manner (they start at the Command Pointer when no addresses are provided, they respond to the STOP key, they continue listing with SHIFT engaged, etc.).
When you start designing your plug-in, you'll want to consider which of the two types is most appropriate for the task you wish your plug-in to perform.
Plug-In Structure
The basic outline of a wAx user plug-in is this:
@A JMP @S
: NORMAL | LIST ; Bit 7 set is List, otherwise Normal
"TEMPLATE" ; Usually a one-line reminder of parameters, but can be up to 255 characters
: 00 ; The template string is $00-terminated
@S ; The plug-in does something
RTS | JMP NextList ; Normal-type plug-ins RTS, which will generate a wAx prompt
; List-type plug-ins jump to the list item endpoint to handle the next item
The rest of this chapter will deal with "The plug-in does something," but first note that plug-ins are installed with the Plug-In Manager tool, using the address of the plug-in:
.P [addr] ; The value of @A above
The plug-in is invoked with .U, followed by any parameters it expects. The user may view the template text in two ways:
.U?
.P
If you use the P command by itself, you'll see the current plug-in's address, the plug-in type, and the template. U? only shows the template.
A Short Example
.A 1800 JMP @S
:00 ; Normal-type
"ADDR" ; Template: a required address
:00 ; ,,
@S BCC @E ; Carry clear means no valid 16-bit address was provided
JSR $A01E ; ResetOut
JSR $A00F ; IncAddr
JSR $A00C ; HexOut
JSR $A018 ; PrintBuff
RTS ; Return to wAx prompt
@E JMP $CF08 ; ?Syntax Error, and return to BASIC
Install the plug-in with the Plug-In Manager, then try a few invocations:
.P 1800 ; Install plug-in
ADDR ; Shows the template after installation
.U? ; Shows the template with help request
ADDR ; ,,
.U C000 ; Provides a working address
78 ; Gets the value, converts it to hex, displays it
.U ; No address provided, so error out
?SYNTAX
ERROR
READY.
Getting Parameters
When a plug-in is invoked, the wAx API makes an input buffer available. Since nearly all monitor tools require at least a starting 16-bit address, wAx processes the address automatically and places it in the working address. Many plug-ins start by checking for a valid address, which is what the above example does.
The wAx API provides subroutines to get two types of parameters: characters and hexadecimal bytes. Note that wAx does not care about spaces (unless they're inside quotation marks), so you typically don't need to handle spaces when getting parameters. These invocations are the same, as far as wAx is concerned:
.U 1234
.U 12 34
.U 1 2 3 4
However, you may wish to differentiate between addresses and hex bytes in documentation, where it makes sense.
You may get the next character with the CharGet routine. This will advance the input buffer index and return the next non-space character in the Accumulator. This can be used to create variants of your plug-in, based on entered characters.
As mentioned above, wAx starts any tool by looking for four hex bytes to create a 16-bit address. This advances the input buffer index past the first four characters of the input buffer. But what if your plug-in doesn't need an address, or needs to use CharGet for the first character? That's where ResetIn comes in handy:
.A 1800 JMP @&
.A 1803 :00
.A 1804 ".U [C]"
.A 180A :00
.A 180B @& JSR $A01B ; ResetIn
.A 180E JSR $A006 ; CharGet
.A 1811 CMP #"C"
.A 1813 BEQ @&
.A 1815 RTS
.A 1816 @& INC $900F
.A 1819 RTS
.A 181A {RETURN}
.P 1800
.U [C]
.U C
Generating Output
The wAx API features a 22-character output buffer that is kept in memory, and flushed to the screen all at once. This can be used for providing feedback during the instruction, or for displaying list items. The output routines are:
- ResetOut - Starts a new line of output
- HexOut - Adds two hex digits (in A), representing one byte, to the output buffer
- CharOut - Adds one character (in A) to the output buffer
- ShowAddr - Adds the working address to the output buffer
- ShowCP - Adds the Command Pointer to the output buffer
- Disasm - Adds disassembly at the working address to the output buffer
- PrintBuff - Prints the output buffer to the screen
- PrintStr - Prints a string (in A,Y), bypassing the output buffer
The general pattern for generating lines of output with the wAx API is:
.A * JSR ResetOut ; Start a new output buffer
.A * LDA #":" ; To add a character
.A * JSR CharOut ; ,,
.A * LDA #$4F ; To add a hex byte
.A * JSR HexOut ; ,,
.A * JSR ShowAddr ; To add the working address
.A * JSR PrintBuff ; Print the buffer to the screen
Note that this pattern is somewhat different with List-type plug-ins. wAx's list mechanism handles the ResetOut and PrintBuff for you, and they would be omitted for List-type plug-ins.
Memory Usage
For this section, we'll create a List-type plug-in. This will be a disassembler variant. Rather than showing disassembled code, this plug-in will display hex codes, one line per instruction.
When you build your own plug-ins, the VIC-20 is yours and you may use memory however you like. However, wAx earmarks 8 bytes, from $0247 to $024E, for plug-in workspace.
For other memory locations, including the location for the working address and the Command Pointer, see wAx Memory Usage.
.A 1800 JMP @&
.A 1803 :80 ; Specifies List-type plug-in
.A 1804 ".U [FROM] [TO]"
.A 1812 :00
.A 1813 @& LDA #":" ; Show colon so the user can edit bytes
.A 1815 JSR $A009 ; CharOut
.A 1818 LDY #0 ; $A6 is the working address. Get the current
.A 181A LDA ($A6),Y ; instruction in A
.A 181C JSR $A033 ; SizeOf instruction A into X
.A 181F STX $0247 ; Save instruction size as an iterator
.A 1822 @@ LDA #" " ; Show a space between hex bytes
.A 1824 JSR $A009 ; CharOut
.A 1827 JSR $A00F ; IncAddr (put value in A and increment address)
.A 182A JSR $A00C ; HexOut
.A 182D DEC $0247 ; Decrement the iterator
.A 1830 BNE @@
.A 1832 JMP $A02D ; NextList
.A 1835 {RETURN}
.P 1800 ; Installation
.U [FROM] [TO] ; Post-installation template reminder
.U 1813 1834
Note that List-type plug-ins don't (usually) have variation in their usage templates. So you'll probably always use ".U [FROM] [TO]" as a convention, unless you want to provide additional information. The above plug-in will automatically behave like other List-type tools. That is, it will respond to STOP and SHIFT in the same ways, use and set the Command Pointer in the same ways.