I draw a line and it appears exactly where I thought it would on any screen and any printer. The P1, future P2, and even the Pi have nothing similar to offer.
I know you are not into this but this is why we have abstractions in software.
If your app, the Plain English system in this case, wants to draw a line in should use and rely on an API to do that "draw (startPoint, endPoint)" or whatever. The simpler the API to get the job done the better. The layer implementing that abstraction may well be implemented with a Windows API in your system. But at least the abstraction boundary makes it possible for someone like me to come along and implement an alternative that uses whatever I have available on my machine/OS.
Similarly, when compiling Plain English to code an abstract intermediate representation of the code would allow anyone to easily generate real code from that intermediate representation for whatever machine they happened to have.
I don't think such abstraction layers make the code much bigger or harder to understand than what you have already. In fact I would claim that such layering and separation of concerns makes things easier to comprehend.
I think my point is that you don't have to "guarantee that it will be there on all the target machines." You only have to provide the low level implementation that maps the abstractions to Intel x86 and Windows. We out here could provide implementations for whatever we have. If people are interested they will do it and contribute to the project. Which is why you need github by the way.
This has the magic effect of freeing you to develop the actual language/IDE system rather than spend time supporting this or that platforms.
Here's an example of the "portability" problem so you can see why the opcodes don't seem like the critical path to me.
A do-nothing Plain English program looks like this:
To Run:
Start up.
Shut down.
Now that standard "Start up" routine is in the Noodle and looks like this:
To start up:
Initialize com.
Initialize winsock.
Initialize gdi+.
Initialize the talker.
Initialize the module.
Initialize the colors.
Initialize the screen.
Initialize the window.
Initialize the fonts.
Initialize the cursors.
Initialize the mouse.
Initialize the canvases.
Create the default console.
And each of those things that we initialize turn out to be quite different in each and every operating system. Sometimes they're implemented with an alternate architecture in mind; sometimes they're only partly there; sometimes they're not there at all. But they're hardly ever trivial. For example, here's what we have to do to just get a blank freaking screen to work on in Windows:
To initialize the window:
Put a window class's magnitude into the window class' cbsize.
Put 40 [cs_owndc + cs_dblclks] into the window class' style.
Point the window class' lpfnwndproc to default window proc.
Put the module's handle into the window class' hinstance.
Put the module's name's first into the window class' lpszclassname.
Call "user32.dll" "RegisterClassExA" with the window class's whereabouts.
Call "user32.dll" "CreateWindowExA" with 0 and the module's name's first and the module's name's first and -2147483648 [ws_popup]
and 0 and 0 and the screen's pixel width and the screen's pixel height and 0 and 0 and the module's handle and 0.
Call "user32.dll" "ShowWindow" with the main window and 1 [sw_shownormal].
All of that has to be rewritten to get the same effect in, say Linux with KDE. And written again to accomplish the same thing in Linux with Gnome. And written altogether differently to get a blank screen on the P1 or P2.
See what I'm saying? The major hurdle to porting occurs at a much higher level than machine code.
What I don't think you realize is that the barriers to portability are primarily the API facilities (or lack thereof) on each platform. Windows, for example, gives me guaranteed access to a reasonably complete and device-independent and set of graphics routines on any machine running Windows XP thru 10: I draw a line and it appears exactly where I thought it would on any screen and any printer. The P1, future P2, and even the Pi have nothing similar to offer. So I have to figure out how to beg, borrow, or steal that functionality from elsewhere, and how to guarantee that it will be there on all the target machines. I could translate the opcodes a dozen times, by hand, in the time it will take to resolve that problem!
Well, yes, that bit is obvious. The point of opcodes is to get the compiler side working, at least.
Step 1 would be a terminal/command line interface.
What you describe is more along the lines of full self hosting, and yes, that is always more work.
Assuming P1 and P2 analogs for all those opcodes exist. I'm not sure how this helps, though. The big problem is not enough memory. Our compiler assumes there will be a nearly bottomless stack and gigabyte heap. Getting that to work in 512KB on a P2 may be possible, but it will require a major re-thinking of the whole thing.
Here again you are talking immediate self-hosting.
Sure, any present PC Compiler will be lazy, because it can be.
- but you can do useful work with a cross-compiler, which is how all Microcontroller development is done now.
If you then find you can tune your compiler to self-host, in 512k, that is great.
If that takes longer, that is not drop-dead.
I'll declare a bias here. I don't particularly like Spin. I don't like the way the indenting is part of the language, and how whitespace is an important part of the code. I am never fully confident when I copy and paste code between various programs that the whitespace will get copied faithfully and the code will run as expected. I also don't like the heavy dependence on ~!@#$%^&*()<> characters.
So as I non-fanboi of Spin, I'm kind of hoping that might lend a little more weight when I suggest that Spin is the best initial target language for Plain English.
C as a target language is another option. The big advantage of C is that there are external memory cache drivers which mean you can start knowing that you have megabytes of ram available with a SPI chip that is only using 4 propeller pins.
But the big advantage of Spin is that most of the Obex object exchange is written in Spin. There are years and years of work in that Obex. It makes things so much easier thinking of a project as grabbing a video object, a keyboard object, a serial port object, a mouse object and a SD card object and gluing them together with a few lines of Spin. As an example of using other people's work to speed up a project, I once wrote a movie player in Spin using just two lines of code
But there are only a handful of those. The bulk of a program's vocabulary (and grammar, for that matter) is inherent in the TYPES, GLOBALS, and ROUTINE HEADERS that the programmer has coded. And those are indexed using hash tables (with linked lists for overflow) so we can the find the things we need quickly.
As someone who has written a compiler (as have quite a number of people on this forum and this thread, many of whom may well undersell their considerable talents), this sort of information is very helpful.
Let's take a "Hello World". The actual text is the rather trivial bit. The complex part is where it actually gets printed. If to an actual printer, the hard work (in the olden days) was toggling centronics pins to send out the bytes. Nowadays, the hard part is the arcane world of printer USB drivers. If printing to a dumb terminal, the hard work is the code of a bitbanging serial driver. If printing that on a VGA monitor, the hard work is the video driver. For printing on a touchscreen, it is the font drivers and getting the pixels updated quickly. If it is a TV monitor, again the video driver.
Printing "Hello World" on one particular device is interesting. But printing it on multiple devices with a few line changes in the code is the clever part. On the Propeller (and the Arduino) it is a matter of including objects easily to do these things.
There is some Obex code to do this in various versions of Basic. Ditto C. But the most complete library is in Spin.
Below is my half finished code for Pacman. It is written in Spin, and yes, it is a bit slower than it should be. There are a whole lot of video drivers behind the scenes, but the core of the Spin code, the part that works out the logic of how the ghosts move, is not that many lines.
I know you are not into this but this is why we have abstractions in software. If your app, the Plain English system in this case, wants to draw a line it should use and rely on an API to do that "draw (startPoint, endPoint)" or whatever. The simpler the API to get the job done the better. The layer implementing that abstraction may well be implemented with a Windows API in your system. But at least the abstraction boundary makes it possible for someone like me to come along and implement an alternative that uses whatever I have available on my machine/OS.
I must be misunderstanding, because I'm pretty sure that's what we've got. The Noodle (our standard routine library) is, in essence and in every other way, the Plain English API "abstraction layer". It's capabilities are documented in the second half of our instruction manual, topic-by-topic. And if you leave the headers alone, you can rework the implementation of any routine to suit another platform and all of the Plain English code above will run as before. Here, for example, is our bottom-level "Draw a line" routine:
To draw a line with a color:
Create the hpen of the current canvas given the color.
Call "gdi32.dll" "MoveToEx" with the current canvas and the line's start's x and the line's start's y and nil.
Call "gdi32.dll" "LineTo" with the current canvas and the line's end's x and the line's end's y.
Convert the color to a colorref.
Call "gdi32.dll" "SetPixelV" with the current canvas and the line's end's x and the line's end's y and the colorref.
Destroy the hpen of the current canvas.
The first line is the exposed part of the API and defines how the routine is called by any and all Plain English programmers. The rest is an implementation suitable for Windows; that's the part that would be different in a Linux/KDE Noodle or a Linux/Gnome Noodle or a P1 or P2 Noodle.
The compiler, I admit, is handled a little differently: in this case, the compiler itself is the abstraction layer and the only exposed routine in the compile's API is "compile a directory". We could easily move every compiler routine with a hex literal in it into the Noodle and then all the platform specific stuff would be in one place. We kept this stuff in the compiler simply because it was more convenient for us while we were coding, and more direct for the student who wants to see how the thing works. It also saved us some parameter passing since we could count on the compiler's global variables being there; the overall abort flag, for example: if we moved all those routines to an "independent" library we'd have to pass a boolean (and possibly an error message) back from each routine. But again, there's not that much of it in the compiler; where exact analogs of the machine instructions exist, it's a mere day's work (at most) to make the necessary changes.
Windows, for example, gives me guaranteed access to a reasonably complete and device-independent and set of graphics routines on any machine running Windows XP thru 10: I draw a line and it appears exactly where I thought it would on any screen and any printer. The P1, future P2, and even the Pi have nothing similar to offer. So I have to figure out how to beg, borrow, or steal that functionality from elsewhere, and how to guarantee that it will be there on all the target machines. I could translate the opcodes a dozen times, by hand, in the time it will take to resolve that problem!
Is it accurate to assume that Prop1 and Prop2 are among your last big hurdles?
Is it accurate to assume that Prop1 and Prop2 are among your last big hurdles?
I'm not exactly sure what the question means. I like the Parallax and the Propeller and the folks on the forums here -- definitely a cut above what I encountered over in Arduino-land -- and I'm simply trying to see if there's a way to mate up Plain English with the Propeller. Hosting Plain English on the Propeller is one possible avenue; making up a special Plain English/Propeller kit for kids who have Windows on their laptop or desktop is another; etc.
Below is my half finished code for Pacman. It is written in Spin, and yes, it is a bit slower than it should be. There are a whole lot of video drivers behind the scenes, but the core of the Spin code, the part that works out the logic of how the ghosts move, is not that many lines.
That's impressive. Is each character assigned to a separate COG?
Okay, the quickest way to make that happen would be put you on a Windows computer with a version of Plain English that both:
(a) generates Spin source code, and
(b) has a Noodle full of Plain English headers, each followed by the Spin source code that calls the various routines (methods) in all those helpful libraries (objects) you mention.
The resulting intermediate file would then be passed to the usual Spin compiler and that would create the byte-code that gets passed to the machine as usual.
The question is whether such a system is a significant improvement over what we've got now.
PRO: You'll be able to program in English. You won't have to deal with Spin's unique and artificial syntax, and won't have to remember the exact names and parameters of the routines you want to call (since we can put enough variants in that Noodle so whatever you're naturally inclined to type in will be recognized). You won't have to worry about proper indention and whitespace any more. And you'll save time by not having to comment your code!
CON: We'll be adding both more machinery and an extra step to get the job done. We'll now have two compilers (a new one for Plain English, and the old one for Spin) where we used to have just one (the Spin compiler). It would obviously be better if we went directly from Plain English to Spin byte-code. But it's a start, and we can always consider the more direct route later.
So if you're willing, let's investigate this idea a little further and see where it goes. Questions:
1. Why would you "love to recode" your Pacman in Plain English?" Exactly what attracts you to the idea, and/or what is it about your current workflow you hope to eliminate?
2. Do you have a Windows machine?
3. Can you give me a sample of more-or-less stand-alone Spin code (bigger than "Hello, World!" but smaller than Pacman) so I can (a) consider what it would look like in Plain English, and (b) figure out how to get from that Plain English back to the Spin code we need to hand the Spin compiler?
Thanks for your time and interest, Dr A. And the rest of you fine folks, as well.
That's impressive. Is each character assigned to a separate COG?
You could do that. Right now, it is more that one cog is handling the SD card, one is handling low level video drivers, one is working at a higher level doing tile moving. And I think there is one left over that was for serial debugging messages. One is needed for the spin interpreter.
The propeller is very flexible. I tend to write everything in one cog to start with, which is slow but works, then think about which processes can be farmed off to other cogs and run in parallel.
There are also techniques to stop cogs and reload them with new code on the fly. Move code fragments from an SD card into a cog, run for a while, load in new code. Means you can have a virtual number of many cogs rather than just 8.
We are starting to think here in parallel code. And that leads to a question about Plain English - can the IDE also handle parallel code?
I'm sure it can. The Spin IDE allows you to have lots of tabs open with code in each, so you can have multiple objects open at once and flick quickly between editing each one.
Why would you "love to recode" your Pacman in Plain English?"
Good question, and I'm glad you asked!
Two reasons. First is that flipping between coding in C, Basic, Spin, I get confused with the slightly different syntax for logic operations. I constantly put commas instead of semicolons in 'for' loops etc. Having a common syntax across platforms would be handy.
Second, it would be fantastic to be able to run the same program on multiple platforms. Taking that pacman program for instance, the back end video drivers will be different for each platform, but the main program would be the same, and it would be so much faster with the debug/rewrite/recompile to do this on a windows PC
Yes, I have a Windows machine. Actually, there are five in the house, and another four in the Man Cave. You are going to ask me to try out Plain English on one of them, aren't you? *grin*.
Re an example, I'll rummage around in the obex and see what is there.
Addit, see below. There are lots of things in the obex that combine Spin and Assembly, but I thought it best to start with something that is just Spin. This is one of the 'official' drivers, ie one written by someone in Parallax.
There are some good examples there of the Spin syntax using things like & |> >> := ~~
All of which would be perfect for changing to Plain English.
''*******************************************************************
''* Simple Asynchronous Serial Driver v1.4 *
''* Authors: Chip Gracey, Phil Pilgrim, Jon Williams, Jeff Martin *
''* Copyright (c) 2006 Parallax, Inc. *
''* See end of file for terms of use. *
''*******************************************************************
''
'' Performs asynchronous serial input/output at low baud rates (~19.2K or lower) using high-level code
'' in a blocking fashion (ie: single-cog (serial-process) rather than multi-cog (parallel-process)).
''
'' To perform asynchronous serial communication as a parallel process, use the FullDuplexSerial object instead.
''
''
'' v1.4 - Feb 3, 2014 Fixed Init method to initialize txPin (if specified and not bi-directional communication).
'' Previous versions did not initialize txPin until transmission.
'' v1.3 - May 7, 2009 - Updated by Jeff Martin to fix rx method bug, noted by Mike Green and others, where uninitialized
'' variable would mangle received byte.
'' v1.2 - Mar 26, 2008 - Updated by Jeff Martin to conform to Propeller object initialization standards and compress by 11 longs.
'' v1.1 - Apr 29, 2006 - Updated by Jon Williams for consistency.
''
''
'' The init method MUST be called before the first use of this object.
'' Optionally call finalize after final use to release transmit pin.
''
'' Tested to 19.2 kbaud with clkfreq of 80 MHz (5 MHz crystal, 16x PLL)
VAR
long sin, sout, inverted, bitTime, rxOkay, txOkay
PUB init(rxPin, txPin, baud): Okay
{{Call this method before first use of object to initialize pins and baud rate.
• For true mode (start bit = 0), use positive baud value. Ex: serial.init(0, 1, 9600)
For inverted mode (start bit = 1), use negative baud value. Ex: serial.init(0, 1, -9600)
• Specify -1 for "unused" rxPin or txPin if only one-way communication desired.
• Specify same value for rxPin and txPin for bi-directional communication on that pin and connect a pull-up/pull-down resistor
to that pin (depending on true/inverted mode) since pin will set it to hi-z (input) at the end of transmission to avoid
electrical conflicts. See "Same-Pin (Bi-Directional)" examples, below.
EXAMPLES:
Standard Two-Pin Bi-Directional True/Inverted Modes Standard One-Pin Uni-Directional True/Inverted Mode
Ex: serial.init(0, 1, ±9600) Ex: serial.init(0, -1, ±9600) -or- serial.init(-1, 0, ±9600)
┌────────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐
│Propeller P0├─────────────┤I/O Device│ │Propeller P0├───────────────┤I/O Device│
│ P1├─────────────┤ │ └────────────┘ └──────────┘
└────────────┘ └──────────┘
Same-Pin (Bi-Directional) True Mode Same-Pin (Bi-Directional) Inverted Mode
Ex: serial.init(0, 0, 9600) Ex: serial.init(0, 0, -9600)
┌────────────┐ ┌──────────┐
│ │Propeller P0├─────┳─────┤I/O Device│
4.7 kΩ └────────────┘ │ └──────────┘
┌────────────┐ │ ┌──────────┐ 4.7 kΩ
│Propeller P0├─────┻─────┤I/O Device│ │
└────────────┘ └──────────┘
}}
finalize ' clean-up if restart
rxOkay := rxPin > -1 ' receiving?
txOkay := txPin > -1 ' transmitting?
sin := rxPin & $1F ' set rx pin
sout := txPin & $1F ' set tx pin
inverted := baud < 0 ' set inverted flag
bitTime := clkfreq / ||baud ' calculate serial bit time
if txOkay and (sin <> sout) ' if txPin specified and not Bi-Directional communication; initialize sout pin
outa[sout] := !inverted ' set idle state
dira[sout]~~ ' make tx pin an output
return rxOkay | TxOkay
PUB finalize
{{Call this method after final use of object to release transmit pin.}}
if txOkay ' if tx enabled
dira[sout]~ ' float tx pin
rxOkay := txOkay := false
PUB rx: rxByte | t
{{ Receive a byte; blocks caller until byte received. }}
if rxOkay
dira[sin]~ ' make rx pin an input
waitpeq(inverted & |< sin, |< sin, 0) ' wait for start bit
t := cnt + bitTime >> 1 ' sync + 1/2 bit
repeat 8
waitcnt(t += bitTime) ' wait for middle of bit
rxByte := ina[sin] << 7 | rxByte >> 1 ' sample bit
waitcnt(t + bitTime) ' allow for stop bit
rxByte := (rxByte ^ inverted) & $FF ' adjust for mode and strip off high bits
PUB tx(txByte) | t
{{ Transmit a byte; blocks caller until byte transmitted. }}
if txOkay
outa[sout] := !inverted ' set idle state
dira[sout]~~ ' make tx pin an output
txByte := ((txByte | $100) << 2) ^ inverted ' add stop bit, set mode
t := cnt ' sync
repeat 10 ' start + eight data bits + stop
waitcnt(t += bitTime) ' wait bit time
outa[sout] := (txByte >>= 1) & 1 ' output bit (true mode)
if sout == sin
dira[sout]~ ' release to pull-up/pull-down
PUB str(strAddr)
{{ Transmit z-string at strAddr; blocks caller until string transmitted. }}
if txOkay
repeat strsize(strAddr) ' for each character in string
tx(byte[strAddr++]) ' write the character
{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ TERMS OF USE: MIT License │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │
│files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │
│modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│
│is furnished to do so, subject to the following conditions: │
│ │
│The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.│
│ │
│THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │
│WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │
│COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │
│ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │
I'm not exactly sure what the question means. I like the Parallax and the Propeller and the folks on the forums here -- definitely a cut above what I encountered over in Arduino-land -- and I'm simply trying to see if there's a way to mate up Plain English with the Propeller. Hosting Plain English on the Propeller is one possible avenue; making up a special Plain English/Propeller kit for kids who have Windows on their laptop or desktop is another; etc.
I wondered if you were close to making "Plain English" a reality and that Parallax was one of the last few hurdles you faced.
Fortunately I see Dr. Acula has offered his services. I'll be following your progress with interest.
Incidently, has anyone else discovered that it's often easier to find something on a particular site by using Google rather than that site's own set of menus and/or search mechanism? Happens to me all the time, say, with the Windows API reference -- and here, where Googling "parallax propeller datasheet" took me directly to the PDF.
As one of the younger folks around here, I truly have no idea how any of you functioned before Google. I can only imagine everyone standing on their rooftops yelling "WHOEVER HAS THE CAT PICTURES BOOK, CAN I BORROW IT FOR AN HOUR!?"
As one of the older folks here, I can tell you it wasn't easy. Mostly, we had lower expectations.
Overnight parts shipment? How about 4 to 6 weeks delivery.
Well Gerry, I'm am very intrigued and impressed with what you have done with "Plain English" and your patience with this forum too. I don't expect PE to compile on a P1 or P2 but having it compile something that the Prop can run is what is required. I run a fast VM using bytecodes except unlike Spin which needs to decode all this bytecodes I simply use them as an address directly into the first 256 instructions in the VM cog. Being stack based (Forth is built on top of it) it is zero operand and fast. Given that there are a handful of actual bytecode instructions that Tachyon uses could this be a basis for generating code?
Here is an example of some Tachyon code is a simple VGA breakout game I wrote, it takes just over 1k bytes of code and it's fast. Of course the Tachyon bytecode supports Forth but it is simply an efficient and sensible way to get the most out of P1. I also have Tachyon running on P2 including FAT32 and networking (same as P1).
BTW, Tachyon source code is compiled in Tachyon running on the Prop, so it's interactive too.
pub BREAKOUT.fth PRINT" BREAKOUT GAME using 32x15 VGA text 150625.1400 " ;
{ BREAKOUT for 32x15 VGA text
Use VGA Explorer binary then load this source into it by copy and pasting through the terminal
Use Teraterm or minicom or some terminal that supports pasting and 10ms line delay.
NAMES: $6ABF...715F for 1,696 bytes (+513)
CODE: $0924...681A for 24,310 bytes (+1,193)
}
IFDEF HZ
10 == audio --- square wave output audio
TIMER duration
--- init sound
pri !SOUND
audio APIN
MUTE
0 duration TIMEOUT --- setup duration timer
;
pri ?SOUND
duration TIMEOUT? IF MUTE THEN
;
--- hitting a wall sound
pub BONK
audio APIN
300 HZ 50 duration TIMEOUT
;
}
IFNDEF HZ --- allow for compact build - skip sound
pri BONK ;
pri !SOUND ;
pri ?SOUND ;
}
--- palette to suit game
TABLE gamepal
--- RRGGBB RRGGBB
%100000 | %000000 | --- red on black
%001000 | %000000 | --- green on black
%000010 | %000000 | --- blue
%111100 | %000000 | --- yel
%100010 | %000000 | --- mag
%001010 | %000000 | --- cyan
%010101 | %000000 | --- gry
%101010 | %000000 | --- white
0 == red
1 == grn
2 == blu
3 == yel
4 == mag
5 == cyan
6 == gry
7 == wht
3 == backwall --- back of the brick wall - leave room for the ball to bounce behind
$7F00 == bram
$03FC == bval
BYTE score,balls
pri BRICKS? ( -- cnt )
score C~
backwall cols * 2* screen + cols 4 * 2*
ADO I C@ 14 <> IF score C++ THEN 2 +LOOP
;
--- calculate how many bricks have been removed and display
pri .SCORE
BRICKS?
grn HUE cols 7 - 0 XY
VGA score C@ $30A PRINTNUM PRINT" /128"
<CR> balls C@ . PRINT" balls"
;
--- ball position variables
WORD bx,by,abx,aby
BYTE bdir
--- Set the ball direction
pub BDIR! ( n -- ) $0F AND bdir C! ;
--- initialize the brick wall with 4 rows of random colored bricks/tiles from backwall (gap)
pub WALL
0 backwall XY cols 4 * FOR RND 8 SHR 7 AND HUE 14 VCHAR NEXT
;
{ The ball is drawn with a single character but by using codes which force the driver to access RAM
for the font we can have finer movement by drawing a ball character dynamically in x 16x32 matrix
Use code $3FA to access table at $7E80
This routine is essentially the same as VCHAR but optimized for directly writing without scroll
Execution time: 48.8us
}
pub VCHAR! ( ch -- ) --- 10-bit character
DUP 1 AND color C@ 2* + --- (color << 1 + c & 1 )
2* 1+ 9 << --- form color field
SWAP 1 ANDN + --- merge 7 msb of data
row C@ 5 << col C@ + --- current screen position
DUP 960 <
IF
2* screen + W! --- write to the screen word
col C++ --- increment column position
ELSE
2DROP
THEN
;
TIMER balldly --- timeout leads to next ball movement
WORD speed
--- rebound translation table looks up the corresponding rebound direction ( 7 into left returns with 9 )
TABLE rebound
--- 0 1 2 3 4 5 6 7 8 9
0 | 7 | 8 | 9 | 6 | 5 | 4 | 1 | 2 | 3 | --- bottom/top rebound
0 | 3 | 8 | 1 | 6 | 5 | 4 | 9 | 2 | 7 | --- left/right rebound
--- bounce the ball back
pub BOUNCE ( table-offset -- )
rebound + --- lookup the table for the rebound action
bdir C@ + C@ BDIR!
BONK --- make a sound
;
--- read the contents of the screen cell/tile where the ball will be next
pri BALL@ ( -- word )
bx W@ 2/ 2/ by W@ 3 >> cols * + 2* screen + W@
;
pri NEWBALL
0 BDIR!
64 bx W! 60 by W!
50 speed W! 1 BDIR!
1500 balldly TIMEOUT --- delay serving 1st ball for 1.5secs
pub BALL
bx W@ cols 2* 2* MIN bx W! --- limit ball x
by W@ rows 2* 2* 2* MIN by W! --- limit ball y
BALL@ >B BL "z" WITHIN NOT IF 0 BOUNCE EXIT THEN --- bounce off special tiles (not blank or alphanumeric)
by W@ rows 3 << 1- => IF 0 BOUNCE EXIT THEN --- bounce off bottom
by W@ 1 < IF 0 BOUNCE EXIT THEN --- bounce off top
bx W@ 0= IF 10 BOUNCE EXIT THEN --- bounce off left wall
bx W@ 2/ 2/ cols => IF 10 BOUNCE EXIT THEN --- bounce off right wall
pub DRAWBALL ( -- ) --- plot ball as 4x4 block - 185us
wht HUE bx W@ by W@
OVER 2/ 2/ OVER 2/ 2/ 2/ XY --- address the screen position for the tile - 17.2us
bval VCHAR! --- maps to programmable char at $7E80 - 63.6us
SWAP 3 AND 2* 2* 2* $AA SWAP << --- generate a dot in the correct x position - 7.8us
bram 128 ERASE --- wipe the character clean - 64.6us
SWAP 7 AND 4 << bram + ( mask addr ) --- calculate y position in character - 8.4us
SWAP OVER ! DUP 4 + 12 CMOVE --- create the pattern
;
pub NOBALL
bx W@ 2/ 2/ by W@ 3 SHR XY
BL VCHAR!
;
--- setup constants for ball x and y movement increments which contain 2 fractional bits
1 == abxcon
1 == abycon
pri BALL+! ( x y -- )
NOBALL
by W+! bx W+!
BALL
;
--- Check if ball is ready for next movement and proceed
pub ?BALL
balldly TIMEOUT? 0EXIT --- ready yet?
speed W@ balldly TIMEOUT --- yes, set next timeout period
bdir C@ SWITCH --- proceed using the ball direction
7 CASE abxcon NEGATE abycon NEGATE BALL+! BREAK
9 CASE abxcon abycon NEGATE BALL+! BREAK
1 CASE abxcon NEGATE abycon BALL+! BREAK
3 CASE abxcon abycon BALL+! BREAK
;
BYTE px,py --- paddle xy
$8E88 == mypad --- two characters that make up the paddle
$2020 == nopad
pub PADDLE ( shape -- )
yel HUE
px C@ py C@ XY W>B VCHAR VCHAR --- draw the paddle
;
--- PADDLE MOVEMENTS
pub L px C@ IF nopad PADDLE px C-- mypad PADDLE THEN ;
pub R px C@ cols 2 - < IF nopad PADDLE px C++ mypad PADDLE THEN ;
--- check to see if the ball has hit the paddle and which change it's direction based on the edge hit
pub ?PADDLE
mypad PADDLE
;
pri NEWPADDLE rows 1- py C! 15 px C! mypad PADDLE ;
pub GAMEOVER red " GAME OVER" 11 8 TEXTBOX ;
pub NEWGAME
VGA CLS NEWPADDLE NEWBALL WALL
7 0 XY 2 HUE PRINT" TACHYON BREAKOUT "
3 balls C! score C~
;
--- game control keys ---
pub GAMEKEY
SWITCH
"Z" CASE L BREAK
"X" CASE R BREAK
"R" CASE REVERSE BREAK
"+" CASE speed W-- BREAK
"-" CASE speed W++ BREAK
"B" CASE NOBALL NEWBALL BREAK
$0D CASE NEWGAME BREAK
SWITCH@ "0" "9" WITHIN IF SWITCH@ BDIR! BREAK
$1B CASE GAMEOVER CON CONSOLE BREAK
;
pub SPLASH
VGA CLS
--- draw boxes within boxes
7 0 DO I I XY 7 I - HUE 30 I 2* - 15 I 2* - BOX 50 ms LOOP
7 7 XY VGA PRINT" TACHYON BREAKOUT"
1 second 8 FOR REVERSE 100 ms NEXT
10 FOR home cols rows * FOR RND 8 SHR DUP HUE 8 AND IF 14 ELSE BL THEN VCHAR NEXT 100 ms NEXT
;
pub ACTION
130 score C@ - 3 / speed W! --- make it speed up proportionaly to the score
KEY UPPER GAMEKEY
?BALL
?PADDLE
?SOUND
.SCORE
score C@ 128 = IF NEWGAME THEN --- testing: start a new game
;
pub BREAKOUT
!VGA !SOUND
gamepal setcolors
2 seconds SPLASH --- wait for VGA monitor to stabilize then SPLASH screen
NEWGAME --- start a new game
pub RESUME
30 FLOAT
VGA
BEGIN
ACTION
AGAIN
;
Gerry said "I like the Parallax and the Propeller and the folks on the forums here -- definitely a cut above what I encountered over in Arduino-land -- and I'm simply trying to see if there's a way to mate up Plain English with the Propeller."
Ouch. I do a bit of Arduino coding too but it is harder because their forum is not nearly as friendly so you are on your own.
Over here in Propeller land it is a bit different. Ask for help and someone will provide it. In fact, I suspect more people may chime in with their favourite language on the Propeller and why it could work translating Plain English to that language.
There was a thread a few years back where someone was tallying up all the languages that could run on the Propeller. It was sort of a zombie thread that wouldn't die as more languages kept being added. Especially when it got a bit silly with the Z80 being added (thanks to Heater et al) , then every single language ever written for CP/M now was included.
There are going to be many ways to solve this, and all of them are going to be good.
I wonder if we could think about some core modules that would be very helpful. VGA driver, Keyboard, Serial port to start with. It may not matter so much which Propeller language we use.
I wondered if you were close to making "Plain English" a reality and that Parallax was one of the last few hurdles you faced.
Plain English has been a reality, on Windows, for ten years. But it isn't very widely used, mainly because most "desktop" programmers these days are writing internet stuff. Nobody in the desktop world starts with a blank screen anymore. That's why, for example, a cash register at the dollar store has things like scroll bars and pull-of palettes on it! So I've been looking for a "niche" where Plain English might prove helpful.
Fortunately I see Dr. Acula has offered his services. I'll be following your progress with interest.
My wife of 30 years would say I'm "spiraling" here -- considering this, considering that, then making another loop around the circle, hopefully zeroing in on something that's both useful and feasible -- and she'd be right.
Plain English has been a reality, on Windows, for ten years. But it isn't very widely used, mainly because...
I think there are many "becauses"...
Programmers are a very emotional bunch. Despite what they think about how rational and logical they are. They get very attached to the languages and tools they use. They will have very heated arguments as to why what they like is best and anyone who does not agree is "obviously" not thinking clearly.
The classic example of this is in "language wars". A lot of that is about syntax. Do we like block scope and curly brackets? Or do we like blocks delimited by indentation. Do we like "{..}" or "begin..end"? Do we like infix or postfix notation for expressions? Do we like "wordy" languages like COBOL, Plain English etc or do we like succinct mathematical notation? And so on, ad nauseum.
Then there are more semantically oriented arguments. The "goto" is obviously evil to some but vigorously defended as essential by others. The "goto" argument went on for decades before we started to get languages that don't have it, Java, Javascript, ect.
Standing back and looking at it there is nothing rational about these arguments. They are very emotive. After all, common programming languages basically all do the same thing:
sequence
selection
iteration
Rationally they are all equivalent. The heated arguments are born largely from emotional attachments to the ways the opponents do things.
Having a closed source Windows only system is not the way to attract developers now a days.
What do do?
Let's get back to your original post where you gave a nice example:
The red light is on pin 9.
To run:
Turn on the red light .
Wait for 1 second.
Turn the red light off.
Wait for 1 second.
Repeat.
Surely the course is clear. The Plain English system, running on Windows, has to be able to generate code for the Propeller, or whatever target you decide on. That is only a hand full of instructions and can be easily done in Plain English as far as I can see. That code can be native instructions or LMM or whatever, to be decided.
The Plain English system also needs to be able to download that code to a Propeller so that we get to see our LED flashing. The down load protocol is simple and there are many example programs to borrow from.
To get wide adoption, the closed source and single platform nature of Plain English needs to be addressed. The abstractions I mentioned above would help with that. Then others could contribute code generators or display subsystems for whatever machines/environments they like.
There needs to be a way for people to build a Plain English system from source. Nobody likes to rely on a binary executable download. That means creating a compiler for Plain English (or perhaps a required small subset) in some commonly used language, C, Python, Javascript, whatever so that people can bootstrap their own Plain English system.
I run a fast VM using bytecodes except unlike Spin which needs to decode all this bytecodes I simply use them as an address directly into the first 256 instructions in the VM cog. Being stack based (Forth is built on top of it) it is zero operand and fast.
Brilliant!
Aside:
I've always been attracted to FORTH. (Even spoke with Charles Moore some years ago about developing some hardware for me. Alas, he was busy trying to write a full-fledged browser in FORTH at the time.) So when my son and I started our Plain English project, we called it FIFTH. The plan was to create an easier-to-read 32-bit FORTH. So we started with code you'd recognize, then moved on stuff like this (to decide if a point is in a rectangle):
IN? ( POINT RECT > BOOL )
OVER OVER LEFT? OVER OVER ABOVE? OVER OVER RIGHT? BELOW? OR OR OR NOT
END
The problem was that every time we'd come back to a routine like that after leaving it a while, we'd find ourselves more-or-less re-inventing it in our heads as we tried to figure out what we had written! So one day I asked my son, "What are you really trying to say in this routine?" And as he replied I typed his very words:
To decided if a point is in a rect:
If the point is left of the rect, say no.
If the point is right of the rect, say no.
If the point is above the rect, say no.
If the point is below the rect, say no.
Say yes.
Then I said, "Let's compile that," and Plain English was born.
But what's good for people isn't necessarily good for machines. So I hasten to add that I like the idea of targeting the VM you've described. I like it very much.
Given that there are a handful of actual bytecode instructions that Tachyon uses could this be a basis for generating code?
No doubt. Once our compiler has digested the source code it does very little except generate instructions to (a) push parameters, (b) make a call to a routine, and (c) pop parameters. It's very FORTH-like in spirit on the computer's end. The essential primitive routines, of course, actually do things in step (b) like compare, or add, or shift. But these, too, are very FORTH-like in spirit since they are typically only a few instructions long.
Here is an example of some Tachyon code is a simple VGA breakout game I wrote, it takes just over 1k bytes of code and it's fast.
I hope you don't really expect me to read postfix code! For comparison purposes, here's a mini PONG game that I coded up for my younger son in Plain English. See if you can read that:
To play pong in a box:
Use the green pen.
Put -1 pixel into a vertical change number.
Put -2 pixels into a horizontal change number.
Put the box's center into a current spot.
Loop.
Erase inside the box.
Draw the pong paddle.
Draw the pong ball.
Refresh the screen.
Add the horizontal change and the vertical change to the current spot.
If the current spot is above or below the box, reverse the vertical change.
If the current spot is left or right of the box, reverse the horizontal change.
If the current spot is within 1/2 inch of the pong paddle, beep; reverse the horizontal change.
Repeat.
To draw the pong ball:
Draw a circle 1/2 inch wide.
The pong paddle is a box.
To draw the pong paddle:
Make the pong paddle 1/4 inch by 1-1/2 inch.
Put the work area's right minus 1 inch and the mouse's spot's y into a spot.
Center the pong paddle on the spot.
Draw the pong paddle.
Of course the Tachyon bytecode supports Forth but it is simply an efficient and sensible way to get the most out of P1. I also have Tachyon running on P2 including FAT32 and networking (same as P1).
Now we just have to see if Dr. Acula agrees that Tachyon byte-code is a better target than Spin source code!
I would choose byte-code over Source code, as it is
a) Closer to what you do now
b) Is more predictable, and faster to test
c) Knowing what byte codes Spin will spawn, is not easy at best, and Spin is locked in ROM in P1, so it cannot be easily tuned.
Peter's Tachyon byte-code is highly tuned already, but could be nudged to better suit your code generator.
I tend to write everything in one cog to start with, which is slow but works, then think about which processes can be farmed off to other cogs and run in parallel.
I would probably do the same. Which seems (with our huge sampling of two people) to indicate that it's difficult or "unnatural" to think directly about parallel processes, but that it's easy to see what can be separated and delegated once the big picture is in view. Interesting. I say this because we did a lot of this kind of "stepping back to see what's naturally easy and hard for people" when we were developing Plain English. User friendly, of course, is mainly what the user is used to. But it seems that when we can get "behind" our habits we discover things that are easy for everyone.
There are also techniques to stop cogs and reload them with new code on the fly. Move code fragments from an SD card into a cog, run for a while, load in new code. Means you can have a virtual number of many cogs rather than just 8.
Yes, I see that. But you're still limited to eight at a time.
Recall my remark above. It's my view that any kind of thinking that has to be learned is less desirable than the kinds of thinking that come naturally to us. I've spent a lot of decades educating children and have found that leading them in natural paths gets the student much farther, much faster. And is much easier on the teacher! Note that that doesn't mean that parallel processing is a bad thing -- just that it needs to be approached in the most natural manner possible. The "big picture followed by separation and delegation" approach that I mentioned above seems, at this point, the most natural to me. But I'm a beginner in this field.
And that leads to a question about Plain English - can the IDE also handle parallel code?
Sure. When you say "Run" in our IDE we start up your program in another "thread" which is just a virtual COG implemented with interrupts (the IDE is still running in the original thread and can also be accessed). So, yes, no problem.
The Spin IDE allows you to have lots of tabs open with code in each, so you can have multiple objects open at once and flick quickly between editing each one.
Our IDE is similar -- it has ten tabs at the bottom so you can have ten files open at once.
I'm glad you asked [why I would love to re-code in Plain English]
Two reasons. First is that flipping between coding in C, Basic, Spin, I get confused with the slightly different syntax for logic operations. I constantly put commas instead of semicolons in 'for' loops etc. Having a common syntax across platforms would be handy.
Second, it would be fantastic to be able to run the same program on multiple platforms. Taking that pacman program for instance, the back end video drivers will be different for each platform, but the main program would be the same, and it would be so much faster with the debug/rewrite/recompile to do this on a windows PC
Okay. But both of those objectives can only be met with a lot of different Plain English compilers (or at least a lot of different "back ends"), one for each platform. Lots of work there.
Yes, I have a Windows machine. Actually, there are five in the house, and another four in the Man Cave. You are going to ask me to try out Plain English on one of them, aren't you? *grin*.
You haven't already? Seriously: its best to print off the first 54 pages of the manual and to follow the instructions exactly as they're written. Programming in English is different than programming in other languages, and our IDE is quite different as well. But both work conveniently and efficiently once your "unlearn" all the bad habits you've developed over the years!
There are lots of things in the obex that combine Spin and Assembly, but I thought it best to start with something that is just Spin. This is one of the 'official' drivers, ie one written by someone in Parallax. There are some good examples there of the Spin syntax using things like & |> >> := ~~ All of which would be perfect for changing to Plain English.
That's a little more than I had in mind. And I'm not sure you guys are going to like this. Let me take on just the first routine:
PUB init(rxPin, txPin, baud): Okay
finalize ' clean-up if restart
rxOkay := rxPin > -1 ' receiving?
txOkay := txPin > -1 ' transmitting?
sin := rxPin & $1F ' set rx pin
sout := txPin & $1F ' set tx pin
inverted := baud < 0 ' set inverted flag
bitTime := clkfreq / ||baud ' calculate serial bit time
if txOkay and (sin <> sout) ' if txPin specified and not Bi-Directional communication; initialize sout pin
outa[sout] := !inverted ' set idle state
dira[sout]~~ ' make tx pin an output
return rxOkay | TxOkay
The first statement in this routine translates directly: "finalize" simply becomes "Finalize."
But then we run into some philosophical differences in programming style. Allow me to elaborate. Two goals of the kind of programming we see above are compactness and efficiency, while the overriding goal of Plain English programming is naturalness and clarity of expression. Now, do you see the comment above that says "receiving?" The fact that comment exists means the programmer felt the need to explain what the statement "rxOkay := rxPin > -1" was meant to determine; and the fact that the comment ends with a question mark and the resulting value is being stored in a boolean variable suggests that the question may come up again. So in Plain English we'd convert that thought -- that statement and its associated comment -- into a "decider" routine so the question could be naturally asked, both here and elsewhere. This is the code we'd write in lieu of the rxOkay variable:
To decide if we're receiving:
If the receive pin is valid, say yes.
Say no.
Elsewhere, then, instead of checking the value of rxOkay, we would say things starting with:
If we're receiving...
We'll take a teensy performance hit for this, of course, because of the call; but it probably won't be a problem because I suspect this question is not asked all that often.
We'd then do a similar thing with the txOkay flag. And the inverted flag. And to answer the questions, "Are we transmitting bi-directionally?" and (as the return value of this function is intended to indicate), "Are we receiving or transmitting at all?"
Now I'm not exactly clear what "sin" and "sout" are -- serial input and serial output somethings, I suppose, but what? And why are they set to the corresponding pins bitwise-anded with $1F? I'm going to call them "ports" and create a routine to initialize a port given a pin number, like so:
To initialize a serial port given a pin number:
Put the pin number into the serial port.
Bitwise and the serial port with $1F.
It strikes me that this routine may be more appropriately placed in a library of useful general-purpose routines. And I would imagine there would be a similar routine to get the bit time given a baud rate, like this:
To get a bit time given a baud rate:
Put the clock frequency divided by the baud rate into the bit time.
Now with a couple of similar helper routines, the initialization routine would now be something like this:
Finalize.
Initialize the input port given the receive pin.
Initialize the output port given the transmit pin.
If we're not transmitting uni-directionally, exit.
Set the idle state.
Make the output port an output only port.
Or something along those lines. I'm going to stop there. I'm interested to see your reactions to this kind of coding; I'm sure it's different from what you're used to.
What do do? Surely the course is clear. The Plain English system, running on Windows, has to be able to generate code for the Propeller, or whatever target you decide on. That is only a hand full of instructions and can be easily done in Plain English as far as I can see. That code can be native instructions or LMM or whatever, to be decided.
Okay, that's being discussed right now. One proposal has Spin source code as the target, another has Tachyon byte-code as the target. Got any thoughts on those two?
The Plain English system also needs to be able to download that code to a Propeller so that we get to see our LED flashing.
Yes, that's a given. But if we generate Spin source code that task will be done in the usual way with the Spin compiler (and, I assume, IDE). If we go Tachyon we'll have to include it in our IDE.
Nobody likes to rely on a binary executable download.
Everybody relies on binary executable downloads. How can you compile GCC from source if you don't already have a binary version of a compatible C compiler? So why do you think it odd that to compile Plain English from source you need a binary version of a compatible Plain English compiler?
That means creating a compiler for Plain English (or perhaps a required small subset) in some commonly used language, C, Python, Javascript, whatever so that people can bootstrap their own Plain English system.
A small subset is all you'll get, and a small subset of Plain English isn't Plain English. Plain English is not just a language; it's a whole philosophy of programming embodied in a special, peculiar, and unique program. See my post above where I convert some of Dr. Acula's serial port code to Plain English -- it's not the kind of thing that can be done a line at a time; and the result is something quite different from the original.
Wow, I just found the very long forum thread over at anandtech.com about Plain English. http://forums.anandtech.com/showthread.php?t=2358744 That swamp of mudslinging and abuse that ended up being locked pretty well demonstrates what I was saying above.
A rowdy bunch over there indeed. And such unbiased and empathetic moderation! I persisted for the sake of the non-posters and for the handful who voted positively in the poll at the top. Did you notice the view count? Almost 38,000 views, last I checked. People may hate Plain English, but the sure love to hate it!
But it's not just programmers. My threads on a variety of electronics and physics forums have also been locked for not sticking with the "party line." See, for example, here:
Do not use Spin as the target. See my previous post above.
At least missing out a Spin compilation step is in line with your "keep it simple" philosophy. Less moving parts.
You don't need the Spin compiler or Spin IDE to get a loader for the Propeller. There are many loaders available. In Python or C or whatever. The loader is actually a very simple thing than can be done in 100 lines of code or so. I imagine it can be written in Plain English.
Yes there are instructions on building Plain English. However that is no good on my Linux systems.
Everybody relies on binary executable downloads. How can you compile GCC from source if you don't already have a binary version of a compatible C compiler?
A very good question. And one that bothers me from time to time.
The situation with a standardized language like C is somewhat different:
a) The language is very rigorously defined and standardized.
b) There are many compiler implementations available.
c) There is a question of trust when it comes to down loading random binaries from unknown people off the net.
Ideally all this means that given only the source code of GCC I can get it compiled without already having a working GCC. Even if that means writing my own C compiler according to the spec. from scratch.
In practice getting a compilation system like GCC compiled and working without an existing GCC compilation system is pretty hard. The bootstrapping thing.
Which leaves me with c) I trust Debian folks with binaries they give me.
That's the bit that bothers me. If it takes 100's guys decades to build a half decent C++ compiler, which it has, and if that C++ compiler is itself written in C++, and if by some accident we lost all the binary executables of C++ compilers, how on Earth are we going to bootstrap ourselves back up again in any reasonable time?
So why do you think it odd that to compile Plain English from source you need a binary version of a compatible Plain English compiler?
The expectation is that there is a Plain English compiler written in C or Python or whatever such that almost anyone anywhere can compile Plain English on whatever machine/OS they have. Then they can use that C/Python to compile the Plain English compiler source thus producing their own Plain English compiler from the given Plain English source.
At one point you described an early bootstrapping of the Plain English compiler using a Pascal-like existing compiler. This is what's missing in terms of documentation to support the bootstrapping of Plain English to other systems. What's a minimum self-compiling version of the Plain English compiler with the output being some kind of mostly universal source code? This could be done using C macros that implement a very simple stack machine and assume some kind of minimal abstract API that can be functionally translated into a variety of API calls for common OSs. Advantages: Can make use of existing open-source optimizing C compilers. Long term, such optimizers could be transliterated into Plain English. Disadvantages: You need the operating systems and run-time libraries used by the optimizing C (or whatever) compiler until such time you can re-implement them.
As one of the younger folks around here, I truly have no idea how any of you functioned before Google. I can only imagine everyone standing on their rooftops yelling "WHOEVER HAS THE CAT PICTURES BOOK, CAN I BORROW IT FOR AN HOUR!?"
Or spending hours and hours trying to find books at the library in the card catalog. The cards were indexed only with the ISBN subject information from the book (a separate card for each subject).
This meant that if you were looking for a specific electronic circuit, you had to actually find and read each book filed under "Electronic Circuits".
Full text searches? If only. PDF's? Science fiction; computers for the most part could only display monospaced text.
And as these were just paper cards, lazy people would steal the card instead of copying the book's location. Good luck finding the book after that.
And all of this assumed that the book was actually on the correct shelf and not checked out. And that someone hadn't cut out the pages needed from the book.
Looking back, your method might actually have been more effective!
On the other hand, life was good. If you were young and super interested in electronics or computing or physics or whatever you probably managed to get into university or polytechnic. The government would pay your tuition fees and board and lodging for 3 or 4 years. None of this getting up to you neck in debt before you even think about starting a paying job. Then searching was much easier, you were surrounded by lots of smart people with similar interests who knew lots of stuff. Just ask around.
Sometimes it frightens me that I can no longer function without Google.
It seems to me the "ideal" implementation of Plain English for you would be:
1. a small command-line compiler
2. written entirely in C
3. that takes in Plain English source
4. and generates equivalent C as output
Such a program would compile and run on any computer with a C compiler. And the programs produced by it would also compile and run on any computer with a C compiler. And the original program, and all of its offspring, could be posted on GitBit or HubBucket (or whatever they're called) for further development by open-source crowd coders. Dream come true, yes?
So let's, you and I, make it happen. Here's how:
1. Put a copy of our latest Compiler and Noodle source files on your Linux machine.
2. Open them up in your favorite editor and comment out all the lines.
3. Uncomment, line-by-line, and translate just the parts we need into C.
Voila! I'll show you where to start and answer any questions you have as you go along.
I estimate that 80% of the compiler, but only 10% of the Noodle, will need to be converted: about 5,000 lines in all. So if we can translate, say, 50 lines a day, we'll be done in 100 days and Plain English programming will be within the reach of everyone everywhere. Woohoo!
There's a much simpler way to get a C version of the Plain English compiler. It can already compile itself; so you don't need to translate the whole compiler to C. Just change it to produce C output instead of Intel opcodes and then run it on itself. Voila -- you'll have a C version. It may not be idiomatic C, but it will serve to bootstrap new platforms if necessary.
At one point you described an early bootstrapping of the Plain English compiler using a Pascal-like existing compiler. This is what's missing in terms of documentation to support the bootstrapping of Plain English to other systems.
It's missing because the CAL-1000, the program we "bootstrapped" as described above, was not a fully functional compiler; it was mainly a very primitive text editor with just enough compiler to recompile itself. But it was enough so we could continue development exclusively in Plain English. So there never was a fully functional Plain English compiler written in another language.
Comments
If your app, the Plain English system in this case, wants to draw a line in should use and rely on an API to do that "draw (startPoint, endPoint)" or whatever. The simpler the API to get the job done the better. The layer implementing that abstraction may well be implemented with a Windows API in your system. But at least the abstraction boundary makes it possible for someone like me to come along and implement an alternative that uses whatever I have available on my machine/OS.
Similarly, when compiling Plain English to code an abstract intermediate representation of the code would allow anyone to easily generate real code from that intermediate representation for whatever machine they happened to have.
I don't think such abstraction layers make the code much bigger or harder to understand than what you have already. In fact I would claim that such layering and separation of concerns makes things easier to comprehend.
I think my point is that you don't have to "guarantee that it will be there on all the target machines." You only have to provide the low level implementation that maps the abstractions to Intel x86 and Windows. We out here could provide implementations for whatever we have. If people are interested they will do it and contribute to the project. Which is why you need github by the way.
This has the magic effect of freeing you to develop the actual language/IDE system rather than spend time supporting this or that platforms.
Here's an example of the "portability" problem so you can see why the opcodes don't seem like the critical path to me.
A do-nothing Plain English program looks like this:
To Run:
Start up.
Shut down.
Now that standard "Start up" routine is in the Noodle and looks like this:
To start up:
Initialize com.
Initialize winsock.
Initialize gdi+.
Initialize the talker.
Initialize the module.
Initialize the colors.
Initialize the screen.
Initialize the window.
Initialize the fonts.
Initialize the cursors.
Initialize the mouse.
Initialize the canvases.
Create the default console.
And each of those things that we initialize turn out to be quite different in each and every operating system. Sometimes they're implemented with an alternate architecture in mind; sometimes they're only partly there; sometimes they're not there at all. But they're hardly ever trivial. For example, here's what we have to do to just get a blank freaking screen to work on in Windows:
To initialize the window:
Put a window class's magnitude into the window class' cbsize.
Put 40 [cs_owndc + cs_dblclks] into the window class' style.
Point the window class' lpfnwndproc to default window proc.
Put the module's handle into the window class' hinstance.
Put the module's name's first into the window class' lpszclassname.
Call "user32.dll" "RegisterClassExA" with the window class's whereabouts.
Call "user32.dll" "CreateWindowExA" with 0 and the module's name's first and the module's name's first and -2147483648 [ws_popup]
and 0 and 0 and the screen's pixel width and the screen's pixel height and 0 and 0 and the module's handle and 0.
Call "user32.dll" "ShowWindow" with the main window and 1 [sw_shownormal].
All of that has to be rewritten to get the same effect in, say Linux with KDE. And written again to accomplish the same thing in Linux with Gnome. And written altogether differently to get a blank screen on the P1 or P2.
See what I'm saying? The major hurdle to porting occurs at a much higher level than machine code.
Step 1 would be a terminal/command line interface.
What you describe is more along the lines of full self hosting, and yes, that is always more work.
Here again you are talking immediate self-hosting.
Sure, any present PC Compiler will be lazy, because it can be.
- but you can do useful work with a cross-compiler, which is how all Microcontroller development is done now.
If you then find you can tune your compiler to self-host, in 512k, that is great.
If that takes longer, that is not drop-dead.
I'll declare a bias here. I don't particularly like Spin. I don't like the way the indenting is part of the language, and how whitespace is an important part of the code. I am never fully confident when I copy and paste code between various programs that the whitespace will get copied faithfully and the code will run as expected. I also don't like the heavy dependence on ~!@#$%^&*()<> characters.
So as I non-fanboi of Spin, I'm kind of hoping that might lend a little more weight when I suggest that Spin is the best initial target language for Plain English.
C as a target language is another option. The big advantage of C is that there are external memory cache drivers which mean you can start knowing that you have megabytes of ram available with a SPI chip that is only using 4 propeller pins.
But the big advantage of Spin is that most of the Obex object exchange is written in Spin. There are years and years of work in that Obex. It makes things so much easier thinking of a project as grabbing a video object, a keyboard object, a serial port object, a mouse object and a SD card object and gluing them together with a few lines of Spin. As an example of using other people's work to speed up a project, I once wrote a movie player in Spin using just two lines of code
But there are only a handful of those. The bulk of a program's vocabulary (and grammar, for that matter) is inherent in the TYPES, GLOBALS, and ROUTINE HEADERS that the programmer has coded. And those are indexed using hash tables (with linked lists for overflow) so we can the find the things we need quickly.
As someone who has written a compiler (as have quite a number of people on this forum and this thread, many of whom may well undersell their considerable talents), this sort of information is very helpful.
Let's take a "Hello World". The actual text is the rather trivial bit. The complex part is where it actually gets printed. If to an actual printer, the hard work (in the olden days) was toggling centronics pins to send out the bytes. Nowadays, the hard part is the arcane world of printer USB drivers. If printing to a dumb terminal, the hard work is the code of a bitbanging serial driver. If printing that on a VGA monitor, the hard work is the video driver. For printing on a touchscreen, it is the font drivers and getting the pixels updated quickly. If it is a TV monitor, again the video driver.
Printing "Hello World" on one particular device is interesting. But printing it on multiple devices with a few line changes in the code is the clever part. On the Propeller (and the Arduino) it is a matter of including objects easily to do these things.
There is some Obex code to do this in various versions of Basic. Ditto C. But the most complete library is in Spin.
Below is my half finished code for Pacman. It is written in Spin, and yes, it is a bit slower than it should be. There are a whole lot of video drivers behind the scenes, but the core of the Spin code, the part that works out the logic of how the ghosts move, is not that many lines.
I'd love to recode this in Plain English
To draw a line with a color:
Create the hpen of the current canvas given the color.
Call "gdi32.dll" "MoveToEx" with the current canvas and the line's start's x and the line's start's y and nil.
Call "gdi32.dll" "LineTo" with the current canvas and the line's end's x and the line's end's y.
Convert the color to a colorref.
Call "gdi32.dll" "SetPixelV" with the current canvas and the line's end's x and the line's end's y and the colorref.
Destroy the hpen of the current canvas.
The first line is the exposed part of the API and defines how the routine is called by any and all Plain English programmers. The rest is an implementation suitable for Windows; that's the part that would be different in a Linux/KDE Noodle or a Linux/Gnome Noodle or a P1 or P2 Noodle.
The compiler, I admit, is handled a little differently: in this case, the compiler itself is the abstraction layer and the only exposed routine in the compile's API is "compile a directory". We could easily move every compiler routine with a hex literal in it into the Noodle and then all the platform specific stuff would be in one place. We kept this stuff in the compiler simply because it was more convenient for us while we were coding, and more direct for the student who wants to see how the thing works. It also saved us some parameter passing since we could count on the compiler's global variables being there; the overall abort flag, for example: if we moved all those routines to an "independent" library we'd have to pass a boolean (and possibly an error message) back from each routine. But again, there's not that much of it in the compiler; where exact analogs of the machine instructions exist, it's a mere day's work (at most) to make the necessary changes.
Okay, the quickest way to make that happen would be put you on a Windows computer with a version of Plain English that both:
(a) generates Spin source code, and
(b) has a Noodle full of Plain English headers, each followed by the Spin source code that calls the various routines (methods) in all those helpful libraries (objects) you mention.
The resulting intermediate file would then be passed to the usual Spin compiler and that would create the byte-code that gets passed to the machine as usual.
The question is whether such a system is a significant improvement over what we've got now.
PRO: You'll be able to program in English. You won't have to deal with Spin's unique and artificial syntax, and won't have to remember the exact names and parameters of the routines you want to call (since we can put enough variants in that Noodle so whatever you're naturally inclined to type in will be recognized). You won't have to worry about proper indention and whitespace any more. And you'll save time by not having to comment your code!
CON: We'll be adding both more machinery and an extra step to get the job done. We'll now have two compilers (a new one for Plain English, and the old one for Spin) where we used to have just one (the Spin compiler). It would obviously be better if we went directly from Plain English to Spin byte-code. But it's a start, and we can always consider the more direct route later.
So if you're willing, let's investigate this idea a little further and see where it goes. Questions:
1. Why would you "love to recode" your Pacman in Plain English?" Exactly what attracts you to the idea, and/or what is it about your current workflow you hope to eliminate?
2. Do you have a Windows machine?
3. Can you give me a sample of more-or-less stand-alone Spin code (bigger than "Hello, World!" but smaller than Pacman) so I can (a) consider what it would look like in Plain English, and (b) figure out how to get from that Plain English back to the Spin code we need to hand the Spin compiler?
Thanks for your time and interest, Dr A. And the rest of you fine folks, as well.
You could do that. Right now, it is more that one cog is handling the SD card, one is handling low level video drivers, one is working at a higher level doing tile moving. And I think there is one left over that was for serial debugging messages. One is needed for the spin interpreter.
The propeller is very flexible. I tend to write everything in one cog to start with, which is slow but works, then think about which processes can be farmed off to other cogs and run in parallel.
There are also techniques to stop cogs and reload them with new code on the fly. Move code fragments from an SD card into a cog, run for a while, load in new code. Means you can have a virtual number of many cogs rather than just 8.
We are starting to think here in parallel code. And that leads to a question about Plain English - can the IDE also handle parallel code?
I'm sure it can. The Spin IDE allows you to have lots of tabs open with code in each, so you can have multiple objects open at once and flick quickly between editing each one.
Why would you "love to recode" your Pacman in Plain English?"
Good question, and I'm glad you asked!
Two reasons. First is that flipping between coding in C, Basic, Spin, I get confused with the slightly different syntax for logic operations. I constantly put commas instead of semicolons in 'for' loops etc. Having a common syntax across platforms would be handy.
Second, it would be fantastic to be able to run the same program on multiple platforms. Taking that pacman program for instance, the back end video drivers will be different for each platform, but the main program would be the same, and it would be so much faster with the debug/rewrite/recompile to do this on a windows PC
Yes, I have a Windows machine. Actually, there are five in the house, and another four in the Man Cave. You are going to ask me to try out Plain English on one of them, aren't you? *grin*.
Re an example, I'll rummage around in the obex and see what is there.
Addit, see below. There are lots of things in the obex that combine Spin and Assembly, but I thought it best to start with something that is just Spin. This is one of the 'official' drivers, ie one written by someone in Parallax.
There are some good examples there of the Spin syntax using things like & |> >> := ~~
All of which would be perfect for changing to Plain English.
}} [/code]
I wondered if you were close to making "Plain English" a reality and that Parallax was one of the last few hurdles you faced.
Fortunately I see Dr. Acula has offered his services. I'll be following your progress with interest.
As one of the older folks here, I can tell you it wasn't easy. Mostly, we had lower expectations.
Overnight parts shipment? How about 4 to 6 weeks delivery.
Here is an example of some Tachyon code is a simple VGA breakout game I wrote, it takes just over 1k bytes of code and it's fast. Of course the Tachyon bytecode supports Forth but it is simply an efficient and sensible way to get the most out of P1. I also have Tachyon running on P2 including FAT32 and networking (same as P1).
BTW, Tachyon source code is compiled in Tachyon running on the Prop, so it's interactive too.
Just on a whim, I looked up your thread https://forum.arduino.cc/index.php?topic=385052.0
Ouch. I do a bit of Arduino coding too but it is harder because their forum is not nearly as friendly so you are on your own.
Over here in Propeller land it is a bit different. Ask for help and someone will provide it. In fact, I suspect more people may chime in with their favourite language on the Propeller and why it could work translating Plain English to that language.
There was a thread a few years back where someone was tallying up all the languages that could run on the Propeller. It was sort of a zombie thread that wouldn't die as more languages kept being added. Especially when it got a bit silly with the Z80 being added (thanks to Heater et al) , then every single language ever written for CP/M now was included.
There are going to be many ways to solve this, and all of them are going to be good.
I wonder if we could think about some core modules that would be very helpful. VGA driver, Keyboard, Serial port to start with. It may not matter so much which Propeller language we use.
My wife of 30 years would say I'm "spiraling" here -- considering this, considering that, then making another loop around the circle, hopefully zeroing in on something that's both useful and feasible -- and she'd be right.
Programmers are a very emotional bunch. Despite what they think about how rational and logical they are. They get very attached to the languages and tools they use. They will have very heated arguments as to why what they like is best and anyone who does not agree is "obviously" not thinking clearly.
The classic example of this is in "language wars". A lot of that is about syntax. Do we like block scope and curly brackets? Or do we like blocks delimited by indentation. Do we like "{..}" or "begin..end"? Do we like infix or postfix notation for expressions? Do we like "wordy" languages like COBOL, Plain English etc or do we like succinct mathematical notation? And so on, ad nauseum.
Then there are more semantically oriented arguments. The "goto" is obviously evil to some but vigorously defended as essential by others. The "goto" argument went on for decades before we started to get languages that don't have it, Java, Javascript, ect.
Standing back and looking at it there is nothing rational about these arguments. They are very emotive. After all, common programming languages basically all do the same thing:
sequence
selection
iteration
Rationally they are all equivalent. The heated arguments are born largely from emotional attachments to the ways the opponents do things.
Having a closed source Windows only system is not the way to attract developers now a days.
What do do?
Let's get back to your original post where you gave a nice example:
Surely the course is clear. The Plain English system, running on Windows, has to be able to generate code for the Propeller, or whatever target you decide on. That is only a hand full of instructions and can be easily done in Plain English as far as I can see. That code can be native instructions or LMM or whatever, to be decided.
The Plain English system also needs to be able to download that code to a Propeller so that we get to see our LED flashing. The down load protocol is simple and there are many example programs to borrow from.
To get wide adoption, the closed source and single platform nature of Plain English needs to be addressed. The abstractions I mentioned above would help with that. Then others could contribute code generators or display subsystems for whatever machines/environments they like.
There needs to be a way for people to build a Plain English system from source. Nobody likes to rely on a binary executable download. That means creating a compiler for Plain English (or perhaps a required small subset) in some commonly used language, C, Python, Javascript, whatever so that people can bootstrap their own Plain English system.
That swamp of mudslinging and abuse that ended up being locked pretty well demonstrates what I was saying above.
I'm coming to that conclusion myself.
Okay. That's the same thing that Dr. Acula is proposing. So far...
Brilliant!
But what's good for people isn't necessarily good for machines. So I hasten to add that I like the idea of targeting the VM you've described. I like it very much.
No doubt. Once our compiler has digested the source code it does very little except generate instructions to (a) push parameters, (b) make a call to a routine, and (c) pop parameters. It's very FORTH-like in spirit on the computer's end. The essential primitive routines, of course, actually do things in step (b) like compare, or add, or shift. But these, too, are very FORTH-like in spirit since they are typically only a few instructions long.
I hope you don't really expect me to read postfix code! For comparison purposes, here's a mini PONG game that I coded up for my younger son in Plain English. See if you can read that:
Cool.
That's cool too.
Now we just have to see if Dr. Acula agrees that Tachyon byte-code is a better target than Spin source code!
Generating Spin byte code may be a better idea.
Or perhaps even better Perter's Forth byte codes.
Hope you did not miss my recent posts above.
a) Closer to what you do now
b) Is more predictable, and faster to test
c) Knowing what byte codes Spin will spawn, is not easy at best, and Spin is locked in ROM in P1, so it cannot be easily tuned.
Peter's Tachyon byte-code is highly tuned already, but could be nudged to better suit your code generator.
Yes, I see that. But you're still limited to eight at a time.
Recall my remark above. It's my view that any kind of thinking that has to be learned is less desirable than the kinds of thinking that come naturally to us. I've spent a lot of decades educating children and have found that leading them in natural paths gets the student much farther, much faster. And is much easier on the teacher! Note that that doesn't mean that parallel processing is a bad thing -- just that it needs to be approached in the most natural manner possible. The "big picture followed by separation and delegation" approach that I mentioned above seems, at this point, the most natural to me. But I'm a beginner in this field.
Sure. When you say "Run" in our IDE we start up your program in another "thread" which is just a virtual COG implemented with interrupts (the IDE is still running in the original thread and can also be accessed). So, yes, no problem.
Our IDE is similar -- it has ten tabs at the bottom so you can have ten files open at once.
Okay. But both of those objectives can only be met with a lot of different Plain English compilers (or at least a lot of different "back ends"), one for each platform. Lots of work there.
You haven't already? Seriously: its best to print off the first 54 pages of the manual and to follow the instructions exactly as they're written. Programming in English is different than programming in other languages, and our IDE is quite different as well. But both work conveniently and efficiently once your "unlearn" all the bad habits you've developed over the years!
That's a little more than I had in mind. And I'm not sure you guys are going to like this. Let me take on just the first routine:
The first statement in this routine translates directly: "finalize" simply becomes "Finalize."
But then we run into some philosophical differences in programming style. Allow me to elaborate. Two goals of the kind of programming we see above are compactness and efficiency, while the overriding goal of Plain English programming is naturalness and clarity of expression. Now, do you see the comment above that says "receiving?" The fact that comment exists means the programmer felt the need to explain what the statement "rxOkay := rxPin > -1" was meant to determine; and the fact that the comment ends with a question mark and the resulting value is being stored in a boolean variable suggests that the question may come up again. So in Plain English we'd convert that thought -- that statement and its associated comment -- into a "decider" routine so the question could be naturally asked, both here and elsewhere. This is the code we'd write in lieu of the rxOkay variable:
To decide if we're receiving:
If the receive pin is valid, say yes.
Say no.
Elsewhere, then, instead of checking the value of rxOkay, we would say things starting with:
If we're receiving...
We'll take a teensy performance hit for this, of course, because of the call; but it probably won't be a problem because I suspect this question is not asked all that often.
We'd then do a similar thing with the txOkay flag. And the inverted flag. And to answer the questions, "Are we transmitting bi-directionally?" and (as the return value of this function is intended to indicate), "Are we receiving or transmitting at all?"
Now I'm not exactly clear what "sin" and "sout" are -- serial input and serial output somethings, I suppose, but what? And why are they set to the corresponding pins bitwise-anded with $1F? I'm going to call them "ports" and create a routine to initialize a port given a pin number, like so:
To initialize a serial port given a pin number:
Put the pin number into the serial port.
Bitwise and the serial port with $1F.
It strikes me that this routine may be more appropriately placed in a library of useful general-purpose routines. And I would imagine there would be a similar routine to get the bit time given a baud rate, like this:
To get a bit time given a baud rate:
Put the clock frequency divided by the baud rate into the bit time.
Now with a couple of similar helper routines, the initialization routine would now be something like this:
Finalize.
Initialize the input port given the receive pin.
Initialize the output port given the transmit pin.
If we're not transmitting uni-directionally, exit.
Set the idle state.
Make the output port an output only port.
Or something along those lines. I'm going to stop there. I'm interested to see your reactions to this kind of coding; I'm sure it's different from what you're used to.
Yes, that's a given. But if we generate Spin source code that task will be done in the usual way with the Spin compiler (and, I assume, IDE). If we go Tachyon we'll have to include it in our IDE.
Done. The instructions are on page 9 of the manual.
Everybody relies on binary executable downloads. How can you compile GCC from source if you don't already have a binary version of a compatible C compiler? So why do you think it odd that to compile Plain English from source you need a binary version of a compatible Plain English compiler?
A small subset is all you'll get, and a small subset of Plain English isn't Plain English. Plain English is not just a language; it's a whole philosophy of programming embodied in a special, peculiar, and unique program. See my post above where I convert some of Dr. Acula's serial port code to Plain English -- it's not the kind of thing that can be done a line at a time; and the result is something quite different from the original.
But it's not just programmers. My threads on a variety of electronics and physics forums have also been locked for not sticking with the "party line." See, for example, here:
https://www.physicsforums.com/threads/electron-re-arrangement-and-flow-in-power-lines.835411/
One wouldn't think people could get so worked up over something as small as an electron!
Do not use Spin as the target. See my previous post above.
At least missing out a Spin compilation step is in line with your "keep it simple" philosophy. Less moving parts.
You don't need the Spin compiler or Spin IDE to get a loader for the Propeller. There are many loaders available. In Python or C or whatever. The loader is actually a very simple thing than can be done in 100 lines of code or so. I imagine it can be written in Plain English.
Yes there are instructions on building Plain English. However that is no good on my Linux systems. A very good question. And one that bothers me from time to time.
The situation with a standardized language like C is somewhat different:
a) The language is very rigorously defined and standardized.
b) There are many compiler implementations available.
c) There is a question of trust when it comes to down loading random binaries from unknown people off the net.
Ideally all this means that given only the source code of GCC I can get it compiled without already having a working GCC. Even if that means writing my own C compiler according to the spec. from scratch.
In practice getting a compilation system like GCC compiled and working without an existing GCC compilation system is pretty hard. The bootstrapping thing.
Which leaves me with c) I trust Debian folks with binaries they give me.
That's the bit that bothers me. If it takes 100's guys decades to build a half decent C++ compiler, which it has, and if that C++ compiler is itself written in C++, and if by some accident we lost all the binary executables of C++ compilers, how on Earth are we going to bootstrap ourselves back up again in any reasonable time? The expectation is that there is a Plain English compiler written in C or Python or whatever such that almost anyone anywhere can compile Plain English on whatever machine/OS they have. Then they can use that C/Python to compile the Plain English compiler source thus producing their own Plain English compiler from the given Plain English source.
Perhaps expecting too much.
Or spending hours and hours trying to find books at the library in the card catalog. The cards were indexed only with the ISBN subject information from the book (a separate card for each subject).
This meant that if you were looking for a specific electronic circuit, you had to actually find and read each book filed under "Electronic Circuits".
Full text searches? If only. PDF's? Science fiction; computers for the most part could only display monospaced text.
And as these were just paper cards, lazy people would steal the card instead of copying the book's location. Good luck finding the book after that.
And all of this assumed that the book was actually on the correct shelf and not checked out. And that someone hadn't cut out the pages needed from the book.
Looking back, your method might actually have been more effective!
Walter
Sometimes it frightens me that I can no longer function without Google.
It seems to me the "ideal" implementation of Plain English for you would be:
1. a small command-line compiler
2. written entirely in C
3. that takes in Plain English source
4. and generates equivalent C as output
Such a program would compile and run on any computer with a C compiler. And the programs produced by it would also compile and run on any computer with a C compiler. And the original program, and all of its offspring, could be posted on GitBit or HubBucket (or whatever they're called) for further development by open-source crowd coders. Dream come true, yes?
So let's, you and I, make it happen. Here's how:
1. Put a copy of our latest Compiler and Noodle source files on your Linux machine.
2. Open them up in your favorite editor and comment out all the lines.
3. Uncomment, line-by-line, and translate just the parts we need into C.
Voila! I'll show you where to start and answer any questions you have as you go along.
I estimate that 80% of the compiler, but only 10% of the Noodle, will need to be converted: about 5,000 lines in all. So if we can translate, say, 50 lines a day, we'll be done in 100 days and Plain English programming will be within the reach of everyone everywhere. Woohoo!
You in?