jsTIfied Graphing Calculator Emulator
Table of Contents
Overview
jsTIfied is an online graphing calculator emulator, emulating the TI-83 Plus, TI-83 Plus Silver Edition, TI-84 Plus, and TI-84 Plus Silver Edition. It runs entirely in your browser using HTML5 and Javascript, so it requires neither Java nor Flash. For legal reasons, you must load your own ROM image, which is stored in your browser and never sent to the Cemetech server. As a full calculator emulator, it offers lots of features:
- Full TI-83+/TI-84+ emulation including accurate LCD physics for good-looking grayscale
- Runs on all major browsers and operating systems
- Can take animated and still screenshots
- Load any .8xp, .8xk, etc program or App to test it
- Drag calculator files onto the jsTIfied LCD to import them
- Can export all files from the emulated calculator
- Integrated with the SourceCoder TI-BASIC IDE/editor, so that you can write programs in SourceCoder and immediately test them on a calculator
- Built-in debugger and CPU/memory view for assembly programmers
Click here to launch the jsTIfied online graphing calculator emulator, check out the video below for an overview of the major highlights, or read on for more details about how jsTIfied works.
How Does It Work?
The jsTIfied emulator is written entirely in Javascript and HTML5. It was long a dream of mine to be able to create such an online emulator, but until recently, this was impossible. A variety of factors came together to finally make jsTIfied feasible, namely:
- The Javascript File API: This API provides a means for Javascript to directly load files, rather than simply sending them off to a remote server as a form attachment. This API was important because for legal reasons, I did not want users’ ROM images to be stored on or even pass through the Cemetech web server.
- DOM Storage: Just as Flash applications are allowed to store a small amount of data in clients’ browsers for the next time they launch, a reasonable graphing calculator emulator would need to store data in the user’s browser. The most obvious piece of data is the ROM image, which can be up to 2MB for a TI graphing calculator, but there’s also the calculator’s RAM, current flash, ASIC, keypad, and LCD state, preferences, debugger settings, and more.
- The Canvas API: Instead of doing something kludgy like manipulating thousands of tiny divs, programmers can create images from Javascript with Canvases, vital for a Javascript graphing calculator emulator to have a fast, realistic LCD screen. Browsers like Firefox even let users right-click on the canvas and save it as an image.
- Fast, efficient JIT JS interpreters: Perhaps the most important on this list, modern Javascript interpreters coupled with modern consumer hardware are actually fast enough to emulate a 15MHz processor in Javascript. Other coders’ projects have demonstrated GameBoy, ZX Spectrum, and even 386 emulators in Javascript.
In order to understand how it works, you must know a little bit about how graphing calculators (or any basic embedded system) works. I’ll give you a very high-level overview, assuming you know the basics of computer architecture theory and practice.
High-Level Overview
jsTIfied is built as a series of modules, roughly corresponding to either a major component of a graphing calculator or a major feature of the emulator. Figure 1 shows the major modules of jsTIfied, categorizing them as emulator features or calculator components.
Figure 1 shows that while the majority of the code in jsTIfied corresponds to physical hardware features of a TI graphing calculator, a substantial amount is also necessary for emulator-specific features and tools. Let me tell you about each of the modules in Figure 1, how they work, and how they communicate with other pieces of jsTIfied.
- z80 CPU: The CPU (processor) is the heart of jsTIfied, and occupies a huge amount of code. Although automated tools adapted from the FUSE emulator helped simplify the process, a great deal of tweaking, optimization, and adjustment was necessary. The CPU includes the code to process instructions, all of the z80’s registers, and most of the code to manage interrupts.
- Memory: The memory module handles raw memory reads and writes. While many go to RAM, some also go to ROM, and writes to special addresses trigger special “meta” flash commands. The ASIC in the TI-83+ and TI-84+ includes several numerical ports that remap pages around the device’s address space, and intercepts all memory commands, so this module shares some of the work of the ASIC modules.
- Flash: Handles only the “meta” flash commands, such as those used to erase sectors, pages, or the entire chip, or fetch its capacity or ID. Reads and writes from/to EEPROM are handled by the memory module.
- TI ASIC: The second-largest module other than the z80 CPU. Serializes and deserializes calculator-wide state including the RAM, ROM, and ports into and out of DOM storage. Mediates between the SourceCoder and file import/export module and the calculator’s virtual link port to allow files to be send to and from the emulated calculator. Handles the main event loop that feeds opcodes to the CPU, times and hands interrupts off to the CPU, and handles z80 port input and output. Not shown: timer module that handles TI-84+-specific crystal timers, technically part of the ASIC but broken out into a separate module for jsTIfied.
- 96x64 LCD: Thanks in part to research from the PindurTI emulator, emulates very accurate LCD physics, which makes grayscale look good and calculations and graphs look authentic. Emulated physics include LCD power levels, pixel turn-on and turn-off intensity profiles, and sub-pixel emulated LCD pixel divisions.
- Keypad: Practically a single function, sets internal ASIC registers in response to Javascript key up and key down events.
- Debugger: Reads CPU state and memory state from the z80 CPU and memory modules. Also includes a rudimentary disassembler to render memory as z80 ASM code.
- Linking Assist: Part of the ASIC; a variety of functions to perform silent linking to send files to the calculator and get them out again. Also used to list the variables present on the emulated calculator.
- Screenshot Tools: Includes code to once or periodically grab an LCD image, and generate a GIF image. Although a public GIF library was used as the base for this functionality, such extensive changes were made to fix bugs, introduce new GIF features, and optimize the size of the resulting files that it is practically a new GIF library.
- SourceCoder, File Import/Export: A simple set of functions to interact with SourceCoder’s API.
- AJAX Interface: Standard AJAX functions to push and pull data to and from the Cemetech server. Because Javascript cannot directly output files to the client’s browser, jsTIfied uses the Cemetech server as a mediator for screenshots and files. It pushes the data to be exported, then opens an invisible iFrame that causes a file download dialog to appear.
To the layperson, the logic behind some of the design decisions that I made may not be obvious. Why, for example, was it so important to keep the ROM images on the users’ computers? Or why did I choose to implement/overhaul GIF rendering in Javascript rather than use simple server-side GIF libraries?
Critical Design Decisions
jsTIfied’s design was influenced by several key considerations. First and foremost, it had to be fast, whether one or 1000 users were using it simultaneously. Secondly, it had to be reasonably secure, preventing the possibility that users might lose proprietary state stored on their devices. Thirdly, it had to be legal: many emulators have been become unavailable over the years by including or releasing ROM images. Fourthly, it had to be as compatible as possible and require minimal prerequisites. Finally, it had to offer most if not all (or more!) features compared to offline, installable emulators. Among the decisions that each factor influenced:
- Fast: Although Javascript is not an inherently fast language, modern JIT Javascript engines help. To eke out every bit of speed possible, I use Google’s Closure compiler, compress global names to reduce symbol table lookup times, and performed many rounds of profiling and optimization using Firebug and Chrome’s JS profiler. To make sure that users get a consistent experience, I made sure that almost none of jsTIfied’s required computing power comes from Cemetech’s own servers. The most server-intensive features are exporting screenshots and exporting files, and even those simply require the server to echo a data stream back to the client. It would also be impossible to include the Cemetech server in the loop for any critical part of the emulation due to variations in latency.
- Secure: All RAM and ROM state is kept on the clients. Screenshots and file exports are bounced through the Cemetech server due to Javascript limitations, but are not cached or stored server-side.
- Legal: According to most interpretations of the law, ROM images are specific to the device from which they’re dumped, on the grounds that in buying the device (ie, the calculator) you purchased a license for its software (ie, its operating system), and by duplicating or distributing a ROM you are illegally distributing software without authorization or a license. By using DOM Storage to keep the jsTIfied ROM from touching the Cemetech server, there is no question that users are using only their own ROM, and that Cemetech isn’t inadvertently turning either itself or its users into legally-vulnerable criminals.
- Compatible: By requiring only a browser and Javascript, jsTIfied can run on any operating system with a decent browser, from Windows, Linux, and Mac OS to mobile devices. Flash and Java would both be too cumbersome of requirements for the job, not to mention that I dislike Java and have little Flash experience.
- Features: By using a webapp approach, new features will be instantly pushed to every user. Javascript is now powerful and expressive enough that all the essential emulation features, plus extras like a debugger and screenshots, are possible without sacrificing the real-time speed of the emulation.
Emulation Example: The DJNZ Instruction
To help you understand the very basics of how jsTIfied works, let me show you an example of one opcode being executed. In z80 assembly, the djnz
opcode is a “decrement and jump if not zero” instruction. It operates on the b
register, one of the several 8-bit registers in the z80 CPU. It first decrements b
, then checks if it is zero. If so, it continues to next instruction past the djnz
, but if b
is not zero, it jumps to the label mentioned after the djnz. An example of some real code that uses djnz:
ld a,(randData) ;save randomness of data
ld b,255 ;255 bytes to clear
ld hl,MainSafeRAM ;clear our saferam areas
InitClearLoop:
ld (hl),0 ;put zero in it
inc hl ;go to next byte
djnz InitClearLoop ;done yet?
ld (randData),a ;recall contents of random data
First, I want to tell you a little more about djnz
. Every z80 instruction takes a certain number of clock cycles, and as on most processors, jump instructions take longer when the jump is taken. Luckily, there are plenty of tables for z80 opcodes that tell you everything you need to know:
Mnemonic Clock Siz Opcode
DJNZ $+2 13/8 1 10
As you can see, the djnz
intruction takes either 13 or 8 cycles to execute and starts with byte 0x10. It is followed by one byte that indicates how far to jump forwards or backwards. Here’s how the djnz
line in that code above gets assembled:
Addr Data Code
6C80 10 FB djnz InitClearLoop
As you can see, this instruction happens to be at address 0x6C80. The key bytes from the program here are 0x10, the djnz
instruction itself, and 0xFB, which means to step backwards 256-251-2 = 3 bytes to address 0x6C80-3=0x6C7D. Let’s imagine that we’re executing this on jsTIfied. When we start executing this instruction, the CPU will already have plenty of state. The z80
variable in jsTIfied holds the current CPU state, including registers z80.pc
(the program counter, the address of the currently-executing instruction), and z80.b
(the value of the b register). We’ll start with z80.pc
= 0x6C80 and z80.b
= 2. The very core of the emulated z80 processor is the z80.step()
function, which looks like this:
function z80.step() {
if (z80.breaks) { // trigger on breakpoints
for(var i=0; iif (z80.breakp[i] == z80.pc && !debug_stepping) {
debug_trapped = true
return
}
}
}
im = z80.im // used for breaking when the interrupt mode changes
var tss_old = tss;
tss += 4; z80.r = (z80.r+1) & 0x7f //every instruction is at least 4 t-states
var op_code = ti_83p_read(z80.pc++); z80.pc &= 0xffff
switch(op_code) {
...
case 0x10: // if opcode is a djnz...
tss+=4 // We already added 4, now we have 8
z80.b = (z80.b-1) & 0xff // decrement b and overflow as necessary
if(z80.b) {
tss += 5
z80.pc += extend_sign(ti_83p_read(z80.pc)); //sign-extend the byte after PC to a word, add
z80.pc &= 0xffff // wrap around over 0xffff
}
z80.pc++ // increment past the jump distance byte
z80.pc &= 0xffff //fix the overflow if there is any
break //and done
...
}
...
}
Focus on the part of the code near the end that processes the case where op_code
=0x10, the code for djnz
. The tss
variable tracks the number of clock states that have elapsed, and for every instruction, the z80.step()
function adds at least 4 clock states, since the fastest instruction takes 4 clock states. In the case 0x10:
code, we add another 4, since the no-jump djnz
case takes 8 clocks. Next, the function decrements b
, and since b
is an 8-bit variable, fixes an underflow from -1 to 255. If z80.b
is not equal to zero, then the code adds another 8 clocks to tss
(making 13 total), and modifies z80.pc
to take the jump into account. Finally, whether the jump is taken or not, the code increments z80.pc
one more time, and fixes z80.pc
if it overflows.
And that’s all there is to it! Of course, there are about 500 other cases for all of the other instructions the z80 CPU supports. There’s plenty of other code around this, including the code that refreshes the screen every time a certain number of clock cycles passes, but this is all you need to know to get an idea of what jsTIfied spends most of its time doing: emulating many z80 instructions very fast.
Conclusion
I hope you enjoyed this short introduction to jsTIfied’s inner working and found it edifying. Don’t hesitate to snag me (Christopher “Kerm Martian” Mitchell) on the Cemetech forum, the SAX chat widget, or #cemetech on the Efnet IRC network if you have any questions, comments, or suggestions.