I've documented my design for the NEXT routines, which I've committed to Github here. It's a little sketchy, and I can already see mistakes. I haven't run, or even assembled any of the code yet, but hopefully I haven't made up any of the op-codes, even if some of my assembly syntax is invented! I think the big ideas are clear and I shan't try to explain it again here. I originally wrote that document to post to the Gigatron forum, but I decided it was a bit long to post at this stage.
One thing I will say about that design is that I'm really disappointed that the ROM-mode version of NEXT3 is so expensive. I'd really like running threads in ROM to be cheaper than running threads in RAM. I'm aware that I can encode jumps inline in my instructions, which would vastly speed things up, by removing the double-jump dispatch loop, however it would add 50% to the space usage. I think the current design will probably work, and I can switch to inline jumps later if it turns out that speed is more valuable than space. Also, I really love the double-jump trick - it's so cool!
I'd like to talk a little more about the general approach I'm planning to follow in developing my Forth. I think it can be summarised as:
- Try to avoid shaving any yaks.
- Prefer automation to manual work, except where such automation would conflict with the First Law.
- Prefer writing Forth to Python, except where writing Forth would conflict with the First or Second Law.
- Prefer using Python to write assembly to writing assembly by hand, except where writing Python would conflict with any of the other laws.
In practice, I intend to lean very heavily on Python, following the existing approach of using it as "macro assembler", but also trying to use it to unit test my code, and probably even having a Forth compiler in Python for generating embedded Forth threads.
The first 'law' about not shaving yaks is important. I find it easy to get bogged down in minor details and lose velocity. I'm going to try to enjoy, and even celebrate kicking cans down the road. To that end, here are some yaks I have left unshaven so far:
- The various Python modules / scripts for generating the ROM are in Python 2. I'd rather be using Python 3, and they don't look hard to port. Not the task at hand though!
- I'm clearly going to need to add to the ROM, but all of the space is used by vCPU applications and data. Am I going to work out the conflicts? No, I'll cross that bridge if I come to it. I've copied the dev.py file and the bare minimum dependencies into my contrib area. If I prove the concept, and want to merge later, I'll work out what I've broken.
- I want a Python Gigatron emulator, in order to unit test my code from Python. A Python implementation would be straightforward, but not an insignificant amount of effort. Instead I wrapped gtemu.c as a Python extension using cffi.
- I should probably have some proper build automation, at least a Makefile, but once again, I'm on Windows, no make installed, not going to bother. A bad PowerShell script will have to do.
- I'm developing on Windows, and the appropriate C compiler for Python 2 extensions didn't like compiling gtemu.c (it seems that by default it interprets anything with a .c file extension as C '89, which wanted variables declared at the start of blocks, with no inline initialisation). I could have easily lost hours trying to work out the compiler flags to fix this. Instead I'm treating it as C++. Some other minor issues were the absence of stdint.h, which I fixed with typedefs copypasted from StackOverflow, and needing some explicit casts from void *. It seemed like the path of least resistance.
The CFFI wrapping for gtemu.c is pretty neat actually; I hadn't used CFFI before. Here's an example of using it from iPython:
In [1]: import _gtemu In [2]: f_rom = open('dev.rom', 'rb') In [3]: ROM_buffer = _gtemu.ffi.buffer(_gtemu.lib.ROM) In [4]: f_rom.readinto(ROM_buffer) Out[4]: 131072 In [5]: inital_state = {'PC': 0} In [6]: state = inital_state In [7]: def loop(): global state; state = _gtemu.lib.cpuCycle(state) In [8]: loop() In [9]: state.PC Out[9]: 1 In [10]: state.IR Out[10]: 0 In [11]: state.D Out[11]: 0 In [12]: loop() In [13]: state.PC, state.IR, state.D Out[13]: (2, 24, 128)
This shows running the first two instructions in the ROM! I can see this being really useful.
Now that I have all my tools in place, and a clear idea of what I'm going to write, hopefully my next update will show some real progress towards the goal.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.