65C02 Emulator for esp32
-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
65C02 Emulator for esp32
I wont to run Taliforth2 with an esp32.
Is there a working version for emulating the 65C02 on the esp32?
Is there a working version for emulating the 65C02 on the esp32?
Re: 65C02 Emulator for esp32
There is a copy of fake6502.h included with Tali Forth 2 (in the c65 simulator folder) that contains a working 65C02 simulator written in C. You would #include this in your main program and then you need to provide a function for reading "memory" and a function for writing to "memory". You can just use an array of 65536 bytes to hold the memory. You can look for reads and writes to special addresses (eg. $f001 and $f004 are common) for input and output (you can also simulate other hardware this way). Read the comments near the top of the .h file to see the names and prototypes for the functions you need to write.
Do note that there are other copies of fake6502.h on the interwebs, however not all of them support 65C02 instructions. The version included with Tali Forh 2 does (including some fixes from the Tali2 team to provide correct behavior for the unused opcodes) and it's what is used to test Tali Forth 2 releases.
This isn't a ready-to-use simulator, in that you will need to write a bit of C code to incorporate it into your own program. If you want to give it a try, let us know how it goes.
Do note that there are other copies of fake6502.h on the interwebs, however not all of them support 65C02 instructions. The version included with Tali Forh 2 does (including some fixes from the Tali2 team to provide correct behavior for the unused opcodes) and it's what is used to test Tali Forth 2 releases.
This isn't a ready-to-use simulator, in that you will need to write a bit of C code to incorporate it into your own program. If you want to give it a try, let us know how it goes.
-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
Re: 65C02 Emulator for esp32
Thanks for the replay, I'm very happy about it.
Can you give me an link where to find it please.
Can you give me an link where to find it please.
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: 65C02 Emulator for esp32
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
Mike B. (about me) (learning how to github)
Mike B. (about me) (learning how to github)
- BigDumbDinosaur
- Posts: 9425
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: 65C02 Emulator for esp32
barrym95838 wrote:
I see this simulation is propagating the BBSx, SMBx et al syntax, although in keeping with the general nature of the 6502 assembly language—three-character mnemonics (at one time, an IEEE standard), the x (bit number), which is technically part of the addressing mode, should be part of the operand field, e.g., SMB #0,$80, versus SMB0 $80. The former is how it is done in the Kowalski assembler.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: 65C02 Emulator for esp32
Mike's link is correct.
With regards to BBS0 through BBS7, those are the mnemonics given by Rockwell. You can see it on page 1-11 (9 in my PDF viewer) and again on page 1-12 (10 in my PDF viewer) in their datasheet:
https://web.archive.org/web/20221112220 ... essors.pdf
Interestingly, they do show them without the number earlier in the datasheet. A quick look in WDC's datasheet shows the same situation. In WDC's matrix (Table 5-2), they clearly label them as BBS0, BBS1, etc. but they have them without the number on the previous page.
Isn't Rockwell the company that added those instructions? I suspect that's where it all started. If I was an assembler writer, I would be using the matrix as it includes the opcode byte and extra information the assembler writer needs and I would have ended up with BBS0, BBS1, etc.
Regardless, that was a decision made by whoever added 65C02 support to the fake6502 code. I use that code, but didn't write it. I was just pointing to this particular version of it because Patrick helped me get it to pass the Dormann tests including the 65C02 instructions so it should run Tali Forth 2 (which requires a 65C02 processor). In this case, I'm quite certain that Tali will run on this simulator because it's the one I use to run all of the test cases and Tali has about 90% test coverage.
powersoft_51, I'm not sure what your C programming skill level is, and this is just a library that needs to be incorporated into your own main program. You'll need to arrange an array for memory, write some functions to access that array for simulated memory reads and writes (as well as handling I/O at special addresses), and load Tali into the array to "load" it into the 65C02. Then you can call a function to start the simulation. If you need more assistance, just let us know how far you have made it. We'd also love to hear if you get it all working yourself, as there is some interest in simulating 6502 processors on other hardware and it's nice to know what combinations work (or don't work) well.
With regards to BBS0 through BBS7, those are the mnemonics given by Rockwell. You can see it on page 1-11 (9 in my PDF viewer) and again on page 1-12 (10 in my PDF viewer) in their datasheet:
https://web.archive.org/web/20221112220 ... essors.pdf
Interestingly, they do show them without the number earlier in the datasheet. A quick look in WDC's datasheet shows the same situation. In WDC's matrix (Table 5-2), they clearly label them as BBS0, BBS1, etc. but they have them without the number on the previous page.
Isn't Rockwell the company that added those instructions? I suspect that's where it all started. If I was an assembler writer, I would be using the matrix as it includes the opcode byte and extra information the assembler writer needs and I would have ended up with BBS0, BBS1, etc.
Regardless, that was a decision made by whoever added 65C02 support to the fake6502 code. I use that code, but didn't write it. I was just pointing to this particular version of it because Patrick helped me get it to pass the Dormann tests including the 65C02 instructions so it should run Tali Forth 2 (which requires a 65C02 processor). In this case, I'm quite certain that Tali will run on this simulator because it's the one I use to run all of the test cases and Tali has about 90% test coverage.
powersoft_51, I'm not sure what your C programming skill level is, and this is just a library that needs to be incorporated into your own main program. You'll need to arrange an array for memory, write some functions to access that array for simulated memory reads and writes (as well as handling I/O at special addresses), and load Tali into the array to "load" it into the 65C02. Then you can call a function to start the simulation. If you need more assistance, just let us know how far you have made it. We'd also love to hear if you get it all working yourself, as there is some interest in simulating 6502 processors on other hardware and it's nice to know what combinations work (or don't work) well.
-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
Re: 65C02 Emulator for esp32
I have now a running version, but not a working version!
Make some changes in the base code to get it compi;in/running on my esp32.
The biggest change was rewriting these two declarations
//static void (*addrtable[256])();
//static void (*optable[256])();
With a Lille pyton program I have combine both array's to a case structure.
Also changed the types of variable into the original structure, uin8_t, uint16_t etc.
Also changed the structure of execute6502 in a one trough loop.
Also hardcoded the startadress etc to $fffc, as found in thailiforth.
Please take a look to my first iteration of the translation. For the moment is quick and dirty!
Include my esp32 version, running in the arduino side. The code is working for the esp32 serie and also for the teensy 4.1.
What I not understand is how the single step should working, and the call back function. But that is for later as Thailforth is running.
Thanks for any help to get is running.
Cheers Jan
Make some changes in the base code to get it compi;in/running on my esp32.
The biggest change was rewriting these two declarations
//static void (*addrtable[256])();
//static void (*optable[256])();
With a Lille pyton program I have combine both array's to a case structure.
Also changed the types of variable into the original structure, uin8_t, uint16_t etc.
Also changed the structure of execute6502 in a one trough loop.
Also hardcoded the startadress etc to $fffc, as found in thailiforth.
Please take a look to my first iteration of the translation. For the moment is quick and dirty!
Include my esp32 version, running in the arduino side. The code is working for the esp32 serie and also for the teensy 4.1.
What I not understand is how the single step should working, and the call back function. But that is for later as Thailforth is running.
Thanks for any help to get is running.
Cheers Jan
- Attachments
-
- taliforth2_teensy41_v2.zip
- (31.27 KiB) Downloaded 88 times
Re: 65C02 Emulator for esp32
At the beginning you say you don't have a working version, but then later you say that Tali is running. Are you able to get to Tali Forth fully running? If not, what exactly is not working? I don't have an esp32 or teensy, so I can't easily try your code here.
It looks like you are setting the PC to $FFFC, when you really want to set the PC to the address held in $FFFC and $FFFD (eg. the reset vector). That's what this line was doing:
pc = mem_6502_read16(0xfffc);
Alternatively, you can set the PC direclty to $F010 as that's where kernel_init lives and it should start there.
I think your method stumbles into Tali eventually by starting execution at $FFFC (interpreting the address there as opcodes instead) until it eventually hit a byte with $00, which is the BRK instruction. The Tali2 image you are using has the break vector set to kernel_init as well, so that's how you ended up getting Tali to start.
The single step function, if you wanted to use it, would just need the same treatment you gave to execute6502(). The only difference is it only runs one instruction and then returns. If you are just running TaliForth2 at full speed, then you may not need to fix or use it.
I suspect there was a way to massage the declaration of the "optable" so your compiler would like it, rather than convert everything to a case statement. The original version used an array of pointers to functions. Your case statement version likely runs slower, but if it's fast enough for you then it should be fine to do it that way.
The callback is a way to have a function that you write be called after every instruction. It might be used in a debugger to update the user interface, for example. If you just want to run TaliForth2 and will only be using I/O to interact with it, then you probably don't need to bother with it.
It looks like you are setting the PC to $FFFC, when you really want to set the PC to the address held in $FFFC and $FFFD (eg. the reset vector). That's what this line was doing:
pc = mem_6502_read16(0xfffc);
Alternatively, you can set the PC direclty to $F010 as that's where kernel_init lives and it should start there.
I think your method stumbles into Tali eventually by starting execution at $FFFC (interpreting the address there as opcodes instead) until it eventually hit a byte with $00, which is the BRK instruction. The Tali2 image you are using has the break vector set to kernel_init as well, so that's how you ended up getting Tali to start.
The single step function, if you wanted to use it, would just need the same treatment you gave to execute6502(). The only difference is it only runs one instruction and then returns. If you are just running TaliForth2 at full speed, then you may not need to fix or use it.
I suspect there was a way to massage the declaration of the "optable" so your compiler would like it, rather than convert everything to a case statement. The original version used an array of pointers to functions. Your case statement version likely runs slower, but if it's fast enough for you then it should be fine to do it that way.
The callback is a way to have a function that you write be called after every instruction. It might be used in a debugger to update the user interface, for example. If you just want to run TaliForth2 and will only be using I/O to interact with it, then you probably don't need to bother with it.
Re: 65C02 Emulator for esp32
I was able to get the opcode array to compile properly. There are a few issues due to the fact that Arduino uses a C++ compiler (rather than straight C) so there are conflicts with some keywords like "and" (which I just learned is an alternative to && in the modern C++ standard) and also some Arduino keywords/functions like "bit". It looks like you already found them all and put and underscore at the end of their names.
To get the opcode array to compile, first comment out the uninitialized versions of the arrays near the top just below reset6502() - you've already done this in your version):
Then, near the bottom of the file, just change all of the names that got an underscore to your names:
Then you can use the original code to execute an instruction:
Looking at your other changes, it looks like you made execute6502() run a single instruction and then return. That's technically what step6502() is for, but you call it in your loop() function so it will be called over and over, which has the effect of running the entire 6502 program.
To get the opcode array to compile, first comment out the uninitialized versions of the arrays near the top just below reset6502() - you've already done this in your version):
Code: Select all
//static void (*addrtable[256])();
//static void (*optable[256])();Code: Select all
static void (*optable[256])() = {
/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */
/* 0 */ brk_6502, ora, nop, nop, tsb, ora, asl, rmb0, php, ora, asl, nop, tsb, ora, asl, bbr0, /* 0 */
/* 1 */ bpl, ora, ora, nop, trb, ora, asl, rmb1, clc, ora, inc, nop, trb, ora, asl, bbr1, /* 1 */
/* 2 */ jsr, and_, nop, nop, bit_, and_, rol, rmb2, plp, and_, rol, nop, bit_, and_, rol, bbr2, /* 2 */
/* 3 */ bmi, and_, and_, nop, bit_, and_, rol, rmb3, sec, and_, dec, nop, bit_, and_, rol, bbr3, /* 3 */
/* 4 */ rti, eor, nop, nop, nop, eor, lsr, rmb4, pha, eor, lsr, nop, jmp, eor, lsr, bbr4, /* 4 */
/* 5 */ bvc, eor, eor, nop, nop, eor, lsr, rmb5, cli_, eor, phy, nop, nop, eor, lsr, bbr5, /* 5 */
/* 6 */ rts, adc, nop, nop, stz, adc, ror, rmb6, pla, adc, ror, nop, jmp, adc, ror, bbr6, /* 6 */
/* 7 */ bvs, adc, adc, nop, stz, adc, ror, rmb7, sei_, adc, ply, nop, jmp, adc, ror, bbr7, /* 7 */
/* 8 */ bra, sta_, nop, nop, sty, sta_, stx, smb0, dey,bit_imm,txa, nop, sty, sta_, stx, bbs0, /* 8 */
/* 9 */ bcc, sta_, sta_, nop, sty, sta_, stx, smb1, tya_, sta_, txs, nop, stz, sta_, stz, bbs1, /* 9 */
/* A */ ldy, lda, ldx, nop, ldy, lda, ldx, smb2, tay_, lda, tax, nop, ldy, lda, ldx, bbs2, /* A */
/* B */ bcs, lda, lda, nop, ldy, lda, ldx, smb3, clv, lda, tsx, nop, ldy, lda, ldx, bbs3, /* B */
/* C */ cpy, cmp, nop, nop, cpy, cmp, dec, smb4, iny, cmp, dex, wai, cpy, cmp, dec, bbs4, /* C */
/* D */ bne, cmp, cmp, nop, nop, cmp, dec, smb5, cld, cmp, phx,db6502, nop, cmp, dec, bbs5, /* D */
/* E */ cpx, sbc, nop, nop, cpx, sbc, inc, smb6, inx, sbc, nop, nop, cpx, sbc, inc, bbs6, /* E */
/* F */ beq, sbc, sbc, nop, nop, sbc, inc, smb7, sed_, sbc, plx, nop, nop, sbc, inc, bbs7 /* F */
};Code: Select all
(*addrtable[opcode])();
(*optable[opcode])();-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
Re: 65C02 Emulator for esp32
I have now Taliforth2 running on my esp32/teensy 4.1(600MHz).
In this case I have used the case structure for the opcodes.
See here my first run.
Will try to get the source as so original as posible.
In this case I have used the case structure for the opcodes.
See here my first run.
Will try to get the source as so original as posible.
-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
Re: 65C02 Emulator for esp32
I have a question about implementing my own blocks structure.
Found in the manual this code
' myblockreader BLOCK-READ-VECTOR !
' myblockwriter BLOCK-WRITE-VECTOR !
As an example
myblockreader = $f020 if detected -> void ReadFromSD()
myblockwriter = $f030 if detected -> writeToSD()
Where to put these in the read6502() and write6502 procedure.
Found in the manual this code
' myblockreader BLOCK-READ-VECTOR !
' myblockwriter BLOCK-WRITE-VECTOR !
As an example
myblockreader = $f020 if detected -> void ReadFromSD()
myblockwriter = $f030 if detected -> writeToSD()
Where to put these in the read6502() and write6502 procedure.
Code: Select all
uint8_t read6502(uint16_t address) {
if ( address == IO_AREA + 0x04) return(char_input());
return(RAM[address]);
}
void write6502(uint16_t address, uint8_t value) {
RAM[address] = value;
if (address == IO_AREA + 0x01){char_output(value);
//Serial.printf("char_output=%02x\n",value);
}
}-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
Re: 65C02 Emulator for esp32
Here is my final version. This is now according to the original, with a few small adjustments for the esp32/teensy.
Did a few tests and it works as it should. Now I'm going to try if I can use the sd card to store the blocks.
Did a few tests and it works as it should. Now I'm going to try if I can use the sd card to store the blocks.
- Attachments
-
- 65C02EmalatorTaliforth_V0.zip
- (30.83 KiB) Downloaded 86 times
Re: 65C02 Emulator for esp32
You need to decide how much of the code you want running as 65C02 code and how much you want in C code on your esp32. Tali is expecting to run a routine entirely written in 65C02 code. The easiest method is likely going to be to use another "magic" address similar to how you currently handle terminal I/O. You might, for example, write to this address to start a block write (saving a block to SD card) or a read from this address to start a block read (reading from SD card into simulated RAM). Then, the routines in Tali will just access this magic address to cause the data to be transferred.
Tali expects the routines whose addresses are placed in BLOCK-READ-VECTOR and BLOCK-WRITE-VECTOR to get the buffer address and block number from the Forth data stack. You can access this directly in C using your simulated memory and the simulated x register. The first item on the Forth data stack is located in zero page (eg. starting at memory location zero) using X as the index, and increasing values of X move further down Forth's data stack (it grows upside down). Each Forth item on the stack uses two bytes. Here's an example of getting the block number (on top of the stack) and the buffer address (next on stack) using C code in the simulator (note: not tested):
Once you have the two values, you can open a file, seek to 1024*blocknum, and then read or write the data. The data can be put into (or saved from) the simulated memory starting at address "buffer_address" that you got off the Forth stack. Blocks are always 1024 bytes.
The last bit is to clean up the Forth data stack. Forth expects you to remove the two items from the stack. In assembly, we would normally run "inx" twice for each item we want to remove from the Forth stack. You have direct access to the simulated value in the x register, so you can just add 4 directly to the simulated x register:
Then you just need to create some Forth routines to access the magic address. If you used $f002 as the magic address for block i/o, you might do the following in Forth:
If you cloned the Tali repository, you can edit the forth_code/forth_words.fs file and add the above, then reassemble Tali. That will make it so these words are created and the vectors updated when Tali boots.
Tali expects the routines whose addresses are placed in BLOCK-READ-VECTOR and BLOCK-WRITE-VECTOR to get the buffer address and block number from the Forth data stack. You can access this directly in C using your simulated memory and the simulated x register. The first item on the Forth data stack is located in zero page (eg. starting at memory location zero) using X as the index, and increasing values of X move further down Forth's data stack (it grows upside down). Each Forth item on the stack uses two bytes. Here's an example of getting the block number (on top of the stack) and the buffer address (next on stack) using C code in the simulator (note: not tested):
Code: Select all
uint16_t blocknum = mem_6502_read16(x);
uint16_t buffer_address = mem_6502_read16(x + 2);The last bit is to clean up the Forth data stack. Forth expects you to remove the two items from the stack. In assembly, we would normally run "inx" twice for each item we want to remove from the Forth stack. You have direct access to the simulated value in the x register, so you can just add 4 directly to the simulated x register:
Code: Select all
x+=4;Code: Select all
: myblockreader $f002 c@ ; \ Read a single byte from $f002 to start simulated block read.
' myblockreader BLOCK-READ-VECTOR ! ; \ Store the address of myblockreader in the BLOCK-READ-VECTOR.
: myblockwriter $f002 c! ; \ Write a single byte to $f002 to start a simulated block write.
' myblockwriter BLOCK-WRITE-VECTOR ! ; \ Store the address of myblockwriter in the BLOCK-WRITE-VECTOR.-
powersoft_51
- Posts: 51
- Joined: 22 Jun 2024
Re: 65C02 Emulator for esp32
Thanks for this information, is very helpfull.
Where can I find the free zero page addresses that are left from Taliforth?
I have make some floating point routines for my fig forth, and wont try to reuse these for Taliforth, just for fun!
A'm not able to recompile Taliforth, because don't have the knowledge of the assembler, and have tried to install pymon
on my MacBook, but get no access to to it, installing was succesfull.
For some things I am to old!
Can I distribute my code , the zip file I placed before on this forum with for example the Arduino Forum. I found on that forum no 65c02 emulator, and
notting about Taliforth. Perhaps we can blow in a new life for this forth.
Cheers
Jan
Where can I find the free zero page addresses that are left from Taliforth?
I have make some floating point routines for my fig forth, and wont try to reuse these for Taliforth, just for fun!
A'm not able to recompile Taliforth, because don't have the knowledge of the assembler, and have tried to install pymon
on my MacBook, but get no access to to it, installing was succesfull.
For some things I am to old!
Can I distribute my code , the zip file I placed before on this forum with for example the Arduino Forum. I found on that forum no 65c02 emulator, and
notting about Taliforth. Perhaps we can blow in a new life for this forth.
Cheers
Jan
Re: 65C02 Emulator for esp32
Tali uses the first half of zero page ($00-$7F). The upper half from $80-$FF is available for you to use.
I think it's OK to upload your working 65C02 simulator source code here as a zip as long as it's not too large. It would be good to add licensing information. The fake6502.h file is using a public domain license (eg. anyone can use it for any purpose).
If you want some help setting up the tools to reassemble Tali, just create a new thread (or use one of your existing threads) over in the Forth section, as that's not specific to the simulation stuff we've been discussing here. I don't have a Mac anymore, but you should be able to do it all on your Mac and we can help you get everything set up properly.
I think it's OK to upload your working 65C02 simulator source code here as a zip as long as it's not too large. It would be good to add licensing information. The fake6502.h file is using a public domain license (eg. anyone can use it for any purpose).
If you want some help setting up the tools to reassemble Tali, just create a new thread (or use one of your existing threads) over in the Forth section, as that's not specific to the simulation stuff we've been discussing here. I don't have a Mac anymore, but you should be able to do it all on your Mac and we can help you get everything set up properly.