├── docs ├── c64 │ ├── CNAME │ ├── batalyx-192.png │ ├── batalyx-512.png │ ├── favicon-left.png │ ├── gridrunner.s64 │ ├── c64 │ │ ├── c64_tiny.wasm │ │ ├── pp_javascript.js │ │ └── c64_tiny_host.js │ ├── favicon-right.png │ ├── speaker-white.png │ ├── tips.js │ ├── manifest.json │ ├── Server.py │ ├── serviceworker.js │ ├── server.pem │ ├── key.pem │ ├── index.html │ └── index.css ├── atarist │ ├── fd0.st │ ├── fd1.psi │ ├── gr.st │ ├── hd0.qed │ ├── GR-ST.prg │ ├── tos-1.04-uk.rom │ ├── index.html │ └── pce-config.cfg ├── atari800 │ ├── A8E.data │ ├── gridrunner.atr │ ├── Server.py │ └── index.html └── vic20 │ ├── event.js │ ├── js │ └── common.js │ ├── vic20 │ ├── keyboard.js │ ├── basic.js │ ├── vic20.js │ └── tapedrive.js │ ├── cpu │ ├── cpu6502disassemble.js │ ├── cpu6502assemble.js │ └── cpu6502.js │ ├── index.html │ └── peripheral │ └── via6522.js ├── bin ├── gr-st.prg ├── gr-st-bench.prg ├── gridrunner-bench.prg └── patch-atari-end-byte.bin ├── orig ├── GR-ST.prg ├── Llamasoft1.d64 ├── gridrunner.prg ├── gridrunner.xex ├── gridrunnervic20.prg └── Gridrunner_1982_HesWare_US.pdf ├── src └── README.md ├── utils ├── ConvertCharSetToBinary.py ├── RemoveUnusedVars.py ├── AddCharsetVars.py ├── AddColorVars.py └── ParseCharsetData.py ├── Makefile └── README.md /docs/c64/CNAME: -------------------------------------------------------------------------------- 1 | gridrunner.xyz -------------------------------------------------------------------------------- /bin/gr-st.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/bin/gr-st.prg -------------------------------------------------------------------------------- /orig/GR-ST.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/orig/GR-ST.prg -------------------------------------------------------------------------------- /bin/gr-st-bench.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/bin/gr-st-bench.prg -------------------------------------------------------------------------------- /docs/atarist/fd0.st: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atarist/fd0.st -------------------------------------------------------------------------------- /docs/atarist/fd1.psi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atarist/fd1.psi -------------------------------------------------------------------------------- /docs/atarist/gr.st: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atarist/gr.st -------------------------------------------------------------------------------- /docs/atarist/hd0.qed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atarist/hd0.qed -------------------------------------------------------------------------------- /orig/Llamasoft1.d64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/orig/Llamasoft1.d64 -------------------------------------------------------------------------------- /orig/gridrunner.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/orig/gridrunner.prg -------------------------------------------------------------------------------- /orig/gridrunner.xex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/orig/gridrunner.xex -------------------------------------------------------------------------------- /docs/atari800/A8E.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atari800/A8E.data -------------------------------------------------------------------------------- /docs/atarist/GR-ST.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atarist/GR-ST.prg -------------------------------------------------------------------------------- /bin/gridrunner-bench.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/bin/gridrunner-bench.prg -------------------------------------------------------------------------------- /docs/c64/batalyx-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/batalyx-192.png -------------------------------------------------------------------------------- /docs/c64/batalyx-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/batalyx-512.png -------------------------------------------------------------------------------- /docs/c64/favicon-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/favicon-left.png -------------------------------------------------------------------------------- /docs/c64/gridrunner.s64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/gridrunner.s64 -------------------------------------------------------------------------------- /orig/gridrunnervic20.prg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/orig/gridrunnervic20.prg -------------------------------------------------------------------------------- /docs/c64/c64/c64_tiny.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/c64/c64_tiny.wasm -------------------------------------------------------------------------------- /docs/c64/favicon-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/favicon-right.png -------------------------------------------------------------------------------- /docs/c64/speaker-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/c64/speaker-white.png -------------------------------------------------------------------------------- /bin/patch-atari-end-byte.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/bin/patch-atari-end-byte.bin -------------------------------------------------------------------------------- /docs/atari800/gridrunner.atr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atari800/gridrunner.atr -------------------------------------------------------------------------------- /docs/atarist/tos-1.04-uk.rom: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/docs/atarist/tos-1.04-uk.rom -------------------------------------------------------------------------------- /orig/Gridrunner_1982_HesWare_US.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwenge/gridrunner/HEAD/orig/Gridrunner_1982_HesWare_US.pdf -------------------------------------------------------------------------------- /docs/c64/tips.js: -------------------------------------------------------------------------------- 1 | var tips = [ 2 | { 3 | "level": 2, 4 | "tip": "Gun and run from these guys, picking off one or two at a time", 5 | }, 6 | ]; 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/vic20/event.js: -------------------------------------------------------------------------------- 1 | var EventManager=new function(){var eventListeners={};this.listen=function(eventtype,func){if(!eventListeners[eventtype]){eventListeners[eventtype]=new Array();} 2 | eventListeners[eventtype].push(func);};this.event=function(event){if(eventListeners[event.type]){for(var i=0;i li > ul > li > a + ul'),function(index,value){$.each($('> a',$(value).closest('li')),function(index2,value2){$(value2).html($(value2).text()+">");})});} 4 | function binToArray(data){var v=0,cnt=0,out=[],ii=0;for(var i=0;i=8){out[ii++]=(v&255);cnt-=8;v>>=8;}} 5 | return out;} 6 | function toHex2(num){var r=num.toString(16);return "$"+("00"+r).substring(r.length).toUpperCase();} 7 | function toHex4(num){var r=num.toString(16);return "$"+("0000"+r).substring(r.length).toUpperCase();} 8 | function toHex6(num){var r=num.toString(16);return "$"+("000000"+r).substring(r.length).toUpperCase();} 9 | function toHexColour(num){var r=num.toString(16);return("000000"+r).substring(r.length).toUpperCase();} 10 | function toBin8(num){var r=num.toString(2);return "%"+("00000000"+r).substring(r.length);} 11 | function bin2String(array,start,length){var result="";for(var i=start;i= 8: 47 | writeChar(cur, cur_raw, char_count) 48 | char_count += 1 49 | cur_raw = [] 50 | 51 | cb = bytes[index] 52 | if cb == tag: 53 | addSequence(cur, index) 54 | index += 3 55 | cur_raw += bytes[i:i+3] 56 | continue 57 | 58 | if i in labels: 59 | o.write(labels[i] + '\n') 60 | 61 | b = bytes[i] 62 | cur_raw += [b] 63 | cur.append(b) 64 | index += 1 65 | 66 | 67 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean run 2 | 3 | D64_IMAGE = "bin/gridrunner.d64" 4 | XVIC_IMAGE = "bin/gridrunner-vic20.prg" 5 | XATARI_IMAGE = "bin/gridrunner.xex" 6 | XATARIST_IMAGE = "bin/gr-st.prg" 7 | X64 = x64 8 | XVIC = xvic 9 | X64SC = x64sc 10 | C1541 = c1541 11 | XATARI = atari800 12 | XATARIST = hatari 13 | 14 | all: clean d64 run 15 | vic: clean vic runvic 16 | 17 | gridrunner.prg: src/c64/gridrunner.asm 18 | 64tass -Wall -Wno-implied-reg --cbm-prg -o bin/gridrunner.prg -L bin/list-co1.txt -l bin/labels.txt src/c64/gridrunner.asm 19 | md5sum bin/gridrunner.prg bin/gridrunner-bench.prg 20 | 21 | gridrunner-vic20.prg: src/vic20/gridrunner.asm 22 | 64tass -Wall -Wno-implied-reg --cbm-prg -o bin/gridrunner-vic20.prg -L bin/list-co1.txt -l bin/labels.txt src/vic20/gridrunner.asm 23 | md5sum bin/gridrunner-vic20.prg orig/gridrunnervic20.prg 24 | 25 | gridrunner.xex: src/atari800/gridrunner.asm 26 | 64tass -Wall -Wno-implied-reg --atari-xex -o bin/gridrunner.xex -L bin/list-co1.txt -l bin/labels.txt src/atari800/gridrunner.asm 27 | # the original xex file has an incorrect end-byte which we need to patch here. 28 | dd if=bin/patch-atari-end-byte.bin of=bin/gridrunner.xex bs=1 seek=4 count=1 conv=notrunc 29 | md5sum bin/gridrunner.xex orig/gridrunner.xex 30 | 31 | gridrunner-st.prg: src/atarist/gridrunner.asm 32 | vasmm68k_mot -Ftos -spaces -devpac src/atarist/gridrunner.asm -o bin/gr-st.prg 33 | md5sum bin/gr-st.prg bin/gr-st-bench.prg 34 | 35 | sgr-st.prg: src/atarist-sg/gridrunner.asm 36 | vasmm68k_mot -Ftos -spaces -devpac src/atarist-sg/gridrunner.asm -o bin/sgr-st.prg 37 | md5sum bin/gr-st.prg bin/gr-st-bench.prg 38 | 39 | d64: gridrunner.prg 40 | $(C1541) -format "gridrunner,rq" d64 $(D64_IMAGE) 41 | $(C1541) $(D64_IMAGE) -write bin/gridrunner.prg "gridrunner" 42 | $(C1541) $(D64_IMAGE) -list 43 | 44 | runvic: gridrunner-vic20.prg 45 | $(XVIC) -verbose $(XVIC_IMAGE) 46 | 47 | runatari: gridrunner.xex 48 | $(XATARI) -win-height 800 -win-width 1200 $(XATARI_IMAGE) 49 | 50 | runatarist: gridrunner-st.prg 51 | $(XATARIST) $(XATARIST_IMAGE) 52 | 53 | run: d64 54 | $(X64) -verbose $(D64_IMAGE) 55 | 56 | clean: 57 | -rm $(D64_IMAGE) 58 | -rm $(XVIC_IMAGE) 59 | -rm bin/gridrunner.prg 60 | -rm bin/gridrunner.xex 61 | -rm bin/gridrunner-vic20.prg 62 | -rm bin/*.txt 63 | -------------------------------------------------------------------------------- /docs/atarist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Gridrunneri in the Atari ST in the browser | atari st emulator 5 | 27 | 28 | 29 |
30 |

Jeff Minter's Gridrunner Demo for the Atari ST

31 |
Downloading...
32 |
33 | 34 |
35 |

This is Gridrunner running in the Atari ST pce.js emulator.

36 |

Open Floppy Disk A and Double-Click on GR-ST.prg to run 'Gridrunner'

37 |

Use the Mouse Button to Fire and the Mouse to move around.

38 |
39 | 40 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /docs/c64/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
Downloading...
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 | 62 | 63 | 64 | 65 | 78 | 79 | -------------------------------------------------------------------------------- /docs/atarist/pce-config.cfg: -------------------------------------------------------------------------------- 1 | # pce-atarist.cfg 2 | # 3 | # Example config file 4 | 5 | 6 | # Add a directory to the end of the file search path. 7 | # 8 | # The search path is used if an input file is specified with 9 | # a relative file name. In that case the file is searched for 10 | # in all directories of the search path. The current directory 11 | # is always implicitly added to the end of the search path. 12 | # 13 | # If the first character of the string is a "-", the directory 14 | # is added to the beginning of the search path. 15 | path = "rom" 16 | path = "roms/rom" 17 | path = "-." 18 | 19 | 20 | system { 21 | # The ST model to emulate. Valid models are: 22 | # st: An Atari ST 23 | model = "st" 24 | 25 | # The monitor type. 26 | # If this is true, a mono monitor is attached, a color 27 | # monitor otherwise. 28 | mono = 0 29 | 30 | # Disable the startup memory test. 31 | fastboot = 1 32 | 33 | # Only update the screen every nth frame. A value of 1 34 | # skips every other frame and is a good compromise between 35 | # accuracy and emulation speed. 36 | frame_skip = 1 37 | 38 | # The parallel port character device. 39 | # Not all character drivers are supported on 40 | # all platforms. 41 | #parport = "null" 42 | #parport = "posix:file=parport.out" 43 | #parport = "pty:symlink=parport" 44 | parport = "stdio:file=parport.out:flush=1" 45 | #parport = "tios:file=/dev/ttyS0" 46 | #parport = "tcp:port=5555:telnet=0:telnetinit=1:usectl=1" 47 | 48 | # The serial port character device. 49 | #serport = "null" 50 | #serport = "pty:symlink=serport" 51 | serport = "stdio:file=serport.out:flush=1" 52 | #serport = "tios:file=/dev/ttyS0" 53 | #serport = "tcp:port=5556:telnet=1:telnetinit=1:usectl=1" 54 | 55 | # Raw MIDI data is written to this character driver. 56 | midi_raw = "stdio:file=midi-raw.out:flush=1" 57 | 58 | # MIDI data is written to this standard MIDI file. 59 | midi_smf = "midi-smf.smf" 60 | } 61 | 62 | cpu { 63 | # The CPU model. Valid models are "68000" and "68010". 64 | model = "68000" 65 | 66 | # The CPU speed multiplier. A value of 1 emulates a 67 | # 8 MHz CPU. A higher value emulates a faster CPU 68 | # but also takes up more host CPU time. A value of 0 69 | # dynamically adjusts the CPU speed. 70 | speed = 4 71 | } 72 | 73 | 74 | # Multiple "ram" sections may be present. 75 | ram { 76 | # The base address 77 | address = 0 78 | 79 | # The memory block size 80 | size = 512K 81 | } 82 | 83 | 84 | # Multiple "rom" sections may be present. 85 | rom { 86 | # The file from which the rom code is loaded 87 | file = "tos-1.04-uk.rom" 88 | 89 | # The base address 90 | address = 0xfc0000 91 | 92 | # The rom size 93 | size = 192K 94 | 95 | # The memory block is initialized with this value. 96 | default = 0xff 97 | } 98 | 99 | 100 | # All "load" sections are processed after all the "ram" and "rom" 101 | # sections. It is possible to overwrite read-only blocks, too. 102 | #load { 103 | # format = "srec" 104 | # file = "tos-1.04-us.srec" 105 | #} 106 | 107 | 108 | # Multiple "terminal" sections may be present. The first 109 | # one will be used unless a terminal type is specified 110 | # on the command line. 111 | terminal { 112 | driver = "sdl" 113 | 114 | # The terminal escape key. The default is "ESC". 115 | #escape = "CtrlRight" 116 | #escape = "ESC" 117 | #escape = "F12" 118 | #escape = "Menu" 119 | 120 | # The terminal scale factor. Only integral values are 121 | # allowed. 122 | scale = 2 123 | 124 | # Add a border around the image 125 | border = 0 126 | 127 | # Start in fullscreen mode. 128 | fullscreen = 0 129 | 130 | # The mouse speed. The host mouse speed is multiplied by 131 | # (mouse_mul_x / mouse_div_x) and (mouse_mul_y / mouse_div_y) 132 | mouse_mul_x = 1 133 | mouse_div_x = 1 134 | mouse_mul_y = 1 135 | mouse_div_y = 1 136 | } 137 | 138 | # terminal { 139 | # driver = "x11" 140 | 141 | # # The terminal escape key. The default is "ESC". 142 | # #escape = "CtrlRight" 143 | # #escape = "ESC" 144 | # #escape = "F12" 145 | # #escape = "Menu" 146 | 147 | # scale = 1 148 | 149 | # # The mouse speed 150 | # mouse_mul_x = 1 151 | # mouse_div_x = 1 152 | # mouse_mul_y = 1 153 | # mouse_div_y = 1 154 | # } 155 | 156 | 157 | fdc { 158 | file0 = "fd0.pri" 159 | } 160 | 161 | disk { 162 | drive = 0 163 | type = "auto" 164 | file = "gr.st" 165 | optional = 1 166 | } 167 | 168 | 169 | -------------------------------------------------------------------------------- /docs/vic20/vic20/keyboard.js: -------------------------------------------------------------------------------- 1 | $.extend(Config, new function() { 2 | this.joykeys = true; 3 | } 4 | ); 5 | var keysdown = [0, 0, 0, 0, 0, 0, 0, 0]; 6 | var up, down, left, right, fire; 7 | var pageup = 0; 8 | keymap = [[50], [52], [54], [56], [48], [187, 107, 61], [36], [118], [81], [69], [84], [85], [79], [219, 91], [-1], [116], [17], [83], [70], [72], [75], [186, 59], [220, 92], [114], [32], [90], [67], [66], [77], [190, 46], [16], [112], [20], [16], [88], [86], [78], [188, 44], [191, 47], [40], [9], [65], [68], [71], [74], [76], [222], [39], [192, 96], [87], [82], [89], [73], [80], [221, 93], [13], [49], [51], [53], [55], [57], [189, 109], [220, 92], [8]]; 9 | function KeyboardInit() { 10 | Config.joykeys = 1; 11 | up = down = left = right = fire = 0; 12 | KeyboardHook(); 13 | document.onhelp = function() { 14 | return (false); 15 | } 16 | window.onhelp = function() { 17 | return (false); 18 | } 19 | } 20 | function KeyboardHook() { 21 | document.onkeypress = function() { 22 | return false 23 | } 24 | ; 25 | document.onkeydown = keyDown; 26 | document.onkeyup = keyUp; 27 | } 28 | function KeyboardUnhook() { 29 | document.onkeypress = null 30 | document.onkeydown = null; 31 | document.onkeyup = null; 32 | } 33 | function keyDown(e) { 34 | var keyCode = document.all ? event.keyCode : e.which; 35 | cancelKeyEvent(e); 36 | applyKey(keyCode, false); 37 | return false; 38 | } 39 | function cancelKeyEvent(e) { 40 | if (window.event) { 41 | try { 42 | window.event.keyCode = 0; 43 | } catch (e) {} 44 | window.event.returnValue = false; 45 | window.event.cancelBubble = true; 46 | } 47 | if (e.preventDefault) 48 | e.preventDefault(); 49 | if (e.stopPropagation) 50 | e.stopPropagation(); 51 | } 52 | function keyUp(e) { 53 | var keyCode = document.all ? event.keyCode : e.which; 54 | cancelKeyEvent(e); 55 | applyKey(keyCode, true); 56 | return false; 57 | } 58 | function applyKey(sym, keyup) { 59 | if (Config.joykeys) { 60 | var isJoystick = true; 61 | if (sym == 37) 62 | left = !keyup; 63 | else if (sym == 39) 64 | right = !keyup; 65 | else if (sym == 38) 66 | up = !keyup; 67 | else if (sym == 40) 68 | down = !keyup; 69 | else if (sym == 192 || sym == 96 || sym == 17) 70 | fire = !keyup; 71 | else 72 | isJoystick = false; 73 | if (isJoystick) 74 | return; 75 | } 76 | if (sym == 33) { 77 | pageup = keyup ? 1 : 0; 78 | vic20.via2.ca1 = pageup; 79 | } 80 | for (var i = 0; i < 64; i++) { 81 | var kml = keymap[i].length; 82 | for (var j = 0; j < kml; j++) { 83 | if (keymap[i][j] == sym) { 84 | if (keyup) 85 | keysdown[7 - (i >> 3)] &= ~(1 << (i & 7)); 86 | else 87 | keysdown[7 - (i >> 3)] |= (1 << (i & 7)); 88 | } 89 | } 90 | } 91 | if (sym == 113) { 92 | if (keyup) { 93 | keysdown[3] &= ~0x02; 94 | keysdown[4] &= ~0x80; 95 | } else { 96 | keysdown[3] |= 0x02; 97 | keysdown[4] |= 0x80; 98 | } 99 | } else if (sym == 115) { 100 | if (keyup) { 101 | keysdown[3] &= ~0x02; 102 | keysdown[5] &= ~0x80; 103 | } else { 104 | keysdown[3] |= 0x02; 105 | keysdown[5] |= 0x80; 106 | } 107 | } else if (sym == 117) { 108 | if (keyup) { 109 | keysdown[3] &= ~0x02; 110 | keysdown[6] &= ~0x80; 111 | } else { 112 | keysdown[3] |= 0x02; 113 | keysdown[6] |= 0x80; 114 | } 115 | } else if (sym == 119) { 116 | if (keyup) { 117 | keysdown[3] &= ~0x02; 118 | keysdown[7] &= ~0x80; 119 | } else { 120 | keysdown[3] |= 0x02; 121 | keysdown[7] |= 0x80; 122 | } 123 | } else if (sym == 37) { 124 | if (keyup) { 125 | keysdown[3] &= ~0x02; 126 | keysdown[2] &= ~0x80; 127 | } else { 128 | keysdown[3] |= 0x02; 129 | keysdown[2] |= 0x80; 130 | } 131 | } else if (sym == 38) { 132 | if (keyup) { 133 | keysdown[3] &= ~0x02; 134 | keysdown[3] &= ~0x80; 135 | } else { 136 | keysdown[3] |= 0x02; 137 | keysdown[3] |= 0x80; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /docs/vic20/cpu/cpu6502disassemble.js: -------------------------------------------------------------------------------- 1 | var nems = ["BRK", "ORA X,i", "JAM", "SLO X,i", "NOP z", "ORA z", "ASL z", "SLO z", "PHP", "ORA #", "ASL", "ANC #", "NOP a", "ORA a", "ASL a", "SLO a", "BPL r", "ORA i,Y", "JAM", "SLO i,Y", "NOP z,X", "ORA z,X", "ASL z,X", "SLO z,X", "CLC", "ORA a,Y", "NOP", "SLO a,Y", "NOP a,X", "ORA a,X", "ASL a,X", "SLO a,X", "JSR a", "AND X,i", "JAM", "RLA X,i", "BIT z", "AND z", "ROL z", "RLA z", "BIT a", "AND #", "ROL", "ANC #", "BIT a", "AND a", "ROL a", "RLA a", "BMI r", "AND i,Y", "JAM", "RAL i,Y", "NOP z,X", "AND z,X", "ROL z,X", "RLA z,X", "SEC", "AND a,Y", "NOP", "RLA a,Y", "NOP a,X", "AND a,X", "ROL a,X", "RLA a,X", "RTI", "EOR X,i", "JAM", "SRE X,i", "NOP z", "EOR z", "LSR z", "SRE z", "PHA", "EOR #", "LSR", "ASR #", "JMP a", "EOR a", "LSR a", "SRA a", "BVC r", "EOR i,Y", "JAM", "SRE i,Y", "NOP z,X", "EOR z,X", "LSR z,X", "SRE z,X", "CLI", "EOR a,Y", "NOP", "SRE a,Y", "NOP a,X", "EOR a,X", "LSR a,X", "SRE a,X", "RTS", "ADC X,i", "JAM", "RRA X,i", "NOP z", "ADC z", "ROR z", "RRA z", "PLA", "ADC #", "ROR", "ARR #", "JMP i", "ADC a", "ROR a", "RRA a", "BVS r", "ADC i,Y", "JAM", "RRA i,Y", "NOP z,X", "ADC z,X", "ROR z,X", "SAX z", "SEI", "ADC a,Y", "NOP", "RRA a,Y", "NOP a,X", "ADC a,X", "ROR a,X", "RRA a,X", "NOP #", "STA X,i", "NOP #", "SAX X,i", "STY z", "STA z", "STX z", "SAX z,X", "DEY", "NOP #", "TXA", "ANE #", "STY a", "STA a", "STX a", "SAX a", "BCC r", "STA i,Y", "JAM", "SHA a,X", "STY z,X", "STA z,X", "STX z,Y", "LAX z", "TYA", "STA a,Y", "TXS", "SHS a,X", "SHY a,Y", "STA a,X", "SHX a,Y", "SHA a,Y", "LDY #", "LDA X,i", "LDX #", "LAX X,i", "LDY z", "LDA z", "LDX z", "LAX z,X", "TAY", "LDA #", "TAX", "LXA #", "LDY a", "LDA a", "LDX a", "LAX a", "BCS r", "LDA i,Y", "JAM", "LAX i,Y", "LDY z,X", "LDA z,X", "LDX z,Y", "DCP z", "CLV", "LDA a,Y", "TSX", "LAE a,Y", "LDY a,X", "LDA a,X", "LDX a,Y", "LAX a,Y", "CPY #", "CMP X,i", "NOP #", "DCP X,i", "CPY z", "CMP z", "DEC z", "DCP z,X", "INY", "CMP #", "DEX", "SBX #", "CPY a", "CMP a", "DEC a", "DCP a", "BNE r", "CMP i,Y", "JAM", "DCP i,Y", "NOP z,X", "CMP z,X", "DEC z,X", "DCP a,Y", "CLD", "CMP a,Y", "NOP", "DCP a,Y", "NOP a,X", "CMP a,X", "DEC a,X", "DCP a,X", "CPX #", "SBC X,i", "NOP #", "ISB X,i", "CPX z", "SBC z", "INC z", "ISB z", "INX", "SBC #", "NOP", "SBC #", "CPX a", "SBC a", "INC a", "ISB a", "BEQ r", "SBC i,Y", "JAM", "ISB i,Y", "NOP z,X", "SBC z,X", "INC z,X", "ISB z,X", "SED", "SBC a,Y", "NOP", "ISB a,Y", "NOP a,X", "SBC a,X", "INC a,X", "ISB a,X"]; 2 | var instructionLength = [2, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 3, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 1, 2, 1, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 3, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 1, 2, 2, 2, 2, 2, 1, 3, 1, 3, 3, 3, 3, 3, ]; 3 | var BRK = 0; 4 | var RTI = 0x40; 5 | var RTS = 0x60; 6 | var JMP = 0x4C; 7 | var JMPi = 0x6C; 8 | var JSR = 0x20; 9 | function isJumpInstruction(inst) { 10 | return (inst == JSR || inst == JMP); 11 | } 12 | function disassemble(pc, mem) { 13 | var originalPc = pc; 14 | var inst = mem[pc++]; 15 | if (inst === undefined) { 16 | inst = 0; 17 | } 18 | var n = nems[inst]; 19 | var nl = n.length > 4 ? n.substring(0, 4) : n; 20 | var result = ""; 21 | result += nl; 22 | var branch = -1; 23 | var reference = -1; 24 | if (n.length == 3) {} else if (n.indexOf(" A") != -1) { 25 | result += " A"; 26 | } else if (n.indexOf(" z,X") != -1) { 27 | var zp = mem[pc++ & 0xFFFF]; 28 | result += "$" + mem[zp].toString(16) + ",X"; 29 | } else if (n.indexOf(" z") != -1) { 30 | reference = mem[pc++ & 0xFFFF]; 31 | result += "$" + reference.toString(16); 32 | } else if (n.indexOf(" #") != -1) { 33 | result += "#$" + mem[pc++ & 0xFFFF].toString(16); 34 | } else if (n.indexOf(" a,X") != -1) { 35 | reference = mem[pc++ & 0xFFFF] | (mem[pc++ & 0xFFFF] << 8); 36 | result += "$" + reference.toString(16) + ",X"; 37 | } else if (n.indexOf(" a,Y") != -1) { 38 | reference = (mem[pc++ & 0xFFFF] | (mem[pc++ & 0xFFFF] << 8)); 39 | result += "$" + reference.toString(16) + ",Y"; 40 | } else if (n.indexOf(" a") != -1) { 41 | var val = (mem[pc++ & 0xFFFF] | (mem[pc++ & 0xFFFF] << 8)); 42 | result += "$" + val.toString(16); 43 | if (isJumpInstruction(inst)) { 44 | branch = val; 45 | } else { 46 | reference = val; 47 | } 48 | } else if (n.indexOf(" X,i") != -1) { 49 | result += "($" + mem[pc++ & 0xFFFF].toString(16) + ",X)"; 50 | } else if (n.indexOf(" i,Y") != -1) { 51 | result += "($" + mem[pc++ & 0xFFFF].toString(16) + "),Y"; 52 | } else if (n.indexOf(" i") != -1) { 53 | reference = (mem[pc++ & 0xFFFF] | (mem[pc++ & 0xFFFF] << 8)); 54 | result += "($" + reference.toString(16) + ")"; 55 | } else if (n.indexOf(" r") != -1) { 56 | var diff = mem[pc++ & 0xFFFF]; 57 | if (diff > 127) 58 | diff -= 256; 59 | result += "*" + diff; 60 | branch = (pc + diff) & 0xFFFF; 61 | } else { 62 | result += "Unknown instruction " + inst.toString(16); 63 | } 64 | var byteCount = pc - originalPc; 65 | var bytes = new Array(byteCount); 66 | var cnt = 0; 67 | for (var i = originalPc; i < pc; i++) { 68 | bytes[cnt++] = mem[i & 0xFFFF]; 69 | } 70 | if (inst == RTS || inst == RTI) { 71 | result = "" + result + ""; 72 | } 73 | return { 74 | "pc": originalPc, 75 | "bytes": bytes, 76 | "result": result, 77 | "branch": branch, 78 | "reference": reference 79 | }; 80 | } 81 | function disassembleToString(pc, mem) { 82 | var v = disassemble(pc, mem); 83 | return toHex4(v.pc) + " " + v.result + "\n"; 84 | } 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gridrunner (1982) by Jeff Minter 2 | 3 | 4 | 5 | [](https://github.com/mwenge/gridrunner/releases/download/v0.1/gridrunner2x.exe) 6 | [](https://github.com/mwenge/gridrunner/releases/download/v0.1/gridrunner2x.exe) 7 | [](https://mwenge.github.io/gridrunner/c64/) 8 | 9 | 10 | This is the reverse-engineered and [commented source code] for all known versions of Gridrunner by Jeff Minter. It is part of the [llamasource project](https://mwenge.github.io/llamaSource/). 11 | 12 | I wrote up my first attempt at reverse-engineering [the C64 version of the game here](Disassembling.md) 13 | 14 | If you want to read more about the Gridrunner internals, take a look at [Gridrunner; The Little Black Book](https://github.com/mwenge/llamaSource/blob/main/GridrunnerTheLittleBlackBook.md) where I try to unpack the workings and design of the various versions of Gridrunner Minter wrote. 15 | 16 | 17 | 18 | 19 | * [Play in your Browser](#play-in-your-browser) 20 | * [Building the C64 Source Code](#building-the-c64-source-code) 21 | * [Requirements](#requirements) 22 | * [Setup](#setup) 23 | * [Compiling](#compiling) 24 | * [Building the Vic20 Source Code](#building-the-vic20-source-code) 25 | * [Requirements](#requirements-1) 26 | * [Compiling](#compiling-1) 27 | * [Building the Atari-8 bit Source Code](#building-the-atari-8-bit-source-code) 28 | * [Requirements](#requirements-2) 29 | * [Compiling](#compiling-2) 30 | * [Building the Atari ST Source Code](#building-the-atari-st-source-code) 31 | * [Requirements](#requirements-3) 32 | * [Setup](#setup-1) 33 | * [Compiling](#compiling-3) 34 | * [Conversion to the Nintendo Entertainment System](#conversion-to-the-nintendo-entertainment-system) 35 | 36 | 37 | 38 | ## Play in your Browser 39 | [C64:](https://mwenge.github.io/gridrunner/c64/) (Ctrl key is 'Fire', Arrow Keys to move.) 40 | 41 | [Vic20:](https://mwenge.github.io/gridrunner/vic20/) (Ctrl key is 'Fire', Arrow Keys to move.) 42 | 43 | [Atari800:](https://mwenge.github.io/gridrunner/atari800/?disk_filename=gridrunner.atr) (Alt key is 'Fire', Arrow Keys to move.) 44 | 45 | [Atari ST:](https://mwenge.github.io/gridrunner/atarist/) (Mouse to fire and move.) 46 | 47 | ## Building the C64 Source Code 48 | 49 | ### Requirements 50 | * [VICE][vice] - The most popular C64 emulator 51 | * [64tass][64tass] - An assembler for 6502 source code. 52 | 53 | ### Setup 54 | On Ubuntu you can install [VICE] as follows: 55 | ``` 56 | sudo apt install vice 57 | ``` 58 | 59 | ### Compiling 60 | To compile and run: 61 | 62 | ```sh 63 | $ make 64 | ``` 65 | 66 | To just compile the game and get a binary (`gridrunner.prg`) do: 67 | 68 | ```sh 69 | $ make gridrunner.prg 70 | ``` 71 | 72 | ## Building the Vic20 Source Code 73 | 74 | ### Requirements 75 | * [VICE][vice] - The most popular C64/Vic20 emulator 76 | * [64tass][64tass] - An assembler for 6502 source code. 77 | 78 | ### Compiling 79 | To compile and run: 80 | 81 | ```sh 82 | $ make runvic 83 | ``` 84 | 85 | To just compile the game and get a binary (`gridrunner-vic20.prg`) do: 86 | 87 | ```sh 88 | $ make gridrunner-vic20.prg 89 | ``` 90 | 91 | ## Building the Atari-8 bit Source Code 92 | 93 | ### Requirements 94 | * [Atari800 Emulator][atari800] - An Atari 400/8000 emulator 95 | * [64tass][64tass] - An assembler for 6502 source code. 96 | 97 | ### Compiling 98 | To compile and run: 99 | 100 | ```sh 101 | $ make runatari 102 | ``` 103 | 104 | To just compile the game and get a binary (`gridrunner.xex`) do: 105 | 106 | ```sh 107 | $ make gridrunner.xex 108 | ``` 109 | 110 | ## Building the Atari ST Source Code 111 | 112 | 113 | This unfinished and unpublished gem was written as a challenge by Minter to see what he could fit into 3.5k on the Atari ST. It's a basic gameplay demo with no sound or levels, but is very enjoyable and addictive to play. 114 | 115 | "There was one more version of Gridrunner on the Atari ST 116 | which I shall mention for completeness - in truth I haven't seen it for 117 | years and I'm not sure if it's in the archive anywhere. I can't remember 118 | why I was asked - for a coverdisk or for a demo I guess - but I was 119 | asked to do a game in 4K. Since the dear old unexpanded Vic had been 120 | 3.5K I thought it would be natural to choose Gridrunner as the subject 121 | of the demo and so I made a little version that fit in 4K on the ST. It 122 | was super primitive but it was just about fully functional." 123 | 124 | ### Requirements 125 | * [Hatari][hatari] - A popular Atari ST emulator 126 | * [vasm][vasm] - An assembler for Motorola 68000 source code 127 | 128 | ### Setup 129 | On Ubuntu you can install [Hatari] as follows: 130 | ``` 131 | sudo apt install hatari 132 | ``` 133 | 134 | You will need to download the source of [vasm] and compile it as follows: 135 | ``` 136 | make CPU=m68k SYNTAX=mot 137 | ``` 138 | 139 | ### Compiling 140 | To compile and run: 141 | 142 | ```sh 143 | $ make runatarist 144 | ``` 145 | 146 | To just compile the game and get a binary (`gridrunner-st.prg`) do: 147 | 148 | ```sh 149 | $ make gridrunner-st.prg 150 | ``` 151 | ## Conversion to the Nintendo Entertainment System 152 | 153 | 154 | I converted Gridrunner to the NES. [You can try it out here](https://github.com/mwenge/gridnes). 155 | 156 | [64tass]: http://tass64.sourceforge.net/ 157 | [vice]: http://vice-emu.sourceforge.net/ 158 | [atari800]: https://atari800.github.io/ 159 | [hatari]: https://hatari.tuxfamily.org/download.html 160 | [vasm]: http://sun.hasenbraten.de/vasm/index.php?view=relsrc 161 | [https://gridrunner.xyz]: https://mwenge.github.io/gridrunner.xyz 162 | [commented source code]:https://github.com/mwenge/gridrunner/blob/master/src/ 163 | 164 | -------------------------------------------------------------------------------- /docs/atari800/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | jsA8E Demo 7 | 105 | 106 | 107 | 108 |
109 |
Downloading...
110 | 111 |
112 | 113 |
114 | 115 | 116 |
117 | 118 |
119 | 120 | 121 | 195 | 196 | 197 | Resize canvas 198 | Lock/hide mouse pointer     199 | 201 | 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /docs/vic20/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Javascript Vic20 Emulator 6 | 7 | 8 | 9 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |   46 | 47 | 67 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /docs/vic20/peripheral/via6522.js: -------------------------------------------------------------------------------- 1 | function Via6522(startAddress, vianame) { 2 | console.log("Init VIA at " + startAddress.toString(16)); 3 | var ORA = 1; 4 | var DDRA = 3; 5 | var SR = 10; 6 | var t1c, t1l, t2l_l, t2c; 7 | var reg = new Array(16); 8 | var hasPreCycled = false; 9 | var inhibitT1Interrupt, inhibitT2Interrupt, acrTimedCountdown; 10 | var ca2, lastca1, cb1, cb2; 11 | this.reset = function() { 12 | console.log("Vic reset"); 13 | for (var i = 0; i < reg.length; i++) 14 | reg[i] = 0; 15 | for (var i = 4; i < 10; i++) 16 | reg[i] = 0xFF; 17 | t1l = t1c = t2c = 0xFFFF; 18 | t2l_l = 0xFF; 19 | inhibitT1Interrupt = inhibitT2Interrupt = true; 20 | this.pins_ira = 0; 21 | this.pins_irb = 0; 22 | this.ca1 = true; 23 | this.lastca1 = true; 24 | ca2 = true; 25 | newca1 = true; 26 | newca2 = true; 27 | cb1 = false; 28 | cb2 = false; 29 | acrTimedCountdown = true; 30 | this.hasInterrupt = 0; 31 | hasPreCycled = false; 32 | } 33 | ; 34 | this.debugInformation = function() { 35 | var html = ""; 36 | html += ""; 37 | html += ""; 38 | html += ""; 39 | html += ""; 40 | html += ""; 41 | html += ""; 42 | html += ""; 43 | html += ""; 44 | html += ""; 45 | html += ""; 46 | html += ""; 47 | var reg11 = this.invisibleRead(11); 48 | var t1control = ["Timed Int.", "Cont. Int", "Timed Int. + PB7 One shot", "Cont. Int. + PB7 Square wave"]; 49 | var t2control = ["Timed Int.", "Count Down PB6"]; 50 | var srControl = ["Disabled ", "Shift in T2", "Shift in PHI2", "Shift in ext. clock", "Shift out free run T2", "Shift out T2", "Shift out PHI2", "Shift out ext. clock"]; 51 | html += ""; 52 | html += ""; 53 | html += ""; 54 | html += ""; 55 | html += ""; 56 | html += ""; 57 | var reg12 = this.invisibleRead(12); 58 | var cb2Control = reg12 >> 5; 59 | var cb1Control = (reg12 >> 4) & 1; 60 | var ca2Control = (reg12 >> 1) & 7; 61 | var ca1Control = reg12 & 1; 62 | var ca2andcb2Strings = ["Input-negative active edge", "Independent interrupt input-negative edge*", "Input-positive active edge", "Independent interrupt input-positive edge*", "Handshake output", "Pulse output", "Low output", "High output"]; 63 | var ca1andcb1Strings = ["Interrupt -ve edge", "Interrupt +ve edge"]; 64 | html += ""; 65 | html += ""; 66 | html += ""; 67 | html += ""; 68 | html += ""; 69 | html += ""; 70 | html += ""; 71 | html += "
" + vianame + "(" + startAddress.toString(16) + ")
ORB:" + toBin8(this.invisibleRead(0)) + "
ORA:" + toBin8(this.invisibleRead(1)) + "
DDRB:" + toBin8(this.invisibleRead(2)) + "
DDRB:" + toBin8(this.invisibleRead(3)) + "
T1C:" + toHex4(t1c) + "
T1L:" + toHex4(t1l) + "
T2C:" + toHex4(t2c) + "
T2L:" + toHex2(t2l_l) + "
SR:" + toHex2(this.invisibleRead(10)) + "
ACR:" + toBin8(this.invisibleRead(11)) + "
ACR-T1 Control:" + t1control[reg11 >> 6] + "
ACR-T2 Control:" + t2control[(reg11 >> 5) & 1] + "
ACR-SR Control:" + srControl[(reg11 >> 2) & 7] + "
ACR-PA Latch:" + (((reg11 >> 1) & 1) ? "Yes" : "No") + "
ACR-PB Latch:" + ((reg11 & 1) ? "Yes" : "No") + "
PCR:" + toHex2(reg12) + "
PCR-CB1:" + ca1andcb1Strings[cb1Control] + "
PCR-CB2:" + ca2andcb2Strings[cb2Control] + "
PCR-CA1:" + ca1andcb1Strings[ca1Control] + "
PCR-CA2:" + ca2andcb2Strings[ca2Control] + "
IFR:" + toBin8(this.invisibleRead(13)) + "
IER:" + toBin8(this.invisibleRead(14)) + "
"; 72 | return html; 73 | } 74 | this.cycleUp = function() { 75 | if (t1c == -1) { 76 | if (!inhibitT1Interrupt) { 77 | reg[13] |= 0x40; 78 | inhibitT1Interrupt = true; 79 | } 80 | } 81 | if (t2c == -1) { 82 | if (acrTimedCountdown && !inhibitT2Interrupt) { 83 | reg[13] |= 0x20; 84 | inhibitT2Interrupt = true; 85 | } 86 | } 87 | if (reg[12] & 1) { 88 | if (this.ca1 != this.lastca1) { 89 | console.debug(this.ca1); 90 | this.lastca1 = this.ca1; 91 | if (this.ca1) { 92 | reg[13] |= 2; 93 | } 94 | } 95 | } 96 | this.hasInterrupt = reg[14] & reg[13] & 0x7F; 97 | } 98 | ; 99 | this.cycleDown = function() { 100 | if (hasPreCycled) { 101 | hasPreCycled = false; 102 | return; 103 | } 104 | if (t1c-- == -1) { 105 | if (reg[11] & 0x40) { 106 | t1c = t1l; 107 | inhibitT1Interrupt = false; 108 | } else { 109 | t1c = 0xFFFE; 110 | } 111 | } 112 | if (t2c-- == -1) { 113 | t2c = 0xFFFE; 114 | } 115 | if ((~reg[12]) & 1) { 116 | if (this.ca1 != this.lastca1) { 117 | this.lastca1 = this.ca1; 118 | if (!this.ca1) { 119 | reg[13] |= 2; 120 | } 121 | } 122 | } 123 | } 124 | ; 125 | this.read = function(regnum) { 126 | regnum &= 0xF; 127 | switch (regnum) { 128 | case 0: 129 | { 130 | var ddrb = reg[2]; 131 | var pins_in = (~this.pins_irb) & (~ddrb); 132 | var reg_in = reg[0] & ddrb; 133 | return (pins_in | reg_in) & 0xFF; 134 | } 135 | case 1: 136 | var ic = (reg[12] >> 1) & 7; 137 | reg[13] &= (ic != 1 && ic != 3) ? ~3 : ~1; 138 | case 15: 139 | return (~this.pins_ira) & (~reg[DDRA]) & 0xFF; 140 | case 4: 141 | { 142 | reg[13] &= ~0x40; 143 | inhibitT1Interrupt = false; 144 | return t1c & 0xFF; 145 | } 146 | case 5: 147 | { 148 | return (t1c >> 8) & 0xFF; 149 | } 150 | case 6: 151 | { 152 | return t1l & 0xFF; 153 | } 154 | case 7: 155 | { 156 | return (t1l >> 8) & 0xFF; 157 | } 158 | case 8: 159 | { 160 | reg[13] &= ~0x20; 161 | inhibitT2Interrupt = false; 162 | return t2c & 0xFF; 163 | } 164 | case 9: 165 | { 166 | return (t2c >> 8) & 0xFF; 167 | } 168 | case 13: 169 | { 170 | var result = reg[13] & 0x7F; 171 | return result ? (result | 0x80) : result; 172 | } 173 | case 14: 174 | return reg[14] | 0x80; 175 | default: 176 | return reg[regnum]; 177 | } 178 | } 179 | ; 180 | this.getReg = function(regnum) { 181 | return reg[regnum]; 182 | } 183 | ; 184 | this.invisibleRead = function(regnum) { 185 | regnum &= 15; 186 | switch (regnum) { 187 | case 4: 188 | { 189 | return t1c & 0xFF; 190 | } 191 | case 5: 192 | { 193 | return (t1c >> 8) & 0xFF; 194 | } 195 | case 6: 196 | { 197 | return t1l & 0xFF; 198 | } 199 | case 7: 200 | { 201 | return (t1l >> 8) & 0xFF; 202 | } 203 | case 8: 204 | { 205 | return t2c & 0xFF; 206 | } 207 | case 9: 208 | { 209 | return (t2c >> 8) & 0xFF; 210 | } 211 | case 15: 212 | { 213 | return reg[1]; 214 | } 215 | default: 216 | return reg[regnum]; 217 | } 218 | } 219 | this.write = function(regnum, value) { 220 | this.cycleDown(); 221 | hasPreCycled = true; 222 | switch (regnum & 15) { 223 | case 0: 224 | reg[0] = value; 225 | break; 226 | case 1: 227 | var ic = (reg[12] >> 1) & 7; 228 | reg[13] &= (ic != 1 && ic != 3) ? ~3 : ~1; 229 | case 15: 230 | reg[ORA] = value; 231 | break; 232 | case 2: 233 | reg[2] = value; 234 | break; 235 | case 3: 236 | reg[3] = value; 237 | break; 238 | case 4: 239 | t1l = (t1l & ~0xFF) | value; 240 | break; 241 | case 5: 242 | value <<= 8; 243 | t1l = (t1l & 0xFF) | value; 244 | t1c = t1l; 245 | reg[13] &= ~0x40; 246 | inhibitT1Interrupt = false; 247 | break; 248 | case 6: 249 | t1l = (t1l & 0xFF00) | value; 250 | break; 251 | case 7: 252 | value <<= 8; 253 | t1l = (t1l & 0xFF) | value; 254 | reg[13] &= ~0x40; 255 | break; 256 | case 8: 257 | t2l_l = value; 258 | break; 259 | case 9: 260 | t2c = (value << 8) | t2l_l; 261 | reg[13] &= ~0x20; 262 | inhibitT2Interrupt = false; 263 | break; 264 | case 10: 265 | reg[10] = value; 266 | break; 267 | case 11: 268 | reg[11] = value; 269 | acrTimedCountdown = (reg[11] & 0x20) == 0; 270 | break; 271 | case 12: 272 | reg[12] = value; 273 | break; 274 | case 13: 275 | reg[13] &= ~value; 276 | break; 277 | case 14: 278 | if (value & 0x80) { 279 | reg[14] |= value; 280 | } else { 281 | reg[14] &= ~value; 282 | } 283 | break; 284 | default: 285 | reg[1] = value; 286 | break; 287 | } 288 | } 289 | this.reset(); 290 | } 291 | -------------------------------------------------------------------------------- /docs/vic20/vic20/basic.js: -------------------------------------------------------------------------------- 1 | function loadPrgFromBasicString(basic) { 2 | var prgData = new Array(); 3 | var startAddress = 0x1001; 4 | if (Config.memoryAt0400) { 5 | startAddress = 0x0401; 6 | } else if (Config.memoryAt2000) { 7 | startAddress = 0x1201; 8 | } 9 | prgData.push(basicToPrg(startAddress, basic)); 10 | loadPrgFromData(prgData); 11 | } 12 | var escCodes = { 13 | "SPACE": 0x20, 14 | "WHT": 0x05, 15 | "RED": 0x1C, 16 | "GRN": 0x1E, 17 | "BLU": 0x1F, 18 | "BLK": 0x90, 19 | "PUR": 0x9C, 20 | "YEL": 0x9E, 21 | "CYN": 0x9F, 22 | "HOME": 0x13, 23 | "DOWN": 0x11, 24 | "LEFT": 0x9D, 25 | "RIGHT": 0x1D, 26 | "UP": 0x91, 27 | "CLR": 0x93, 28 | "RVS ON": 0x12, 29 | "RVS OFF": 0x92, 30 | "RVS": 0x12 31 | }; 32 | var tokens = { 33 | "END": 128, 34 | "FOR": 129, 35 | "NEXT": 130, 36 | "DATA": 131, 37 | "INPUT#": 132, 38 | "INPUT": 133, 39 | "DIM": 134, 40 | "READ": 135, 41 | "LET": 136, 42 | "GOTO": 137, 43 | "RUN": 138, 44 | "IF": 139, 45 | "RESTORE": 140, 46 | "GOSUB": 141, 47 | "RETURN": 142, 48 | "REM": 143, 49 | "STOP": 144, 50 | "ON": 145, 51 | "WAIT": 146, 52 | "LOAD": 147, 53 | "SAVE": 148, 54 | "VERIFY": 149, 55 | "DEF": 150, 56 | "POKE": 151, 57 | "PRINT#": 152, 58 | "PRINT": 153, 59 | "CONT": 154, 60 | "LIST": 155, 61 | "CLR": 156, 62 | "CMD": 157, 63 | "SYS": 158, 64 | "OPEN": 159, 65 | "CLOSE": 160, 66 | "GET": 161, 67 | "NEW": 162, 68 | "TAB(": 163, 69 | "TO": 164, 70 | "FN": 165, 71 | "SPC(": 166, 72 | "THEN": 167, 73 | "NOT": 168, 74 | "STEP": 169, 75 | "+": 170, 76 | "-": 171, 77 | "*": 172, 78 | "/": 173, 79 | "^": 174, 80 | "AND": 175, 81 | "OR": 176, 82 | ">": 177, 83 | "=": 178, 84 | "<": 179, 85 | "SGN": 180, 86 | "INT": 181, 87 | "ABS": 182, 88 | "USR": 183, 89 | "FRE": 184, 90 | "POS": 185, 91 | "SQR": 186, 92 | "RND": 187, 93 | "LOG": 188, 94 | "EXP": 189, 95 | "COS": 190, 96 | "SIN": 191, 97 | "TAN": 192, 98 | "ATN": 193, 99 | "PEEK": 194, 100 | "LEN": 195, 101 | "STR$": 196, 102 | "VAL": 197, 103 | "ASC": 198, 104 | "CHR$": 199, 105 | "LEFT$": 200, 106 | "RIGHT$": 201, 107 | "MID$": 202, 108 | "GO": 203, 109 | "PRINT": 153, 110 | "~": 255 111 | }; 112 | var vicmap = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ' ', '!', '"', '#', '$', '%', '&', '.', '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '', ']', '', '']; 113 | var reverseTokens = {} 114 | , reverseTokensEsc = {}; 115 | for (var key in tokens) { 116 | reverseTokens[tokens[key]] = " " + key + " "; 117 | } 118 | for (var key in escCodes) { 119 | reverseTokensEsc[escCodes[key]] = "[" + key + "]"; 120 | } 121 | for (var i = 0; i < vicmap.length; i++) { 122 | if (vicmap[i] != '') { 123 | reverseTokens[i] = (vicmap[i] == '?') ? 'PRINT' : vicmap[i]; 124 | reverseTokensEsc[i] = vicmap[i]; 125 | } 126 | } 127 | var asciimap = new Array(256); 128 | for (var i = 0; i < vicmap.length; i++) 129 | asciimap[vicmap[i]] = i; 130 | var databasic = new Array(); 131 | function trim(str) { 132 | return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 133 | } 134 | function memoryToAsciiBasic(mem) { 135 | var basicStart = mem[43] + (mem[44] << 8); 136 | console.debug("basic start", basicStart.toString(16)); 137 | var nextLineOffset = basicStart; 138 | result = ""; 139 | for (; ; ) { 140 | nextLineOffset = mem[basicStart++] + (mem[basicStart++] << 8); 141 | console.debug("nextLineOffset", nextLineOffset.toString(16)); 142 | if (nextLineOffset == 0) 143 | break; 144 | var lineNumber = mem[basicStart++] + (mem[basicStart++] << 8); 145 | console.debug("lineNumber", lineNumber); 146 | result += "
" + lineNumber + " "; 147 | var ch; 148 | var inQuotes = false; 149 | while (ch = mem[basicStart++]) { 150 | if (!inQuotes) { 151 | var rt = reverseTokens[ch]; 152 | result += rt; 153 | if (rt == '"') { 154 | inQuotes = true; 155 | } 156 | } else { 157 | var rt = reverseTokensEsc[ch]; 158 | if (rt == undefined) { 159 | console.debug(ch, "undefined"); 160 | } 161 | result += rt; 162 | if (rt == '"') { 163 | inQuotes = false; 164 | } 165 | } 166 | } 167 | result += "
\n"; 168 | } 169 | return result; 170 | } 171 | var rvson = false; 172 | function asiiToChar(ch) { 173 | if (ch < 0x20) { 174 | if (ch == 0x12) 175 | rvson = true; 176 | return ch | 0x80; 177 | } else { 178 | if (ch >= 0x60) { 179 | ch &= 0xDF; 180 | } else { 181 | ch &= 0x3F; 182 | } 183 | } 184 | return ch; 185 | } 186 | function memoryToAsciiBasic2(mem) { 187 | var basicStart = mem[43] + (mem[44] << 8); 188 | var nextLineOffset = basicStart; 189 | var lineCount = 0; 190 | for (; ; ) { 191 | nextLineOffset = mem[nextLineOffset] + (mem[nextLineOffset + 1] << 8); 192 | console.debug("nextLineOffset", nextLineOffset.toString(16)); 193 | if (nextLineOffset == 0) 194 | break; 195 | lineCount++; 196 | } 197 | var canvas = $("#debugContent_basicprogram_canvas")[0]; 198 | canvas.height = lineCount * 8; 199 | var context = canvas.getContext("2d"); 200 | var data = context.getImageData(0, 0, canvas.width, canvas.height); 201 | for (var loop = 0; loop < data.width * data.height * 4; loop++) 202 | data.data[loop] = 0xFF; 203 | var basicStart = mem[43] + (mem[44] << 8); 204 | console.debug("basic start", basicStart.toString(16)); 205 | var nextLineOffset = basicStart; 206 | result = ""; 207 | var funcBase = 0xC09E; 208 | var ox = 0; 209 | var oy = 0; 210 | for (; ; ) { 211 | rvson = false; 212 | nextLineOffset = mem[basicStart++] + (mem[basicStart++] << 8); 213 | console.debug("nextLineOffset", nextLineOffset.toString(16)); 214 | if (nextLineOffset == 0) 215 | break; 216 | var lineNumber = mem[basicStart++] + (mem[basicStart++] << 8); 217 | var st = "" + lineNumber; 218 | for (var n = 0; n < st.length; n++) { 219 | drawChar(data, st.charCodeAt(n), ox++, oy); 220 | } 221 | drawChar(data, 0x60, ox++, oy); 222 | var ch; 223 | var inQuotes = false; 224 | while (ch = mem[basicStart++]) { 225 | if (!inQuotes) { 226 | if (ch < 128) { 227 | ch = asiiToChar(ch); 228 | if (ch != -1) 229 | drawChar(data, ch, ox++, oy); 230 | } else { 231 | var dex = funcBase; 232 | ch &= 127; 233 | while (ch-- > 0) { 234 | while (mem[dex++] < 128) 235 | ; 236 | } 237 | var endloop = false; 238 | for (; endloop == false; dex++) { 239 | var ch2 = mem[dex]; 240 | if (ch2 >= 128) { 241 | ch2 &= 0x7F; 242 | endloop = true; 243 | } 244 | ch2 = asiiToChar(ch2); 245 | if (ch2 != -1) 246 | drawChar(data, ch2, ox++, oy); 247 | } 248 | } 249 | if (ch == 0x22) { 250 | inQuotes = true; 251 | } 252 | } else { 253 | if (ch == 0x22) { 254 | inQuotes = false; 255 | rvson = false; 256 | } 257 | if (ch < 128) { 258 | ch = asiiToChar(ch); 259 | if (ch != -1) 260 | drawChar(data, ch, ox++, oy); 261 | } 262 | } 263 | } 264 | oy++; 265 | ox = 0; 266 | } 267 | context.putImageData(data, 0, 0); 268 | } 269 | function drawChar(data, ch, ox, oy) { 270 | var charMap = 0x8000 + ch * 8; 271 | var onColour = 0X2713D0; 272 | var offColour = 0xFFFFFF; 273 | for (var y = 0; y < 8; y++) { 274 | var bite = mem[charMap + y]; 275 | for (var x = 0; x < 8; x++) { 276 | var bit = (bite & 128) ? onColour : offColour; 277 | var xx = ox * 8 + x; 278 | var yy = oy * 8 + y; 279 | data.data[yy * 4 * data.width + xx * 4] = bit >> 16; 280 | data.data[yy * 4 * data.width + xx * 4 + 1] = (bit >> 8) & 0xFF; 281 | data.data[yy * 4 * data.width + xx * 4 + 2] = bit & 0xFF; 282 | bite <<= 1; 283 | } 284 | } 285 | } 286 | function basicToPrg(startAddress, basic) { 287 | var cnt = 0; 288 | var databasic = new Array(); 289 | databasic[cnt++] = startAddress & 255; 290 | databasic[cnt++] = startAddress >> 8; 291 | basic = basic.replace(/\r/g, "\n").replace(/\n\n/g, "\n"); 292 | var lines = basic.toUpperCase().split("\n"); 293 | for (var i = 0; i < lines.length; i++) { 294 | var line = trim(lines[i]); 295 | var lineNumber = parseInt(lines[i]); 296 | if (lineNumber == null || lineNumber.toString() == "NaN") {} else { 297 | var lineAddressBase = cnt; 298 | databasic[cnt++] = 0; 299 | databasic[cnt++] = 0; 300 | databasic[cnt++] = lineNumber & 255; 301 | databasic[cnt++] = lineNumber >> 8; 302 | line = trim(line.substring(lineNumber.toString().length)); 303 | var inQuotes = false 304 | , inCurls = false 305 | , inData = false; 306 | while (line.length != 0) { 307 | var addedToken = false; 308 | if (inQuotes && inCurls) { 309 | for (escCode in escCodes) { 310 | if (line.indexOf(escCode) == 0) { 311 | addedToken = true; 312 | databasic[cnt++] = escCodes[escCode]; 313 | line = line.substring(escCode.length); 314 | } 315 | } 316 | } else if (!inQuotes) { 317 | for (token in tokens) { 318 | if (line.indexOf(token) == 0) { 319 | if (token == '-' && inData) 320 | continue; 321 | addedToken = true; 322 | if (token == 'DATA') 323 | inData = true; 324 | databasic[cnt++] = tokens[token]; 325 | line = line.substring(token.length); 326 | } 327 | } 328 | } 329 | if (!addedToken) { 330 | var rch = line.charCodeAt(0); 331 | if (inCurls) { 332 | if (rch == '}'.charCodeAt(0)) { 333 | inCurls = false; 334 | } 335 | } else { 336 | if (!inCurls && rch == '{'.charCodeAt(0)) { 337 | inCurls = true; 338 | } else { 339 | if (rch == ':'.charCodeAt(0)) 340 | inData = false; 341 | if (rch == '"'.charCodeAt(0)) 342 | inQuotes = !inQuotes; 343 | var ch = vicmap[rch]; 344 | if (typeof (ch) != 'undefined' && ch != '') 345 | databasic[cnt++] = ch.charCodeAt(0); 346 | } 347 | } 348 | line = line.substring(1); 349 | } 350 | } 351 | databasic[cnt++] = 0; 352 | var lineAddress = startAddress + databasic.length - 2; 353 | databasic[lineAddressBase++] = lineAddress & 255; 354 | databasic[lineAddressBase++] = lineAddress >> 8; 355 | } 356 | } 357 | databasic[cnt++] = 0; 358 | databasic[cnt++] = 0; 359 | return databasic; 360 | } 361 | -------------------------------------------------------------------------------- /docs/vic20/vic20/vic20.js: -------------------------------------------------------------------------------- 1 | var Config = new function() { 2 | this.memoryAt0400 = false; 3 | this.memoryAt2000 = false; 4 | this.memoryAt4000 = false; 5 | this.memoryAt6000 = false; 6 | this.memoryAtA000 = false; 7 | this.speed = 1; 8 | this.soundChannel1Enabled = true; 9 | this.soundChannel2Enabled = true; 10 | this.soundChannel3Enabled = true; 11 | this.soundChannel4Enabled = true; 12 | } 13 | ; 14 | var useUint8Array = typeof (Uint8Array) != 'undefined'; 15 | var mem = useUint8Array ? new Uint8Array(65536) : new Array(65536); 16 | var memExec = useUint8Array ? new Uint8Array(65536) : new Array(65536); 17 | var memBp = useUint8Array ? new Uint8Array(65536) : new Array(65536); 18 | var bincodes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.!"; 19 | cmset = undefined; 20 | function Vic20() { 21 | var self = this; 22 | $("body").click(function() { 23 | self.vic.initSound() 24 | }); 25 | this.isPal = false; 26 | var readFuncs = new Array(65536 >> 4); 27 | var invisibleReadFuncs = new Array(65536 >> 4); 28 | var writeFuncs = new Array(65536 >> 4); 29 | var invisibleWriteFuncs = new Array(65536 >> 4); 30 | this.nextFrameTime = 0; 31 | this.readDebug = function(offset) { 32 | memExec[offset] = 8; 33 | return readFuncs[offset >> 4](offset); 34 | } 35 | this.readNormal = function(offset) { 36 | return readFuncs[offset >> 4](offset); 37 | } 38 | this.read = this.readDebug; 39 | this.invisibleRead = function(offset) { 40 | return invisibleReadFuncs[offset >> 4](offset); 41 | } 42 | this.writeDebug = function(offset, value) { 43 | memExec[offset] = 16; 44 | writeFuncs[offset >> 4](offset, value); 45 | } 46 | this.writeNormal = function(offset, value) { 47 | writeFuncs[offset >> 4](offset, value); 48 | } 49 | this.write = this.writeDebug; 50 | this.invisibleWrite = function(offset, value) { 51 | invisibleWriteFuncs[offset >> 4](offset, value); 52 | } 53 | function read0(offset) { 54 | return 0; 55 | } 56 | function readMem(offset) { 57 | return mem[offset]; 58 | } 59 | function readVia1(offset) { 60 | return vic20.via1.read(offset); 61 | } 62 | function invisibleReadVia1(offset) { 63 | return vic20.via1.invisibleRead(offset); 64 | } 65 | function readVia2(offset) { 66 | return vic20.via2.read(offset); 67 | } 68 | function invisibleReadVia2(offset) { 69 | return vic20.via2.invisibleRead(offset); 70 | } 71 | function readVic(offset) { 72 | return vic20.vic.read(offset); 73 | } 74 | function invisibleReadVic(offset) { 75 | return vic20.vic.invisibleRead(offset); 76 | } 77 | function write0() {} 78 | function writeMem(offset, value) { 79 | mem[offset] = value; 80 | } 81 | function writeVia1(offset, value) { 82 | vic20.via1.write(offset, value); 83 | } 84 | function invisibleWriteVia1(offset, value) { 85 | vic20.via1.invisibleWrite(offset, value); 86 | } 87 | function writeVia2(offset, value) { 88 | vic20.via2.write(offset, value); 89 | } 90 | function invisibleWriteVia2(offset, value) { 91 | vic20.via2.invisibleWrite(offset, value); 92 | } 93 | function writeVic(offset, value) { 94 | vic20.vic.write(offset, value); 95 | } 96 | this.init = function() { 97 | KeyboardInit(); 98 | this.cpu = new Cpu6502(this,this,this.hasIrq,this.hasNmi); 99 | this.tapedrive = new TapeDrive(); 100 | this.via1 = new Via6522(0x9120,"VIA1"); 101 | this.via1.tapedrive = this.tapedrive; 102 | this.via1.oldCycleDown = this.via1.cycleDown; 103 | this.via1.cycleDown = function() { 104 | if (Config.tapePlay) { 105 | this.ca1 = !this.tapedrive.readTapeBit(); 106 | } 107 | this.oldCycleDown(); 108 | } 109 | this.via1.oldRead = this.via1.read; 110 | this.via1.read = function(reg) { 111 | switch (reg & 0xF) { 112 | case 0x0: 113 | case 0x1: 114 | case 0xF: 115 | this.pins_ira = this.pins_irb = 0; 116 | var orb = this.getReg(0); 117 | var ddrb = this.getReg(2); 118 | var ora = this.getReg(1); 119 | var ddra = this.getReg(3); 120 | var column = ~(orb & ddrb) & 0xFF 121 | , row = ~(ora & ddra) & 0xFF; 122 | for (var i = 0; i < 8; i++) { 123 | if ((column & (1 << i)) != 0) { 124 | this.pins_ira |= keysdown[i]; 125 | } 126 | this.pins_irb |= (keysdown[i] & row) ? (1 << i) : 0; 127 | } 128 | if ((ddrb & 0x80) == 0 && right) { 129 | this.pins_irb |= 0x80; 130 | } 131 | break; 132 | } 133 | var r = this.oldRead(reg); 134 | return r; 135 | } 136 | ; 137 | this.via2 = new Via6522(0x9110,"VIA2"); 138 | this.via2.tapedrive = this.tapedrive; 139 | this.via2.oldCycleUp = this.via2.cycleUp; 140 | this.via2.cycleUp = function() { 141 | this.oldCycleUp(); 142 | var pcrCa2 = (this.getReg(12) >> 1) & 7; 143 | if (pcrCa2 != this.lastPcrCa2) { 144 | if (pcrCa2 == 7) { 145 | this.tapedrive.triggerTapeMotor(false); 146 | } else if (pcrCa2 == 6) { 147 | this.tapedrive.triggerTapeMotor(true); 148 | } 149 | this.lastPcrCa2 = pcrCa2; 150 | } 151 | } 152 | this.via2.oldRead = this.via2.read; 153 | this.via2.read = function(reg) { 154 | this.pins_ira = 1; 155 | if (Config.tapePlay) 156 | this.pins_ira |= 64; 157 | else 158 | this.pins_ira &= ~64; 159 | this.pins_irb = 0xFF; 160 | if (fire) { 161 | this.pins_ira |= 32; 162 | } else { 163 | this.pins_ira &= ~32; 164 | } 165 | if (left) { 166 | this.pins_ira |= 16; 167 | } else { 168 | this.pins_ira &= ~16; 169 | } 170 | if (up) { 171 | this.pins_ira |= 4; 172 | } else { 173 | this.pins_ira &= ~4; 174 | } 175 | if (down) { 176 | this.pins_ira |= 8; 177 | } else { 178 | this.pins_ira &= ~8; 179 | } 180 | return this.oldRead(reg); 181 | } 182 | ; 183 | this.vic = new Vic6560(this); 184 | this.reset(); 185 | } 186 | ; 187 | this.hasIrq = new function(machine) { 188 | this.test = function() { 189 | return machine.via1.hasInterrupt; 190 | } 191 | } 192 | (this); 193 | this.hasNmi = new function(machine) { 194 | this.test = function() { 195 | return machine.via2.hasInterrupt; 196 | } 197 | } 198 | (this); 199 | this.softreset = function() { 200 | console.debug("Vic20 soft reset"); 201 | this.cpu.reset(); 202 | this.loadPrgCntr = 5; 203 | } 204 | this.reset = function() { 205 | console.debug("Vic20 reset"); 206 | for (var i = 0; i < 65536; i++) { 207 | mem[i] = 0xCE; 208 | memExec[i] = 0; 209 | memBp[i] = 0; 210 | } 211 | var bindata = binToArray(chardata); 212 | for (var i = 0; i < bindata.length; i++) { 213 | mem[0x8000 + i] = bindata[i]; 214 | } 215 | bindata = binToArray(basicdata); 216 | for (var i = 0; i < bindata.length; i++) { 217 | mem[0xc000 + i] = bindata[i]; 218 | } 219 | bindata = binToArray(kerneldata); 220 | for (var i = 0; i < bindata.length; i++) { 221 | mem[0xe000 + i] = bindata[i]; 222 | } 223 | for (var i = 0; i < readFuncs.length; i++) { 224 | readFuncs[i] = readMem; 225 | invisibleReadFuncs[i] = readMem; 226 | writeFuncs[i] = writeMem; 227 | invisibleWriteFuncs[i] = writeMem; 228 | } 229 | for (var i = 0x9000; i < 0x9100; i += 16) { 230 | readFuncs[i >> 4] = readVic; 231 | invisibleReadFuncs[i >> 4] = invisibleReadVic; 232 | writeFuncs[i >> 4] = writeVic; 233 | invisibleWriteFuncs[i >> 4] = writeVic; 234 | } 235 | for (var i = 0x9110; i <= 0x93F0; i += 32) { 236 | readFuncs[i >> 4] = readVia2; 237 | invisibleReadFuncs[i >> 4] = invisibleReadVia2; 238 | writeFuncs[i >> 4] = writeVia2; 239 | invisibleWriteFuncs[i >> 4] = invisibleWriteVia2; 240 | } 241 | for (var i = 0x9120; i <= 0x93E0; i += 64) { 242 | readFuncs[i >> 4] = readVia1; 243 | invisibleReadFuncs[i >> 4] = invisibleReadVia1; 244 | writeFuncs[i >> 4] = writeVia1; 245 | invisibleWriteFuncs[i >> 4] = invisibleWriteVia1; 246 | } 247 | for (var i = 0x8000 >> 4; i < 0x9000 >> 4; i++) { 248 | writeFuncs[i] = write0; 249 | invisibleWriteFuncs[i] = write0; 250 | } 251 | for (var i = 0xA000 >> 4; i < 0x10000 >> 4; i++) { 252 | writeFuncs[i] = write0; 253 | invisibleWriteFuncs[i] = write0; 254 | } 255 | this.updateMemoryModules(); 256 | this.cpu.reset(); 257 | this.via1.reset(); 258 | this.via2.reset(); 259 | this.vic.reset(); 260 | this.loadPrgCntr = 5; 261 | } 262 | this.updateMemoryModules = function() { 263 | for (var i = 0x400 >> 4; i < 0x1000 >> 4; i++) { 264 | writeFuncs[i] = Config.memoryAt0400 ? writeMem : write0; 265 | invisibleWriteFuncs[i] = Config.memoryAt0400 ? writeMem : write0; 266 | } 267 | for (var i = 0x2000 >> 4; i < 0x4000 >> 4; i++) { 268 | writeFuncs[i] = Config.memoryAt2000 ? writeMem : write0; 269 | invisibleWriteFuncs[i] = Config.memoryAt2000 ? writeMem : write0; 270 | } 271 | for (var i = 0x4000 >> 4; i < 0x6000 >> 4; i++) { 272 | writeFuncs[i] = Config.memoryAt4000 ? writeMem : write0; 273 | invisibleWriteFuncs[i] = Config.memoryAt4000 ? writeMem : write0; 274 | } 275 | for (var i = 0x6000 >> 4; i < 0x8000 >> 4; i++) { 276 | writeFuncs[i] = Config.memoryAt6000 ? writeMem : write0; 277 | invisibleWriteFuncs[i] = Config.memoryAt6000 ? writeMem : write0; 278 | } 279 | for (var i = 0xA000 >> 4; i < 0xC000 >> 4; i++) { 280 | writeFuncs[i] = Config.memoryAtA000 ? writeMem : write0; 281 | invisibleWriteFuncs[i] = Config.memoryAtA000 ? writeMem : write0; 282 | } 283 | } 284 | var hasStarted = false; 285 | this.execute = function() { 286 | console.debug("Vic20 execute"); 287 | this.nextFrameTime = new Date().getTime(); 288 | this.stop = false; 289 | this.running = true; 290 | if (!hasStarted) { 291 | hasStarted = true; 292 | this.oneFrame(); 293 | } 294 | } 295 | this.stopExecution = function() { 296 | this.stop = true; 297 | } 298 | this.stepToInstruction = function() { 299 | while (!this.cpu.isNextInst()) { 300 | this.cycle(); 301 | } 302 | } 303 | this.cycle = function() { 304 | this.via1.cycleUp(); 305 | this.via2.cycleUp(); 306 | this.cpu.cycle(); 307 | this.via1.cycleDown(); 308 | this.via2.cycleDown(); 309 | this.vic.cycle(); 310 | } 311 | var oneFrameTimeSum = 0; 312 | var timeloop = 100; 313 | var frameTimeEl = $("#frameTime"); 314 | this.oneFrame = function() { 315 | var _vic = this.vic; 316 | var _cpu = this.cpu; 317 | var _via1 = this.via1; 318 | var _via2 = this.via2; 319 | var frameRate = this.isPal ? 25 : 29.97; 320 | var cyclesPerFrame = Math.round((this.isPal ? 1108404 : 1017900) / frameRate); 321 | var startTime = new Date().getTime(); 322 | for (var j = 0; j < Config.speed; j++) 323 | if (!this.stop) { 324 | for (var i = cyclesPerFrame; i; --i) { 325 | var pc = _cpu.getPc(); 326 | if (memBp[pc]) { 327 | if (memBp[pc] == 1) { 328 | memBp[pc] = 2; 329 | memExec[pc] |= 2; 330 | if (!isDebugging) 331 | debug(); 332 | break; 333 | } 334 | if (memBp[pc] == 2) 335 | memBp[pc] = 1; 336 | } 337 | _via1.cycleUp(); 338 | _via2.cycleUp(); 339 | _cpu.cycle(); 340 | _via1.cycleDown(); 341 | _via2.cycleDown(); 342 | _vic.cycle(); 343 | } 344 | oneFrameTimeSum += new Date().getTime() - startTime; 345 | if (timeloop-- == 0) { 346 | frameTimeEl.text((oneFrameTimeSum / 100) + " ms"); 347 | oneFrameTimeSum = 0; 348 | timeloop = 100; 349 | } 350 | } 351 | if (typeof loadPrgSrc != 'undefined' && (mem[43] != 0 || mem[44] != 0) && this.loadPrgCntr-- == 0) { 352 | if (loadPrgSrc == "tape") { 353 | mem[631] = 131; 354 | mem[198] = 1; 355 | Config.tapePlay = 1; 356 | } else { 357 | loadPrgFileData(loadPrgSrc); 358 | } 359 | delete loadPrgSrc; 360 | } 361 | frameRate *= Config.speed; 362 | var timeWaitUntilNextFrame = this.nextFrameTime - new Date().getTime(); 363 | if (timeWaitUntilNextFrame < 0) { 364 | timeWaitUntilNextFrame = 0; 365 | this.nextFrameTime = new Date().getTime() + (1000 / frameRate); 366 | } else { 367 | this.nextFrameTime += (1000 / frameRate); 368 | } 369 | setTimeout("vic20.oneFrame()", timeWaitUntilNextFrame); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /docs/c64/c64/pp_javascript.js: -------------------------------------------------------------------------------- 1 | function handleVirtualKey( value, down ) 2 | { 3 | Module.setKey(value, down); 4 | } 5 | 6 | function handleMute(event) 7 | { 8 | var mute = document.getElementById("mute"); 9 | Module.toggleMute(); 10 | mute.style.visibility = "hidden"; 11 | return false; 12 | } 13 | 14 | function handleSnapClick(event) 15 | { 16 | var selJoy = document.getElementById("joySelect"); 17 | if( selJoy.selectedIndex == 0 ) { 18 | selJoy.selectedIndex = 1; 19 | handleJoyConf(false); 20 | } 21 | Module.loadSnapshot( document.getElementById("selectGame").value, false ); 22 | return false; 23 | } 24 | 25 | function handleSnapJoy() 26 | { 27 | var joyTest = document.getElementById("call_joy_test"); 28 | var selJoy = document.getElementById("joySelect"); 29 | if( selJoy.selectedIndex == 0 ) { 30 | selJoy.selectedIndex = 1; 31 | handleJoyConf(false); 32 | } 33 | if( joyTest.innerHTML != joyTest.getAttribute('data-back') ) { 34 | joyTestSave = Module.saveSnapshotData(); 35 | Module.loadSnapshot( 'Joystick_Test.s64', false ); 36 | joyTest.innerHTML = joyTest.getAttribute('data-back'); 37 | } 38 | else { 39 | if(joyTestSave) Module.loadSnapshotData( joyTestSave ); 40 | joyTestSave = null; 41 | joyTest.innerHTML = 'Joystick Test'; 42 | } 43 | return false; 44 | } 45 | 46 | function handleJoyConf(event) 47 | { 48 | var joy = document.getElementById("joySelect"); 49 | Module.selectJoystick( joy.value&0xff, joy.value>>8 ); 50 | joy.blur(); 51 | return false; 52 | } 53 | 54 | function handleHighscore(event) 55 | { 56 | var myForm = document.getElementById("highSave"); 57 | var userName = myForm.elements["userName"].value; 58 | var scoreId = myForm.elements["scoreid"].value; 59 | var snapName = myForm.elements["game"].value; 60 | document.getElementById('highScore_status').innerHTML = ''; 61 | Module.uploadHighscore(userName, scoreId, snapName); 62 | return false; 63 | } 64 | 65 | function handleHighscoreCancel(event) 66 | { 67 | document.getElementById('highScore_status').innerHTML = ''; 68 | Module.cancelHighscore(); 69 | return false; 70 | } 71 | 72 | function c64_drag_checkfile(file) { 73 | var validExt = ['S64', 'D64', 'G64', 'X64', 'ZIP', 'PRG', 'P00', 'T64', 'TAP', 'CRT']; 74 | var ext = file.name.split('.').pop(); 75 | var exthi = ext.toUpperCase(); 76 | for (var i=0; i < validExt.length; i++) { 77 | if( ext == validExt[i] || exthi == validExt[i] ) { 78 | return true; 79 | } 80 | } 81 | return false; 82 | } 83 | 84 | function c64_checkfile_type(file, startup) { 85 | var myForm = document.getElementById('c64_status'); 86 | var selJoy = document.getElementById("joySelect"); 87 | var ext = file.name.split('.').pop(); 88 | var exthi = ext.toUpperCase(); 89 | 90 | if( startup || exthi == 'S64' || exthi == 'CRT' ) { 91 | if( exthi == 'S64' ) { 92 | myForm.style='display:none'; 93 | } 94 | else { 95 | myForm.style='display:block'; 96 | } 97 | } 98 | 99 | if( startup || exthi == 'S64' || exthi == 'CRT' ) { 100 | if( selJoy.selectedIndex == 0 || selJoy.selectedIndex == 2 ) { 101 | selJoy.selectedIndex = 1; 102 | handleJoyConf(false); 103 | } 104 | else if( selJoy.selectedIndex == 3 ) { 105 | selJoy.selectedIndex = 4; 106 | handleJoyConf(false); 107 | } 108 | } 109 | } 110 | 111 | function c64_dragover_handler(ev) { 112 | ev.preventDefault(); 113 | ev.stopPropagation(); 114 | ev.dataTransfer.dropEffect = 'copy'; 115 | } 116 | 117 | function c64_drop_handler(ev) { 118 | ev.preventDefault(); 119 | ev.stopPropagation(); 120 | var myForm = document.getElementById('c64FileInput'); 121 | var startup = (myForm.elements['startup'].checked) ? 1 : 0; 122 | 123 | var dt = ev.dataTransfer; 124 | if( dt.items ) { 125 | for (var i=0; i < dt.items.length; i++) { 126 | if (dt.items[i].kind == "file") { 127 | var f = dt.items[i].getAsFile(); 128 | if( c64_drag_checkfile(f)) { 129 | Module.loadFile(f, startup); 130 | c64_checkfile_type(f, startup); 131 | break; 132 | } 133 | } 134 | } 135 | } 136 | else { 137 | for (var i=0; i < dt.files.length; i++) { 138 | if( c64_drag_checkfile(dt.files[i])) { 139 | Module.loadFile(dt.files[i], startup); 140 | c64_checkfile_type(dt.files[i], startup); 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | function c64_handleDiskInput(event) 148 | { 149 | var myForm = document.getElementById('c64FileInput'); 150 | var startup = (myForm.elements['startup'].checked) ? 1 : 0; 151 | var myfiles = myForm.elements['c64FileDialog'].files; 152 | for (var i=0; i < myfiles.length; i++) { 153 | if( c64_drag_checkfile(myfiles[i])) { 154 | Module.loadFile(myfiles[i], startup); 155 | c64_checkfile_type(myfiles[i], startup); 156 | break; 157 | } 158 | } 159 | return false; 160 | } 161 | 162 | function showPic() 163 | { 164 | var name = document.getElementById("selectGame").value; 165 | document.getElementById('selectPic').innerHTML = ''; 166 | } 167 | 168 | 169 | function process_touch(ev) { 170 | if( 0==touchEnabled ) { 171 | touchEnabled = 1; 172 | Module.setProperty( 'touchtype', touchType.toString() ); 173 | if(touchType == 1) { 174 | document.getElementById("canvas_overlay_right").style.display = 'block'; 175 | document.getElementById("canvas_overlay_left").style.display = 'none'; 176 | } 177 | else { 178 | document.getElementById("canvas_overlay_right").style.display = 'none'; 179 | document.getElementById("canvas_overlay_left").style.display = 'block'; 180 | } 181 | } 182 | if( touchEnabled > 0 ) { 183 | window.clearTimeout(touchTimer); 184 | touchTimer = window.setTimeout(function(){document.getElementById("canvas_overlay_right").style.display = 'none';document.getElementById("canvas_overlay_left").style.display = 'none';touchEnabled=0;}, 15000); 185 | } 186 | } 187 | 188 | 189 | function openKeyboard() 190 | { 191 | if(!myKeyboard) myKeyboard = new c64_keyboard(); 192 | myKeyboard.VKI_click(document.getElementById("virtualKeyboard"), handleVirtualKey); 193 | } 194 | 195 | function addKeyboardHandler(kb) 196 | { 197 | kb.addEventListener("mousedown", function() {}, false); 198 | kb.addEventListener("mouseup", openKeyboard, false); 199 | kb.addEventListener("touchstart", function(e) { if(e.preventDefault) e.preventDefault(); }, false); 200 | kb.addEventListener("touchcancel", function(e) { if(e.preventDefault) e.preventDefault(); }, false); 201 | kb.addEventListener("touchend", function(e) { openKeyboard(); if(e.preventDefault) e.preventDefault(); }, false); 202 | } 203 | 204 | function addControlHandler(parent) 205 | { 206 | const ctrl = 207 | { 208 | cmd_fs: function() { Module.requestC64FullScreen(); }, 209 | cmd_tm: function() { Module.toggleMute(); }, 210 | cmd_ss: function() { Module.snapshotStorage(1); }, 211 | cmd_sl: function() { Module.snapshotStorage(0); }, 212 | cmd_uh: function() { Module.uploadHighscore('', '', 'self'); } 213 | }; 214 | 215 | var children = parent.children; 216 | for (var i = 0; i < children.length; i++) 217 | { 218 | var child = children[i]; 219 | if(child.nodeName == "BUTTON") 220 | { 221 | if(child.name.startsWith('cmd_')) 222 | { 223 | child.addEventListener("click", function(e) { 224 | ctrl[this.name](); 225 | }, false); 226 | 227 | child.addEventListener("touchstart", function(e) { if(e.preventDefault) e.preventDefault(); }, false); 228 | child.addEventListener("touchcancel", function(e) { if(e.preventDefault) e.preventDefault(); }, false); 229 | child.addEventListener("touchend", function(e) { ctrl[this.name](); if(e.preventDefault) e.preventDefault(); }, false); 230 | } 231 | } 232 | else 233 | { 234 | addControlHandler(child); 235 | } 236 | } 237 | } 238 | 239 | function addButtonHandler(parent) 240 | { 241 | var children = parent.children; 242 | for (var i = 0; i < children.length; i++) 243 | { 244 | var child = children[i]; 245 | if(child.nodeName == "BUTTON") 246 | { 247 | if(child.name.startsWith('key_')) 248 | { 249 | child.addEventListener("mousedown", function(e) { Module.setKey(this.name.substr(4), 1); }, false); 250 | child.addEventListener("mouseup", function(e) { Module.setKey(this.name.substr(4), 0); }, false); 251 | 252 | child.addEventListener("touchstart", function(e) { Module.setKey(this.name.substr(4), 1); if(e.preventDefault) e.preventDefault(); }, false); 253 | child.addEventListener("touchcancel", function(e) { Module.setKey(this.name.substr(4), 0); if(e.preventDefault) e.preventDefault(); }, false); 254 | child.addEventListener("touchend", function(e) { Module.setKey(this.name.substr(4), 0); if(e.preventDefault) e.preventDefault(); }, false); 255 | } 256 | else if(child.name.startsWith('joy_')) 257 | { 258 | child.addEventListener("mousedown", function(e) { Module.setJoystick(this.name.substr(4), 1); }, false); 259 | child.addEventListener("mouseup", function(e) { Module.setJoystick(this.name.substr(4), 0); }, false); 260 | 261 | child.addEventListener("touchstart", function(e) { Module.setJoystick(this.name.substr(4), 1); if(e.preventDefault) e.preventDefault(); }, false); 262 | child.addEventListener("touchcancel", function(e) { Module.setJoystick(this.name.substr(4), 0); if(e.preventDefault) e.preventDefault(); }, false); 263 | child.addEventListener("touchend", function(e) { Module.setJoystick(this.name.substr(4), 0); if(e.preventDefault) e.preventDefault(); }, false); 264 | } 265 | else if(child.name.startsWith('tape_')) 266 | { 267 | child.addEventListener("mousedown", function(e) { Module.setTape(this.name.substr(5)); }, false); 268 | 269 | child.addEventListener("touchstart", function(e) { Module.setTape(this.name.substr(5)); if(e.preventDefault) e.preventDefault(); }, false); 270 | } 271 | } 272 | else 273 | { 274 | addButtonHandler(child); 275 | } 276 | } 277 | } 278 | 279 | function addConfigHandler(parent) 280 | { 281 | document.getElementById('print_version').addEventListener("click", function(e) { 282 | e.stopPropagation(); 283 | document.getElementById("c64config_popup").classList.toggle("show"); 284 | }, false); 285 | 286 | document.getElementById('config_inverty').addEventListener("click", function(e) { 287 | var check = document.getElementById('config_inverty'); 288 | Module.setProperty("joy_inverty", check.checked?"48":"0"); 289 | }, false); 290 | 291 | document.getElementById('config_fastload').addEventListener("click", function(e) { 292 | var check = document.getElementById('config_fastload'); 293 | Module.setProperty("disk_fast_loading", check.checked?"1":"0"); 294 | }, false); 295 | 296 | document.getElementById('config_wrprotect').addEventListener("click", function(e) { 297 | var check = document.getElementById('config_wrprotect'); 298 | Module.setProperty("disk_write_protection", check.checked?"2":"0"); 299 | }, false); 300 | 301 | document.getElementById('config_reset').addEventListener("click", function(e) { 302 | Module.resetEmulation(1); 303 | }, false); 304 | 305 | document.getElementById('config_touch').addEventListener("click", function(e) { 306 | var check = document.getElementById('config_touch'); 307 | touchEnabled = 0; 308 | touchType = check.checked?2:1; 309 | process_touch(null); 310 | }, false); 311 | } 312 | 313 | 314 | function c64_global_startup() 315 | { 316 | document.getElementById('print_version').innerHTML = 'V'+Module.getProperty('version')+'/'+Module.getProperty('revision'); 317 | document.getElementById('config_inverty').checked = Module.getProperty("joy_inverty") != '0'; 318 | document.getElementById('config_fastload').checked = Module.getProperty("disk_fast_loading") != '0'; 319 | document.getElementById('config_wrprotect').checked = Module.getProperty("disk_write_protection") != '0'; 320 | document.getElementById('config_touch').checked = Module.getProperty("touchtype") == '2'; 321 | } 322 | 323 | 324 | function changeIcon(url) { document.getElementById("favicon").href = url; } 325 | 326 | var arrow_keys_handler = function(e) { 327 | switch(e.keyCode){ 328 | case 37: case 39: case 38: case 40: case 8: case 9: case 13: 329 | case 32: case 17: case 112: case 114: case 116: case 118: 330 | if ( Module.hasFocus ) { 331 | e.preventDefault(); 332 | } 333 | break; 334 | default: break; 335 | } 336 | switch(e.keyCode){ 337 | case 37: 338 | changeIcon('favicon-left.png'); 339 | break; 340 | case 39: 341 | changeIcon('favicon-right.png'); 342 | break; 343 | case 83: 344 | Module.saveGame('gridrunner_progress.s64'); 345 | break; 346 | case 82: 347 | Module.loadGame('gridrunner_progress.s64'); 348 | break; 349 | case 72: 350 | Module.loadHighScoreGame(); 351 | break; 352 | default: 353 | break; 354 | } 355 | }; 356 | 357 | function addHandler() 358 | { 359 | if (!String.prototype.startsWith) { 360 | String.prototype.startsWith = function(searchString, position) { 361 | position = position || 0; 362 | return this.indexOf(searchString, position) === position; 363 | }; 364 | } 365 | 366 | if( window.addEventListener ) { 367 | window.addEventListener("keydown", arrow_keys_handler); 368 | window.addEventListener("keyup", arrow_keys_handler); 369 | window.addEventListener("click", function(e){ 370 | if(document.getElementById("c64config_popup").classList.contains('show')) { 371 | if(!document.getElementById("c64config_popup").contains(e.target)) 372 | document.getElementById("c64config_popup").classList.remove('show'); 373 | } 374 | }); 375 | } 376 | else { 377 | window.attachEvent("onkeydown", arrow_keys_handler); 378 | window.attachEvent("onkeyup", arrow_keys_handler); 379 | } 380 | 381 | addButtonHandler(document.getElementById("key_field")); 382 | addKeyboardHandler(document.getElementById("virtualKeyboard")); 383 | addControlHandler(document.getElementById("controls")); 384 | addConfigHandler(document.getElementById("c64config_popup")); 385 | 386 | document.getElementById("canvas").addEventListener("contextmenu", function(e) { 387 | e.preventDefault(); 388 | }, false); 389 | 390 | document.getElementById('canvas').addEventListener("touchstart", function(e) { 391 | process_touch(); 392 | }, false); 393 | document.getElementById('canvas').addEventListener("touchmove", function(e) { 394 | process_touch(); 395 | }, false); 396 | 397 | document.getElementById('help_div').addEventListener("click", function(e) { 398 | this.style.display='none'; 399 | }, false); 400 | 401 | document.getElementById('call_joy_test').addEventListener("mousedown", function(e) { 402 | handleSnapJoy(); 403 | }, false); 404 | 405 | document.getElementById('id_joyconf').addEventListener("submit", function(e) { 406 | e.preventDefault(); 407 | handleJoyConf(); 408 | }, false); 409 | 410 | document.getElementById('joySelect').addEventListener("change", function(e) { 411 | handleJoyConf(); 412 | }, false); 413 | 414 | document.getElementById('c64FileInput').addEventListener("submit", function(e) { 415 | e.preventDefault(); 416 | c64_handleDiskInput(); 417 | }, false); 418 | 419 | document.getElementById('drop_zone').addEventListener("click", function(e) { 420 | document.getElementById('c64FileInput').elements['c64FileDialog'].click(); 421 | }, false); 422 | 423 | document.getElementById('drop_zone').addEventListener("dragover", function(e) { 424 | c64_dragover_handler(e); 425 | }, false); 426 | 427 | document.getElementById('drop_zone').addEventListener("drop", function(e) { 428 | c64_drop_handler(e); 429 | }, false); 430 | 431 | document.getElementById('c64fd').addEventListener("change", function(e) { 432 | c64_handleDiskInput(); 433 | }, false); 434 | 435 | document.getElementById('snap_form').addEventListener("submit", function(e) { 436 | e.preventDefault(); 437 | handleSnapClick(); 438 | }, false); 439 | 440 | document.getElementById('mute').addEventListener("click", function(e) { 441 | e.preventDefault(); 442 | handleMute(); 443 | }, false); 444 | } 445 | 446 | var myKeyboard = false; 447 | var touchEnabled = 0; 448 | var touchType = 1; 449 | var touchTimer = null; 450 | var joyTestSave = null; 451 | Module.setStatus('Downloading...'); 452 | addHandler(); 453 | 454 | 455 | 456 | 457 | -------------------------------------------------------------------------------- /docs/c64/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | height: 100%; 3 | background-color: black; 4 | } 5 | 6 | #mute:hover{ 7 | cursor: pointer; 8 | } 9 | #mute { 10 | position: fixed; 11 | font-size: 2vh; 12 | top: 1vh; 13 | right: 1vw; 14 | } 15 | 16 | #canvas 17 | { 18 | display: block; 19 | font-size: 15px; 20 | position: fixed; 21 | left: 50%; 22 | transform: translateX(-50%); 23 | top: 0; 24 | animation-duration: 0.75s; 25 | animation-delay: 0.25s; 26 | animation-timing-function: cubic-bezier(0.1,0.2,0.8,1); 27 | height: 100vh; 28 | animation-name: zoomout; 29 | } 30 | @keyframes zoomout {from{ height: 200vh; } to{ }} 31 | 32 | #canvas:-webkit-full-screen 33 | { 34 | float: none; 35 | width: 100%; 36 | height: 100%; 37 | padding: 0; 38 | margin: 0; 39 | border: 0 none; 40 | } 41 | 42 | #canvas:-moz-full-screen 43 | { 44 | float: none; 45 | width: 100%; 46 | height: 100%; 47 | padding: 0; 48 | margin: 0; 49 | border: 0 none; 50 | } 51 | 52 | #canvas:-ms-full-screen 53 | { 54 | float: none; 55 | width: 100%; 56 | height: 100%; 57 | padding: 0; 58 | margin: 0; 59 | border: 0 none; 60 | } 61 | 62 | #canvas:-o-full-screen 63 | { 64 | float: none; 65 | width: 100%; 66 | height: 100%; 67 | padding: 0; 68 | margin: 0; 69 | border: 0 none; 70 | } 71 | 72 | #canvas:full-screen 73 | { 74 | float: none; 75 | width: 100%; 76 | height: 100%; 77 | padding: 0; 78 | margin: 0; 79 | border: 0 none; 80 | } 81 | 82 | 83 | #canvas_container { 84 | position: relative; 85 | } 86 | #canvas_overlay_left { 87 | position: absolute; 88 | z-index: 2147483647; 89 | width: 100%; 90 | height: 100%; 91 | pointer-events: none; 92 | display: none; 93 | 94 | background-image: 95 | url("data:image/svg+xml;utf8,\ 96 | \ 97 | \ 98 | \ 99 | \ 100 | "); 101 | } 102 | 103 | 104 | #canvas_overlay_right { 105 | position: absolute; 106 | z-index: 2147483647; 107 | width: 100%; 108 | height: 100%; 109 | pointer-events: none; 110 | display: none; 111 | 112 | background-image: 113 | url("data:image/svg+xml;utf8,\ 114 | \ 115 | \ 116 | \ 117 | \ 118 | "); 119 | } 120 | 121 | 122 | /* Tooltip text */ 123 | #help_main 124 | { 125 | position: relative; 126 | } 127 | 128 | #help_main #help_div 129 | { 130 | display: none; 131 | width: 300px; 132 | background-color: #555; 133 | color: #fff; 134 | text-align: left; 135 | padding: 5px 0 5px 10px; 136 | border-radius: 6px; 137 | 138 | /* Position the tooltip text */ 139 | position: absolute; 140 | z-index: 1; 141 | top: 125%; 142 | left: 50%; 143 | margin-left: -100px; 144 | } 145 | 146 | #drop_zone { 147 | cursor: pointer; 148 | background-color: #dddddd; 149 | padding:10px; 150 | border:2px dashed #a0a0ff; 151 | width: 85%; 152 | height: 32px; 153 | } 154 | 155 | 156 | @media screen and (min-width: 480px) { 157 | #print_version { 158 | float: right; 159 | font-size: x-small; 160 | color: #868686; 161 | cursor: pointer; 162 | } 163 | #print_version:after { 164 | content: "\1F528"; 165 | } 166 | .hide_mobile { 167 | } 168 | } 169 | @media screen and (max-width: 479px) { 170 | #print_version { 171 | display: none; 172 | } 173 | 174 | .hide_mobile { 175 | display: none !important; 176 | } 177 | } 178 | 179 | .hide_tape { 180 | display: none; 181 | } 182 | .show_tape { 183 | } 184 | 185 | .keyboardInput { 186 | background-color:#aecaff; 187 | white-space: nowrap; 188 | height: 24px; 189 | } 190 | 191 | .button { 192 | display: inline-block; 193 | white-space: nowrap; 194 | background-color: #ccc; 195 | background-image: linear-gradient(top, #eee, #ccc); 196 | filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#cccccc'); 197 | border: 1px solid #777; 198 | padding: 0 1.0em; 199 | margin: 0.1em; 200 | font: bold 0.9em/2em Arial, Helvetica; 201 | text-decoration: none; 202 | color: #333; 203 | text-shadow: 0 1px 0 rgba(255,255,255,.8); 204 | border-radius: .2em; 205 | box-shadow: 0 0 1px 1px rgba(255,255,255,.8) inset, 0 1px 0 rgba(0,0,0,.3); 206 | } 207 | 208 | .button:hover { 209 | background-color: #ddd; 210 | background-image: linear-gradient(top, #fafafa, #ddd); 211 | filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa', EndColorStr='#dddddd'); 212 | } 213 | 214 | .button:enabled:active { 215 | box-shadow: 0 0 4px 2px rgba(0,0,0,.3) inset; 216 | position: relative; 217 | top: 1px; 218 | } 219 | 220 | .button:disabled { 221 | opacity: 0.5; 222 | } 223 | 224 | .button#highlight { 225 | animation-duration: .5s; 226 | animation-name: hblink; 227 | animation-iteration-count: 6; 228 | animation-direction: alternate; 229 | animation-timing-function: ease-in-out; 230 | animation-delay: 5s; 231 | } 232 | 233 | @keyframes hblink { 234 | from { 235 | background-color: #ccc; 236 | } 237 | to { 238 | background-color: #def; 239 | } 240 | } 241 | 242 | .button:before { 243 | background: #ccc; 244 | background: rgba(0,0,0,.1); 245 | float: left; 246 | width: 1em; 247 | text-align: center; 248 | font-size: 1.0em; 249 | margin: 0 0.6em 0 -1em; 250 | padding: 0 .4em; 251 | box-shadow: 1px 0 0 rgba(0,0,0,.5), 2px 0 0 rgba(255,255,255,.5); 252 | border-radius: .15em 0 0 .15em; 253 | pointer-events: none; 254 | } 255 | 256 | 257 | .bhighlight { 258 | color: #33b; 259 | } 260 | 261 | /* Hexadecimal entities for the icons */ 262 | 263 | .add:before { 264 | content: "\271A"; 265 | } 266 | 267 | .edit:before { 268 | content: "\270E"; 269 | } 270 | 271 | .delete:before { 272 | content: "\2718"; 273 | } 274 | 275 | .save:before { 276 | content: "\2714"; 277 | } 278 | 279 | .email:before { 280 | content: "\2709"; 281 | } 282 | 283 | .like:before { 284 | content: "\2764"; 285 | } 286 | 287 | .next:before { 288 | content: "\279C"; 289 | } 290 | 291 | .full:before { 292 | content: "\25A3"; 293 | } 294 | 295 | .sound:before { 296 | content: "\2706"; 297 | } 298 | 299 | .play:before { 300 | content: "\25B6"; 301 | } 302 | 303 | .tapeplay:before { 304 | content: "\25BA"; 305 | } 306 | 307 | .taperewind:before { 308 | content: "\AB"; 309 | } 310 | 311 | .tapezero:before { 312 | content: "\25CE"; 313 | } 314 | 315 | 316 | 317 | 318 | .c64config { 319 | position: relative; 320 | float: right; 321 | display: inline-block; 322 | } 323 | 324 | .c64config .configcontent { 325 | visibility: hidden; 326 | width: 200px; 327 | background-color: #555; 328 | color: #fff; 329 | text-align: left; 330 | border-radius: 6px; 331 | padding: 8px 0 8px 8px; 332 | position: absolute; 333 | z-index: 1; 334 | bottom: 125%; 335 | left: 50%; 336 | margin-left: -200px; 337 | } 338 | 339 | .c64config .configcontent::after { 340 | content: ""; 341 | position: absolute; 342 | top: 100%; 343 | left: 50%; 344 | margin-left: -5px; 345 | border-width: 5px; 346 | border-style: solid; 347 | border-color: #555 transparent transparent transparent; 348 | } 349 | 350 | .c64config .show { 351 | visibility: visible; 352 | -webkit-animation: configfadeIn 1s; 353 | animation: configfadeIn 1s 354 | } 355 | 356 | 357 | .horform2 { 358 | padding:1px; border:2px solid #a0a0ff; 359 | margin-top: 5px; 360 | width: 100%; 361 | } 362 | .horform2 form { 363 | padding:5px; border:0px; 364 | } 365 | .horform2 form .Auswahl { 366 | width:100%; 367 | } 368 | 369 | #c64_status { 370 | display: none; 371 | } 372 | 373 | @-webkit-keyframes configfadeIn { 374 | from {opacity: 0;} 375 | to {opacity: 1;} 376 | } 377 | 378 | @keyframes configfadeIn { 379 | from {opacity: 0;} 380 | to {opacity:1 ;} 381 | } 382 | #keyboardInputMaster { 383 | position:absolute; 384 | font:normal 11px Arial,sans-serif; 385 | border-top:1px solid #eeeeee; 386 | border-right:1px solid #888888; 387 | border-bottom:1px solid #444444; 388 | border-left:1px solid #cccccc; 389 | -webkit-border-radius:0.6em; 390 | -moz-border-radius:0.6em; 391 | border-radius:0.6em; 392 | -webkit-box-shadow:0px 2px 10px #444444; 393 | -moz-box-shadow:0px 2px 10px #444444; 394 | box-shadow:0px 2px 10px #444444; 395 | opacity:0.95; 396 | filter:alpha(opacity=95); 397 | background-color:#dddddd; 398 | text-align:left; 399 | z-index:1000000; 400 | width:auto; 401 | height:auto; 402 | min-width:0; 403 | min-height:0; 404 | margin:0px; 405 | padding:0px; 406 | line-height:normal; 407 | -moz-user-select:none; 408 | cursor:default; 409 | } 410 | #keyboardInputMaster * { 411 | position:static; 412 | color:#000000; 413 | background:transparent; 414 | font:normal 11px Arial,sans-serif; 415 | width:auto; 416 | height:auto; 417 | min-width:0; 418 | min-height:0; 419 | margin:0px; 420 | padding:0px; 421 | border:0px none; 422 | outline:0px; 423 | vertical-align:baseline; 424 | line-height:1.3em; 425 | } 426 | #keyboardInputMaster table { 427 | table-layout:auto; 428 | } 429 | #keyboardInputMaster.keyboardInputSize1, 430 | #keyboardInputMaster.keyboardInputSize1 * { 431 | font-size:9px; 432 | } 433 | #keyboardInputMaster.keyboardInputSize3, 434 | #keyboardInputMaster.keyboardInputSize3 * { 435 | font-size:13px; 436 | } 437 | #keyboardInputMaster.keyboardInputSize4, 438 | #keyboardInputMaster.keyboardInputSize4 * { 439 | font-size:16px; 440 | } 441 | #keyboardInputMaster.keyboardInputSize5, 442 | #keyboardInputMaster.keyboardInputSize5 * { 443 | font-size:20px; 444 | } 445 | 446 | #keyboardInputMaster thead tr th { 447 | padding:0.3em 0.3em 0.1em 0.3em; 448 | background-color:#999999; 449 | white-space:nowrap; 450 | text-align:right; 451 | -webkit-border-radius:0.6em 0.6em 0px 0px; 452 | -moz-border-radius:0.6em 0.6em 0px 0px; 453 | border-radius:0.6em 0.6em 0px 0px; 454 | } 455 | #keyboardInputMaster thead tr th div { 456 | float:left; 457 | font-size:130% !important; 458 | height:1.3em; 459 | font-weight:bold; 460 | position:relative; 461 | z-index:1; 462 | margin-right:0.5em; 463 | cursor:pointer; 464 | background-color:transparent; 465 | } 466 | #keyboardInputMaster thead tr th div ol { 467 | position:absolute; 468 | left:0px; 469 | top:90%; 470 | list-style-type:none; 471 | height:9.4em; 472 | overflow-y:auto; 473 | overflow-x:hidden; 474 | background-color:#f6f6f6; 475 | border:1px solid #999999; 476 | display:none; 477 | text-align:left; 478 | width:12em; 479 | } 480 | #keyboardInputMaster thead tr th div ol li { 481 | padding:0.2em 0.4em; 482 | cursor:pointer; 483 | white-space:nowrap; 484 | width:12em; 485 | } 486 | #keyboardInputMaster thead tr th div ol li.selected { 487 | background-color:#ffffcc; 488 | } 489 | #keyboardInputMaster thead tr th div ol li:hover, 490 | #keyboardInputMaster thead tr th div ol li.hover { 491 | background-color:#dddddd; 492 | } 493 | #keyboardInputMaster thead tr th span, 494 | #keyboardInputMaster thead tr th strong, 495 | #keyboardInputMaster thead tr th small, 496 | #keyboardInputMaster thead tr th big { 497 | display:inline-block; 498 | padding:0px 0.4em; 499 | height:1.4em; 500 | line-height:1.4em; 501 | border-top:1px solid #e5e5e5; 502 | border-right:1px solid #5d5d5d; 503 | border-bottom:1px solid #5d5d5d; 504 | border-left:1px solid #e5e5e5; 505 | background-color:#cccccc; 506 | cursor:pointer; 507 | margin:0px 0px 0px 0.3em; 508 | -webkit-border-radius:0.3em; 509 | -moz-border-radius:0.3em; 510 | border-radius:0.3em; 511 | vertical-align:middle; 512 | -webkit-transition:background-color .15s ease-in-out; 513 | -o-transition:background-color .15s ease-in-out; 514 | transition:background-color .15s ease-in-out; 515 | } 516 | #keyboardInputMaster thead tr th strong { 517 | font-weight:bold; 518 | } 519 | #keyboardInputMaster thead tr th small { 520 | -webkit-border-radius:0.3em 0px 0px 0.3em; 521 | -moz-border-radius:0.3em 0px 0px 0.3em; 522 | border-radius:0.3em 0px 0px 0.3em; 523 | border-right:1px solid #aaaaaa; 524 | padding:0px 0.2em 0px 0.3em; 525 | } 526 | #keyboardInputMaster thead tr th big { 527 | -webkit-border-radius:0px 0.3em 0.3em 0px; 528 | -moz-border-radius:0px 0.3em 0.3em 0px; 529 | border-radius:0px 0.3em 0.3em 0px; 530 | border-left:0px none; 531 | margin:0px; 532 | padding:0px 0.3em 0px 0.2em; 533 | } 534 | #keyboardInputMaster thead tr th span:hover, 535 | #keyboardInputMaster thead tr th span.hover, 536 | #keyboardInputMaster thead tr th strong:hover, 537 | #keyboardInputMaster thead tr th strong.hover, 538 | #keyboardInputMaster thead tr th small:hover, 539 | #keyboardInputMaster thead tr th small.hover, 540 | #keyboardInputMaster thead tr th big:hover, 541 | #keyboardInputMaster thead tr th big.hover { 542 | background-color:#dddddd; 543 | } 544 | 545 | #keyboardInputMaster tbody tr td { 546 | text-align:left; 547 | padding:0.2em 0.3em 0.3em 0.3em; 548 | vertical-align:top; 549 | } 550 | #keyboardInputMaster tbody tr td div { 551 | text-align:center; 552 | position:relative; 553 | zoom:1; 554 | } 555 | #keyboardInputMaster tbody tr td table { 556 | white-space:nowrap; 557 | width:100%; 558 | border-collapse:separate; 559 | border-spacing:0px; 560 | } 561 | #keyboardInputMaster tbody tr td#keyboardInputNumpad table { 562 | margin-left:0.2em; 563 | width:auto; 564 | } 565 | #keyboardInputMaster tbody tr td table.keyboardInputCenter { 566 | width:auto; 567 | margin:0px auto; 568 | } 569 | #keyboardInputMaster tbody tr td table tbody tr td { 570 | vertical-align:middle; 571 | padding:0px 0.45em; 572 | white-space:pre; 573 | height:1.8em; 574 | font-family:'Lucida Console','Arial Unicode MS',monospace; 575 | border-top:1px solid #e5e5e5; 576 | border-right:1px solid #5d5d5d; 577 | border-bottom:1px solid #5d5d5d; 578 | border-left:1px solid #e5e5e5; 579 | background-color:#eeeeee; 580 | cursor:default; 581 | min-width:0.75em; 582 | -webkit-border-radius:0.2em; 583 | -moz-border-radius:0.2em; 584 | border-radius:0.2em; 585 | -webkit-transition:background-color .15s ease-in-out; 586 | -o-transition:background-color .15s ease-in-out; 587 | transition:background-color .15s ease-in-out; 588 | } 589 | #keyboardInputMaster tbody tr td table tbody tr td.last { 590 | width:99%; 591 | } 592 | #keyboardInputMaster tbody tr td table tbody tr td.space { 593 | padding:0px 4em; 594 | } 595 | #keyboardInputMaster tbody tr td table tbody tr td.deadkey { 596 | background-color:#ccccdd; 597 | } 598 | #keyboardInputMaster tbody tr td table tbody tr td.target { 599 | background-color:#ddddcc; 600 | } 601 | #keyboardInputMaster tbody tr td table tbody tr td:hover, 602 | #keyboardInputMaster tbody tr td table tbody tr td.hover { 603 | border-top:1px solid #d5d5d5; 604 | border-right:1px solid #555555; 605 | border-bottom:1px solid #555555; 606 | border-left:1px solid #d5d5d5; 607 | background-color:#cccccc; 608 | } 609 | #keyboardInputMaster thead tr th span:active, 610 | #keyboardInputMaster thead tr th span.pressed, 611 | #keyboardInputMaster tbody tr td table tbody tr td:active, 612 | #keyboardInputMaster tbody tr td table tbody tr td.pressed { 613 | border-top:1px solid #555555 !important; 614 | border-right:1px solid #d5d5d5; 615 | border-bottom:1px solid #d5d5d5; 616 | border-left:1px solid #555555; 617 | background-color:#cccccc; 618 | } 619 | 620 | #keyboardInputMaster tbody tr td table tbody tr td small { 621 | display:block; 622 | text-align:center; 623 | font-size:0.6em !important; 624 | line-height:1.1em; 625 | } 626 | 627 | #keyboardInputMaster tbody tr td div label { 628 | position:absolute; 629 | bottom:0.2em; 630 | left:0.3em; 631 | } 632 | #keyboardInputMaster tbody tr td div label input { 633 | background-color:#f6f6f6; 634 | vertical-align:middle; 635 | font-size:inherit; 636 | width:1.1em; 637 | height:1.1em; 638 | } 639 | #keyboardInputMaster tbody tr td div var { 640 | position:absolute; 641 | bottom:0px; 642 | right:3px; 643 | font-weight:bold; 644 | font-style:italic; 645 | color:#444444; 646 | } 647 | 648 | .keyboardInputInitiator { 649 | margin:0px 3px; 650 | vertical-align:middle; 651 | cursor:pointer; 652 | } 653 | -------------------------------------------------------------------------------- /docs/vic20/cpu/cpu6502assemble.js: -------------------------------------------------------------------------------- 1 | function maskedEval(scr, labels) { 2 | var mask = {}; 3 | for (p in this) 4 | mask[p] = undefined; 5 | for (l in labels) 6 | mask[l] = labels[l]; 7 | scr = scr.toLowerCase(); 8 | return (new Function("with(this) { return " + scr + "; }")).call(mask); 9 | } 10 | function Cpu6502assembler() { 11 | var feedbackArray = new Array(); 12 | var inError = false; 13 | var m = new Array(65536); 14 | var linenum = 0 15 | , oline = ""; 16 | function error(str) { 17 | feedbackArray.push({ 18 | type: "Error", 19 | line: linenum + 1, 20 | message: str + "\nLine " + (linenum + 1) + ": " + str + "\nCompile aborted" 21 | }); 22 | console.debug("ERRORERRORERRORERROR", str); 23 | inError = true; 24 | } 25 | function info(str) { 26 | feedbackArray.push({ 27 | type: "Info", 28 | message: str 29 | }); 30 | } 31 | function getNumberOrLabelValue(line, pass, labels) { 32 | line = $.trim(line); 33 | line = line.toLowerCase(); 34 | line = line.replace(/\$([0-9a-f]+)/gi, "0x$1"); 35 | line = line.replace(/\%([01]+)/gi, "parseInt('$1',2)"); 36 | var lg = line[0]; 37 | if (lg == '<' || lg == '>') 38 | line = line.substring(1); 39 | var num; 40 | try { 41 | num = maskedEval(line, labels); 42 | console.debug(line, num); 43 | if ((typeof num) == 'string') { 44 | if (num.length < 1) { 45 | error("String constant too short '" + line + "'"); 46 | num = 0; 47 | } else if (num.length > 1) { 48 | error("String constant too long '" + line + "'"); 49 | num = 0; 50 | } else 51 | num = num.charCodeAt(0); 52 | } 53 | } catch (e) { 54 | console.debug("exception ", e); 55 | num = 0; 56 | if (pass == 2) { 57 | error("Cannot evaluate(1) '" + line + "'"); 58 | debugger ; 59 | } 60 | } 61 | if (num == undefined) { 62 | num = 0; 63 | if (pass == 2) { 64 | error("Cannot evaluate(2) '" + line + "'"); 65 | } 66 | } 67 | if (lg == '<') 68 | num &= 255; 69 | if (lg == '>') 70 | num >>= 8; 71 | return num; 72 | } 73 | var nems = ["BRKs", "ORA(z,x)", "JAMi", "SLO(z,x)", "NOPz", "ORAz", "ASLz", "SLOz", "PHPs", "ORA#", "ASLi", "ANC#", "NOPa", "ORAa", "ASLa", "SLOa", "BPLr", "ORA(z),y", "JAMi", "SLO(z),y", "NOPz,x", "ORAz,x", "ASLz,x", "SLOz,x", "CLCi", "ORAa,y", "NOPi", "SLOa,y", "NOPa,x", "ORAa,x", "ASLa,x", "SLOa,x", "JSRa", "AND(z,x)", "JAMi", "RLA(z,x)", "BITz", "ANDz", "ROLz", "RLAz", "PLPs", "AND#", "ROLi", "ANC#", "BITa", "ANDa", "ROLa", "RLAa", "BMIr", "AND(z),y", "JAMi", "RLA(z),y", "NOPz,x", "ANDz,x", "ROLz,x", "RLAz,x", "SECi", "ANDa,y", "NOPi", "RLAa,y", "NOPa,x", "ANDa,x", "ROLa,x", "RLAa,x", "RTIs", "EOR(z,x)", "JAMi", "SRE(z,x)", "NOPz", "EORz", "LSRz", "SREz", "PHAs", "EOR#", "LSRi", "ASR#", "JMPa", "EORa", "LSRa", "SREa", "BVCr", "EOR(z),y", "JAMi", "SRE(z),y", "NOPz,x", "EORz,x", "LSRz,x", "SREz,x", "CLIi", "EORa,y", "NOPi", "SREa,y", "NOPa,x", "EORa,x", "LSRa,x", "SREa,x", "RTSs", "ADC(z,x)", "JAMi", "RRA(z,x)", "NOPz", "ADCz", "RORz", "RRAz", "PLAs", "ADC#", "RORi", "ARR#", "JMP(a)", "ADCa", "RORa", "RRAa", "BVSr", "ADC(z),y", "JAMi", "RRA(z),y", "NOPz,x", "ADCz,x", "RORz,x", "RRAz,x", "SEIi", "ADCa,y", "NOPi", "RRAa,y", "NOPa,x", "ADCa,x", "RORa,x", "RRAa,x", "NOP#", "STA(z,x)", "NOP#", "SAX(z,x)", "STYz", "STAz", "STXz", "SAXz", "DEYi", "NOP#", "TXAi", "ANE#", "STYa", "STAa", "STXa", "SAXa", "BCCr", "STA(z),y", "JAMi", "SHAa,x", "STYz,x", "STAz,x", "STXz,y", "SAXz,y", "TYAi", "STAa,y", "TXSi", "SHSa,x", "SHYa,y", "STAa,x", "SHXa,y", "SHAa,y", "LDY#", "LDA(z,x)", "LDX#", "LAX(z,x)", "LDYz", "LDAz", "LDXz", "LAXz", "TAYi", "LDA#", "TAXi", "LXA#", "LDYa", "LDAa", "LDXa", "LAXa", "BCSr", "LDA(z),y", "JAMi", "LAX(z),y", "LDYz,x", "LDAz,x", "LDXz,y", "LAXz,y", "CLVi", "LDAa,y", "TSXi", "LAEa,y", "LDYa,x", "LDAa,x", "LDXa,y", "LAXa,y", "CPY#", "CMP(z,x)", "NOP#", "DCP(z,x)", "CPYz", "CMPz", "DECz", "DCPz", "INYi", "CMP#", "DEXi", "SBX#", "CPYa", "CMPa", "DECa", "DCPa", "BNEr", "CMP(z),y", "JAMi", "DCP(z),y", "NOPz,x", "CMPz,x", "DECz,x", "DCPz,x", "CLDi", "CMPa,y", "NOPi", "DCPa,y", "NOPa,x", "CMPa,x", "DECa,x", "DCPa,x", "CPX#", "SBC(z,x)", "NOP#", "ISB(z,x)", "CPXz", "SBCz", "INCz", "ISBz", "INXi", "SBC#", "NOPi", "SBC#", "CPXa", "SBCa", "INCa", "ISBa", "BEQr", "SBC(z),y", "JAMi", "ISB(z),y", "NOPz,x", "SBCz,x", "INCz,x", "ISBz,x", "SEDi", "SBCa,y", "NOPi", "ISBa,y", "NOPa,x", "SBCa,x", "INCa,x", "ISBa,x"]; 74 | var datanems = ["DB", "DCB", "BYTE", "\\.BYTE", "EQUB", "DCW", "WORD", "\\.WORD", "EQUW", "DCL", "LONG", "\\.LONG", "EQUL"]; 75 | var label1 = "(?:\\.([a-z_][a-z0-9_\\$\\.]*))?"; 76 | var label2 = "([a-z_][a-z0-9_\\$\\.]*):"; 77 | var label3 = "([a-z_][a-z0-9_\\$\\.]*)"; 78 | var comment = "(?:;.*)?"; 79 | var regexps = { 80 | ORG: /^(?:\*=|org)[ \\t]*([\$%0-9A-Za-z_\-\+]+)/i, 81 | LABEL1: /^\.([a-z_][a-z0-9_\$\.]*)/i, 82 | LABEL2: /^([a-z_][a-z0-9_\$\.]*):/i, 83 | LABEL3: /^([a-z_][a-z0-9_\$\.]*)[ \t]*\r?\n?$/i, 84 | IGNORE1: /^#.*/, 85 | COMMENT: /^[ \t]*;.*/, 86 | ASSIGNMENT: [/^([a-z_][a-z0-9_\$]*)[ \t]*eqm[ \t]*([^;\r\n]*)/i, /^([a-z_][a-z0-9_\$]*)[ \t]*=[ \t]*([^;\r\n]*)/i, /^([a-z_][a-z0-9_\$]*)[ \t]*equ[ \t]*([^;\r\n]*)/i] 87 | }; 88 | var nemlut = {}; 89 | for (var i = 0; i < nems.length; i++) { 90 | var nem = nems[i]; 91 | var onem = nem; 92 | nemlut[onem] = i; 93 | nem = nem.replace(/\(/g, "[ \\t]+\\("); 94 | nem = nem.replace(/\)/g, "\\)"); 95 | if (nem.indexOf("s") != -1 || nem.indexOf("i") != -1) { 96 | nem = nem.replace(/[si]/g, ""); 97 | } else if (nem.indexOf("z") != -1 || nem.indexOf("a") != -1) { 98 | if (nem.indexOf("(") == -1) { 99 | nem = nem.replace(/[az]/g, "[ \\t]+([\\$%0-9a-z_\\-\\+]+)"); 100 | } else { 101 | nem = nem.replace(/[az]/g, "[ \\t]*([\\$%0-9a-z_\\-\\+]+)"); 102 | } 103 | } else if (nem.indexOf("#") != -1) { 104 | nem = nem.replace(/[#]/g, "[ \\t]*#([^ \\t;]+)"); 105 | } else if (nem.indexOf("r") != -1) { 106 | nem = nem.replace(/[r]/g, "[ \\t]+([\\$%0-9a-z_\\-\\+]*)"); 107 | } 108 | nem1 = "/^(?:" + label1 + ")?[ \\t]*" + nem + "[ \\t]*" + comment + "/i"; 109 | nem2 = "/^(?:" + label2 + ")?[ \\t]*" + nem + "[ \\t]*" + comment + "/i"; 110 | nem3 = "/^(?:" + label3 + ")?[ \\t]*" + nem + "[ \\t]*" + comment + "/i"; 111 | regexps[onem] = []; 112 | regexps[onem][0] = eval(nem1); 113 | regexps[onem][1] = eval(nem2); 114 | regexps[onem][2] = eval(nem3); 115 | } 116 | for (var i = 0; i < datanems.length; i++) 117 | regexps[datanems[i]] = eval("/^" + datanems[i] + "[ \\t]*([^\\r\\n;]*)[ \\t]*" + comment + "/i"); 118 | function nextToken(text) { 119 | if (text.length == 0) 120 | return null; 121 | while (text.charCodeAt(0) > 128 || text[0] == ' ' || text[0] == '\r' || text[0] == '\n' || text[0] == '\t') { 122 | text = text.substring(1); 123 | if (text.length == 0) 124 | return null; 125 | } 126 | var result; 127 | for (var key in regexps) { 128 | var match; 129 | if (regexps[key].length) { 130 | for (var i = 0; i < regexps[key].length; i++) { 131 | if (match = regexps[key][i].exec(text)) { 132 | if (result === undefined || match[0].length > result.match[0].length) { 133 | result = { 134 | match: match, 135 | type: key 136 | }; 137 | } 138 | } 139 | } 140 | } else { 141 | if (match = regexps[key].exec(text)) { 142 | if (result === undefined || match[0].length > result.match[0].length) { 143 | result = { 144 | match: match, 145 | type: key 146 | }; 147 | } 148 | } 149 | } 150 | } 151 | if (result) 152 | return result; 153 | alert("error no match '" + text + "'"); 154 | exit; 155 | } 156 | this.compile = function(compileText) { 157 | if (typeof (compileText) == 'function') { 158 | compileText = fnToText(compileText); 159 | } 160 | feedbackArray = new Array(); 161 | info("Compile start at " + new Date()); 162 | var labels = {}; 163 | inError = false; 164 | for (var i = 0; i < m.length; i++) 165 | m[i] = undefined; 166 | for (var pass = 1; !inError && pass <= 2; pass++) { 167 | console.debug(">> PASS", pass); 168 | var pc = -1; 169 | var test = compileText.split("\n"); 170 | linenum = -1; 171 | while ((++linenum) != test.length) { 172 | var result = nextToken(test[linenum]); 173 | if (result == null) 174 | continue; 175 | console.debug(result.type, result.match); 176 | if (result.type == "IGNORE1") 177 | continue; 178 | if (result.type == "ORG") { 179 | pc = getNumberOrLabelValue(result.match[1], pass, labels); 180 | if (pc > 65535) { 181 | pc = -1; 182 | error("ORIGIN exceeds 64KB"); 183 | } else if (pc < 0) { 184 | error("ORIGIN must be greater than or equal to 0"); 185 | } 186 | continue; 187 | } 188 | if (result.type == "LABEL1" || result.type == "LABEL2" || result.type == "LABEL3") { 189 | labels[result.match[1].toLowerCase()] = pc; 190 | continue; 191 | } 192 | if (result.type == "ASSIGNMENT") { 193 | var num = getNumberOrLabelValue(result.match[2], pass, labels); 194 | labels[result.match[1].toLowerCase()] = num; 195 | continue; 196 | } 197 | if (pc == -1) { 198 | error("Origin not set"); 199 | break; 200 | } 201 | if (result.match[1]) { 202 | labels[result.match[1].toLowerCase()] = pc; 203 | } 204 | if (result.type == 'DB' || result.type == 'DCB' || result.type == 'BYTE' || result.type == 'EQUB' || result.type == '\\.BYTE') { 205 | var vals = result.match[1].split(","); 206 | for (var i = 0; i < vals.length; i++) { 207 | var num = getNumberOrLabelValue(vals[i], pass, labels); 208 | if (num > 255 || num < -128) { 209 | error("byte constant too big"); 210 | break; 211 | } 212 | m[pc++] = num & 0xFF; 213 | } 214 | continue; 215 | } 216 | if (result.type == 'DCW' || result.type == 'WORD' || result.type == 'EQUW') { 217 | var vals = result.match[1].split(","); 218 | for (var i = 0; i < vals.length; i++) { 219 | var num = getNumberOrLabelValue(vals[i], pass, labels); 220 | if (num > 65535 || num < -32768) { 221 | error("word constant too big"); 222 | break; 223 | } 224 | m[pc++] = num & 255; 225 | m[pc++] = (num >> 8) & 0xFF; 226 | } 227 | continue; 228 | } 229 | if (result.type == 'DCL' || result.type == 'LONG' || result.type == 'EQUL') { 230 | var vals = result.match[1].split(","); 231 | for (var i = 0; i < vals.length; i++) { 232 | var num = getNumberOrLabelValue(vals[i], pass, labels); 233 | if (num > 4294967295 || num < -214783648) { 234 | error("word constant too big"); 235 | break; 236 | } 237 | m[pc++] = num & 255; 238 | m[pc++] = (num >> 8) & 255; 239 | m[pc++] = (num >> 16) & 255; 240 | m[pc++] = (num >> 24) & 255; 241 | } 242 | continue; 243 | } 244 | if (result.type.indexOf("z") != -1 || result.type.indexOf("a") != -1) { 245 | var num = getNumberOrLabelValue(result.match[2], pass, labels); 246 | if (num < 0 || num > 65535) { 247 | if (pass == 1) 248 | continue; 249 | else 250 | error("Absolute constant out of 16 bit range '" + result.text + "'"); 251 | break; 252 | } 253 | if (result.type.indexOf("z") != -1) { 254 | if (num > 255) { 255 | if (result.type.indexOf("(") != -1) { 256 | error("ZP constant out of 8 bit range '" + num + "'" + result.text); 257 | break; 258 | } 259 | result.type = result.type.replace(/z/g, "a"); 260 | } 261 | } 262 | if (result.type.indexOf("a") != -1) { 263 | if (num < 256) { 264 | var newType = result.type.replace(/a/g, "z"); 265 | if (nemlut[newType] !== undefined) { 266 | result.type = newType; 267 | } 268 | } 269 | } 270 | } 271 | m[pc++] = nemlut[result.type]; 272 | if (result.match === undefined) { 273 | continue; 274 | } 275 | if (result.type.indexOf("i") != -1 || result.type.indexOf("s") != -1) { 276 | continue; 277 | } 278 | var num = getNumberOrLabelValue(result.match[2], pass, labels); 279 | if (result.type.indexOf("r") != -1) { 280 | var diff = num - pc - 1; 281 | if (diff > 127 || diff < -128) { 282 | if (pass == 2) { 283 | error("Branch out of range " + num); 284 | break; 285 | } else 286 | diff = 0; 287 | } 288 | m[pc++] = (diff >= 0) ? diff : (256 + diff); 289 | continue; 290 | } 291 | if (result.type.indexOf("#") != -1) { 292 | if (num < -128 || num > 255) { 293 | error("Constant out of 8 bit range '" + num + "'"); 294 | break; 295 | } 296 | m[pc++] = num & 0xFF; 297 | continue; 298 | } 299 | if (result.type.indexOf("z") != -1) { 300 | m[pc++] = num & 0xFF; 301 | continue; 302 | } 303 | if (result.type.indexOf("a") != -1) { 304 | m[pc++] = num & 0xFF; 305 | m[pc++] = (num >> 8) & 0xFF; 306 | continue; 307 | } 308 | } 309 | } 310 | if (!inError) { 311 | info("Compile success"); 312 | } else { 313 | info("Compile failed"); 314 | } 315 | for (var i = 0; i < 256; i++) { 316 | var str = ""; 317 | var po = false; 318 | for (var j = 0; j < 256; j++) { 319 | var vv = m[(i << 8) + j]; 320 | if (vv !== undefined) { 321 | if (!po) { 322 | str = toHex4((i << 8) + j) + ": " + str; 323 | po = true; 324 | } 325 | str += toHex2(vv) + " "; 326 | } 327 | } 328 | console.debug(str); 329 | } 330 | return { 331 | feedback: feedbackArray, 332 | m: m, 333 | success: !inError 334 | }; 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /docs/vic20/vic20/tapedrive.js: -------------------------------------------------------------------------------- 1 | $.extend(Config, new function() { 2 | this.EXCESS = 0; 3 | this.S = ((0x30 + this.EXCESS) * 8); 4 | this.M = ((0x42 + this.EXCESS) * 8); 5 | this.L = ((0x56 + this.EXCESS) * 8); 6 | this.tapePlay = false; 7 | } 8 | ); 9 | EventTape = { 10 | createEjectEvent: function() { 11 | return { 12 | type: "EventTape", 13 | rewind: false, 14 | filepath: "" 15 | }; 16 | }, 17 | createInsertEvent: function(filepath) { 18 | return { 19 | type: "EventTape", 20 | rewind: false, 21 | filepath: filepath 22 | }; 23 | }, 24 | createRewindEvent: function() { 25 | return { 26 | type: "EventTape", 27 | rewind: true, 28 | filepath: "" 29 | }; 30 | } 31 | }; 32 | function EOFException(message) { 33 | this.message = message; 34 | console.debug("EOFException", message); 35 | } 36 | function InvalidFormatException(message) { 37 | this.message = message; 38 | console.debug("InvalidFormatException", message); 39 | } 40 | function TapeDrive() { 41 | var tapeDatasource = null; 42 | this.isTapeLoaded = function() { 43 | return tapeDatasource != null; 44 | } 45 | var pulseClock = 0; 46 | var pulseWidth = 0; 47 | var tapeDriveMotor = false; 48 | this.triggerTapeMotor = function(isTapeMotorGoing) { 49 | tapeDriveMotor = isTapeMotorGoing; 50 | console.log("Tape drive motor " + (isTapeMotorGoing ? "on" : "off")); 51 | } 52 | this.readTapeBit = function() { 53 | if (tapeDatasource != null && tapeDriveMotor) { 54 | if (++pulseClock > pulseWidth) { 55 | try { 56 | pulseWidth = tapeDatasource.nextPulseWidth(); 57 | } catch (e) { 58 | if (e instanceof EOFException) { 59 | pulseWidth = -1; 60 | } else { 61 | throw e; 62 | } 63 | } 64 | if (pulseWidth == -1) { 65 | console.log("Tape end"); 66 | Config.tapePlay = false; 67 | return true; 68 | } 69 | pulseClock = 0; 70 | } 71 | return pulseClock < (pulseWidth >> 1); 72 | } 73 | return true; 74 | } 75 | this.handleEvent = function(event) { 76 | if (event.rewind) { 77 | console.debug("rewind tape"); 78 | tapeDatasource.rewind(); 79 | } else if (event.filepath == null) { 80 | tapeDatasource = null; 81 | console.debug("eject tape"); 82 | } else { 83 | try { 84 | console.debug("load tape"); 85 | tapeDatasource = new TapFile(event.filepath); 86 | } catch (e) { 87 | if (e instanceof InvalidFormatException) { 88 | tapeDatasource = new CsmFile(event.filepath); 89 | } else { 90 | throw e; 91 | } 92 | } 93 | } 94 | return true; 95 | } 96 | EventManager.listen("EventTape", this.handleEvent); 97 | } 98 | function PulseWidthsFromBitData(bitData) { 99 | var bit = false; 100 | var count = 0; 101 | this.reset = function() { 102 | count = 1; 103 | } 104 | this.reset(); 105 | this.nextPulseWidth = function() { 106 | if (--count == 0) { 107 | count = 20; 108 | return Config.L; 109 | } 110 | if (count == 19) { 111 | return bitData.hasNextBit() ? Config.M : Config.S; 112 | } 113 | bit = ((count & 1) != 0) ? !bit : bitData.nextBit(); 114 | return bit ? Config.M : Config.S; 115 | } 116 | } 117 | function PulseWidthSyncGenerator() { 118 | var value = 0; 119 | var count = 0; 120 | var bit = false; 121 | var xor = 0; 122 | this.reset = function(isRepeat) { 123 | value = isRepeat ? 0x0A : 0x8A; 124 | count = 0; 125 | } 126 | this.nextPulseWidth = function() { 127 | if (--count < 0) { 128 | if ((--value & 0xF) == 0) { 129 | throw new EOFException(); 130 | } 131 | count = 19; 132 | xor = 1; 133 | return Config.L; 134 | } 135 | if (count == 18) { 136 | return Config.M; 137 | } 138 | if (count < 1) { 139 | return ((xor ^ count) != 0) ? Config.S : Config.M; 140 | } 141 | if ((count & 1) != 0) { 142 | bit = ((value >> (8 - (count >> 1))) & 1) != 0; 143 | xor ^= bit ? 1 : 0; 144 | } else { 145 | bit = !bit; 146 | } 147 | return bit ? Config.M : Config.S; 148 | } 149 | } 150 | function TapeBitDataStream() { 151 | var data = null; 152 | var offset = 0; 153 | var datum = 0; 154 | var xor = 0; 155 | var xorbyte = 0; 156 | var bitpos = 0; 157 | var hasAddedByteChecksum = false; 158 | this.nextBit = function() { 159 | datum >>= 1; 160 | bitpos++; 161 | if (bitpos == 8) { 162 | return xor > 0; 163 | } 164 | if (bitpos > 8) { 165 | if (++offset >= data.length) { 166 | if (!hasAddedByteChecksum) { 167 | hasAddedByteChecksum = true; 168 | datum = xorbyte; 169 | } else { 170 | throw new EOFException(); 171 | } 172 | } else { 173 | datum = data[offset] & 0xFF; 174 | xorbyte ^= datum; 175 | } 176 | bitpos = 0; 177 | xor = 1; 178 | } 179 | var result = datum & 1; 180 | xor ^= result; 181 | return result != 0; 182 | } 183 | this.hasNextBit = function() { 184 | return offset < data.length; 185 | } 186 | this.setData = function(srcdata, offsetsrc, length) { 187 | data = new Array(); 188 | for (var i = 0; i < length; i++) 189 | data.push(srcdata[offsetsrc + i]); 190 | offset = -1; 191 | bitpos = 10; 192 | xorbyte = 0; 193 | hasAddedByteChecksum = false; 194 | } 195 | } 196 | function TapFile(data) { 197 | var FILE_MAGIC = "C64-TAPE-RAW"; 198 | var HEADER_LENGTH = 0x15; 199 | var offset = 0; 200 | var rewindOffset = 0; 201 | var tapVersion = 0; 202 | var fileDataSize = 0; 203 | this.skipBytes = function(count) { 204 | offset += count; 205 | if (offset > data.length) { 206 | offset = data.length; 207 | } 208 | } 209 | this.nextByte = function() { 210 | if (offset == data.length) { 211 | return -1; 212 | } 213 | return data[offset++] & 0xFF; 214 | } 215 | this.rewind = function() { 216 | offset = rewindOffset; 217 | } 218 | this.nextPulseWidth = function() { 219 | var datum = this.nextByte(); 220 | if (datum == 0) { 221 | if (tapVersion != 1) { 222 | datum = 512 << 8; 223 | } else { 224 | var nextByte = this.nextByte(); 225 | if (nextByte == -1) { 226 | console.log("Unexpected end of tape encountered during extended pulse"); 227 | return -1; 228 | } 229 | datum = nextByte; 230 | nextByte = this.nextByte(); 231 | if (nextByte == -1) { 232 | console.log("Unexpected end of tape encountered during extended pulse"); 233 | return -1; 234 | } 235 | datum |= nextByte << 8; 236 | nextByte = this.nextByte(); 237 | if (nextByte == -1) { 238 | console.log("Unexpected end of tape encountered during extended pulse"); 239 | return -1; 240 | } 241 | datum |= nextByte << 16; 242 | } 243 | } else if (datum != -1) { 244 | datum <<= 3; 245 | } 246 | return datum; 247 | } 248 | if (FILE_MAGIC != bin2String(data, 0, FILE_MAGIC.length)) { 249 | throw new InvalidFormatException("File is not a TAP file: Header magic is incorrect"); 250 | } 251 | this.skipBytes(FILE_MAGIC.length); 252 | tapVersion = this.nextByte(); 253 | if (tapVersion != 0 && tapVersion != 1) { 254 | alert("Tap version is not understood, version=" + tapVersion + ", should be either 0 or 1"); 255 | } 256 | this.skipBytes(3); 257 | fileDataSize = this.nextByte() + (this.nextByte() << 8) + (this.nextByte() << 16) + (this.nextByte() << 24); 258 | rewindOffset = offset; 259 | } 260 | function CsmFile(filedata) { 261 | var HEADER_SIZE = 192; 262 | var TapeState = { 263 | PILOT_HEADER: 0, 264 | SYNC_HEADER: 1, 265 | HEADER: 2, 266 | HEADER_END_OF_DATA: 3, 267 | PILOT_HEADER_END: 4, 268 | SYNC_HEADER_REPEAT: 5, 269 | HEADER_REPEAT: 6, 270 | HEADER_REPEAT_END_OF_DATA: 7, 271 | PILOT_HEADER_TRAILER: 8, 272 | SILENCE: 9, 273 | PILOT_DATA: 10, 274 | SYNC_DATA: 11, 275 | DATA: 12, 276 | DATA_END_OF_DATA: 13, 277 | PILOT_DATA_END: 14, 278 | SYNC_DATA_REPEAT: 15, 279 | DATA_REPEAT: 16, 280 | DATA_REPEAT_END_OF_DATA: 17, 281 | PILOT_DATA_TRAILER: 18, 282 | END_TAPE: 19 283 | } 284 | var currentOffset = 0; 285 | var data = filedata; 286 | var dataSize = 0; 287 | var bitData = null; 288 | var pulseData = null; 289 | var pulseDataSync = null; 290 | var state = 0; 291 | var count = 0; 292 | this.rewind = function() { 293 | pulseDataSync = new PulseWidthSyncGenerator(); 294 | bitData = new TapeBitDataStream(); 295 | pulseData = new PulseWidthsFromBitData(bitData); 296 | state = TapeState.PILOT_HEADER; 297 | count = 0x600; 298 | currentOffset = 0; 299 | } 300 | this.nextPulseWidth = function() { 301 | if (state == TapeState.PILOT_HEADER) { 302 | if (--count == 0) { 303 | state = TapeState.SYNC_HEADER; 304 | console.log(state); 305 | pulseDataSync.reset(false); 306 | } else { 307 | return Config.S; 308 | } 309 | } 310 | if (state == TapeState.SYNC_HEADER) { 311 | try { 312 | return pulseDataSync.nextPulseWidth(); 313 | } catch (ex) { 314 | if (!(ex instanceof EOFException)) 315 | throw ex; 316 | state = TapeState.HEADER; 317 | console.log(state); 318 | pulseData.reset(); 319 | bitData.setData(data, currentOffset, HEADER_SIZE); 320 | console.log("header currentOffset = " + currentOffset.toString(16)); 321 | var startAddress = (data[currentOffset + 1] & 0xFF) | ((data[currentOffset + 2] & 0xFF) << 8); 322 | console.log("start address = " + startAddress.toString(16)); 323 | var endAddress = (data[currentOffset + 3] & 0xFF) | ((data[currentOffset + 4] & 0xFF) << 8); 324 | console.log("end address = " + endAddress.toString(16)); 325 | dataSize = endAddress - startAddress; 326 | console.log("dataSize = " + dataSize.toString(16)); 327 | } 328 | } 329 | if (state == TapeState.HEADER) { 330 | try { 331 | return pulseData.nextPulseWidth(); 332 | } catch (ex) { 333 | if (!(ex instanceof EOFException)) 334 | throw ex; 335 | state = TapeState.HEADER_END_OF_DATA; 336 | console.log(state); 337 | count = 3; 338 | } 339 | } 340 | if (state == TapeState.HEADER_END_OF_DATA) { 341 | state = TapeState.PILOT_HEADER_END; 342 | console.log(state); 343 | count = 0x4F; 344 | } 345 | if (state == TapeState.PILOT_HEADER_END) { 346 | if (--count == 0) { 347 | state = TapeState.SYNC_HEADER_REPEAT; 348 | console.log(state); 349 | pulseDataSync.reset(true); 350 | } else 351 | return Config.S; 352 | } 353 | if (state == TapeState.SYNC_HEADER_REPEAT) { 354 | try { 355 | return pulseDataSync.nextPulseWidth(); 356 | } catch (ex) { 357 | if (!(ex instanceof EOFException)) 358 | throw ex; 359 | state = TapeState.HEADER_REPEAT; 360 | console.log(state); 361 | pulseData.reset(); 362 | bitData.setData(data, currentOffset, HEADER_SIZE); 363 | currentOffset += HEADER_SIZE; 364 | } 365 | } 366 | if (state == TapeState.HEADER_REPEAT) { 367 | try { 368 | return pulseData.nextPulseWidth(); 369 | } catch (ex) { 370 | if (!(ex instanceof EOFException)) 371 | throw ex; 372 | state = TapeState.HEADER_REPEAT_END_OF_DATA; 373 | console.log(state); 374 | count = 3; 375 | } 376 | } 377 | if (state == TapeState.HEADER_REPEAT_END_OF_DATA) { 378 | state = TapeState.PILOT_HEADER_TRAILER; 379 | console.log(state); 380 | count = 0x4E; 381 | } 382 | if (state == TapeState.PILOT_HEADER_TRAILER) { 383 | if (--count == 0) { 384 | state = TapeState.SILENCE; 385 | console.log(state); 386 | count = 400000; 387 | } 388 | return Config.S; 389 | } 390 | if (state == TapeState.SILENCE) { 391 | if (--count == 0) { 392 | state = TapeState.PILOT_DATA; 393 | console.log(state); 394 | count = 0x1A00; 395 | } 396 | return 0; 397 | } 398 | if (state == TapeState.PILOT_DATA) { 399 | if (--count == 0) { 400 | state = TapeState.SYNC_DATA; 401 | console.log(state); 402 | pulseDataSync.reset(false); 403 | } 404 | return Config.S; 405 | } 406 | if (state == TapeState.SYNC_DATA) { 407 | try { 408 | return pulseDataSync.nextPulseWidth(); 409 | } catch (ex) { 410 | if (!(ex instanceof EOFException)) 411 | throw ex; 412 | state = TapeState.DATA; 413 | console.log(state); 414 | pulseData.reset(); 415 | console.log("data currentOffset = " + currentOffset.toString(16)); 416 | bitData.setData(data, currentOffset, dataSize); 417 | } 418 | } 419 | if (state == TapeState.DATA) { 420 | try { 421 | return pulseData.nextPulseWidth(); 422 | } catch (ex) { 423 | if (!(ex instanceof EOFException)) 424 | throw ex; 425 | state = TapeState.DATA_END_OF_DATA; 426 | console.log(state); 427 | count = 3; 428 | } 429 | } 430 | if (state == TapeState.DATA_END_OF_DATA) { 431 | state = TapeState.PILOT_DATA_END; 432 | console.log(state); 433 | count = 0x4F; 434 | } 435 | if (state == TapeState.PILOT_DATA_END) { 436 | if (--count == 0) { 437 | state = TapeState.PILOT_DATA_TRAILER; 438 | console.log(state); 439 | count = 0x4E; 440 | } 441 | return Config.S; 442 | } 443 | if (state == TapeState.PILOT_DATA_TRAILER) { 444 | if (--count == 0) { 445 | state = TapeState.SYNC_DATA_REPEAT; 446 | console.log(state); 447 | pulseDataSync.reset(true); 448 | } 449 | return Config.S; 450 | } 451 | if (state == TapeState.SYNC_DATA_REPEAT) { 452 | try { 453 | return pulseDataSync.nextPulseWidth(); 454 | } catch (ex) { 455 | if (!(ex instanceof EOFException)) 456 | throw ex; 457 | state = TapeState.DATA_REPEAT; 458 | console.log(state); 459 | pulseData.reset(); 460 | bitData.setData(data, currentOffset, dataSize); 461 | currentOffset += dataSize; 462 | } 463 | } 464 | if (state == TapeState.DATA_REPEAT) { 465 | try { 466 | return pulseData.nextPulseWidth(); 467 | } catch (ex) { 468 | if (!(ex instanceof EOFException)) 469 | throw ex; 470 | state = TapeState.DATA_REPEAT_END_OF_DATA; 471 | console.log(state); 472 | } 473 | } 474 | if (state == TapeState.DATA_REPEAT_END_OF_DATA) { 475 | if (currentOffset == data.length) { 476 | state = TapeState.END_TAPE; 477 | console.log(state); 478 | } else { 479 | console.log("next file: " + currentOffset.toString(16) + "/" + data.length.toString(16)); 480 | state = TapeState.PILOT_HEADER; 481 | console.log(state); 482 | count = 0x1000; 483 | } 484 | return 0; 485 | } 486 | if (state == TapeState.END_TAPE) { 487 | return -1; 488 | } 489 | throw new RuntimeException("CsmFile::nextPulseWidth INVALID STATE"); 490 | } 491 | this.rewind(); 492 | } 493 | -------------------------------------------------------------------------------- /docs/c64/c64/c64_tiny_host.js: -------------------------------------------------------------------------------- 1 | var statusElement = document.getElementById('status'); 2 | var progressElement = document.getElementById('progress'); 3 | var diskBoxCombo = document.getElementById('diskbox_combo'); 4 | var networkStatusDiv = document.getElementById('network_status'); 5 | var highScoreStatus = document.getElementById('highScore_status'); 6 | var highScoreButton = document.getElementById('highScore_button'); 7 | var highScoreTitle = document.getElementById('highScore_title'); 8 | var helpElement = document.getElementById('help_div'); 9 | var highScoreSnapshot = 'gridrunner_highscore.s64'; 10 | var progressSnapshot = 'gridrunner_savedgame.s64'; 11 | 12 | 13 | var Module = { 14 | keyboardListeningElement: document.getElementById('c64TextInputReceiver'), 15 | hasFocus: false, 16 | startSequence: 0, 17 | error: function(v) { 18 | console.log(v); 19 | }, 20 | preRun: [ function(){Module.c64preRun();} ], 21 | postRun: [ function(){Module.c64postRun();} ], 22 | c64preRun: function() { 23 | try { 24 | FS.mkdir('/data'); 25 | FS.mount(IDBFS, {}, '/data'); 26 | FS.syncfs(true, function (err) { 27 | Module.c64FsSync(); 28 | }); 29 | } 30 | catch(e) { 31 | Module.startSequence|= 2; 32 | } 33 | }, 34 | c64postRun: function() { 35 | Module.startSequence|= 1; 36 | if( Module.startSequence == 3 ) Module.c64startup(); 37 | }, 38 | c64FsSync: function() { 39 | Module.startSequence|= 2; 40 | if( Module.startSequence == 3 ) Module.c64startup(); 41 | }, 42 | c64startup: function() { 43 | Module.keyboardListeningElement.focus(); 44 | Module.hasFocus = true; 45 | Module.selectJoystick(0, 0); 46 | Module.setMute(true); 47 | 48 | c64_global_startup(); 49 | 50 | var room = this.requestValue('room'); 51 | var file = 'gridrunner.s64' 52 | var game = this.hasSavedGame(); 53 | if( game != null ) { 54 | this.loadGame(game); 55 | } 56 | else if( file != null ) { 57 | this.loadFileUrl(file, true); 58 | } 59 | else { 60 | this.updateLoadSnapshot(); 61 | } 62 | if( room != null ) { 63 | this.enableNetwork(room); 64 | } 65 | // Set the preferred joystick port for gridrunner 66 | setTimeout(function() { Module.ccall('js_selectJoystick', 'number', ['number', 'number'], [29, 32]); }, 5); 67 | // Save the game every 20 seconds 68 | setInterval(function() { Module.maybeSaveGame(progressSnapshot); }, 20000); 69 | }, 70 | requestValue: function(name) { 71 | if(name=(new RegExp('[?&]'+encodeURIComponent(name)+'=([^&]*)')).exec(location.search)) 72 | return decodeURIComponent(name[1]); 73 | return null; 74 | }, 75 | canvas: document.getElementById('canvas'), 76 | setStatus: function(text) { 77 | if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' }; 78 | if (text === Module.setStatus.text) return; 79 | var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); 80 | var now = Date.now(); 81 | if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon 82 | if (m) { 83 | text = m[1]; 84 | progressElement.value = parseInt(m[2])*100; 85 | progressElement.max = parseInt(m[4])*100; 86 | progressElement.hidden = false; 87 | } else { 88 | progressElement.value = null; 89 | progressElement.max = null; 90 | progressElement.hidden = true; 91 | } 92 | statusElement.innerHTML = text; 93 | }, 94 | registerCallback: function(name, callback) { 95 | return Module.ccall('js_registerCallback', 'number', ['string', 'number'], [name, Module.addFunction(callback, 'vii')]); 96 | }, 97 | totalDependencies: 0, 98 | monitorRunDependencies: function(left) { 99 | this.totalDependencies = Math.max(this.totalDependencies, left); 100 | Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); 101 | }, 102 | lastSnapshot: '', 103 | lastFilename: '', 104 | loadSnapshot: function(fileName, useStorage) { 105 | Module.keyboardListeningElement.focus(); 106 | Module.hasFocus = true; 107 | Module.lastSnapshot = fileName; 108 | Module.lastFilename = fileName; 109 | if( useStorage && fileName != '' && Module.snapshotStorage(0) ) { 110 | return; 111 | } 112 | Module.lastSnapshot = ''; 113 | Module.lastFilename = ''; 114 | var request; 115 | if (window.XMLHttpRequest) { 116 | request = new XMLHttpRequest(); 117 | } else { 118 | request = new ActiveXObject('Microsoft.XMLHTTP'); 119 | } 120 | // load 121 | request.open('GET', fileName, true); 122 | request.responseType = "arraybuffer"; 123 | request.onload = function (oEvent) { 124 | if (request.readyState === 4 && request.status === 200 ) { 125 | var arrayBuffer = request.response; 126 | if (arrayBuffer) { 127 | var byteArray = new Uint8Array(arrayBuffer); 128 | if( Module.ccall('js_LoadSnapshot', 'number', ['array', 'number'], [byteArray, byteArray.byteLength]) ) { 129 | Module.lastSnapshot = fileName; 130 | Module.lastFilename = fileName; 131 | highScoreButton.disabled = false; 132 | Module.uploadHighscore('', 'request', 'self'); 133 | } 134 | Module.updateDiskBox(); 135 | Module.updateLoadSnapshot(); 136 | } 137 | } 138 | }; 139 | request.send(); 140 | }, 141 | loadSnapshotData: function(snapshotData) { 142 | Module.keyboardListeningElement.focus(); 143 | Module.hasFocus = true; 144 | Module.lastSnapshot = ''; 145 | Module.lastFilename = ''; 146 | if(snapshotData) { 147 | if( Module.ccall('js_LoadSnapshot', 'number', ['array', 'number'], [snapshotData[1], snapshotData[1].byteLength]) ) { 148 | Module.lastSnapshot = snapshotData[0]; 149 | Module.lastFilename = snapshotData[0]; 150 | highScoreButton.disabled = snapshotData[2]; 151 | Module.uploadHighscore('', 'request', 'self'); 152 | } 153 | Module.updateDiskBox(); 154 | } 155 | }, 156 | saveSnapshotData: function() { 157 | var buffer = Module.ccall('js_SaveSnapshot', 'number', ['number'], [0]); 158 | var bsize = Module.ccall('js_SaveSnapshotSize', 'number', [], []); 159 | return [Module.lastSnapshot, new Uint8Array(Module.HEAPU8.buffer, buffer, bsize), highScoreButton.disabled]; 160 | }, 161 | loadFile: function(file, startup) { 162 | Module.keyboardListeningElement.focus(); 163 | Module.hasFocus = true; 164 | Module.lastSnapshot = ''; 165 | Module.lastFilename = file.name; 166 | 167 | var fileReader = new FileReader(); 168 | fileReader.onload = function() { 169 | var byteArray = new Uint8Array(this.result); 170 | if(startup) Module.ccall('js_removeDevice', 'number', ['number'], 0); 171 | Module.ccall('js_LoadFile', 'number', ['string', 'array', 'number', 'number'], [file.name, byteArray, byteArray.byteLength, startup]); 172 | //allowed: highScoreButton.disabled = true; 173 | highScoreTitle.innerHTML = ''; 174 | Module.updateDiskBox(); 175 | Module.updateLoadSnapshot(); 176 | } 177 | fileReader.readAsArrayBuffer(file); 178 | }, 179 | loadFileUrl(url, startup) { 180 | Module.keyboardListeningElement.focus(); 181 | Module.hasFocus = true; 182 | Module.lastSnapshot = ''; 183 | Module.lastFilename = url.substring(url.lastIndexOf('/')+1); 184 | 185 | var request; 186 | if (window.XMLHttpRequest) { 187 | request = new XMLHttpRequest(); 188 | } else { 189 | request = new ActiveXObject('Microsoft.XMLHTTP'); 190 | } 191 | // load 192 | request.open('GET', url, true); 193 | request.responseType = "arraybuffer"; 194 | request.onload = function (oEvent) { 195 | if (request.readyState === 4 && request.status === 200 ) { 196 | var arrayBuffer = request.response; 197 | if (arrayBuffer) { 198 | var byteArray = new Uint8Array(arrayBuffer); 199 | if(startup) Module.ccall('js_removeDevice', 'number', ['number'], 0); 200 | Module.ccall('js_LoadFile', 'number', ['string', 'array', 'number', 'number'], [Module.lastFilename, byteArray, byteArray.byteLength, startup]); 201 | //allowed: highScoreButton.disabled = true; 202 | highScoreTitle.innerHTML = ''; 203 | Module.updateDiskBox(); 204 | Module.updateLoadSnapshot(); 205 | } 206 | } 207 | }; 208 | request.send(); 209 | }, 210 | openHelp: function(lang) { 211 | if( Module.lastSnapshot == '' ) return false; 212 | var helprequest; 213 | if (window.XMLHttpRequest) { 214 | helprequest = new XMLHttpRequest(); 215 | } else { 216 | helprequest = new ActiveXObject('Microsoft.XMLHTTP'); 217 | } 218 | helpElement.style.display = 'none'; 219 | helprequest.open('GET', '/c64/pp_help.php?game='+Module.lastSnapshot+'&lang='+lang, true); 220 | helprequest.onload = function () 221 | { 222 | if (helprequest.readyState == 4 && helprequest.status == 200) 223 | { 224 | helpElement.innerHTML = helprequest.responseText; 225 | if( helprequest.responseText != '' ) { 226 | helpElement.style.display = 'inline-block'; 227 | window.clearTimeout(Module.helpTimer); 228 | Module.helpTimer = window.setTimeout(function(){document.getElementById('help_div').style.display='none';}, 20000); 229 | } 230 | } 231 | } 232 | helprequest.send(); 233 | return true; 234 | }, 235 | helpTimer: null, 236 | highscoreTimer: null, 237 | cancelHighscore: function() { 238 | Module.keyboardListeningElement.focus(); 239 | Module.hasFocus = true; 240 | Module.setPause(false); 241 | }, 242 | uploadHighscore: function(userName, scoreId, snapName) { 243 | Module.keyboardListeningElement.focus(); 244 | Module.hasFocus = true; 245 | Module.setPause(false); 246 | var lang = document.head.querySelector("[name=Content-Language]").content; 247 | if( snapName == 'self' ) snapName = Module.lastSnapshot; 248 | if( snapName == '' && scoreId != '' ) return false; 249 | 250 | var boundary = "---------------------------7da24f2e50046"; 251 | var hirequest; 252 | if (window.XMLHttpRequest) { 253 | hirequest = new XMLHttpRequest(); 254 | } else { 255 | hirequest = new ActiveXObject('Microsoft.XMLHTTP'); 256 | } 257 | hirequest.open('POST', '/c64/pp_highscore_upload.php', true); 258 | hirequest.setRequestHeader('Content-type', 'multipart/form-data; boundary='+boundary); 259 | hirequest.onreadystatechange = function () 260 | { 261 | if (hirequest.readyState == 4) 262 | { 263 | var defaultText = 'Highscore Upload'; 264 | if( hirequest.status == 200 && hirequest.responseText.substring(0, 5) == 'json:' ) { 265 | var obj = JSON.parse(hirequest.responseText.substring(5)); 266 | if (typeof obj !== 'undefined') 267 | { 268 | if (typeof obj.status !== 'undefined') { 269 | highScoreTitle.innerHTML = obj.status; 270 | } 271 | else highScoreTitle.innerHTML = ''; 272 | if (typeof obj.button !== 'undefined') { 273 | highScoreButton.innerHTML = obj.button; 274 | highScoreButton.className='button add bhighlight'; 275 | } 276 | if (typeof obj.help !== 'undefined') { 277 | helpElement.innerHTML = obj.help; 278 | if( obj.help != '' ) { 279 | helpElement.style.display = 'inline-block'; 280 | window.clearTimeout(Module.helpTimer); 281 | Module.helpTimer = window.setTimeout(function(){document.getElementById('help_div').style.display='none';}, 20000); 282 | } 283 | } 284 | } 285 | } 286 | else if( hirequest.status == 200 && hirequest.responseText.substring(0, 5) == 'form:' ) { 287 | highScoreStatus.innerHTML = hirequest.responseText.substring(5); 288 | document.getElementById('highSave').addEventListener("submit", function(e) { 289 | e.preventDefault(); 290 | handleHighscore(); 291 | }, false); 292 | document.getElementById('highCancel').addEventListener("click", function(e) { 293 | handleHighscoreCancel(); 294 | }, false); 295 | Module.hasFocus = false; 296 | Module.setPause(true); 297 | } 298 | else { 299 | highScoreButton.innerHTML = hirequest.responseText; 300 | highScoreButton.className='button add bhighlight'; 301 | } 302 | window.clearTimeout(Module.highscoreTimer); 303 | Module.highscoreTimer = window.setTimeout(function(){highScoreButton.innerHTML = defaultText; highScoreButton.className='button add';}, 3000); 304 | } 305 | } 306 | 307 | var body2 = '--' + boundary + '\r\n' 308 | + 'Content-Disposition: form-data; name="game"\r\n\r\n' 309 | + snapName + '\r\n' 310 | + '--' + boundary + '\r\n' 311 | + 'Content-Disposition: form-data; name="userName"\r\n\r\n' 312 | + userName + '\r\n' 313 | + '--' + boundary + '\r\n' 314 | + 'Content-Disposition: form-data; name="scoreid"\r\n\r\n' 315 | + scoreId + '\r\n' 316 | + '--' + boundary + '\r\n' 317 | + 'Content-Disposition: form-data; name="lang"\r\n\r\n' 318 | + lang + '\r\n' 319 | + '--' + boundary + '--' + '\r\n'; 320 | 321 | var blob; 322 | if( scoreId == '' && snapName != '' ) 323 | { 324 | var buffer = Module.ccall('js_SaveSnapshot', 'number', ['number'], [1]); 325 | var bsize = Module.ccall('js_SaveSnapshotSize', 'number', [], []); 326 | var imgData = new Uint8Array(Module.HEAPU8.buffer, buffer, bsize); 327 | 328 | var body = '--' + boundary + '\r\n' 329 | + 'Content-Disposition: form-data; name="highscore";' 330 | + 'filename="temp.s64"\r\n' 331 | + 'Content-type: application/octet-stream\r\n' 332 | + 'Content-length: ' + imgData.length + '\r\n\r\n'; 333 | 334 | blob = new Blob([body, imgData, '\r\n', body2]); 335 | } 336 | else 337 | { 338 | blob = new Blob([body2]); 339 | } 340 | 341 | var fileReader = new FileReader(); 342 | fileReader.onload = function() { 343 | hirequest.send(this.result); 344 | } 345 | fileReader.readAsArrayBuffer(blob); 346 | return true; 347 | }, 348 | getProperty: function(name) { 349 | return Module.ccall('js_getProperty', 'string', ['string'], [name]); 350 | }, 351 | setProperty: function(name, value) { 352 | return Module.ccall('js_setProperty', 'number', ['string', 'string'], [name, value]); 353 | }, 354 | setJoystick: function(key, down) { 355 | Module.ccall('js_setJoystick', 'number', ['number', 'number'], [key, down]); 356 | }, 357 | selectJoystick: function(player1, player2) { 358 | Module.ccall('js_selectJoystick', 'number', ['number', 'number'], [player1, player2]); 359 | }, 360 | setKey: function(key, down) { 361 | Module.ccall('js_setKey', 'number', ['number', 'number'], [key, down]); 362 | }, 363 | handleDiskBoxClick: function(event) { 364 | if(event && event.preventDefault) event.preventDefault(); 365 | Module.ccall('js_setDiskbox', 'number', ['number'], [document.getElementById("diskSelect").value]); 366 | document.getElementById("diskSelect").blur(); 367 | return false; 368 | }, 369 | getDiskBoxHtml: function() { 370 | return Module.ccall('js_getDiskboxHtml', 'string', ['number'], [1]); 371 | }, 372 | updateDiskBox: function() { 373 | var db = Module.getDiskBoxHtml(); 374 | if( db != '' ) { 375 | diskBoxCombo.innerHTML = db; 376 | document.getElementsByName('diskbox')[0].addEventListener('submit', Module.handleDiskBoxClick, false); 377 | document.getElementById('diskSelect').addEventListener('change', Module.handleDiskBoxClick, false); 378 | } 379 | else { 380 | diskBoxCombo.innerHTML = ''; 381 | } 382 | }, 383 | updateLoadSnapshot: function() { 384 | var ext = Module.lastFilename.split('.').pop().toUpperCase(); 385 | var isTape = (ext == 'TAP') || (Module.getProperty('tape_pos') != '-1'); 386 | document.getElementById('tape_keys').className = isTape ? 'show_tape' : 'hide_tape'; 387 | }, 388 | openAndUnlockAudioContext: function() { 389 | Module.SDL.openAudioContext(); 390 | var buffer = Module.SDL.audioContext.createBuffer(1, 1, 48000); 391 | var source = Module.SDL.audioContext.createBufferSource(); 392 | source.connect(Module.SDL.audioContext.destination); 393 | if (typeof source['start'] !== 'undefined') { 394 | source['start'](0); 395 | } else if (typeof source['noteOn'] !== 'undefined') { 396 | source['noteOn'](0); 397 | } 398 | }, 399 | muteState: 0, 400 | toggleMute: function() { 401 | Module.setMute(Module.muteState^1); 402 | return Module.muteState; 403 | }, 404 | setMute: function(mute) { 405 | if( !mute && Module.hasOwnProperty('SDL') && !Module.SDL.audioContext ) { 406 | Module.openAndUnlockAudioContext(); 407 | } 408 | Module.muteState = mute; 409 | Module.ccall('js_setMute', 'number', ['number'], [mute]); 410 | }, 411 | setPause: function(pause) { 412 | Module.ccall('js_setPause', 'number', ['number'], [pause]); 413 | }, 414 | setTape: function(cmd) { 415 | Module.ccall('js_setTape', 'number', ['number'], [cmd]); 416 | }, 417 | resetEmulation: function(type) { 418 | Module.ccall('js_reset', 'number', ['number'], [type]); 419 | Module.updateDiskBox(); 420 | Module.updateLoadSnapshot(); 421 | }, 422 | storageFileExists: function() { 423 | var storageSnapshot = Module.lastSnapshot; 424 | if( storageSnapshot == '' ) storageSnapshot = 'default.s64'; 425 | try { 426 | var fst = FS.stat('data/'+storageSnapshot); 427 | return fst!= null && fst.size; 428 | } 429 | catch(e) { 430 | } 431 | return false; 432 | }, 433 | hasSnapshot: function(storageSnapshot) { 434 | try { 435 | var fst = FS.stat('data/'+storageSnapshot); 436 | return storageSnapshot; 437 | } 438 | catch(e) { 439 | } 440 | return null; 441 | }, 442 | hasSavedGame: function() { 443 | return Module.hasSnapshot(progressSnapshot); 444 | }, 445 | loadHighScoreGame: function() { 446 | var snapshot = Module.hasSnapshot(highScoreSnapshot); 447 | if (!snapshot) { 448 | return; 449 | } 450 | Module.loadGame(snapshot); 451 | }, 452 | saveHighScoreGame: function(score) { 453 | var key = "gridrunner_hiscore"; 454 | var chs = parseInt(localStorage.getItem(key), 10); 455 | if (!chs) { 456 | chs = 0; 457 | } 458 | if (score <= chs) { 459 | return false; 460 | } 461 | Module.saveGame(highScoreSnapshot); 462 | localStorage.setItem(key, score); 463 | return true; 464 | }, 465 | maybeSaveGame: function(storageSnapshot) { 466 | var l = Module.saveSnapshotData()[1]; 467 | var s = new TextDecoder("latin1").decode(l); 468 | 469 | var score = /' ([0-9]{8})/.exec(s); 470 | console.log("Score", score); 471 | if (!score) { 472 | return; 473 | } 474 | score = score[1]; 475 | score = parseInt(score, 10); 476 | if (score == 0) { 477 | return; 478 | } 479 | Module.saveHighScoreGame(score); 480 | 481 | Module.saveGame(storageSnapshot); 482 | }, 483 | saveGame: function(storageSnapshot) { 484 | var res = Module.ccall('js_snapshotStorage', 'number', ['number', 'string'], [1, 'data/'+storageSnapshot]); 485 | if( res ) { 486 | FS.syncfs(function (err) { 487 | Module.updateLoadSnapshot(); 488 | }); 489 | } 490 | return res; 491 | }, 492 | loadGame: function(storageSnapshot) { 493 | var res = Module.ccall('js_snapshotStorage', 'number', ['number', 'string'], [0, 'data/'+storageSnapshot]); 494 | if( res ) { 495 | FS.syncfs(function (err) { 496 | Module.updateLoadSnapshot(); 497 | }); 498 | } 499 | return res; 500 | }, 501 | snapshotStorage: function(save) { 502 | var storageSnapshot = Module.lastSnapshot; 503 | if( storageSnapshot == '' ) storageSnapshot = 'default.s64'; 504 | var res = Module.ccall('js_snapshotStorage', 'number', ['number', 'string'], [save, 'data/'+storageSnapshot]); 505 | if( res && save ) { 506 | FS.syncfs(function (err) { 507 | Module.updateLoadSnapshot(); 508 | }); 509 | } 510 | else if( !save ) { 511 | if( res ) { 512 | highScoreButton.disabled = true; 513 | c64_checkfile_type( new File(["data"], storageSnapshot), true ); 514 | } 515 | Module.updateDiskBox(); 516 | Module.updateLoadSnapshot(); 517 | } 518 | return res; 519 | }, 520 | requestC64FullScreen: function() { 521 | Module.ccall('js_requestFullscreen', 'number', ['number', 'number'], [1, 0]); 522 | }, 523 | onFullScreenExit: function(softFullscreen) { 524 | if(softFullscreen) { 525 | Module.keyboardListeningElement.focus(); 526 | Module.hasFocus = true; 527 | } 528 | }, 529 | connection: null, 530 | isConnected: false, 531 | MODERATOR_SESSION_ID: "C64-8682F682-3230-4A4C-A1B6-FC49AD2DFE68", 532 | MODERATOR_SESSION: { 533 | audio: false, // by default, it is true 534 | video: false, // by default, it is true 535 | screen: false, 536 | 537 | data: true, 538 | 539 | oneway: false, 540 | broadcast: false 541 | }, 542 | MODERATOR_EXTRA: {}, 543 | enableNetwork: function(roomName) { 544 | networkStatusDiv.innerHTML = roomName!='' ? '

Connecting to Network...

' : '

Network Off

'; 545 | return Module.ccall('js_enableNetwork', 'number', ['string'], ['c64emulator-'+roomName]); 546 | }, 547 | connectionStatus: function(state) { 548 | var c_onopen = Module.cwrap('js_net_OnOpen', 'number', ['number']); 549 | this.isConnected = state; 550 | networkStatusDiv.innerHTML = state ? '

Network Connected

' : '

Network Disconnected

'; 551 | c_onopen(state); 552 | }, 553 | createConnection: function(roomName) { 554 | var c_onopen = Module.cwrap('js_net_OnOpen', 'number', ['number']); 555 | 556 | this.connection = new RTCMultiConnection( this.MODERATOR_SESSION_ID ); 557 | 558 | var SIGNALING_SERVER = 'https://webrtcweb.com:9559/'; 559 | this.connection.openSignalingChannel = function(config) { 560 | var channel = config.channel || Module.connection.channel || 'default-namespace'; 561 | var sender = Math.round(Math.random() * 9999999999) + 9999999999; 562 | io.connect(SIGNALING_SERVER).emit('new-channel', { 563 | channel: channel, 564 | sender: sender 565 | }); 566 | var socket = io.connect(SIGNALING_SERVER + channel); 567 | socket.channel = channel; 568 | socket.on('connect', function() { 569 | if (config.callback) config.callback(socket); 570 | }); 571 | socket.send = function(message) { 572 | socket.emit('message', { 573 | sender: sender, 574 | data: message 575 | }); 576 | }; 577 | socket.on('message', config.onmessage); 578 | }; 579 | 580 | this.connection.join(roomName); 581 | 582 | this.connection.onopen = function(event) { 583 | Module.connectionStatus(1); 584 | } 585 | this.connection.onerror = function(event) { 586 | Module.connectionStatus(0); 587 | } 588 | this.connection.onclose = function() { 589 | Module.connectionStatus(0); 590 | } 591 | 592 | this.connection.onmessage = function(event) { 593 | var c_onmessage = Module.cwrap('js_net_OnMessage', 'number', ['string', 'number']); 594 | c_onmessage(event.data, 0); 595 | } 596 | } 597 | }; 598 | 599 | 600 | -------------------------------------------------------------------------------- /docs/vic20/cpu/cpu6502.js: -------------------------------------------------------------------------------- 1 | function Cpu6502(memory, resetSource, irqSource, nmiSource) { 2 | var N = 1 << 7 3 | , V = 1 << 6 4 | , R = 1 << 5 5 | , B = 1 << 4 6 | , D = 1 << 3 7 | , I = 1 << 2 8 | , Z = 1 << 1 9 | , C = 1; 10 | var _N = ~N 11 | , _C = ~C 12 | , _I = ~I; 13 | var NZ = N | Z 14 | , NC = N | C 15 | , NV = N | V 16 | , NCZ = NZ | C 17 | , NCZV = NCZ | V 18 | , NVZ = N | V | Z; 19 | var _NZ = ~NZ 20 | , _NC = ~NC 21 | , _NV = ~NV 22 | , _NCZ = ~NCZ 23 | , _NCZV = ~NCZV 24 | , _NVZ = ~NVZ; 25 | var pc, sp, a, x, y, p, np; 26 | this.getRegisters = function() { 27 | return { 28 | "pc": pc, 29 | "sp": sp, 30 | "a": a, 31 | "x": x, 32 | "y": y, 33 | "p": p 34 | }; 35 | } 36 | this.setA = function(_a) { 37 | a = parseInt(_a, 16); 38 | } 39 | this.setX = function(_x) { 40 | x = parseInt(_x, 16); 41 | } 42 | this.setY = function(_y) { 43 | y = parseInt(_y, 16); 44 | } 45 | this.toggle = function(value) { 46 | p ^= value; 47 | debug(); 48 | } 49 | function genTogglePBit(bit) { 50 | return ""; 51 | } 52 | this.debugInformation = function() { 53 | var regs = vic20.cpu.getRegisters(); 54 | var html = "
"; 55 | html += "CPU Registers
"; 56 | html += ""; 57 | html += ""; 58 | html += ""; 59 | html += ""; 60 | html += ""; 61 | html += ""; 70 | html += "
PCAXYP
" + regs.pc.toString(16).toUpperCase() + ""; 62 | html += genTogglePBit(N) + ((regs.p & (1 << 7)) ? "N" : "n") + " "; 63 | html += genTogglePBit(V) + ((regs.p & (1 << 6)) ? "V" : "v") + " "; 64 | html += genTogglePBit(B) + ((regs.p & (1 << 4)) ? "B" : "b") + " "; 65 | html += genTogglePBit(D) + ((regs.p & (1 << 3)) ? "D" : "d") + " "; 66 | html += genTogglePBit(I) + ((regs.p & (1 << 2)) ? "I" : "i") + " "; 67 | html += genTogglePBit(Z) + ((regs.p & (1 << 1)) ? "Z" : "z") + " "; 68 | html += genTogglePBit(C) + ((regs.p & (1 << 0)) ? "C" : "c") + " "; 69 | html += "
"; 71 | html += "
"; 72 | return html; 73 | } 74 | var currentInst; 75 | var currentInstOffset; 76 | var instruction; 77 | var b, offset; 78 | var prevNmi = 0; 79 | var irqBits = 0; 80 | var nmiBits = 0; 81 | function push(value) { 82 | memory.write(0x100 + sp, value); 83 | sp = (sp - 1) & 0xFF; 84 | } 85 | function pop() { 86 | sp = (sp + 1) & 0xFF; 87 | return memory.read(0x100 + sp); 88 | } 89 | function nz(value) { 90 | p = value ? ((p & _NZ) | (value & N)) : ((p & _N) | Z); 91 | } 92 | function nzc(reg, value) { 93 | p &= ~(Z | C | N); 94 | reg -= value; 95 | p = ((reg == 0) ? ((p & ~N) | Z) : (p & ~(N | Z) | (reg & N))); 96 | if (reg >= 0) { 97 | p |= C; 98 | } 99 | } 100 | this.getPc = function() { 101 | return pc; 102 | } 103 | var colour = 1; 104 | this.incColour = function() { 105 | colour <<= 1; 106 | } 107 | function nextInst() { 108 | if ((nmiBits & 4) ^ prevNmi) { 109 | prevNmi = (nmiBits & 4); 110 | if (prevNmi) { 111 | currentInst = INST_NMI; 112 | currentInstOffset = 0; 113 | } 114 | } 115 | if (currentInstOffset != 0) { 116 | if (irqBits & 2) { 117 | currentInst = INST_IRQ; 118 | } else { 119 | instruction = memory.read(pc); 120 | currentInst = instfunc[instruction]; 121 | memExec[pc] = 2 | (p & I); 122 | pc = (pc + 1) & 0xFFFF; 123 | } 124 | currentInstOffset = 0; 125 | } 126 | if (np != null) { 127 | p = np; 128 | np = null; 129 | } 130 | } 131 | function badInst() { 132 | alert("Bad instruction pc=$" + pc.toString(16) + " instruction = $" + instruction.toString(16)); 133 | resetSource.reset(); 134 | } 135 | function jamInst() { 136 | alert("Jam instruction pc=$" + pc.toString(16) + " instruction = $" + instruction.toString(16)); 137 | resetSource.reset(); 138 | } 139 | function halt() { 140 | exit(); 141 | } 142 | function nullOp() {} 143 | ;function pcNull() { 144 | pc = (pc + 1) & 0xFFFF; 145 | } 146 | ;function pushA() { 147 | push(a); 148 | } 149 | ;function pushP() { 150 | push(p); 151 | } 152 | ;function pushPcH() { 153 | push(pc >> 8); 154 | } 155 | ;function pushPcL() { 156 | push(pc & 0xFF); 157 | } 158 | ;function pushPorBsetI() { 159 | push(p | B); 160 | p |= I; 161 | } 162 | ;function pushPsetI() { 163 | push(p); 164 | p |= I; 165 | } 166 | ;function plp() { 167 | p = (pop() & ~B) | R; 168 | } 169 | ;function pla() { 170 | nz(a = pop()); 171 | } 172 | ;function popPcL() { 173 | pc = pop(); 174 | } 175 | ;function popPcH() { 176 | pc |= (pop() << 8); 177 | } 178 | ;function popPcHaddOne() { 179 | pc = ((pop() << 8) | pc) + 1; 180 | pc &= 0xFFFF; 181 | } 182 | ;function loadPCResetL() { 183 | pc = memory.read(0xFFFC); 184 | } 185 | ;function loadPCResetH() { 186 | pc |= memory.read(0xFFFD) << 8; 187 | } 188 | ;function loadPCInterruptL() { 189 | pc = memory.read(0xFFFE); 190 | } 191 | ;function loadPCInterruptH() { 192 | pc |= memory.read(0xFFFF) << 8; 193 | } 194 | ;function loadPCNmiL() { 195 | pc = memory.read(0xFFFA); 196 | } 197 | ;function loadPCNmiH() { 198 | pc |= memory.read(0xFFFB) << 8; 199 | } 200 | ;function zp_x_from_pc() { 201 | offset = (memory.read(pc) + x) & 0xFF; 202 | pc = (pc + 1) & 0xFFFF; 203 | } 204 | ;function load_offset_zp_l() { 205 | b = memory.read(offset++); 206 | } 207 | ;function load_offset_zp_h() { 208 | offset = b | (memory.read(offset & 0xFF) << 8); 209 | } 210 | ;function load_offset_zp_h_plus_y() { 211 | offset = ((b | (memory.read(offset & 0xFF) << 8)) + y) & 0xFFFF; 212 | if ((offset & 0xFF) >= y) { 213 | currentInstOffset++; 214 | } 215 | } 216 | ;function op_offset() { 217 | b = memory.read(offset); 218 | op[instruction](); 219 | } 220 | ;function op_offset_rmw() { 221 | b = memory.read(offset); 222 | } 223 | ;function op_offset_w() { 224 | op[instruction](); 225 | memory.write(offset, b); 226 | } 227 | ;function zp_from_pc() { 228 | offset = memory.read(pc); 229 | pc = (pc + 1) & 0xFFFF; 230 | } 231 | ;function pc_from_zp_and_pc() { 232 | pc = (memory.read(pc) << 8) | offset; 233 | } 234 | ;function offset_plus_y() { 235 | offset = (offset + y) & 0xFFFF; 236 | if ((offset & 0xFF) >= y) { 237 | currentInst[currentInstOffset++](); 238 | } 239 | } 240 | ;function offset_plus_x() { 241 | offset = (offset + x) & 0xFFFF; 242 | if ((offset & 0xFF) >= x) { 243 | currentInst[currentInstOffset++](); 244 | } 245 | } 246 | ;function offset_plus_x_w() { 247 | offset = (offset + x) & 0xFFFF; 248 | } 249 | ;function offset_plus_y_w() { 250 | offset = (offset + y) & 0xFFFF; 251 | } 252 | function b_offset_cond_from_pc() { 253 | var diff = memory.read(pc); 254 | if (diff > 127) 255 | diff = diff - 256; 256 | var pp = ((instruction & 0x20) != 0) ? (p ^ 0xFF) : p; 257 | var mask = ((Z << 24) | (C << 16) | (V << 8) | N) >>> ((instruction >> 6) << 3); 258 | if ((pp & mask) != 0) { 259 | pc++; 260 | irqBits = 0; 261 | currentInstOffset += 2; 262 | } else { 263 | offset = (pc + (diff) + 1); 264 | } 265 | } 266 | function bra_diff_page() { 267 | if (((offset ^ pc) >> 8) == 0) { 268 | currentInstOffset++; 269 | } 270 | pc = offset; 271 | } 272 | ;function logic_imm() { 273 | b = memory.read(pc); 274 | pc = (pc + 1) & 0xFFFF; 275 | op[instruction](); 276 | } 277 | ;function logic_imm_sbx() { 278 | x = (x & a) - memory.read(pc); 279 | if (x < 0) 280 | p &= ~C; 281 | else 282 | p |= C; 283 | nz(x &= 0xFF); 284 | pc = (pc + 1) & 0xFFFF; 285 | } 286 | ;function logic_zp() { 287 | b = memory.read(offset); 288 | op[instruction](); 289 | } 290 | ;function nullread_offset() { 291 | memory.read(offset); 292 | } 293 | ;function logic_zp_w() { 294 | op[instruction](); 295 | memory.write(offset, b); 296 | } 297 | ;function zp_y_from_pc() { 298 | offset = (memory.read(pc) + y) & 0xFF; 299 | pc = (pc + 1) & 0xFFFF; 300 | } 301 | ;function temp_to_zp() { 302 | memory.write(offset, b); 303 | } 304 | ;function clc() { 305 | p &= _C; 306 | } 307 | ;function sec() { 308 | p |= C; 309 | } 310 | ;function cli() { 311 | np = p & _I; 312 | } 313 | ;function sei() { 314 | np = p | I; 315 | } 316 | ;function clv() { 317 | p &= ~V; 318 | } 319 | ;function cld() { 320 | p &= ~D; 321 | } 322 | ;function sed() { 323 | p |= D; 324 | } 325 | ;function dey() { 326 | nz(y = (y - 1) & 0xFF); 327 | } 328 | ;function dex() { 329 | nz(x = (x - 1) & 0xFF); 330 | } 331 | ;function iny() { 332 | nz(y = (y + 1) & 0xFF); 333 | } 334 | ;function inx() { 335 | nz(x = (x + 1) & 0xFF); 336 | } 337 | ;function tya() { 338 | nz(a = y); 339 | } 340 | ;function tay() { 341 | nz(y = a); 342 | } 343 | ;function txa() { 344 | nz(a = x); 345 | } 346 | ;function tax() { 347 | nz(x = a); 348 | } 349 | ;function txs() { 350 | sp = x; 351 | } 352 | ;function tsx() { 353 | nz(x = sp); 354 | } 355 | ;function load_offset_abs_l() { 356 | offset = memory.read(pc); 357 | pc = (pc + 1) & 0xFFFF; 358 | } 359 | ;function load_offset_abs_h() { 360 | offset |= (memory.read(pc) << 8); 361 | pc = (pc + 1) & 0xFFFF; 362 | } 363 | ;function op_offset_plus_x() { 364 | offset = (offset + x) & 0xFFFF; 365 | if ((offset & 0xFF) >= x) { 366 | b = memory.read(offset); 367 | op[instruction](); 368 | currentInstOffset++; 369 | } 370 | } 371 | function op_offset_plus_y() { 372 | offset = (offset + y) & 0xFFFF; 373 | if ((offset & 0xFF) >= y) { 374 | b = memory.read(offset); 375 | op[instruction](); 376 | currentInstOffset++; 377 | } 378 | } 379 | function load_offset_abs_h_plus_y() { 380 | offset = (offset | (memory.read(pc) << 8)) + y; 381 | pc = (pc + 1) & 0xFFFF; 382 | if ((offset & 0xFF) >= y) { 383 | currentInstOffset++; 384 | } 385 | } 386 | function opa() { 387 | b = a; 388 | op[instruction](); 389 | a = b; 390 | } 391 | ;function load_pc_abs_l() { 392 | b = memory.read(pc); 393 | pc = (pc + 1) & 0xFFFF; 394 | } 395 | ;function load_pc_abs_h() { 396 | pc = (memory.read(pc) << 8) | b; 397 | } 398 | ;function load_pc_offset_l() { 399 | pc = memory.read(offset++); 400 | if ((offset & 0xFF) == 0) { 401 | offset -= 0x100; 402 | } 403 | offset &= 0xFFFF; 404 | } 405 | ;function load_pc_offset_h() { 406 | pc |= (memory.read(offset++) << 8); 407 | } 408 | function temp_to_offset() { 409 | memory.write(offset, b); 410 | } 411 | function nullProc() {} 412 | function ora() { 413 | nz(a |= b); 414 | } 415 | function sre() { 416 | if (b & C) { 417 | p |= C; 418 | } else { 419 | p &= ~C; 420 | } 421 | b >>= 1; 422 | nz(a ^= b); 423 | } 424 | function rra() { 425 | ror(); 426 | adc(); 427 | } 428 | function rla() { 429 | var oldCarry = p & C; 430 | p &= ~C; 431 | p |= (b >> 7); 432 | b = (b << 1) | oldCarry; 433 | b &= 0xFF; 434 | nz(a &= b); 435 | } 436 | function slo() { 437 | if ((b & N) != 0) { 438 | p |= C; 439 | } else { 440 | p &= ~C; 441 | } 442 | b = (b << 1) & 0xFF; 443 | nz(a |= b); 444 | } 445 | function and() { 446 | nz(a &= b); 447 | } 448 | function eor() { 449 | nz(a ^= b); 450 | } 451 | function adc() { 452 | if ((p & D) != 0) { 453 | var sum = (a & 0xF) + (b & 0xF) + (p & C); 454 | p &= ~(N | Z | C | V); 455 | if (sum > 0x09) { 456 | sum += 0x06; 457 | } 458 | if (sum > 0x1F) { 459 | sum -= 0x10; 460 | } 461 | sum += (a & 0xF0) + (b & 0xF0); 462 | p |= (sum & N); 463 | p |= ((~(a ^ b) & (a ^ sum)) & N) >> 1; 464 | a = (sum & 0xFF); 465 | if (a == 0) { 466 | p |= Z; 467 | } 468 | if (sum >= 0xA0) { 469 | a += 0x60; 470 | a &= 0xFF; 471 | p |= C; 472 | } 473 | } else { 474 | var sum2 = a + b + (p & C); 475 | p &= ~(N | Z | C | V); 476 | p |= ((~(a ^ b) & (a ^ sum2)) & N) >> 1; 477 | p |= ((sum2 >> 8) & C); 478 | a = (sum2 & 0xFF); 479 | if (a == 0) { 480 | p |= Z; 481 | } 482 | p |= (a & N); 483 | } 484 | } 485 | function sbc() { 486 | if ((p & D) != 0) { 487 | var sumadd = 0 488 | , sum = (a & 0xF) - (b & 0xF) - (~p & C); 489 | p &= ~(N | Z | C | V); 490 | if (sum < -10) { 491 | sumadd = 10; 492 | } else if (sum < 0) { 493 | sumadd = -6; 494 | } 495 | sum += (a & 0xF0) - (b & 0xF0); 496 | if ((sum & 0xFF) == 0) { 497 | p |= Z; 498 | } 499 | p |= (sum & N); 500 | p |= (((a ^ b) & (a ^ sum)) & N) >> 1; 501 | sum += sumadd; 502 | if (sum < 0) { 503 | sum += 0xA0; 504 | } else { 505 | if (sum >= 0xA0) { 506 | sum -= 0x60; 507 | } 508 | p |= C; 509 | } 510 | a = (sum & 0xFF); 511 | } else { 512 | var sum2 = a - b - (~p & C); 513 | p &= ~(N | Z | C | V); 514 | p |= (((a ^ b) & (a ^ sum2)) & N) >> 1; 515 | a = (sum2 & 0xFF); 516 | p |= ((~sum2 >> 8) & C); 517 | p |= (a & N); 518 | if (a == 0) { 519 | p |= Z; 520 | } 521 | } 522 | } 523 | function cmp() { 524 | nzc(a, b); 525 | } 526 | function cpx() { 527 | nzc(x, b); 528 | } 529 | ;function cpy() { 530 | nzc(y, b); 531 | } 532 | function bit() { 533 | p &= _NVZ; 534 | if ((a & b) == 0) 535 | p |= Z; 536 | p |= (b & NV); 537 | } 538 | function lda() { 539 | nz(a = b); 540 | } 541 | function ldx() { 542 | nz(x = b); 543 | } 544 | function ldy() { 545 | nz(y = b); 546 | } 547 | function sta() { 548 | b = a; 549 | } 550 | function sax() { 551 | b = a & x; 552 | } 553 | function stx() { 554 | b = x; 555 | } 556 | function sty() { 557 | b = y; 558 | } 559 | function asl() { 560 | p &= ~C; 561 | p |= (b >> 7); 562 | nz(b = (b << 1) & 0xFF); 563 | } 564 | function rol() { 565 | var oldCarry = p & C; 566 | p &= ~C; 567 | p |= (b >> 7); 568 | nz(b = ((b << 1) | oldCarry) & 0xFF); 569 | } 570 | function lsr() { 571 | p &= ~(C | NZ); 572 | p |= (b & C); 573 | if ((b >>= 1) == 0) { 574 | p |= Z; 575 | } 576 | } 577 | function ror() { 578 | var newN = (p & C) << 7; 579 | p &= ~(C | NZ); 580 | p |= (b & C) | newN; 581 | b = ((b >> 1) | newN); 582 | if (b == 0) { 583 | p |= Z; 584 | } 585 | } 586 | function anc() { 587 | nz(a &= b); 588 | p &= ~C; 589 | p |= (a >> 7); 590 | } 591 | function ane() { 592 | a = (a | 0xEE) & x; 593 | nz(a &= b); 594 | } 595 | function lxa() { 596 | a = x = ((a | 0xEE) & b); 597 | nz(a); 598 | } 599 | function arr() { 600 | and(); 601 | ror(); 602 | p &= ~(C | V); 603 | p |= (b >> 5) & C; 604 | p |= ((b << 2) | (b << 1)) & V; 605 | } 606 | function asr() { 607 | and(); 608 | lsr(); 609 | } 610 | function dec() { 611 | nz(b = (b - 1) & 0xFF); 612 | } 613 | function inc() { 614 | nz(b = (b + 1) & 0xFF); 615 | } 616 | function dcp() { 617 | dec(); 618 | cmp(); 619 | } 620 | function isb() { 621 | inc(); 622 | sbc(); 623 | } 624 | function lae() { 625 | a &= b; 626 | nz(x = a); 627 | sp = x; 628 | } 629 | function lax() { 630 | a = b; 631 | nz(x = a); 632 | } 633 | var INST_RESET = [nullOp, nullOp, loadPCResetL, loadPCResetH, nextInst]; 634 | var INST_IRQ = [nullOp, nullOp, pushPcH, pushPcL, pushPsetI, loadPCInterruptL, loadPCInterruptH, nextInst]; 635 | var INST_NMI = [nullOp, nullOp, pushPcH, pushPcL, pushPsetI, loadPCNmiL, loadPCNmiH, nextInst]; 636 | var INST_BAD = [badInst, halt]; 637 | var INST_JAM = [jamInst, halt]; 638 | var BRK = [pcNull, pushPcH, pushPcL, pushPorBsetI, loadPCInterruptL, loadPCInterruptH, nextInst]; 639 | var INST_X_IND = [zp_x_from_pc, nullOp, load_offset_zp_l, load_offset_zp_h, op_offset, nextInst]; 640 | var INST_X_IND_RMW = [zp_x_from_pc, nullOp, load_offset_zp_l, load_offset_zp_h, op_offset_rmw, nullOp, op_offset_w, nextInst]; 641 | var INST_X_IND_W = [zp_x_from_pc, nullOp, load_offset_zp_l, load_offset_zp_h, op_offset_w, nextInst]; 642 | var INST_X_IND_I = [zp_x_from_pc, nullOp, load_offset_zp_l, load_offset_zp_h, op_offset, nullOp, op_offset_w, nextInst]; 643 | var INST_Y_IND_I = [zp_y_from_pc, nullOp, load_offset_zp_l, load_offset_zp_h, op_offset, nullOp, op_offset_w, nextInst]; 644 | var INST_IND_Y = [zp_from_pc, load_offset_zp_l, load_offset_zp_h_plus_y, nullOp, op_offset, nextInst]; 645 | var INST_IND_Y_RMW = [zp_from_pc, load_offset_zp_l, load_offset_zp_h_plus_y, nullOp, op_offset_rmw, nullOp, op_offset_w, nextInst]; 646 | var INST_IND_Y_W = [zp_from_pc, load_offset_zp_l, load_offset_zp_h, offset_plus_y_w, op_offset_w, nextInst]; 647 | var INST_BCOND = [b_offset_cond_from_pc, bra_diff_page, nullOp, nextInst]; 648 | var INST_JSR = [zp_from_pc, nullOp, pushPcH, pushPcL, pc_from_zp_and_pc, nextInst]; 649 | var INST_RTI = [nullOp, nullOp, plp, popPcL, popPcH, nextInst]; 650 | var INST_RTS = [nullOp, nullOp, popPcL, popPcHaddOne, nullOp, nextInst]; 651 | var INST_LOGIC_IMM = [logic_imm, nextInst]; 652 | var INST_LOGIC_IMM_SBX = [logic_imm_sbx, nextInst]; 653 | var INST_ZPG = [zp_from_pc, logic_zp, nextInst]; 654 | var INST_ZPG_W = [zp_from_pc, logic_zp_w, nextInst]; 655 | var INST_ZPG_X = [zp_x_from_pc, logic_zp, nullOp, nextInst]; 656 | var INST_ZPG_Y = [zp_y_from_pc, logic_zp, nextInst]; 657 | var INST_ZPG_X_W = [zp_x_from_pc, nullOp, logic_zp_w, nextInst]; 658 | var INST_ZPG_Y_W = [zp_y_from_pc, nullOp, logic_zp_w, nextInst]; 659 | var INST_ZPGs = [zp_from_pc, logic_zp, nullOp, temp_to_zp, nextInst]; 660 | var INST_ZPGs_X = [zp_x_from_pc, nullOp, logic_zp, nullOp, temp_to_zp, nextInst]; 661 | var INST_PUSH_P = [nullOp, pushP, nextInst]; 662 | var INST_PHA = [nullOp, pushA, nextInst]; 663 | var INST_PLP = [nullOp, nullOp, plp, nextInst]; 664 | var INST_PLA = [nullOp, nullOp, pla, nextInst]; 665 | var INST_FLAG_CLC = [clc, nextInst]; 666 | var INST_FLAG_SEC = [sec, nextInst]; 667 | var INST_FLAG_CLI = [cli, nextInst]; 668 | var INST_FLAG_SEI = [sei, nextInst]; 669 | var INST_FLAG_CLV = [clv, nextInst]; 670 | var INST_FLAG_CLD = [cld, nextInst]; 671 | var INST_FLAG_SED = [sed, nextInst]; 672 | var INST_DEY = [dey, nextInst]; 673 | var INST_DEX = [dex, nextInst]; 674 | var INST_INY = [iny, nextInst]; 675 | var INST_INX = [inx, nextInst]; 676 | var INST_TYA = [tya, nextInst]; 677 | var INST_TAY = [tay, nextInst]; 678 | var INST_TXA = [txa, nextInst]; 679 | var INST_TAX = [tax, nextInst]; 680 | var INST_TXS = [txs, nextInst]; 681 | var INST_TSX = [tsx, nextInst]; 682 | var INST_LOGIC_ABS_Y = [load_offset_abs_l, load_offset_abs_h_plus_y, nullOp, op_offset, nextInst]; 683 | var INST_LOGIC_ABS_Y_RMW = [load_offset_abs_l, load_offset_abs_h, offset_plus_y, op_offset_rmw, nullOp, op_offset_w, nextInst]; 684 | var INST_LOGIC_ABS_Y_W = [load_offset_abs_l, load_offset_abs_h, offset_plus_y_w, op_offset_w, nextInst]; 685 | var INST_OP_A = [opa, nextInst]; 686 | var INST_NOP = [nullOp, nextInst]; 687 | var INST_ABS = [load_offset_abs_l, load_offset_abs_h, op_offset, nextInst]; 688 | var INST_ABS_W = [load_offset_abs_l, load_offset_abs_h, op_offset_w, nextInst]; 689 | var INST_JMP = [load_pc_abs_l, load_pc_abs_h, nextInst]; 690 | var INST_JMP_IND = [load_offset_abs_l, load_offset_abs_h, load_pc_offset_l, load_pc_offset_h, nextInst]; 691 | var INST_ABS_X = [load_offset_abs_l, load_offset_abs_h, op_offset_plus_x, op_offset, nextInst]; 692 | var INST_ABS_Y = [load_offset_abs_l, load_offset_abs_h, op_offset_plus_y, op_offset, nextInst]; 693 | var INST_ABS_X_W = [load_offset_abs_l, load_offset_abs_h, offset_plus_x_w, op_offset_w, nextInst]; 694 | var INST_ABSs = [load_offset_abs_l, load_offset_abs_h, op_offset, nullOp, temp_to_offset, nextInst]; 695 | var INST_ABS_Xs = [load_offset_abs_l, load_offset_abs_h, offset_plus_x_w, op_offset, nullOp, temp_to_offset, nextInst]; 696 | var INST_ABS_Ys = [load_offset_abs_l, load_offset_abs_h, offset_plus_y_w, op_offset, nullOp, temp_to_offset, nextInst]; 697 | var instfunc = [BRK, INST_X_IND, INST_JAM, INST_X_IND_RMW, INST_ZPG, INST_ZPG, INST_ZPGs, INST_ZPGs, INST_PUSH_P, INST_LOGIC_IMM, INST_OP_A, INST_LOGIC_IMM, INST_ABS, INST_ABS, INST_ABSs, INST_ABSs, INST_BCOND, INST_IND_Y, INST_JAM, INST_IND_Y_RMW, INST_ZPG_X, INST_ZPG_X, INST_ZPGs_X, INST_ZPGs_X, INST_FLAG_CLC, INST_LOGIC_ABS_Y, INST_NOP, INST_LOGIC_ABS_Y_RMW, INST_ABS_X, INST_ABS_X, INST_ABS_Xs, INST_ABS_Xs, INST_JSR, INST_X_IND, INST_JAM, INST_X_IND_RMW, INST_ZPG, INST_ZPG, INST_ZPGs, INST_ZPGs, INST_PLP, INST_LOGIC_IMM, INST_OP_A, INST_LOGIC_IMM, INST_ABS, INST_ABS, INST_ABSs, INST_ABSs, INST_BCOND, INST_IND_Y, INST_JAM, INST_IND_Y_RMW, INST_ZPG_X, INST_ZPG_X, INST_ZPGs_X, INST_ZPGs_X, INST_FLAG_SEC, INST_LOGIC_ABS_Y, INST_NOP, INST_LOGIC_ABS_Y_RMW, INST_ABS_X, INST_ABS_X, INST_ABS_Xs, INST_ABS_Xs, INST_RTI, INST_X_IND, INST_JAM, INST_X_IND_RMW, INST_ZPG, INST_ZPG, INST_ZPGs, INST_ZPGs, INST_PHA, INST_LOGIC_IMM, INST_OP_A, INST_LOGIC_IMM, INST_JMP, INST_ABS, INST_ABSs, INST_ABSs, INST_BCOND, INST_IND_Y, INST_JAM, INST_IND_Y_RMW, INST_ZPG_X, INST_ZPG_X, INST_ZPGs_X, INST_ZPGs_X, INST_FLAG_CLI, INST_LOGIC_ABS_Y, INST_NOP, INST_LOGIC_ABS_Y_RMW, INST_ABS_X, INST_ABS_X, INST_ABS_Xs, INST_ABS_Xs, INST_RTS, INST_X_IND, INST_JAM, INST_X_IND_RMW, INST_ZPG, INST_ZPG, INST_ZPGs, INST_ZPGs, INST_PLA, INST_LOGIC_IMM, INST_OP_A, INST_LOGIC_IMM, INST_JMP_IND, INST_ABS, INST_ABSs, INST_ABSs, INST_BCOND, INST_IND_Y, INST_JAM, INST_IND_Y_RMW, INST_ZPG_X, INST_ZPG_X, INST_ZPGs_X, INST_ZPGs_X, INST_FLAG_SEI, INST_LOGIC_ABS_Y, INST_NOP, INST_LOGIC_ABS_Y_RMW, INST_ABS_X, INST_ABS_X, INST_ABS_Xs, INST_ABS_Xs, INST_LOGIC_IMM, INST_X_IND_W, INST_LOGIC_IMM, INST_X_IND_W, INST_ZPG_W, INST_ZPG_W, INST_ZPG_W, INST_ZPG_W, INST_DEY, INST_LOGIC_IMM, INST_TXA, INST_LOGIC_IMM, INST_ABS_W, INST_ABS_W, INST_ABS_W, INST_ABS_W, INST_BCOND, INST_IND_Y_W, INST_JAM, INST_BAD, INST_ZPG_X_W, INST_ZPG_X_W, INST_ZPG_Y_W, INST_ZPG_Y_W, INST_TYA, INST_LOGIC_ABS_Y_W, INST_TXS, INST_BAD, INST_BAD, INST_ABS_X_W, INST_BAD, INST_BAD, INST_LOGIC_IMM, INST_X_IND, INST_LOGIC_IMM, INST_X_IND, INST_ZPG, INST_ZPG, INST_ZPG, INST_ZPG, INST_TAY, INST_LOGIC_IMM, INST_TAX, INST_LOGIC_IMM, INST_ABS, INST_ABS, INST_ABS, INST_ABS, INST_BCOND, INST_IND_Y, INST_JAM, INST_IND_Y, INST_ZPG_X, INST_ZPG_X, INST_ZPG_Y, INST_ZPG_Y, INST_FLAG_CLV, INST_LOGIC_ABS_Y, INST_TSX, INST_LOGIC_ABS_Y, INST_ABS_X, INST_ABS_X, INST_ABS_Y, INST_ABS_Y, INST_LOGIC_IMM, INST_X_IND, INST_LOGIC_IMM, INST_X_IND_I, INST_ZPG, INST_ZPG, INST_ZPGs, INST_ZPGs, INST_INY, INST_LOGIC_IMM, INST_DEX, INST_LOGIC_IMM_SBX, INST_ABS, INST_ABS, INST_ABSs, INST_ABSs, INST_BCOND, INST_IND_Y, INST_JAM, INST_Y_IND_I, INST_ZPG_X, INST_ZPG_X, INST_ZPGs_X, INST_ZPGs_X, INST_FLAG_CLD, INST_LOGIC_ABS_Y, INST_NOP, INST_ABS_Ys, INST_ABS_X, INST_ABS_X, INST_ABS_Xs, INST_ABS_Xs, INST_LOGIC_IMM, INST_X_IND, INST_LOGIC_IMM, INST_X_IND_I, INST_ZPG, INST_ZPG, INST_ZPGs, INST_ZPGs, INST_INX, INST_LOGIC_IMM, INST_NOP, INST_LOGIC_IMM, INST_ABS, INST_ABS, INST_ABSs, INST_ABSs, INST_BCOND, INST_IND_Y, INST_JAM, INST_Y_IND_I, INST_ZPG_X, INST_ZPG_X, INST_ZPGs_X, INST_ZPGs_X, INST_FLAG_SED, INST_LOGIC_ABS_Y, INST_NOP, INST_ABS_Ys, INST_ABS_X, INST_ABS_X, INST_ABS_Xs, INST_ABS_Xs]; 698 | var op = [null, ora, null, slo, nullProc, ora, asl, slo, null, ora, asl, anc, nullProc, ora, asl, slo, null, ora, null, slo, nullProc, ora, asl, slo, null, ora, null, slo, nullProc, ora, asl, slo, null, and, null, rla, bit, and, rol, rla, null, and, rol, anc, bit, and, rol, rla, null, and, null, rla, nullProc, and, rol, rla, null, and, null, rla, nullProc, and, rol, rla, null, eor, null, sre, nullProc, eor, lsr, sre, null, eor, lsr, asr, null, eor, lsr, sre, null, eor, null, sre, nullProc, eor, lsr, sre, null, eor, null, sre, nullProc, eor, lsr, sre, null, adc, null, rra, nullProc, adc, ror, rra, null, adc, ror, arr, null, adc, ror, rra, null, adc, null, rra, nullProc, adc, ror, rra, null, adc, null, rra, nullProc, adc, ror, rra, nullProc, sta, nullProc, sax, sty, sta, stx, sax, null, nullProc, null, ane, sty, sta, stx, sax, null, sta, null, null, sty, sta, stx, sax, null, sta, null, null, null, sta, null, null, ldy, lda, ldx, lax, ldy, lda, ldx, lax, null, lda, null, lxa, ldy, lda, ldx, lax, null, lda, null, lax, ldy, lda, ldx, lax, null, lda, null, lae, ldy, lda, ldx, lax, cpy, cmp, nullProc, dcp, cpy, cmp, dec, dcp, null, cmp, null, null, cpy, cmp, dec, dcp, null, cmp, null, dcp, nullProc, cmp, dec, dcp, null, cmp, null, dcp, nullProc, cmp, dec, dcp, cpx, sbc, nullProc, isb, cpx, sbc, inc, isb, null, sbc, null, sbc, cpx, sbc, inc, isb, null, sbc, null, isb, nullProc, sbc, inc, isb, null, sbc, null, isb, nullProc, sbc, inc, isb]; 699 | this.isNextInst = function() { 700 | return currentInst[currentInstOffset] == nextInst; 701 | } 702 | this.cycle = function() { 703 | currentInst[currentInstOffset++](); 704 | nmiBits = (nmiBits << 1) | (nmiSource.test() ? 1 : 0); 705 | irqBits = (irqBits << 1) | (((p & I) || !irqSource.test()) ? 0 : 1); 706 | } 707 | this.reset = function() { 708 | currentInst = INST_RESET; 709 | currentInstOffset = 0; 710 | prevNmi = 0; 711 | irqBits = 0; 712 | nmiBits = 0; 713 | a = x = y = sp = pc = b = 0; 714 | np = null; 715 | p = R; 716 | } 717 | this.reset(); 718 | } 719 | --------------------------------------------------------------------------------