META/L CX-16 Assembly Language Editor

Ed Minchau, spider_boris@yahoo.com

version 1.3.41.0712

Table of Contents

Introduction

The META/L CX16 Assembly Language Editor was made to allow easy development of assembly language programs on the Commander X16. The editor is meant to be as small as possible, while still having lots of functionality. Well, it started out small. Users can write their code directly in the CX16 environment, test it from within the editor, and easily save their code to file, whether in low RAM or banked RAM.

Addresses can be replaced with labels, and code can be commented. With parameter referencing by label low byte, high byte, bank number, string length, by the label's address, or an offset from that address, your assembly language program can be as readable as precompiled source code. The labels, comments, and other metadata are saved separately from your program in a set of files in the META folder every time you save a program located in low RAM.

This editor is not a compiler; it is an interpreter, in the same way that BASIC is an interpreter. That means there is no source code stored anywhere in memory. When you type in your code, it is immediately interpreted into machine language code and metadata. When the editor is displaying code, it is interpreting machine language and metadata into readable text; VERA is the only place where a source code -like text is stored, and then only as it is displaying that screen of code. As a result, there is no importing of source code from another editor into this editor. You can however load already-compiled code into the editor and add your own metadata and turn it into something readable that you can edit, even if you've never seen the source code.

The editor is located in the last 16 banks of banked RAM. On a 2Mb CX-16, that's banks F0 to FF; on a 512kb system it's in banks 30-3F. Most of that space, about 100kb, is reserved for the metadata associated with the user's program. There is enough space allocated for metadata to write very large programs, perhaps 25 thousand lines of code before you start running out of metadata space.

There is a short bootstrap program located from $0400 to $080D, which loads and runs the editor. It also contains lots of 16 bit subroutines and several short subroutines that end users might find useful in their own programs. There's also some code that isn't generally useful but was absolutely required to be in low RAM for the editor to function.

This leaves the RAM from $080E to $9EFF available for the user's own assembly language program, almost 37.75 kilobytes of contiguous space. You can also edit assembly language code in any of the blocks of banked RAM.

Bank 00 (used by the system) and the banks containing the editor are protected by a safety switch to prevent inadvertently changing critical data, but you can turn the safetys off if you want - you can even edit the editor itself if you like. Also, editing the RAM from addresses $0000-07FF or $9F00-9FFF can have unpredictable results, so these areas are also protected by the safety switch - but it's your computer and you can do what you want; you can turn the safeguards off and edit there too. You have full control in real time, programming without a net.

The editor sets the screen to 40x30 character display mode, because my eyes are getting old and I need new glasses. Thirty lines of assembly language should be enough for most subroutines; if you find your subroutines getting much longer than that, you should probably consider breaking them up into shorter subroutines.

To keep the parsing code short, the keypresses that are accepted by the editor are limited. When entering a command, only letters, numbers, and the symbols + - # and / are allowed. When entering two hex digits, only the keys 0 to 9 and A to F are accepted. When moving the cursor in mode 1, 2, 4, or 5, only up or down arrow keys, F1-F5, and letters are allowed. When moving the cursor in mode 3, only F1-F5, the four arrow keys, or a hexadecimal digit are allowed. When entering a filename, only letters, numbers, a slash, a period, or a Return are allowed. For comments, only letters, numbers, spaces, Return, and the symbols + - # / < and > are allowed. For labels, only letters, numbers, and the symbols + - # / < and > are allowed.

In all cases, whether entering commands or parameters or other data, backspace is allowed, to a point. If you backspace to the beginning, then whatever you were entering aborts and goes back to the editing mode, as if nothing ever happened and let us never speak of this thing again. For instance, if you start entering a command and change your mind halfway through entering a parameter, backspacing to the beginning of that parameter just aborts the command entry, the command isn't stored at that location, and the display reverts to as it was before you started typing the command. If you enter a four character code that isn't a 65c02 command or an editor function, again the display reverts to as it was before you started typing the erroneous code. Syntax errors simply cannot happen at all.

Version and License

Major version: 1
Minor version: 3
emulator version: 41
build number: 0712

IMPORTANT: this is *only for version 41* of the emulator. It will not work with earlier versions than r39, and may need an update to work with higher version numbers.

The editor is released under the MIT Open Source license:

Copyright 2020-22 Ed Minchau

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Revision History

Getting Started

If you're reading this README file, then you've already downloaded the code from GitHub. Good. Just make sure you've extracted everything from the zip file (EDIT.PRG, EDIT.BIN, EDIT.BFF, R41PATCH.BIN, the META/ folder, and this readme file in the docs folder) into your directory containing the emulator version 41. To start, launch the emulator, and then type:

LOAD"EDIT.PRG",8,1
RUN

You will be shown the version information, the license, a status bar and PRESS ANY KEY TO CONTINUE. Click a key and the editor starts in mode 1 at address $0818, the first available address for user programs after the BASIC bootstrap.

if you have been using the META/L editor with r38 of the emulator and want to contnue in r41, there's a couple of steps involved. First, you need to load your program into memory as before i.e.:

LOAD"EDITMATH.PRG",8,1

and then instead of RUN, type:

LOAD"R41PATCH.BIN",8,1

SYS 1942

This will do various bug fixes and patches and then prompt you to save the file and ask for a filename. After that, your file will be updated to r41 and you can continue editing it as before. See also the section on CRAM.

Demo videos

A series of videos showing various features of the editor is available here.

I will be adding videos to the playlist from time to time.

Display Modes

There are five display modes available in the editor. Use the F1-F5 keys to switch between the display modes.

In all five display modes, there is a message bar at the top of the screen. This bar tells you what mode you're in, what area of memory you're looking at, and additional information like the limits of the file you're working on or whether the memory safeguards are on or not.

mode 0: Status Bar

Mode 0 isn't really an editing mode. It simply displays a truncated status bar (no program counter), and

PRESS ANY KEY TO CONTINUE

As soon as a key is pressed, it clears the screen and switches to mode 1. This mode is called on startup and any time you EXECute code. Peri Fractic, can I get a keyboard with one of the X16 keys replaced with a key marked ANY, please? Then I could say, there it is.

The reason there is no program counter on the status bar is that there is no simulation or stepping through your program. The program is interpreted into machine code as you type it, so when you execute code you're running the compiled code. There is no way of determining from which address your program has returned, because the processor overwrites that address in the program counter in order to return to the editor.

mode 1: Assembly code, branch to address

The first mode shows the assembly language code in the notation (see section 7) unique to this editor. This mode also shows all branches as being to an absolute address rather than to a relative address. The branch commands themselves are displayed differently in this mode e.g. BNEA instead of BNE#. In either mode 1 or mode 2 you can enter branches as either to an absolute address or a relative address (though they are all stored as relative addresses), but in this mode all branches are displayed with absolute addresses.

Each line on this screen corresponds to one command. It shows the address, the command, any parameters, the list of 1-3 bytes representing that line of code, and any comments. If the address at the top of the screen happens to be a parameter rather than a command, the editor will still try to display it as a command. As a result the top few lines of the screen may be muddled. Moving the cursor up to the top of the screen, and then up one or two more times, will eventually bring an actual command to the top of the screen.

If the address at the top of the screen is greater than $9FFF, then you're looking at banked memory. In that case, the editor uses the top line of the screen to show which bank you're viewing. If it is a RAM bank, then the top line also shows whether that bank is in one of the protected memory areas (either showing TAME or WILD in the top right), or the BEND address. ROM banks have a different message for each bank, noting the contents of that ROM bank.

mode 2: Assembly code, branch relative

The second mode differs from the first mode in the way it displays branches, always showing branches to relative addresses. You can still enter branches to absolute addresses in this mode, but they will immediately be displayed as the correct relative addresses.

In either mode 1 or mode 2 you can enter zero page parameters as a pointer to the low byte of a label, using the < key followed by the label. If the label you choose isn't on zero page the editor will point a finger and laugh at you and just abort out of the command entry, the command won't be stored in memory, and the display will revert to as it was before you started typing the command.

This includes the BBR and BBS commands as well, but the other way around. In mode 2 a command might be displayed as BR7A 03,LOOP and in mode 1 that same command would be displayed as BBR7<ZPLB,F3, as long as the label ZPLB is attached to address 0003. You could enter that command in either mode 1 or 2 as BR7A<ZPLB,LOOP or BBR7<ZPLB,F3 and the display would immediately change to BR7A 03,LOOP in mode 2 or BBR7<ZPLB,F3 in mode 1.

mode 3: Data mode, block byte

