Difference between revisions of "Getting Started Guide"
(→Downloading with the C256Mgr) |
(→Sample Program: Hello, World!) |
||
Line 46: | Line 46: | ||
<code>run256 hello.hex</code> | <code>run256 hello.hex</code> | ||
− | == Sample Program: Hello, World! == | + | == Sample Assembly Program: Hello, World! == |
+ | |||
+ | The following code is a bare-bones example of C256 assembly code to print the usual "Hello, world!" message. | ||
+ | The code may look a bit daunting for such a simple task, but that is partly because it is self-contained. | ||
+ | Several things being done here would be provided by include files or be in macros. | ||
+ | Also, this code is using a special trick to auto-run the program when it is uploaded to the C256: it over-writes | ||
+ | the 65816's hardware RESET vector with a pointer to the start of the program. When the code is uploaded, | ||
+ | the debug interface automatically resets the CPU, triggering the CPU to execute this code. | ||
+ | |||
+ | The complete example, along with a batch file to assemble it can be found at: https://github.com/pweingar/C256Samples | ||
+ | |||
+ | <pre> | ||
+ | .cpu "65816" ; Tell 64TASS that we are using a 65816 | ||
+ | |||
+ | ; Kernel jump points... this would normally be in an include file | ||
+ | |||
+ | PUTS = $00101C ; Print a string to the currently selected channel | ||
+ | |||
+ | ; Hardware RESET vector | ||
+ | |||
+ | * = $00FFFC | ||
+ | |||
+ | RESET .word <>START ; Over-ride the RESET vector with the start of our code | ||
+ | |||
+ | ; Code | ||
+ | |||
+ | * = $002000 ; Set the origin for the file | ||
+ | |||
+ | START CLC ; Make sure we're native mode | ||
+ | XCE | ||
+ | |||
+ | ; This would normally be done with a macro "setas" | ||
+ | SEP #$20 ; Set M to 1 for 8-bit accumulator | ||
+ | .as ; Tell 64TASS that the accumulator is 8-bit | ||
+ | |||
+ | ; This would normally be done with a macro "setxl" | ||
+ | REP #$10 ; Set X to 0 for 16-bit index registers | ||
+ | .xl ; Tell 64TASS that the index registers are 16-bit | ||
+ | |||
+ | ; Set the data bank register to this bank. This might normally be done by a macro "setdbr" | ||
+ | LDA #`GREET ; Set the data bank register to be the current bank of the program | ||
+ | PHA | ||
+ | PLB | ||
+ | .databank `GREET ; Tell 64TASS which data bank we're using | ||
+ | |||
+ | LDX #<>GREET ; Point to the message in an ASCIIZ string | ||
+ | JSL PUTS ; And ask the kernel to print it | ||
+ | |||
+ | _done NOP ; Infinite loop when we're finished | ||
+ | BRA _done | ||
+ | |||
+ | GREET .null "Hello, world!", 13 ; The text to display. Will include a terminal NUL | ||
+ | </pre> | ||
+ | |||
+ | === Step-by-step Explanation of the Program === | ||
+ | |||
+ | The first thing our code has to do is tell 64TASS that the code is intended for a 65816 processor. | ||
+ | It can do this using the .cpu assembler directive. | ||
+ | |||
+ | <pre> | ||
+ | .cpu "65816" | ||
+ | </pre> | ||
+ | |||
+ | Next, the program will be printing a string using the kernel, so we need a reference to the kernel's routine to print a string. | ||
+ | In this case, we'll be using the PUTS kernel function to print an ASCIIZ string. Normally, a program would use a kernel jump table | ||
+ | include file, but here we'll just define a label. Note that on the FMX, the kernel jump table is located in bank 0, at $00:1000. | ||
+ | |||
+ | <pre> | ||
+ | PUTS = $00101C | ||
+ | </pre> | ||
+ | |||
+ | Since we're taking control after a CPU reset, we need to tell the 65816 to go to native mode. | ||
+ | When the 65816 resets, it starts up in emulation mode, where it acts almost exactly like a 6502. | ||
+ | We enter native mode by clearly the carry bit and exchanging the carry bit with the emulation flag: | ||
+ | |||
+ | <pre> | ||
+ | START CLC ; Make sure we're native mode | ||
+ | XCE | ||
+ | </pre> | ||
+ | |||
+ | The 65816 allows the accumulator and index registers to be different sizes. The registers can be either 8-bits wide, as in | ||
+ | the standard 6502 processors, or they can be 16-bits wide. For this program, we want the accumulator to be 8-bits wide, and | ||
+ | the index registers to be 16-bits wide (PUTS expects the 16-bit pointer to the string to print in X). | ||
+ | This is done using a combination of the REP and SEP instructions, which clear or set bits in the processor status register, | ||
+ | and assembler directives. The assembler directives are needed because the assembler needs to know how big the registers are | ||
+ | for that section of code (some instructions are different lengths, depending on the register sizes): | ||
+ | |||
+ | <pre> | ||
+ | ; This would normally be done with a macro "setas" | ||
+ | SEP #$20 ; Set M to 1 for 8-bit accumulator | ||
+ | .as ; Tell 64TASS that the accumulator is 8-bit | ||
+ | |||
+ | ; This would normally be done with a macro "setxl" | ||
+ | REP #$10 ; Set X to 0 for 16-bit index registers | ||
+ | .xl ; Tell 64TASS that the index registers are 16-bit | ||
+ | </pre> | ||
+ | |||
+ | Next, we need to set the data bank register (B). This is a new register on the 65816, which specifies what address bits 23..16 | ||
+ | should be for any addressing mode that only provides 16-bits. The PUTS function expects that the data bank register is | ||
+ | set to the bank containing the string to print, so the full address of the string might be written as "B:X". | ||
+ | As with the register sizes, the assembler needs to know what the data bank register setting is, and we can set that using the | ||
+ | ".databank" assembler directive. | ||
+ | |||
+ | <pre> | ||
+ | ; Set the data bank register to this bank. This might normally be done by a macro "setdbr" | ||
+ | LDA #`GREET ; Set the data bank register to be the current bank of the program | ||
+ | PHA | ||
+ | PLB | ||
+ | .databank `GREET ; Tell 64TASS which data bank we're using | ||
+ | </pre> | ||
+ | |||
+ | Now, we can print our message. We do that by setting X to the address of the string and calling the PUTS function. | ||
+ | Since we need X to be set to the lower 16-bits of the address of the string, we use the "<>" prefix, which is like the | ||
+ | "<" and ">" prefixes used in 6502 assembly, but selects bits 15..0 of the address. Also, we use "JSL" instead of "JSR" to | ||
+ | call the PUTS function, since all kernel calls are long subroutine calls. | ||
+ | |||
+ | <pre> | ||
+ | LDX #<>GREET ; Point to the message in an ASCIIZ string | ||
+ | JSL PUTS ; And ask the kernel to print it | ||
+ | </pre> | ||
+ | |||
+ | Finally, we have the program enter an infinite loop at the end: | ||
+ | |||
+ | <pre> | ||
+ | _done NOP ; Infinite loop when we're finished | ||
+ | BRA _done | ||
+ | </pre> | ||
+ | |||
+ | The message to print is just provided using the ".null" directive, which stores the string as a null-terminated (ASCIIZ) string: | ||
+ | |||
+ | <pre> | ||
+ | GREET .null "Hello, world!", 13 ; The text to display. Will include a terminal NUL | ||
+ | </pre> |
Revision as of 12:51, 11 February 2020
Contents
Getting Started Guide
Installing Drivers
Data and programs can be sent to the C256 Foenix over the USB debug port (the USB port on the right side of the board, next to the expansion connector). The debug interface is a USB serial port which requires a driver to be installed on the host system. The drivers for the serial USB device (XR21V1411) can be found at: https://www.maxlinear.com/support/design-tools/software-drivers.
Helpful Tools
- Foenix IDE
- The Foenix IDE is a full-featured emulator and board manager tool written in C#. The bulk of the Foenix IDE is an emulator of the C256 Foenix which emulates several key systems of the C256 and allows for breakpoints in the code, examination of memory, and so on. The IDE can also connect to the physical C256 through the USB debug interface. It can use the interface to upload data from a BIN file, an Intel HEX file, or the IDE emulator's memory into the C256, download memory from the C256 into the emulator's memory to be examined through the IDE, or to reprogram the C256's flash memory. Although a GUI application by default, the IDE can be used as a command line tool. The IDE also includes a character set / font editor tool, allowing you to design your own characters to use in your C256 programs. The source code for the IDE as well as the latest binary can be found at: https://github.com/Trinity-11/FoenixIDE.
- C256Mgr
- C256Mgr is a set of Python scripts allowing access to the C256's USB debug interface. The scripts allow you to use the command line to upload BIN and HEX files to the C256, reprogram the flash memory, or examine memory on the C256. If your code includes a 64Tass compatible LBL file, the C256Mgr scripts can use that LBL file to allow you to examine memory based on the labels in your code. C256Mgr was originally intended to provide a cross-platform option for those who cannot run .Net applications. The C256Mgr can be found at: https://github.com/pweingar/C256Mgr.
- 64Tass
- Although any assembler that can produce 65816 code can be used to write code for the C256, the kernel, BASIC interpreter, and much of the sample code are written using 64Tass, which can be found at: https://sourceforge.net/projects/tass64/.
Uploading with the Foenix IDE
To setup the Foenix IDE, not much is required beyond copying or cloning the repository from GitHub. There is no special installation required, and the EXE should just run.
To upload a BIN or HEX file with the Foenix IDE's user interface:
- Select the Tools > Uploader menu option. An "Uploader Window" will open.
- Select the COM port associated with the C256 debug interface in the drop down selector.
- Click the "Connect" button to establish a connection to the C256.
- Click the button labeled with ellipsis (...) to select the file to upload. By default, the uploader will show BIN files, but you can choose to search for HEX files instead.
- If you are uploading a BIN file, enter the starting destination address in the field "C256 Dest Address". This step is not needed with HEX files, since they have the destination addresses for their data included.
- Click the "Send Binary" button to start the upload process.
Uploading with the C256Mgr
To install the C256Mgr, you will need to make sure you have Python 3.7 installed as well as the PySerial library. You will need to edit the c256.ini file to set the default COM port and LBL file for your project, although both settings can be overridden by the command line options.
Once the manager is installed and configured correctly, there are two batch files that can be used to upload a file to the C256:
- Binary
- To upload a binary file, you use the UPLOAD.BAT file, which takes a BIN file and destination address (in hex) as arguments:
upload kernel.bin 380000
- HEX File
- To upload an Intel HEX file, you use the RUN256.BAT file, which takes just the HEX file as an argument:
run256 hello.hex
Sample Assembly Program: Hello, World!
The following code is a bare-bones example of C256 assembly code to print the usual "Hello, world!" message. The code may look a bit daunting for such a simple task, but that is partly because it is self-contained. Several things being done here would be provided by include files or be in macros. Also, this code is using a special trick to auto-run the program when it is uploaded to the C256: it over-writes the 65816's hardware RESET vector with a pointer to the start of the program. When the code is uploaded, the debug interface automatically resets the CPU, triggering the CPU to execute this code.
The complete example, along with a batch file to assemble it can be found at: https://github.com/pweingar/C256Samples
.cpu "65816" ; Tell 64TASS that we are using a 65816 ; Kernel jump points... this would normally be in an include file PUTS = $00101C ; Print a string to the currently selected channel ; Hardware RESET vector * = $00FFFC RESET .word <>START ; Over-ride the RESET vector with the start of our code ; Code * = $002000 ; Set the origin for the file START CLC ; Make sure we're native mode XCE ; This would normally be done with a macro "setas" SEP #$20 ; Set M to 1 for 8-bit accumulator .as ; Tell 64TASS that the accumulator is 8-bit ; This would normally be done with a macro "setxl" REP #$10 ; Set X to 0 for 16-bit index registers .xl ; Tell 64TASS that the index registers are 16-bit ; Set the data bank register to this bank. This might normally be done by a macro "setdbr" LDA #`GREET ; Set the data bank register to be the current bank of the program PHA PLB .databank `GREET ; Tell 64TASS which data bank we're using LDX #<>GREET ; Point to the message in an ASCIIZ string JSL PUTS ; And ask the kernel to print it _done NOP ; Infinite loop when we're finished BRA _done GREET .null "Hello, world!", 13 ; The text to display. Will include a terminal NUL
Step-by-step Explanation of the Program
The first thing our code has to do is tell 64TASS that the code is intended for a 65816 processor. It can do this using the .cpu assembler directive.
.cpu "65816"
Next, the program will be printing a string using the kernel, so we need a reference to the kernel's routine to print a string. In this case, we'll be using the PUTS kernel function to print an ASCIIZ string. Normally, a program would use a kernel jump table include file, but here we'll just define a label. Note that on the FMX, the kernel jump table is located in bank 0, at $00:1000.
PUTS = $00101C
Since we're taking control after a CPU reset, we need to tell the 65816 to go to native mode. When the 65816 resets, it starts up in emulation mode, where it acts almost exactly like a 6502. We enter native mode by clearly the carry bit and exchanging the carry bit with the emulation flag:
START CLC ; Make sure we're native mode XCE
The 65816 allows the accumulator and index registers to be different sizes. The registers can be either 8-bits wide, as in the standard 6502 processors, or they can be 16-bits wide. For this program, we want the accumulator to be 8-bits wide, and the index registers to be 16-bits wide (PUTS expects the 16-bit pointer to the string to print in X). This is done using a combination of the REP and SEP instructions, which clear or set bits in the processor status register, and assembler directives. The assembler directives are needed because the assembler needs to know how big the registers are for that section of code (some instructions are different lengths, depending on the register sizes):
; This would normally be done with a macro "setas" SEP #$20 ; Set M to 1 for 8-bit accumulator .as ; Tell 64TASS that the accumulator is 8-bit ; This would normally be done with a macro "setxl" REP #$10 ; Set X to 0 for 16-bit index registers .xl ; Tell 64TASS that the index registers are 16-bit
Next, we need to set the data bank register (B). This is a new register on the 65816, which specifies what address bits 23..16 should be for any addressing mode that only provides 16-bits. The PUTS function expects that the data bank register is set to the bank containing the string to print, so the full address of the string might be written as "B:X". As with the register sizes, the assembler needs to know what the data bank register setting is, and we can set that using the ".databank" assembler directive.
; Set the data bank register to this bank. This might normally be done by a macro "setdbr" LDA #`GREET ; Set the data bank register to be the current bank of the program PHA PLB .databank `GREET ; Tell 64TASS which data bank we're using
Now, we can print our message. We do that by setting X to the address of the string and calling the PUTS function. Since we need X to be set to the lower 16-bits of the address of the string, we use the "<>" prefix, which is like the "<" and ">" prefixes used in 6502 assembly, but selects bits 15..0 of the address. Also, we use "JSL" instead of "JSR" to call the PUTS function, since all kernel calls are long subroutine calls.
LDX #<>GREET ; Point to the message in an ASCIIZ string JSL PUTS ; And ask the kernel to print it
Finally, we have the program enter an infinite loop at the end:
_done NOP ; Infinite loop when we're finished BRA _done
The message to print is just provided using the ".null" directive, which stores the string as a null-terminated (ASCIIZ) string:
GREET .null "Hello, world!", 13 ; The text to display. Will include a terminal NUL