Compare commits

..

No commits in common. '5a453478154470b247f5e58cb7407e252f067011' and '2964ff3b6070ff5ccaa4cd4b8ca84e1de385e376' have entirely different histories.

@ -1,5 +1,5 @@
# RE6502 # 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. 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 # 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.

@ -1,18 +1,8 @@
# The Simple Test Machine # 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. # Running the Simple Test Machine
## 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

@ -1,9 +1,8 @@
@echo off
REM Usage: Drop the win2c64 directory into the same directory with this script REM Usage: Drop the win2c64 directory into the same directory with this script
REM Directory structure should be: ./assemble.bat ./win2c64/win2c64.exe ./bin REM Directory structure should be: ./assemble.bat ./win2c64/win2c64.exe ./bin
@echo off
set src_file=%1 set src_file=%1
echo assembling %src_file% echo assembling %src_file%

@ -1,6 +1,6 @@
; echo example - takes input and then prints it back out ; echo example - takes input and then prints it back out
; This is for testing I/O of the Simple Test Machine ; This is for testing the InputConsole of the Simple Test Machine
; by Joey Pollack ; by Joey Pollack
; assemble with win2c64 (or lin2c64, or mac2c64) using the -R option ; 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_in .equ $1200 ; console input buffer address
con_flags .equ $009A ; console flags address con_flags .equ $009A ; console flags address
prt_str_flag .equ $0002 ; print string flag prt_str_flag .equ $0002 ; print string flag
prmp_input_flag .equ $0010 ; prompt for input flag prmp_input_flag .equ $0004 ; prompt for input flag
; MAIN ; MAIN
main .org $0200 ; program load address for the Simple Test Machine main .org $0200 ; program load address for the Simple Test Machine

@ -6,30 +6,26 @@
; Adapted for the RE6502 emulator simple test machine ; Adapted for the RE6502 emulator simple test machine
; compile with win2c64 using the -R option ; compile with win2c64 using the -R option
; This example shows how to print a string to the console
strout .equ $1100 ; console output address strout .equ $1100 ; console output address
con_flags .equ $009A ; console flags address con_flags .equ $009A ; console flags address
prt_str_flag .equ $0002 ; the print flag bitmask prt_str_flag .equ $0002
main .org $0200 ; program load address for the simple test machine main .org $0200 ; program load address for the simple test machine
ldx #0
; Copy the string into the console output buffer loop lda text,x
ldx #0 ; initialize the offset counter sta strout,x
loop lda text,x ; load byte from the text buffer, offset by x register inx
sta strout,x ; store byte into the output buffer, offset by x register cpx #11
inx ; increment the offset counter bne loop
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 ; null terminate the string
lda #0 lda #0
sta strout,x sta strout,x
; Set flag to do the print ; Set flag to do the print
lda con_flags ; load flags into accumulator lda con_flags
ora #prt_str_flag ; set the print string flag ora #prt_str_flag
sta con_flags ; store flags back into memory - this will trigger the print sta con_flags ; 0x86, 0x9A, ; Print string flag is at 0x9A
; End the program ; End the program
rts ; 0x60 rts ; 0x60

@ -10,22 +10,22 @@ prt_str_flag .equ $0001 ; print string flag
; FAST MULTIPLY program from: ; FAST MULTIPLY program from:
; http://6502.org/source/integers/fastx10.htm ; http://6502.org/source/integers/fastx10.htm
main LDA #7 ; load 7 into the accumulator main LDA #7 ; load 7 into the accumulator
ASL ; multiply by 2 ASL ;multiply by 2
STA TEMP ; temp store in TEMP STA TEMP ;temp store in TEMP
ASL ; again multiply by 2 (*4) ASL ;again multiply by 2 (*4)
ASL ; again multiply by 2 (*8) ASL ;again multiply by 2 (*8)
CLC 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 STA strout ; store A into the console output address
LDX #0 ; 0xA2, 0x00, ; null terminator LDX #0 ; 0xA2, 0x00, ; null terminator
STX strout+1 ; store null terminator to output addr + 1 STX strout+1 ; store null terminator to output addr + 1
; Set flag to do the print ; 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 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 ; End the program
RTS ; 0x60 RTS ; 0x60

