diff --git a/README.md b/README.md index 5128b3d..25f899c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # RE6502 - RE6502 is an emulator for the 6502 cpu written in rust. The project comes with a very basic virtual machine for testing (it just has simple I/O functionality) as well as some test programs (find these in simple_test_machine/programs). The test programs can be built with the win2c64 (or lin2c64, or mac2c64) assembler which can be found here: https://www.aartbik.com/retro.php. Theoretically any 6502 assembler should work but win2c64 is the one I've been using for testing. # Building +The emulator doesn't really do anything on it's own but you can build it with the normal `cargo build` or `cargo run` if you run this program it will just do some simple internal tests. There are also unit tests you can run with `cargo test`. To make better use of the emulator you'll need to use it as a component of a larger emulator/vm. Take a look a the Simple Test Machine project to see how to use the RE6502 as a component. \ No newline at end of file diff --git a/simple_test_machine/README.md b/simple_test_machine/README.md index d2f8168..964b62a 100644 --- a/simple_test_machine/README.md +++ b/simple_test_machine/README.md @@ -1,8 +1,18 @@ # The Simple Test Machine - +The Simple Test Machine is a very basic virtual machine that uses the RE6502 as the cpu and a custom Console for simple I/O operations. There are example programs in the programs directory for reference/testing. The important memory addresses (I/O buffers, bit flags, etc) are listed in the src/machine.rs file. # Running the Simple Test Machine +To build the project you can `cd simple_test_machine` then `cargo build` or `cargo run`. If you run it on it's own it will just run some basic internal test programs. To run a specific progam you can pass the name of the binary to load when the machine starts up. To do this with cargo try `cargo run -- path/to/the/program` (ex. `cargo run -- programs/bin/hello.rw`). + +# Assembling Programs for the Simple Test Machine +Program binaries are not included in this repo but you can build them from the .asm files in the programs subdirectory. I've tested building these programs with the `win2c64` assembler (it also has linux `lin2c64` and mac `mac2c64` versions) that can be found here: https://www.aartbik.com/retro.php. + +To use the assembler you can invoke it directly or use the `assemble.bat` script in the programs subdirectory. + +## Using the assemble.bat script +To use the assemble.bat script you'll need to cd into the programs directory: `cd programs` (or, if you're in the project root: `cd simple_test_machine/programs`) then you can run the script with `.\assemble.bat hello.asm`. The script will move the resulting binary into the bin directory. +## Using win2c64 directly +You can invoke win2c64 directly with `win2c64.exe -R my_program.asm` The `-R` option is necessary to have the assembler output the binary in the correct format. -# Assembling Programs for the Simple Test Machine \ No newline at end of file diff --git a/simple_test_machine/programs/echo.asm b/simple_test_machine/programs/echo.asm index 779d64b..ea431ca 100644 --- a/simple_test_machine/programs/echo.asm +++ b/simple_test_machine/programs/echo.asm @@ -1,6 +1,6 @@ ; echo example - takes input and then prints it back out -; This is for testing the InputConsole of the Simple Test Machine +; This is for testing input of the Simple Test Machine ; by Joey Pollack ; assemble with win2c64 (or lin2c64, or mac2c64) using the -R option @@ -12,7 +12,7 @@ con_out .equ $1100 ; console output buffer address con_in .equ $1200 ; console input buffer address con_flags .equ $009A ; console flags address prt_str_flag .equ $0002 ; print string flag -prmp_input_flag .equ $0004 ; prompt for input flag +prmp_input_flag .equ $0010 ; prompt for input flag ; MAIN main .org $0200 ; program load address for the Simple Test Machine diff --git a/simple_test_machine/programs/hello.asm b/simple_test_machine/programs/hello.asm index b53aa94..da54887 100644 --- a/simple_test_machine/programs/hello.asm +++ b/simple_test_machine/programs/hello.asm @@ -6,26 +6,30 @@ ; Adapted for the RE6502 emulator simple test machine ; compile with win2c64 using the -R option +; This example shows how to print a string to the console + strout .equ $1100 ; console output address con_flags .equ $009A ; console flags address -prt_str_flag .equ $0002 +prt_str_flag .equ $0002 ; the print flag bitmask main .org $0200 ; program load address for the simple test machine - ldx #0 -loop lda text,x - sta strout,x - inx - cpx #11 - bne loop + + ; Copy the string into the console output buffer + ldx #0 ; initialize the offset counter +loop lda text,x ; load byte from the text buffer, offset by x register + sta strout,x ; store byte into the output buffer, offset by x register + inx ; increment the offset counter + cpx #11 ; check if we've reached the end of the string - text is 11 bytes long + bne loop ; branch if we haven't reached the end yet ; null terminate the string lda #0 sta strout,x ; Set flag to do the print - lda con_flags - ora #prt_str_flag - sta con_flags ; 0x86, 0x9A, ; Print string flag is at 0x9A + lda con_flags ; load flags into accumulator + ora #prt_str_flag ; set the print string flag + sta con_flags ; store flags back into memory - this will trigger the print ; End the program rts ; 0x60 diff --git a/simple_test_machine/programs/mult10.asm b/simple_test_machine/programs/mult10.asm index 85bc174..ed9f0fe 100644 --- a/simple_test_machine/programs/mult10.asm +++ b/simple_test_machine/programs/mult10.asm @@ -10,22 +10,22 @@ prt_str_flag .equ $0001 ; print string flag ; FAST MULTIPLY program from: ; http://6502.org/source/integers/fastx10.htm main LDA #7 ; load 7 into the accumulator - ASL ;multiply by 2 - STA TEMP ;temp store in TEMP - ASL ;again multiply by 2 (*4) - ASL ;again multiply by 2 (*8) + ASL ; multiply by 2 + STA TEMP ; temp store in TEMP + ASL ; again multiply by 2 (*4) + ASL ; again multiply by 2 (*8) CLC - ADC TEMP ;as result, A = x*8 + x*2 + ADC TEMP ; as result, A = x*8 + x*2 - ; PRINT RESULT + ; print result STA strout ; store A into the console output address LDX #0 ; 0xA2, 0x00, ; null terminator STX strout+1 ; store null terminator to output addr + 1 ; Set flag to do the print - LDA con_flags ; load the current console flag set + LDA con_flags ; load the current console flag set ORA #prt_str_flag ; turn on the print string flag - STA con_flags ; store the flags back in memory + STA con_flags ; store the flags back in memory ; End the program RTS ; 0x60 diff --git a/simple_test_machine/programs/mult10_orig.asm b/simple_test_machine/programs/mult10_orig.asm deleted file mode 100644 index 0ae4a8b..0000000 --- a/simple_test_machine/programs/mult10_orig.asm +++ /dev/null @@ -1,29 +0,0 @@ - -; Multiply 7 with 10 and print the result -; For testing this program was assembled with c64 (using the -R option): -; https://www.aartbik.com/MISC/c64.html - - ; FAST MULTIPLY program from: - ; http://6502.org/source/integers/fastx10.htm -main LDA #7 ; load 7 into the accumulator - ASL ;multiply by 2 - STA TEMP ;temp store in TEMP - ASL ;again multiply by 2 (*4) - ASL ;again multiply by 2 (*8) - CLC - ADC TEMP ;as result, A = x*8 + x*2 - - ; PRINT RESULT - STA $A0 ; 0x85, 0xA0, ; store A into the console output address - LDX #0 ; 0xA2, 0x00, ; null terminator - STX $A1 ; 0x86, 0xA1, ; store null terminator to output addr + 1 - - ; Set flag to do the print - LDX #1 ; 0xA2, 0x01, - STX $9F ; 0x86, 0x9F, ; Print byte flag is at 0x9F - - ; End the program - RTS ; 0x60 - - ; Variables - TEMP .byte 0 diff --git a/simple_test_machine/src/machine.rs b/simple_test_machine/src/machine.rs index 0153924..d7e085c 100644 --- a/simple_test_machine/src/machine.rs +++ b/simple_test_machine/src/machine.rs @@ -3,25 +3,35 @@ use std::{io, str}; //||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -// MEMORY ADDRESSES AND FLAGS +// MEMORY ADDRESSES AND FLAG MASKS //||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -const PROGRAM_START_ADDR: u16 = 0x0200; // Program code starts on page 2 -const CPU_RESET_START_ADDR: u16 = 0xFFFC; // This is where the cpu looks for the address to start executing code at -pub const OUTPUT_BUF_ADDR: u16 = 0x1100; // Output buffer -- Put values to be printed at this location! -pub const INPUT_BUF_ADDR: u16 = 0x1200; // Input buffer -pub const INPUT_BUF_SIZE: u16 = 0x00FF; // Input buffer is 255 bytes +// CPU Addresses +const PROGRAM_START_ADDR: u16 = 0x0200; // Program code starts on page 2 +const CPU_RESET_START_ADDR: u16 = 0xFFFC; // This is where the cpu looks for the address to start executing code at +// Console Addresses +pub const OUTPUT_BUF_ADDR: u16 = 0x1100; // Output buffer -- Put values to be printed at this location! +pub const INPUT_BUF_ADDR: u16 = 0x1200; // Input buffer +pub const INPUT_BUF_SIZE: u16 = 0x00FF; // Input buffer is 255 bytes pub const CONSOLE_FLAGS_ADDR: u16 = 0x009A; // Grouping all of the console flags into a single byte -pub const PRINT_BYTE_FLAG: u8 = 0x01; // Then set one of these flags to trigger the print -pub const PRINT_STR_FLAG: u8 = 0x02; // and indicate what type is being printed. -pub const READ_LINE_FLAG: u8 = 0x04; // Set this flag to request user input from the keyboard -pub const READ_OVERFLOW_FLAG: u8 = 0x08; // This flag is set after reading input if there is too much input for the buffer + +// Console Flag Masks +pub const PRINT_BYTE_FLAG: u8 = 0x01; // Trigger printing a single byte +pub const PRINT_STR_FLAG: u8 = 0x02; // Trigger printing a string (continues printing bytes until a null byte is encountered) +pub const READ_LINE_FLAG: u8 = 0x10; // Set this flag to request user input from the keyboard +pub const READ_OVERFLOW_FLAG: u8 = 0x20; // This flag is set after reading input if there is too much input for the buffer ///////////////////////////////////////////////////////////////////// // BUS ///////////////////////////////////////////////////////////////////// + use re6502::r6502::{R6502, Bus, Flags}; + +// The Bus is how you connect other components to the RE6502 cpu. +// At minimium the read() and write() traits must be implement for the Bus. +// The following is a very basic Test Bus implementation. + struct TBus { memory: [u8; 64 * 1024] @@ -128,10 +138,7 @@ impl Console // Make sure the string will fit in the input buffer if (buffer.len() + 1) as u16 >= INPUT_BUF_SIZE - { - // TODO: Change this to set an error flag instead of printing. This way - // the program can detect and handle these errors. - + { // reset the read flag and set the overflow flag value &= !(READ_LINE_FLAG); value |= READ_OVERFLOW_FLAG; @@ -158,26 +165,10 @@ impl Console } } -//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -// CONSOLE INPUT -//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| - - -struct InputConsole {} - -impl InputConsole -{ - -} - - - ///////////////////////////////////////////////////////////////////// // MACHINE ///////////////////////////////////////////////////////////////////// - - pub struct TestMachine { bus: TBus,