This third mode shows 29 rows of 8 bytes per row, with each of the 8 bytes first displayed as hex digits, and then displayed as 8 characters. If a byte is not a printable character then it is displayed as a default checkerboard character.

The address at the left side of the screen is the address of the first of the eight bytes on that row. Data can be entered wherever the * cursor is, as hex digits. In this mode, you can use the left and right arrows as well as the up and down arrows to move the cursor around. The editor will only allow you to enter hex digits, cursor moves, or F1-F5.

mode 4: Data mode, single byte

The fourth mode shows one byte per row, first as two hex digits and then the individual bits, with a period representing a zero and a filled circle representing a one. Any comment attached to that address is also shown. Data can be entered as hex digits wherever the * cursor is, or you can move the cursor up or down. Other than that, F1-F5 are the only other keys the editor will recognize.

mode 5: Character mode

The fifth display mode shows one byte per row again, this time represented as characters. The hex digits of the byte are also shown, and any comment attached to that address. In this mode, characters can be entered wherever the * cursor is. Certain keys will not be entered as characters in this mode: the up and down arrow keys just move the cursor up and down, and the F!-F5 keys will just switch mode. However, all other keys on the keyboard can be entered as characters this way.

If a byte is not a printable character, then it is shown as a default checkerboard character.

Function and Arrow Keys

In all editor modes, F1-F5 switches the display mode, and the up and down arrow keys move the cursor. The left and right arrow keys only work in mode 3.

If you press a mode key (F1-F5) to switch to a mode you're already in, then whatever address is pointed to by the cursor is moved to the top of the screen. Moving the cursor to the bottom of the screen is very fast, so if you move the cursor to the bottom and then press the F1-F5 key again, it's almost like using a Page Down. You can scan through a program very quickly this way.

Assembly Language Notation

Four Character Codes

In an effort to keep the editor as small as possible, I had to make certain tradeoffs. The most important tradeoff was changing the notation of the assembly language code from a standard notation to one where each command is represented by a unique four- character code. For most commands, the first three characters are identical to the standard notation, and the fourth character represents the addressing mode. It might raise the hackles of experienced 6502 programmers, but it doesn't take long to get used to the notation, and once you're used to it it's actually easier to read than the standard notation; one's eyes move straight down the screen instead of bouncing left and right.

Let's take LDA as an example. This command loads the accumulator. There are nine different LDA commands, and in standard 65c02 editors a parser would have to figure out which of the nine LDA commands was desired by looking at the syntax of the parameters which followed. In this editor however, each of the nine LDA commands has a unique fourth character indicating addressing mode, eliminating much of the code needed to parse user input. By entering the unique four character code, the editor knows exactly what type of parameters to expect and will only allow those type of parameters. For instance, LDAA expects a parameter four characters long, either an address or a label. It won't take just two or three characters. Five is RIGHT OUT.


Addressing mode4-character codestandard notation
immediateLDA#LDA #
zero pageLDA0LDA zp
zero page,XLDAZLDA zp,X
absoluteLDAALDA addr
absolute,XLDAXLDA addr,X
absolute,YLDAYLDA addr,Y
(indirect,X)LDA+LDA (zp,X)
(indirect),YLDA-LDA (zp),Y
(indirect)LDA/LDA (zp)

There are a couple of commands (LDX and STX) that have a zero page,Y addressing mode. These are represented by the four- character codes LDXZ and STXZ.

There is also an implied addressing mode (e.g. INXI, TAYI, PHYI, RTSI). There are a few of the Implied mode commands represented with other characters than I as the fourth character, mostly having to do with flags or clarity: e.g. CLCF, SECF, CLID, SEID, CLDM, SEDM, NOPE, BRK0, WAIT, STOP

The new 65c02 commands RMB0 through RMB7 and SMB0 through SMB7 are the same as the standard notation, since they are already unique four-character codes.

The branching commands are all in a relative addressing mode. For BBR0 through BBR7 and BBS0 through BBS7, the commands are identical to standard 65c02 notation, since they're already unique four-character codes. The other branch operations use the symbol # to represent relative addressing mode, with one exception: the new 65c02 unconditional branch command has the code BRAN.

Relative branches are easy for the computer to understand, but a little harder for programmers. The editor has the ability to input branches to an absolute address, which it then converts for you to a relative address. These pseudo-commands are: BPLA, BMIA, BVCA, BVSA, BRAA (the new unconditional branch, but to an address), BCCA, BCSA, BNEA, and BEQA. There are equivalent commands for the BBR and BBS commands as well.

A few commands - ASL, LSR, ROL, ROR, DEC, and INC - have both an absolute addressing mode and an Accumulator mode. They can't both use the letter A as the fourth character. For these six commands the Accumulator mode is treated as Implied mode.


command Addressing modestandard notation4-character code
ASL AccumulatorASL A ASLI
ASL absoluteASL addrASLA
LSR AccumulatorLSR A LSRI
LSR absoluteLSR addrLSRA
ROL AccumulatorROL A ROLI
ROL absoluteROL addrROLA
ROR AccumulatorROR A RORI
ROR absoluteROR addrRORA
DEC AccumulatorDEC A DECI
DEC absoluteDEC addrDECA
INC AccumulatorINC A INCI
INC absoluteINC addrINCA

The 6502 had a number of unused / undocumented opcodes, some of which actually did something useful and some which wreaked havoc. This has been changed for the 65c02, where all unused opcodes are one, two, or three byte NOP commands. These are represented in the editor by the codes NOP1, NOP2, and NOP3, respectively.

The full list of commands is given in both Appendices A and B. Appendix A lists the commands in order of opcode, with the 4-character codes assigned to each command and a link for each to Appendix B. Appendix B lists the commands in alphabetical order, including the addressing mode, opcode, 4-character code, standard notation, number of bytes, number of cycles, and the flags affected by each command.

Parameters

Once you enter a four character command, if it is a two or three byte command then you need to enter the parameters. For three byte commands (other than BBR0..7 and BBS0..7) the parameter entered can be either an absolute address or a label (see section 9) or an address offset some number of bytes from a label. For the BBR and BBS commands, the parameters are a zero page address and a relative branch address.

For two byte commands, the editor expects the parameter to be two hex digits, for the most part. However, there are exceptions to this to make the programmer's life easier, detailed next.

Referencing the Low Byte, High Byte, Bank Number, or String Length of a Label

For immediate mode commands such as LDA#, you can refer to an attribute of a label: either the length of a string associated with that label, the low byte of the label, the high byte of the label, or the bank number. For instance, if a string has been associated with a label then we can load the accumulator with the length of the string like this:

LDA#L and then the label name.

Or, to load X with the low byte of a label:

LDX#< and then the label name.

Similarly, to Exclusive-Or with the high byte of a label:

EOR#> and then the label name.

If you want to load the accumulator with the bank number of a label located in banked memory:

LDA## and then the label name.

When you view your code in mode 2, these immediate mode parameters are displayed as hex digits, but in mode 1 they are displayed with the L or < or > or # in reverse character mode, and then the associated label is displayed.

Any time the memory location of any label referenced this way changes (via the INSErt, DELEte, or MOVE functions), the parameters referring to the low byte, high byte, or bank number of that label are changed in memory to match. Any time the string length of a string attatched to a label changes (via STRIng, DSTR, INSErt, or DELEte), then any parameters referring to the string length are changed in your code to match.

There is space in the metadata tables for 1024 references each to string lengths or bank numbers, and space for 1536 references each to the low byte or high byte of a label.

Referencing Zero page addresses by Label

For zero page commands like LDA0 or STA0, if the zero page address is associated with a label, then you can refer to the label instead of entering the address as hex digits:

LDA0< and then the label name

This is a change from version 1.1 of the editor, which used to use a different symbol because I hadn't thought it through.

This works for any labels in zero page, and will reject the input if the label isn't in zero page. It also works on the zero page parameter of the BBR and BBS commands.

In mode 2, the zero page parameters are displayed as labels if a label is associated with that zero page address, and in mode 1 the zero page parameters are displayed as two hex digits.

Reference by label address

For three-byte commands (other than BBR or BBS commands), the editor is expecting either a four-character hex address or a label. Entering a label as the parameter not only increases your program's readability, it also links that label to the address of the parameter. The label's address is stored in memory as the parameter. Any time that label's address changes (via the INSErt, DELEte, or MOVE functions), all parameters linked to that label also change to match the new address.

To refer to an absolute memory address by label, just enter the label in the parameter field. e.g.:

LDAA HEXD

