Sirius 65816 by S.L Judd 1/00 last update: 3/13/2001 Sirius is a 65816 development system for the SuperCPU. It is based on El Cheapo Assembler and has numerous features and improvements, including: - integrated editor/assembler/monitor - jammon v4.1 (full-featured single-stepping, 24-bit load/save, etc.) - editor designed specifically for writing 65xx code - simultaneously edit up to eight files of arbitrary size - a linker, for serious project development - clean restart ability (e.g. after machine crashes). It requires a SuperCPU with SuperRAM to run (you can use El Cheapo if you don't have SuperRAM). Sirius incorporates many years of experience developing both small and large programs, as well as numerous interactions with other programmers. I used the features I liked or found useful and threw away features I disliked or felt were useless. It is rapidly becoming what I consider to be an ideal development system. I hope you agree. sjudd@ffd2.com Contents -------- 1. Boot program 2. The Editor 3. The Assembler 4. The Linker 5. The Monitor 6. Memory map and technical notes 7. 65816 hints and tips 8. Frequently asked questions -------- 1. Boot program With El Cheapo, some people did not seem to understand the purpose of the boot program. The boot program loads in the various components of the system, and allows you to customize different aspects of the system, such as the colors and the ML monitor used. Jammon is the included monitor, but any monitor may be substituted. You can LIST the program and make any changes you see fit to make. The purpose of the border, background, and text color are obvious, but three more parameters may be set. "HIBANK" is the highest bank of RAM used by Sirius. It defaults to the top of usable memory, but by lowering this value you can set aside a chunk of RAM at the top of memory that will remain untouched. The remaining two parameters are the start and end address of the ML monitor. Thus (in principle) any monitor may be used, at any location in memory, by default. 2. The Editor The editor's name is NED. Hola, NED. NED is a 240-column editor which has been optimized for writing assembly code. When you enter a line of text, it will be automatically formatted into label/opcode/argument/comment fields. The rule is that each field is separated by a space, and the label field begins on the first column. So if you enter LDA#$FF;comment it will be formatted as LDA #$FF ;comment and so on. Run/Stop is the "tab" key, and you can press R/S or shift-R/S to quickly move between the fields if you need to. For normal navigation you can use the usual cursor keys etc.; for rapid navigation, use CTRL-i/j/k/m. You'll also want to make copious use of the "bookmark": press "HOME" to set the bookmark, and press C=-HOME to return to that bookmark at any time. Thus if you are writing some code, and want to check another subroutine to see how it works/what parameters it requires, you can press , use CTRL-L to find the subroutine, and when you're done poking around just press C=-HOME to resume coding. CTRL-L is a "find" command that is restricted to the label field, so it is extremely useful for locating subroutines. CTRL-F is the normal "find" command. By themselves, CTRL-L and CTRL-F stop at the first match, but you can use CTRL-shift-L/F to find subsequent matches, until the end of the text is reached. CTRL-R (R is for Robin) will find the label in the _operand_ field of the current instruction. So, for example, if you have the cursor on a line like JSR BLAH and press CTRL-R, the cursor will move to the label BLAH. If, while typing, you royally screw up the line you're working on, you can press CTRL-o ("oops") which will restore the old line. This version of NED has eight "buffers", i.e. you can edit up to eight files simultaneously. The text is all stored in SuperRAM, so the size of your text files are limited only by the size of your RAM. Press CTRL-1 through 8 to switch between buffers. Usually, you'll only need two or three buffers, either for cutting and pasting purposes, or when developing a program with the linker. To load/save a buffer, press F7. When entering text, such as a filename, shift-clear will erase the current text string. A complete list of commands is below, but one more command worth highlighting is the "reformat" command, CTRL-shift-Z. Sirius does the automatic formatting using special tab characters (shifted spaces), so sometimes you might incorporate some code written in a different assembler, which doesn't get formatted. The "reformat" command will convert this code into Sirius format, which will not only make the code look much nicer but will also do things like convert upper-case letters to "normal" upper-case (e.g. ascii values 192-223 instead of 96-127, so that CTRL-F searches will work right). And now, the commands: C=-INST - Toggle insert mode shift-return - Insert a new line cursor keys, return - What you would expect insert, delete - Likewise shift-clear - Clear all text HOME - Set bookmark C=-HOME - Goto bookmark R/S - Forwards tab (move to next field) shift-R/S - Backwards tab F1 - Assemble F2 - Exit to ML monitor F7 - Disk menu CTRL-a - Delete all characters to right of cursor -d - Delete line -D - Delete multiple lines -b/n - Move to beginning/end of line -B/N - Move to beginning/end of text -c/x - Copy/cut lines to clipboard -p - Paste line from clipboard -j/k - Move one screen left/right (max 240 columns) -i/m - Move one screen up/down -g - Goto line # -f/F - Find / Find next -l/L - Find label -r/R - Find label in operand field -o - Oops -- undo changes to current line -= - Comment line of equals-signs -^ - Comment line of * -lira - Comment line of - -Z - Re-format all text -shift-backarrow - Exit to BASIC (SYS 1020 to re-enter) -1 through 8 - Move to buffer 1-8 3. The Assembler From the editor, press F1 to assemble the current text. Lines of code are structured as follows: label opcode argument comment Each field must be separated by at least one space, with the exception of comments, which may begin at any column. The assembler is case insensitive, both for labels and opcodes. Thus lda LDA lDa are equivalent, as would be labels such as warez and wArEz. While assembling, RUN/STOP will halt assembly and <- (backarrow) will toggle the screen output. 3.1 Comments ;this is a comment * This is also a comment, provided the * is in column 1 3.2 Opcodes All the 65816 opcodes are supported, and alternate mnemonics are provided for the following commands: BCC BLT BCS BGE DEC A DEA INC A INA JSR JSL (Long JSR) JMP JML (Force long JMP) Moreover, BRK and COP are in principle two-byte instructions, so they can include an optional argument, i.e. BRK assembles as 00, but BRK $02 assembles as 00 02. 3.3 Addressing modes All of the 65816 modes are supported. Arguments may be - Numbers: LDA 100 LDA $64 LDA %01100100 - Labels: JSR CHROUT - Characters: LDA #'a' LDA #"a" - PC: LDA $D011 BPL *-3 Graphics characters are allowed inside of quotes. When specifying characters, single and double quotes have different meaning. Upper-case letters are 96-127 within single quotes, and 192-223 within double quotes; space also has high bit clear/set. CMP #' ' ;CMP #32 CMP #" " ;CMP #160 Note that kernal routines such as GETIN return upper-case letters in the range 192-223. Simple mathematical operations are supported -- currently just addition and subtraction (but multiplication and division are a cinch and just a matter of me adding them in). Thus arguments like CHROUT+$20-* are perfectly valid. 3.4 Labels Labels are composed of letters and numbers. Labels must start with a colon or a letter. Labels may be any length (although shorter lengths save memory, and lengths greater than eight chars will produce messy program listings). Labels beginning with a colon are _local_ labels -- their scope only extends to the next global variable (think of them as being 'attached' to the last global variable). Example: GLOBAL1 LDX #$20 :L1 JSR CHROUT DEX BNE :L1 ;Backwards branch GLOBAL2 CMP #$80 BCS :L1 ;Forwards branch AND #$7F :L1 Thus local labels are reusable labels. Pretty cool, eh? 3.5 Prefixes The standard 65816 prefixes are supported. These are < ^ > for #immediate mode, and < ! > for absolute addresses. Immediate-mode prefixes specify which part of the byte to use: Operand One-byte result Two-byte result #$01020304 04 04 03 #<$01020304 04 04 03 #>$01020304 03 03 02 #^$01020304 02 02 01 It should be noted that, currently, Sirius only supports 24-bit numbers, not 32-bits as the above indicates (coming later!). Address prefixes specify the length of the address: < One byte (direct a.k.a. zero page) ! Two bytes (absolute) > Three bytes (abs long) Thus LDA $0203 and LDA !$010203 are equivalent. It used to be that this was the only way to force long addressing in the assembly bank (LDA $000102 would assemble to LDA $0102), but that has been fixed in v2.0 (LDA $000102 will assemble as 24-bit addressing). The assembler will use the shortest addressing mode possible, in general. 3.6 Pseudo-Opcodes A number of useful pseudo-opcodes are supported. ORG address ORG *= address ORG * ORG ORG sets the program ORiGin to address, and is thus the first line of most programs. For example, ORG $C000 assembles the program to $00C000. Subsequent ORG directives may be specified change the address again -- this is useful when you have a section of code to copy to another area of memory. Finally, ORG without any argument performs a re-ORG, that is, resets the program counter to its proper value. For example: ORG $0801 ... basic header here LDX #END-START :LOOP LDA START-1,X STA $C000,X DEX BNE :LOOP RTS START ORG $C000 ;Assemble below code as if to $C000 L1 JSR CHROUT JMP L1 ;JMP $C000 ORG ;Re-ORG END EQU or = EQUate a label to some value. Examples: CHROUT = $FFD2 ZERO EQU 0 --- Storage opcodes --- DFB or DB DeFine Byte -- output bytes directly into code. Examples: DFB $20 DFB 100,$64,%1100100 Note separating commas. DFB treats arguments identically to normal opcodes, so different bytes in a label are specified as DFB #LABEL The # is needed because DFB >LABEL will treat >LABEL as a 24-bit absolute value, not the third byte of LABEL. DA or DW Define Address -- assembles 16-bit address in lo/hi order: DA $FFD2 ;assembles as D2 FF DA $0801,$FFD2 ;assembles as 01 08 D2 FF DLA Define Long Address -- like DA, but 24-bit HEX Define hex bytes. No comma separators are used and each byte is two characters HEX 20D2FF ;Assembles as 20 D2 FF DS and DS ^ Define storage -- fill memory with specified number of bytes. DS ^ fills to the end of the page. DS 5 ;Assembles as 00 00 00 00 00 DS 5,$3D ;Assembles as 3D 3D 3D 3D 3D DS ^ ;Fills memory with zeros to end of page DS ^,$3D ;Fills memory with 3D to end of page TXT Assemble text. Text is delimited using either ' or ". Hex bytes, separated by commas, may follow the string. Upper-case letters are 96-127 within single quotes, 192-223 within double quotes; space also has high bit clear/set. Examples: TXT 'Hola' ;104 79 76 65 TXT "Hola",0d,00;200 79 76 65 0d 00 --- Conditional Assembly --- DO arg If arg=0 then assembly is halted, until an ELSE or FIN condition is reached. Examples: DO 0 ;Don't assemble DO DEBUG ;Don't assemble if DEBUG=0 DO *-$C000 ;Don't assemble if *=$C000 ELSE Reverse the last DO condition. FIN End DO/ELSE constructs. All DO's should end with a FIN. --- I/O --- PUT 'filename' PUT 'filename',device Assemble from disk. PUT essentially inserts the file "filename" into the text and assembles it. In El Cheapo, PUT was quite slow, but in Sirius it is quite fast. BIN 'filename' BIN 'filename',device Include a file from disk, raw. Like PUT, but doesn't try to assemble, so you can use it to include things like tables. PRT PRT 'filename' PRT 'filename',device Redirect assembler output. This does not "assemble to disk" (that feature is coming later), but rather sends the printed code output, i.e. things like :C000 A9 00 LDA #00 to either the printer (PRT) or a disk file. --- 16-bit support --- REG #$xx REG ON REG OFF The 65816 uses the same opcode for LDA #$12 and LDA #$1234 -- should the assembler assume 8 or 16 bits? In the code this is determined by the settings of the M and X flags. Normally Sirius tracks these flags by remembering any changes made by REP or SEP. REG OFF turns off this feature; REG ON turns it back on. REG #$30 will set the register bits to #$30 -- REG acts like LDA, NOT like REP or SEP. If your code crashes for mysterious reasons, the first thing to check is that the M and X flags are set correctly so that the assembler generates the code you actually want! Also note that REP/SEP tracking does not track E. So if you SEC XCE the assembler will NOT automatically set M and X to 1. --- Linker --- REL Assemble as RELocatable file EXT Label is EXTernal to code ENT ENTry point RELocatable modules -- See next section! 4. The Linker The linker is a very powerful tool for serious program development. With the linker, code is divided into relocatable modules which are then hooked together (linked) by the linker to create the object code. Labels may be shared between modules, but only those labels marked as EXT/ENT. Using the linker, you can a) develop extremely large programs efficiently b) divide programs up into much more managable sub-tasks c) develop routine libraries which may be easily shared between projects d) quickly re-assemble code to any address For example, Sirius is created with the following: link $1000 init.l cheap3.l link.l diskio.l ned.l The first line invokes the linker, and sets the link address to $1000. The files that follow are RELocatable files, created with the assembler. The first file, init, contains various initialization routines. The second file, cheap3.l, is the assembler; link.l is the linker; diskio.l is a set of kernal routines for i/o, memory management, and contains all of the disk access code; and ned.l is the editor. The result is the main object code, beginning at $1000. Each module makes certain labels visible to other modules. For example, NED has the following line: TEXT ENT DLA $040000 It turns out that TEXT contains the beginning of the current text buffer. By declaring TEXT as an ENTry point, other modules can access TEXT directly by declaring it as an EXT label. For example, the save routine in diskio uses LDX TEXT LDY TEXT+1 LDA TEXT+2 to determine where to start saving from. The assembler also uses TEXT as a pointer to the text to be assembled. In both files there is a line TEXT EXT which tells the assembler that the label TEXT is EXTernal to this module and will be resolved by the linker at link-time. Now let's say that I write a new editor (which I did, going from Blahtune to Tunesmith). All that editor has to do is have a label called TEXT which contains the start of text, and it can be immediately linked into the existing code. The actual location of TEXT doesn't matter, because it is resolved at link time. Labels not marked EXT or ENT are visible only to the specific module. When writing the original El Cheapo, I first wrote and tested the assembler until it seemed to work OK. The next step was to modify diskio and ned, which were originally written for Tunesmith. Once they were working they were integrated with the assembler. Once that was working the initialization routines were integrated. With Sirius, a new linker module was written and incorporated straight into the existing code. Some key features here are that each part was developed independently, that several major parts of the code came from a completely different program, and that new capabilities could be quickly integrated. Now let's think about the alternative. Instead of having a set of independent modules, I could have one gigantic 400-500 block file. I could make a cup of tea while it was assembling, and keep lots of disks on hand for each revision of the source. I could make copious use of NED's FIND command. I could incorporate the tunesmith code using a massive cut and paste operation, and constantly pray for no label conflicts. And I could watch the chances for this program ever being finished rapidly dwindle to zero. 5.1 Rules As you might imagine, RELocatable object files impose a few limitations on the code, but not limitations so great as you also might imagine. There are three pseudo-opcodes which deal directly with relocatable modules: REL, EXT, and ENT. The REL pseudo-opcode must appear in the code before any labels or code. The REL directive tells the assembler to assemble the code as a RELocatable module. REL files contain additional information for the linker to fix up all the labels and locations in the module at link time. EXT defines a label as EXTernal to the current module, to be fixed up at link time. ENT defines a label as an ENTry point, accessible to other REL files with a corresponding EXT definition. Short branches to EXTernal labels are forbidden, i.e. BEQ BLAH where BLAH is an EXTernal label. Long branches are allowed, however. When an EXTernal variable is used in an arithmetic expression (i.e. is added to or subtracted from something), the result MUST be a 16-bit value. Thus LDA #