Mostrando entradas con la etiqueta 6502. Mostrar todas las entradas
Mostrando entradas con la etiqueta 6502. Mostrar todas las entradas

AMP with Raspberry Pi: 6502 emulation

This is an example of "Asymmetric Multi Processing (AMP) with Raspberry Pi"
Previous steps involved are datiled in the series of articles
"AMP with Raspberry Pi: Cookbook".

Based on previous work with 6502 emulation an AMP example app was developed:
AMP on Linux GUI (Raspbian)
A remote process (bare-metal) runs a 6502 emulator (fake6502) with a Basic Interpreter (EhBASIC), using two (shared) memory locations for data exchange, one as (keyboard) input for basic interpreter and one for (monitor) output.
One local process (Linux)  send keystrokes to a shared memory location (remote keyboard)
Second local process (Linux)  show Ascii data comming to the other shared memory location (remote monitor)
The process run asyncronously and the IPC (inter process comunication) is imperfect, as no signaling was implemented.



In order to get the example working you need:
AMP framework: (the same as previous posts) Linux on Cores 0,1,2 using lower RAM and Bare Metal on Core 3 using upper RAM (above 0x20000000). See Step 1 for details.

Get example files from git:
git clone https://github.com/telmomoya/AMP

Enough Linux privileges

In a Linux terminal do
cd /bare-metal
./start-metal.sh
cd ..
./monitor6510-char 0x20002ed1

This will start the bare-metal process and the Linux process for monitor basic output.

Keep that terminal open and in another one type:
./keyboard6510-char 0x200058d4

Test the EhBASIC interpeter (answer only "enter" for Memory size)

You can stop emulator with
./stop-metal.sh

And restart it ( again with  ./start-metal.sh) so you have LCM.
If you start ehbasic with Warm option you can list your previous "sesion" basic program, as it remained in memory.

Using bare-metal app via ssh


Under the hood

In bare-metal folder you will find 3 scripts:

start-metal.sh
This script can be used for load the img file (up-metal-6510.img) at 0x2000000 (upper 512Mb) and point Core3 Mailbox3 to that address to start it's execution.

stop-metal.sh
This script stops bare-metal execution

build-up-metal.sh
Used for bare-metal compilation & linking (uses rpi.x linker script)

In root folder you have two files for local (Linux) run.

keyboard6510-char
Waits one text line and sends characters to remote process (bare-metal)

monitor6510-char
Looks to mailbox variable physical address and print if value changes (no signaling implemented, must be improved)


Life control managment
When Linux boots puts all unused cores in a loop, looking for their mailbox 3. When that mailbox is no 0 the core jumps to the address contained there.
The linker script used sets at execution start the code contained in the startup assembler file armc-08-start.S (file listing and comments in this post).
That file prepare the environment (stack and variables initialization) and junps to C code kernel_main function, located at 6510.c in this case.
When kernel_main returns to assembler startup (armc-08-start.S) encounters a loop, similar to Linux one, looking for mailbox3.
So, the restart procedure is identical to initial start: write address execution in mailbox 3.

Now let's see how the stop process was implemented. Take a look to kernel_main function, included at 6510.c

volatile char live=0x1; //LCM flag
volatile char mailbox=0x20; //Emulator OUT (Monitor)
volatile char mailbox2=0x0; //Emulator IN(Keyboard)

#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "6502/6502.h"
#include "io/gpios.h"


void kernel_main( unsigned int r0, unsigned int r1, unsigned int atags )
{
reset6502();
while(live){
step6502();
}
}

Please note: live and mailbox definitions as volatile, in order to avoid compiler optimizations, ensuring variables in memory, accesible for Linux process.

You can see that kernel_main execution depends on "live" value. The idea here is make live=0 from Linux to stop execution (really return Core 3 to the loop looking to mailbox 3 in armc-08-start.S).
In order to determine the physical address for "live" variable we can  lists symbols from object file:

nm up-metal-6510.elf | grep live

In that way the provided script stop-metal.sh does:

lcm_control=0x$(nm up-metal-6510.elf | grep 'D live' | awk '{print $1}')
./devmem $lcm_control b 0x00

First obtain physical address for "live" variable and the writes 0 to it.


IPC
Inter Process Comunication is implemented using shared memory, specifically locations used by mailbox and mailbox2 variables (volatile). Lets list mailbox symbols:

nm up-metal-6510.elf | grep mailbox
20002ed1 D mailbox

200058d4 B mailbox2

On linux you can call
./monitor6510-char 0x20002ed1

that prints on terminal any change to that physicall address, that is mailbox bare-metal variable.

And in another terminal 
./keyboard6510-char 0x200058d4

Sends to mailbox2 the typed chars.

-------------------------------------------------------------
If you want to compile provided sourcefiles do:

Linux keyboard and monitor apps:
cd linux
gcc -o monitor6510-char monitor6510-char.c
gcc -o keyboard6510-char keyboard6510-char.c

Bare-metal emulator:
cd bare-metal
build-up-metal.sh

C64: CP/M & SID Playing

Hi, today I'm pressenting a new video about the ARM powered C64 in Dual Core mode (6510 & Z80) playing a SID file. That is, while Z80 executes CP/M, 6510 executes irq-based SID player.
Border color denotes:
Yellow: irq
Light blue: 6510 instruction
Red: Z80 instruction