If a parameter is given as a hex address, and that address corresponds to a label, then the label is considered an implied label and will be displayed in reverse character mode. Only labels that are explicitly entered by the user are stored in the metadata tables and displayed in normal character mode. This is useful if you are reading code written by someone else, such as the ROM subroutines. For instance, you can add a label to a ROM address, and then any time that address appears as a parameter in the same ROM bank your label will be shown instead.

If you are programming in banked RAM, the editor will not recognize labels that are in other banks of RAM. There is one exception to this, the WORD marker can accept a label in another bank as a parameter. This prevents you from calling a subroutine in another bank without using JFAR, or from reading or writing to or comparing from another bank without using FTCH or SFAR or CFAR. This only applies to 3-byte commands like JSRA, LDAX, STAA, etc.; you can still refer to just the low byte or high byte or string length or bank number in 2-byte immediate mode commands.

There is enough space in the metadata tables for 7168 references by label address.

Reference by address plus offset

For three-byte commands (other than BBR or BBS commands) it is also possible to make a reference to a label's address plus some offset value between 00 and FF. An offset of 00 is the same as the label's address, which is useful in the unlikely case that you run out of space in metadata for references by address. Any other value is the label's address plus that offset value.

To enter an absolute address as an offset from a label, press the + key followed by the offset value. The editor will then print a comma, after which you enter the label. e.g:

LDAA+04,HEXD

This will look up the address of HEXD, add 04, and store the result in memory as the actual parameter. It also stores the offset value and label in memory attached to the address of that parameter. If the label moves (via INSErt, DELEte, or MOVE) then this result is changed to match. If you INSErt or DELEte bytes between the label address and a calculated offset address from that label, that offset is also changed to match. So, if you were referring to a byte offset +0C from a label, and later inserted 4 bytes of code between the label and the label plus 0C bytes, then that 0C would change to 10 for that label offset.

If an INSErt pushes an offset past FF bytes, or if a DELEte is deleting data at the address being pointed to, then the metadata is removed from the list, so be careful when deleting data referred to by other parts of your program.

This type of reference is very useful if you're writing self-modifying code. The first couple of commands of a subroutine could be used to store data at offsets from the start of the subroutine, data which could either be commands or parameters. That code then changes depending on what you stored in the first few commands of the subroutine.

There is enough space in the metadata tables for 1280 references to address plus offset.

Editor Functions

Besides 65c02 commands, there are 32 editor functions and 25 pseudocommands recognized by the editor.

These functions and pseudocommands only work in modes 1 and 2.

GOTO

GOTO allows you to go to any address. Just type GOTO and the address or label. The address will then be moved to the top of the screen and the cursor will point at that address.

BANK

When the address at the top of the screen is greater than 9FFF, the BANK function becomes available. The message bar at the top of the screen indicates which bank of memory you're looking at. If the address is greater than BFFF, then the ROM bank number and bank name are in the message bar.

Betweem A000 and BFFF is a RAM bank. Some of these are protected banks; you cannot edit these banks without taking the safeguards off. Bank 00 is used by the system, and the last 16 banks of RAM are used by the editor. In these banks, the message at the top right of the screen will either be TAME (safeguards are on) or WILD (safeguards off). In all other RAM banks, there is a different message, "BEND:" followed by an address. This indicates the address of the last byte of code plus one.

In addresses above 9FFF, you can switch between banks by typing BANK and the destination bank number. In ROM, whatever two digit hex value entered is ANDed with 1F to keep it in the 32 bank range. In RAM, type BANK followed by the hexadecimal two digit address.

BACK

Whenever you enter a command that changes the address at the top of the screen (not including cursor moves, of course), the editor records the address at the top of the screen before making the change. Whenever you want to go back to the address you just came from, just type

BACK

and the editor will switch the address to that recorded address. BACK itself is a function that changes the address at the top of the screen, so it too records the current address before making the switch. So, you can type BACK repeatedly to toggle between two addresses.

QUIT

QUIT takes no parameters. It triggers an ARE YOU SURE?(Y/N) prompt, which waits for Y or N to be pressed. If Y, the screen returns to the normal 80 column mode and is cleared and control is returned to BASIC. If N, it returns back to mode 1 or 2.

EXEC

EXEC turns over control of the computer from the editor to the code located at the address entered. Just type EXEC and the address or label of the address to be executed. If your code ends in an RTSI command, then when your code is finished control will return to the editor in mode 0, displaying the status bar before returning you to mode 1. This is very useful for testing sections of code or even an entire program.

Because control of the computer is being turned over to whatever code you're executing, unexpected things could happen to the display upon return; colors might be messed up, for instance. If that happens, type EXEC NULL and the screen should be reset to normal. NULL is a null subroutine - its only command is RTSI - but calling it should trigger the screen reset.

The editor uses almost all of the zero page adresses between 02 and 7F. The most critical of these bytes are saved in the space set aside for that purpose in the editor, and when EXEC returns control to the editor these bytes are copied back down to zero page. Your own programs will likely be using lots of zero page addresses as well, and if you want to read those addresses after doing an EXEC, you should include something in your program that copies those bytes to a different area of memory, as they may be overwritten by the editor.

MetaMeta: LIST, FREE, HELP, TAME, WILD, USED, and CRAM

LIST

Once your program becomes large, you may have hundreds of labels. It's pretty easy to forget which label is which after a while. It's also pretty easy to forget a label name when you've got hundreds of them on the go. Since you can't GOTO a label that you can't remember, you could find yourself looking through lots of code trying to find a specific label.

But there is an easier way. LIST gives you an alphabetical list of all labels, both yours and the default labels. You can then press the first letter of the label you're looking for, or use the up and down arrow keys to go through the label list. Besides the label itself, the address and bank number are also shown, along with any comment that happens to be at that address. So, if you're looking for a label and you think it begins with the letter G, just type LIST and G and you'll be taken to the labels beginning with the letter G. If you have comments at the same address as a label on most subroutines, then you'll easily see the label you're looking for.

Once you have the cursor at the label you want, press a function key F1-F5 to go to that label in the corresponding editor mode. This changes the address that was at the top of the screen, so the old address is recorded before making the switch, and BACK will take you back to where you were before you typed LIST.

If you want to leave the label list without changing your location in memory, just press the backspace key.

FREE

This function just displays a small information window. Listed are each of the ten metadata types, the number of slots used so far, and the number of slots remaining. This is mostly useful when your program has become very large - more than filling up low RAM - and you're reaching the limits of the available space in the metadata tables. All the numbers listed by each metadata type is diplayed in hexadecimal notation.

HELP

HELP brings up the help screen, which lists most of the editor functions and a brief description of each. The last two functions added to the program, FREE and BACK, are not shown on the help screen because there just isn't room without scrolling, which would have just added complexity without any real added value. Press any key to return to the editor from the help screen.

TAME and WILD

There are several areas of RAM used by the system and the editor. Changing bytes in these areas of RAM can have unexpected results, and may cause your computer to freeze or crash. So, you don't want to accidentally be changing data in these areas. But, it's YOUR computer and you can do what you want. If you intend to edit those protected areas anyway, you should be able to do that if you want.

In order to prevent inadvertent editing of system-critical RAM, there is a safeguard called TAME. When the cursor is at an address in one of these protected areas of memory, the upper right corner of the screen will show either TAME or WILD. When TAME is displayed in the top right corner, memory in that area can not be edited; the editor treats it as ROM. When WILD is displayed in the top right corner, the editor treats that area of RAM just like any other RAM, and you can edit to your heart's content.

To turn the safeguards off, type WILD and WILD will be displayed in the top right corner. To turn the safeguards back on and prevent accidentally changing critical system data, type TAME and TAME will again be displayed in the top right corner. The editor always starts in TAME mode.

The protected areas in low RAM are addresses 0000-7FFF and 9F00-9FFF. 0000-00FF is Zero Page, 0100-01FF is the Stack, 0200-03FF is System RAM (used by the Kernal and other ROM subroutines), 0400-07FF is Golden* RAM (used by the editor), and 9F00-9FFF is the Memory-Mapped I/O area (physically connected to VERA, the VIAs, and other I/O ports).

