johnearnest / octo Goto Github PK
View Code? Open in Web Editor NEWA Chip8 IDE
License: MIT License
A Chip8 IDE
License: MIT License
I'm not sure if this is a problem with my browser (Chrome) or not, but every time I want to search my Octo code for something, using the browser's "Find" functionality (ie. Ctrl+F and F3), it fails to find any text that is not currently visible in the editor. It will not search through the entire file.
It'd be nice if the values of the delay and buzzer timers were displayed in the interrupt mode, so you don't have to waste timer cycles to put them in a register every place you want to know what they are.
Issue reported by https://github.com/ColourTann - rerunning a program such as http://johnearnest.github.io/Octo/index.html?gist=76e39ee2dc749815715b1e55c9d50f7c results in a black buffer.
Suggested fix is to clear out c.last in SetRenderTarget in shared.js while I'll be attaching in like 20 seconds because I guess I don't know the issue number yet.
Now, octo generates instruction i := 0
. Debugging such occasional overflows could be very painful.
it would be more error-proof to throw an error on such occasions. :)
sample code
: main
i := test
:org 0x4000
: test
0x11 0x22
[0xA0, 0x00, 0x00, ...
It's not very surprising that different CHIP-8 interpreters used different fontsets, but this issue shows what some of the common ones were.
I'm not sure if it's worth it to implement them as options in Octo, as it's unlikely to affect ROMs in any meaningful way (unless a game depends on pixel-perfect collision with hex digits…), but they might affect a game's artistic vision perhaps.
Clicking on the Binary Tools button allows me to click on the Save .ch8 File button to save the compiled program to a .ch8 file. However, I do not see any convenient way of saving the uncompiled source code to a text file. Your example files are saved with a .8o extension. I can easily copy and paste the source code to NotePad and save it from there, but it would be great to save button to save the source code from within Octo. Perhaps this already exists and I am just not seeing it. If so, just point me in the right direction.
I do not hear any sound when the buzzer is set. I do see the change in background color for the buzzing period but no sound. I am running this using Chrome on a Windows 10 computer. If I enable the XO-Chip extended instruction set, I do hear sound, but shouldn't I hear something with just the plain buzzer?
I'm getting the following error:
Shared program source text must be less than 32k. Store larger programs in a Gist.
Also I've noticed it sends everything to different host, https://vectorland.nfshost.com/storage/octo/, no github gist. We've just started our new masterpiece and we can't share it anymore :(
The original chip-8 interpreter would wait until after an interrupt before drawing a sprite, which means that you can only draw 1 sprite per frame. There should be an option for this.
v0 := delay
works, but v0 := buzzer
does not :(
Many manuals I could find on the internet mention that scroll up instruction has 00 BN form, but it looks that octo uses 00 DN form. Which is right opcode?
Forked octo about a week ago. Added npm package management, converted the code to typescript, and used webpack to build. Getting close to decent state I think. Doing manual testing and resolving issues.
Any interest in those changes?
In progress here:
https://github.com/zZeck/Octo/tree/typescript
Never tried to contribute to a project before, so hopefully I'm asking this in the right place...
Really like octo. Helped me sort out if my own chip8 interpreter had problems or if I was just seeing ROM quirks.
0 Initialisation of the memory array, emulator.m, appears to be reliant on the rom the user provides, combined with how javascript is feeling that day. For a standard rom this seems to usually be 8k (emulator.m.length). However, XO chip allows addressing up to 64k - memory will be allocated to cover the rom and some additional as per JS's whim, so normal programs will not have problems. It is however possible to then manually address into this unallocated memory. A subsequent load will load 'undefined' into octo's emulator.v array, rather than 0, which will cause js errors when attempting to eg print these in the debugger, and perhaps cause other issues further down.
An example rom that will demonstrate this issue can be found here:
http://johnearnest.github.io/Octo/index.html?gist=44eb5e7c3c482581c445b58c6fc6094e
A check in the emulator's handling of the 0x65 command, such that it loads 0 instead of undefined into the registers, seems like it would maintain expected behaviour.
I was using DXY0
as a simple method to create a single frame of delay, but Octo assumes this is the Super Chip instruction, which draws a 16 pixel sprite instead.
If the editor has text with long lines, the toolbox will be pushed offscreen so it cannot be used.
Since Octo games work so well on mobile otherwise, it'd be great if there were an on-screen keyboard on mobile devices.
Not sure how to make all 16 keys fit though…
As an Octo user, I would like to be able to load a .8o from my file system, so that I can continue working on previously saved Octo programs.
#62 added the ability to download Octo source code as a .8o file. Subsequently, the ability to load a .8o from the user's file system was requested in the comments of #58.
This is purely a convenience feature; currently, the user can easily copy and paste into Octo.
This might be implemented using the FileReader API.
If you try to write code similar to
unpack 0, some_addr
:org 0x4000
: some_addr
you will get 12-bit error.
It would be nice to have a possibility to unpack 16-bit address (I tried use this.veryLongValue() if input mask == 0), but it corrupts generated code if the label was forward-declared
<script>window.onload = runGist();</script>
Is it intended?
window.onload assigned return value of runGist, instead of calling it after everything's been loaded
Should it be like
window.onload = runGist
?
Consider the following example:
: main
vc := 10
vd := 10
v0 := 0
:breakpoint before
if v0 == 0 then i := long data
if v0 == 1 then i := long data
:breakpoint after #CORRUPTED V0 HERE
loop again
:org 0x7000
0 1 2 3 4 5 6 7 8
: data
0 1 2 3 4 5 6 7 8
0 1 2 3 4 5 6 7 8
Good day, I'm trying to implement continuous audio playback with the following code
: audio_play
v2 := 16
v4 := 2
i := long audio_data
loop
audio
delay := v4
buzzer := v4
i += v2
loop
v6 := delay
if v6 != 0 then
again
again
return
And it produces illegible results full of clicks and noise.
I implemented 4kHz converter back to .wav file, it's kinda damp and echoed, but perfectly recognizeable and at relative good quality. So I believe, the problem is in emulator audio streaming (or its absence).
Maybe emulator should continuously feed empty audio data after first audio
instruction, and mix incoming data into it? I could implement it, but we threw all resources on the upcoming awful jam! :)))
Thanks
v0 := 10.3
v9 := 0b00bs
v8 += 0xEN
v7 += 0=1=2=3=4
It doesn't really break anything, but it does seem a tad undefined.
See the running example:
https://gist.github.com/anonymous/4bff9d3f107c10eec80f
http://johnearnest.github.io/Octo/index.html?gist=4bff9d3f107c10eec80f
From the manual:
Octo also provides pseudo-ops for using <, >, <= and >= to compare two registers or a register with a constant
...
These are implemented by using the subtraction instructions -= and =- and querying vf, and will destroy the contents of a temporary register as well as vf. By default this temporary register will be ve, but defining an :alias named compare-temp can reassign it to any register but vf.
There is no reason why vF can't be used (in fact, there is no reason to use any other register)
if v0 > v1 then ...
This code is currently compiled to (written here in pseudo-code):
vE = v1
vE -= v0
skip if vF == 01
Which is not ideal. First, of course, it uses that temporary register rather than vF (using vF is ok since the flags are set after the subtraction is preformed)
Additionally, it's recommended to always compare vF with 0 rather than 1, because some interpreters set it to values other than 1 in some situations.
I believe a better result would be this:
vF = v1
vF -= v0
skip if vF != 0
Tested game are one of Octo Jam II submission: SK8 H8 1988
http://johnearnest.github.io/Octo/index.html?gist=4b793e2222ca725d540c
I expect that, this occurs when after switching screen mode.
Perhaps could be fixed?
Playable: https://a-kouz1.github.io/Octo/index.html?gist=4b793e2222ca725d540c
Hi, I love to see how new features that's would calculate variable values automatically when compiling through :calc
token. But, I want to mobilize more freely to express myself with this thing! I suggest, new available expression for CHIP-8 assembler in Octo which used in the most programming language such as boolean expression like x > y
, x == y
, x < y
will added. And blablabla ya know that will report 1 if expression's were true, and otherwise, 0 if expression's were false.
Which useful for periodic checking whether this and that,
yea... like... "that value is true then output this value, otherwise just keep old value".
Oh ya other things, there's few expressions that's needs to be added in meta-programming:
/*Unary functions*/
'round' : function(x) { return Math.round(x); },
'random' : function(x) { return Math.random()*x; }
/*Binary functions */
'<' : function(x,y) { return +(x<y); },
'>' : function(x,y) { return +(x>y); },
'==' : function(x,y) { return +(x==y); },
'!=' : function(x,y) { return +(x!=y); },
'<=' : function(x,y) { return +(x!=y); },
'>=' : function(x,y) { return +(x!=y); },
Yehg, round and random unary functions was necessary...
Round function will used when doing fraction things that's should output rounded value instead nothing which it's trucated as sometimes doesn't makes sense. Aaaand a functional random which leaves a random value when compiling, it is sometimes useful for debugging purpose.
And, decimal constants that could be written in :calc block.
:calc a { 0.75 } # This is more acceptable.
:calc b { 75 / 100 } # This is rather than.
I have write a program which prerenders Mandelbrot set in compiling time and
output is shall as this screenshot, this is ch8 output
Make sure this source is could be compiled and works correctly.
I'm insist to implement those and yea, thanks for that a lot!
Due penformance lacks, make Chip-8 emulation through this interpreter uses array of functions instead of big switch statements. That's boosts penformance up, makes program runs more natively in high cycles. Seems there's tutorial that written for C. But javascript has it, because javascript is more dynamic.
Names which constant could be used as its negative value for registers assignment, rather than creating another name and redefining it.
: const offset_X 64
v0 := -offset_X loop ...
: const screen_width 128
center_X := { screen_width / 2 }
loop ... if center_X = { -screen_width / 2 } begin blablabla
XO-Chip extention has extra high resolution which 256x128 (megachip) uses token xhires
and toggled in emulation by 00FA
opcode, which it is not used by Super-Chip(?).
This is its result which it did.
XO-Chip extention allows program counter executes opcodes beyond 0x0FFF
I have said earlier, program counter is sticks its higher nibble of word by any regular jump instructions, but not by long jump xxxx
which allows to jump to other zone.
\*regular jump which sticks*\
this.jump = function(nnn) {
this.pc = (this.pc & 0xF000) || nnn;
}
This is same for calling routine, for using calls from other zone shall using long call xxxx
token. This is never breaks earlier octo XO-chip games (that i've seen in jams). but there's issue that for compiling values which defined in metaprogramming things, HERE variable is still HERE, but LONG_HERE is shall exist...
Further cycles/frames speed, I like to experiment things like working for long computations but impatient... It will affects browser penformance which makes it impact. But just make it standard, 10000 cycles/frames is ok tho.
Another significant penformace optimization, screen refreshes only in certain parts (with list of changed pixel in sprite drawing and another list which copies last screen to compare by) and which it is triggered by draw flag (there's three kind: zero is for don't refresh anything, one for certain part, two for the whole screen like scrolling thingy), too. Browser keeps canvas on so that makes elimination of frameskipping or unconsistent delays/flickering in high resolution mode...
blablabla, maybe I should pull this one as request, this is hard to explain! >.<
Hng, allow me to suggest an interface for this emulator...
It is would be nice if this is has, also for debugging are there tho.
a. Basic layout.
b. Load menu.
c. Load from explore menu.
d. Tools menu
e. I hev no further ideas, so sorrehg ._.
Could be implement through html, this is should be... a big improvement for octo.
An anyways,
If request were asked, I'll pull it later If I am supposed to.
Another compability comparison in program of original CHIP-8 Interpreter was any arithmetical instructions in 8xyn except n = 0, also destroys vf in bitwise operation.
Thanks!
A-KouZ1/Kouzeru
Having to remember what keyboard key maps to which number for instructions is tedious.
There should be predefined constants like
:const KEY_W 5
:const KEY_S 8
And so on, so I could just do.
v0 := KEY_W
if v0 key then move_up
If something like this already exists, it should be added to the docs.
Since the original COSMAC VIP had an ortholinear keypad, it'd be really nice if Octo supported using the ortholinear numpad for input.
There isn't room for all the keys on most numpads – since the 0 key is wider, the + and Enter key are taller, and it'd be really weird to use the top row for anything – but the keys 1-9 being ortholinear would make it much smoother to move in an 8-directional game.
See also #92 which could make this issue obsolete.
Most of these are from CHIP-8E (found in VIPER) with some moved around and some others added. Thoughts?
Opcode | Description |
---|---|
B0KK |
Branch forward by KK bytes |
B1KK |
Branch backward by KK bytes |
B20X |
Branch forward by VX bytes |
B30X |
Branch backward by VX bytes |
B21X |
Branch forward by VX bytes if VF is zero |
B22X |
Branch forward by VX bytes if VF is non-zero |
B31X |
Branch backward by VX bytes if VF is zero |
B32X |
Branch backward by VX bytes if VF is non-zero |
Opcode | Description |
---|---|
00FC |
Disable display |
004B |
Enable display |
Opcode | Description |
---|---|
8XY8 |
Set VF , VX equal to VX multipled by VY |
8XY9 |
Set VX equal to VX divided by VY . VF is set to the remainder. |
Opcode | Description |
---|---|
5XY4 |
Skip next instruction if VX is greater than VY |
5XY5 |
Skip next instruction if VX is greater than or equal to VY |
5XY6 |
Skip next instruction if VX is less than VY |
5XY7 |
Skip next instruction if VX is less than or equal to VY |
Opcode | Description |
---|---|
00F2 |
No operation |
0015 |
Halt until the delay timer is zero |
This feature is certainly for XO-Chip expansion.
If this is interesting and useful, add this feature for that expansion.
PC (Program Counter Nyyy) sticks at first high nibble (N) of first byte of its address, and N is never affected by regular jump instruction until there's a certain instruction that would do longbranching just like how long I (Memory Index) does. I hope this is considerable, thank you!
Under the Animation header in the Beginner's Guide, the second example pasted verbatim just gives a blank screen.
: arrow
0x20 0x71 0xFB 0x7F 0x3F 0x1F 0x3F 0x7F
: main
i := arrow
va := 0 # the arrow's horizontal position
vb := 0 # the arrow's vertical position
loop
clear
sprite va vb 8
va += 2
vb += 1
again
Commenting out either va += 2
or vb +=1
works fine, but for some reason it just does nothing when both are active.
First noticed after pasting the first example and trying to increment va
myself, then when I saw the next example covered that I pasted that in and still got no output. Tried in both Chrome and Safari on OSX El Capitan.
Other examples in the dropdown on Octo all work fine.
Please... Add autosave feature. A was programming a little game for whole day, but I accidently pull screen up(mobile browser) and the page reloaded. Default example were loaded.
Current emulation system is not suitable for the good quality sound generation. It's inaccurate, and missing some features we can use in CHIP8 games. This is a collection of more-or-less random thoughts relevant to sound generation. I'd love to get some feedback on it from you and/or anyone interested in, before I start doing anything to avoid wasting my/your time. :)
Picture 'audio' instruction loads memory address from I register to some hypothetical DAC. Now it copies data from memory, which prevents us from modifying source sample data in time, allowing some sort of FM synthesis. Example (some temporary register assignments have been omitted for better readability)
i := pattern
audio
buzzer := 2
delay := 2
loop
if delay == 2 then
again
i := pattern
v0 := 0x80
save v0 # change first byte of audio patten to 0x80
or maybe add some accurate loop-delays to achieve some realtime sample modification.
Typical chip8 timer resolution is 16.7ms, typical audio sample length is 32ms (128 bits / 4000). Typical latency of 4k buffer for 44100Hz sampling rate of typical audio device is ~93ms.
That's why I introduced AudioBuffer queue some time ago, to collect all data sent to that hypothetical DAC and render it. Putting buffer in queue has its own drawbacks, when we cancel buffer (different data was scheduled/0 sent to buzzer register), we have to 'dequeue' some data from this buffer. Now it's done with 1/60 resolution, which is obviously not accurate enough. We can fix it by adding some fractional points to .st register, but I'd think of better approach.
On the interrupt screen I see a nice listing with address, code, source. I sure would be nice if such a listing could be sent to a file or printed.
save vX - vY does not increment i, but save vX does. Probably it's a good idea to control this behaviour via quirks mode, and make both commands consistent. (increment i after save range instruction)
I found it while figuring out something for quick 4-byte wipe, something better than:
v0 := 0
save v0
save v0
save v0
save v0
I replaced it with
v1 := v0
save v0-v1
save v0-v1
And it stopped working. Spent an hour with breakpoints looking for a bug in my code, but it turned out that emulator just did not increment i, and wiped only two first bytes.
Thanks.
Labels are forwarded automatically
jump foo
: foo
but consts are not
00 foo 00
: const foo 1
Undefined names: foo
The reaching set of an address representing a return ( 0xEE) can somehow have itself as a successor. This can result in an infinite loop.
function analyzeWork() {
// ...
var here = fringe.pop();
var prevret = reaching[here]['rets']; // can contain address `here`
// ....
var children = successors(here, prevret); // enlists `prevret` as `children` if `here` is a return, potentially including `here`
//eventually `child` == `here`
fringe.push(child);
//repeat
}
What do you think about adding a header that allows the ROM developer to specify the needed compat. options (including "cycles per frame")?
The header could start with a 1nnn
instruction to allow the game to still be loaded by a non-header-aware interpreter.
Thoughts?
Hi!
Your IDE is amazing. I tried a while ago, and it was pretty easy to decompile any program and edit it or create new programs with your language. However, today I've been messing around with Octo again, but everything seems to be decompiled wrong. Most of the code appears as "# unused?" and all the values appear like this: "v0 += 0x0 0X04 # result is always 0x00". I've tried with several games.
I just wanted to let you know. Maybe it's a very recent bug or something.
Have a nice day!
Referring to David Winter's Blinky.src
Both compilation (Chip-8 or SuperChip) could achieved by only single source.
I suggest these kind of tokens:
:if (condition) { statement } :else { statement }
For example, both features could be enabled by choice:
:const useXO 1 # |useXO| > 0 is treated as true
:const SCres 0 # |SCres| = 0 is treated as false
:if useXO { plane 3 }
:if SCres {
:const W { 128 }
:const H { 64 }
hires
....
} :else {
:const W { 64 }
:const H { 32 }
....
}
Could add this option as quirks?
For specific,
I am pointing to 8xy1, 8xy2, and 8xy3 opcode.
This is proven did so, when working program for VIP.
What is the purpose of the Share button on the Octo page? I cannot find it described in the docs and it is not obvious what it does.
It would be nice if Octo had options for remapping the keys, preferably also with the ability to export that remapping to the HTML file.
See also the discussion in #35.
It's Halloween, and the bit shift instructions 8XY6
and 8XYE
keep haunting us…
It seems that those instructions don't just have two behaviors, but three! On the original COSMAC VIP, they shifted VY and put the result into VX, resulting in VX and VY containing the exact same value. (This might partly explain why SCHIP decided to simply not care about VY at all.)
See discussion and 1802 machine code disassembly.
I'm not sure if this quirk is worth implementing, but it was news to me!
I've always thought it would be nice to have the ability to download a .ch8 file corresponding to the raw CHIP-8 data generated from a compiled Octo source.
I noticed @JohnEarnest recently added FileSaver.js into the repository as part of an experimental screenshot capture feature. We might be able to use FileSaver.js for a .ch8 save feature, as well.
So I was trying to make the phrases more legible than hex numbers.
But I ran into a snag I think- you can't use consts to build a phrase?
Check this out- I build a phrase with hex digits and build the same
phrase with constants. They don't display the same thing.
It would be nice if this was supported. What do you think?
: font
0x20 0x20 0x20 0x00 0x20 0xF0 0x10 0x70 0x00 0x40 0xE0 0x90 0xE0 0x90 0x90 0x90
0xE0 0x90 0xE0 0x90 0xE0 0x80 0x80 0xD0 0xB0 0xB0 0x90 0x90 0x90 0x90 0x60 0x60
0x90 0x90 0x90 0x60 0x90 0xD0 0xB0 0x90 0x90 0x90 0x50 0x20 0x40 0x00 0x00 0x00
0x00 0x00 0x60 0x90 0xF0 0x90 0x90 0xF0 0x90 0x90 0xF0 0x80 0xE0 0x80 0x80 0x80
0x80 0xF0 0x80 0xE0 0x80 0xF0 0x40 0x40 0x40 0xF0 0x20 0x40 0x80 0xF0 0x20 0x20
0x20 0xC0 0x60 0x90 0x80 0x90 0x60 0x90 0x90 0xB0 0x70 0x80 0xB0 0x90 0x70 0x80
0x60 0x10 0xE0 0x90 0xA0 0xC0 0xA0 0x90 0x90 0xB0 0xB0 0xD0 0xF0 0x40 0x40 0x40
0x40 0x90 0x90 0x70 0x10 0xE0
: readyLiteral
0x0A 0x41 0x32 0x0C 0x71
:const iR 0x0A
:const iE 0x41
:const iA 0x32
:const iD 0x0C
:const iY 0x71
: readyConst
iR iE iA iD iY
: draw-text
v1 := 2 # x
#v2 := 1 # y
v3 := 0 # byte
# v4 contains length
loop
: text-addr i := 0 # draw phrase modifies to phrase addr
i += v3 # inc to next char in phrase
load v0 # lad char offest into v0 from i
i := font # now make i beginging of font
i += v0 # add char offset. i is pointing to sprite.
sprite v1 v2 5
v1 += 5
if v1 == 62 then v2 += 6
if v1 == 62 then v1 := 2
v3 += 1
if v3 != v4 then
again
;
: draw-ready-literal
:unpack 0xA readyLiteral # Get address of phrase into v1 v2
i := text-addr # load addrss of loop init in draw-text
save v1 # save v1/v2 into loop init code
v4 := 5 # load count of chars
draw-text
;
: draw-ready-const
:unpack 0xA readyConst
i := text-addr
save v1
v4 := 5
draw-text
;
: wait
v0 := 128
delay := v0
loop
v0 := delay
if v0 != 0 then
again
;
: stop
: here jump here
: main
v2 := 1
draw-ready-literal
v2 := 10
draw-ready-const
stop
When switching from high to low resolution or vice versa in XO-CHIP, the display is cleared. Correct me if I'm wrong, but this is different from how it worked in SCHIP. I believe that in SCHIP, the display is always 128 x 64, but in low-res mode each displayed "pixel" is in fact 2 x 2 pixels large and the DXYN instruction takes this into account. (This is why SCHIP is able to scroll a "half-pixel" up, for example.) Switching modes would just change how DXYN addresses pixels on the screen, and would not erase the screen in the process.
This might have been intentional, but if so the incompatibility should probably be documented.
Hi,
I'm writing a small algorithm to flip a bitmap horizontally and bumped into "breakpoint: machine code is not supported." issue, running this code (was reduced from full version):
: buffer
0xC0 0xB0 0x8E 0x81 0x80 0x80 0x80 0x80
: sub
va := 0
loop
load v0
v1 := v0
v2 := 0x55
v0 >>= v0
v0 &= v2
i := buffer
i += va
save v0
va += 8
if va != 32 then
again
;
: main
i := buffer
sub
;
Is that an emulator issue or am I doing something wrong?
Regards,
Aleksey
Ideally:
:const x 10
:const step 12
va := x
vb := x + step
maybe introducing braces () for simplicity, like vb := (x + step) or something.
this might be done with simple split('*')... split(['+-']) trick
Not the biggest deal, but it does get a little old to sift through output (7).ch8
, output (13).8o
, output (2).gif
and (for some reason) download.html
in the browser's Downloads/ directory.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.