Great sid tune comes from Uctumi of PVM, sorry for dirty irq, nasty timmings and Lo-fi.



Tools used:
sidreloc to relocate sid player&data from $1000 to $6000 avoiding CP/M overlappings
PSID64 for prg generation (-n: no interface)
bin2hex for .h header file generation to include from C code

Also, some 6502/10 asm code for SID player initializing and irq vector redirecting:

SRC Code:
* = $5FE7
INIT     LDA #$00
         JSR $6000
         RTS
REDIR    SEI
         LDA #<VECTOR
         LDX #>VECTOR
         STA $0314
         STX $0315
         CLI
         RTS
VECTOR   JSR $6003
         JMP $EA31


OBJ Code:
A9 00 20 00 60 60 78 A9
FA A2 5F 8D 14 03 8E 15
03 58 60 20 03 60 4C 31
EA

Start the thing with:

SYS 24551      $5FE7 (INIT)
SYS 24557      $5FE7 (REDIR)
SYS 2176       CP/M Start

Dual Core C64

Exploring reconfigurability of the ARM-powered C64 I added a Z80 emulator to the existing 6510 emulator. And for dynamic testing what better than cartridgeless C64 CP/M.
So, heterogeneous multi-software-core C64 is obtained. Of course non-parallel concurrency is obtained, as only one hardware core (ARM) is available.

For photos and videos, a very visual (and retro) effect was included, setting screen border color  according to working core (Light Blue: 6510, Red:Z80)





C64 CP/M Background
In 80's Commodore developed a CP/M-cartridge that contained a Z80 to benefit C64 of software
available for CP/M. See more about this at Ruud Baltissen's site.

As the original cartridge shares buses between 6510 and Z80 (and also VIC), not allowing simultaneous processors operation, the presented ARM based, non-parallel dual core, is enough for C64 CP/M execution.
Some CP/M BIOS portions, like disk access take advantage of C64 kernal (ROM) and were written for 6510, and CP/M core, running on Z80, calls them continually (as border colors in the video).

6510 core:
In previous post an ARM based C64 was presented, with a C coded 6502 emulator modified for 6510-like operation. It's based on the great Mike Chambers fake6502 emulator.

Z80 emulator
Looking for free portable Z80 C coded emulator I found Marcel de Kogel's Z80emu: "written in pure C, which can be used on just about every 32+ bit system". It was easyly integrated to the existing project IDE: a bare-metal LPC1769 Eclipse workspace.
For ARM compilation "low-endian CPU" option must be declared in Z80.h at "Machine dependent definitions" section.
Z80 use in the C64 cartridge is limited, as  IORQ and interrupts are not used, only memory access must be implemented.

Z80 memory access
User must provide Z80emu with the Z80_RDMEM() and Z80_WRMEM() functions. As buses are shared with 6510 CPU, the same memory access C functions used by 6510 emulator are used by Z80 emulator: externalread() and externalwrite()

/****************************************************************************/
/* Read a byte from given memory location                                   */
/****************************************************************************/
unsigned Z80_RDMEM(dword A){
return(externalread((A&0xffff)+0x1000););
}

/****************************************************************************/
/* Write a byte to given memory location                                    */
/****************************************************************************/
void Z80_WRMEM(dword A,byte V){
externalwrite(((A&0xffff)+0x1000), V&0xff);
}

Note the 0x1000 term added to adresses, recreating the 74LS283 adder included in the CP/M
cartridge for address-space shift. This is so because of the conflict betwheen 6510's I/O port and Z80's reset vector, both located at 0x0000.

Core switching
Without a core scheduler, the C64 CP/M cartridge, implementes a simple scheme. The Z80 is enabled or dissabled writing a byte to an address in the range $DE00/$DEFF with LSB = 0 or 1.
So, core switch is entrusted to software, look how CP/M does it:

6510 assembler code, part of C64 CP/M Bios: http://www.z80.eu/c64/BIOS65.ASM

MODESW = $DE00 1 = Z80 OFF, 0 = Z80 ON

LDA #0 turn Z80 back on
STA MODESW
NOP delay only


Z80 assembler code, part of Z80 bootstrap routine for the C64: http://www.z80.eu/c64/C64BOOT.ASM
OFF EQU 01H
MODESW EQU 0CE00H

MVI A,OFF
STA MODESW ;TURN OFF SELF
NOP

Note MODESW definitios due to address shift.

This functionality was implemented as follows:
A catch in externalwrite() function enables changes the value of a "processor flag" variable (like the Flip Flop in the cartridge )
In main loop, according to "processor flag" variable, one of the following action is performed:

  • Z80 instruction execution
  • 6510 instruction execution and 6510 interrupt  treatment

Recently I found Kernal64, a Scala Commodore 64 emulator supporting CP/M.
Its autor resolved this, long before, in a very simmilar way.

CP/M loading
For previous C64 IEC testing the Uno2IEC was used, but this simple Arduno based drive emulator does not support sector read and write needed by CP/M disk access.
Not having available a disk drive or highly compatible device (SD2IEC, uIEC, etc.) another solution is necessary, described on its own post: Software-core C64 diskless CP/M boot

More photos