6502.org http://forum.6502.org/ |
|
WDC/Rockwell 65C02 core with MMU http://forum.6502.org/viewtopic.php?f=10&t=7377 |
Page 1 of 2 |
Author: | kakemoms [ Tue Oct 25, 2022 6:24 pm ] | |||
Post subject: | WDC/Rockwell 65C02 core with MMU | |||
I started some time ago to modify the 65C02 core by David Banks and Ed Spittles, which is based on Arlet Ottens compact 6502 core. The main focus was a MMU that could help with memory access and protection. In the end I also added the WDC/Rockwell 65C02 instructions that were missing: SMB $ZP, RMB $ZP, BBS $ZP,branch BBR $ZP,branch WAI STP These have been documented elsewere, so no need to say anything about them. This release is currently a beta release, so if you find bugs, push it into this tread so they can be fixed. If you don't care about a MMU, you don't need to know anything else. You can use the core as a WDC65C02 or R65C02. The MMU The 65C02 is a fabulous CPU, but it lacks one essencial feature: A way to properly access memory! A MMU is not only a way to access more memory, but also a way to protect that memory. Think about Linux or any other system that needs memory protection. And that is essencially what you get with this MMU. Plus a way to efficiently access all that memory (without having to think about shifting memory banks around). The MMU uses unused opcodes, so it doesn't really interfere with the 65C02. In the future I may also release the MMU as a separate unit so that one can use a real 65C02 and get the same features. The following is a list of MMU instuctions with opcodes. They are explained in the attached file. Code: $82 $xy MMS #$xy Memory Management System register $44 $ZP MMB $ZP Memory Management BLOCK register from Zero Page $E2 $xx MMB #$xx Memory Management BLOCK register $C2 $xx MMZ #$xx Memory Management Zeropage BANK register $5C $xx $yy MMI $yyxx Memory Management Interrupt BANK register $D4 $ZP MMP $ZP,X Memory Management Protection Table from Indexed Zeropage $FC $xx $yy MMF $yyxx Memory Management Fetch BANK register $54 $ZP MMF $ZP,x Memory Management Fetch BANK register from Indexed Zeropage $DC $xx $yy MMJ $yyxx Memory Management Jump BANK register $F4 $ZP MMJ $ZP,X Memory Management Jump BANK register from Indexed Zeropage Please note that this is a beta release so that slight adjustments may be needed to fix some (unknown) bug. The address bus is currently 20 bits. ------------------------------------------------------------------------------------------- Update 20221028: Updated core to correctly handle MMI instruction. MMJ_STORAGE flag now resides at bit 6 of the MMZ instruction (manual needs update).
|
Author: | BigEd [ Tue Oct 25, 2022 8:55 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Wow - quite a lot to digest there! Quite a lot of functionality, too, as far as I can tell. If I tried to summarise, I'd say that this can map either a single 4k or a whole 64k from high memory into the 64k addressable space, and also can remap zero page (or page one? Or both?) into high memory, and can map an ISR into high memory... but I'm not completely sure! There seems also to be some possible separate treatment of code and data, or of reads and writes? |
Author: | fachat [ Wed Oct 26, 2022 8:07 am ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
I'm trying to understand, but I am missing an overview on what internal state the MMU has, how it is used in addressing,. I find it difficult to deduce the effects on the addressing of normal opcode addresses just from the description of the opcodes that change this state |
Author: | kakemoms [ Wed Oct 26, 2022 11:40 am ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Well, I guess the manual was rather short. But I will improve it over time, so hopefully it gets better. Anyway I will try to explain: - The MMU either act on a 4KB memory block (4K-mode) of your choice or on the full 64KB block (64K-mode). This can be changed by the MMS instruction by setting or clearing the flag "FULL_BLOCK_MOVE". If you change this several times in a program, be careful as the program counter (PC) is affected by the JBANK register (it changes the 4 MSB of the 20-bit address bus in the current implementation). - The Zero page can be moved around with the ZBANK register. In 4K-mode, the ZBANK affects bits 20-12 and in 64K-mode the ZBANK affects bits 20-16. Every memory block (4KB blocks in 4K-mode or 64KB blocks in 64K-mode) can have a Zero page that is located in another memory block. If you don't touch the ZBANK register, all Zero page accesses will be into $0000-$00ff (as normal 65C02 operation). Note that stack location also changes with ZBANK, but always starts at Zero Page location+$100. Thus you have to be careful when moving Zero Page location around as it can affect the next RTS. A safe way is to set all Zero page locations before you start using the stacks. - In 4KB mode, 60KB of the 0-64KB adressable range of the 65C02 is not affected by the MMU. E.g. it operates "normally" as without a MMU. The rest (4KB) that is affected by the MMU can be located in any 4KB block in the 0-64KB adressable range. The location of this 4KB block is decided by the BLOCK register. If for example BLOCK register=$A, the 4KB block that starts at $A000 is going to be the MMU memory range ($A000-$AFFF). Any code or access by the 65C02 into this memory range will be controlled by the MMU. The rest of the memory is not controlled by the MMU, but can be affected by it anyway. As you may understand this gives some interesting possibilities. For example, if we go into 4K-mode, set the BLOCK register to $A and jump into the $A000-$AFFF memory range with MMJ set to $00 (=JBANK set to $00), the MMU will allocate memory area $000-$FFF as the $A000-$AFFF. It means that the Zero page of non-MMU memory will also start from $A000 (I know, kind of confusing). If you now set ZBANK register (which always sets ZBANK of the current JBANK to (for example) $1, the Zeropage of the current MMU block will be allocated to $1000 (even more confusing). The thing is that if code in the $A000-$AFFF area is now running and accessing Zero Page, the bytes will appear at $1000-$10FF (which by coincidence is also visible in non-MMU memory area). I will try to make some drawings to better visualize this as it can be very confusing, but it all makes sense once you understand it. E.g. in 4K-mode, the whole memory is divided into 4KB blocks and whatever block you see will appear in the $A000-$AFFF area (in the above example) according to what the MMJ or MMF instructions sets as the block number. Each block can have its zero page located in another block, which gives versatility. 4K-mode address: Code: <----bits decided by MMU---><--bits decided by 65C02--> [20 19 18 17 16 15 14 13 12][11 10 9 8 7 6 5 4 3 2 1 0] The bits decided by MMU is set using MMJ (for JMP or JSR, e.g. PC location) or MMF for reading or storing (LDA/X/Y,STA/X/Y,INC,PHP,TSX and so on...). MMJ sets the JBANK register and MMF the FBANK register. In 4K-mode, bits 20-12 of the PC are equal to the current JBANK register. For example program starts at $1000 in non-MMU mode: Code: $1000 MMS %10101010 Set MMU to standby $1002 MMS %01010101 Start MMU $1004 MMS %01000000 Set MMU flags: BANK_REG_MEM=OFF, IGNORE_LSB=OFF, IBANK MODE/MMJ STORE=OFF, SWAP=0, FULL_BLOCK_MOVE=OFF (4K-mode), PROTECTED_MEMORY=OFF $1006 MMB #$f Set BLOCK register to $f ($f000-$ffff is MMU memory) $1008 MMF $0010 Set MMU memory block to start at $0010000 At this point, any access to $f000-$ffff by the 65C02 will either read or store bytes in the $0010000-$0010fff memory area (or $10000-$10fff if you trunkate it to 20 bits). So we can do that with ease: Code: $100B LDA #$D0 Load the value $D0 into accumulator $100D STA $f001 Store the accumulator value into $10001 So in practice, the MMU does banking but in a more transparent way. If you copy some code to $10000, you can then jump to this code using JMP or JSR with a preceding MMJ: Code: $100B MMJ $0010 $100E JMP $f000 Jump to $10000 As you are probably thinking; how does this work with JSR and RTS? Well, there is a JBANK stack that stores current JBANK value so that any RTS will return to the previous JBANK. Just as the normal 65C02 stack, but stored in another location. The JBANK stack can be exposed by setting the BANK_REG_MEM to ON (it will then be exposed at $0100-$01ff until BANK_REG_MEM is set to OFF). For 64K-mode, the whole adressable 65C02 range is banked (and shifted with the different BANK registers). Thus there is no non-MMU memory in 64K-mode, and the whole memory is divided into 64KB blocks (instead of 4KB blocks as above). Zero page location will always start at $0000 in the current memory block, but can be located at other memory blocks using the ZBANK register. E.g. several 64KB program blocks can share the same Zero page. |
Author: | fachat [ Wed Oct 26, 2022 12:01 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Sorry I'm still confused. So, block selects a 4k window in the CPU address space, and MMf will select the physical address for normal load/store access, MMJ similarly for control jumps. Right? I didn't get the part of the zeropage mapping. Some drawings would be nice, esp how the CPu address is translated to physical address. Maybe a rewrite of your explanation with terms defined more clearly would help as well (like always specify either 'CPU address' or 'physical address' ie where it is in an attached memory chip. So, if I get it right, you can have either a 4k window in the CPUs 64k CPU address space, that you can map to anyof 256 4k blocks in the 1M physical address space - or map the entire 64k CPU address space to any of 16 64k blocks in physical memory. I wonder what is the use case behind it? The first one allows for something like RAM disks. The second one ... what do you want to achieve? |
Author: | kakemoms [ Wed Oct 26, 2022 12:48 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Ok some ASCII drawing may help: Lets divide 65C02 memory range into 4KB blocks and number them from 0 to F: Code: 00 01 02 03 04 05 06 07 08 0A 0B 0C 0D 0E 0F In this drawing, $02 is the $2000-$2FFF memory range and so on.. This is the normal 65C02 representation of memory. There is nothing above 0F here since $F000-$FFFF is the last 4KB block that the 65C02 can access. Now we go into 4K-mode and set block E as MMU memory. We have $FF blocks of 4KB memory (total 1MByte), so the MMU can point to any of those. Lets start with pointing MMU memory to to block $10 using MMF. I will draw the memory block above and below the current MMU memory block on the line above or below to better visualize it: Code: MMF memory map 0F 00 01 02 03 04 05 06 07 08 0A 0B 0C 0D 10 0F 11 The MMF affects were data is read or written to, but not were the program is running (e.g. not the PC). So, with the above memory map, if we read or write something from $E000, the 65C02 thinks it is accessing $E000, but the MMU redirects it to $10000 (since $10 has taken $0E's place). That happens with any instruction that reads or writes to memory, but not the actual program location (which is controlled by the PC). The MMJ on the other part only affects were the program is running (the PC) and not the reading or writing address of the data. Now, lets use MMJ to try to get code running in $11000: Code: MMJ $11 The memory map then becomes Code: MMJ memory map 10 00 01 02 03 04 05 06 07 08 0A 0B 0C 0D 11 0F 12 so we can use: Code: MMJ $11 JMP $E000 From the 65C02 viewpoint we are jumping into the $0E memory block, but the MMU has replaced this with the $11 memory block when reading instructions, so the 65C02 is actually running code that starts at memory location $11000. Any reading or writing to memory by the instructions themselves will still be using the MMF memory map (e.g. to $10000-$10FFF), so code and memory access are not necessarily linked. The Zero Page location of the 65C02 is always at $0000-$00FF with stack at $0100-$01FF. This resides at the firsts 4KB block of the 65C02 memory map. The ZBANK register changes this so that any instructions that accesses Zero Page or Stack will be looking elsewere in the MMU memory. We represent this with replacing block $00 in the above memory map (even if the $0200-$0FFF range is not affected). Lets set it to $24 using MMZ: Code: MMZ/MMF memory map 23 0F 24 01 02 03 04 05 06 07 08 0A 0B 0C 0D 10 0F 25 11 Now, lets look at the instructions Code: LDA #$E0 STA $04 LDA #$00 STA $03 LDX #$01 LDA ($02,X) Here, A(ccumulator) is loaded as $E0 and stored into $04 by the 65C02. The MMU changes the ZP address to $24004, so that is were $E0 is stored. Next, $00 is stored into $03 which becomes memory location $24003 as the MMU handles it. Fourth line loads 1 into the X-register, then LDA reads a memory pointer from memory location ($02+1=)$03 and $04. The MMU also changes this to $24003 and $24004 as they are Zero page locations. The instruction then uses the content from $24003(=$00) and $24004(=$E0) as a pointer and reads from address $E000. Again the MMU recognizes $E000 as MMU memory and fetches data from $10000 according to the above memory map. |
Author: | BigEd [ Wed Oct 26, 2022 1:08 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Overall I think this is very interesting but it does still need a clear explanation. Thanks for the two recent clarifications - they've helped me, at least. So, it seems, in this 4k mode, you have three registers which affect memory mapping MMF for data loads and stores, and rmw MMJ for program fetches - opcodes and operand bytes MMZ for page 0 and page 1 accesses (It feels better to me to make this two very different discussions, one about the 4k mode and another about the 64k mode - in the manual you interleave these two mechanisms and I felt that made it more difficult to understand.) It would be important, i think, to clarify that MMZ affects all accesses to pages 0 and 1, if that is true. That is, it does not selectively affect only zero page modes or stack accesses. If I TSX LDA 0103,X then I would, I think, want to access the mapped page 1. |
Author: | BigEd [ Wed Oct 26, 2022 1:21 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
BTW, it may well be that different people have to take different routes to understanding this kind of thing. For me, knowing that a big RAM chip has 20 address pins, and the 6502 has 16 address pins, I most need to know which bits have to go through a magic box, and what the configuration state of that box is. The actual names and numbers of the instructions which configure the box are a bit less important. But in this case, if we perhaps have three different mechanisms all working for different purposes, we also need to know what the conditions are for each mechanism to kick in. It feels like perhaps documentation is the hardest part! |
Author: | gfoot [ Wed Oct 26, 2022 6:39 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
It's a nice idea, integrating it into the core. I know Andre has done this sort of thing externally before with great success. I also had a go a few years ago, seeing how far I could go without needing a formal MMU. The biggest challenge there was dealing with transitions - atomically paging the stack while returning to user code was I believe a sticking point. MMZ for example will mean you lose all thread context, so at that point you probably want to immediately execute an RTI or similar to pick up where whatever thread is using that zero page left off. Edit - you'll also want to get the SP in the right place first... |
Author: | kakemoms [ Thu Oct 27, 2022 7:10 am ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Documentation is always the hardest part, but its a work-in-progress, so just bear with me. There are indeed 3 registers that affect memory locations. ZBANK (Zero page and stack location), the JBANK stack (PC page location) and the FBANK (address read/write page location). Two registers IBANK and NBANK affects IRQ and NMI address vector locations, so you may want to add those. The MMZ allows several things: - When running simple banked memory, you can have more than one stack and more than one zeropage. More stack may be helpful in some programming environments (Fortran for example). - When using more than 64KB of memory, you can have the Zero page point to the same place in all the memory blocks so that you don't loose that context when shifting to another 64KB block (or 4KB if you want to stick to 4K-mode for some reason). - When using protected memory and shifting between supervisor mode and user mode, there are different Zero pages and stacks. It means that a subprocess in user mode can pass parameteres to the supervisor through the stack. This is important since the subprocess will never be able to read the memory area of the supervisor. And that is were the MMU shines, its not just about banking memory. The signee of this posts is not going to give you Linux on the 6502, but with this MMU its at least theoretical possibility. Security in microcontrollers hasn't been on the agenda in the past, but recent developments will probably mean that one has to rethink that part. I tried to put the MMU into a separate module, but the supervisor/user mode made it hard... at least I didn't succeed with adding it to a NMOS6502 based system... Maybe a real 65C02 can get a MMU that retains all the features here. The RDY signal will certainly help, but there are a few obstacles to overcome. Current bugs: * The MMI instruction is either not there or has been lost somewere. * There is a special mode were MMJ also affects the storage destination. The flag seems to be either misplaced or lost. * Documentation version is more like V0.1 beta. * Lack of programming examples |
Author: | fachat [ Thu Oct 27, 2022 1:23 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Now that it has been mentioned... - here is my CPU board with an external MMU http://www.6502.org/users/andre/csa/cpu/index.html It maps any of the 16 4k blocks in CPU address space to any of 256 4k blocks in the 1M physical address space. Specifically it does: - reset handling where the first write actually enables it - use a No-Execute bit with the SYNC CPU output to signal an error condition on the bus - use a No-Write bit with R/-W to signal an error condition on the bus when read-only pages are written to - use Valid bit to signal an error condition on the bus if an invalid page is being accessed - It is mapped into an IO area that is selected separately from the MMU translation so access does not get lost - registers are accessible from the external bus when an external CPU controls it I implemented a multitasking operating system for it that uses the mapping to separate memory for different processes. The error conditions on the bus can trigger an external CPU to stop the main CPU and manipulate the MMU. I did plan for paging, but never implemented it. I did have the external CPU trace the main CPU with it using the no-exec condition. Putting that into the CPU proper would be great, esp if error conditions could trigger an internal supervisor mode instead. You could also add a supervisor-only bit to protect memory from manipulation by user programs. |
Author: | BigEd [ Thu Oct 27, 2022 1:45 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
(Just to note, this uses a 74LS610 or equivalent, I think, which has a 16 word by 12 bit configuration. I suppose attaching it to an 8 bit bus reduces it to 16 words by 8 bits? Or perhaps with more circuitry the high nibbles could also be written.) |
Author: | fachat [ Thu Oct 27, 2022 8:44 pm ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Indeed. The first versions of the board only used 8 of the 12 bits for pure address mapping. Later I used a separate register (edit: that's the CPU control register IC6 in the schematics linked above) to store the no-exec, write protect and valid bits into further 3 MMU bits, i.e. now using 11 of the 12 bits. |
Author: | kakemoms [ Fri Oct 28, 2022 5:06 am ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
gfoot wrote: It's a nice idea, integrating it into the core. I know Andre has done this sort of thing externally before with great success. I also had a go a few years ago, seeing how far I could go without needing a formal MMU. The biggest challenge there was dealing with transitions - atomically paging the stack while returning to user code was I believe a sticking point. MMZ for example will mean you lose all thread context, so at that point you probably want to immediately execute an RTI or similar to pick up where whatever thread is using that zero page left off. Edit - you'll also want to get the SP in the right place first... Its what it was designed for. I will make programming examples which shows how to achieve this. The programmer must set up everything correctly for the secure memory layout to work, but all the tools are there. MMZ can be used to make certain memory blocks stick to the same stack (so a memory area can span many blocks while sharing the same stack). Going into user mode is always done using a JSR to a protected memory block. The return address is stored in the stack of the supervisor, then everything runs in the protected memory block until it encounters a RTS back to the supervisor. At that point, the return address is pulled from the supervisor stack and the cpu gets back there. There are no other ways to get out of the user mode memory, so efficiently a user mode process is isolated with no direct access to supervisor mode memory. One must remember that MMJ (the J for Jump) will store the target memory block for the next JMP or JSR. That is stored on a separate stack (the JBANK stack). For that reason you always want to set up zero page/stack locations with MMZ before you start with the MMJ instructions. If you stick to that, you can prevent confusion. There is also only one JBANK stack, so that should be quite straight forward. As for interrupt, the memory bank with the interrupt routine is specified through the IBANK register. Before any interrupt can occur, the programmer must specify the location of this bank with the MMI instruction. As an interrupt is triggered, the interrupt routine will pull the interrupt location from this bank, then execute it. Thus, the interrupt return address gets stored in the stack of the IBANK, and at an RTI it will correctly pull the right address, and the MMU will direct it towards the last JBANK (which was stored by the last MMJ). Now there are probably ways to break this, but if the programmer adheres to a certain way of doing things, it should work. |
Author: | BigEd [ Fri Oct 28, 2022 7:59 am ] |
Post subject: | Re: WDC/Rockwell 65C02 core with MMU |
Just to be clear, the JBANK stack is not a stack of values within the MMU, I think(*), rather the JBANK value points to a page-one mapping which replaces the 6502 stack. (Or indeed, both a page zero and a page one, because that's what you've chosen to build.) (*) Edit: I was wrong, see downthread. |
Page 1 of 2 | All times are UTC |
Powered by phpBB® Forum Software © phpBB Group http://www.phpbb.com/ |