(* as an aside, Golden RAM is an area of RAM on Commodore computers that was used by the tape cassette. Yes, people used to store computer programs on the same type of cassette tapes that they used to play music. If you had a floppy disk on a Commodore, though, that RAM was available for use, untouched by BASIC. Golden RAM was an extra one kilobyte nugget of gold on a system that only came with 3583 bytes of RAM. BASIC still starts at hex address 0801, and the Kernal is still in use, but we're using SD cards instead of cassette tapes here, so that kilobyte is still untouched by BASIC. I used that kilobyte for the subroutines that the editor simply had to have in low RAM, but many of those subroutines are just useful in general.)

There is also protected RAM in some RAM banks. Bank 00 is used by the system for things like file access and other things that just don't fit into low RAM. Changing anything in that bank could mean that certain parts of your system don't work correctly or make the system crash. So, bank 00 is protected with TAME and you need to type WILD and enter WILD mode to edit bank 00.

Most of the editor and all of the metadata tables are located in the last sixteen banks of RAM, so banks F0-FF are also protected. If you really want to, though, you can type WILD and tinker with the editor itself, and type TAME to turn the safeguards back on.

The Commander X-16 will have several variants. There will either be one, two, or four 512k RAM ICs devoted to banked RAM, giving either 512kb, 1Mb, or 2Mb of banked RAM. In a 2Mb system there are 256 banks of RAM, each with a unique bank number. In a 512kb system though, there are only 64 banks of RAM. Banks 40-7F, 80-BF, and C0-FF are all just wired to banks 00-3F. Similarly, for a 1Mb system there are only 128 banks of RAM and banks 80-FF are just wired to banks 00-7F. The editor recognizes the size of system that it is on, and for 1Mb or 512kb system the editor also protects banks 70-80, and a 512kb system protects those as well as banks 30-40, and B0-C0.

USED

Once a program becomes large, it's easy to forget what each subroutine is for, and with enough edits it's possible to have subroutines that are no longer used. You may find yourself wondering, do I even use this subroutine anymore? That's what USED is for. Just go to any label and type USED, and a list is created of every time something branches to that label or is referred to by any other part of your program. If nothing calls that subroutine and nothing branches to it, a message will appear saying that the label is not used. However, that does not mean that the code there isn't actually being used - your main program isn't called by anything else, for instance. But if USED says it isn't used and it isn't obviously important, that's a good indication that you can probably do without that subroutine.

USED is also a good way to go backwards through your code. If you scroll through the list and click on a function key from F1 to F5, the display will jump to that line of code. You can always use BACK to then go back to the label you were interested in.

CRAM

When the emulator switched to r39, one of the major changes was moving the RAM bank pointer from 9F61 to 0000 and the ROM bank pointer from 9F60 to 0001. These correspond to the default labels RAMB and ROMB, and if you had been programming in META/L for r38 and used these labels rather than the addresses, you're in luck; your program can be salvaged. The R41PATCH.BIN patch file takes care of updating any of your code that uses RAMB and/or ROMB. However, the patch can only work on code that's already in memory.

If your program loads a bunch of files with code in them, and some of those files use the RAMB or ROMB labels, the patch won't fix those. Instead you need to load those other files into memory and then type CRAM. This function calls the part of the patch subroutine that updates RAMB and ROMB, so that your code uses the correct updated values. After that, you need to save those files so you don't have to CRAM again.

The CRAM function will probably only be around for this version of the editor; it likely won't be needed in future versions of the emulator or the final Commander X16 hardware, so another function might take its place in future.

File functions:

META : Is So Meta Even The Acronym

You may want to explore the editor to get some ideas for how to do things in your own assembly language programs. The editor's own metadata files are stored separately from the default (nearly empty) metadata files with which you start writing your program. If you want to explore the editor, first SAVE your program to save an up to date copy of your metadata, and then type

META

This will bring up a prompt reminding you to save first, and if you continue will bring up a list of all low-RAM programs saved by the editor. Each of these files has its own metadata files. You can use the up and down cursor keys to scroll through the list, and then press space to select the metadata files you want. If you change your mind and don't want to change metadata, just press the backspace key and you will return to the editor.

If you put the cursor on the EDITOR entry and press space, then all the metadata associated with the editor is loaded into memory. You can then see all the labels and comments and other metadata for the editor. Then you can answer such questions as "how'd he display hex characters?" or "why did he do it that way?" or "what in God's name was he thinking?". You can also explore through the entire program, and COPY sections of code to other areas of RAM if you like. When you're done, just type META again to re-load your own program's metadata.

Besides the default and editor metadata files, there is space for another 125 programs' metadata files in this list. If you delete a program from your file system, then you can also delete it from the META list by scrolling to that entry and pressing the X key. The editor will ask if you are sure you want to delete that file, and if Y the entry is removed from the list. The default metadata and editor metadata cannot be removed from the list.

Any time you save a program that is located in low RAM, there will be a set of metadata files saved which are linked to that program in the META list. If you are saving a new copy of a program that is already in the list, then only the associated metadata file name might change, and that change would only be in a file extension in a future version of the emulator.

SAVE

SAVE lets you save your work. To save a file in low RAM just type SAVE. The function then prompts for a filename, up to 32 characters long including path and file extension. The only characters allowed are letters, numbers, the period, the slash, and Return. There are three filenames that are not allowed, regardless of file extension; those prohibited file names are EDIT, DEFAULT, and SEQF. If a file name is invalid (matches a prohibited name, or starts with a slash, or has spaces) then an INVALID FILENAME message is displayed. Note that SAVE will not create a folder for you, so if you are saving to a folder (include the path at the start of the filename) then that folder must already exist, or your file will not be saved.

The file save starts at the FSTR file start marker, and ends at the address one byte less than the FEND file end marker.

Then the function prompts ARE YOU SURE?(Y/N) and waits for either a Y or N to be pressed.

If Y, the function creates another filename by searching through the file name for the first period, placing the file extension .LB1 afterwards. If the filename is so long that the first period is the 23rd or later character, then the label filename is the first 23 characters of the user's filename followed by .LB1 ; if there is no period in the user's filename, then .LB1 is appended to the user's filename at the end or at the 23rd character, whichever is less. (In the future when this is saved as a sequential file the extension will be .SEQ) The string "META/" is prepended to this file name. This LB1 filename is the first of 14 metadata files associated with your program.

Both your program file name and the first metadata file name are then saved in the META/LIST.DAT file used by the META function, and then your file and the associated metadata files are saved.

It's important to note that the EDIT.PRG file starts at $0400, and that is also the default value of FSTR. The only BASIC command in this file is

1 SYS$0400

The command stored at $0400 loads and runs the editor, but when you want your program to run upon loading rather than the editor, just change the SYS call in BASIC to the start of your program. If you want to get back into the editor after having done that and saved your program, just load your program and type

SYS 1024

This will go into the routine that loads and launches the editor and loads the metadata for your program. This is also useful if your programs crashes while testing; just Ctrl-R to do a soft reset, and SYS1024 to load the editor and the metadata stored in your last SAVE. Of course a crash could have been caused by something overwriting part of the editor, so a reload may be necessary. It's always a good idea to save your program before testing with EXEC.

saving banks of RAM

If the address at the top of the screen is in banked RAM, SAVE operates differently. Instead of using something like FSTR and FEND, SAVE will just save the entire 8k bank of RAM as a file. If you're in banked RAM and type SAVE, you'll be prompted for a filename, and then the whole bank will save with that name.

If your program is in both low RAM and banked RAM, you must save the program in low RAM in order to save the metadata associated with your code in banked RAM. SAVE does not do anything with metadata when saving banked RAM, and only saves from banked RAM into files.

In the last version of the editor I had a complicated way of saving multiple banks with default file extensions and, nah, why bother with all that? So I took that out and now it just saves a single bank with whatever filename you choose.

LOAD

This command only works when the address at the top of the screen is in banked RAM. It does not work if the bank is a bank of protected RAM. This will prompt you for the file name. It will then try to load a file of that name into the bank. Do not load any file longer than 8kb into RAM using this function, as the loading file will overwrite RAM at $9000 and beyond - write your own code to load files longer than 8kb.

If your program is in both low RAM and banked RAM, you must save the program in low RAM in order to save the metadata associated with your code in banked RAM. LOAD does not do anything with metadata, and only loads from file into banked RAM.

In your final program, you must write part of your program to load these banked RAM files. There are some subroutines in Golden RAM than can help with this task.

File markers: FSTR, FEND, BEND

In low RAM, the markers FSTR and FEND tell the editor where your program code is located. By default, FSTR is set at $0400 and FEND is set at $0818, the first byte after the BASIC bootstrap program. As you type code in low RAM, FEND is updated automatically.

You can manually set FSTR and FEND if you want. Just go to the location where you want to set the marker and type FSTR or FEND. The FEND marker must be in low RAM below $9F00, the FSTR marker must be in low RAM above $3FFF, and FSTR must be before FEND.

In banked RAM, there is something similar to FEND called BEND. This is a unique address for each bank of banked RAM. This will also automatically update as you type code in banked RAM. However, BEND is not used to mark the end of a file, but rather the end of your code in that bank.

FEND and BEND are used by the INSE and DELE functions to let the editor know where to stop when inserting bytes for new code or deleting bytes from your code.

Main meta: SLBL, DLBL, COMM, DCOM

These four commands set and delete labels and comments, respectively. Labels and comments are the two main types of metadata, so each gets its own section below.

Code markers: WORD, DWRD, BYTE, DBYT, STRI, DSTR

Sometimes data needs to be entered within your code that isn't a 65c02 command. Occasionally you'll want to mark an address as being a word (ie 16 bit data) or a single byte of data. For instance, the JSRFAR subroutine in the CX-16 API needs to be followed by a word to indicate the address being jumped to, and a byte to indicate the block of paged memory. Suppose you wanted to JSRFAR to memory location A432 in block 1C of RAM:

JSRA JFAR ; calls JSRFAR
WORD A432 ; address
BYTE 1C ; block of paged RAM

If you want to deallocate a WORD, just go to that address and type DWRD. Similarly to deallocate a BYTE, just go to the address and type DBYT.

Suppose you want to allocate a section of memory (up to 255 bytes) as a string. To do so, first set a label at the first byte of the string, then at that address type

STRI

The editor will fill in NG and prompt you for the number of bytes in the string. If you enter 00 then the function will abort to the editor, and if a string had been allocated there it will be deallocated. Otherwise the editor will declare that label to be a string of length equal to the number of bytes you entered. If you had previously entered a string length and then change it later on, any immediate mode command that refers to that label's string length will be changed automatically.

Any label with a string length greater than zero is displayed by the editor as the PETSCII characters corresponding to the bytes in the string. Any non-printable character is displayed as a checkerboard character. Although your string may be up to 255 bytes, only the first 13 are displayed if the string length is greater than $10, followed by an ellipsis. If the string is 16 characters or fewer then the whole string is displayed.

A more convenient way to deallocate a string is to go to the first byte in the string and type

DSTR

This will deallocate the memory as a string (by setting the string length to zero). Any time a string is deallocated, any immediate mode command that points to a string length will also be deallocated.

Only user labels have string length parameters; no default label is a string. That's why HEXD is the only label set at startup; although it is set by default it is not a default label. You could rename it if you like, or even delete the label. However, HEXD is a very useful string, "0123456789ABCDEF", so you might want to keep it around anyhow. Only delete it if you have never used it in your program and you're out of space for more labels and absolutely must use that 1536th label.

Code block: COPY, MOVE, INSE, DELE

These four functions allow you to shuffle around large blocks of code.

COPY and MOVE

The COPY and MOVE functions are closely related. For each of these commands, there are three prompts: FROM, TO, and #BYTES. Unsurprisingly, these are the source address, destination address, and number of bytes to be moved or copied. Enter a number from 01 to FF for #BYTES; entering 00 means 256 bytes. If the destination address is in a protected area of RAM and the editor is in TAME mode or if the destination address is in ROM then the COPY or MOVE is aborted.

In both cases, if the data is being copied or moved from a lower address to a higher address, it starts at the last byte and works its way to the first, and if going from higher address to lower address it does so from first to last. This avoids the possibility of accidentally overwriting data before it can be copied or moved.

In both cases if there is any metadata at an address in the destination group of bytes, that metadata is deleted; if on the other hand a user label is attempted to be MOVEd onto an existing default label, the user label gets deleted. Of course, the only default labels attached to RAM locations are in protected memory areas, so you would need to be in WILD mode to make this happen at all. Although you can MOVE code into these memory locations in WILD mode, it's probably not a good idea to do so.

The difference between COPY and MOVE is that COPY does not affect the source data. For MOVE, if any metadata is encountered in the source data, that metadata is also moved to the destination (unless the label is being moved onto a default label). And once data is moved to the destination, the source byte is replaced by a NOPE.

Note that when you MOVE a byte from an address associated with a label, the label itself moves and any code that references that label also changes. Any parameter metadata that references that label (other than string length) will also change to reflect the new label address.

To COPY or MOVE data into banked RAM or from one bank to another, simply place a temporary label at the destination address. Then you can refer to the label of your source in the FROM and the temporary label in the TO, and COPY or MOVE will shuffle the code into the destination.

INSE(rt) and DELE(te)

These two commands take a lot of the pain out of assembly language programming, particularly when you're debugging.

INSE and DELE are closely related to the MOVE command, except instead of moving a small number of bytes these commands shift your entire program by the #BYTES entered (a value of 00 means 256 bytes), starting wherever the cursor happens to be. The end point of this shift is either FEND (for low RAM) or BEND (for banked RAM; there is a unique BEND for each bank). If an INSErt would push FEND past address $9FFF or BEND past $BFFF, then the function is aborted. Otherwise FEND or BEND is adjusted by #BYTES up for INSE and down for DELE. It is a good idea to verify that FEND or BEND are properly located at the end of your code before using INSE or DELE.

After shifting all the data, INSE replaces the #BYTES from the current editing address with NOPEs. DELE does the same at the end, counting down from FEND or BEND before those get moved.

Any label that gets moved alters any code referring to that label in the same way as does MOVE.

When INSErting or DELEting bytes within a string, the string length value changes and any command referencing that string length changes to the new length.

Branches within 128 bytes that need to be changed get changed automatically. If an INSErt pushes the branch beyond 127 bytes forward or 128 bytes backward, the branch is changed to a relative address of 00 (the next command). Similarly if the branch is to an address being DELEted, the branch is again changed to a relative address of 00. If a branch is to a relative address of 00, the editor will display that branch command in reverse characters; this really stands out as you're reading your code.

If the cursor is between a label and a label plus offset reference anywhere in the program, then that offset value gets increased by #BYTES for an INSErt or decreased for a DELEte. If the offset+label address is itself being DELEted, or if an INSErt pushes the offset past 255 bytes, then the offset metadata is deleted but the data in the code is not changed.

Oh yeah, there's one undocumented function thrown in there as an Easter egg. Just Enter the Frakkin Formula. Join Every Flying Fraternity.

Branch-to-address pseudocommands

All branch commands on the 65c02 are to relative addresses. The parameter is the relative address in two's complement notation, so the byte represents an offset in the range -128 to +127. That offset is calculated starting from the address of the command following the branch command.

Figuring out the relative address can be done by simple counting if it's within a short range, or by subtracting the destination address from the address of the command following the branch.

However, we're people, and want to do things the easy way. For each of the following branch commands

BPL#, BMI#, BVC#, BVS#, BRAN, BCC#, BCS#, BNE#, BEQ#

There is a corresponding pseudocommand

BPLA, BMIA, BVCA, BVSA, BRAA, BCCA, BCSA, BNEA, BEQA

These pseudocommands take an address or a label as a parameter, and convert it to the relative address for you. In memory, the regular branch command corresponding to the pseudocommand is stored, along with the correct relative address.

In editor mode 1, all these branches are displayed as if they were the pseudocommands, showing branches to an address or label. In mode 2 however they are shown as the regular branch commands and relative addresses, exactly as they are stored in memory. In either mode 1 or mode 2 you can type in either the regular branch command or the pseudocommand, the editor doesn't mind.

The BBR0..7 and BBS0..7 commands also have associated pseudocommands:

BBR0..7: BR0A, BR1A, BR2A, BR3A, BR4A, BR5A, BR6A, BR7A
BBS0..7: BS0A, BS1A, BS2A, BS3A, BS4A, BS5A, BS6A, BS7A

These pseudocommands can be used in place of BBR0..7 and BBS0..7; the editor will just expect an absolute address or a label for the second parameter instead of a relative branch address. The code is stored as the corresponding BBR0..7 or BBS0..7 code, but only displayed that way in mode 2. In mode 1 these commands are displayed as the pseudocommands, with branches to addresses. In mode 2, if the zero page address referenced is attached to a label, then the label of that address is displayed instead of the 2 hex digits for the address, and in mode 1 the zero page address is displayed as hex digits.

If the zero page address used as a parameter in BBR* or BBS* or BR*A or BS*A happens to be attached to a label, then you can enter the label when entering the command:

i.e. BR7A<GLOP,addr

If the address you have chosen is outside the -128 to +127 range from the address of the next command, then the branch is not entered into memory.

Labels

When writing an assembly language program, you're usually not writing one huge program but rather hundreds of subroutines. Many of those subroutines will have lots of JSRs and JMPs to these various other subroutines. Well, one hex address pretty much looks like any other, and it's easy to make mistakes jumping to the wrong address.

To make life easier for programmers, the editor allows you to set a label on an address. The programmer can then use the label anywhere one otherwise would have used the address. This makes the program much more readable, as a series of JSRs is not to obscure addresses but to slightly-less-obscure labels.

You can have up to 1536 labels in your program. There is one special label already set, HEXD, but the pther 1535 labels are yours. There are also default labels for the Commodore Kernal, the CX-16 API, the VERA registers, and the 16 bit subroutines included with EDIT.PRG, plus a few more. These default labels are stored separately from your labels.

setting and deleting labels

A label is a four-character code starting with a letter, with the remaining characters being only letters, numbers, and the symbols # + - / < >

To set a label (mode 1 or mode 2 only), move the cursor to the address you want to label and type

SLBL

Then enter the four character code you want for your label.

At least one of the four characters must not be hexadecimal; there has to be a letter higher than F or a symbol in the label. Using BEEF as a label would be bad, because the parser wouldn't know if it referred to the hexadecimal address $BEEF or to the label of the same name. So, attempting to use a hex address as a label is not allowed by the editor.

Also, a label cannot duplicate an existing label; two addresses can't be named the same thing. Attempting to set a label that is already in use would be bad. Try to imagine all life as you know it stopping instantaneously and every molecule in your body exploding at the speed of light. So, the editor won't allow it. (Note: there are four exceptions to this in default VERA labels.)

If the label metadata is full (1536 labels used) then the editor won't allow SLBL to work at all.

If you want to change an existing label, just GOTO that label and type SLBL and the new label. The new label is just stored over the old one.

To delete a label, goto the label you want to delete and type

DLBL

and just like that, it's gone. If you try do DLBL on an address that doesn't have a label or has a default label, nothing happens. If you delete a label that is used by other parts of the program, then the associated metadata is also deleted. So, if your program has a command JSRA GOOP and the label GOOP was located at 5E4C, after deleting the label GOOP your program would read JSRA 5E4C.

default labels

There are 180 labels set by default. These labels are stored in EDIT.BIN, separate from your own labels. They cannot be overwritten by user labels. These labels correspond to the Commodore Kernal, the Commander X-16 API, the VERA registers, 16 bit subroutines, and various other useful subroutines and memory locations in EDIT.PRG.

There are 1901712 possible labels, and I've only used 180, so please don't tell me I "used all the good ones".

Commodore Kernal labels

There are 40 default labels that correspond to the Commodore Kernal jump vectors. The source for these addresses and subroutine names was the Commodore VIC-20 Programmer's Reference Guide (pages 182-210, highly recommended).

Some of the original Kernal functions have been superseded or eliminated; only the ones currently in ROM are listed. There are some Commodore 128 Kernal functions also included.

All Kernal labels are located in ROM bank 4. The Kernal labels, their addresses, and corresponding Kernal subroutine names are as follows:


LabelAddressKernal NameOperation
IECIFFA5IECIN read byte from serial bus
IECOFFA8IECOUT send byte to serial bus
CHKIFFC6CHKIN set channel for character input
CHKOFFC9CHKOUT set channel for character output
CHRIFFCFBASIN get character
CHROFFD2BSOUT write character
CLALFFE7CLALL close all channels
CLOSFFC3CLOSE close a channel
CLRCFFCCCLRCHN restore character I/O to screen/keyboard
GETIFFE4GETIN get character from keyboard
IOBAFFF3IOBASE return start of I/O area
LSTNFFB1LISTEN send listen command
LOADFFD5LOAD load a file into memory
MEMBFF9CMEMBOT read/write address of start of usable RAM
MEMTFF99MEMTOP read/write address of end of usable RAM
OPENFFC0OPEN open a channel
PLOTFFF0PLOT read/write cursor position
RDTIFFDERDTIM read system clock
RDSTFFB7READST return status byte
SAVEFFD8SAVE save a file from memory
SCNDFF93SECOND send LISTEN secondary address
SCRNFFEDSCREEN get the screen resolution
SLFSFFBASETLFS set logical, first, and secondary address
SMSGFF90SETMSG set verbosity
SNAMFFBDSETNAM set filename
STIMFFDBSETTIM write system clock
STMOFFA2SETTIMO set timeout (no function)
STOPFFE1STOP test for stop key
TALKFFB4TALK send TALK command
TKSAFF96TKSA send TALK secondary address
UDTIFFEAUDTIM advance clock
ULSNFFAEUNLSN send UNLISTEN command
UTLKFFABUNTLK send UNTALK command
LUSAFF8ALKUPSA search tables for given secondary address
CLOAFF4ACLOSE_ALL close all files on a device
LULAFF8DLKUPLA search tables for given logical address
FTCHFF74FETCH LDA(fetvec), Y from any bank
STSHFF77STASH STA(stavec),Y to any bank (currently non-functional)
CMPRFF7ACMPARE CMP(cmpvec),Y to any bank (currently non-functional)
PRIMFF7DPRINM print string following the caller's code

Commander X-16 API labels

The Commander X-16 is more than just a souped-up VIC-20 with more RAM. It also has its own API. The following are the 48 default labels assigned to the CX-16 API, the corresponding addresses, and the name listed in the CX-16 Programmer's Reference guide. All the API labels are in ROM bank 4.


LabelAddressAPI NameOperation
CSDTFF4Dclock_set_date_time set date and time
CGDTFF50clock_get_date_time get date and time
MOUCFF68mouse_config configure mouse pointer
MOUGFF6Bmouse_get get state of mouse
JOYSFF53joystick_scan query joysticks
JOYGFF56joystick_get get state of one joystick
SPSIFEF0sprite_set_image set the image of a sprite
SPSPFEF3sprite_set_position set the position of a sprite
CINTFF81CINT initialize VERA chip, upload PETSCII graphics character set
JSRFFF6EJSRFAR execute a routine on another ROM or RAM bank
FBINFEF6FB_init enable graphics mode
FBGIFEF9FB_get_info get screen size and color depth
FBSPFEFCFB_set_palette set (parts of) the palette
FCPOFEFFFB_cursor_position position the direct-access cursor
FCNLFF02FB_cursor_next_line move direct-access cursor to next line
FGPXFF05FB_get_pixel read one pixel, update cursor
FGPSFF08FB_get_pixels copy pixels into RAM, update cursor
FSPXFF0BFB_set_pixel set one pixel, update cursor
FSPSFF0EFB_set_pixels copy pixels from RAM, update cursor
FS8PFF11FB_set_8_pixels set 8 pixels from bit mask (transparent), update cursor
FS8OFF14FB_set_8_pixels_opaque set 8 pixels from bit mask (opaque), update cursor
FFPXFF17FB_fill_pixels fill pixels with constant color, update cursor
FBFPFF1AFB_filter_pixels apply transform to pixels, update cursor
FBMPFF1DFB_move_pixels copy horizontally consecutive pixels to a different position
GINIFF20GRAPH_init initialize graphics
GCLSFF23GRAPH_clear clear screen
GRSWFF26GRAPH_set_windowset clipping region
GRSCFF29GRAPH_set_colorsset stroke, fill, and background colors
GRDLFF2CGRAPH_draw_line draw a line
GRDRFF2FGRAPH_draw_rect draw a rectangle (optionally filled)
GRMRFF32GRAPH_move_rect move pixels
GRDOFF35GRAPH_draw_oval draw an oval or circle
GRDIFF38GRAPH_draw_image draw a rectangular image
GRSFFF3BGRAPH_set_font set the current font
GGCSFF3EGRAPH_get_char_size get size and baseline of a character
GRPCFF41GRAPH_put char print a character
CONIFEDBconsole_init initialize console mode
CONPFEDEconsole_put_char print character to console
CONGFEE1console_get_char get character from console
MEMFFEE4memory_fill fill memory region with a byte value
MEMCFEE7memory_copy copy memory region
MCRCFEEAmemory_crc calculate CRC16 of memory region
MDCMFEEDmemory_decompress decompress LZSA2 block
MONIFF44monitor enter machine language monitor
RBASFF47restore_basic enter BASIC
SSCMFF5Fset_screen_mode set screen mode
SSCSFF62screen_set_charset acrivate 8x8 text mode charset
PFNKFF65PFKEY program a function key (not yet implemented)

VERA labels

The VERA video card has its own set of 36 labels. Following is a list of those labels, their addresses, and VERA names; check out the VERA Programmer's Reference guide for more details.

(note: $9F29-$9F2C each have two labels associated with them. These are the only addresses that can have more than one label. The user can enter either one, and the editor will remember which one it is even though they are the same addresses. The difference is in the VERA DCSEL value; if it is 0 you would use one set of labels and if it's 1 you'd use the other set. This lets you see the context when you read the code later.)


LabelAddressVERA Name
VELO9F20VERA_ADDR_LO
VEMD9F21VERA_ADDR_MID
VEHI9F22VERA_ADDR_HI
VDT09F23VERA_DATA0
VDT19F24VERA_DATA1
VCON9F25VERA_CTRL
VIEN9F26VERA_IEN
VISR9F27VERA_ISR
VIRQ9F28IRQLINE_L
VVID9F29DC_VIDEO (DCSEL=0)
VHSC9F2ADC_HSCALE (DCSEL=0)
VVSC9F2BDC_VSCALE (DCSEL=0)
VBOR9F2CDC_BORDER (DCSEL=0)
VHST9F29DC_HSTART (DCSEL=1)
VHEN9F2ADC_HSTOP (DCSEL=1)
VVST9F2BDC_VSTART (DCSEL=1)
VVEN9F2CDC_VSTOP (DCSEL=1)
VL0C9F2DL0_CONFIG
VL0M9F2EL0_MAPBASE
VL0T9F2FL0_TILEBASE
V0HL9F30L0_HSCROLL_L
V0HH9F31L0_HSCROLL_H
V0VL9F32L0_VSCROLL_L
V0VH9F33L0_VSCROLL_H
VL1C9F34L1_CONFIG
VL1M9F35L1_MAPBASE
VL1T9F36L1_TILEBASE
V1HL9F37L1_HSCROLL_L
V1HH9F38L1_HSCROLL_H
V1VL9F39L1_VSCROLL_L
V1VH9F3AL1_VSCROLL_H
VAUC9F3BAUDIO_CTRL
VAUR9F3CAUDIO_RATE
VAUD9F3DAUDIO_DATA
VSPD9F3ESPI_DATA
VSPC9F3FSPI_CTRL

16 bit subroutine labels

These 18 subroutines are detailed fully in an alphabetical listing in section 11 below. They have already proven very useful as the editor called subroutines from this list more than 180 times, more than 60 calls to CMXY alone.


LabelAddressDescription
ROAL04B4rotate left 16bit zero page variable pointed to by A
ROAR04C6rotate right 16bit zero page variable pointed to by A
SHAL04DCshift left 16bit zero page variable pointed to by A
SHAR04E3shift right 16bit zero page variable pointed to by A
AINC04ECincrement 16bit zero page variable pointed to by A
ADCR04F8decrement left 16bit zero page variable pointed to by A
SBA#0522subtract with borrow X(low byte) Y(high byte) from 16bit zero page variable pointed to by A
SBXY052Dsubtract with borrow 16 bit zp pointed to by Y from 16 bit zp pointed to by X
ADA#053Eadd with carry X(low byte) Y(high byte) to 16bit zero page variable pointed to by A
ADXY0545add with carry 16 bit zp pointed to by Y to 16 bit zp pointed to by X
CMA#0585compare X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
CMXY058Bcompare 16 bit zp pointed to by Y with 16 bit zp pointed to by X
EOA#0595Exclusive-OR X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
EOXY0598Exclusive-OR 16 bit zp pointed to by Y with 16 bit zp pointed to by X
ORA#05A5logical OR X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
ORXY05A8logical OR 16 bit zp pointed to by Y with 16 bit zp pointed to by X
ANA#05B5logical AND X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
ANXY05B8logical AND 16 bit zp pointed to by Y with 16 bit zp pointed to by X

Other Default Labels

There are 13 subroutines that are not directly called by the user; they are used by the 16 bit subroutines or the editor.


LabelAddressDescription
LSL/047F15 bit left shift
LSR/49015 bit right shift
LOP104A3logical operation part 1
LOP204ABlogical operation part 2
GLU10628test for next non-empty bank
GLU20635test next bank or all higher banks empty
BNDI0686initialize bank# shift
B/DI0690check if bank has metadata, adjusting bank# for ROM banks
BN/D069Edelete metadata from a bank
BN/I06A7insert new metadata in a bank
GLUB072Fget metadata list lower/upper bounds given bank number
GMB#0773get metadata bank number given index
MEMP0796set zero page locations 42/43 to point to the RAM bank select (currently $9F61, $0001 in future)

There is a group of 14 labels that correspond to useful subroutines in the bootstrap code:


LabelAddressDescription
S1810409set logical, first, secondary address to 1, 8, 1 respectively
S180040Dset logical, first, secondary address to 1, 8, 0 respectively
LDED0416load editor
LBNK0426load bank of RAM pointed to by X, filename set in FNAM
LB#X0431load bank of RAM pointed to by X, filename set by user beforehand
CPG-0444load file TEMP.TMP into $7000-8FFF
CPG/0448save $7000-8FFF as file TEMP.TMP
CPG>044Amove $7000-8FFF to bank pointed to by Y
CPG<0452move bank pointed to by Y to $7000-8FFF
NULL048Fnull subroutine; the only command is RTSI
GBYP05D5allows you to write subroutines that take an extra BYTE following subroutine call
CFAR05FBreplaces missing Kernal function CMPARE
SFAR0604replaces missing Kernal function STASH
JFAR063AJSRFAR (r39 fixed the bug in JSRFAR, so this now just redirects there)

There are also 8 other default labels are used by the above routines, and you may find them useful as well:


LabelAddressDescription
CBUF07E032 byte character buffer
FNAM07C032 byte filename buffer (note the FNAM buffer and CBUF buffer are contiguous, if you need to copy a subroutine up to 64 bytes long from banked RAM to low RAM temporarily. EXEC does this.)
FNLN042Cfilename length
BYTP0602byte parameter stored by GBYP
ROM#0697number of ROM banks in the system minus one
BASC0801start of BASIC
ROMB0001ROM bank select
RAMB0000RAM bank select

New in version 0712 for r41 of the emulator, there's a half dozen new functions that change the way banked RAM gets loaded and saved; you can delete these labels if you want, it won't hurt anything, but they're helpful for reading the code:


LabelAddressDescription
F/FF063Dset bank FF filename
FTMP0641set temp filename
LBFF064Aload EDIT.BFF into bank FF
L/ED064Fload EDIT.BIN into bank FE
LTMP0661load temp file TEMP.TMP to 7000-8FFF
STMP0670save temp file TEMP.TMP at 7000-8FFF

Finally, there is one user label that comes pre-set; it couldn't be part of the default label list because it has a string length variable attached to it:


LabelAddressDescription
HEXD07B0lookup table of the PETSCII characters that correspond to the hex values 0-F

forbidden labels: FSTR, FEND, and BEND

These three labels are markers on your code and cannot be used as parameters by your program. You cannot EXEC these labels, or COPY or MOVE to or from these labels. You can however GOTO these labels, and change their locations by typing FSTR, FEND, or BEND wherever you want these markers located. Each bank of banked RAM has its own unique BEND value.

Comments

There is space for 1024 comments available for each program you write with this editor. The bootstrap program starts with a set of 40 default comments for some of the short useful subroutines available in $0400 - $07FF. You can delete these comments if you desire.

Because there are only 1024 comments available, and because each is limited to a maximum of 16 characters, your comments need to be useful, concise, and sparse. Comment only what you have to, and cram as much information into your comment as you can. You're limited to using only letters, numbers, spaces, and the symbols + - # / < and > so get creative.

Avoid useless comments; a comment like TRANSFER Y TO A is useless if the command it refers to is TYAI. We can see that it is transferring Y to A. We're smart, not like everybody says.

A comment like JOY UP HANDLE is far more useful. Be like JOY UP HANDLE.

To add a comment to an address, enter the command

COMM

The editor changes it to read COMMENT: and puts a semicolon and a cursor where you type your comment, to a maximum of 16 characters. If a comment already existed at that address, it gets wiped out and replaced with the new comment. If your comment is less than 16 characters you can end it by pressing Return. If the number of comments is already at the limit, then COMM does nothing.

To delete a comment at an address, enter the command

DCOM

and the comment will be deleted. If there was no comment at that address then nothing happens.

Another way to delete an existing comment is to type COMM and then press one space followed by the Return or Enter key. The editor recognizes this as a blank comment and simply deletes any comment that was there before.

If you are changing a comment by overwriting it with a new comment, and then decide halfway through you don't want to change it after all, just press backspace until the cursor disappears from the comment field, and the comment editing will abort.

16 bit subroutines

These subroutines allow you to use any two consecutive zero page locations as a sixteen bit variable. For some operations, X and Y each point to a zero page address which is the low byte of a 16 bit variable; the next byte is the high byte. For other operations A points to a zero page address which is the low byte of a 16 bit variable, and there are some where A is used for that but X and Y are the low byte and high byte, respectively, of a sort of immediate-mode 16 bit value. In the tables below, [A], [X], and [Y] indicate that these registers are pointing to zero page values, and XY is an immediate-mode 16 bit value.

Although any two consecutive zero page locations can be used as a 16 bit variable, you should avoid using locations 00 and 01, and should avoid using 80-FF if you want to also continue using BASIC. If you do use zero page locations in 80-FF, be aware that the 16 bit functions will wipe out any data you have stored at zero page locations 82, 83, 90, 91, 92, and 93.

The SHAR, SHAL, ROAR, ROAL, AINC, and ADCR functions all take [A] as a parameter and the result is stored in those same two zero page addresses. For all the other 16 bit functions, the result is returned in XY. Also, the Z, V, C, and N flags returned by these subroutines exactly match the 8 bit counterparts of these functions.

For instance, an 8 bit ADC# operation will return Z=1 if the result is 00, will set the carry flag if the result exceeds FF, will set the V overflow flag if two numbers less than $80 add up to more than $7F or if two numbers greater than 7F add up to less than 80, and will set the N flag if the most significant bit is 1. Similarly, ADXY and ADA# set Z if the result is 0000, set N if the most significant bit of the result is 1, set carry if the total result is greater than FFFF, and sets V if two numbers less than 8000 add up to more than 7FFF or if two numbers greater than 7FFF add up to a number less than 8000.

These functions pretty much all change A, X, and Y, so use the stack or RAM to save any of these that you need after a 16 bit function call. Calling any of these subroutines will of course use more clock cycles than custom-written inline code that serves the same purpose, but if you find yourself writing a lot of sixteen bit functions over and over these are a much more compact use of RAM.

The 18 sixteen bit functions included in Golden RAM are listed below in alphabetical order by label, and include the label and address, a brief description, the inputs, a symbolic representation of the operation, the flags affected, and the closest 8-bit analogue. In all cases the 16 bit values stored in zero page as inputs must be set before calling these subroutines. Note that the flags are affected in exactly the same way as with the 8-bit equivalent operation.

ADA#

Description: add with carry X(low byte) Y(high byte) to 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
053E[A], XY, CXY=[A]+XY+CC, N, V, ZADC#

ADCR

Description: decrement left 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
04F8[A][A]=[A]-0001N, ZDECI

ADXY

Description: add with carry 16 bit zp pointed to by Y to 16 bit zp pointed to by X
AddressInputsOperationFlags8 bit equivalent
0545[X], [Y], CXY=[X]+[Y]+CC, N, V, ZADCX

AINC

Description: increment 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
04EC[A][A]=[A]+0001N, ZINCI

ANA#

Description: logical AND X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
05B5[A], XYXY=[A] AND XYN, ZAND#

ANXY

Description: logical AND 16 bit zp pointed to by Y with 16 bit zp pointed to by X
AddressInputsOperationFlags8 bit equivalent
05B8[X], [Y]XY = [X] AND [Y]N, ZANDX

CMA#

Description: compare X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
0585[A], XY[A] – XYC, N, ZCMP#

CMXY

Description: compare 16 bit zp pointed to by Y with 16 bit zp pointed to by X
AddressInputsOperationFlags8 bit equivalent
058B[X], [Y][X] – [Y]C, N, ZCMPX

EOA#

Description: Exclusive-OR X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
0595[A], XYXY = [A] XOR XYN, ZEOR#

EOXY

Description: Exclusive-OR 16 bit zp pointed to by Y with 16 bit zp pointed to by X
AddressInputsOperationFlags8 bit equivalent
0598[X], [Y]XY = [X] XOR [Y]N, ZEORX

ORA#

Description: logical OR X(low byte) Y(high byte) with 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
05A5[A], XYXY = [A] OR XYN, ZORA#

ORXY

Description: logical OR 16 bit zp pointed to by Y with 16 bit zp pointed to by X
AddressInputsOperationFlags8 bit equivalent
05A8[X], [Y]XY = [X] OR [Y]N, ZORAX

ROAL

Description: rotate left 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
04B4[A][A]rot<1C, N, ZROLI

ROAR

Description: rotate right 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
04C6[A][A]rot>1C, N, ZRORI

SBA#

Description: subtract with borrow X(low byte) Y(high byte) from 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
0522[A], XY, CXY=[A]+C+!XYC, N, V, ZSBC#

SBXY

Description: subtract with borrow 16 bit zp pointed to by Y from 16 bit zp pointed to by X
AddressInputsOperationFlags8 bit equivalent
052D[X], [Y], CXY=[X]+C+![Y]C, N, V, ZSBCX

SHAL

Description: shift left 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
04DC[A][A]<<1C, N, ZASLI

SHAR

Description: shift right 16bit zero page variable pointed to by A
AddressInputsOperationFlags8 bit equivalent
04E3[A][A]>>1C, N, ZLSRI

Other useful subroutines

These fourteen subroutines are used quite a bit by the editor and are also just generally useful. The following list shows these label names, the address, a brief description, and a list of the inputs required. In particular, LBNK or LB#X will be useful if you need to load banks of RAM from file into memory. SFAR, CFAR, and JFAR are replacements for the kernal functions STASH, CMPARE, and JSRFAR (although JSRFAR works fine for ROM banks, it returns the wrong values for flags in the RAM banks).

GBYP allows you to create subroutines that take an extra BYTE parameter as well as A, X, and Y; just make a JSRA GBYP the first command of your subroutine and the byte will be stored at BYTP.

The four CP** functions allow you to move banked RAM to 7000-8FFF and back, making moving large data tables from one bank to another much easier (don't use this to move any metadata though, it's more for loading and saving purposes as the file system monopolizes the RAM bank pointer). These functions use the file TEMP.TMP as a place to store the code normally located in 7000-8FFF.

The NULL subroutine is included as a convenience. It is just an RTSI command. If you are writing code and know you want to make a call to a subroutine, but that routine isn't written yet, you can use JSRA NULL as a placeholder to be changed later. It won't hurt anything to run code that has a call like this in it, and eventually you can update the label in the parameter once you've written the subroutine being called. Also, if you EXEC code and your screen ends up garbled, doing an EXEC NULL should trigger the code that resets the screen. This may not work of course, as the reason your screen gets messed up might have something to do with part of the editor getting overwritten by something in your code, but it's worth a shot.

S181

Description: set logical, first, secondary address to 1, 8, 1 respectively
AddressInputs
0409none

S180

Description: set logical, first, secondary address to 1, 8, 0 respectively
AddressInputs
040Dnone

LDED

Description: load editor
AddressInputs
0416none, but filename EDIT.BIN must be located at 07E0 (CBUF)

LBNK

Description: load bank of RAM pointed to by X, filename set in FNAM
AddressInputs
0426filename in FNAM (07C0), bank number in X

LB#X

Description: load bank of RAM pointed to by X, filename set by user beforehand (i.e. SNAM must have been already called)
AddressInputs
0431bank number in X, must have already called SNAM before calling LB#X

CPG-

Description: load the file TEMP.TMP at $7000-8FFF
AddressInputs
0444none

CPG/

Description: save $7000-8FFF as file TEMP.TMP
AddressInputs
0448none

CPG>

Description: move $7000-8FFF to bank pointed to by Y
AddressInputs
044AY = bank number to move low RAM to

CPG<

Description: move bank pointed to by Y to $7000-8FFF
AddressInputs
0452Y = bank number to move to low RAM

NULL

Description: null subroutine; the only command is RTSI
AddressInputs
048Fnone

GBYP

Description: allows you to write subroutines that take an extra BYTE following subroutine call
AddressInputs
05D5extracts BYTE parameter for any subroutine call which is followed by the BYTE. Make this call the first command of any subroutine which requires the extra BYTE parameter

CFAR

Description: replaces missing Kernal function CMPARE
AddressInputs
05FBA points to ZP location with address, X is bank#, Y is an offset, followed by BYTE to compare

SFAR

Description: replaces missing Kernal function STASH
AddressInputs
0604A points to ZP location with address, X is bank#, Y is an offset, followed by BYTE to store

JFAR

Description: redirects to JSRFAR; the fix to JSRFAR in r39 made JFAR unnecessary.
AddressInputs
063Acall is followed by a WORD for address and BYTE for bank#

Future work

I crammed every single "wouldn't it be nice if" I could think of into this program. Nothing was left on the table.

And in version 0712 for r41, I fixed all of the bugs that came up and added USED. I don't think I'm going to be making any more changes to this editor in future, except for bug fixes and updates forced by breaking changes to VERA or the emulator. Any future versions will come with a patch file to make the necessary updates.