Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
olivierjan committed Apr 26, 2020
2 parents 65d9809 + 22b5fa8 commit 5f92470
Show file tree
Hide file tree
Showing 12 changed files with 1,991 additions and 50 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

.DS_Store
.gitattributes
.vscode
139 changes: 107 additions & 32 deletions 6502-SBC-using-Teensy.ino
Original file line number Diff line number Diff line change
@@ -1,27 +1,42 @@
/*
Bus Access system
Access 65C02 address and databus on 24 Teensy Pins
Start simulate some memory for 65c02 to Read/Write.
*/
//
// TEENSY 65C02
//
// Access 65C02 address and databus using Teensy 3.6(3.3v) or 3.5 (5v)
// Can simulate RAM, ROM and ACIA (6551 or 6850)
// Provide access to all pins.


#include "SBC.h"

// ROM image declared as a C Array.
// Can be easily created from a real ROM image using srec_cat
// http://srecord.sourceforge.net/man/man1/srec_cat.html

#ifdef ROMEMU
#include "rom.h"
#endif


// Declare the Pins to use

// Bus Pins (DATA and ADDRESS)
// As the Teensy doens't expose 16 consecutive PINS attached to the same PORT
// we need to split the Address bus over two different PORTS

byte dataPins[] = {5,21,20,6,8,7,14,2}; // Data Bus
byte addressHPins [] = {30,29,1,0,18,19,17,16}; // Address Bus High byte
byte addressLPins[]={12,11,13,10,9,23,22,15}; // Address Bus Low byte

// Variable declarations

byte dataPins[] = {2,14,7,8,6,20,21,5}; // Data Bus
byte addressHPins [] = {16,17,19,18,0,1,29,30}; // Address Bus High byte
byte addressLPins[]={15,22,23,9,10,13,11,12}; // Address Bus Low byte
uint8_t chipEnable; // RAM, ROM or Serial to access

#ifdef DEBUG
byte databyte=0;
byte databyte=0;
uint16_t savedaddress=0;
#endif


// Declare global variables
// Must be volatile to avoid any compiler optimization

Expand All @@ -31,50 +46,98 @@ volatile boolean rw;
volatile byte ACIAStatus;
volatile uint16_t address;

// If RAM emulation is required
// declare a corresponding array

#ifdef RAMEMU
byte mem[RAMSIZE]; // declare RAM as a byte array.
byte mem[RAMSIZE]; // declare RAM as a byte array.
#endif

// Redefine yield() as the default one is useless and waste time.
// This is required to achieve a descent speed

void yield(){}

