====== Introduction ======
If you followed the [[:introduction_to_programming_the_beamracer|other chapter]] you already know that BeamRacer is an extremely powerful new tool allowing you to explore the VIC-II and Commodore 64 graphics and visual effects in both old, well-known as well as new, completely uncharted ways. At the very core of BeamRacer lies a programmable coprocessor called VASYL with its own registers, instruction set and memory.
Programming VASYL is definitely easy for seasoned Commodore 64 machine-language programmers who mastered their favourite development toolchain and can spawn another PRG in "no-time". But we wanted it to be even more accessible and enabling. Therefore an idea for BASIC extension was born and materialised shortly thereafter.
How about some complex graphics effects and augmentation for BASIC programs? We all know that those are not possible without machine code routines stashed somewhere (typically at 49152 upwards) in available RAM. With BeamRacer installed you can still use the same methods. You can POKE or read from storage whatever you need. But you can also load "VBASIC" and have all the VASYL power at your BASIC programming fingertips with zero setup time.
At least that was the idea that initially guided us. And yes - it's all there. But to be completely frank we have to admit that once VBASIC matured, we also found it an indispensable tool for developing, testing and debugging not only VASYL programming ideas but for example checking quickly how VIC behaves when treated one or another way, without all the boilerplate code needed to cycle-exactly trigger something. An eye opening and mouth corners rising experience :-)
Summing up - you will appreciate VBASIC for:
* all the additions aimed at helping in low-level programming
* fast-turnaround, zero boilerplate verification of VIC programming ideas
* immediate full access to VASYL's power from within BASIC programs
* lowest barriers to entry into VASYL programming and programming techniques development
* verifying/debugging of VASYL code and display lists
====== Installation ======
VBASIC is an integral part of BeamRacer support software. It is currently distributed in binary form of a BASIC loadable PRG file that needs to be RUN after loading.
====== Initialisation ======
As part of initialisation process VBASIC installs itself in the RAM area, address of which is normally occupied by BASIC ROM code, and adds additional routines at the very top of memory available for BASIC programs. It also verifies the presence of an installed BeamRacer card and activates it if found. Thanks to that your programs do not need to perform any additional steps to check and activate BeamRacer. Last but not least it adds helper routines and modifies several system variables and vectors. This allows to provide correct information about available memory and enables seamless BASIC programming experience. As a result all the standard behaviour for STOP, STOP/RESTORE etc. remains unchanged, while actively maintaining both the VBASIC extension and BeamRacer active at the same time.
====== Features ======
There's a lot to talk about here so let's begin without additional delays.
===== Hex all over and around =====
Before we go into BeamRacer specific stuff – first things first: hexadecimal numbers. BASIC programmers are usually used to having only decimal numbers at their disposal. We didn't want to leave it that way though. We know very well that the more one's skills grow, the more important an easy way of working with hexadecimal addresses and data becomes. Therefore hexadecimal numbers received first-class VBASIC citizenship from day one.
==== Hexadecimal entry ====
VBASIC allows up to 16 bit hexadecimal entry wherever appropriate:
10 POKE 53280,14
20 POKE $D021,$0E
30 PRINT PEEK($D012)
40 POKE $0400,16:REM P
50 POKE 1025,$11:REM Q
60 POKE $C000,96:REM RTS
70 SYS $C000
80 PRINT $D000/$10
etc. are all valid now. So are:
10 FOR I = $100 TO $1000 STEP $10
20 REM DO GREAT STUFF WITH I
30 NEXT I
==== Hexadecimal output ====
Hexadecimal input is one important thing, the other side of the coin is getting your numbers __output__ in hex. VBASIC has you covered there too.
10 PRINT 49152/16 : REM WE KNOW WE GET 3072
20 PRINT 49152/256 &W : REM HUH ?
30 PRINT HI(49152) &B : REM HUH^2 ?
Any numeric expression can be passed to one of ''&'' modifiers:
* ''B'' - for one ''BYTE'' (eight bit)
* ''W'' - for one ''WORD'' (16 bit)
* ''L'' - for one ''LONG'' (32 bit)
hexadecimal output. Please note that there is no automatic promotion to higher number of bits. If you enter
PRINT 49152 &B
what you get is only the eight __least__ significant bits as a hexadecimal number.
The same applies for ''&W'', obviously with the output being limited to 16 least significant bits in this case. Try
PRINT 70000 &B
PRINT 70000 &W
PRINT 70000 &L
and you'll get the idea. If you don't explicitly specify the modifier
PRINT 255&
PRINT 256&
PRINT 53248&
PRINT 16777215&
the most appropriate one will be chosen automatically. Values up to 32-bit long are supported.
===== 16-bit PEEKs and POKEs =====
PEEKs and especially POKEs are inseparable part of every BASIC programs that strives for utilising graphics capabilities of the computer. Be it bitmapped graphics, be it the the first "up up and away" sprites experience or actually _anything_ beyond the blue-on-blue textual interface the machine powers up with. Built-in support for PEEK and POKE is limited to eight bit values though. VBASIC adds keywords for handling 16 bit values easily. Setting address vectors or pointers is as easy as
DPOKE $C000,$0400
or
DPOKE 49152,1024
which in both forms sets a pointer to address 1024 with its LO byte (0) located at 49152 and HI byte (4) at 49153.
Obtaining the starting address of BASIC memory becomes as easy as
PRINT DPEEK(43)
Length of current BASIC program can be retrieved as
PRINT DPEEK(45) - DPEEK(43) - 2
And the (in)famous
10 POKE 53280,0
20 POKE 53281,0
sequence becomes
10 DPOKE 53280,0
Basically everywhere, where dealing with "little endian" 16-bit values is needed, these come in handy and do all the LO/HI calculations for you.
===== HI and LO bytes =====
Speaking of HI and LO bytes of an address (or any 16-bit value for that matter) – VBASIC provides functions that simplify working with them too. And they are named… yeah, you guessed that already, didn't you?
10 PRINT HI(53281)
20 PRINT LO(53281)
===== VASYL access and programming =====
Even if the above mentioned features alone are noteworthy additions to BASIC programming environment, the most important part is the full-fledged support for BeamRacer, including displaylist programming.
==== Automatic BeamRacer activation ====
As explained in [[:introduction_to_programming_the_beamracer|Introduction to BeamRacer Programming]], to maintain highest possible compatibility, BeamRacer remains hidden on power-up and needs to be software activated with a "register knocking" technique before accessing any of its software accessible features. All programs that want to interact with VASYL or other BeamRacer features, like GreydotKilla™ for example, need to perform the init'n check sequence as described in [[:introduction_to_programming_the_beamracer#initialization|Initialisation section]]. VBASIC takes that responsibility on its shoulders and not only activates BeamRacer as part of its startup-sequence but also automatically reactivates it after every BRK/NMI/STOP-RESTORE combination. Breaking out of VASYL programs and disabling display list is therefore as easy as hitting the famous keys combination, without worries about disabling either VBASIC or BeamRacer on the way. All regular BASIC programming activities, short of directly overwriting values at any of vital memory locations or SW/HW resets/jams do not affect VBASIC and BeamRacer's availability.
==== VASYL programming ====
VBASIC enables and in some cases simplifies VASYL programming, debugging and testing directly from within BASIC. It is achieved in a few distinctive ways.
=== VASYL complex commands ===
Certain VASYL programming sequences can be simplified with VBASIC statements, which combine several low level accesses into a single high level directive. The most common and required for virtually every displaylist employing program is the configuration of VASYL PORT register(s), which act as proxy gateways between the CPU and VASYL memory. In order to transfer any displaylist code to where it can be executed, at least one of the two available PORT registers needs to be employed. Configuring them is therefore the first step of displaylist transfer. In contrast to [[:introduction_to_programming_the_beamracer#local_memory|how it is done in assembler]], when using VBASIC this can be done in one shot as:
10 VCFG 0,0
which configures
* Conveyor #0 (connected to VASYL PORT #0)
* to point to VASYL memory address 0 ($0000)
* with the default step of 1
Other examples of such statements are:
1000 DLON : REM START DISPLAYLIST EXECUTION
1000 DLOFF : REM STOP DISPLAYLIST EXECUTION
1000 BANK 3 : REM SET VASYL MEMORY BANK TO 3
Full list and description can be found in the [[#reference]] section below.
=== VASYL inline displaylist assembler ===
VBASIC provides full support for all VASYL/displaylist [[:isa|opcodes]] in the form of BASIC statements and functions. In order to form them, assembly opcodes are prepended with letter "V". Thus ''WAIT'' becomes ''VWAIT'', ''MOV'' becomes ''VMOV'', ''END'' becomes ''VEND'' and so on.
The way BASIC interprets those keywords is tuned for both flexibility and the easiest possible way of transferring displaylists into VASYL local memory. I. e. whenever such keyword is encountered, its arguments are parsed and assembled into appropriate VASYL binary code. The resulting bytes are then either immediately sent to previously configured [[#vcfg|"conveyor"]] (if invoked as statement) and no additional steps are necessary, or returned to be e. g. stored in a variable for later use (when invoked as funcion).
=== Displaylist disassembler ===
Even if creating simple to medium complexity blocks of VASYL programs (aka displaylists) is very easy, having a method for verifying the state of displaylist memory is a hard to overrate feature. Especially at times when things don't work as imagined. For this purpose we equipped VBASIC with a complete displaylist disassembler, allowing us to peek into what VASYL sees and executes while doing its hard work.
Disassembler is invoked with ''VLIST'' statement:
VLIST [[,]]
====== Reference ======
All VBASIC keywords grouped into three categories:
* representing only statements
* representing only functions
* representing both statements and functions
===== Functions only =====
Tokens from this group require to be called as functions, with parentheses, even if no arguments are expected for some of them
==== ABOUT ====
FUNCTION: Returns detailed information about VBASIC and BeamRacer (if the latter is installed) versions
__Synopsis__: ''ABOUT()''
__Examples__:
PRINT ABOUT()
AB$ = ABOUT()
REM NOW DO SOMETHING WITH THE STRING
==== DPEEK ====
FUNCTION: Returns "double peek" value from in main computer address space
__Synopsis__: ''DPEEK()''
__Examples__:
BL = DPEEK(45) - DPEEK(43) - 2 : REM GET CURRENT BASIC PROGRAM SIZE IN BYTES
PRINT BL&W : REM PRINT IT AS 16-BIT HEXADECIMAL NUMBER
==== HI ====
FUNCTION: Returns "HI" byte (eight most significant bits) of interpreted as 16-bit unsigned integer
__Synopsis__: ''LO()''
Where is a number in the 16-bit unsigned int range: 0..65535
__Examples__:
PRINT HI(53281)&B
==== LO ====
FUNCTION: Returns "LO" byte (eight least significant bits) of interpreted as 16-bit unsigned integer
__Synopsis__: ''LO()''
Where is a number in the 16-bit unsigned int range: 0..65535
__Examples__:
PRINT LO(53280)&B
==== VDPEEK ====
FUNCTION: Returns "double peek" value from in currently selected bank of VASYL memory
__Synopsis__: ''VDPEEK()''
__Examples__:
PRINT VDPEEK($100)&W
==== VPEEK ====
FUNCTION: Returns 8-bit value from in currently selected bank of VASYL memory
__Synopsis__: ''VPEEK()''
__Examples__:
PRINT VPEEK($100)
===== Statements only =====
Tokens from this group require to be used as statements with arguments given without parentheses
==== COPY ====
----
STATEMENT: Copy data between different VASYL RAM regions or between computer's main and VASYL's memory.
__Synopsis__: ''COPY #, #, ''
* TO is the number of configured destination "conveyor"
* FROM is the number of configured source "conveyor"
* LENGTH is the number of bytes to be copied
__Discussion__:
There are two "conveyors", which are used every time data needs to be shifted in or out of VASYL RAM. Every display list program employs at least one of them in order to find its way into VASYL accessible memory. VBASIC provides one more "conveyor" though. While the first two, numbered ''0'' and ''1'' are directly connected with two VASYL , the third one extends this form of memory access to cover also the main computer RAM.
__Conveyors__:
* 0 - communicates with VASYL memory through [[https://docs.beamracer.net/doku.php?id=registers#port0|PORT0]]
* 1 - communicates with VASYL memory through [[https://docs.beamracer.net/doku.php?id=registers#port1|PORT1]]
* 2 - communicates with computer's main memory. Please note that currently there are no protection mechanisms preventing accidental overwrite of vital memory ranges - use with caution, especially when employed as destination
__Examples__:
VCFG 2,49152,1 : REM ALWAYS CONFIGURE BEFORE USING
VCFG 0,$1000,1 : REM ALWAYS CONFIGURE BEFORE USING
COPY #0,#2,4096: REM STASH CONTENT OF $C000-$CFFF TO VASYL RAM AT $1000
VCFG 2,49152,1 : REM ALWAYS CONFIGURE BEFORE USING
VCFG 0,$1000,1 : REM ALWAYS CONFIGURE BEFORE USING
COPY #2,#0,4096: REM PULL 4KIB OF DATA FROM VASYL RAM AT $1000 TO MAIN RAM AT $C000
BANK 1 : REM SET VASYL RAM BANK TO 1
VCFG 1,0,1 : REM ALWAYS CONFIGURE BEFORE USING
FOR L=0 TO 7 : REM WE NEED EIGHT CHUNKS by 1KIB
VCFG 2,$4000+L,8 : REM ALWAYS CONFIGURE BEFORE USING
COPY #1,#2,1024 : REM CONVERT 1KIB OF HIRES DATA TO LINEAR
NEXT L : REM REPEAT UNTIL 8KIB TRANSFERRED
==== DLOFF ====
----
STATEMENT: Turn off displaylist execution
__Synopsis__: ''DLOFF''
__Examples__:
100 DLOFF : REM TURN DISPLAYLIST EXECUTION OFF
==== DLON ====
----
STATEMENT: Turn on displaylist execution
__Synopsis__: ''DLON''
__Examples__:
100 VCFG 0,0 : REM PORT0, ADDRESS0
110 VWAIT $83,0
120 VMOV $21,$14
130 VDELAYV $28
140 VMOV $21,6
999 VEND : REM END OF DISPLAYLIST
1000 DLON : REM START DISPLAYLIST EXECUTION
__Discussion__:
Turning displaylist execution on needs to be done ___after___ the displaylist program is transferred to VASYL memory. It is a common practice to put it right after the displaylist closing ''VEND'' statement.
==== DPOKE ====
----
STATEMENT: Double POKE
__Synopsis__: ''DPOKE ,''
* ADDRESS is a 16 bit value denoting memory address within Commodore 64 memory
* VALUE is a 16 bit (two byte) value to be "poked" into ADDRESS. Bytes are stored in [[wp>Endianness|Little-Endian (first LO then HI byte)]] order
__Examples__:
DPOKE $1000,32768 : REM STORES $00 AT $1000 AND $80 AT $1001
DPOKE $10FE,$FF : REM STORES $00 AT $10FE AND $FF AT $10FF
DPOKE $D020,$00 : REM STORES $00 AT $D020 AND $D021
==== LABEL ====
----
STATEMENT: Define a label in displaylist code
__Synopsis__: ''LABEL X%''
* X is a valid integer variable name
__Examples__:
[...]
130 VSETA 10
140 LABEL A%
150 VMOV $20,0
160 VMOV $20,1
170 VDECA
180 VBRA A%
999 VEND
__Discussion__:
The variable after the ''LABEL'' statement needs to be declared (and later referred to) as //integer// variable – with percent (''%'') sign following the name. Declaration of this variable with ''LABEL'' statement must precede any reference to it
==== VCFG ====
----
STATEMENT: Configure VBASIC's "conveyor"
__Synopsis__: ''VCFG ,[,[,]]''
* CONVEYOR is a valid number of the "conveyor" to be configured (''0'', ''1'' or ''2'')
* 0 - communicates with VASYL memory through [[https://docs.beamracer.net/doku.php?id=registers#port0|PORT0]]
* 1 - communicates with VASYL memory through [[https://docs.beamracer.net/doku.php?id=registers#port1|PORT1]]
* 2 - communicates with computer's main memory. Please note that currently there are no protection mechanisms preventing accidental overwrite of vital memory ranges - use with caution, especially when employed as destination
* ADDRESS is a 16 bit value denoting memory address within either currently selected VASYL memory (for ''PORT'' numbers ''0'' and ''1'') or Commodore 64 memory (for ''PORT'' number ''2'')
* STEP is an optional, signed 8 bit value (-128..127) added to internal counter after each access. If omitted defaults to ''1''
* BANK is an optional number of VASYL memory bank. If omitted, defaults to currently selected VASYL memory bank. Ignored if given when configuring "conveyor" #2.
__Examples__:
VCFG 0,0 : REM CONFIGURE CONVEYOR 0 TO POINT TO ADDRESS $0000 WITH STEP 1
VCFG 0,0,1 : REM CONFIGURE CONVEYOR 0 TO POINT TO ADDRESS $0000 WITH STEP 1
VCFG 1,32768,-$80,3 : REM CONFIGURE CONVEYOR 1 TO POINT TO ADDRESS $8000 WITH STEP -128 IN BANK 3
VCFG 2,49152 : REM CONFIGURE CONVEYOR 2 TO POINT TO ADDRESS $C000 IN COMPUTER RAM WITH STEP 1
==== VDATA ====
----
STATEMENT: Stores '''' in VASYL memory through currently [[#vcfg|configured]] ''PORT''
__Synopsis__: ''VDATA ''
* DATA is a stream of 8-bit byte values in form of
* comma separated list of numbers in the range of 0..255 (or hexadecimal equivalents $00..$ff)
* valid 8-bit hexadecimal numbers concatenated into single string
__Examples__:
1000 VCFG 1,$200,2
1100 VDATA 12,$0d,1 : REM STORE 12 AT $0200, 13 AT $0202 AND 1 AT $0204
1000 VCFG 1,$200,2
1000 VDATA "0c0d01" : REM THE SAME AS PREVIOUS EXAMPLE
__Discussion__:
While CBM BASIC provides ''DATA'' and ''READ'' statements, which can be used to place binary data in VASYL memory
100 FOR I=0 TO 7
110 READ A
120 VPOKE $1000+I,A
130 NEXT I
140 DATA 0,6,14,15,1,15,14,6
this method is both inflexible and leaving much to be desired in terms of speed of execution. Both aspects become especially important when dealing with larger amounts of data. Using ''VDATA'' not only transfers the data much faster but also allows far greater flexibility
100 VCFG 1,$1000
110 VDATA 0,6,14,15,1,15,14,6
achieves the same results but requires no (slow) BASIC loop, no upfront knowledge of data stream length and moreover allows also a special "string" form
100 VCFG 1,$1000
110 VDATA "00060e0f010f0e06"
==== VDPOKE ====
----
STATEMENT: Double POKE for VASYL memory
__Synopsis__: ''VDPOKE ,''
* ADDRESS is a 16 bit value denoting memory address within currently selected VASYL memory
* VALUE is a 16 bit (two byte) value to be "poked" into ADDRESS. Bytes are stored in [[wp>Endianness|Little-Endian]] order
__Examples__:
VDPOKE $1000,32768 : REM STORES $00 AT $1000 AND $80 AT $1001
VDPOKE $10FE,$FF : REM STORES $FF AT $10FE AND $00 AT $10FF
==== VLIST ====
----
STATEMENT: Invoke displaylist disassembler
__Synopsis__: ''VLIST [[,]]''
* START inclusive starting address of memory range to be disassembled
* END exclusive end of memory address range to be disassembled
__Examples__:
VLIST
VLIST 4096
VLIST $100
VLIST 256,512
__Discussion__:
Disassembler can be invoked in three different ways
- with no arguments
- with one argument
- with two arguments
With no arguments disassembly begins from the next address after the last, previously disassembled one (or $0000 on the first invocation) and outputs full screen less one top and three bottom lines. This allows easy browsing of subsequent memory ranges by simply pressing ''HOME'' and ''RETURN'' to display next screen worth of disassembly
VLIST
When one argument is given, that argument determines the first VASYL memory address to be disassembled. Output is limited to one screen, the same as when no arguments are given
VLIST $100
With two arguments, the second one determines the exclusive end of address range to be disassembled. Output is continuous and scrolling can be slowed down by holding ''CONTROL'' key depressed
VLIST 256,512
==== VPOKE ====
----
STATEMENT: VASYL memory equivalent of the regular POKE statement.
__Synopsis__: ''VPOKE ,''
* ADDRESS is a 16 bit value denoting memory address within currently [[#bank|selected]] VASYL memory bank
* VALUE is an 8-bit byte value to be "poked" into ADDRESS
__Examples__:
VPOKE 4096,128
VPOKE $100,$FF
===== Both statements and functions =====
All the following keywords accept both statement (no parentheses) and function (with parentheses) methods of invocation. Obviously they behave differently depending on whether they are invoked as a statement or as a function
==== BANK ====
----
STATEMENT: Set current VASYL RAM bank to
__Synopsis__: ''BANK ''
* BANK is an integer number in the range of 0...7, representing VASYL memory bank number to be selected
__Examples__:
BANK 1
FUNCTION: Returns currently selected VASYL RAM bank
__Synopsis__: ''BANK()''
__Examples__:
PRINT BANK()
==== RACER ====
----
STATEMENT: Set BeamRacer status to
__Synopsis__: ''RACER ''
Where can be 0, 1, 2, 3 meaning
* 0 - BeamRacer inactive
* 1 - BeamRacer active but RUN/STOP-RESTORE combination deactivates it
* 2 - BeamRacer active and automatically reactivated after every RUN/STOP-RESTORE (default)
* 3 - Disable VBASIC without changing BeamRacer status (VBASIC V1.2 and higher)
__Examples__:
RACER 0 : REM DEACTIVATE BEAMRACER
FUNCTION: Returns current status of BeamRacer
Where returned value can be 0, 1, 2, 255 meaning
* 0 - BeamRacer inactive
* 1 - BeamRacer active but RUN/STOP-RESTORE combination deactivates it
* 2 - BeamRacer active and automatically reactivated after every RUN/STOP-RESTORE (default)
* 255 - BeamRacer not found
__Synopsis__: ''RACER()''
__Examples__:
100 RS=RACER()
110 PRINT "BEAMRACER ";
120 IF RS=0 THEN PRINT "INACTIVE"
130 IF RS=1 THEN PRINT "TRANSIENT"
140 IF RS=2 THEN PRINT "ACTIVE"
150 IF RS=255 THEN PRINT "NOT FOUND"
__Discussion__:
When VBASIC is run, and BeamRacer is installed, the status of BeamRacer is set to ''2'', which means that it is activated and gets reactivated after every RUN/STOP-RESTORE combination. When BeamRacer is not installed, its status is set to ''255''
==== VBADLINE ====
----
DISPLAYLIST: Force a badline after ''RASTERLINES'' of rasterlines
STATEMENT: Assembles [[:isa#badline|BADLINE ]] and stores the result in memory
__Synopsis__: ''VBADLINE [''
* RASTERLINES is a 3-bit integer number in the range 0..7
__Examples__:
VBADLINE 3 : REM NEXT BADLINE THREE LINES LATER
FUNCTION: Returns assembled [[:isa#badline|BADLINE ]]
__Synopsis__: ''VBADLINE()''
* RASTERLINES is a 3-bit integer number in the range 0..7
__Examples__:
A = VBADLINE(3) : REM ASSEMBLE AND ASSIGN THE RESULT TO A VARIABLE
__Discussion__: The command sets the vertical scroll bits in VIC-II ''SCROLY'' register to a calculated value that will trigger badline on requested positive, vertical offset from the current rasterline. It does NOT delay displaylist execution
==== VBRA ====
----
DISPLAYLIST: Continue Display List execution at address
STATEMENT: Assembles [[:isa#bra|BRA ]] and stores the result in memory
__Synopsis__: ''VBRA |*''
* ADDRESS is a memory address within the -128..127 range from the current one
* OFFSET is an 8-bit signed number from range -128..127
__Examples__:
[...]
150 VSETA 12
160 LABEL A%
[...]
190 VDECA
200 VBRA A%
999 VEND
[...]
200 VBRA 100 : REM CONTINUE AT ADDRESS $64
999 VEND
[...]
200 VBRA *-2 : REM INFINITE LOOP
999 VEND
FUNCTION: Returns assembled [[:isa#bra|BRA ]]
__Synopsis__: ''VBRA(*)''
__Examples__:
A = VBRA(*+10): REM ASSEMBLE AND ASSIGN THE RESULT TO A VARIABLE
__Discussion__: While it is technically possible to use VBRA() notation also in function mode, it is not very useful.
==== VDECA ====
----
DISPLAYLIST: Decrement counter ''A'' or skip next instruction if zero reached
STATEMENT: Assembles [[:isa#deca|DECA]] and stores the result in memory
__Synopsis__: ''VDECA''
__Examples__:
100 VCFG 0,0 : REM ALWAYS CONFIGURE
110 VWAIT $33,0 : REM WAIT FOR FIRST PAPER LINE
120 VSETA 12 : REM TWELVE TIMES
130 LABEL A% : REM LOOP CODE BEGINS HERE
140 VMOV $21, 14 : REM LIGHT BLUE
150 VDELAYV 8 : REM DELAY EIGHT RASTERLINES
160 VMOV $21, 6 : REM DARK BLUE
170 VDELAYV 8 : REM DELAY EIGHT RASTERLINES
180 VDECA : REM DECREMENT COUNTER
190 VBRA A% : REM LOOP TO LABEL A% UNLESS COUNTER REACHED ZERO
999 VEND : REM ALWAYS CLOSE DISPLAYLIST OFF
FUNCTION: Returns assembled [[:isa#deca|DECA]]
__Synopsis__: ''VDECA()''
__Examples__:
A = VDECA : REM ASSEMBLE AND ASSIGN THE RESULT TO A VARIABLE
==== VDECB ====
----
DISPLAYLIST: Decrement counter ''B'' or skip next instruction if zero reached
STATEMENT: Assembles [[:isa#decb|''DECB'']] and stores the result in memory
__Synopsis__: ''VDECB''
__Examples__:
VDECB : REM DECREMENT COUNTER B
FUNCTION: Returns assembled [[:isa#decb|DECB]]
__Synopsis__: ''VDECB()''
__Examples__:
B = VDECB : REM ASSEMBLE AND ASSIGN THE RESULT TO A VARIABLE
==== VDELAYH ====
----
DISPLAYLIST: Delays displaylist execution for given number of ''''
STATEMENT: Assembles and stores [[:isa#delayh|DELAYH ]] in VASYL memory
__Synopsis__: ''VDELAYH ''
* CYCLES is a 6-bit integer number in the range 0..63
__Examples__:
VDELAYH 3 : REM WAIT THREE CYCLES
FUNCTION: Returns assembled [[:isa#delayh|DELAYH ]]
__Synopsis__: ''VDELAYH()''
* CYCLES is a 6-bit integer number in the range 0..63
__Examples__:
PRINT VDELAYH(3)&W : REM PRINT ASSEMBLED 3-CYCLE DELAYH AS HEX WORD
__Discussion__:
The '''' argument accepts values up to 63, which makes it easy to delay displaylist execution across rasterline boundary. While this is technically correct, please note that the number of cycles per rasterline varies between VIC variants. Crossing rasterline boundary with ''DELAYH'' leads therefore to VIC variant dependent code and should be avoided. Please see also note at [[:isa#delayh|DELAYH]]
==== VDELAYV ====
----
DISPLAYLIST: Delays displaylist execution for given number of ''''
STATEMENT: Assembles [[:isa#delayv|DELAYV ]] and stores the result in memory
__Synopsis__: ''VDELAYV ''
* RASTERLINES is a 9-bit integer number in the range 0..511
__Examples__:
VDELAYV $28 : REM WAIT FORTY RASTERLINES
FUNCTION: Returns assembled [[:isa#delayv|DELAYV ]]
__Synopsis__: ''VDELAYV()''
* RASTERLINES is a 9-bit integer number in the range 0..511
__Examples__:
PRINT VDELAYV(200)&W : REM PRINT ASSEMBLED 200-LINE DELAYV AS HEX WORD
__Discussion__:
Vertical delay AKA ''VDELAYV'' delays execution until the //beginning// (cycle 0) of the line coming specified number of lines later than the one this statement is executed. Please also note that on physical hardware the first cycles of each rasterline are located within "horizontal blanking interval", i. e. out of visible range. This means a program:
VCFG 0,0
VWAIT $50,20
VDELAYV 10
VMOV $20,0
VMOV $20,14
VEND
will NOT show the expected black dash, even if your display device is capable of showing all of the picture generated by the computer((Professional monitors are typically equipped with "underscan" function, which makes picture areas not normally visible to be displayed. Those cannot include horizontal and vertical "blanking intervals" though)).
==== VEND ====
----
DISPLAYLIST: Close displaylist program off
STATEMENT: Assembles [[:isa#end|END]] and stores the result in memory
__Synopsis__: ''VEND''
__Examples__:
VEND : REM END OF DISPLAYLIST
FUNCTION: Returns assembled [[:isa#end|END]]
__Synopsis__: ''VEND()''
__Examples__:
A = VEND() : REM STORE ASSEMBLED END OF DISPLAYLIST BYTES IN A VARIABLE
==== VIRQ ====
----
DISPLAYLIST: Send Interrupt Request (IRQ)
STATEMENT: Assembles and stores [[:isa#irq|IRQ]] in memory
__Synopsis__: ''VIRQ''
__Examples__:
VIRQ : REM TRIGGER INTERRUPT REQUEST
FUNCTION: Returns assembled [[:isa#irq|IRQ]]
__Synopsis__: ''VIRQ()''
__Examples__:
?VIRQ() : REM PRINT IRQ AS 8-BIT NUMBER
__Discussion__:
Check also [[:isa#irq|IRQ]] for more details
==== VMASKH ====
----
DISPLAYLIST: Set mask for next horizontal target comparison to
STATEMENT: Assembles and stores [[:isa#maskh|MASKH ]] (''P'' bit cleared) in memory
__Synopsis__: ''VMASKH ''
* MASKVALUE is a 6-bit wide bit mask number in the range 0..63
__Examples__:
VMASKH 7 : REM ONLY THREE LOWER BITS COUNT ON NEXT HORIZONTAL COMPARISON
FUNCTION: Returns [[:isa#maskh|MASKH ]] assembled with ''P'' bit cleared
__Synopsis__: ''VMASKH()''
* MASKVALUE is a 6-bit wide bit mask number in the range 0..63
__Examples__:
?VMASKH(7) : REM PRINT ASSEMBLED MASKH WITH P BIT CLEARED AND THREE LSB ACTIVE
__Discussion__:
Setting non-standard mask value right before ''VEND'' is highly discouraged as it may lead to undesirable results. For more information check also [[:isa#maskh|MASKH]]
==== VMASKPH ====
----
DISPLAYLIST: Set mask for all horizontal targets comparison to
STATEMENT: Assembles and stores [[:isa#maskh|MASKH ]] (''P'' bit set) in memory
__Synopsis__: ''VMASKPH ''
* MASKVALUE is a 6-bit wide bit mask number in the range 0..63
__Examples__:
VMASKPH 7 : REM ONLY THREE LOWER BITS COUNT ON HORIZONTAL COMPARISONS
FUNCTION: Returns [[:isa#maskh|MASKH ]] assembled with ''P'' bit set
__Synopsis__: ''VMASKPH()''
* MASKVALUE is a 6-bit wide bit mask number in the range 0..63
__Examples__:
?VMASKPH(7) : REM PRINT ASSEMBLED MASKH P BIT SET AND THREE LSB ACTIVE
__Discussion__:
Setting non-standard mask value right before ''VEND'' is highly discouraged as it may lead to undesirable results. Check also [[:isa#maskh|MASKH]]
==== VMASKPV ====
----
DISPLAYLIST: Set mask for all vertical target comparison to <>
STATEMENT: Assembles [[:isa#maskv|MASKV]] with ''P'' bit set and stores it in memory
__Synopsis__: ''VMASKPV ''
* MASKVALUE is a 9-bit wide bit mask number in the range 0..511
__Examples__:
VMASKPV 127 : REM SEVEN LOWER BITS COUNT ON VERTICAL COMPARISONS
FUNCTION: Returns [[:isa#maskv|MASKV]] assembled with ''P'' bit set
__Synopsis__: ''VMASKPV()''
* MASKVALUE is a 9-bit wide bit mask number in the range 0..511
__Examples__:
PV = VMASKPV(127) : REM SEVEN LOWER BITS COUNT ON VERTICAL COMPARISONS
__Discussion__:
Setting non-standard mask value right before ''VEND'' is highly discouraged as it may lead to undesirable results. For more information see also [[:isa#maskv|MASKV]]
==== VMASKV ====
----
DISPLAYLIST: Set mask for next vertical target comparison to
STATEMENT: Assembles [[:isa#maskv|MASKV]] with ''P'' bit cleared and stores result in memory
__Synopsis__: ''VMASKV ''
* MASKVALUE is a 9-bit wide bit mask number in the range 0..511
__Examples__:
VMASKV 127 : REM SEVEN LOWER BITS COUNT ON NEXT VERTICAL COMPARISON
FUNCTION: Returns [[:isa#maskv|MASKV]] assembled with ''P'' bit cleared
__Synopsis__: ''VMASKV()''
* MASKVALUE is a 9-bit wide bit mask number in the range 0..511
__Examples__:
V = VMASKV(127) : REM SEVEN LOWER BITS COUNT ON NEXT VERTICAL COMPARISON
__Discussion__:
Setting non-standard mask value right before ''VEND'' is highly discouraged as it may lead to undesirable results. For more information check also [[:isa#maskv|MASKV]]
==== VMOV ====
----
DISPLAYLIST: Store in
STATEMENT: Assembles [[:isa#mov|MOV]] and stores the result in memory
__Synopsis__: ''VMOV ,''
* REGISTER is a 6-bit number in the range 0..63
* VALUE is an 8-bit number in the range of 0..255
__Examples__:
VMOV $20,3 : REM SET BORDER COLOUR TO CYAN
VMOV $43,128 : REM STROBE DISPLAYLIST EXECUTION IN ANOTHER BANK
FUNCTION: Returns assembled [[:isa#mov|MOV]]
__Synopsis__: ''VMOV(,)''
* REGISTER is a 6-bit number in the range 0..63
* VALUE is an 8-bit number in the range of 0..255
__Examples__:
BR = VMOV($20,3) : REM STORE ASSEMBLED VMOV TO A VARIABLE
SR = VMOV($43,128) : REM STROBE DISPLAYLIST EXECUTION IN ANOTHER BANK
__Discussion__:
REGISTER can refer to either VIC-II or VASYL register. Depending on REGISTER value falling into one of the three possible ranges it can refer to
- ''$00..$2e'' - VIC-II registers, accessible by CPU in the ''$d000..$d02e'' address range. Gets assembled as [[:isa#mov|MOV]]
- ''$31..$3e'' - bus accessible VASYL registers. CPU can access them in the ''$d031..$d03e'' address range. Gets assembled as [[:isa#mov|MOV]]
- ''$40..$4c'' - VASYL internal registers. Not available for the CPU. Can only be acessed by VASYL himself, while executing displaylist programs. Gets assembled as [[:isa#movi|MOVI]]
Please note that writing to VIC-II and VASYL registers from within a displaylist program differs substantially in the way it is technically executed. Writing to VIC-II registers requires bus access and cannot happen during time periods when VIC occupies the bus completely (e. g. during "badlines"). Writing to VASYL registers on the other hand can be done internally and is therefore not restricted by bus availability. Please also check notes at [[:isa#mov|MOV]] and [[:isa#movi|MOVI]] for additional information.
==== VNOP ====
----
DISPLAYLIST: Does nothing (other than using one cycle)
STATEMENT: Assembles [[:isa#vnop|VNOP]] and stores it in memory
__Synopsis__: ''VNOP''
__Examples__:
VNOP : REM USE A CYCLE
FUNCTION: Returns assembled [[:isa#vnop|VNOP]]
__Synopsis__: ''VNOP()''
__Examples__:
NP = VNOP() : REM ASSIGN ASSEMBLED VNOP TO A VARIABLE
__Discussion__:
As useless as it looks, among other uses:
* cheap yet precise fine-tuning of displaylist program timing
* displaylist program space reservations
* address alignment of displaylist program sections
* …
can be achieved by careful placement of VNOP opcodes in the displaylist programs.
==== VSETA ====
----
DISPLAYLIST: Set counter A to
STATEMENT: Assembles [[:isa#seta|SETA]] and stores the result in memory
__Synopsis__: ''VSETA ''
* VALUE is an 8-bit integer number in the range of 0..255
__Examples__:
VSETA 12 : REM TWELVE TIMES
100 VCFG 0,0 : REM ALWAYS CONFIGURE
110 VWAIT $33,0 : REM WAIT FOR FIRST PAPER LINE
120 VSETA 12 : REM TWELVE TIMES
130 VLABEL A% : REM LOOP CODE BEGINS HERE
140 VMOV $21, 14 : REM LIGHT BLUE
150 VDELAYV 8 : REM DELAY EIGHT RASTERLINES
160 VMOV $21, 6 : REM DARK BLUE
170 VDELAYV 8 : REM DELAY EIGHT RASTERLINES
180 VDECA : REM DECREMENT COUNTER
190 VBRA A% : REM LOOP TO LABEL A% UNLESS COUNTER REACHED ZERO
999 VEND : REM ALWAYS CLOSE DISPLAYLIST OFF
FUNCTION: Returns assembled [[:isa#seta|SETA]]
__Synopsis__: ''VSETA()''
* VALUE is an 8-bit integer number in the range of 0..255
__Examples__:
?VSETA(12)&W : REM PRINT ASSEMBLED SETA VALUE AS HEX WORD
__Discussion__:
See also [[#vdeca|VDECA]] and [[:isa#seta|SETA]] for more information
==== VSETB ====
----
DISPLAYLIST: Set counter B to <>
STATEMENT: Assemble [[:isa#setb|SETB]] and store the result in memory
__Synopsis__: ''VSETB ''
* VALUE is an 8-bit integer number in the range of 0..255
__Examples__:
VSETB 210
FUNCTION: Returns assembled [[:isa#setb|SETB]]
__Synopsis__: ''VSETB()''
* VALUE is an 8-bit integer number in the range of 0..255
__Examples__:
CNT = VSETB(48) : REM ASSIGN ASSEMBLED SETB TO A VARIABLE
__Discussion__:
See also [[#vdecb|VDECB]] and [[:isa#setb|SETB]] for more information
==== VSKIP ====
----
DISPLAYLIST: Make following ''WAIT'' skip an instruction if past the target
STATEMENT: Assembles [[:isa#skip|SKIP]] and stores the result in memory
__Synopsis__: ''VSKIP''
__Examples__:
VSKIP : REM SKIP TWO BYTES AFTER NEXT WAIT IF PAST TARGET
FUNCTION: Returns assembled [[:isa#skip|SKIP]]
__Synopsis__: ''VSKIP()''
__Examples__:
?VSKIP() : REM PRINT ASSEMBLED SKIP VALUE
__Discussion__:
Check also [[:isa#skip|SKIP]]
==== VWAIT ====
----
DISPLAYLIST: Wait until cycle of rasterline
STATEMENT: Assembles [[:isa#wait|WAIT]] and stores the result in memory
__Synopsis__: ''VWAIT ,''
* H is a 6-bit integer number in the range 0..63
* V is a 9-bit integer number in the 0..511 range
__Examples__:
VWAIT $26,$0A : REM WAIT UNTIL FIRST VISIBLE CYCLE OF RASTERLINE $26
FUNCTION: Returns assembled [[:isa#wait|WAIT]]
__Synopsis__: ''VWAIT(,)''
* H is a 6-bit integer number in the range 0..63
* V is a 9-bit integer number in the 0..511 range
__Examples__:
WT = VWAIT($26,$0A) : REM ASSIGN ASSEMBLED WAIT TO A VARIABLE
__Discussion__:
More information available in the [[:isa#wait|WAIT]] opcode description
==== VWAITBAD ====
----
DISPLAYLIST: Delays displaylist execution until beginning (cycle 0) of a rasterline immediately __preceding__ the next badline.
STATEMENT: Assemble [[:isa#waitbad|WAITBAD]] and store the result in memory
__Synopsis__: ''VWAITBAD''
__Examples__:
VWAITBAD : REM WAIT FOR A LINE BEFORE NEXT BADLINE
FUNCTION: Return assembled [[:isa#waitbad|WAITBAD]]
__Synopsis__: ''VWAITBAD()''
__Examples__:
?VWAITBAD()
__Discussion__:
Check also notes at [[:isa#waitbad|WAITBAD]] for more information
==== VWAITREP ====
----
DISPLAYLIST: Wait for VASYL's to finish repeating writes
STATEMENT: Assemble [[:isa#waitrep|WAITREP]] and store the result in memory
__Synopsis__: ''VWAITREP ''
__Examples__:
VWAITREP 0 : REM WAIT FOR PORT 0 TO FINISH REPEATING WRITES
FUNCTION: Returns assembled [[:isa#waitrep|WAITREP]]
__Synopsis__: ''VWAITREP()''
__Examples__:
WR = VWAITREP(0) : REM ASSIGN ASSEMBLED WAITREP TO A VARIABLE
__Discussion__:
Check [[:isa#waitrep|WAITREP]] for more details
==== VXFER ====
----
DISPLAYLIST: Transfer byte TO from
STATEMENT: Assembles [[:isa#xfer|XFER]] and stores the result in memory
__Synopsis__: ''VXFER ,''
* is a 7-bit unsigned integer in the range 0..127
* is a 1-bit unsigned value in the range 0..1
__Examples__:
VXFER $20,1 : REM READ BYTE FROM PORT1 WRITE TO EXTCOL ($D020)
100 REM PREPARE DATA - STORE $00 TO $0F STARTING AT $100
110 FOR I=0 TO 15:POKE $100+I,I:NEXT I
120 VCFG 0,0 : REM CONFIGURE PORT0
130 REM CONFIGURE PORT1 FROM WITHIN THE DISPLAYLIST
140 VMOV $38,15:VMOV $39,1 : REM POINT IT TO $10F
150 VMOV $3A,255 : REM WITH STEP -1
160 VSETA 15 : REM OUR COLOUR COUNTER
170 VWAIT $26,$0A : REM WAIT UNTIL FIRST VISIBLE CYCLE OF RASTERLINE $26
180 LABEL A% : REM LOOP TARGET
190 VXFER $20,1 : REM TRANSFER TO EXTCOL REGISTER FROM PORT1
200 VDECA
210 VBRA A% : REM BRANCH TO PREVIOUSLY DEFINED LABEL
999 VEND
1000 DLON
FUNCTION: Returns assembled [[:isa#xfer|XFER]]
__Synopsis__: ''VXFER(,)''
* REGISTER is a 7-bit unsigned integer in the range 0..127
* PORT is a 1-bit unsigned value in the range 0..1
__Examples__:
?VXFER($20,1) : REM PRINT VALUE OF ASSEMBLED XFER $20,1
__Discussion__:
There are seven bits reserved for REGISTER value, even if currently only numbers in the range of $00..$4e are valid. XFER does not require port to be set for reading. See also [[:isa#xfer|XFER]] for more details
====== Program Examples ======
TBD