@ -0,0 +1,29 @@
; 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

@ -3,35 +3,25 @@
use std::{io, str}; use std::{io, str};
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| //|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// MEMORY ADDRESSES AND FLAG MASKS // MEMORY ADDRESSES AND FLAGS
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| //|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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
// CPU Addresses pub const OUTPUT_BUF_ADDR: u16 = 0x1100; // Output buffer -- Put values to be printed at this location!
const PROGRAM_START_ADDR: u16 = 0x0200; // Program code starts on page 2 pub const INPUT_BUF_ADDR: u16 = 0x1200; // Input buffer
const CPU_RESET_START_ADDR: u16 = 0xFFFC; // This is where the cpu looks for the address to start executing code at pub const INPUT_BUF_SIZE: u16 = 0x00FF; // Input buffer is 255 bytes
// 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 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
// Console Flag Masks pub const PRINT_STR_FLAG: u8 = 0x02; // and indicate what type is being printed.
pub const PRINT_BYTE_FLAG: u8 = 0x01; // Trigger printing a single byte pub const READ_LINE_FLAG: u8 = 0x04; // Set this flag to request user input from the keyboard
pub const PRINT_STR_FLAG: u8 = 0x02; // Trigger printing a string (continues printing bytes until a null byte is encountered) pub const READ_OVERFLOW_FLAG: u8 = 0x08; // This flag is set after reading input if there is too much input for the buffer
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 // BUS
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
use re6502::r6502::{R6502, Bus, Flags}; 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 struct TBus
{ {
memory: [u8; 64 * 1024] memory: [u8; 64 * 1024]
@ -132,47 +122,62 @@ impl Console
let mut value = bus.read(CONSOLE_FLAGS_ADDR); let mut value = bus.read(CONSOLE_FLAGS_ADDR);
if (value & READ_LINE_FLAG) != 0 if (value & READ_LINE_FLAG) != 0
{ {
// reset the flag
value &= !(READ_LINE_FLAG);
let mut buffer = String::new(); let mut buffer = String::new();
let stdin = io::stdin(); let stdin = io::stdin();
stdin.read_line(&mut buffer).expect("Failed to read input from the console"); stdin.read_line(&mut buffer).expect("Failed to read input from the console");
// Make sure the string will fit in the input buffer // Make sure the string will fit in the input buffer
if (buffer.len() + 1) as u16 >= INPUT_BUF_SIZE if (buffer.len() + 1) as u16 >= INPUT_BUF_SIZE
{ {
// The input is too large for the buffer so // TODO: Change this to set an error flag instead of printing. This way
// set the overflow flag // 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; value |= READ_OVERFLOW_FLAG;
bus.write(CONSOLE_FLAGS_ADDR, value);
println!("ERROR: Console cannot store input string into memory, string is too large");
return;
} }
// Store input in the input buffer // Store input in the input buffer
for (i, byte) in buffer.chars().enumerate() for (i, byte) in buffer.chars().enumerate()
{ {
// Truncate the input if it's too large for the buffer
if i as u16 >= INPUT_BUF_SIZE
{
break;
}
bus.write(INPUT_BUF_ADDR + (i as u16), byte as u8); bus.write(INPUT_BUF_ADDR + (i as u16), byte as u8);
} }
// Add the null byte to the end // Add the null byte to the end
bus.write(INPUT_BUF_ADDR + buffer.len() as u16, 0); bus.write(INPUT_BUF_ADDR + buffer.len() as u16, 0);
// store the flags back into memory // reset the flag
value &= !(READ_LINE_FLAG);
bus.write(CONSOLE_FLAGS_ADDR, value); bus.write(CONSOLE_FLAGS_ADDR, value);
} }
} }
} }
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// CONSOLE INPUT
//|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
struct InputConsole {}
impl InputConsole
{
}
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// MACHINE // MACHINE
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
pub struct TestMachine pub struct TestMachine
{ {
bus: TBus, bus: TBus,

Loading…
Cancel
Save