void setup() {

Serial.begin(0); // Start the serial port

#ifdef DEBUG
Serial.print("Bringing RESET LOW\n");
#endif

// First thing, let's bring reset LOW
// This will stall the 6502 while we Initialize

pinMode(RESETPIN, OUTPUT);
digitalWrite(RESETPIN,LOW); // Bring the RESET LOW to get the 6502
// in a reset state

// Set all pins to their initial state.
#ifdef DEBUG
delay(2000);
#ifdef RAMEMU
Serial.print("RAM Emulation\n");
#endif

#ifdef ROMEMU
Serial.print("ROM Emulation\n");
#endif

#ifdef ACIA6551
Serial.print("6551 ACIA Emulation\n");
#endif

#ifdef ACIA6850
Serial.print("6850 ACIA Emulation\n");
#endif

#ifdef BARECPU
Serial.print("CPU Only\n");
#endif
Serial.print("Initializing PINs\n");
#endif
#endif

// Initialize Data and Address BUSs to Input

for (int i=0;i<8;i++) {
pinMode(dataPins[i],INPUT); //Ensure we're not writing to the Bus
pinMode(addressLPins[i],INPUT_PULLDOWN);
pinMode(addressHPins[i],INPUT_PULLDOWN);
}

// Initialize R/W' and PHI2

pinMode(RWPIN, INPUT);
pinMode(CLOCKPIN, OUTPUT);
pinMode(RESETPIN, OUTPUT);

// With a bare CPU we need to drive these pins high.
// And SO LOW
// On a real SBC, this should be done by the circuit.

#ifdef BARECPU
pinMode(BEPIN, OUTPUT);
pinMode(RDYPIN, OUTPUT);
pinMode(NMIPIN, OUTPUT);
pinMode(IRQPIN, OUTPUT);
pinMode(MLPIN, INPUT);
pinMode(VPPIN, INPUT);
pinMode(SYNCPIN, INPUT);
pinMode(SOPIN,OUTPUT);
digitalWrite(BEPIN,HIGH);
digitalWrite(RDYPIN,HIGH);
digitalWrite(IRQPIN,HIGH);
digitalWrite(NMIPIN,HIGH);
digitalWrite(SOPIN,LOW);
#endif

// With a CPU on its own circuit we just want to read what happens.

#ifndef BARECPU
pinMode(BEPIN, INPUT);
pinMode(RDYPIN, INPUT);
pinMode(NMIPIN, INPUT);
pinMode(IRQPIN, INPUT);
#endif

#ifdef DEBUG
Serial.print("Initializing varialbles\n");
Serial.print("Initializing variables\n");
#endif

// Initialize some variables
Expand All @@ -83,13 +146,9 @@ void setup() {
chipEnable=0x0;
rw=true;

#ifdef DEBUG
Serial.print("Bringing RESET LOW\n");
#endif

digitalWrite(RESETPIN,LOW); // Bring the RESET LOW to get the 6502
// in a reset state



#ifdef RAMEMU
#ifdef DEBUG
Serial.print("Initializing RAM\n");
Expand All @@ -104,20 +163,30 @@ void setup() {
#endif

digitalWrite(RESETPIN,HIGH); // Release RESET and start working

Serial.print("Starting SBC....\n");
Serial.print("Starting Teensy 65C02....\n");
}


void loop(){

GPIOA_PDOR &=0x0000; // Bring Clock LOW to start Phase 1
for (i=0; i< WAITCYCLE; i++){} // Keep Phase 1 LOW

// Start the loop by bring Clock low and up again
// Duration of PHI1 (Clock low) is defined by WAITCYCLE

GPIOA_PDOR &=~(1<<13); // Bring Clock LOW to start Phase 1
// Clock is on PIN 13 of PORT A

for (i=0; i< WAITCYCLE; i++){} // Keep Phase 1 LOW for WAITCYCLES

GPIOA_PDOR |=1<<13; // Bring Clock HIGH to start Phase 2

// Read the Address BUSs

addressL = GPIOC_PDIR; // Read the Address Bus Low byte
addressH = GPIOB_PDIR; // Read the Address Bus High byte

// PORT B doesn't have 4 consecutive Pins so we need to concatenate

addressH |= (GPIOB_PDIR >> 12) & 0xF0; // Shift the register right by 12 bits
// to get bits 16,17,18 and 19
// in position 4,5,6 and 7.
Expand All @@ -129,27 +198,32 @@ void loop(){
savedaddress=address; // Saving original address for DEBUG as it's modified for ROM access.
#endif

// Address decoding

if (address < RAMSIZE) {
chipEnable=RAMENABLE;
} else if (address >= ROMADDRESS) {
chipEnable=ROMENABLE;
} else if (address >= ACIADDRESS) {
chipEnable=ACIAENABLE;
}


rw=(GPIOA_PDIR>>12)&0x1; // Check if it's a Write Cycle
// or Read Cycle

// If it's a READ Cycle, we need to reconfigure DATA Pins to OUTPUT
GPIOD_PDDR=(rw) ? GPIOOUTPUT : GPIOINPUT; // Configure PIN direction.



// Based on Address decoding, now is time to taje appropriate actions.
switch (chipEnable) {


#if defined ACIA6551 || defined ACIA6850
#if defined ACIA6551 || defined ACIA6850
case ACIAENABLE:
if (rw) {
if (address == ACIADATA){ // CPU wants to get data
GPIOD_PDOR=Serial.read() ; // Send what's in buffer (CPU should have checked buffer ia not empty)
GPIOD_PDOR=Serial.read() ; // Send what's in buffer (CPU should have checked buffer is not empty)
} else if (address == ACIASTATUS) { // CPU Wants to check buffer status
GPIOD_PDOR=(Serial.available()>0) ? (RDRFBIT | TDREBIT) : TDREBIT; // We only check if read buffer is empty.
}
Expand All @@ -160,7 +234,7 @@ void loop(){
// We simply ignore the case where we receive config /Control/Reset byte for ACIA.
}
break;
#endif
#endif


#ifdef ROMEMU
Expand All @@ -184,6 +258,7 @@ void loop(){
}
#ifdef DEBUG
databyte=(rw) ? GPIOD_PDOR : GPIOD_PDIR;

Serial.print(savedaddress,HEX);
Serial.print("\t");
Serial.print(rw);
Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SBC (for Teensy 3.6)

This codes uses a Teensy to emulate hardware of a **6502** based computer apart from CPU itself.
This codes uses a Teensy to emulate hardware of a **65C02** based computer apart from CPU itself.
The following parts can be emulated :

- Clock (mandatory)
Expand All @@ -24,7 +24,7 @@ Edit `SBC.h` and `#define` which part are to be emulated by commenting out the u
> 3. If you emulate a component, make sure a real IC is not actually connected, as both Teensy and the IC will put data on the bus at the same time !
### ROM configuration
If you emulate a ROM, provide it as a C Array in a file called `rom.h`. Two ROMs are provided here:
If you emulate a ROM, provide it as a C Array in a file called `rom.h`. Three ROMs are provided here:

1. **OSI Basic ROM** (rom.h.OSI)
- Uses a 6850.
Expand All @@ -38,11 +38,24 @@ If you emulate a ROM, provide it as a C Array in a file called `rom.h`. Two ROMs
- ACIADDRESS: 0xA000
- ROMADDRESS: 0xB000

3. **Java6502** (rom.h)
My own ROM using Microsoft Basic 2
- RAMSIZE(max): 0xC000
- ACIADDRESS: 0xC100
- ROMADDRESS: 0xD000

You can replace with your own ROM, using srec_cat to generate the C Array. See my [ROM Software repo](https://github.com/olivierjan/ROM-software-for-6502-SBC) for more details.

The code is very basic but is meant to help debug a hombrew computer. It can be greatly optimised and I will try to add new features over time (usable debugging, support for VIAs and other components, etc...).

**WARNING:** Teensy 3.6 is only 3.3v and will provide power to the whole circuit. A 5V version should be possible using Teensy 3.5 but I haven't tested yet.
**WARNING:** Teensy 3.6 is only 3.3v and will provide power to the whole circuit. A 5V version should be possible using Teensy 3.5 but I can't get this to work on Teensy 3.5 at the moment.

### PCB Board
A PCB to host the Teensy and the 65C02 is provided, I designed it using EasyEDA and ordered it from JLCPCB.
I provided the design here in Altium and Gerber format, but haven't tested these, only the EasyEDA.

The PCB can be plugged in an existing circuit, and you can choose to power the circuit from Teensy or not using the jumper.


Any comments welcome !

Expand Down
54 changes: 39 additions & 15 deletions SBC.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
/*
Bus Access system
Access 65C02 address and databus on 24 Teensy Pins
Start simulate some memory for 65c02 to Read/Write.
*/
//
// TEENSY 65C02
//
// Access 65C02 address and databus using Teensy 3.6(3.3v) or 3.5 (5v)
// Can simulate RAM, ROM and ACIA (6551 or 6850)
// Provide access to all pins.


//#define DEBUG
#define ACIA6551 // Model of ACIA to emulate

//#define DEBUG 0
#define ACIA6551 1 // Model of ACIA to emulate

//#define ACIA6850 // 6850 or 6551
#define ROMEMU // Do we emulate RAM or is it a real chip ?
//#define ROMEMU // Do we emulate RAM or is it a real chip ?
//#define RAMEMU // Do we emulate ROM or is it a real chip ?
#define BARECPU 1 // Are we just using the 65C02 ?

#ifdef BARECPU
#ifndef ROMEMU
#define ROMEMU 1
#endif
#ifndef RAMEMU
#define RAMEMU 1
#endif
#endif


// For the time being, memory map is :
// 0x0000 - (RAMSIZE-1): RAM
Expand All @@ -18,9 +32,9 @@ Start simulate some memory for 65c02 to Read/Write.
// Make sure you provide right values below, even if the component is not emulated


#define RAMSIZE 0xA000 // Size of RAM to emulate
#define ACIADDRESS 0xA000 // Base address of Serial Device
#define ROMADDRESS 0xB000 // Base address of ROM Data
#define RAMSIZE 0xC000 // Size of RAM to emulate
#define ACIADDRESS 0xC100 // Base address of Serial Device
#define ROMADDRESS 0xD000 // Base address of ROM Data
// make sure your ROM fit as there is nocheck.
// if the ROM is too big some of it won't be reachable with 16bits addresses

Expand All @@ -42,13 +56,23 @@ Start simulate some memory for 65c02 to Read/Write.
#define ACIASTATUS ACIADDRESS
#endif

#define WAITCYCLE 5 // How long do we wante Phase 1 to be.
#define WAITCYCLE 1 // How long do we wante Phase 1 to be.

// PIN definitions

#define RWPIN 3 // Read/Write' signal
#define CLOCKPIN 4 // Clock generation
#define RESETPIN 33 // Reset signal
#define RWPIN 3 // Read/Write' signal
#define CLOCKPIN 4 // Clock generation
#define RESETPIN 33 // Reset signal
#define BEPIN 24
#define RDYPIN 34
#define IRQPIN 27
#define NMIPIN 26
#define MLPIN 31
#define SYNCPIN 32
#define VPPIN 36
#define SOPIN 35

// #define SOPIN

#define GPIOOUTPUT 0xFF // Value to configure Pins for Output
#define GPIOINPUT 0x00 // Value to configure Pins for Input
Expand Down
Loading

0 comments on commit 5f92470

Please sign in to comment.