diff --git a/com.wudsn.ide.asm.compilers.test/.classpath b/com.wudsn.ide.asm.compilers.test/.classpath new file mode 100644 index 00000000..8a8f1668 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/com.wudsn.ide.asm.compilers.test/.project b/com.wudsn.ide.asm.compilers.test/.project new file mode 100644 index 00000000..0ef5742c --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/.project @@ -0,0 +1,28 @@ + + + com.wudsn.ide.asm.compilers.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/com.wudsn.ide.asm.compilers.test/.settings/org.eclipse.jdt.core.prefs b/com.wudsn.ide.asm.compilers.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..f287d53c --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/com.wudsn.ide.asm.compilers.test/META-INF/MANIFEST.MF b/com.wudsn.ide.asm.compilers.test/META-INF/MANIFEST.MF new file mode 100644 index 00000000..680be15e --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: WUDSN IDE Assembler Test Compilers Plug-in +Bundle-SymbolicName: com.wudsn.ide.asm.compilers.test;singleton:=true +Bundle-Version: 1.7.0.qualifier +Bundle-Vendor: Peter Dell +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-Localization: plugin +Require-Bundle: com.wudsn.ide.asm +Bundle-ActivationPolicy: lazy diff --git a/com.wudsn.ide.asm.compilers.test/bin/.gitignore b/com.wudsn.ide.asm.compilers.test/bin/.gitignore new file mode 100644 index 00000000..43e58b99 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/bin/.gitignore @@ -0,0 +1 @@ +/com diff --git a/com.wudsn.ide.asm.compilers.test/build.properties b/com.wudsn.ide.asm.compilers.test/build.properties new file mode 100644 index 00000000..74239926 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/build.properties @@ -0,0 +1,16 @@ +source.. = src/ +output.. = bin/ +bin.includes = .,\ + plugin.xml,\ + icons/,\ + META-INF/,\ + plugin.properties,\ + src/,\ + build.properties,\ + plugin_de_DE.properties,\ + .classpath,\ + .project +jars.compile.order = . +src.includes = bin/,\ + icons/ + diff --git a/com.wudsn.ide.asm.compilers.test/icons/editor-test-16x16.gif b/com.wudsn.ide.asm.compilers.test/icons/editor-test-16x16.gif new file mode 100644 index 00000000..751df8b8 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/editor-test-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_acorn.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_acorn.gif new file mode 100644 index 00000000..2eec3b57 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_acorn.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_alambik.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_alambik.gif new file mode 100644 index 00000000..12a44a06 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_alambik.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga.gif new file mode 100644 index 00000000..36791b85 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga_aga.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga_aga.gif new file mode 100644 index 00000000..3b17c6fe Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga_aga.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga_ppc.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga_ppc.gif new file mode 100644 index 00000000..9af7ceea Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_amiga_ppc.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_amplus.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_amplus.gif new file mode 100644 index 00000000..754e255f Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_amplus.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_apple2.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_apple2.gif new file mode 100644 index 00000000..9755078c Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_apple2.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_falcon.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_falcon.gif new file mode 100644 index 00000000..9dd7a559 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_falcon.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_st.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_st.gif new file mode 100644 index 00000000..2ed2102e Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_st.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_ste.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_ste.gif new file mode 100644 index 00000000..1637f160 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_ste.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_vcs.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_vcs.gif new file mode 100644 index 00000000..ab28795f Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_vcs.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_xl_xe.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_xl_xe.gif new file mode 100644 index 00000000..c93689bf Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_atari_xl_xe.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_bbcmicro.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_bbcmicro.gif new file mode 100644 index 00000000..05cb0459 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_bbcmicro.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_beos.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_beos.gif new file mode 100644 index 00000000..12ef66b7 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_beos.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_bk.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_bk.gif new file mode 100644 index 00000000..7876d9d5 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_bk.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_c16.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_c16.gif new file mode 100644 index 00000000..81d60af1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_c16.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_coco.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_coco.gif new file mode 100644 index 00000000..25fce626 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_coco.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_commodore.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_commodore.gif new file mode 100644 index 00000000..c58eaa56 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_commodore.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_cpc.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_cpc.gif new file mode 100644 index 00000000..470d5f41 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_cpc.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_dreamcast.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_dreamcast.gif new file mode 100644 index 00000000..b4ca761a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_dreamcast.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ds.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ds.gif new file mode 100644 index 00000000..ac3b894a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ds.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_dtv.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_dtv.gif new file mode 100644 index 00000000..337e4f78 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_dtv.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ent.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ent.gif new file mode 100644 index 00000000..bdecb692 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ent.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_flash.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_flash.gif new file mode 100644 index 00000000..d445a378 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_flash.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_freebsd.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_freebsd.gif new file mode 100644 index 00000000..79fae70a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_freebsd.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gameboy.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gameboy.gif new file mode 100644 index 00000000..2bb23105 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gameboy.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gamepark32.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gamepark32.gif new file mode 100644 index 00000000..31a52215 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gamepark32.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gba.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gba.gif new file mode 100644 index 00000000..75adba30 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gba.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gbc.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gbc.gif new file mode 100644 index 00000000..7521d7f8 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gbc.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gg.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gg.gif new file mode 100644 index 00000000..ecfc9552 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gg.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gp2x.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gp2x.gif new file mode 100644 index 00000000..1594ebfe Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gp2x.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_gus.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_gus.gif new file mode 100644 index 00000000..33fa35aa Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_gus.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_iigs.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_iigs.gif new file mode 100644 index 00000000..ec06b373 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_iigs.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_intellivision.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_intellivision.gif new file mode 100644 index 00000000..8ef3bcc0 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_intellivision.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ipod.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ipod.gif new file mode 100644 index 00000000..3a1bfbcd Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ipod.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_jag.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_jag.gif new file mode 100644 index 00000000..46d72705 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_jag.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_java.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_java.gif new file mode 100644 index 00000000..61be6df9 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_java.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_js.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_js.gif new file mode 100644 index 00000000..9c7ed9d1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_js.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_linux.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_linux.gif new file mode 100644 index 00000000..563d9051 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_linux.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_lynx.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_lynx.gif new file mode 100644 index 00000000..c8d5f8c2 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_lynx.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_mac.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_mac.gif new file mode 100644 index 00000000..16a3c824 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_mac.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_macb.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_macb.gif new file mode 100644 index 00000000..6a038657 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_macb.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_macosx.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_macosx.gif new file mode 100644 index 00000000..94ed00cf Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_macosx.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_mastersystem.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_mastersystem.gif new file mode 100644 index 00000000..37af7cf0 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_mastersystem.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_megadrive.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_megadrive.gif new file mode 100644 index 00000000..a8e455e3 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_megadrive.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_mobiles.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_mobiles.gif new file mode 100644 index 00000000..4d37e153 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_mobiles.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_msdos.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_msdos.gif new file mode 100644 index 00000000..879bc890 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_msdos.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_msx.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_msx.gif new file mode 100644 index 00000000..01ca621b Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_msx.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_msx2.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_msx2.gif new file mode 100644 index 00000000..38a9d0d8 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_msx2.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_msx2p.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_msx2p.gif new file mode 100644 index 00000000..eb5e0099 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_msx2p.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_msxt.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_msxt.gif new file mode 100644 index 00000000..420e76f4 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_msxt.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_n64.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_n64.gif new file mode 100644 index 00000000..761f3c17 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_n64.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_neogeopocket.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_neogeopocket.gif new file mode 100644 index 00000000..49425f5c Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_neogeopocket.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_nes.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_nes.gif new file mode 100644 index 00000000..3269e0a1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_nes.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ngc.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ngc.gif new file mode 100644 index 00000000..1449e442 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ngc.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_oric.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_oric.gif new file mode 100644 index 00000000..14e7c040 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_oric.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_palmos.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_palmos.gif new file mode 100644 index 00000000..028625a9 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_palmos.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_pce.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_pce.gif new file mode 100644 index 00000000..c8b61d27 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_pce.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_php.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_php.gif new file mode 100644 index 00000000..350cb279 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_php.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_pocketpc.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_pocketpc.gif new file mode 100644 index 00000000..8983388a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_pocketpc.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_pokemon.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_pokemon.gif new file mode 100644 index 00000000..2c88b9b0 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_pokemon.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_processing.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_processing.gif new file mode 100644 index 00000000..1e09d303 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_processing.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ps1.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ps1.gif new file mode 100644 index 00000000..ff6d5a12 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ps1.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ps2.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ps2.gif new file mode 100644 index 00000000..d7d017d8 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ps2.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ps3.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ps3.gif new file mode 100644 index 00000000..234803b7 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ps3.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_psp.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_psp.gif new file mode 100644 index 00000000..58b0ca2a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_psp.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_samcoupe.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_samcoupe.gif new file mode 100644 index 00000000..fc0420b1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_samcoupe.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_sgi.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_sgi.gif new file mode 100644 index 00000000..bcae6722 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_sgi.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_snes.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_snes.gif new file mode 100644 index 00000000..e288e90f Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_snes.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_solaris.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_solaris.gif new file mode 100644 index 00000000..b0cc1c6a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_solaris.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_sv.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_sv.gif new file mode 100644 index 00000000..fc6fb052 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_sv.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_thomson.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_thomson.gif new file mode 100644 index 00000000..bd0d28b7 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_thomson.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_ti8x.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_ti8x.gif new file mode 100644 index 00000000..119d40c1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_ti8x.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_tt.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_tt.gif new file mode 100644 index 00000000..d60db5ba Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_tt.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_vb.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_vb.gif new file mode 100644 index 00000000..80a7b256 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_vb.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_vectrex.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_vectrex.gif new file mode 100644 index 00000000..9d75b216 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_vectrex.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_vic20.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_vic20.gif new file mode 100644 index 00000000..4b689928 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_vic20.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_wii.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_wii.gif new file mode 100644 index 00000000..a0637b6c Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_wii.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_wild.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_wild.gif new file mode 100644 index 00000000..9bbbcd60 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_wild.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_win.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_win.gif new file mode 100644 index 00000000..bf216199 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_win.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_wonderswan.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_wonderswan.gif new file mode 100644 index 00000000..d71dad8f Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_wonderswan.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_xbox.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_xbox.gif new file mode 100644 index 00000000..99cf3abd Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_xbox.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_xbox360.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_xbox360.gif new file mode 100644 index 00000000..9360895d Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_xbox360.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_zx.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_zx.gif new file mode 100644 index 00000000..9d54103a Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_zx.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/icons/os/k_zx81.gif b/com.wudsn.ide.asm.compilers.test/icons/os/k_zx81.gif new file mode 100644 index 00000000..b6a56118 Binary files /dev/null and b/com.wudsn.ide.asm.compilers.test/icons/os/k_zx81.gif differ diff --git a/com.wudsn.ide.asm.compilers.test/plugin.properties b/com.wudsn.ide.asm.compilers.test/plugin.properties new file mode 100644 index 00000000..51dfa568 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/plugin.properties @@ -0,0 +1,10 @@ +com.wudsn.ide.asm.preferences.test.AssemblerPreferencesTestCompilersPage.name=Test Compilers + +com.wudsn.ide.asm.compiler.test.TestCompiler.name=TEST + +com.wudsn.ide.asm.editor.test.TestEditor.name=Test Editor + +com.wudsn.ide.asm.editor.test.TestSourceFile.name=Test Source File + +com.wudsn.ide.asm.runner.test.runner1.name=First Application +com.wudsn.ide.asm.runner.test.runner2.name=Second Application diff --git a/com.wudsn.ide.asm.compilers.test/plugin.xml b/com.wudsn.ide.asm.compilers.test/plugin.xml new file mode 100644 index 00000000..d8947fd6 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/plugin.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm.compilers.test/plugin_de_DE.properties b/com.wudsn.ide.asm.compilers.test/plugin_de_DE.properties new file mode 100644 index 00000000..eaf6a521 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/plugin_de_DE.properties @@ -0,0 +1,10 @@ +com.wudsn.ide.asm.preferences.test.AssemblerPreferencesTestCompilersPage.name=Test Kompiler + +com.wudsn.ide.asm.compiler.test.TestCompiler.name=TEST + +com.wudsn.ide.asm.editor.test.TestEditor.name=Test Editor + +com.wudsn.ide.asm.editor.test.TestSourceFile.name=Test Quell-Datei + +com.wudsn.ide.asm.runner.test.runner1.name=Erste Anwendung +com.wudsn.ide.asm.runner.test.runner2.name=Zweite Anwendung diff --git a/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompiler.java b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompiler.java new file mode 100644 index 00000000..dc70e2f3 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.test; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for TEST. + * + * @author Peter Dell + */ +public final class TestCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public TestCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new TestCompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new TestCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompiler.xml b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompiler.xml new file mode 100644 index 00000000..a86fd45e --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompiler.xml @@ -0,0 +1,362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompilerProcessLogParser.java new file mode 100644 index 00000000..3dc665f1 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompilerProcessLogParser.java @@ -0,0 +1,174 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.test; + +import java.util.List; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link TestCompiler}. + * + * + * Sample error message: + * + * "Pass 1: In Abbuc99-NetBalls-Init.asm, line 24-- Warning: Resizing + * 'BALLSCOLORTAB1', forcing another pass" + * + * "Pass 2: In Abbuc99-NetBalls-Init.asm, line 25-- Warning: Resizing + * 'BALLSCOLORTAB2', forcing another pass" + * + * @author Peter Dell + */ +final class TestCompilerProcessLogParser extends CompilerProcessLogParser { + + private Pattern pattern; + private String sourceFilePattern; + + @Override + protected void initialize() { + pattern = Pattern.compile("In .* line "); + sourceFilePattern = "In " + mainSourceFilePath + ", line "; + } + + @Override + protected void findNextMarker() { + + int index; + String line; + boolean include; + String includeFile; + Matcher matcher = pattern.matcher(errorLog); + + if (matcher.find()) { + index = matcher.start(); + line = errorLog.substring(index); + if (line.startsWith(sourceFilePattern)) { + include = false; + includeFile = ""; + } else { + include = true; + includeFile = line.substring(3, matcher.end() - matcher.start() - 7); + } + index = matcher.end(); + String lineNumberLine = errorLog.substring(index); + errorLog = lineNumberLine; + int numberEndIndex = lineNumberLine.indexOf("--"); + if (numberEndIndex > 0) { + String lineNumberString; + lineNumberString = lineNumberLine.substring(0, numberEndIndex); + + try { + lineNumber = Integer.parseInt(lineNumberString); + int nextIndex = lineNumberLine.indexOf('\n'); + if (index > 0) { + message = lineNumberLine.substring(nextIndex + 1); + int nextIndex2 = message.indexOf('\n'); + if (nextIndex > 0) { + message = message.substring(0, nextIndex2 - 1); + } + message = message.trim(); + } + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } + + if (message.startsWith("Error:")) { + severity = IMarker.SEVERITY_ERROR; + message = message.substring(6); + } else if (message.startsWith("Warning:")) { + severity = IMarker.SEVERITY_WARNING; + message = message.substring(8); + } + + if (include) { + if (lineNumber >= 0) { + message = includeFile + " line " + lineNumber + ": " + message; + } else { + message = includeFile + ": " + message; + } + lineNumber = -1; + } + message = message.trim(); + + // Message mapping. + if (severity == IMarker.SEVERITY_WARNING && message.startsWith("Using bank")) { + severity = IMarker.SEVERITY_INFO; + } + markerAvailable = true; + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + final String EQUATES = "Equates:"; + final String SYMBOL = "Symbol"; + final String TABLE = "table:"; + + String log; + int index; + + log = outputLog; + index = log.indexOf(EQUATES); + if (index >= 0) { + log = log.substring(index + EQUATES.length()); + + StringTokenizer st = new StringTokenizer(log); + String token; + String name; + String hexValue; + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (token.equals(SYMBOL)) { + break; + } + name = token.substring(0, token.length() - 1); + hexValue = st.nextToken(); // Must be there + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + + if (st.hasMoreTokens()) { + token = st.nextToken(); + if (token.equals(TABLE)) { + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (!token.endsWith(":")) { + break; + } + name = token.substring(0, token.length() - 1); + hexValue = st.nextToken(); // Must be there + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + } + } + } + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompilerSourceParser.java b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompilerSourceParser.java new file mode 100644 index 00000000..ee96654a --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/compiler/test/TestCompilerSourceParser.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.test; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link TestCompiler}. + * + * @author Peter Dell + */ +final class TestCompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + // Check for origin statement + if (symbol.equals("*")) { //$NON-NLS-1$ + beginImplementationSection(startOffset, startOffset + symbolOffset, operand, comment); + + } else { + if (instruction.equals("=")) { //$NON-NLS-1$ + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + + } + } + + } // Symbol not empty + } +} diff --git a/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/editor/test/TestEditor.java b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/editor/test/TestEditor.java new file mode 100644 index 00000000..2c497f73 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/editor/test/TestEditor.java @@ -0,0 +1,18 @@ +package com.wudsn.ide.asm.editor.test; + +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class TestEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public TestEditor() { + + } + + @Override + public String getCompilerId() { + return "test"; + } +} diff --git a/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/preferences/test/AssemblerPreferencesTestCompilersPage.java b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/preferences/test/AssemblerPreferencesTestCompilersPage.java new file mode 100644 index 00000000..57d83797 --- /dev/null +++ b/com.wudsn.ide.asm.compilers.test/src/com/wudsn/ide/asm/preferences/test/AssemblerPreferencesTestCompilersPage.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences.test; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding arbitrary compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesTestCompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesTestCompilersPage() { + super(Hardware.TEST); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/.classpath b/com.wudsn.ide.asm.compilers/.classpath new file mode 100644 index 00000000..8a8f1668 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/com.wudsn.ide.asm.compilers/.project b/com.wudsn.ide.asm.compilers/.project new file mode 100644 index 00000000..1c2697d4 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/.project @@ -0,0 +1,40 @@ + + + com.wudsn.ide.asm.compilers + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + + + binaries/build-compilers-new.bat + 1 + C:/jac/wudsn/Tools/WWW/build-compilers-new.bat + + + binaries/build.bat + 1 + C:/jac/wudsn/Tools/WWW/build.bat + + + diff --git a/com.wudsn.ide.asm.compilers/.settings/org.eclipse.jdt.core.prefs b/com.wudsn.ide.asm.compilers/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..f287d53c --- /dev/null +++ b/com.wudsn.ide.asm.compilers/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/com.wudsn.ide.asm.compilers/META-INF/MANIFEST.MF b/com.wudsn.ide.asm.compilers/META-INF/MANIFEST.MF new file mode 100644 index 00000000..696a81fa --- /dev/null +++ b/com.wudsn.ide.asm.compilers/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: WUDSN IDE Assembler Compilers Plug-in +Bundle-SymbolicName: com.wudsn.ide.asm.compilers;singleton:=true +Bundle-Version: 1.7.0.qualifier +Bundle-Vendor: Peter Dell +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-Localization: plugin +Require-Bundle: com.wudsn.ide.asm, + org.eclipse.ui.ide +Bundle-ActivationPolicy: lazy +Bundle-ClassPath: . diff --git a/com.wudsn.ide.asm.compilers/bin/.gitignore b/com.wudsn.ide.asm.compilers/bin/.gitignore new file mode 100644 index 00000000..43e58b99 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/bin/.gitignore @@ -0,0 +1 @@ +/com diff --git a/com.wudsn.ide.asm.compilers/binaries/.gitignore b/com.wudsn.ide.asm.compilers/binaries/.gitignore new file mode 100644 index 00000000..d1d483cb --- /dev/null +++ b/com.wudsn.ide.asm.compilers/binaries/.gitignore @@ -0,0 +1 @@ +/compilers diff --git a/com.wudsn.ide.asm.compilers/build.properties b/com.wudsn.ide.asm.compilers/build.properties new file mode 100644 index 00000000..236bd606 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/build.properties @@ -0,0 +1,15 @@ +source.. = src/ +output.. = bin/ +bin.includes = .,\ + plugin.xml,\ + icons/,\ + META-INF/,\ + plugin.properties,\ + src/,\ + build.properties,\ + .project,\ + .classpath,\ + plugin_de_DE.properties +jars.compile.order = . + + diff --git a/com.wudsn.ide.asm.compilers/icons/editor-acme-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-acme-16x16.gif new file mode 100644 index 00000000..d6d12fd1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-acme-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-asm6-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-asm6-16x16.gif new file mode 100644 index 00000000..3269e0a1 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-asm6-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-atasm-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-atasm-16x16.gif new file mode 100644 index 00000000..e4633f21 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-atasm-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-dasm-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-dasm-16x16.gif new file mode 100644 index 00000000..933dc281 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-dasm-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-kickass-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-kickass-16x16.gif new file mode 100644 index 00000000..21a1fbd3 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-kickass-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-mads-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-mads-16x16.gif new file mode 100644 index 00000000..23b9db7c Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-mads-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-merlin32-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-merlin32-16x16.gif new file mode 100644 index 00000000..9755078c Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-merlin32-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-tass-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-tass-16x16.gif new file mode 100644 index 00000000..21a1fbd3 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-tass-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/icons/editor-xasm-16x16.gif b/com.wudsn.ide.asm.compilers/icons/editor-xasm-16x16.gif new file mode 100644 index 00000000..86b761f4 Binary files /dev/null and b/com.wudsn.ide.asm.compilers/icons/editor-xasm-16x16.gif differ diff --git a/com.wudsn.ide.asm.compilers/plugin.properties b/com.wudsn.ide.asm.compilers/plugin.properties new file mode 100644 index 00000000..c08f51bc --- /dev/null +++ b/com.wudsn.ide.asm.compilers/plugin.properties @@ -0,0 +1,75 @@ +com.wudsn.ide.asm.compiler.acme.AcmeCompiler.name=ACME +com.wudsn.ide.asm.compiler.asm6.Asm6Compiler.name=ASM6 +com.wudsn.ide.asm.compiler.atasm.AtasmCompiler.name=ATASM +com.wudsn.ide.asm.compiler.dasm.DasmCompiler.name=DASM +com.wudsn.ide.asm.compiler.kickass.KickAssCompiler.name=KICKASS +com.wudsn.ide.asm.compiler.mads.MadsCompiler.name=MADS +com.wudsn.ide.asm.compiler.merlin32.Merlin32Compiler.name=MERLIN32 +com.wudsn.ide.asm.compiler.tass.TassCompiler.name=TASS +com.wudsn.ide.asm.compiler.xasm.XasmCompiler.name=XASM + +com.wudsn.ide.asm.compiler.acme.AcmeSourceFile.name=ACME Source File +com.wudsn.ide.asm.compiler.asm6.Asm6SourceFile.name=ASM6 Source File +com.wudsn.ide.asm.compiler.atasm.AtasmSourceFile.name=ATASM Source File +com.wudsn.ide.asm.compiler.dasm.DasmSourceFile.name=DASM Source File +com.wudsn.ide.asm.compiler.kickass.KickAssSourceFile.name=KICKASS Source File +com.wudsn.ide.asm.compiler.mads.MadsSourceFile.name=MADS Source File +com.wudsn.ide.asm.compiler.merlin32.Merlin32SourceFile.name=MERLIN32 Source File +com.wudsn.ide.asm.compiler.tass.TassSourceFile.name=TASS Source File +com.wudsn.ide.asm.compiler.xasm.XasmSourceFile.name=XASM Source File + +com.wudsn.ide.asm.editor.acme.AcmeEditor.name=ACME Editor +com.wudsn.ide.asm.editor.asm6.Asm6Editor.name=ASM6 Editor +com.wudsn.ide.asm.editor.atasm.AtasmEditor.name=ATASM Editor +com.wudsn.ide.asm.editor.dasm.DasmEditor.name=DASM Editor +com.wudsn.ide.asm.editor.kickass.KickAssEditor.name=KICKASS Editor +com.wudsn.ide.asm.editor.mads.MadsEditor.name=MADS Editor +com.wudsn.ide.asm.editor.merlin32.Merlin32Editor.name=MERLIN32 Editor +com.wudsn.ide.asm.editor.tass.TassEditor.name=TASS Editor +com.wudsn.ide.asm.editor.xasm.XasmEditor.name=XASM Editor + +# +# Hardware: APPLE2 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesApple2CompilersPage.name=Apple II Compilers +com.wudsn.ide.asm.runner.apple2.applewin.name=AppleWin +com.wudsn.ide.asm.runner.apple2.jace.name=JACE +com.wudsn.ide.asm.runner.apple2.virtu.name=Virtu +com.wudsn.ide.asm.runner.apple2.virtualii.name=Virtual ][ + +# +# Hardware: ATARI2600 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesAtari2600CompilersPage.name=Atari 2600 Compilers +com.wudsn.ide.asm.runner.atari2600.stella.name=Stella + +# +# Hardware: ATARI7800 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesAtari7800CompilersPage.name=Atari 7800 Compilers +com.wudsn.ide.asm.runner.atari7800.emu7800.name=EMU7800 + +# +# Hardware: ATARI8 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesAtari8CompilersPage.name=Atari 8-bit Compilers +com.wudsn.ide.asm.runner.atari8.atari800.name=Atari800 +com.wudsn.ide.asm.runner.atari8.atari800_win.name=Atari800Win +com.wudsn.ide.asm.runner.atari8.atari800_macx.name=Atari800MacX +com.wudsn.ide.asm.runner.atari8.altirra.name=Altirra +com.wudsn.ide.asm.runner.atari8.atari_plus_plus.name=Atari++ + +# +# Hardware: C64 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesC64CompilersPage.name=C64 Compilers +com.wudsn.ide.asm.runner.c64.ccs64.name=CCS64 +com.wudsn.ide.asm.runner.c64.vice.name=VICE + +# +# Hardware: NES +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesNESCompilersPage.name=NES Compilers +com.wudsn.ide.asm.runner.nes.bsnes.name=BSNES +com.wudsn.ide.asm.runner.nes.fceux.name=FCEUX +com.wudsn.ide.asm.runner.nes.nintendulator.name=Nintendulator diff --git a/com.wudsn.ide.asm.compilers/plugin.xml b/com.wudsn.ide.asm.compilers/plugin.xml new file mode 100644 index 00000000..ead35b4b --- /dev/null +++ b/com.wudsn.ide.asm.compilers/plugin.xml @@ -0,0 +1,529 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm.compilers/plugin_de_DE.properties b/com.wudsn.ide.asm.compilers/plugin_de_DE.properties new file mode 100644 index 00000000..4ba0fae0 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/plugin_de_DE.properties @@ -0,0 +1,75 @@ +com.wudsn.ide.asm.compiler.acme.AcmeCompiler.name=ACME +com.wudsn.ide.asm.compiler.asm6.Asm6Compiler.name=ASM6 +com.wudsn.ide.asm.compiler.atasm.AtasmCompiler.name=ATASM +com.wudsn.ide.asm.compiler.dasm.DasmCompiler.name=DASM +com.wudsn.ide.asm.compiler.kickass.KickAssCompiler.name=KICKASS +com.wudsn.ide.asm.compiler.mads.MadsCompiler.name=MADS +com.wudsn.ide.asm.compiler.merlin32.Merlin32Compiler.name=MERLIN32 +com.wudsn.ide.asm.compiler.tass.TassCompiler.name=TASS +com.wudsn.ide.asm.compiler.xasm.XasmCompiler.name=XASM + +com.wudsn.ide.asm.compiler.acme.AcmeSourceFile.name=ACME Quelll-Datei +com.wudsn.ide.asm.compiler.asm6.Asm6SourceFile.name=ASM6 Quelll-Datei +com.wudsn.ide.asm.compiler.atasm.AtasmSourceFile.name=ATASM Quelll-Datei +com.wudsn.ide.asm.compiler.dasm.DasmSourceFile.name=DASM Quelll-Datei +com.wudsn.ide.asm.compiler.kickass.KickAssSourceFile.name=KICKASS Quelll-Datei +com.wudsn.ide.asm.compiler.mads.MadsSourceFile.name=MADS Quelll-Datei +com.wudsn.ide.asm.compiler.merlin32.Merlin32SourceFile.name=MERLIN32 Quelll-Datei +com.wudsn.ide.asm.compiler.tass.TassSourceFile.name=TASS Quelll-Datei +com.wudsn.ide.asm.compiler.xasm.XasmSourceFile.name=XASM Quelll-Datei + +com.wudsn.ide.asm.editor.acme.AcmeEditor.name=ACME Editor +com.wudsn.ide.asm.editor.asm6.Asm6Editor.name=ASM6 Editor +com.wudsn.ide.asm.editor.atasm.AtasmEditor.name=ATASM Editor +com.wudsn.ide.asm.editor.dasm.DasmEditor.name=DASM Editor +com.wudsn.ide.asm.editor.kickass.KickAssEditor.name=KICKASS Editor +com.wudsn.ide.asm.editor.mads.MadsEditor.name=MADS Editor +com.wudsn.ide.asm.editor.merlin32.Merlin32Editor.name=MERLIN32 Editor +com.wudsn.ide.asm.editor.tass.TassEditor.name=TASS Editor +com.wudsn.ide.asm.editor.xasm.XasmEditor.name=XASM Editor + +# +# Hardware: APPLE2 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesApple2CompilersPage.name=Apple II Kompiler +com.wudsn.ide.asm.runner.apple2.applewin.name=AppleWin +com.wudsn.ide.asm.runner.apple2.jace.name=JACE +com.wudsn.ide.asm.runner.apple2.virtu.name=Virtu +com.wudsn.ide.asm.runner.apple2.virtualii.name=Virtual ][ + +# +# Hardware: ATARI2600 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesAtari2600CompilersPage.name=Atari 2600 Kompiler +com.wudsn.ide.asm.runner.atari2600.stella.name=Stella + +# +# Hardware: ATARI7800 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesAtari7800CompilersPage.name=Atari 7800 Kompiler +com.wudsn.ide.asm.runner.atari7800.emu7800.name=EMU7800 + +# +# Hardware: ATARI8 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesAtari8CompilersPage.name=Atari 8-bit Kompiler +com.wudsn.ide.asm.runner.atari8.atari800.name=Atari800 +com.wudsn.ide.asm.runner.atari8.atari800_win.name=Atari800Win +com.wudsn.ide.asm.runner.atari8.atari800_macx.name=Atari800MacX +com.wudsn.ide.asm.runner.atari8.altirra.name=Altirra +com.wudsn.ide.asm.runner.atari8.atari_plus_plus.name=Atari++ + +# +# Hardware: C64 +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesC64CompilersPage.name=C64 Kompiler +com.wudsn.ide.asm.runner.c64.ccs64.name=CCS64 +com.wudsn.ide.asm.runner.c64.vice.name=VICE + +# +# Hardware: NES +# +com.wudsn.ide.asm.preferences.AssemblerPreferencesNESCompilersPage.name=NES Kompiler +com.wudsn.ide.asm.runner.nes.bsnes.name=BSNES +com.wudsn.ide.asm.runner.nes.fceux.name=FCEUX +com.wudsn.ide.asm.runner.nes.nintendulator.name=Nintendulator diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/CompilerId.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/CompilerId.java new file mode 100644 index 00000000..cbc5569b --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/CompilerId.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +/** + * Compiler IDs for static use. + * + * @author Peter Dell + * @since 1.6.1 + */ +public class CompilerId { + public static final String ACME = "acme"; + public static final String ASM6 = "asm6"; + public static final String ATASM = "atasm"; + public static final String DASM = "dasm"; + public static final String KICKASS = "kickass"; + public static final String MADS = "mads"; + public static final String MERLIN32 = "merlin32"; + public static final String TASS = "tass"; + public static final String XASM = "xasm"; +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompiler.java new file mode 100644 index 00000000..c3fa7461 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompiler.java @@ -0,0 +1,49 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.compiler.acme; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for ACME. + * + * @author Peter Dell + */ +public final class AcmeCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public AcmeCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new AcmeCompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new AcmeCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompiler.xml new file mode 100644 index 00000000..2b62c867 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompiler.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompilerProcessLogParser.java new file mode 100644 index 00000000..c79f5762 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompilerProcessLogParser.java @@ -0,0 +1,123 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.acme; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link AcmeCompiler}. + * + * Sample error message: + * + *
+ * Error - File include/ACME-Reference-Source-Include.a, line 3 (Zone ): Value not yet defined.
+ * Error - File C:\Users\D025328\Documents\Eclipse\workspace.jac\com.wudsn.ide.ref\ASM\C64\ACME\ACME-Error-Reference.a, line 9 (Zone ): Value not yet defined.
+ * 
+ * + * @author Peter Dell + */ +final class AcmeCompilerProcessLogParser extends CompilerProcessLogParser { + private BufferedReader bufferedReader; + + @Override + protected void initialize() { + bufferedReader = new BufferedReader(new StringReader(errorLog)); + } + + @Override + protected void findNextMarker() { + + String line; + line = ""; + + while (line != null && !markerAvailable) { + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + String pattern; + int index; + pattern = "Error - File "; + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(pattern); + if (index < 0) { + pattern = "Warning - File "; + severity = IMarker.SEVERITY_WARNING; + index = line.indexOf(pattern); + } + if (index >= 0) { + + index = index + pattern.length(); + pattern = ", line "; + int i = line.indexOf(pattern); + if (i == -1) { + continue; + } + filePath = line.substring(index, i); + + i = i + pattern.length(); + int j = i; + while (line.charAt(j) != ' ' && j < line.length()) { + j++; + } + + String lineNumberString = line.substring(i, j); + + try { + lineNumber = Integer.parseInt(lineNumberString); + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + + pattern = "): "; + line = line.substring(j); + j = line.indexOf(pattern); + if (j > -1) { + j = j + pattern.length(); + message = line.substring(j); + } else { + message = line; + } + + markerAvailable = true; + } + + } + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompilerSourceParser.java new file mode 100644 index 00000000..2f265526 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/acme/AcmeCompilerSourceParser.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.acme; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link AcmeCompiler}. + * + * @author Peter Dell + */ +final class AcmeCompilerSourceParser extends CompilerSourceParser { + + /** + * Creation is package local. + */ + AcmeCompilerSourceParser() { + + } + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + // Check for origin statement + if (symbol.equals("*")) { + beginImplementationSection(startOffset, startOffset + symbolOffset, operand, comment); + + } else { + if (instruction.equals("=")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + + } + } + + } // Symbol not empty + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6Compiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6Compiler.java new file mode 100644 index 00000000..e29d08d1 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6Compiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.asm6; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for ASM6. + * + * @author Peter Dell + */ +public final class Asm6Compiler extends Compiler { + + /** + * Creates a new instance. + */ + public Asm6Compiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new Asm6CompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new Asm6CompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6Compiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6Compiler.xml new file mode 100644 index 00000000..a03129c1 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6Compiler.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6CompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6CompilerProcessLogParser.java new file mode 100644 index 00000000..f08713da --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6CompilerProcessLogParser.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.asm6; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link Asm6Compiler}. + * + * Sample error message: + * + *
+ * include/ASM6-Reference-Source-Include.asm(2): Illegal instruction.
+ * include/ASM6-Reference-Source-Include.asm(4): Illegal instruction.
+ * 
+ * + * @author Peter Dell + */ +final class Asm6CompilerProcessLogParser extends CompilerProcessLogParser { + + private BufferedReader bufferedReader; + + @Override + protected void initialize() { + bufferedReader = new BufferedReader(new StringReader(errorLog)); + } + + @Override + protected void findNextMarker() { + + String line; + line = ""; + + while (line != null && !markerAvailable) { + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + String pattern; + int index; + pattern = "): "; + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(pattern); + if (index < 2) { + pattern = "): "; + severity = IMarker.SEVERITY_WARNING; + index = line.indexOf(pattern); + } + if (index > 2) { + + int i = index - 2; + while (line.charAt(i) != '(' && i >= 0) { + i--; + } + + if (line.charAt(i) == '(') { + String lineNumberString = line.substring(i + 1, index); + + try { + lineNumber = Integer.parseInt(lineNumberString); + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } else { + lineNumber = -1; + } + message = line.substring(index + pattern.length()).trim(); + + filePath = line.substring(0, i); + markerAvailable = true; + } + + } + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6CompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6CompilerSourceParser.java new file mode 100644 index 00000000..ee30c3a3 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/asm6/Asm6CompilerSourceParser.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.asm6; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link Asm6Compiler}. + * + * @author Peter Dell + */ +final class Asm6CompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + // Check for origin statement + if (symbol.equals("*")) { + beginImplementationSection(startOffset, startOffset + symbolOffset, operand, comment); + + } else { + if (instruction.equals("=")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + + } + } + + } // Symbol not empty + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompiler.java new file mode 100644 index 00000000..cb361dec --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.atasm; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for ATASM. + * + * @author Peter Dell + */ +public final class AtasmCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public AtasmCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new AtasmCompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new AtasmCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompiler.xml new file mode 100644 index 00000000..160487f6 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompiler.xml @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompilerProcessLogParser.java new file mode 100644 index 00000000..271eac64 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompilerProcessLogParser.java @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.atasm; + +import java.util.List; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link AtasmCompiler}. + * + * Sample error message: + * + *
+ * "Pass 1: In Abbuc99-NetBalls-Init.asm, line 24-- Warning: Resizing 'BALLSCOLORTAB1', forcing another pass"
+ * "Pass 2: In Abbuc99-NetBalls-Init.asm, line 25-- Warning: Resizing 'BALLSCOLORTAB2', forcing another pass"
+ * 
+ * + * @author Peter Dell + */ +final class AtasmCompilerProcessLogParser extends CompilerProcessLogParser { + + private Pattern pattern; + + @Override + protected void initialize() { + pattern = Pattern.compile("In .* line "); + } + + @Override + protected void findNextMarker() { + + int index; + String line; + Matcher matcher = pattern.matcher(errorLog); + + if (matcher.find()) { + index = matcher.start(); + line = errorLog.substring(index); + filePath = line.substring(3, matcher.end() - matcher.start() - 7); + index = matcher.end(); + String lineNumberLine = errorLog.substring(index); + errorLog = lineNumberLine; + int numberEndIndex = lineNumberLine.indexOf("--"); + if (numberEndIndex > 0) { + String lineNumberString; + lineNumberString = lineNumberLine.substring(0, numberEndIndex); + + try { + lineNumber = Integer.parseInt(lineNumberString); + int nextIndex = lineNumberLine.indexOf("\n"); + if (nextIndex > 0) { + message = lineNumberLine.substring(nextIndex + 1); + int nextIndex2 = message.indexOf("\n"); + if (nextIndex2 > 0) { + message = message.substring(0, nextIndex2 - 1); + } + message = message.trim(); + } + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } + + if (message.startsWith("Error:")) { + severity = IMarker.SEVERITY_ERROR; + message = message.substring(6); + } else if (message.startsWith("Warning:")) { + severity = IMarker.SEVERITY_WARNING; + message = message.substring(8); + } + + message = message.trim(); + + // Message mapping. + if (severity == IMarker.SEVERITY_WARNING && message.startsWith("Using bank")) { + severity = IMarker.SEVERITY_INFO; + } + markerAvailable = true; + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + final String EQUATES = "Equates:"; + final String SYMBOL = "Symbol"; + final String TABLE = "table:"; + + String log; + int index; + + log = outputLog; + index = log.indexOf(EQUATES); + if (index >= 0) { + log = log.substring(index + EQUATES.length()); + + StringTokenizer st = new StringTokenizer(log); + String token; + String name; + String hexValue; + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (token.equals(SYMBOL)) { + break; + } + name = token.substring(0, token.length() - 1); + hexValue = st.nextToken(); // Must be there + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + + if (st.hasMoreTokens()) { + token = st.nextToken(); + if (token.equals(TABLE)) { + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (!token.endsWith(":")) { + break; + } + name = token.substring(0, token.length() - 1); + hexValue = st.nextToken(); // Must be there + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + } + } + } + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompilerSourceParser.java new file mode 100644 index 00000000..ad3f0f5d --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/atasm/AtasmCompilerSourceParser.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.atasm; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link AtasmCompiler}. + * + * @author Peter Dell + */ +final class AtasmCompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + // Check for origin statement + if (symbol.equals("*")) { + beginImplementationSection(startOffset, startOffset, operand, comment); + + } else { + if (instruction.equals("=")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + } + } + + } // Symbol not empty + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DASM.txt b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DASM.txt new file mode 100644 index 00000000..f524c3f6 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DASM.txt @@ -0,0 +1,750 @@ + + +DOCUMENTATION FOR DASM, a high level macro cross assembler for: + + -6502 (and 6507) + -68705 + -6803 + -HD6303 (extension of 6803) + -68HC11 + +DASM was created in 1988 by Matthew Dillon (dillon@apollo.backplane.com) +and modified in 1995 by Olaf 'Rhialto' Seibert (rhialto@polderland.nl), and +is now updated and maintained by Andrew Davie (atari2600@taswegian.com) + +DASM's homepage is http://www.atari2600.org/dasm + +DASM is freely re-distributable, and provided with full source-code to +allow recompilation for other target-platforms. + + +PREFACE FROM MATT: + + Over the last year my work has included writing software to drive small + single-chip microcomputers for various things (remote telemetry units, + for instance). I have had need to program quite a few different + processors over that time. + + At the beginning, I used an awful macro assembler running on an IBM-PC. + I *really* wanted to do it on my Amiga. Thus the writing of this + program. + + Feel free to suggest other similar processors for me to add to the list! + The processor type is specified with a pseudo-op (see below). This + assembler produces only binary output in one of three formats described + below. In general, one has a master assembly file which INCLUDEs all + the modules. + + Also provided is FTOHEX which converts an output file in one of the + three formats to an intel-hex format suitable for many intelligent + prom programmers (I have a GTEK). + + YES it's packed with features! + +FEATURES: + + -fast assembly + -supports several common 8 bit processor models (NOT 8086, thank god!) + -takes as many passes as needed + -automatic checksum generation, special symbol '...' + -several binary output formats available. Format 2 allows reverse + indexed origins. + -multiple segments, BSS segments (no generation), relocatable origin. + -expressions, as in C but [] is used instead of () for parenthesis. + (all expressions are computed with 32 bit integers) + -no real limitation on label size, label values are 32 bits. + -complex pseudo ops, repeat loops, macros, etc.... + + +PREFACE FROM ANDREW (APRIL/2003) + +The documentation is lagging a bit behind the modifications. Essentially the information contained herein is correct, but there have been minor changes to the formatting of output data. + +Please see the accompanying ‘README.TXT’ in the distributable root directory for a list of recent changes to the package. + + +COMMAND LINE: + + dasm srcfile [options] + + options: -f# select output format 1-3 (default 1, see below) + -oname select output file name (else a.out) + -lname select list file name (else none generated) + -Lname list file, containing all passes + -sname select symbol dump file (else none generated) + -v# select verboseness 0-4 (default 0, see below) + -d debug mode + -DSYMBOL predefine a symbol, set to 0 + -DSYMBOL=EXPRESSION predefine a symbol, set to exp + -MSYMBOL=EXPRESSION define label as in EQM (same as –D) + + -Idir search directory for include and incbin + -p# max number of passes + -P# max number of passes, with less checks + + -T# sort symbol table by (0 = alphabetic (default) + or (non-zero= address/value) + + Example: dasm master.asm -f2 -oout -llist -v3 -DVER=4 + +Note: A slash (/) or dash (-) must prefix options. + + +Return Value: + + The assembler will return 0 on successful compilation, 1 otherwise. + + +FORMAT OPTIONS: + + 1 (DEFAULT) + + The output file contains a two byte origin in LSB,MSB order, then + data until the end of the file. + + Restrictions: Any instructions which generate output (within an + initialized segment) must do so with an ascending PC. Initialized + segments must occur in ascending order. + + 2 RAS (Random Access Segment) + + The output file contains one or more hunks. Each hunk consists + of a 2 byte origin (LSB,MSB), 2 byte length (LSB,MSB), and that + number of data bytes. The hunks occur in the same order as + initialized segments in the assembly. There are no restrictions + to segment ordering (i.e. reverse indexed ORG statements are + allowed). The next hunk begins after the previous hunk's data, + until the end of the file. + + 3 RAW (Raw) + + The output file contains data only (format #1 without the 2 byte + header). Restrictions are the same as for format #1. + + Format 3 RAW (Raw format) + Same as format 1, but NO header origin is generated. You get + nothing but data. + +VERBOSE OPTIONS: + + 0 (default) + + Only warnings and errors are generated + + 1 + -Segment list information generated after each pass + -Include file names are displayed + -statistics on why the assembler is going to make another pass + R1,R2 reason code: R3 + where R1 is the number of times the assembler encountered + something requiring another pass to resolve. R2 is the + number of references to unknown symbols which occured in the + pass (but only R1 determines the need for another pass). R3 + is a BITMASK of the reasons why another pass is required. + See the end of this document for bit designations. + + 2 + mismatches between program labels and equates are displayed + on every pass (usually none occur in the first pass unless you + have re-declared a symbol name). + + displayed information for symbols: + ???? = unknown value + str = symbol is a string + eqm = symbol is an eqm macro + (r) = symbol has been referenced + (s) = symbol created with SET or EQM pseudo-op + + 3 + Unresolved and unreferenced symbols are displayed every pass + (unsorted, sorry) + + 4 + An entire symbol list is displayed every pass to STDOUT. + (unsorted, sorry) + + + +PROCESSOR MODEL: + + The processor model is chosen with the PROCESSOR pseudo-op and should + be the first thing you do in your assembly file. Different processor + models use different integer formats (see below). The word order does + not effect the headers in the output files (-f1 and -f2), which are + always LSB,MSB. The word ordering effects all address, word, and + long generation. + + Only one PROCESSOR pseudo-op may be declared in the entire assembly, + and should be the first thing encountered. + + -6502 LSB,MSB + -68HC11 MSB,LSB + -68705 MSB,LSB + -6803 MSB,LSB + -HD6303 MSB,LSB + -F8 ? + +SEGMENTS: + The SEG pseudo-op creates/sets the current segment. Each segment has + it's own origin and is optionally an 'uninitialized' segment. + Unitialized segments produce no output and have no restrictions. This + is useful for determining the size of a certain assembly sequence + without generating code, and for assigning RAM to labels. + + 'Initialized' segments produce output. The following should be + considered when generating roms: + + (1) The default fill character when using ORG (and format 1 or 3) to + skip forward is 00. This is a GLOBAL default and effects all + segments. See ORG. + + (2) The default fill character for DS is 00 and is independant of + the default fill character for ORG (see DS). + +GENERAL: + Most everything is recursive. You cannot have a macro DEFINITION + within a macro definition, but can nest macro calls, repeat loops, + and include files. + + The other major feature in this assembler is the SUBROUTINE pseudo-op, + which logically separates local labels (starting with a dot). This + allows you to reuse label names (for example, .1 .fail) rather than + think up crazy combinations of the current subroutine to keep it all + unique. + + Almost nothing need be resolved in pass 1. The assembler will make + multiple passes in an attempt to resolve the assembly (including just + one pass if everything is resolved immediately). + + +PSEUDOPS: + + INCLUDE "name" + + Include another assembly file. + +#if OlafIncbin + +[label] INCBIN "name" + + Include another file literally in the output. +#endif +#if OlafIncdir + + INCDIR "directory name" + + Add the given directory name to the list of places where + INCLUDE and INCBIN search their files. First, the names are + tried relative to the current directory, if that fails and + the name is not an absolute pathname, the list is tried. + You can optionally end the name with a /. AmigaDOS filename + conventions imply that two slashes at the + end of an INCDIR (dir//) indicates the parent directory, and + so does an INCLUDE /filename. + + The command-line option -Idir is equivalent to an INCDIR + directive placed before the source file. + + Currently the list is not cleared between passes, but each + exact directory name is added to the list only once. + This may change in subsequent releases. +#endif +[label] SEG[.U] name + + This sets the current segment, creating it if neccessary. If + a .U extension is specified on segment creation, the segment + is an UNINITIALIZED segment. The .U is not needed when going + back to an already created uninitialized segment, though it + makes the code more readable. + +[label] DC[.BWL] exp,exp,exp ... + + Declare data in the current segment. No output is generated if + within a .U segment. Note that the byte ordering for the + selected processor is used for each entry. + + The default size extension is a byte. +#if OlafByte + BYTE, WORD and LONG are synonyms for DC.B, DC.W and DC.L. +#endif + +[label] DS[.BWL] exp[,filler] + + declare space (default filler is 0). Data is not generated if + within an uninitialized segment. Note that the number of bytes + generated is exp * entrysize (1,2, or 4) + + The default size extension is a byte. + + Note that the default filler is always 0 (has nothing to do + with the ORG default filler). + +[label] DV[.BWL] eqmlabel exp,exp,exp.... + + This is equivalent to DC, but each exp in the list is passed + through the symbolic expression specified by the EQM label. + The expression is held in a special symbol dotdot '..' on each + call to the EQM label. + + See EQM below + +[label] HEX hh hh hh.. + + This sets down raw HEX data. Spaces are optional between bytes. + NO EXPRESSIONS are allowed. Note that you do NOT place a $ + in front of the digits. This is a short form for creating + tables compactly. Data is always layed down on a byte-by-byte + basis. + + Example: HEX 1A45 45 13254F 3E12 + + ERR + + Abort assembly. + +[label] ORG exp[,DefaultFillVal] + + This pseudop sets the current origin. You can also set the + global default fill character (a byte value) with this + pseudoop. NOTE that no filler is generated until the first + data-generating opcode/psueoop is encountered after this one. + Sequences like: + + org 0,255 + org 100,0 + org 200 + dc 23 + + will result in 200 zero's and a 23. This allows you to specify + some ORG, then change your mind and specify some other (lower + address) ORG without causing an error (assuming nothing is + generated inbetween). + + Normally, DS and ALIGN are used to generate specific filler + values. + +[label] RORG exp + + This activates the relocatable origin. All generated + addresses, including '.', although physically placed at the + true origin, will use values from the relocatable origin. + While in effect both the physical origin and relocatable origin + are updated. + + The relocatable origin can skip around (no limitations). The + relocatable origin is a function of the segment. That is, you + can still SEG to another segment that does not have a + relocatable origin activated, do other (independant) stuff + there, and then switch back to the current segment and continue + where you left off. + + PROCESSOR model + + do not quote. model is one of: 6502,6803,HD6303,68705,68HC11,F8 + Can only be executed once, and should be the first thing + encountered by the assembler. + + ECHO exp,exp,exp + + The expressions (which may also be strings), are echoed on the + screen and into the list file. + +[label] REND + + Deactivate the relocatable origin for the current segment. + Generation uses the real origin for reference. + +[label] ALIGN N[,fill] + + Align the current PC to an N byte boundry. The default + fill character is always 0, and has nothing to do with + the default fill character specifiable in an ORG. + +[label] SUBROUTINE name + + This isn't really a subroutine, but a boundry between sets of + temporary labels (which begin with a dot). Temporary label + names are unique within segments of code bounded by SUBROUTINE: + + CHARLIE subroutine + ldx #10 + .1 dex + bne .1 + BEN subroutine + ldx #20 + .qq dex + bne .qq + + Automatic temporary label boundries occur for each macro level. + Usually temporary labels are used in macros and within actual + subroutines (so you don't have to think up a thousand different + names) + + +symbol EQU exp +#if OlafAsgn +symbol = exp +#endif + + The expression is evaluated and the result assigned to the + symbol. + +#if OlafDotAssign + If this option is enabled, you can use the common idiom of + + . EQU . + 3 (or . = .+3) + + in other words, you can assign to "." (or "*" if OlafStar + is also enabled) instead of an ORG or RORG directive. + More formally, a directive of the form ". EQU expr" is + interpreted as if it were written " (R)ORG expr". + The RORG is used if a relocatable origin is already in effect, + otherwise ORG is used. Note that the first example is NOT + equivalent with "DS.B 3" when the rorg is in effect. +#endif + +symbol EQM exp + + The STRING representing the expression is assigned to the + symbol. Occurances of the label in later expressions causes + the string to be evaluated for each occurance. Also used in + conjuction with the DV psuedo-op. + +symbol SET exp + + Same as EQU, but the symbol may be reassigned later. + + MAC name + + Declare a macro. lines between MAC and ENDM are the macro. + You cannot recursively declare a macro. You CAN recursively + use a macro (reference a macro in a macro). No label is + allowed to the left of MAC or ENDM. + + Arguments passed to macros are referenced with: {#}. The first + argument passed to a macro would thus be {1}. You should + always use LOCAL labels (.name) inside macros which you use + more than once. {0} represents an EXACT substitution of the + ENTIRE argument line. + + ENDM + + end of macro def. NO LABEL ALLOWED ON THE LEFT! + + MEXIT + + Used in conjuction with conditionals. Exits the current macro + level. + +[label] IFCONST exp + + Is TRUE if the expression result is defined, FALSE otherwise + and NO error is generated if the expression is undefined. + +[label] IFNCONST exp + + Is TRUE if the expression result is undefined, FALSE otherwise + and NO error is generated if the expression is undefined. + +[label] IF exp + + Is TRUE if the expression result is defined AND non-zero. + Is FALSE if the expression result is defined AND zero. + Neither IF or ELSE will be executed if the expression result + is undefined. If the expression is undefined, another assembly + pass is automatically taken. +#if OlafPhase + If this happens, phase errors in the next pass only will not + be reported unless the verboseness is 1 or more. +#endif + +[label] ELSE + + ELSE the current IF. + +[label] ENDIF +[label] EIF + + Terminate an IF. ENDIF and EIF are equivalent. + +[label] REPEAT exp +[label] REPEND + + Repeat code between REPEAT/REPEND 'exp' times. + +#if DAD + if exp == 0, + the code repeats forever. exp is evaluated once. + + If exp == 0, the repeat loop is ignored. + If exp < 0, a warning “REPEAT parameter < 0 (ignored)” is generated and the repeat loop is ignored. +#endif + + Y SET 0 + REPEAT 10 + X SET 0 + REPEAT 10 + DC X,Y + X SET X + 1 + REPEND + Y SET Y + 1 + REPEND + + generates an output table: 0,0 1,0 2,0 ... 9,0 0,1 1,1 2,1 + ... 9,1, etc... + + Labels within a REPEAT/REPEND should be temporary labels with a + SUBROUTINE pseudo-op to keep them unique. + + The Label to the left of REPEND is assigned AFTER the loop + FINISHES. + + +[label] XXX[.force] operand + + XXX is some mnemonic, not necessarily three characters long. + The .FORCE optional extension is used to force specific + addressing modes (see below). + +[label] LIST ON or OFF + + Globally turns listing on or off, starting with the current + line. +#if OlafList + When you give LOCALON or LOCALOFF the effect is local to the + current macro or included file. For a line to be listed both + the global and local list switches must be on. +#endif + +#if OlafDotop + All pseudo-ops (and incidentally also the mnemonics) can be + prefixed with a . for compatibility with other assemblers. + So .IF is the same as IF. This works only because lone .FORCE + extensions are meaningless. +#endif + +#if OlafFreeFormat + The format of each input line is free: first all leading + spaces are discarded, and the first word is examined. If it + does not look like a directive or opcode (as known at that point), + it is taken as a label. This is sort-of nasty if you like labels + with names like END. + The two xxxFormat options are mutually exclusive. +#endif + +#if OlafHashFormat + With this option an initial # (after optional initial spaces) + turns the next word into a directive/opcode. + A ^ skips more spaces and makes the next word a label. +#endif + +GENERAL: + + The label will be set to the current ORG/RORG either before or after + a pseudo-op is executed. Most of the time, the label to the left of a + pseudo-op is the current ORG/RORG. The following pseudo-op's labels are + created AFTER execution of the pseudo-op: + + SEG, ORG, RORG, REND, ALIGN + +EXTENSIONS: + + FORCE extensions are used to force an addressing mode. In some cases, + you can optimize the assembly to take fewer passes by telling it the + addressing mode. Force extensions are also used with DS,DC, and DV + to determine the element size. NOT ALL EXTENSIONS APPLY TO ALL + PROCESSORS! + + example: lda.z charlie + + i -implied + ind -indirect word + 0 -implied + 0x -implied indexing (0,x) + 0y -implied indexing (0,y) + b -byte address + bx -byte address indexed x + by -byte address indexed y + w -word address + wx -word address indexed x + wy -word address indexed y + l -longword (4 bytes) (DS/DC/DV) + r -relative + u -uninitialized (SEG) + + First character equivalent substitutions: + + b z d (byte, zeropage, direct) + w e a (word, extended, absolute) + + +ASSEMBLER PASSES: + The assembler may have to make several passes through the source + code to resolve all generation. The number of passes is not + limited to two. Since this may result in an unexpected, verbose + option 2, 3, and 4 have been provided to allow determination of the + cause. The assembler will give up if it thinks it can't do the + assembly in *any* number of passes. + + Error reporting could be better.... + +#if OlafPasses + + The check if another pass might resolve the source is pretty good, but + not perfect. You can specify the maximum number of passes to do + (default -p10), and with the -P option you can override the normal check. + This allows the following contrived example to resolve in 12 passes: + + org 1 + repeat [[x < 11] ? [x-11]] + 11 + dc.b x + repend + x: + +#endif + +EXPRESSIONS: + [] may be used to group expressions. The precedense of operators + is the same as for the C language in almost all respects. Use + brackets [] when you are unsure. The reason () cannot be used to + group expressions is due to a conflict with the 6502 and other + assembly languages. +#if OlafBraKet + It is possible to use () instead of [] in expressions following + pseudo-ops, but not following mnemonics. So this works: + if target & (pet3001 | pet4001), but this doesn't: + lda #target & (pet3001 | pet4001). +#endif + + Some operators, such as ||, can return a resolved value even if + one of the expressions is not resolved. Operators are as follows: + + NOTE WELL! Some operations will result in non-byte values when a + byte value was wanted. For example: ~1 is NOT $FF, but + $FFFFFFFF. Preceding it with a < (take LSB of) will solve the + problem. ALL ARITHMETIC IS CARRIED OUT IN 32 BITS. The final + Result will be automatically truncated to the maximum handleable + by the particular machine language (usually a word) when applied + to standard mnemonics. + + prec UNARY + + 20 ~exp one's complement. + 20 -exp negation + 20 !exp not expression (returns 0 if exp non-zero, 1 if exp zero) + 20 exp take MSB byte of an expression + + BINARY + + 19 * multiplication + 19 / division + 19 % mod + 18 + addition + 18 - subtraction + 17 >>,<< shift right, shift left + 16 >,>= greater, greater equal + 16 <,<= smaller, smaller equal + 15 == equal to. Try to use this instead of = + 15 = exactly the same as == (exists compatibility) + 15 != not equal to + 14 & logical and + 13 ^ logical xor + 12 | logical or + 11 && left expression is true AND right expression is true + 10 || left expression is true OR right expression is true + 9 ? if left expression is true, result is right expression, + else result is 0. [10 ? 20] returns 20 + 8 [] group expressions + 7 , separate expressions in list (also used in + addressing mode resolution, BE CAREFUL! + + Note: The effect of the C conditional operator a ? b : c can be + had with [a ? b - c] + c. + + Constants: + + nnn decimal + 0nnn octal + %nnn binary + $nnn hex + 'c character + "cc.." string (NOT zero terminated if in DC/DS/DV) + [exp]d the constant expressions is evaluated and it's decimal + result turned into an ascii string. + + Symbols: + + ... -holds CHECKSUM so far (of actual-generated stuff) + .. -holds evaluated value in DV pseudo op + .name -represents a temporary symbol name. Temporary symbols + may be reused inside MACROS and between SUBROUTINES, but + may not be referenced across macros or across SUBROUTINEs. + . -current program counter (as of the beginning of the + instruction). + name -beginning with an alpha character and containing letters, + numbers, or '_'. Represents some global symbol name. +#if OlafStar + * -synonym for ., when not confused as an operator. +#endif +#if OlafDol + nnn$ -temporary label, much like .name, except that defining + a non-temporary label has the effect that SUBROUTINE + has on .name. They are unique within macros, like + .name. Note that 0$ and 00$ are distinct, as are 8$ + and 010$. + (mainly for compatibility with other assemblers.) +#endif + + +WHY codes: + Each bit in the WHY word (verbose option 1) is a reason (why + the assembler needs to do another pass), as follows: + + bit 0 expression in mnemonic not resolved + 1 - + 2 expression in a DC not resolved + 3 expression in a DV not resolved (probably in DV's EQM symbol) + 4 expression in a DV not resolved (could be in DV's EQM symbol) + 5 expression in a DS not resolved + 6 expression in an ALIGN not resolved + 7 ALIGN: Relocatable origin not known (if in RORG at the time) + 8 ALIGN: Normal origin not known (if in ORG at the time) + 9 EQU: expression not resolved + 10 EQU: value mismatch from previous pass (phase error) + 11 IF: expression not resolved + 12 REPEAT: expression not resolved + + 13 a program label has been defined after it has been + referenced (forward reference) and thus we need another + pass + 14 a program label's value is different from that of the + previous pass (phase error) + + Certain errors will cause the assembly to abort immediately, others + will wait until the current pass is other. The remaining allow another + pass to occur in the hopes the error will fix itself. + + +VERSIONS: + + V2.12 + -Fixed macro naming bug (macros wouldn't work if the name after + the 'mac' was in upper case). + + V2.11 + -Fixed exp bug, exp MSB (it used to + be reversed). + -Fixed many bugs in macros and other things + -Added automatic checksumming ... no more doing checksums manually! + -Added several new processors, including 6502. + -Source is now 16/32 bit int compatible, and will compile on an + IBM-PC (the ultimate portability test) + + V2.01 + + -can now have REPEAT/REPEND's within macros + -fill field for DS.W is a word (used to be a byte fill) + -fill field for DS.L is a long (used to be a byte fill) + + diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompiler.java new file mode 100644 index 00000000..33bbfed2 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.dasm; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for DASM. + * + * @author Peter Dell + */ +public final class DasmCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public DasmCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new DasmCompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new DasmCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompiler.xml new file mode 100644 index 00000000..dccb2126 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompiler.xml @@ -0,0 +1,417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompilerProcessLogParser.java new file mode 100644 index 00000000..cb7c1700 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompilerProcessLogParser.java @@ -0,0 +1,270 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.dasm; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.List; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Process log parser for {@link DasmCompiler}. + * + * Sample error message: + * + *
+ * ------- FILE C:\Users\D025328\Documents\Eclipse\workspace.jac\com.wudsn.ide.ref\ASM\Atari2600\DASM\DASM-Error-Reference.asm LEVEL 1 PASS 1
+ *       1  0000 ????
+ * ------- FILE include/DASM-Reference-Source-Include.asm LEVEL 2 PASS 1
+ *       0  0000 ????				      include	"include/DASM-Reference-Source-Include.asm"
+ *       1  0000 ????						;	@com.wudsn.ide.asm.mainsourcefile=../DASM-Error-Reference.asm
+ *       2  0000 ????
+ *       3  0000 ????						;	Reference source include file for DASM
+ *       4  0000 ????
+ * include/DASM-Reference-Source-Include.asm (5): error: Unknown Mnemonic 'jmp'.
+ *       5  0000 ????				      jmp	unknownTest
+ * ------- FILE C:\Users\D025328\Documents\Eclipse\workspace.jac\com.wudsn.ide.ref\ASM\Atari2600\DASM\DASM-Error-Reference.asm
+ *       3  0000 ????
+ * C:\Users\D025328\Documents\Eclipse\workspace.jac\com.wudsn.ide.ref\ASM\Atari2600\DASM\DASM-Error-Reference.asm (4): error: Unknown Mnemonic 'jmp'.
+ *       4  0000 ????				      jmp	nowhere
+ *       5  0000 ????
+ * 
+ * + * @author Peter Dell + */ +final class DasmCompilerProcessLogParser extends CompilerProcessLogParser { + + private Pattern pattern; + + private String listLog; + private String listLogErrorMessage; + private boolean fatalErrorFound; + private boolean unresolvedSymbolsFound; + + @Override + protected void initialize() { + pattern = Pattern.compile(".* (.*): error:"); + + File listFile = new File(files.outputFolder, files.mainSourceFile.fileNameWithoutExtension + ".lst"); + if (listFile.exists()) { + try { + listLog = FileUtility.readString(listFile.getPath(), new FileInputStream(listFile), + FileUtility.MAX_SIZE_UNLIMITED); + listLogErrorMessage = null; + } catch (FileNotFoundException ex) { + listLog = ""; + listLogErrorMessage = ex.getMessage(); + } catch (CoreException ex) { + listLog = ""; + listLogErrorMessage = ex.getStatus().getMessage(); + } + } else { + listLog = ""; + listLogErrorMessage = "Expected list file '" + + listFile.getPath() + + "' does not exist. Check the compiler preferences and make sure you have set the option '-l${outputFilePathWithoutExtension}.lst'."; + } + fatalErrorFound = false; + unresolvedSymbolsFound = false; + } + + @Override + protected void findNextMarker() { + + if (listLogErrorMessage != null) { + filePath = mainSourceFilePath; + lineNumber = 0; + severity = IMarker.SEVERITY_ERROR; + message = listLogErrorMessage; + markerAvailable = true; + listLogErrorMessage = null; + return; + } + + int index; + String line; + Matcher matcher = pattern.matcher(listLog); + + if (matcher.find()) { + index = matcher.start(); + line = listLog.substring(index); + listLog = listLog.substring(matcher.end()); + int numberIndex = line.indexOf(" ("); + + if (numberIndex > 0) { + + filePath = line.substring(0, numberIndex); + + String lineNumberString; + int numberEndIndex = line.indexOf(')'); + if (numberEndIndex > 0) { + lineNumberString = line.substring(numberIndex + 2, numberEndIndex); + } else { + lineNumberString = "-1"; + } + + try { + lineNumber = Integer.parseInt(lineNumberString); + + int nextIndex = line.indexOf("\n"); + if (nextIndex > 0) { + message = line.substring(numberEndIndex + 10, nextIndex - 1); + } + } catch (NumberFormatException ex) { + lineNumber = -1; + message = ex.getMessage(); + } + + severity = IMarker.SEVERITY_ERROR; + message = message.trim(); + markerAvailable = true; + return; + } + } else { + index = outputLog.indexOf("Fatal assembly error: "); + if (index > 0 && !fatalErrorFound) { + int nextIndex = outputLog.indexOf("\n", index); + if (nextIndex > 0) { + message = outputLog.substring(index, nextIndex - 1); + } else { + message = outputLog.substring(index); + } + fatalErrorFound = true; + + severity = IMarker.SEVERITY_ERROR; + message = message.trim(); + markerAvailable = true; + return; + } + + if (fatalErrorFound) { + final String UNRESOLVED = "--- Unresolved Symbol List"; + index = outputLog.lastIndexOf(UNRESOLVED); + if (index > 0) { + unresolvedSymbolsFound = true; + outputLog = outputLog.substring(index + UNRESOLVED.length()).trim(); + } + } + if (unresolvedSymbolsFound) { + index = outputLog.indexOf('\n'); + if (index > 0) { + line = outputLog.substring(0, index - 1); + if (!line.startsWith("--")) { + outputLog = outputLog.substring(index + 1); + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(" "); + if (index > 0) { + line = line.substring(0, index); + } + message = "Unresolved symbol " + line + "."; + markerAvailable = true; + return; + } + } + } + } + + return; + } + + /** + * Type tokens in labels file:
+ * + * ???? = unknown value
+ * str = symbol is a string
+ * eqm = symbol is an eqm macro
+ * (r) = symbol has been referenced
+ * (s) = symbol created with SET or EQM pseudo-op
+ */ + @Override + public void addCompilerSymbols(List compilerSymbols) { + final String SYMBOLS = "--- Symbol List (sorted by symbol)"; + + String log; + int index; + + log = outputLog; + index = log.indexOf(SYMBOLS); + if (index >= 0) { + log = log.substring(index + SYMBOLS.length()).trim(); + + index = log.indexOf('\n'); + while (index > 0) { + String line = log.substring(0, index - 1); + if (line.startsWith("--- End of Symbol List.")) { + break; + } + StringTokenizer st = new StringTokenizer(line); + String name = st.nextToken(); + String hexValue = ""; + if (st.hasMoreTokens()) { + hexValue = st.nextToken(); + } + + String token = ""; + int valueType = CompilerSymbol.NUMBER; + String stringValue = ""; + while (st.hasMoreTokens()) { + token = st.nextToken(); + // "str" indicates that the symbol is a string value + if (token.equals("str")) { + valueType = CompilerSymbol.STRING; + } + + // String values are enclosed in double quotes. + if (valueType == CompilerSymbol.STRING && token.startsWith("\"") && token.length() >= 2 + && token.endsWith("\"")) { + stringValue = token.substring(1, token.length() - 1); + } + } + + switch (valueType) { + case CompilerSymbol.NUMBER: + // Ignore unnamed symbol with value "0000" + if (StringUtility.isSpecified(hexValue)) { + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + break; + case CompilerSymbol.STRING: + compilerSymbols.add(CompilerSymbol.createStringSymbol(name, stringValue)); + break; + default: + throw new IllegalStateException("Unsupported value type '" + valueType + "'."); + } + + log = log.substring(index).trim(); + index = log.indexOf('\n'); + + } + } + + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompilerSourceParser.java new file mode 100644 index 00000000..74098666 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/dasm/DasmCompilerSourceParser.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.dasm; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link DasmCompiler}. + * + * @author Peter Dell + */ +final class DasmCompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + // Check for origin statement + if (symbol.equals("*")) { + beginImplementationSection(startOffset, startOffset + symbolOffset, operand, comment); + + } else { + if (instruction.equals("=")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + + } + } + + } // Symbol not empty + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAss-Libraries.txt b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAss-Libraries.txt new file mode 100644 index 00000000..4e3345ae --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAss-Libraries.txt @@ -0,0 +1,424 @@ +Questions based on your list: +>- There is "ANC_IMM" for "anc" but not "anc2" for "ANC2_IMM"? +Yes thats right, but don't they do the same? + +>- BRA_REL is missing? +From the constants? No, its after RLA_ABSY + + +>- What are the "AT_.." constants? +These are argument type constants. Pseudocommands are a kind of advanced macros that takes instruction arguments. An argument consist of a type (immediate, absolute, etc) and a value and you can manipulate these as you want. Let's say you want to define you own 16bit amgia/pc style move command that can be used like this: + +:mov16 #$1234 ; target // Move immediate to absolute +:mov16 $1000 ; $2000,x // Move from absolute to absolute,x +:mov16 $10,x ; $2000,y // zeropage,x to absolute + +Then you can define it like this: + +.pseudocommand mov16 src;tar { + lda src + sta tar + lda _16bit_nextArgument(src) + sta _16bit_nextArgument(tar) +} + +.function _16bit_nextArgument(arg) { + .if (arg.getType()==AT_IMMEDIATE) .return CmdArgument(arg.getType(),>arg.getValue()) + .return CmdArgument(arg.getType(),arg.getValue()+1) +} + +Btw. Don't worry if you missed a detail in the above. The point is that you use AT_IMMEDIATE constant to detect if the argument is immediate, since you have to treat these differently. + + +>- Is there a list of descriptions & values for the constants? In the content assist, I'd like to display that information. +They are described in the manual. Most of them are seen in tables (left column=constant, right one - description). + + +Hi Peter + +> Why "else" and not ".else"? +Because else is considered part of the .if directive. (In the parse tree the if directive is a node with a true-condition branch and an optional false-condition branch (else)) + +>Is everything case-senstive? +Thats correct + + +Eg: +- All directives start with '.'. For Example: '.byte' '.word' '.import' '.fill' '.for' +- All labels end with ':'. For example: 'label1:' +- All macro / pseudocommand executions start with ':'. For Example: ':MyMacro(27, "Hello")' or ':mov #5 ; $d020' +- Comments are like java / C++ (/* Block comment */, // Line comment) + +The Directives: + +".align" +".assert" +".asserterror" +".by" +".byte" +".const" +".define" +".dw" +".dword" +".easteregg" +".enum" +".error" +".eval" +".filenamespace" +".fill" +".for" +".function" +".if" +".import" +".importonce" +".label" +".macro" +".namespace" +".pc" +".print" +".printnow" +".pseudocommand" +".pseudopc" +".return" +".struct" +".te" +".text" +".var" +".wo" +".word" +"else" + + + +Asm Commands (Including the illegal ones); +"tas"|"php"|"sax"|"rti"|"sta"|"tsx"|"cpy"|"cpx"|"pha"|"bvs"|"bpl"|"dcp"|"adc"| +"tya"|"eor"|"iny"|"inx"|"lax"|"bcs"|"bvc"|"las"|"bit"|"slo"|"txs"|"inc"|"bcc"| +"lsr"|"sre"|"plp"|"txa"|"rla"|"jmp"|"jsr"|"rra"|"pla"|"sei"|"ahx"|"sed"|"sec"| +"ora"|"isc"|"bne"|"and"|"anc"|"cmp"|"bmi"|"asl"|"ldy"|"clv"|"ldx"|"ror"|"cli"| +"alr"|"dey"|"rol"|"cld"|"dex"|"clc"|"arr"|"brk"|"lda"|"shy"|"axs"|"shx"|"sty"| +"stx"|"beq"|"nop"|"dec"|"sbc2"|"sbc"|"tay"|"xaa"|"tax"|"rts" + + +Illegal Ones: +"tas"|"sax"|"dcp"|"lax"|"las"|"slo"|"sre"|"rla"|""rra"|"ahx"|"isc"|"anc" +"alr"|"arr"|"shy"|"axs"|"shx"|"sbc2"|"xaa"| + + +DTV Commands: +"sac"|"sir"|"bra" + +Default macros: +:BasicUpstart(address) +:BasicUpstart2(address) + + +Default Constants: +PI +E +AT_ABSOLUTE +AT_ZEROPAGE +AT_ABSOLUTEX +AT_ABSOLUTEY +AT_IMMEDIATE +AT_INDIRECT +AT_IZEROPAGEX +AT_IZEROPAGEY +AT_NONE +BLACK +WHITE +RED +CYAN +PURPLE +GREEN +BLUE +YELLOW +ORANGE +BROWN +LIGHT_RED +DARK_GRAY +DARK_GREY +GRAY +GREY +LIGHT_GREEN +LIGHT_BLUE +LIGHT_GRAY +LIGHT_GREY +BF_C64FILE +BF_KOALA +BF_FLI +BF_BITMAP_SINGLECOLOR +PLA +SAC_IMM +SLO_ZP +SLO_ZPX +SLO_IZPX +SLO_IZPY +SLO_ABS +SLO_ABSX +SLO_ABSY +PHA +BVC_REL +BRK +CLC +CLD +PLP +EOR_IMM +EOR_ZP +EOR_ZPX +EOR_IZPX +EOR_IZPY +EOR_ABS +EOR_ABSX +EOR_ABSY +INC_ZP +INC_ZPX +INC_ABS +INC_ABSX +PHP +RLA_ZP +RLA_ZPX +RLA_IZPX +RLA_IZPY +RLA_ABS +RLA_ABSX +RLA_ABSY +BRA_REL +JMP_ABS +JMP_IND +BVS_REL +ROR +ROR_ZP +ROR_ZPX +ROR_ABS +ROR_ABSX +CLV +STA_ZP +STA_ZPX +STA_IZPX +STA_IZPY +STA_ABS +STA_ABSX +STA_ABSY +ARR_IMM +SBC_IMM +SBC_ZP +SBC_ZPX +SBC_IZPX +SBC_IZPY +SBC_ABS +SBC_ABSX +SBC_ABSY +CLI +BEQ_REL +LSR +LSR_ZP +LSR_ZPX +LSR_ABS +LSR_ABSX +CPX_IMM +CPX_ZP +CPX_ABS +CPY_IMM +CPY_ZP +CPY_ABS +NOP +BNE_REL +JSR_ABS +ANC_IMM +LDA_IMM +LDA_ZP +LDA_ZPX +LDA_IZPX +LDA_IZPY +LDA_ABS +LDA_ABSX +LDA_ABSY +AND_IMM +AND_ZP +AND_ZPX +AND_IZPX +AND_IZPY +AND_ABS +AND_ABSX +AND_ABSY +RTI +BIT_ZP +BIT_ZPX +BIT_ABS +BIT_ABSX +STX_ZP +STX_ZPY +STX_ABS +TAY +TAX +RTS +TAS_ABSY +SAX_ZP +SAX_ZPY +SAX_IZPX +SAX_ABS +ROL +ROL_ZP +ROL_ZPX +ROL_ABS +ROL_ABSX +INX +INY +STY_ZP +STY_ZPX +STY_ABS +ASL +ASL_ZP +ASL_ZPX +ASL_ABS +ASL_ABSX +TYA +BMI_REL +AHX_IZPY +AHX_ABSY +DEX +DEY +CMP_IMM +CMP_ZP +CMP_ZPX +CMP_IZPX +CMP_IZPY +CMP_ABS +CMP_ABSX +CMP_ABSY +AXS_IMM +ADC_IMM +ADC_ZP +ADC_ZPX +ADC_IZPX +ADC_IZPY +ADC_ABS +ADC_ABSX +ADC_ABSY +TXS +DEC_ZP +DEC_ZPX +DEC_ABS +DEC_ABSX +ISC_ZP +ISC_ZPX +ISC_IZPX +ISC_IZPY +ISC_ABS +ISC_ABSX +ISC_ABSY +ALR_IMM +SRE_ZP +SRE_ZPX +SRE_IZPX +SRE_IZPY +SRE_ABS +SRE_ABSX +SRE_ABSY +TSX +LAS_ABSY +LAX_IMM +LAX_ZP +LAX_ZPY +LAX_IZPX +LAX_IZPY +LAX_ABS +LAX_ABSY +TXA +BCC_REL +SHY_ABSX +SHX_ABSY +RRA_ZP +RRA_ZPX +RRA_IZPX +RRA_IZPY +RRA_ABS +RRA_ABSX +RRA_ABSY +BPL_REL +SEI +XAA_IMM +SIR_IMM +DCP_ZP +DCP_ZPX +DCP_IZPX +DCP_IZPY +DCP_ABS +DCP_ABSX +DCP_ABSY +SED +ORA_IMM +ORA_ZP +ORA_ZPX +ORA_IZPX +ORA_IZPY +ORA_ABS +ORA_ABSX +ORA_ABSY +SEC +ANC2_IMM +BCS_REL +SBC2_IMM +LDY_IMM +LDY_ZP +LDY_ZPX +LDY_ABS +LDY_ABSX +LDX_IMM +LDX_ZP +LDX_ZPY +LDX_ABS +LDX_ABSY + +Default functions: +abs +acos +asin +atan +atan2 +cbrt +ceil +cos +cosh +exp +expm1 +floor +hypot +IEEEremainder +log +log10 +log1p +max +min +pow +random +round +signum +sin +sinh +sqrt +tan +tanh +toDegrees +toRadians +mod +toIntString +toHexString +toOctalString +toBinaryString +Vector +Vector +Matrix +RotationMatrix +ScaleMatrix +MoveMatrix +PerspectiveMatrix +createFile +List +List +Hashtable +CmdArgument +LoadSid +LoadPicture +LoadBinary +asmCommandSize \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.java new file mode 100644 index 00000000..712a040a --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.kickass; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for KICKASS. + * + * @author Peter Dell + */ +public final class KickAssCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public KickAssCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new KickAssCompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new KickAssCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.txt b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.txt new file mode 100644 index 00000000..803a8b0c --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.txt @@ -0,0 +1,52 @@ + if (title.equals(name)) { + int index = name.indexOf("_"); + String opcode; + String addressing; + if (index >= 0) { + opcode = name.substring(0, index); + + addressing = name.substring(index + 1); + } else { + index=name.length(); + opcode = name; + addressing = ""; + } + + StringBuilder result = new StringBuilder("Opcode "); + for (int i = 0; i < index; i++) { + result.append("_"); + result.append(opcode.charAt(i)); + } + if (addressing.equals("ABS")) { + result.append(" _a_b_solute"); + } else if (addressing.equals("ABSX")) { + result.append(" _a_b_solute,_x"); + } else if (addressing.equals("ABSY")) { + result.append(" _a_b_solute,_y"); + } else if (addressing.equals("IMM")) { + result.append(" #_i_m_mediate"); + } else if (addressing.equals("IZPX")) { + result.append(" (_indirect _zero_page,_x)"); + } else if (addressing.equals("IZPY")) { + result.append(" (_indirect _zero_page),_y"); + } else if (addressing.equals("ZP")) { + result.append(" _zero_page"); + } else if (addressing.equals("ZPX")) { + result.append(" _zero_page,_x"); + } else if (addressing.equals("ZPY")) { + result.append(" _zero_page,_y"); + }else if (addressing.equals("REL")) { + result.append(" _r_e_lative"); + } else if (addressing.equals("IND")) { + result.append(" (_i_n_direct)"); + } else if (addressing.equals("")) { + } else { + throw new RuntimeException( + "Unknown addressing mode " + addressing); + } + title = result.toString(); + } + System.out.println(" "); + } \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.xml new file mode 100644 index 00000000..8e82a2fc --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompiler.xml @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompilerProcessLogParser.java new file mode 100644 index 00000000..4a2e8f6a --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompilerProcessLogParser.java @@ -0,0 +1,118 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.kickass; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link KickAssCompiler}. + * + * Sample error message: + * + *
+ * Error: Unknown symbol 'unknownLabel'
+ * at line 9, column 6 in C:\Users\D025328\Documents\Eclipse\workspace.jac\com.wudsn.ide.ref\ASM\C64\KICKASS\KICKASS-Error-Reference.asm
+ * 
+ * + * @author Peter Dell + */ +final class KickAssCompilerProcessLogParser extends CompilerProcessLogParser { + + private BufferedReader bufferedReader; + + @Override + protected void initialize() { + bufferedReader = new BufferedReader(new StringReader(outputLog)); + } + + @Override + protected void findNextMarker() { + + String line; + line = ""; + + while (line != null && !markerAvailable) { + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + String pattern; + int index; + pattern = "Error: "; + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(pattern); + if (index < 0) { + pattern = "Warning: "; + severity = IMarker.SEVERITY_WARNING; + index = line.indexOf(pattern); + } + if (index == 0) { + + message = line.substring(index + pattern.length()).trim(); + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + index = line.indexOf(','); + if (index > 8) { + + String lineNumberString = line.substring(8, + index); + + try { + lineNumber = Integer.parseInt(lineNumberString); + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } else { + lineNumber = -1; + } + index = line.indexOf(" in "); + if (index > 0) { + filePath = line.substring(index + 4); + } + } + markerAvailable = true; + } + + } + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompilerSourceParser.java new file mode 100644 index 00000000..b4e8b83f --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/kickass/KickAssCompilerSourceParser.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.kickass; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link KickAssCompiler}. + * + * @author Peter Dell + */ +final class KickAssCompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + int symbolLength = symbol.length(); + if (symbolLength > 0) { + + if (symbol.charAt(symbolLength - 1) == ':') { + symbol = symbol.substring(0, symbolLength - 1); + } + if (instruction.equals("=")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + + } + + } // Symbol not empty + + // TODO Make .VAR an own type of instruction. Same code as in + // MadsCompilerSourceParser! + if (instruction.equals(".var")) { + operand = operand.trim(); + int index = operand.indexOf('='); + String variable; + String value; + if (index < 0) { + variable = operand; + value = ""; + } else { + variable = operand.substring(0, index).trim(); + value = operand.substring(index).trim(); + if (value.startsWith("=")) { + value = value.substring(1).trim(); + } + } + if (value.length() > 0) { + createEquateDefinitionChild(startOffset, startOffset + instructionOffset, variable, value, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + instructionOffset, variable, comment); + } + } + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompiler.java new file mode 100644 index 00000000..e13a55f7 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.mads; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for MADS. + * + * @author Peter Dell + */ +public final class MadsCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public MadsCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new MadsCompilerSourceParser(); + } + + @Override + public final CompilerProcessLogParser createLogParser() { + + return new MadsCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompiler.xml new file mode 100644 index 00000000..ef47be91 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompiler.xml @@ -0,0 +1,909 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompilerProcessLogParser.java new file mode 100644 index 00000000..7f8ed397 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompilerProcessLogParser.java @@ -0,0 +1,228 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.mads; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; +import com.wudsn.ide.asm.compiler.CompilerSymbolType; +import com.wudsn.ide.base.common.FileUtility; + +/** + * Process log parser for {@link MadsCompiler}. + * + * @author Peter Dell + */ +final class MadsCompilerProcessLogParser extends CompilerProcessLogParser { + + private BufferedReader bufferedReader; + + @Override + protected void initialize() { + bufferedReader = new BufferedReader(new StringReader(outputLog)); + } + + @Override + protected void findNextMarker() { + + String line; + line = ""; + + while (line != null && !markerAvailable) { + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + String pattern; + int index; + pattern = ") ERROR: "; + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(pattern); + if (index < 2) { + pattern = ") WARNING: "; + severity = IMarker.SEVERITY_WARNING; + index = line.indexOf(pattern); + } + if (index > 2) { + + int i = index - 2; + while (line.charAt(i) != '(' && i >= 0) { + i--; + } + + if (line.charAt(i) == '(') { + String lineNumberString = line.substring(i + 1, index); + + try { + lineNumber = Integer.parseInt(lineNumberString); + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } else { + lineNumber = -1; + } + message = line.substring(index + pattern.length()).trim(); + + filePath = line.substring(0, i - 1); + markerAvailable = true; + } + + } + } + } + + /** + *
+     * From the MADS documentation
+     * 
+     * .LAB file format
+     * 
+     * As with XASM, the *.LAB file stores information about labels in the program. There are three columns:
+     * • The first column is the virtual bank number assigned to the label (if <>0).
+     * • The second column is the label value.
+     * • The third column is the label name.
+     * 
+     * Virtual bank numbers with values >= $FFF9 have special meanings:
+     * • $FFF9   label for parameter in procedure defined by .PROC
+     * • $FFFA   label for array defined by .ARRAY
+     * • $FFFB   label for structured data defined by the pseudo-command DTA STRUCT_LABEL
+     * • $FFFC   label for SpartaDOS X symbol defined by SMB
+     * • $FFFD   label for macro defined by .MACRO directive
+     * • $FFFE   label for structure defined by .STRUCT directive
+     * • $FFFF   label for procedure defined by .PROC directive
+     * 
+     * 
+     * Characters with special meanings in label names: 
+     * • label with two colons :: is defined in a macro
+     * • a dot ('.') separates the name of a scope (.MACRO, .PROC, .LOCAL, .STRUCT) from the field name in the scope
+     * 
+     * The numeric value after :: is the number of the macro call. This is an example file:
+     * 
+     * Mad-Assembler v1.4.2beta by TeBe/Madteam
+     * Label table:
+     * 00	0400	@STACK_ADDRESS
+     * 00	00FF	@STACK_POINTER
+     * 00	2000	MAIN
+     * 00	2019	LOOP
+     * 00	201C	LOOP::1
+     * 00	201C	LHEX
+     * 00	0080	LHEX.HLP
+     * 00	204C	LHEX.THEX
+     * 00	205C	HEX
+     * 00	205C	HEX.@GETPAR0.LOOP
+     * 00	2079	HEX.@GETPAR1.LOOP
+     * 
+ */ + @Override + // TODO Parser symbol type and add source tree object type for .DEFs + public void addCompilerSymbols(List compilerSymbols) throws CoreException { + if (compilerSymbols == null) { + throw new IllegalArgumentException("Parameter 'compilerSymbols' must not be null."); + } + String labelsFilePath = files.outputFilePathWithoutExtension + ".lab"; + File labelsFile = new File(labelsFilePath); + if (labelsFile.exists()) { + + String labelsFileContent = FileUtility.readString(labelsFile, FileUtility.MAX_SIZE_UNLIMITED); + String[] lines = labelsFileContent.split("[\\r\\n]+"); + if (lines.length > 2 || lines[0].toLowerCase().startsWith("mads") + || lines[1].toLowerCase().startsWith("label table:")) { + for (int i = 2; i < lines.length; i++) { + String[] parts = lines[i].split("\\t"); + if (parts.length == 3) { + int type = CompilerSymbolType.LABEL_DEFINITION; + String bankString = parts[0]; + + String name = parts[2]; + String valueString = parts[1]; + try { + long bank = Long.parseLong(bankString, 16); + int symbolBank; + if (bank >= 0 && bank < 0xfff9) { + symbolBank = (int) bank; + } else { + symbolBank = CompilerSymbol.UNDEFINED_BANK; + if (bank == 0xfff9) { + // Label for parameter in procedure defined + // by .PROC + // TODO: This would actually be a separate + // type + type = CompilerSymbolType.PROCEDURE_DEFINITION_SECTION; + } else if (bank == 0xfffa) { + // Label for array defined by .ARRAY + // TODO: This would actually be a separate + // type + type = CompilerSymbolType.LABEL_DEFINITION; + } else if (bank == 0xfffb) { + // Label for structured data defined by the + // pseudo-command DTA STRUCT_LABEL + type = CompilerSymbolType.STRUCTURE_DEFINITION_SECTION; + } else if (bank == 0xfffc) { + // Label for SpartaDOS X symbol defined by + // SMB + // TODO: This would actually be a separate + // type + type = CompilerSymbolType.LABEL_DEFINITION; + } else if (bank == 0xfffd) { + // Label for macro defined by .MACRO + // directive + // TODO: This would actually be a separate + // type + type = CompilerSymbolType.MACRO_DEFINITION_SECTION; + } else if (bank == 0xfffe) { + // Label for structure defined by .STRUCT + // directive + type = CompilerSymbolType.STRUCTURE_DEFINITION_SECTION; + } else if (bank == 0xffff) { + // Label for procedure defined by .PROC + // directive + type = CompilerSymbolType.PROCEDURE_DEFINITION_SECTION; + } + } + long value = Long.parseLong(valueString, 16); + CompilerSymbol compilerSymbol = CompilerSymbol.createNumberSymbol(type, name, symbolBank, + value); + compilerSymbols.add(compilerSymbol); + } catch (NumberFormatException ex) { + AssemblerPlugin.getInstance().logError("Cannot parse value {1} of symbol {0}.", + new Object[] { name, valueString }, ex); + } + + } + } + } + + } + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompilerSourceParser.java new file mode 100644 index 00000000..21132606 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/mads/MadsCompilerSourceParser.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.mads; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link MadsCompiler}. + * + * @author Peter Dell + */ +final class MadsCompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + if (instruction.equals("=") || instruction.equals("EQU")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + } + + } // Symbol not empty + + // TODO Make .VAR an own type of instruction + if (instruction.equals(".VAR") || instruction.equals(".ZPVAR")) { + operand = operand.trim(); + int index = operand.indexOf('='); + String variable; + String value; + if (index < 0) { + variable = operand; + value = ""; + } else { + variable = operand.substring(0, index).trim(); + value = operand.substring(index).trim(); + if (value.startsWith("=")) { + value = value.substring(1).trim(); + } + } + if (value.length() > 0) { + createEquateDefinitionChild(startOffset, startOffset + instructionOffset, variable, value, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + instructionOffset, variable, comment); + } + + } + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32Compiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32Compiler.java new file mode 100644 index 00000000..2084fc90 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32Compiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2016 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.merlin32; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for MERLIN32. + * + * @author Peter Dell + */ +public final class Merlin32Compiler extends Compiler { + + /** + * Creates a new instance. + */ + public Merlin32Compiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new Merlin32CompilerSourceParser(); + } + + @Override + public final CompilerProcessLogParser createLogParser() { + + return new Merlin32CompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32Compiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32Compiler.xml new file mode 100644 index 00000000..6bb205de --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32Compiler.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32CompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32CompilerProcessLogParser.java new file mode 100644 index 00000000..5baabfe4 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32CompilerProcessLogParser.java @@ -0,0 +1,180 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.merlin32; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link Merlin32Compiler}. + * + * @author Peter Dell + */ +final class Merlin32CompilerProcessLogParser extends CompilerProcessLogParser { + + private BufferedReader bufferedReader; + + @Override + protected void initialize() { + bufferedReader = new BufferedReader(new StringReader(outputLog)); + } + + @Override + protected void findNextMarker() { + + String line; + line = ""; + + while (line != null && !markerAvailable) { + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + String pattern; + int index; + pattern = ") ERROR: "; + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(pattern); + if (index < 2) { + pattern = ") WARNING: "; + severity = IMarker.SEVERITY_WARNING; + index = line.indexOf(pattern); + } + if (index > 2) { + + int i = index - 2; + while (line.charAt(i) != '(' && i >= 0) { + i--; + } + + if (line.charAt(i) == '(') { + String lineNumberString = line.substring(i + 1, index); + + try { + lineNumber = Integer.parseInt(lineNumberString); + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } else { + lineNumber = -1; + } + message = line.substring(index + pattern.length()).trim(); + + filePath = line.substring(0, i - 1); + markerAvailable = true; + } + + } + } + } + @Override + public void addCompilerSymbols(List compilerSymbols) throws CoreException { + if (compilerSymbols == null) { + throw new IllegalArgumentException("Parameter 'compilerSymbols' must not be null."); + } +// String labelsFilePath = files.outputFilePathWithoutExtension + ".lab"; +// File labelsFile = new File(labelsFilePath); +// if (labelsFile.exists()) { +// +// String labelsFileContent = FileUtility.readString(labelsFile, FileUtility.MAX_SIZE_UNLIMITED); +// String[] lines = labelsFileContent.split("[\\r\\n]+"); +// if (lines.length > 2 || lines[0].toLowerCase().startsWith("mads") +// || lines[1].toLowerCase().startsWith("label table:")) { +// for (int i = 2; i < lines.length; i++) { +// String[] parts = lines[i].split("\\t"); +// if (parts.length == 3) { +// int type = CompilerSymbolType.LABEL_DEFINITION; +// String bankString = parts[0]; +// +// String name = parts[2]; +// String valueString = parts[1]; +// try { +// long bank = Long.parseLong(bankString, 16); +// int symbolBank; +// if (bank >= 0 && bank < 0xfff9) { +// symbolBank = (int) bank; +// } else { +// symbolBank = CompilerSymbol.UNDEFINED_BANK; +// if (bank == 0xfff9) { +// // Label for parameter in procedure defined +// // by .PROC +// // TODO: This would actually be a separate +// // type +// type = CompilerSymbolType.PROCEDURE_DEFINITION_SECTION; +// } else if (bank == 0xfffa) { +// // Label for array defined by .ARRAY +// // TODO: This would actually be a separate +// // type +// type = CompilerSymbolType.LABEL_DEFINITION; +// } else if (bank == 0xfffb) { +// // Label for structured data defined by the +// // pseudo-command DTA STRUCT_LABEL +// type = CompilerSymbolType.STRUCTURE_DEFINITION_SECTION; +// } else if (bank == 0xfffc) { +// // Label for SpartaDOS X symbol defined by +// // SMB +// // TODO: This would actually be a separate +// // type +// type = CompilerSymbolType.LABEL_DEFINITION; +// } else if (bank == 0xfffd) { +// // Label for macro defined by .MACRO +// // directive +// // TODO: This would actually be a separate +// // type +// type = CompilerSymbolType.MACRO_DEFINITION_SECTION; +// } else if (bank == 0xfffe) { +// // Label for structure defined by .STRUCT +// // directive +// type = CompilerSymbolType.STRUCTURE_DEFINITION_SECTION; +// } else if (bank == 0xffff) { +// // Label for procedure defined by .PROC +// // directive +// type = CompilerSymbolType.PROCEDURE_DEFINITION_SECTION; +// } +// } +// long value = Long.parseLong(valueString, 16); +// CompilerSymbol compilerSymbol = CompilerSymbol.createNumberSymbol(type, name, symbolBank, +// value); +// compilerSymbols.add(compilerSymbol); +// } catch (NumberFormatException ex) { +// AssemblerPlugin.getInstance().logError("Cannot parse value {1} of symbol {0}.", +// new Object[] { name, valueString }, ex); +// } +// +// } +// } +// } +// +// } + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32CompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32CompilerSourceParser.java new file mode 100644 index 00000000..7e00fd21 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/merlin32/Merlin32CompilerSourceParser.java @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.merlin32; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link Merlin32Compiler}. + * + * @author Peter Dell + */ +final class Merlin32CompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + if (instruction.equals("=") || instruction.equals("EQU")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + } + + } // Symbol not empty + + // TODO Implement macro and Lup handling +// if (instruction.equals("<<<<")) { +// endMacroDefinition(); +// } else if (instruction.equals(">>>>")) { +// // Same as "PMC" +// } else if (instruction.equals("--^")) { +// endRepeatSection(); +// } + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompiler.java new file mode 100644 index 00000000..97f95609 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompiler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.tass; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for ATASM. + * + * @author Peter Dell + */ +public final class TassCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public TassCompiler() { + } + + @Override + public CompilerSourceParser createSourceParser() { + return new TassCompilerSourceParser(); + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new TassCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompiler.xml new file mode 100644 index 00000000..6d4188ad --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompiler.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompilerProcessLogParser.java new file mode 100644 index 00000000..cac2205c --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompilerProcessLogParser.java @@ -0,0 +1,171 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.tass; + +import java.util.List; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link TassCompiler}. + * + * + * TODO Implement TASS Support + * + * @author Peter Dell + */ +final class TassCompilerProcessLogParser extends CompilerProcessLogParser { + + private Pattern pattern; + private String sourceFilePattern; + + @Override + protected void initialize() { + pattern = Pattern.compile("In .* line "); + sourceFilePattern = "In " + mainSourceFilePath + ", line "; + } + + @Override + protected void findNextMarker() { + + int index; + String line; + boolean include; + String includeFile; + Matcher matcher = pattern.matcher(errorLog); + + if (matcher.find()) { + index = matcher.start(); + line = errorLog.substring(index); + if (line.startsWith(sourceFilePattern)) { + include = false; + includeFile = ""; + } else { + include = true; + includeFile = line.substring(3, matcher.end() - matcher.start() + - 7); + } + index = matcher.end(); + String lineNumberLine = errorLog.substring(index); + errorLog = lineNumberLine; + int numberEndIndex = lineNumberLine.indexOf("--"); + if (numberEndIndex > 0) { + String lineNumberString; + lineNumberString = lineNumberLine.substring(0, numberEndIndex); + + try { + lineNumber = Integer.parseInt(lineNumberString); + int nextIndex = lineNumberLine.indexOf("\n"); + if (index > 0) { + message = lineNumberLine.substring(nextIndex + 1); + int nextIndex2 = message.indexOf("\n"); + if (nextIndex > 0) { + message = message.substring(0, nextIndex2 - 1); + } + message = message.trim(); + } + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } + + if (message.startsWith("Error:")) { + severity = IMarker.SEVERITY_ERROR; + message = message.substring(6); + } else if (message.startsWith("Warning:")) { + severity = IMarker.SEVERITY_WARNING; + message = message.substring(8); + } + + if (include) { + if (lineNumber >= 0) { + message = includeFile + " line " + lineNumber + ": " + + message; + } else { + message = includeFile + ": " + message; + } + lineNumber = -1; + } + message = message.trim(); + + // Message mapping. + if (severity == IMarker.SEVERITY_WARNING + && message.startsWith("Using bank")) { + severity = IMarker.SEVERITY_INFO; + } + markerAvailable = true; + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + final String EQUATES = "Equates:"; + final String SYMBOL = "Symbol"; + final String TABLE = "table:"; + + String log; + int index; + + log = outputLog; + index = log.indexOf(EQUATES); + if (index >= 0) { + log = log.substring(index + EQUATES.length()); + + StringTokenizer st = new StringTokenizer(log); + String token; + String name; + String hexValue; + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (token.equals(SYMBOL)) { + break; + } + name = token.substring(0, token.length() - 1); + hexValue = st.nextToken(); // Must be there + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + + if (st.hasMoreTokens()) { + token = st.nextToken(); + if (token.equals(TABLE)) { + while (st.hasMoreTokens()) { + token = st.nextToken(); + if (!token.endsWith(":")) { + break; + } + name = token.substring(0, token.length() - 1); + hexValue = st.nextToken(); // Must be there + compilerSymbols.add(CompilerSymbol.createNumberHexSymbol(name, hexValue)); + } + } + } + } + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompilerSourceParser.java new file mode 100644 index 00000000..94188246 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/tass/TassCompilerSourceParser.java @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.tass; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link TassCompiler}. + * + * @author Peter Dell + */ +final class TassCompilerSourceParser extends CompilerSourceParser { + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + + if (symbol.length() > 0) { + + // Check for origin statement + if (symbol.equals("*")) { + beginImplementationSection(startOffset, startOffset + symbolOffset, operand, comment); + + } else { + if (instruction.equals("=")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + + } + } + + } // Symbol not empty + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompiler.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompiler.java new file mode 100644 index 00000000..b6eac70e --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompiler.java @@ -0,0 +1,58 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.xasm; + +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Compiler class for XASM. + * + * @author Peter Dell + */ +public final class XasmCompiler extends Compiler { + + /** + * Creates a new instance. + */ + public XasmCompiler() { + + } + + @Override + public CompilerSourceParser createSourceParser() { + return new XasmCompilerSourceParser(); + } + + @Override + public boolean isSuccessExitValue(int exitValue) { + // XASM returns 0 if there is no warning, 1 if there are only warnings, + // 2 if there is at least one error. + return exitValue == 0 || exitValue == 1; + } + + @Override + public CompilerProcessLogParser createLogParser() { + + return new XasmCompilerProcessLogParser(); + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompiler.xml b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompiler.xml new file mode 100644 index 00000000..14cb8415 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompiler.xml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompilerProcessLogParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompilerProcessLogParser.java new file mode 100644 index 00000000..fec3ba1b --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompilerProcessLogParser.java @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.xasm; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerSymbol; + +/** + * Process log parser for {@link XasmCompiler}. Identical to + * MadsCompilerProcessLogParser, except that it is based on the errorLog. + * + * @author Peter Dell + */ +final class XasmCompilerProcessLogParser extends CompilerProcessLogParser { + + private BufferedReader bufferedReader; + + @Override + protected void initialize() { + bufferedReader = new BufferedReader(new StringReader(errorLog)); + } + + @Override + protected void findNextMarker() { + + String line; + line = ""; + + while (line != null && !markerAvailable) { + try { + line = bufferedReader.readLine(); + } catch (IOException ex) { + throw new RuntimeException("Cannot read line", ex); + } + if (line != null) { + String pattern; + int index; + pattern = ") ERROR: "; + severity = IMarker.SEVERITY_ERROR; + index = line.indexOf(pattern); + if (index < 2) { + pattern = ") WARNING: "; + severity = IMarker.SEVERITY_WARNING; + index = line.indexOf(pattern); + } + if (index > 2) { + + int i = index - 2; + while (line.charAt(i) != '(' && i >= 0) { + i--; + } + + if (line.charAt(i) == '(') { + String lineNumberString = line.substring(i + 1, index); + + try { + lineNumber = Integer.parseInt(lineNumberString); + } catch (NumberFormatException ex) { + lineNumber = -1; + severity = IMarker.SEVERITY_ERROR; + message = ex.getMessage(); + } + } else { + lineNumber = -1; + } + message = line.substring(index + pattern.length()).trim(); + filePath = line.substring(0, i - 1); + markerAvailable = true; + } + + } + } + } + + @Override + public void addCompilerSymbols(List compilerSymbols) { + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompilerSourceParser.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompilerSourceParser.java new file mode 100644 index 00000000..1aacc209 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/compiler/xasm/XasmCompilerSourceParser.java @@ -0,0 +1,53 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.xasm; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Source parser for {@link XasmCompiler}. + * + * @author Peter Dell + */ +final class XasmCompilerSourceParser extends CompilerSourceParser { + + /** + * Creation is package local. + */ + XasmCompilerSourceParser() { + + } + + @Override + protected void parseLine(int startOffset, String symbol, int symbolOffset, String instruction, + int instructionOffset, String operand, String comment) { + if (symbol.length() > 0) { + + if (instruction.equals("EQU")) { + createEquateDefinitionChild(startOffset, startOffset + symbolOffset, symbol, operand, comment); + + } else { + createLabelDefinitionChild(startOffset, startOffset + symbolOffset, symbol, comment); + } + + } // Symbol not empty + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/acme/AcmeEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/acme/AcmeEditor.java new file mode 100644 index 00000000..7e03eab7 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/acme/AcmeEditor.java @@ -0,0 +1,37 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.acme; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class AcmeEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public AcmeEditor() { + } + + @Override + public String getCompilerId() { + return CompilerId.ACME; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/asm6/Asm6Editor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/asm6/Asm6Editor.java new file mode 100644 index 00000000..525506ea --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/asm6/Asm6Editor.java @@ -0,0 +1,37 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.asm6; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class Asm6Editor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public Asm6Editor() { + } + + @Override + public String getCompilerId() { + return CompilerId.ASM6; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/atasm/AtasmEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/atasm/AtasmEditor.java new file mode 100644 index 00000000..d89fb2b5 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/atasm/AtasmEditor.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.atasm; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class AtasmEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public AtasmEditor() { + } + + + @Override + public String getCompilerId() { + return CompilerId.ATASM; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/dasm/DasmEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/dasm/DasmEditor.java new file mode 100644 index 00000000..de597fc6 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/dasm/DasmEditor.java @@ -0,0 +1,37 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.dasm; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class DasmEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public DasmEditor() { + } + + @Override + public String getCompilerId() { + return CompilerId.DASM; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/kickass/KickAssEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/kickass/KickAssEditor.java new file mode 100644 index 00000000..f5edf71c --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/kickass/KickAssEditor.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.kickass; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class KickAssEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public KickAssEditor() { + + } + + @Override + public String getCompilerId() { + return CompilerId.KICKASS; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/mads/MadsEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/mads/MadsEditor.java new file mode 100644 index 00000000..006cfa3a --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/mads/MadsEditor.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.mads; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class MadsEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public MadsEditor() { + + } + + @Override + public String getCompilerId() { + return CompilerId.MADS; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/merlin32/Merlin32Editor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/merlin32/Merlin32Editor.java new file mode 100644 index 00000000..5b693050 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/merlin32/Merlin32Editor.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.merlin32; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class Merlin32Editor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public Merlin32Editor() { + + } + + @Override + public String getCompilerId() { + return CompilerId.MERLIN32; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/tass/TassEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/tass/TassEditor.java new file mode 100644 index 00000000..9a12756d --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/tass/TassEditor.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.tass; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class TassEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public TassEditor() { + + } + + @Override + public String getCompilerId() { + return CompilerId.TASS; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/xasm/XasmEditor.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/xasm/XasmEditor.java new file mode 100644 index 00000000..1e713dfe --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/editor/xasm/XasmEditor.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.editor.xasm; + +import com.wudsn.ide.asm.compiler.CompilerId; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +public final class XasmEditor extends AssemblerEditor { + + /** + * Creation is public. Called by the extension "org.eclipse.ui.editors". + */ + public XasmEditor() { + + } + + @Override + public String getCompilerId() { + return CompilerId.XASM; + } +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesApple2CompilersPage.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesApple2CompilersPage.java new file mode 100644 index 00000000..b57f5e55 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesApple2CompilersPage.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding Apple 2 compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesApple2CompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesApple2CompilersPage() { + super(Hardware.APPLE2); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari2600CompilersPage.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari2600CompilersPage.java new file mode 100644 index 00000000..df0a6dcb --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari2600CompilersPage.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding Apple 2 compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesAtari2600CompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesAtari2600CompilersPage() { + super(Hardware.ATARI2600); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari7800CompilersPage.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari7800CompilersPage.java new file mode 100644 index 00000000..190d8f7e --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari7800CompilersPage.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding Apple 2 compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesAtari7800CompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesAtari7800CompilersPage() { + super(Hardware.ATARI7800); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari8CompilersPage.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari8CompilersPage.java new file mode 100644 index 00000000..9d207e4b --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesAtari8CompilersPage.java @@ -0,0 +1,43 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding Atari 8-bit + * compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesAtari8CompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesAtari8CompilersPage() { + super(Hardware.ATARI8BIT); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesC64CompilersPage.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesC64CompilersPage.java new file mode 100644 index 00000000..71396673 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesC64CompilersPage.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding C64 compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesC64CompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesC64CompilersPage() { + super(Hardware.C64); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesNESCompilersPage.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesNESCompilersPage.java new file mode 100644 index 00000000..7cf380d8 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesNESCompilersPage.java @@ -0,0 +1,42 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesCompilersPage; + +/** + * Visual editor page for the assembler preferences regarding NES compilers. + * + * @author Peter Dell + * + */ +public final class AssemblerPreferencesNESCompilersPage extends + AssemblerPreferencesCompilersPage { + + /** + * Create is public. Used by extension point + * "org.eclipse.ui.preferencePages". + */ + public AssemblerPreferencesNESCompilersPage() { + super(Hardware.NES); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/runner/atari8/Altirra.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/runner/atari8/Altirra.java new file mode 100644 index 00000000..fbcf910b --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/runner/atari8/Altirra.java @@ -0,0 +1,78 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.runner.atari8; + +import java.io.File; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IBreakpoint; + +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.asm.editor.AssemblerBreakpoint; +import com.wudsn.ide.asm.runner.Runner; + +/** + * Runner for Altirra 2.0 and above which support source line breakpoints. + * + * @author Peter Dell + * @since 1.6.1 + */ +public final class Altirra extends Runner { + + @Override + public File createBreakpointsFile(CompilerFiles files) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + return new File(files.outputFilePathWithoutExtension + ".atdbg"); + } + + @Override + public int createBreakpointsFileContent(AssemblerBreakpoint[] breakpoints, StringBuilder breakpointBuilder) { + if (breakpoints == null) { + throw new IllegalArgumentException("Parameter 'breakpoints' must not be null."); + } + int activeBreakpoints = 0; + breakpointBuilder.append(".sourcemode on\n"); + breakpointBuilder.append(".echo\n"); + breakpointBuilder.append(".echo \"Loading executable...\"\n"); + breakpointBuilder.append(".echo\n"); + breakpointBuilder.append("bc *\n"); + breakpointBuilder.append(".onexerun .echo \"Launching executable...\"\n"); + for (IBreakpoint breakpoint : breakpoints) { + try { + if (breakpoint.isEnabled()) { + AssemblerBreakpoint assemberBreakpoint = (AssemblerBreakpoint) breakpoint; + IMarker marker = breakpoint.getMarker(); + String sourceFilePath = marker.getResource().getLocation().toOSString(); + breakpointBuilder + .append("bp \"`" + sourceFilePath + ":" + assemberBreakpoint.getLineNumber() + "`\"\n"); + activeBreakpoints++; + } + } catch (CoreException ex) { + throw new RuntimeException(ex); + } + + } + return activeBreakpoints; + } + +} diff --git a/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/runner/atari8/Atari800Win.java b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/runner/atari8/Atari800Win.java new file mode 100644 index 00000000..690b0c71 --- /dev/null +++ b/com.wudsn.ide.asm.compilers/src/com/wudsn/ide/asm/runner/atari8/Atari800Win.java @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.runner.atari8; + +import com.wudsn.ide.asm.runner.Runner; + +/** + * Runner for Atar800Win. + * + * @author Peter Dell + * @since 1.7.0. + */ +public final class Atari800Win extends Runner { + +} diff --git a/com.wudsn.ide.asm/.classpath b/com.wudsn.ide.asm/.classpath new file mode 100644 index 00000000..8a8f1668 --- /dev/null +++ b/com.wudsn.ide.asm/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/com.wudsn.ide.asm/.project b/com.wudsn.ide.asm/.project new file mode 100644 index 00000000..b53cba16 --- /dev/null +++ b/com.wudsn.ide.asm/.project @@ -0,0 +1,28 @@ + + + com.wudsn.ide.asm + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/com.wudsn.ide.asm/.settings/org.eclipse.core.resources.prefs b/com.wudsn.ide.asm/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..e0a59bd1 --- /dev/null +++ b/com.wudsn.ide.asm/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +#Thu Jun 28 19:08:33 CEST 2012 +eclipse.preferences.version=1 +encoding//help/create-links.bat=ISO-8859-1 +encoding/help=UTF-8 diff --git a/com.wudsn.ide.asm/.settings/org.eclipse.jdt.core.prefs b/com.wudsn.ide.asm/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..54e493c0 --- /dev/null +++ b/com.wudsn.ide.asm/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/com.wudsn.ide.asm/.settings/org.eclipse.jdt.ui.prefs b/com.wudsn.ide.asm/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..9985b1d5 --- /dev/null +++ b/com.wudsn.ide.asm/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,4 @@ +#Wed Feb 08 19:13:41 CET 2006 +eclipse.preferences.version=1 +internal.default.compliance=default +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/com.wudsn.ide.asm/META-INF/MANIFEST.MF b/com.wudsn.ide.asm/META-INF/MANIFEST.MF new file mode 100644 index 00000000..0ce7ce4d --- /dev/null +++ b/com.wudsn.ide.asm/META-INF/MANIFEST.MF @@ -0,0 +1,81 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: WUDSN IDE Assembler Plug-in +Bundle-SymbolicName: com.wudsn.ide.asm;singleton:=true +Bundle-Version: 1.7.0.qualifier +Bundle-Activator: com.wudsn.ide.asm.AssemblerPlugin +Bundle-Localization: plugin +Require-Bundle: org.eclipse.core.runtime;visibility:=reexport, + org.eclipse.core.resources;visibility:=reexport, + org.eclipse.core.filesystem, + org.eclipse.jface.text;visibility:=reexport, + org.eclipse.ui;visibility:=reexport, + org.eclipse.ui.console, + org.eclipse.ui.editors;visibility:=reexport, + org.eclipse.ui.ide, + org.eclipse.ui.workbench.texteditor, + org.eclipse.ui.views, + org.eclipse.e4.ui.di;bundle-version="1.0.0", + org.eclipse.e4.ui.model.workbench;bundle-version="1.0.1", + org.eclipse.help, + com.wudsn.ide.base;visibility:=reexport, + com.wudsn.ide.dsk, + com.wudsn.ide.gfx, + org.eclipse.debug.core;visibility:=reexport, + org.eclipse.debug.ui;visibility:=reexport, + org.eclipse.ui.workbench +Bundle-ActivationPolicy: lazy +Bundle-Vendor: Peter Dell +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Export-Package: com.wudsn.ide.asm; + uses:="org.eclipse.osgi.util, + org.eclipse.core.runtime, + com.wudsn.ide.base.common, + org.osgi.framework, + com.wudsn.ide.asm.runner, + com.wudsn.ide.asm.preferences, + com.wudsn.ide.asm.compiler", + com.wudsn.ide.asm.compiler; + uses:="org.eclipse.jface.text, + org.eclipse.ui.console, + org.eclipse.jface.viewers, + com.wudsn.ide.asm, + org.eclipse.jface.text.rules", + com.wudsn.ide.asm.compiler.parser, + com.wudsn.ide.asm.editor; + uses:="org.eclipse.jface.text, + org.eclipse.core.runtime, + org.eclipse.core.commands, + org.eclipse.jface.text.rules, + org.eclipse.debug.core.model, + org.eclipse.jface.action, + org.eclipse.ui.views.contentoutline, + org.eclipse.core.resources, + org.eclipse.jface.viewers, + com.wudsn.ide.asm, + com.wudsn.ide.asm.compiler, + com.wudsn.ide.asm.preferences, + org.eclipse.jface.text.hyperlink, + org.eclipse.ui.texteditor, + org.eclipse.jface.text.reconciler, + org.eclipse.jface.text.source, + org.eclipse.jface.resource, + org.eclipse.ui, + org.eclipse.jface.text.contentassist, + org.eclipse.debug.ui.actions, + org.eclipse.jface.preference, + org.eclipse.jface.text.presentation, + org.eclipse.swt.graphics, + org.eclipse.jface.util, + org.eclipse.swt.widgets, + org.eclipse.ui.editors.text", + com.wudsn.ide.asm.preferences; + uses:="org.eclipse.jface.text, + org.eclipse.jface.preference, + org.eclipse.ui, + org.eclipse.swt.graphics, + org.eclipse.jface.viewers, + com.wudsn.ide.asm, + org.eclipse.core.runtime.preferences, + org.eclipse.swt.widgets", + com.wudsn.ide.asm.runner;uses:="com.wudsn.ide.asm" diff --git a/com.wudsn.ide.asm/WUDSN-IDE (de).launch b/com.wudsn.ide.asm/WUDSN-IDE (de).launch new file mode 100644 index 00000000..481beba3 --- /dev/null +++ b/com.wudsn.ide.asm/WUDSN-IDE (de).launch @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm/WUDSN-IDE (pl).launch b/com.wudsn.ide.asm/WUDSN-IDE (pl).launch new file mode 100644 index 00000000..a6360938 --- /dev/null +++ b/com.wudsn.ide.asm/WUDSN-IDE (pl).launch @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm/WUDSN-IDE.launch b/com.wudsn.ide.asm/WUDSN-IDE.launch new file mode 100644 index 00000000..66779f82 --- /dev/null +++ b/com.wudsn.ide.asm/WUDSN-IDE.launch @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm/bin/.gitignore b/com.wudsn.ide.asm/bin/.gitignore new file mode 100644 index 00000000..43e58b99 --- /dev/null +++ b/com.wudsn.ide.asm/bin/.gitignore @@ -0,0 +1 @@ +/com diff --git a/com.wudsn.ide.asm/build.properties b/com.wudsn.ide.asm/build.properties new file mode 100644 index 00000000..21624a63 --- /dev/null +++ b/com.wudsn.ide.asm/build.properties @@ -0,0 +1,16 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + plugin.xml,\ + plugin.properties,\ + icons/,\ + help/,\ + src/,\ + plugin_de_DE.properties,\ + build.properties,\ + .classpath,\ + .project,\ + schema/ +bin.excludes = help/productions/java/ide/downloads/ + diff --git a/com.wudsn.ide.asm/help/create-links.bat b/com.wudsn.ide.asm/help/create-links.bat new file mode 100644 index 00000000..2711a1d8 --- /dev/null +++ b/com.wudsn.ide.asm/help/create-links.bat @@ -0,0 +1,21 @@ +echo off +rem +rem The image and file resource files for the IDE help are original in the "site\productions\java\ide" folder. +rem The are linked into the "com.wudsn.ide.asm/help/productions/java/ide project" folder via a symbolic link. +rem This way, the Eclipse build uses the latest versions automatically. +rem The HTML files for the online help are original in the com.wudsn.ide.asm/help" folder. +rem They have to be copied into the Joomla using the "export.bat" script when something is changed. +rem +rem Important: Run this script in an Administrator shell. +rem + +echo on +setlocal +set WORKSPACE=C:\jac\system\Java\Programming\Workspaces\WUDSN-IDE +set SITE=C:\jac\system\WWW\Sites\www.wudsn.com +set SYMBOLIC_LINK=%WORKSPACE%\com.wudsn.ide.asm\help\productions\java\ide +set REAL_FOLDER=%SITE%\productions\java\ide +echo on +rmdir %SYMBOLIC_LINK% +mklink /D %SYMBOLIC_LINK% %REAL_FOLDER% +pause diff --git a/com.wudsn.ide.asm/help/ide-credits.section.html b/com.wudsn.ide.asm/help/ide-credits.section.html new file mode 100644 index 00000000..c7aa0923 --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-credits.section.html @@ -0,0 +1,204 @@ + +A project like WUDSN IDE is not possible and not worth anything without the contributions provided by others. So here's +the list of credits of all involved people and related projects. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CompilerContributor
+ ACME + Krzysztof Dabrowski (BruSH/ElysiuM)
+ ATASM + M. Schmelzenbach (schmelze)
+ DASM + Peter H. Froehlich (pfh)
+ KickAss + Mads Nielsen (Slammer)
+ MADS + Tomasz Biela (tebe)
+ XASM + Piotr Fusik (fox)
Linux compilingCarsten Strohmann (cas)
Mac OS X compilingSpookt
Graphic library or toolContributor
+ FAIL + Piotr Fusik (fox)
+ Grafx2 + Adrien Destugues (PulkoMandy), Yves Rizoud (yrizoud)
Sound libraryContributor
+ ASAP + Piotr Fusik (fox)
+ JSIDPLAY2 + Ken + Händel + (kenchis) +
File system libraryContributor
+ AppleCommander + Robert Greene (robgreene)
EmulatorContributor
+ AppleWin (Apple II) + Nick Westgate (sicklittlemonkey)
+ Altirra (Atari 8-bit) + Avery Lee (phaeron)
+ Atari800Win (Atari 8-bit) + Marcin Lewandowski (jaskier)
+ CCS64 (C64) + Håkan Sundell (phs)
+ JACE (Apple II) + Brendan Robert (BLuRry)
+ Stella (Atari VCS) + Stephen Anthony, Bradford Mott, Eckhard Stolberg, Brian Watson
+ Virtu (Apple II) + Sean Fausett (fool)
Eclipse plugin or libraryContributor
+ Eclipse + Eclipse Foundation
+ x86 ASM Plugin + Andy Reek, Daniel Mitte
+ Rhino JavaScript engine + Mozilla Foundation
+ Eclipse Hex Editor Plugin (used as inspiration) + Marcel Palko (randallco)
+ Java Hex Editor & Plugin + Pordi Estaqual (pestatije)
+ Java Expression Language Parser + Aaron Gadberry (aaron)
diff --git a/com.wudsn.ide.asm/help/ide-faq.section.html b/com.wudsn.ide.asm/help/ide-faq.section.html new file mode 100644 index 00000000..f48f1653 --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-faq.section.html @@ -0,0 +1,500 @@ + +Here you can find the answers to some frequently asked questions. If your question is not answered here, please have a look at +the video tutorials or contact me. + +

Installation

+
+

How to I install Eclipse at all?

+

+ If you are not familiar with Eclipse at all, make sure that you have installed the + Eclipse platform distribution + only - without Java or J2EE tools. This distribution is much smaller (normally around 50-70 MB instead of 170 MD) and will not + confuse you with tons of features and buttons you do not need at all. See the section "Installing Eclipse" on the tab + "Installation" for the required steps. If you use Windows, you can use one of the zero installation distributions of WUDSN IDE + which are linked on the tab "Installation". They are simply ".zip" archives that already contain Eclipse and everything else." +

+
+
+

When I try to start Eclipse is get an error like "Failed to load the JNI shared library 'C:\Program Files + (x86)\Java\jre6\bin\client\jvm.dll'". What is wrong? +

+

The Eclipse version and the Java version on your system are not compatible. The Eclipse is not pure Java, but uses platform + specific native libraries to run and debug Java efficiently. For example you must install the 64-bit version Java (JRE or JDK) + if you want to use the 64-bit version of the Eclipse. This is a frequent issue under Windows 7, because be default there is + only the 32-bit version of Java installed. See the section "Installing Eclipse" on the tab "Installation" for the required + steps. +

+
+
+

How to I use Eclipse at all?

+

Start the build-in help of Eclipse via the menu "Help/Help Content" and read the section "Workbench User Guide". +

+ Workbench user guide +
+
+

Why is WUDSN IDE not available via the update site?

+

Most likely you typed in the wrong update site URL, for example using "wusdn" instead of "wudsn". The correct URL is + "http://www.wudsn.com/update". In addition you should disable the checkbox "Hide items that are already installed", so you see + what really is there. See the section "Installing WUDSN IDE" the tab "Installation" for the required steps. +

+ Installation dialog with update site +
+
+

Why do I get "Unable to read repository at ... Read timed out" when accessing the update site?

+

This error message indicates the the Eclipse program is somehow blocked from accessing the site. If you are behind a proxy + server, check the general proxy server setttings in the Eclipse preferences. If you use a firewall or internet security tool, + make sure "Eclipse.exe", "java.exe", "javaw.exe" or the corresponding program on your host platform are allowed to cannot to + the internet. Maybe you have to change the settings, so you are prompted to allow access interactively. +

+
+
+

Why is the "Assembler" section not visible in the preferences?

+

+ If the WUDSN IDE specific sections and features are not visible after a successful installation, you are probably using an + outdated Java version. For example Java 1.6 is + out of maintenance since 2013/02 + . This seems to be a common problem on Mac OS X even in Mavericks (10.9), which still uses Java 1.6 by default. Make sure you + have at least the Java version that is mentioned in the installation section for the IDE installed and that Eclipse is + actually started using that version. +

+
+

Configuration

+
+

Why do I see wrong messages in the "Problems" view?

+

The default configuration of the "Problems" view show all error from all files in the current project. While this is a good + default for Java programming, it does not fit at all for compiling single independent assembler files. Therefore you have to + configure the "Problems" view accordingly. See the section "Installing Eclipse" on the tab "Installation" for the required + steps. +

+
+
+

How to I associate my source file extensions with the correct editor?

+

+ The IDE support many different compilers and provides a specialized editor for each of them. Typically you have some preferred + source file extension (".asm" or ".a") and a preferred compiler. The procedure to associate the file extension with the editor + via the preferences is described in this video tutorial + WUDSN IDE Tutorial 3: Setting up Editors and File Extensions correctly + . +

+
+
+

Why do I have to put ;@com.wudsn.ide.asm.hardware=...in the source file?

+

The association with the file extension with the editor for your compiler (that is done in the preferences, see before) + does not determine for which platform you want to create output. Therefore this additional annotation in the main source file + is required used to tell the IDE which is the target platform. It is used to fine the correct compiler and emulator setting, + which can be different per platform. Every compiler has a default platform (see the online help in the IDE) but can also be + used to every other platform. Therefore you have to specify the target platform in the main source file, if you use a + non-default platform. +

+
+

Editing

+
+

Why is editing sometimes slow or even everything is blocked showing the wait cursor?

+

The core of WUDSN IDE uses the Eclipse Platform Runtime only and does not require any additional plugins. It starts and runs + very fast with that configuration and I personally used it one daily basis. So if you experience performance problems, try to + download and run the zero installation distribution of WUDSN IDE. Performance problem they are very likely caused by + additional plugins or themes installed. Often these plugins are not only slow, but broken. Check the ".metadata/.log" file in + the workspace folder. In some Eclipse versions, this is also available via "Window/Show View.../Error Log". +

+
+
+

Is there support for source version control?

+

Yes, there are several plugins available to connect Eclipse to CVS or subversion. Also the "Local History" feature is + installed by default. You can configure it in the preferences. It automatically records all changes to the source file and + let's you compare versions in-place. +

+ Source version control +
+
+

Is there support for block selection mode?

+

Yes, there is a toolbar button and the shortcut "ALT+SHIFT+A" to toggle block selection mode in all text editors. This can + be very useful for adding and removing common prefixes such as line numbers. +

+ Toggle block selection mode +

In case the toolbar button is not visible, you have to set it to visible via the menu entry "Customize Perspective" in the + context menu of the main toolbar. In the customizing dialog you have to activate the commend group "Editor Presentation" and + then the toolbar entries you want to see. +

+ Toggle block selection mode +
+
+

Why does CTRL-Space not open content assist?

+

There is a known key conflict when using Messenger Plus Live! v4.85.0.386 with Microsoft Messenger 2009 on Windows 7 + Ultimate. This may also occur in other version of course. Justin Payne has provided the following description of the solution. +

+
    +
  1. Start up and log into MS Messenger.
  2. +
  3. From the main window, hit the ALT key to bring up the main menu and select "Plus! | Preferences & Options". +
  4. +
  5. From the Preferences Windows, Select the Messenger tab and uncheck "Activate Messenger Lock with a system-wide shortcut" + OR change the value in it's text box to something other than "CTRL + Space" +
  6. +
  7. Select OK button.
  8. +
+
+

Why do CTRL-SHIFT-0/9 and other key combinations not work?

+

You probably have another program outside of Eclipse that has already captured these keys or key combinations. A frequent + problem is the Windows Input Methods Editor (IME) which is used to switch keyboard layouts. For example if you are using + multiple keyboard layouts, the CTRL-Space is mapped to allow you to cycle between the different keyboard regional layouts. + You should probably be aware of how to turn the feature off since you're probably using this features, but if you don't... +

+

Windows 7

+
    +
  1. Within "Windows Control Panel", open "Region and Settings".
  2. +
  3. Select the "Keyboard and Languages" tab, select "Change Keyboards...".
  4. +
  5. In the "Text Services and Input Languages" windows, select "Advanced Key Settings".
  6. +
  7. In the "Hot Keys for input languages" list box, select "Between input languages" and then select "Change Key Sequence..." +
  8. +
  9. In the "Change Key Sequence" window, choose another radio button other than the one next to CTRL+Shift. At best you + choose "(None)". +
  10. +
  11. Click OK until you closed all popup windows.
  12. +
+

Windows 10

+
    +
  1. Open "Control Panel\Clock, Language, and Region\Language\Advanced settings.
  2. +
  3. Click "Change Language bar hot keys".
  4. +
  5. Set all key sequences to "(None)".
  6. +
  7. Click OK until you closed all popup windows.
  8. +
+

+ Of course, this is Windows 7/10 and we know how Microsoft loves to change their layouts and names, but for fact this option + is available back to Windows XP. +
+ Configure Windows IME hot keys +

+
+

Compiling

+
+

Why is MADS the primary compiler?

+

When I started with WUDSN IDE, ATASM was the first supported compiler. The reason was simple: 90% of my sources are in + ATASM format. ATASM is very comprehensive and fast. Its capabilities to define constants an byte sequence is very complete + (".BYTE", ".WORD", ".DBYTE", ".FLOAT", ."SBYTE" for ATASCII, ".CBYTE" for terminated strings, separate offset for all + constants). Over time additional platform compilers haven been added and support for them will be completed step by step. + When the support for MADS was rather complete, I found that it is the most powerful compiler I have ever seen and used. The + support for ".PROC/.ENDPROC" has revolutionized the way I write assembler code now. It allows logical structuring and + visibility control without any runtime overhead. At the same time MADS is compatible to MAC/65 and XASM and even ATASM + sources can be adapted to MADS with a few minor changes described below. Therefore MADS is the primary compiler since WUDSN + IDE version 1.6.0. +

+
+
+

Why do I get the error "No ORG defined" when compiling the example from the tutorial?

+

Since WUDSN IDE version 1.6.0 MADS is the primary compiler which is registered for the file extensions ".asm" upon + installation. You are trying to run the code example for version 1.5.0 or before, which is in ATASM format. Therefore you can + either +

+ +
+
+

Why are the errors and warnings from an include file assigned to the main source file in the problems view?

+

You use a case-insensitive file system and have used different upper or lower case writing in the source include statement + than in the actual file system. For example you have written "ICL 'example.asm'" for a file named "Example.asm" on the file + system. In Eclipse the file names of all resources are treated as case-sensitive, even if the underlying file system is + case-insensitive. Therefore the file name issued by the compiler will no match with the file name of the source include. As a + fall-back, the IDE assigns the error message to the main source file. To fix this, you have to adapt the spelling of the file + name in the source include statement. +

+
+
+

How to I convert an ATASM source for to MADS format?

+

Because both ATASM and MADS syntax are based on the MAC/65 syntax, there are not really many differences. Therefore manual + conversion using "Find/Replace (CTRL-F)" is very easy. +

+
    +
  • Replace the origin definition "* = address" by "ORG address".
  • +
  • Replace ".INCLUDE" by "ICL" for source includes.
  • +
  • Replace ".INCBIN" by "INS" for binary includes.
  • +
  • Replace "* = $2E0;.WORD address" by "RUN address" to specify the run address.
  • +
  • Replace "* = $2E2;.WORD address" by "INI address" to specify the init address.
  • +
  • Check the quotes of ".BYTE" and "DTA" statements. In MADS, single quotes result in ASCII codes, double quotes in ATARI + screen codes. +
  • +
  • Remove all ".BANK" statements. While ATASM sorts and merges all segments within one bank statement, MADS simply uses the + order of segments as defined in the source file. Every "ORG" statement automatically generates new file segment. +
  • +
+
+
+

How does ATASM generate segments in executable files?

+

A helpful feature for small projects is that by default ATASM sorts the segments by address and warns if the same address + is overwritten by code or data. Since version 1.05 the ".BANK" directive is available, which allows you to create COM files + with "INITAD" segments and arbitrary segment counts. If you don't use the ".BANK" directive, ATASM will sort the segments by + their address and will put consecutive blocks into a single segment by default. While this saves some bytes, it might be + confusing if you are used to other assemblers. Note that you have to use the ".SET 6" directive to set the assembler origin + offset in every bank if you use it in one of the banks. +

+
+ ; Bank 0 +
+ .bank +
+ .set 6,0 +
+ * = $8000 +
+ start lda #0 +
+ jmp * +
+
+ ; Bank 1 +
+ .bank .set 6,0 +
+ * = $2e0 +
+ .word start; +
+
+ ; Bank 2 +
+ .bank +
+ .set 6,$4300-$C000 +
+ * = $C000 +
+ lda #1 +
+ sta label+1 +
+ label lda #2 +
+ jmp * +
+
+
+
+

How do I compile into ROM images?

+

Plain ROM Images do not have header bytes by default, or at least they do not have the same header bytes as executable + files. In order to create raw object files without headers, compiler specific options have to be used. Some cases are listed + below. See the manual of the specific compiler for more details. +

+
    +
  • + ACME: Use the compiler parameter " + -f plain + " instead of "-f cbm" (default) to switch to "plain" mode without header +
  • +
  • + ATASM: Extend the compiler parameter "-o${outputFilePath}" to " + -r + -o${outputFilePath}" to switch to "raw" mode without header +
  • +
  • + MADS: Use " + OPT h-f+ + " at the very beginning of the source file disables header and enabled "fill" mode, i.e. no segments are created if there + are gaps in the object code +
  • +
+
+
+

How do I compile into disk images?

+
Atari 8-bit
+

+ For Atari 8-bit, the ATASM compiler has dedicated parameter to write the executable file directly into ".ATR" or ".XFD" disk + images. The disk image must be formatted with Atari DOS 2.0S, Atari DOS DOS 2.5 or a compatible DOS. All Atari 8-bit disk + formats can be created using the "dir2atr.exe" tool of the + AtariSIO tools + by Matthias Reichl (hias). The tool can create a complete disk image with arbitrary DOS (Atari DOS 2.5, MyDos, SpartaDOS) and + size based on a folder which contains "DOS.SYS", "DUP.SYS" (or the equivalent files of the respective DOS) and all other + files requires. I have packaged an example including the "dir2atr" tool, a batch script to call the tool and the emulator and + the "files" folder in this + archive + . Unpack the archive to your output folder. Double-click "makefile.bat" to see how the disk image is created and started. + Read section + How to run a makefile script instead of an emulator? + for the details how to configure the call to "makefile.bat". For productive usage you should of course put the "hias" folder + into a central location and use the most recent version from hias' site. For MacOS X users, the download also contains a + "makefile.sh" script and MacOS X binaries of Matthias Reichl's tools. The binaries have been provided by Fredrick Holst + (freetz) and you can find the latest versions on his + web site + . +

+
Apple II
+

+ For Apple II, WUDSN IDE automatically generates a bootable AppleDos 3.3 disk image with extension ".dsk" if one of the + predefined emulators is used for execution. If you want to use another DOS or disk size or if you want to but more files into + the disk after compilation, you can use the command line version of + AppleCommander + to achieve this. Create a makefile script and configured it as described in + How to run a makefile script instead of an emulator? + . In case of Apple Disk images always remember using the correct the file content/load/run address. The IDE needs to know the + load address of an executable file in order to store this information in the directory entry. To detect the load address from + the executable file, the IDE evaluate the file extension. Supported extensions are ".b", ".prg" and ".xex". Here's the logic + begin the built in disk image creation: +

+
    +
  • + File extension ".b" +
    + // AppleDos 3.3 binary file: start-lo,start-hi,length-lo,length-hi,data +
    + address = getWord(outputFileContent, 0);length = length - 4;content = getData(outputFileContent, 4); +
    +
  • +
  • + File extension ".prg" and length > 2 +
    + // C64 program file: start-lo,start-hi,data +
    + address = getWord(outputFileContent, 0);length = length - 2;content = getData(outputFileContent, 2); +
    +
  • +
  • + File extension ".xex" and length > 6 and (getWord(outputFileContent, 0) & 0xffff) == 0xffff) +
    + // AtariDOS 2.5 binary file:$ff,$ff,start-lo,start-hi,end-lo,end-hi,data +
    + address = getWord(outputFileContent, 2);length = length - 6;content = getData(outputFileContent, 6); +
    +
  • +
+
Other hardwares
+

+ If you find a tool similar to "dir2atr.exe" for the Atari 8-bit or "AppleCommander" for the Apple II, you can use create your + own script an run it as described in + How to run a makefile script instead of an emulator? + . +

+
+
+

How can I run a makefile or script instead of an emulator?

+

+ Sometimes it is useful to run a makefile script instead of the emulator, for example if the output file shall be combined + with other files into a single ATR file. To execute a such a script select "User Defined Application" as the "Default + Application to open Output File". Specify the path to the shell as "Path to Application". In the command line you can then + use the standard variables to start the shell, pass the name of the script and pass the file path of the compiled output + file. Since the working directory at the time of execution is the output folder of the compiler you must place the script + file there or you must specify the script file with its absolute path. If you are using Windows and "cmd.exe" as shell, you + must specify "/c" before the name of the script to prevent "cmd.exe" from remaining as process after the script has finished. + The resulting command line is "${runnerExecutablePath} /c makefile.bat ${outputFilePath}", assuming "makefile.bat" is located + in the output folder". See + How do I compile into disk images? + for the example how to use this for compiling complete disk images. +

+ Configure makefile script +
+

Emulation

+
+

How can I use other emulators?

+

You can "re-use" the existing tabs and simply specify another emulators' executable. If you use "User Defined + Application", you can specify whatever you want. When using "User Defined Application", no disk image is created or updated. + You can use this setting to have you own script which put the executable file onto a disk image of your choice using + additional tools like "dir2atr.exe" or "AppleCommander" for example. +

+
\ No newline at end of file diff --git a/com.wudsn.ide.asm/help/ide-features.section.html b/com.wudsn.ide.asm/help/ide-features.section.html new file mode 100644 index 00000000..734aa16e --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-features.section.html @@ -0,0 +1,1014 @@ + +The latest version contains the following features: +
+ +
General + IDE enhancements   + » top +
+
    +
  • "Open Folder" context menu available for all folders and files
  • +
  • + "Sort" context menu with sub-menu available for all text files including +
    +
      +
    • Case sensitive, case insensitive and numeric sorting
    • +
    • Sorting with and without removal of duplicates
    • +
    • Reverse ordering
    • +
    + Open folder action in context menu + Sort action in text editor context menu +
  • +
  • + The online help contains the entry "WUDSN IDE Guide" which contains multiple sections. +
      +
    • + WUDSN IDE - mainly the documentation from the web site +
        +
      • Video links to the tutorial and release news
      • +
      • Features
      • +
      • Installation
      • +
      • FQA
      • +
      • Link
      • +
      +
    • +
    • + Assemblers - the information on the supported assemblers and their properties +
        +
      • General - links, syntax and support features
      • +
      • Instructions - all supported instruction including their descriptions grouped by type
      • +
      • Manual - direct access to the PDF, HTML or text manual file or files which are part of the compiler installation
      • +
      +
    • +
    • + Hardware - the information on the supported hardwares and the corresponding emulators and links +
        +
      • Reference documentation - Hardware specific PDF, HTML or text files with CPU and customer chips reference sheets and programming guides
      • +
      +
    • +
    +
    + Integrated Help +
  • +
+
Fully + integrated assembler editor   + » top +
+
    +
  • + The Eclipse platform contains editors like for example the generic text editor. Editors can support one or more + content types identified by file extensions. One editor can be the default for a specific file extension. WUDSN IDE + provides an assembler + editor and a corresponding content type for every compiler. Every assembler + editor has a default compiler associated which in turn defines via the preferences which application is used to run the + output file. +
    + IDE editors, content types and file associations +
  • +
  • + New content types and file associations for Atari 2600 compilers +
      +
    • DASM Source File (*.asm)
    • +
    +
  • +
  • + New content types and file associations for Atari 8-bit compilers +
      +
    • ATASM Source File (*.asm)
    • +
    • MADS Source File (*.asm, default)
    • +
    • XASM Source File (*.asx, default)
    • +
    +
  • +
  • + New content types and file associations for C64 compilers +
      +
    • ACME Source File (*.a, default)
    • +
    +
  • +
  • + New content types and file associations for NES compilers +
      +
    • ASM6 Source File (*.asm)
    • +
    +
  • +
  • + The default editor for an extension can be configured in section "File Associations" of the preferences with the + button "Default" +
    + IDE file associations +
  • +
  • Syntax highlighting colors and styles for comments, directives, all types of identifiers, legal opcodes, illegal + opcodes, pseudo opcodes and strings +
  • +
  • Single line comments for the current line selection can be toggled using "CTRL-7" or the editor context menu entry + "Toggle Comment" +
  • +
  • Configurable support for 16-bit opcodes of 65816
  • +
  • + Built-in support for + ATASM compiler + , + MADS compiler + , + XASM compiler + , + ACME compiler +
  • +
  • Build in support for running compiler output files with the operating system default application without + configuration effort +
  • +
  • + Build in support for + Altirra emulator + , + Atari800Win emulator + , + Atari800MacX emulator + , + Atari++ emulator +
  • +
  • + Adding support for new compilers and application is possible via Eclipse extension points +
    + IDE compiler extensions +
  • +
  • Compile or compile and run with a single key stroke or via menu
  • +
  • Toolbar button for "Compile and Run" which adapts to the hardware of the currently opened editor, for example it + will show a C64 icon when using the "ACME (C64)" editor +
  • +
  • + The toolbar button for "Compile and Run" offers a pulldown menu which allows to run the output file with any of the + applications configured in the preferences. This way you can run the output file easily with different emulators + without changing the + preferences. That can be very helpful if one emulator show a different behaviour than another or + in case you have specified a build script as user defined application in the preferences +
    + Run with... for Atari 2600 + Run with... for Atari 8-bit + Run with... for C64 +
  • +
  • Open source and output folder directly from menu
  • +
  • + Open compiler help directly from menu if the documentation is available in the default folder structure of the + compiler executable +
    + IDE assembler menu +
  • +
  • Complete list of compiler error and warning messages in the problems view
  • +
  • Direct navigation to the source location of the via double click on the problem message
  • +
  • + Problem markers in the scroll area including tooltip with problem message +
    + IDE example source +
  • +
  • + German localization for all Eclipse plugin texts. The original compiler messages are not translated but some are + mapped automatically +
    +
      +
    • ATASM ".bank" warnings are mapped to info messages
    • +
    +
  • +
  • + Console view with original compiler output opens automatically in the background after compiling which useful in + cases where the plugin is not yet complete. The "Compiler Console" is automatically brought to front when the + compiler starts. This prevents + the compiler output from being hidden behind other consoles in the console view +
    + IDE compiler console view +
  • +
+
Content + outline and source folding   + » top +
+
    +
  • Activated via the standard menu "Window/Show View/Outline"
  • +
  • Automated asynchronous parsing while typing
  • +
  • Automated recursive parsing of source includes and merge with current outline
  • +
  • Positioning in the content outline remains stable while typing unless structure changes appear
  • +
  • Folding is active automatically when outline is visible
  • +
  • Folding for if/else/endif blocks
  • +
  • Outline and folding for definition section and implementation sections
  • +
  • Outline for equate definitions (including defining expression), label definitions and variable definitions
  • +
  • Outline and folding for enum and struct definitions
  • +
  • Outline and folding for macros definitions
  • +
  • Outline and folding for repeat sections
  • +
  • Outline and folding for procedure definitions
  • +
  • Outline and folding for local sections
  • +
  • Outline for source includes and binary includes
  • +
  • Type specific outline icons
  • +
  • Nested folding with tooltip for folded sections
  • +
  • Line end comment is used as short description
  • +
  • + Toolbar with button to toggle the sorting order of the sections and labels. The state of the button is persisted + automatically along with the respective source file. For files which do not have a setting yet, the setting from the + currently opened file + will be used +
    + IDE content outline +
    + IDE content folding +
  • +
+
Content + assist and code completion   + » top +
+
    +
  • Content assist by pressing CTRL-Space for directives, legal opcode, illegal opcodes and pseudo opcodes
  • +
  • The content assist also recognizes if there is already and instruction in the current line and suggests the + available identifiers instead if this is the case. This is the first version and it supports global identifiers in + the current source file and + source file included from there. Scoped identifiers of the form "a.b" are not yet + supported +
  • +
  • Completion proposal auto activation without pressing CTRL-Space after typing compiler dependent characters, for + example "." in ATASM, "." or "#" in MADS and "!" in ACME +
  • +
  • Type specific icon and mnemonic highlighting
  • +
  • Progressive filtering as you type
  • +
  • Automatic detection of lower case / upper case based on current input
  • +
  • Default case configurable in preferences
  • +
  • Illegal opcodes can be hidden via preferences
  • +
  • + 16-bit opcodes of 65816 can hidden via preferences +
    + IDE content assist +
  • +
  • + Multi-line content completion and explicit cursor positioning, for example ".MA" becomes ".MACRO <cursor is + here> <newline> .ENDM" +
    + Muli-line content completion and cursor positioning +
  • +
+ +
    +
  • Hyperlink navigation via CTRL-click to source includes and binary includes
  • +
  • Support for relative and absolute file paths
  • +
  • Source file are always opened with the same assembler editor, irrespective of the extension
  • +
  • The ".asm" extension for "ICL" source includes is appended automatically in MADS, if it is missing
  • +
  • The ".asx" extension for "ICL" source includes is appended automatically in XASM, if it is missing
  • +
  • + Choice for binary includes to open the file with +
      +
    • the build in hex editor
    • +
    • the build in graphics editor
    • +
    • the default Eclipse editor (e.g. a text editor)
    • +
    • system editor (e.g. an emulator or a paint program)
    • +
    +
  • +
  • + Hyperlink navigation via CTRL-click to labels, equates, local definitions, macro definitions and procedure + definitions. In case there is only one target, direct navigation takes place. In case there is more than one possible + target, the type and line + number of the target are displayed in a hyperlink popup. All included source files are also + taken into account like in the content outline. In case there are targets from different files, the file name is also + displayed as differentiator in the hyperlink + popup. +
    +
    + IDE navigation to source file +
    + IDE navigation to binary file +
    + IDE navigation to labels, equates, local definitions, macro definitions and procedure definitions +
  • +
+
Hex + Editor   + » top +
+
    +
  • Read-only hex editor to inspect arbitrary files
  • +
  • + Available via context menu including multi-file-selection to open several files at once +
    + Open With Hex Editor +
  • +
  • The "Open With Hex Editor" menu entry closes an existing editor in case the file is already opened and thereby + forces the file to be opened with the hex editor. In addition the hex editor is set as the default editor this one + file. As a consequence + double clicking the file in the package explorer will open it automatically with the hex + editor, no matter which type of file is actually is. This saves you from always using the context menu, just because + the file has no known file type. You can change + the default editor again by just using the "Open With" context menu + entry +
  • +
  • Available via hyperlink navigation for binary includes
  • +
  • + Context menu to copy parts of the file into the clipboard in different formats: +
      +
    • hex values (".byte $01,$02,...")
    • +
    • decimal values (".byte 1,2,...")
    • +
    • ASCII string
    • +
    + This is very helpful to turn parts of a file into source code. The possibility to copy/paste into the binary file + itself in order to modify is prepared but not working yet and will be completed later. +
    + Hex Editor context menu +
  • +
  • + Support for binary files, Atari COM files and Atari Disk Images +
    + Hex Editor file mode selection +
  • +
  • The possible file modes for a binary file are computed automatically and used as default when opening the file +
  • +
  • Unsupported file modes are detected and cannot be used
  • +
  • Files with a block structure get an outline in the outline view
  • +
  • + Files with a corrupted block structure are detected and displayed as good as possible +
    + Hex Editor outline view +
    + Hex Editor with corrupted Atari COM file +
  • +
+
Graphics + Editor   + » +
+
    +
  • + Viewer for binary files and 8-bit image files. +
    + The most suitable supported converter and the corresponding default parameters are computed based on the file + content, size and extension. +
    + The converters for the Atari standard image formats are based on + FAIL + , the excellent First Atari Image Library created by Piotr Fusik and Adrian Matoga. +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PlatformFile ExtensionFile Format
    Atari 8-bitAP380x192, 256 colors, interlaced
    Atari 8-bitAPCAny Point, Any Color, 80x96, 256 colors, interlaced
    Atari 8-bitCHR8x8 charset, mono or multicolor
    Atari 8-bitCCIChampions' Interlace, 160x192, compressed
    Atari 8-bitCINChampions' Interlace, 160x192
    Atari 8-bitCHGGephard Hires Graphics, up to 320x200, mono
    Atari 8-bitCPRTrzmiel, 320x192, mono, compressed
    Atari 8-bitFNTStandard 8x8 font, mono
    Atari 8-bitGR8 Standard 320x192, mono
    Atari 8-bitGR9Standard 80x192, grayscale
    Atari 8-bitHIP Hard Interlace Picture, 160x200, grayscale
    Atari 8-bitHRHires 256x239, 3 colors, interlaced
    Atari 8-bitHR2Hires 320x200, 5 colors, interlaced
    Atari 8-bitILCAPAC 80x192, 256 colors interlaced
    Atari 8-bitINPInterlace Picture 160x200, 7 colors, interlaced
    Atari 8-bitINTINT95a, up to 160x239, 16 colors, interlaced
    Atari 8-bitMCPMcPainter, 160x200, 16 colors, interlaced
    Atari 8-bitMICMicropainter 160x192, 4 colors
    Atari 8-bitPICKoala MicroIllustrator, 160x192, 4 colors, compressed
    Atari 8-bitPLMPlama 256, 80x96, 256 colors
    Atari 8-bitRIPRocky Interlace Picture, up to 160x239
    Atari 8-bitSXS16x16 font, mono
    Atari 8-bitTIPTaquart Interlace Picture, up to 160x119
    C6464CCharset, mono or multi color
    C64SPRSprite, mono or multi color
    +
  • +
  • Viewer for GIF/JPG/BMP/PNG images
  • +
  • + Available via context menu including multi-file-selection to open several files at once +
    + Open With Graphics Editor +
  • +
  • The "Open With Graphics Editor" menu entry closes an existing editor in case the file is already opened and + thereby forces the file to be opened with the graphics editor. In addition the graphics editor is set as the default + editor this one file. As a + consequence double clicking the file in the package explorer will open it automatically + with the graphics editor, no matter which type of file is actually is. This saves you from always using the context + menu, just because the file has no known file + type. You can change the default editor again by just using the "Open + With" context menu entry +
  • +
  • Available via hyperlink navigation for binary includes
  • +
  • + Conversion from binary files or 8-bit images files to GIF/JPG/BMP/PNG images +
      +
    • Up to different 3 source files depending on the converter
    • +
    • Configurable start offset for every source file, useful for extracting character sets
    • +
    • Configurable palette via the Image Palette view
    • +
    • Configurable number of rows and columns
    • +
    • Configurable spacing width for and spacing color to separate tiles and unused areas
    • +
    • Separate aspect ratio for display and saving the image file
    • +
    + Conversion from binary files or 8-bit images files to GIF/JPG/BMP/PNG images +
  • +
  • + Conversion from GIF/JPG/BMP/PNG images to binary files or 8-bit images using JavaScript. +
      +
    • Separate aspect ratio for loading and displaying the image file
    • +
    • Default scripts are included. They can adjusted and saved along with the other parameters
    • +
    +
    + Conversion from GIF/JPG/BMP/PNG images to binary files or 8-bit images files +
  • +
  • + The image palette view associated with the graphics editor displays the palette entries and the color histogram. It + supports filtering of unused colors, sorting by index and color frequencies, pre-settings and editing the palette + when displaying 8-bit + images +
    + Image palette view +
  • +
  • Conversion files with extension ".cnv" are used to save the conversion direction and all conversion parameters +
  • +
  • Support for relative file paths. If the file path is in the same folder as the ".cnv" file, it it automatically + converted to a relative file path when the file name is defaulted or picked from the file browser dialog. This allows + to move the ".cnv" + file and the referenced source files around together without breaking the saved paths +
  • +
+
Preferences + for editing   + » top +
+
    +
  • Available via entry "Preferences" from the menu "Window" (in Windows and Linux) or the menu "Eclipse" (in Mac OS + X) +
  • +
  • Configuration of syntax highlighting colors and styles for comments, directives, all types of identifiers, legal + opcodes, illegal opcodes, numbers, pseudo opcodes and strings +
  • +
  • For non unique identifiers, the syntax highlighting will try to be a best guess
  • +
  • + Configuration of default case in content assist +
    + Preferences for compilers +
  • +
+
Preferences + for compiling   + » top +
+
    +
  • Available via entry "Preferences" from the menu "Window" (in Windows and Linux) or the menu "Eclipse" (in Mac OS + X) +
  • +
  • Separate compiler preferences page for every type of hardware, i.e. Atari 8-bit and C64; Apple 2 support is in + development by Nick Westgate +
  • +
  • Preferences can be maintained for all compilers in parallel
  • +
  • Upon opening the compiler preferences, the tab for the compiler of the active editor is activated automatically +
  • +
  • + Configuration of illegal opcodes and 65816 opcodes support in syntax highlighting and content assist +
    + The configuration is offered only if the compiler supports the respective feature +
  • +
  • Download links, configurable paths and default parameters for all compilers
  • +
  • The default file extension filter is set to "*.exe" on Windows and "*.*" on all other operating systems
  • +
  • The button "Apply Defaults" does explicitly not reset the paths to the executables
  • +
  • + Configuration of parameters per compiler including predefined defaults to run out-of-the-box +
    + Possible variables are: +
      +
    • {$sourceFolderPath} The absolute path to the source folder
    • +
    • {$sourceFilePath} The absolute path to the source file
    • +
    • {$outputFolderPath} The absolute path to the output folder
    • +
    • {$outputFilePath} The absolute path to the output file
    • +
    • {$outputFileName} The name of the output file including its extension, for example "TestFile123.asm"
    • +
    • {$outputFileNameWithoutExtension} The name of the output file without extension, for example "TestFile123"
    • +
    • {$outputFileNameShortWithoutExtension} The name of the output file without extension shortened to 8 alphanumeric + characters, for example "TESTFILE" +
    • +
    + Make sure that you don't remove parameters when you specify your own values +
    + If not, either creating the label definition file or parsing the compiler log may fail +
      +
    • ATASM requires the "-s" parameter to be present"
    • +
    • MADS requires the "-p" parameter to be present
    • +
    • ACME requires the "!to" directive in the source to be commented out to ensure the output file name from the + command line is used +
    • +
    +
  • +
  • + Preferences can be maintained for all possible applications of all compilers in parallel +
      +
    • + Possibility to use the "Operating Sytem Default Application" of the platform to open the output file +
      + For Windows and Mac OS X choosing the "Operating Sytem Default Application" means that for ".xex" file the emulator will be started + automatically without any further configuration +
    • +
    • Possibility to use one of the pre-defined applications and optionally change the command line
    • +
    • Possibility to use a user defined application to open the output file
    • +
    • + Possible variables are: +
        +
      • {$runnerExecutablePath} The absolute path to the executable of the application
      • +
      • {$sourceFolderPath} The absolute path to the source folder
      • +
      • {$sourceFilePath} The absolute path to the source file
      • +
      • {$outputFolderPath} The absolute path to the output folder
      • +
      • {$outputFilePath} The absolute path to the output file
      • +
      • {$outputFileName} The name of the output file including its extension, for example "TestFile123.asm"
      • +
      • {$outputFileNameWithoutExtension} The name of the output file without extension, for example "TestFile123" +
      • +
      • {$outputFileNameShortWithoutExtension} The name of the output file without extension shortened to 8 + alphanumeric characters, for example "TESTFILE" +
      • +
      +
    • +
    +
  • +
  • Download links, configurable paths and default command lines for Atari++, Atari800Win, Atari800MacX emulators per + compiler +
  • +
  • + The default file extension filter is set to "*.exe" on Windows and "*.*" on all other operating systems +
    + Preferences for compilers +
  • +
+
Annotations + for compiling   + » top +
+
    +
  • For most use-cases, the defaults provided for the compilers and in the preferences are sufficient. But if you want have develop in parallel in multiple projects, for different platforms (Apple II and Atari 8-bit) or in different output formats + (".XEX" and ".BIN") with the same compiler it may become cumbersome to change the preferences every time. Therefore WUDSN IDE offers annotations which you can put into the source code files. These annotations override the defaults and the preferences. +
  • +
  • All annotations start with the prefix "@com.wudsn.ide.asm." followed by the lower case name of the annotation, an equals sign and the unquoted value. Example: "@com.wudsn.ide.asm.hardware=ATARI8BIT"
  • +
  • All annotations can be placed in comment lines at the begin of a source file. Some of the annotations are only relevant for the main source file, some are only relevant in include source files, some are + relevant for all source files. +
  • +
  • + @com.wudsn.ide.asm.hardware +
      +
    • Defines the target hardware for which the preferences shall be evaluated, in particular which emulator is used to run the output file.
    • +
    • Allowed values are "APPLE2", "ATARI2600", "ATARI7800", "ATARI8BIT", "C64", "NES".
    • +
    • This annotation is relevant for all source files.
    • +
    • This annotation is only evaluated when a file is opened. So if you add this annotation or change its value, you have to close and re-open the file once.
    • +
    • Example: @com.wudsn.ide.asm.hardware=ATARI8BIT
    • +
    +
  • +
  • + @com.wudsn.ide.asm.mainsourcefile +
      +
    • Defines the main source file to which the current include source file belongs. When the "Compile" action is executed, the main source file is compiler instead of the current file.
    • +
    • Allowed values are file paths relative to the folder of the current include source file and absolute file paths.
    • +
    • This annotation is only relevant for include source files.
    • +
    • Example: @com.wudsn.ide.asm.mainsourcefile=ExampleMain.asm
    • +
    +
  • +
  • + @com.wudsn.ide.asm.outputfoldermode (planned for 1.7.0) +
      +
    • Overrides the "Output Folder Mode" from the preferences.
    • +
    • Allowed values are "SOURCE_FOLDER", "TEMP_FOLDER", "FIXED_FOLDER".
    • +
    • This annotation is only relevant for the main source file.
    • +
    • Example: @com.wudsn.ide.asm.outputfoldermode=SOURCE_FOLDER
    • +
    +
  • +
  • + @com.wudsn.ide.asm.outputfolder (planned for 1.7.0) +
      +
    • Overrides the "Output Folder" from the preferences and the "@com.wudsn.ide.asm.outputfoldermode" annotation.
    • +
    • Allowed values are file paths relative to the folder of the main source file and absolute file paths.
    • +
    • This annotation is only relevant for the main source file.
    • +
    • Example: @com.wudsn.ide.asm.outputfolder=..\out
    • +
    +
  • +
  • + @com.wudsn.ide.asm.outputfileextension (planned for 1.7.0) +
      +
    • Overrides the "Output File Extension" from the preferences.
    • +
    • Allowed values have to start with a period.
    • +
    • This annotation is only relevant for the main source file.
    • +
    • Example: @com.wudsn.ide.asm.outputfileextension=.bin
    • +
    +
  • +
  • + @com.wudsn.ide.asm.outputfile (planned for 1.7.0) +
      +
    • Overrides the "Output Folder", "Output File Extension" from the preferences and the automatic computation of the out file name based on the main source file name.
    • +
    • Allowed values are file paths relative to the folder of the main source file and absolute file paths.
    • +
    • This annotation is only relevant for the main source file.
    • +
    • Example: @com.wudsn.ide.asm.outputfile=..\out\output.bin
    • +
    +
  • +
+
Known + bugs   + » top +
+Open bugs: +
    +
  • If you find any, please contact me
  • +
+Fixed bugs: +
    +
  • + 1.6.5 +
    +
      +
    • Automatic creation of ".DSK" disk images for Apple II now works correctly +
    • +
    • Dirty indicator in Graphics Editor is now updated correctly +
    • +
    +
  • +
  • + 1.6.4 +
    +
      +
    • The toolbar icons now work correctly with newer Eclipse versions
    • +
    • Atari 8-bit Graphics 12 Converter works again
    • +
    • Graphics editor now properly closes input stream for image files
    • +
    • Empty selection and too large numbers no longer lead to exceptions when opening the context menu for "Convert..."
    • +
    • Pressing refresh in the graphics converter now always updates the image pane correctly and not only if the size has changed
    • +
    • Inline repeats like ":64" in MADS are no longer detected as labels
    • +
    • Hex Editor now correctly detects erroneous COM files in case the segment length exceeds the file length (by one)
    • +
    • Disk images (for Apple II) are now updated also if only "Compile" instead of "Compile and Run" is used
    • +
    • Apple Commander integration is now part of the installation, as it should have been with 1.6.3
    • +
    • + The HELLO program generated for the auto-start disk images of Apple II now displays a title and uses "BLOAD/CALL" instead of "BRUN" because of this + bug in Apple DOS +
      10 PRINT "Loading <title>" : PRINT CHR$(4);"BRUN WORLD" : CALL <address>
      +
    • +
    +
  • +
  • + 1.6.3 +
    +
      +
    • First character of numbers is now correctly highlighted in #123
    • +
    • Source file includes via "INCSRC" are now correctly detected for ASM6
    • +
    +
  • +
  • + 1.6.2 +
    +
      +
    • Typing a "." to trigger the automatic content assist no longer locks-up
    • +
    • Default color for illegal opcodes changed to red as it was intended
    • +
    • German localization for the graphics editor is finally completed
    • +
    • Sorting of application in the preferences now is "Default, A...Z, User Defined" also in non English localizations
    • +
    +
  • +
  • + 1.6.1 +
    +
      +
    • Clicking in the outline always positions the cursor correctly in the source, not only the first time
    • +
    • All names of content types are now translated correctly in the preferences
    • +
    • "Open Folder" command now also works for objects which are no resource or file themselves but can be adapted to one of these types. For example project explorer entries for Java classes
    • +
    +
  • +
  • + 1.6.0 +
    +
      +
    • Hyperlink navigation now also works for labels which contain an underscore
    • +
    • The key binding for the "Compile" menu is now "Shift-Ctrl-9" because it turned out that "Ctrl-0" is not available in all cases
    • +
    • The HexEditor is now also detecting the situation that the first block of a COM file is incorrect and display this correctly
    • +
    • Content assist has now correct new lines when inserting #IF, #WHILE and .TEST
    • +
    • Fonts and colors resources are disposed correctly now
    • +
    • Resetting to default syntax colors in the preferences works now
    • +
    • Elements of .ENUM are recognized as equates now also if they do not start of position 0
    • +
    • Elements of .STRUCT are recognized as labels now also if they do not start of position 0
    • +
    • Cursor is now positioned to the first character of an equate or label also if it is not defined starting at position 0
    • +
    • Cursor is now positioned to the first character of an equate or label also if it is not defined starting at position + 0 +
    • +
    +
  • +
  • + 1.5.0 +
    + +
      +
    • Syntax highlighting remains active now also after "Save as..."
    • +
    • Cursor is now placed correctly by content assist
    • +
    • The key binding for the "Compile" menu is now "Ctrl-0" instead of "Ctrl-Alt-0", so entering "}" is now possible again
    • +
    • XASM editor now correctly detects the "ORG" directive
    • +
    • The assembler editor toolbar contribution now also displays the label "Assembler" in the customizing dialog for the perspective
    • +
    +
  • +
  • + 1.4.4 +
    +
      +
    • The directive ".LOCAL" in ATASM is a normal directive now and does not start a folding section while in MADS it is + really is a folding section from ".LOCAL" to ".ENDL" +
    • +
    • "SIN()" and "RND()" in MADS are now recognized correctly even if there are no spaces before or after the directive +
    • +
    • ".EN" and ."END" in MADS are now recognized correctly
    • +
    • Labels in "ORG" lines are now recognized correctly and rendered as separate tree entries
    • +
    • Preferences for lower/upper case instructions in content assist are now also evaluated if the for directives which + do not start with letters like ".end" +
    • +
    • When opening a file which is located outside of the workspace, the action to open folders and to compile the file + are now not doing anything and will not cause exceptions. They will also be disabled once the new Eclipse version is + mandatory +
    • +
    • The 16 bytes per row in the hex editor are now separated by a space
    • +
    +
  • +
  • + 1.4.3 +
    +
      +
    • The output file is not opened anymore in case it has been there before but was not updated by the compiler due to + compiler errors +
    • +
    • The scroll bar and cursor in the editor are now stable also in the cases when the outline is changed by the latest + user input +
    • +
    • The hex editor now gets the focus correctly also when clicking on already opened files
    • +
    +
  • +
  • + 1.4.2 +
    +
      +
    • The folder which contains the output file and the symbols file is refreshed automatically after compiling to + ensure that the Eclipse resource cache is in sync with the file system +
    • +
    • Line end comments are now used as description for source and binary includes in the content outline
    • +
    • The "Open Folder" context menu entry works again, bug was introduced in version 1.4.0
    • +
    +
  • +
  • + 1.4.0 +
    +
      +
    • The "Assembler" menu is visible now only if an Assembler editor is active. The contained entries and their short + cuts are now disabled if no Assembler editor is active +
    • +
    • The output file is not deleted anymore before the compiler is started, instead it is only checked for being + writeable. This allows for direct compilation into an existing ATR image +
    • +
    • The name of the application used to open the output file is now included in the info message
    • +
    +
  • +
  • + 1.3.2 +
    +
      +
    • The content outline sometimes only found the first macro definition
    • +
    • Folding did not show up in all situations
    • +
    • + Under Mac OS X, the file select dialog can now choose an "*.app" application like "Atari800MacX.app" since this is + folder. The solution is a workaround to the a general Eclipse problem under MacOS X, see + BUG 82155 +
    • +
    +
  • +
  • + Before 1.3.2 +
    +
      +
    • The "OK" and "Apply" buttons in the preferences are now always enabled, irrespective of the specified compiler and + emulator paths +
    • +
    • The focus is not back in editor window after compiling
    • +
    • The syntax highlighting of illegal opcodes fixed, now also uses the preferences setting
    • +
    • On German operating systems, the properties for the correct locale are now found
    • +
    +
  • +
+
Planned + features   + » top +
+
    +
  • + See the table below for the current feature set for the features which depend on the compiler. +
    + Features which are not supported by the compiler itself and hence cannot be supported by the IDE are marked as "n/a". +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CompilerDefault HardwareAuto Completion ActivationSingle-Line CommentsBlock CommentsStringsOpcodesDirectivesCompile Log ParsingInclude Log ParsingContent OutlineLabel Definition File Format
    ACMEC64Yes: !Yes: ;|n/aYes: "Yes + Partly + YesTBD + Partly + ACME
    ATASMAtari 8-bitYes: .Yes: ;n/aYes: "YesYesYesTBDYesXASM 3.0.1
    MADSAtari 8-bitYes: . #Yes: ; * //YesYes: ' "YesYesYesTBDYesMADS
    XASMAtari 8-bitn/aYes: ; * |n/aYes: ' "YesYes + Planned + TBDYesXASM 3.0.1
    +
  • +
diff --git a/com.wudsn.ide.asm/help/ide-installation.section.html b/com.wudsn.ide.asm/help/ide-installation.section.html new file mode 100644 index 00000000..1e557552 --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-installation.section.html @@ -0,0 +1,548 @@ +

+There are two ways of installing WUDSN IDE. If you are using a Windows operating system, I recommend downloading the ready to run zero installation distribution: +
+

+ + Click to download the Windows 64-bit version +
+It contains the Eclipse Platform, the Java Runtime Environment, the latest stable version of the WUDSN IDE plugin, all supported compilers and an emulator for +each supported platform. All paths to folders, compilers and emulators are pre-configured. Unpack the content of this archive to +the directory "C:\jac\wudsn" and click the "WUDSN IDE" link. Eclipse will open with the predefined workspace that contains +"Hello World" examples for different platforms. +
+
+If you use another operating system, or want to use the latest version of WUDSN or want to install compilers and emulators more +selectively, read the descriptions of the installation steps below. In addition, the following previous versions of the zero installation distribution are available + +In case something is not correct or not working, please contact me. +

+ +
+ Installing Eclipse   + » top +
+
    +
  • + Download Eclipse from + http://www.eclipse.org/downloads + . +
  • +
  • + If you don't know which version to take, use + Eclipse 4.3.2 + Platform Runtime Binary (61 MB) + . +
    + This is a minimum size installation which does not include the Java Development Toolkit (JDT). WUDSN IDE has no dependency to + the JDT but of course to the Java Runtime Environment (JRE). Make sure you also have the 64-bit version of the JRE installed + if you want to use the 64-bit version of Eclipse. +
  • +
  • Unzip the downloaded archive file and store the contained folder "eclipse" where you want Eclipse to be located on you + local hard drive.
  • +
  • Start the Eclipse executable from that folder.
  • +
  • Upon the first start you are prompted to specify the folder where the workspace shall be located.
  • +
  • Normally a start link a created to this end. You can use the parameter "-nl" to specify the locale if you want. Use "en_US" + for English or "de_DE" for German. Example: "C:\Program Files\Eclipse\4.3.1\eclipse\eclipse.exe" -Xmx512M -nl en_US"
  • +
  • It is recommended to create the workspace folder in your home directory.
  • +
  • After you have read the welcome page and got familiar with the Eclipse UI, just switch to the Resource perspective.
  • +
  • + Open the view "Problems" via the menu "Window/Show View/Problems" and then click the entry "Configure Contents..." from its + view menu. +
    + Open configuration of the problems view +
  • +
  • + Select the configuration "All Errors", the radio button "On selected element and its children" and the check boxes ""Error", + "Warning" and "Info". +
    + Configuration of the problems view +
  • +
  • + Now Eclipse itself is ready and you can proceed with step + Installing WUDSN IDE + . +
  • +
+
+ Installing WUDSN IDE   + » top +
+
    +
  • Start Eclipse
  • +
  • Select the entry "Install New Software..." from the menu "Help".
  • +
  • Enter "http://www.wudsn.com/update" in the "Work with" field and press ENTER.
  • +
  • + Select the latest version of the feature "WUDSN IDE" for in and press the button "Next". +
    + You don't need to install the "General Eclipse Enhancements" feature as it is already included in the "WUDSN IDE" feature". +
  • +
  • Review the installation details and press the button "Next".
  • +
  • Read the license agreement, choose the option "I accept..." and press the button "Finish".
  • +
  • In case you get a security warning that the content is unsigned, confirm the warning by pressing the button "OK".
  • +
  • When you are prompted to restart Eclipse now, press the button "Yes".
  • +
  • + As always with updates, it may happen that the update fails for whatever reason or the installed version turns out to have + severe issues. For example the required Java Version might not be available on your machine. In this case you can uninstall it + via the link "What is already installed" in the "Install New Software..." dialog and restart the IDE. Then you can reinstall + the latest version from "http://www.wudsn.com/update" or previously released versions from the locations listed in the + "Releases" chapter. +
    + IDE installation steps +
  • +
+
+ Installing compilers like ATASM, MADS, XASM...   + » top +
+
    +
  • Start Eclipse
  • +
  • Select the entry "Preferences" from the menu "Window" (in Windows and Linux) or the menu "Eclipse" (in Mac OS X)
  • +
  • Open the preferences page "Assembler/Atari 8-bit Compilers" or the respective page for your platform.
  • +
  • + Select the tab for the compiler of your choice. +
    + The following compilers are or will be supported: +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CompilerDefault Target Platform
    ACMEC64
    ASM6NES
    ATASMAtari 8-bit
    DASMAtari 2600
    KickAssC64
    MADSAtari 8-bit
    XASMAtari 8-bit
    TASSC64 (in preparation)
    +
  • +
  • Click the "Download"" link to open the home page of the compiler.
  • +
  • Follow the instruction on the download site for install the compiler to the folder of your choice.
  • +
  • + Alternatively, you can download the single + archive containing all supported 6502 + compilers + including the Linux and Mac OS X versions where available. The archive contains a readme file with the date of the last update + and the included compiler versions.  +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    CompilerOperating SystemArchitectureCompiler VersionExecutable PathCompile DateCompiled By
    ACMEWindowsIntel - 32 bit0.90ACME / acme.exe2006-03-19Krzysztof Dabrowski
    ASM6WindowsIntel - 32 bit1.6ASM6 / asm6.exe2011-03-11loopy
    ATASMWindowsIntel - 32 bit1.07 (non-final)ATASM / atasm.exe2010-05-10M. Schmelzenbach
    ATASMMac OS XIntel1.07 (non-final)ATASM / atasm.macosx-i3862015-05-01JAC!
    ATASMMac OS XPowerPC1.07 (non-final)ATASM / atasm.macosx-powerpc2015-05-01JAC!
    ATASMLinuxIntel - 32 bit1.07 (non-final)ATASM / atasm.linux-i3862015-05-01JAC!
    ATASMLinuxIntel - 64 bit1.07 (non-final)ATASM / atasm.linux-x86-642015-05-01JAC!
    DASMWindowsIntel - 32 bit2.20.11DASM / bin / dasm.exe2015-05-08JAC!
    DASMMac OS XIntel2.20.11DASM / bin / dasm.macosx-i3862015-05-01JAC!
    DASMMac OS XPowerPC2.20.11DASM / bin / dasm.macosx-powerpc2015-05-01JAC!
    DASMLinuxIntel 32-bit2.20.11-20140304DASM / bin / dasm.linux-i3862015-05-01JAC!
    DASMLinuxIntel 64-bit2.20.11-20140304DASM / bin / dasm.linux-x86-642015-05-01JAC!
    KickAssAllJavaV3.39KICKASS / KickAss.jar2015-03-26Mads Nielsen
    MADSWindowsIntel - 32 bit2.0.7 (new features since 1.9.0 are not fully supported in WUDSN IDE yet)MADS / mads.exe2014-05-20JAC!
    MADSMac OS XIntel2.0.7 (new features since 1.9.0 are not fully supported in WUDSN IDE yet)MADS / mads.macosx-i3862017-11-05JAC!
    MADSMac OS XPowerPC2.0.7 (new features since 1.9.0 are not fully supported in WUDSN IDE yet)MADS / mads.macosx-powerpc2017-11-05JAC!
    MADSLinuxIntel - 32 bit2.0.7 (new features since 1.9.0 are not fully supported in WUDSN IDE yet)MADS / mads.linux-i3862017-11-05JAC!
    TASSWindowsIntel - 32 bit1.46 r38TASS / 64tass.exe2011-07-03Soci
    XASMWindowsIntel - 32 bit3.1.0XASM / xasm.exe2014-07-200xF
    XASMLinuxIntel - 32 bit3.1.0XASM / xasm.linux-i3862014-07-200xF
    XASMMac OS XIntel - 32 bit3.1.0XASM / xasm.macosx-i3862014-07-200xF
    +
  • +
  • In the section "Browse..." button for the field "Path to Compiler" to locate the executable. Note that under Mac OS-X, you + must specify the path to the actual executable inside the ".app" folder. +
  • +
  • If not explicit compiler parameters are specified, the default parameters are used.
  • +
  • If explicit compiler parameters are specified, the default parameters are ignored.
  • +
  • + The variable "${sourceFilePath}" is replaced by the absolute path to the source file. +
    + The variable "${outputFilePath}" is replaced by the absolute path to the output file. +
    + For more variables see section + preferences for compiling. +
  • +
  • Choose if you want to use the source folder or the temporary folder as output folder.
  • +
  • Choose the file extension for the output file, for example ".xex" or ".bin"
  • +
  • Press the button "OK".
  • +
  • + Using the button "Restore Defaults" all values are reset, except for the paths to the compilers. +
    +
    + Configuration of compiler executable path +
  • +
  • + Using the button "Default" in the "File Associations" preferences you can set the default editor for a file extension, for + example "MADS" for "*.asm". +
    + IDE file associations +
  • +
+
+ Installing Atari800Win and other emulators   + » top +
+
    +
  • Start Eclipse.
  • +
  • Select the entry "Preferences" from the menu "Window" (for Windows and Linux) or "Eclipse" (for Mac OS X).
  • +
  • Open the preferences page "Assembler/Atari 8-bit Compilers" and select to tab for your compiler.
  • +
  • + The field "Default Application to open Output File" is defaulted to "Operating Sytstem Default Application". +
      +
    • If you have the emulator registered as default application for the extension of the output file, for example + "Atari800Win" or "Atari800MacX" for ".xex", you don't need to configure anything in addition and can skip the steps below. +
    • +
    • If your operating system, like for example Linux, does not support default applications or if you need a special output + file extension and special parameters to open the output file in the emulator you can choose the emulator in the field + "Application to open Output File" and configure the command line on the corresponding tab. +
    • +
    • You can also use the "User Defined Application" for open the output file with and arbitrary application like a script, + linker or whatever.
    • +
    +
  • +
  • If you have not yet downloaded the corresponding emulator, you can use the download link on the tab of the emulator and + follow the instruction on the download site for install the emulator
  • +
  • Use the "Browse..." button for the field "Path to Application" to locate the executable. If required you can specify your + own command line based on the default command line displayed. Note that under Mac OS-X, you must specify the path to the + actual executable inside the ".app" folder. +
  • +
  • If not explicit command line is specified, the default command line is used.
  • +
  • If an explicit command line is specified, the default command line is ignored.
  • +
  • + The variable "${runnerExecutablePath}" is replaced by the path to the application executable. +
    + The variable "${outputFilePath}" is replaced by the absolute path to the output file. +
    + For more variables see section + preferences for compiling. +
  • +
  • Press the button "OK"
  • +
  • + In case you also need the Atari ROM files, you can find them in the file + PCXF380.ZIP + which is available at + http://www.emulators.com +
  • +
  • + The patch to convert the English version into a German version including a German keyboard layout is available from + ABBUC + . It also contains the ROM files +
    + Configuration of emulator executable path +
  • +
+
+ Creating and compiling an example project   + » top +
+
    +
  • Start Eclipse.
  • +
  • Select the entry "New/Project" from the menu "File".
  • +
  • Select the wizard "General/Project" and press the button "Next".
  • +
  • Enter the project name "Atari800" and press the button "Finish".
  • +
  • The new project will appear in the "Project Explorer".
  • +
  • Select the newly created project and open its context menu.
  • +
  • Select the entry "New/File", enter the file name "Example.asm" and press the button "Finish".
  • +
  • The new empty file will now be opened in the MADS editor.
  • +
  • + Copy the following source text and paste it into the file: +
    +
    +
    + ; WUDSN IDE Atari Rainbow Example - MADS syntax +
    +
    +       org $4000 ;Start of code +
    +
    + start lda #0 ;Disable screen DMA +
    +       sta 559 +
    + loop  lda $d40b ;Load VCOUNT +
    +       clc +
    +       adc 20 ;Add counter +
    +       sta $d40a +
    +       sta $d01a ;Change background color +
    +       jmp loop +
    +
    +       run start ;Define run address +
    +
  • +
  • Select the entry "Compile and run" from the menu "Assembler" or press "SHIFT-CTRL-0".
  • +
  • Enjoy your famous first rainbow effect.
  • +
  • + In case of problems, open the view "Problems" and the output of the view "Console" for details and post a message with the + screenshots in the + English AtariAge forum + if you get stuck: +
    +
    + Congratulations, when you did everything correctly - this is your first rainbow effect +
  • +
+
+ Further information on assembler programming   + » top +
+ \ No newline at end of file diff --git a/com.wudsn.ide.asm/help/ide-releases.section.html b/com.wudsn.ide.asm/help/ide-releases.section.html new file mode 100644 index 00000000..87bbd698 --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-releases.section.html @@ -0,0 +1,201 @@ + +The following table lists all releases of WUDSN IDE, the required minimum Java and Eclipse version the link to the release news +article and release new video. For releases before 1.6.0 no downloads and no videos are offered. The latest version is always +available via the update site "http://www.wudsn.com/update". Older versions are available via version specific update sites +listed below. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WUDSN IDE VersionRelease DateMinimum Java VersionMinimum Eclipse VersionUpdate Site URLRelease News ArticleRelease News Video
1.6.62014-06-11Java 1.64.3.1http://www.wudsn.com/update/1.6.6 + read + planned
1.6.52014-01-02Java 1.74.3.1http://www.wudsn.com/update/1.6.5 + read + planned
1.6.42013-09-13Java 1.63.6.0http://www.wudsn.com/update/1.6.4 + read + planned
1.6.32012-06-09Java 1.63.6.0http://www.wudsn.com/update/1.6.3 + read + planned
1.6.22012-05-16Java 1.63.6.0http://www.wudsn.com/update/1.6.2 + read + + watch +
1.6.12012-05-12Java 1.63.6.0not available + read + + watch +
1.6.02011-04-02Java 1.53.6.0http://www.wudsn.com/update/1.6.0 + read + + watch +
1.5.02010-10-27Java 1.53.6.0not available + read + not available
1.4.42010-09-01Java 1.53.3.0not available + read + not available
1.4.32010-04-28Java 1.53.3.0not available + read + not available
1.4.22009-10-11Java 1.53.3.0not available + read + not available
1.4.02009-09-13Java 1.53.3.0not available + read + not available
1.3.22009-07-26Java 1.53.3.0not available + read + not available
1.2.02009-07-12Java 1.53.3.0not available + read + not available
1.1.02009-06-28Java 1.53.3.0not available + read + not available
1.0.02009-06-08Java 1.53.3.0not available + read + not available
\ No newline at end of file diff --git a/com.wudsn.ide.asm/help/ide-tutorials-videos.html b/com.wudsn.ide.asm/help/ide-tutorials-videos.html new file mode 100644 index 00000000..aef0fe15 --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-tutorials-videos.html @@ -0,0 +1,42 @@ + +Cheat sheet for creating video screen shots - Do not format the source! + +
+
+ + + + + \ No newline at end of file diff --git a/com.wudsn.ide.asm/help/ide-tutorials.section.html b/com.wudsn.ide.asm/help/ide-tutorials.section.html new file mode 100644 index 00000000..5ea869e5 --- /dev/null +++ b/com.wudsn.ide.asm/help/ide-tutorials.section.html @@ -0,0 +1,85 @@ + +When I was thinking about creating tutorials, I decided not to simply write text but to create short videos instead. +They are best viewed in full screen mode and in HD video resolution. +I think this is the best way to show how things are intended to be +used. On the other hand, these tutorials cannot +tackle every detail of a supported feature. So please also check the features section. +
+
+ + + + + + + + + + + + + + + + + + + + + +
+ Part 1: Introduction, Installation and Use +
+ + Tutorial part 1 + +
+ Part 2: Setting up Perspective, Views and Editors +
+ + Tutorial part 2 + +
+ Part 3: Setting up Editors and File Extensions correctly +
+ + Tutorial part 3 + +
+ Part 4: Syntax highlighting and Content Assist +
+ + Tutorial part 4 + +
+ Part 5: Working with Projects, Folders and Files +
+ + Tutorial part 5 + +
+ Part 6: Content Outline and Navigation - the Heart of the IDE +
+ + Tutorial part 6 + +
+ Part 7: New Features in version 1.6.0 +
+ + Tutorial part 7 + +
+ Part 8: New Features in version 1.6.2 +
+ + Tutorial part 8 + +
+ Part 9: Source Level Debugging +
+ + Tutorial part 9 + +
+
diff --git a/com.wudsn.ide.asm/help/productions/java/.gitignore b/com.wudsn.ide.asm/help/productions/java/.gitignore new file mode 100644 index 00000000..92ae9f65 --- /dev/null +++ b/com.wudsn.ide.asm/help/productions/java/.gitignore @@ -0,0 +1 @@ +/ide diff --git a/com.wudsn.ide.asm/help/productions/java/ide.txt b/com.wudsn.ide.asm/help/productions/java/ide.txt new file mode 100644 index 00000000..daec6b32 --- /dev/null +++ b/com.wudsn.ide.asm/help/productions/java/ide.txt @@ -0,0 +1 @@ +This "ide" folder is only symbolic link. \ No newline at end of file diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes.html new file mode 100644 index 00000000..3017746b --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes.html @@ -0,0 +1,24 @@ + + + + + + + +65xx processor series opcodes + + + + +
Welcome to the 65xx resources.

+
Here you will find detailed information about some processors of the 65xx series. Especially the 6502 +document provides a deep look into the internal behaviour of the processor. Also you will find an illegal opcode list +which has been validated for being 100% several times now. The other documents only extend the 6502 document with +additional information. They do not completely describe all opcodes.

+6502/6510/8500/8502 Opcodes

+65C02/65SC02 Opcodes

+65CE02 Opcodes

+65816 Opcodes

+
Main page

+Last change on 30/1/2009. For comments contact Graham
+ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes02.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes02.html new file mode 100644 index 00000000..3d074aa6 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes02.html @@ -0,0 +1,1124 @@ + + + + + + +6502/6510/8500/8502 Opcodes + + + + +

6502/6510/8500/8502 Opcode matrix:

+ +imm = #$00
+zp = $00
+zpx = $00,X
+zpy = $00,Y
+izx = ($00,X)
+izy = ($00),Y
+abs = $0000
+abx = $0000,X
+aby = $0000,Y
+ind = ($0000)
+rel = $0000 (PC-relative) +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 x0x1x2x3x4x5x6x7x8x9xAxBxCxDxExF
0xBRK
7
ORA
izx 6
KILSLO
izx 8
NOP
zp 3
ORA
zp 3
ASL
zp 5
SLO
zp 5
PHP
3
ORA
imm 2
ASL
2
ANC
imm 2
NOP
abs 4
ORA
abs 4
ASL
abs 6
SLO
abs 6
1xBPL
rel 2*
ORA
izy 5*
KILSLO
izy 8
NOP
zpx 4
ORA
zpx 4
ASL
zpx 6
SLO
zpx 6
CLC
2
ORA
aby 4*
NOP
2
SLO
aby 7
NOP
abx 4*
ORA
abx 4*
ASL
abx 7
SLO
abx 7
2xJSR
abs 6
AND
izx 6
KILRLA
izx 8
BIT
zp 3
AND
zp 3
ROL
zp 5
RLA
zp 5
PLP
4
AND
imm 2
ROL
2
ANC
imm 2
BIT
abs 4
AND
abs 4
ROL
abs 6
RLA
abs 6
3xBMI
rel 2*
AND
izy 5*
KILRLA
izy 8
NOP
zpx 4
AND
zpx 4
ROL
zpx 6
RLA
zpx 6
SEC
2
AND
aby 4*
NOP
2
RLA
aby 7
NOP
abx 4*
AND
abx 4*
ROL
abx 7
RLA
abx 7
4xRTI
6
EOR
izx 6
KILSRE
izx 8
NOP
zp 3
EOR
zp 3
LSR
zp 5
SRE
zp 5
PHA
3
EOR
imm 2
LSR
2
ALR
imm 2
JMP
abs 3
EOR
abs 4
LSR
abs 6
SRE
abs 6
5xBVC
rel 2*
EOR
izy 5*
KILSRE
izy 8
NOP
zpx 4
EOR
zpx 4
LSR
zpx 6
SRE
zpx 6
CLI
2
EOR
aby 4*
NOP
2
SRE
aby 7
NOP
abx 4*
EOR
abx 4*
LSR
abx 7
SRE
abx 7
6xRTS
6
ADC
izx 6
KILRRA
izx 8
NOP
zp 3
ADC
zp 3
ROR
zp 5
RRA
zp 5
PLA
4
ADC
imm 2
ROR
2
ARR
imm 2
JMP
ind 5
ADC
abs 4
ROR
abs 6
RRA
abs 6
7xBVS
rel 2*
ADC
izy 5*
KILRRA
izy 8
NOP
zpx 4
ADC
zpx 4
ROR
zpx 6
RRA
zpx 6
SEI
2
ADC
aby 4*
NOP
2
RRA
aby 7
NOP
abx 4*
ADC
abx 4*
ROR
abx 7
RRA
abx 7
8xNOP
imm 2
STA
izx 6
NOP
imm 2
SAX
izx 6
STY
zp 3
STA
zp 3
STX
zp 3
SAX
zp 3
DEY
2
NOP
imm 2
TXA
2
XAA
imm 2
STY
abs 4
STA
abs 4
STX
abs 4
SAX
abs 4
9xBCC
rel 2*
STA
izy 6
KILAHX
izy 6
STY
zpx 4
STA
zpx 4
STX
zpy 4
SAX
zpy 4
TYA
2
STA
aby 5
TXS
2
TAS
aby 5
SHY
abx 5
STA
abx 5
SHX
aby 5
AHX
aby 5
AxLDY
imm 2
LDA
izx 6
LDX
imm 2
LAX
izx 6
LDY
zp 3
LDA
zp 3
LDX
zp 3
LAX
zp 3
TAY
2
LDA
imm 2
TAX
2
LAX
imm 2
LDY
abs 4
LDA
abs 4
LDX
abs 4
LAX
abs 4
BxBCS
rel 2*
LDA
izy 5*
KILLAX
izy 5*
LDY
zpx 4
LDA
zpx 4
LDX
zpy 4
LAX
zpy 4
CLV
2
LDA
aby 4*
TSX
2
LAS
aby 4*
LDY
abx 4*
LDA
abx 4*
LDX
aby 4*
LAX
aby 4*
CxCPY
imm 2
CMP
izx 6
NOP
imm 2
DCP
izx 8
CPY
zp 3
CMP
zp 3
DEC
zp 5
DCP
zp 5
INY
2
CMP
imm 2
DEX
2
AXS
imm 2
CPY
abs 4
CMP
abs 4
DEC
abs 6
DCP
abs 6
DxBNE
rel 2*
CMP
izy 5*
KILDCP
izy 8
NOP
zpx 4
CMP
zpx 4
DEC
zpx 6
DCP
zpx 6
CLD
2
CMP
aby 4*
NOP
2
DCP
aby 7
NOP
abx 4*
CMP
abx 4*
DEC
abx 7
DCP
abx 7
ExCPX
imm 2
SBC
izx 6
NOP
imm 2
ISC
izx 8
CPX
zp 3
SBC
zp 3
INC
zp 5
ISC
zp 5
INX
2
SBC
imm 2
NOP
2
SBC
imm 2
CPX
abs 4
SBC
abs 4
INC
abs 6
ISC
abs 6
FxBEQ
rel 2*
SBC
izy 5*
KILISC
izy 8
NOP
zpx 4
SBC
zpx 4
INC
zpx 6
ISC
zpx 6
SED
2
SBC
aby 4*
NOP
2
ISC
aby 7
NOP
abx 4*
SBC
abx 4*
INC
abx 7
ISC
abx 7
+ +
+"*" : add 1 cycle if page boundary is crossed.
+add 1 cycle on branches if taken.
+
+
+Logical and arithmetic commands: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeimpimmzpzpxzpyizxizyabsabxabyindrelFunctionNVBDIZC
ORA $09$05$15 $01$11$0D$1D$19  A:=A or {adr}*    * 
AND $29$25$35 $21$31$2D$3D$39  A:=A&{adr}*    * 
EOR $49$45$55 $41$51$4D$5D$59  A:=A exor {adr}*    * 
ADC $69$65$75 $61$71$6D$7D$79  A:=A+{adr}**   **
SBC $E9$E5$F5 $E1$F1$ED$FD$F9  A:=A-{adr}**   **
CMP $C9$C5$D5 $C1$D1$CD$DD$D9  A-{adr}*    **
CPX $E0$E4    $EC    X-{adr}*    **
CPY $C0$C4    $CC    Y-{adr}*    **
DEC  $C6$D6   $CE$DE   {adr}:={adr}-1*    * 
DEX$CA           X:=X-1*    * 
DEY$88           Y:=Y-1*    * 
INC  $E6$F6   $EE$FE   {adr}:={adr}+1*    * 
INX$E8           X:=X+1*    * 
INY$C8           Y:=Y+1*    * 
ASL$0A $06$16   $0E$1E   {adr}:={adr}*2*    **
ROL$2A $26$36   $2E$3E   {adr}:={adr}*2+C*    **
LSR$4A $46$56   $4E$5E   {adr}:={adr}/2*    **
ROR$6A $66$76   $6E$7E   {adr}:={adr}/2+C*128*    **
+ +

+Move commands: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeimpimmzpzpxzpyizxizyabsabxabyindrelFunctionNVBDIZC
LDA $A9$A5$B5 $A1$B1$AD$BD$B9  A:={adr}*    * 
STA  $85$95 $81$91$8D$9D$99  {adr}:=A       
LDX $A2$A6 $B6  $AE $BE  X:={adr}*    * 
STX  $86 $96  $8E    {adr}:=X       
LDY $A0$A4$B4   $AC$BC   Y:={adr}*    * 
STY  $84$94   $8C    {adr}:=Y       
TAX$AA           X:=A*    * 
TXA$8A           A:=X*    * 
TAY$A8           Y:=A*    * 
TYA$98           A:=Y*    * 
TSX$BA           X:=S*    * 
TXS$9A           S:=X       
PLA$68           A:=+(S)*    * 
PHA$48           (S)-:=A       
PLP$28           P:=+(S)** ****
PHP$08           (S)-:=P       
+ +

+Jump/Flag commands: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeimpimmzpzpxzpyizxizyabsabxabyindrelFunctionNVBDIZC
BPL           $10branch on N=0       
BMI           $30branch on N=1       
BVC           $50branch on V=0       
BVS           $70branch on V=1       
BCC           $90branch on C=0       
BCS           $B0branch on C=1       
BNE           $D0branch on Z=0       
BEQ           $F0branch on Z=1       
BRK$00           (S)-=:PC,P PC:=($FFFE)  1 1  
RTI$40           P,PC:=+(S)** ****
JSR       $20    (S)-:=PC PC:={adr}       
RTS$60           PC:=+(S)       
JMP       $4C  $6C PC:={adr}       
BIT  $24    $2C    N:=b7 V:=b6 Z:=A&{adr}**   * 
CLC$18           C:=0      0
SEC$38           C:=1      1
CLD$D8           D:=0   0   
SED$F8           D:=1   1   
CLI$58           I:=0    0  
SEI$78           I:=1    1  
CLV$B8           V:=0 0     
NOP$EA                   
+ +

+Flags of the status register:
+
+The processor status register has 8 bits, where 7 are used as flags:
+
+N = negative flag (1 when result is negative)
+V = overflow flag (1 on signed overflow)
+# = unused (always 1)
+B = break flag (1 when interupt was caused by a BRK)
+D = decimal flag (1 when CPU in BCD mode)
+I = IRQ flag (when 1, no interupts will occur (exceptions are IRQs forced by BRK and NMIs))
+Z = zero flag (1 when all bits of a result are 0)
+C = carry flag (1 on unsigned overflow)
+

+Hardware vectors:
+
+$FFFA = NMI vector (NMI=not maskable interupts)
+$FFFC = Reset vector
+$FFFE = IRQ vector
+

+Illegal opcodes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeimpimmzpzpxzpyizxizyabsabxabyindrelFunctionNVBDIZC
SLO  $07$17 $03$13$0F$1F$1B  {adr}:={adr}*2 A:=A or {adr}*    **
RLA  $27$37 $23$33$2F$3F$3B  {adr}:={adr}rol A:=A and {adr}*    **
SRE  $47$57 $43$53$4F$5F$5B  {adr}:={adr}/2 A:=A exor {adr}*    **
RRA  $67$77 $63$73$6F$7F$7B  {adr}:={adr}ror A:=A adc {adr}**   **
SAX  $87 $97$83 $8F    {adr}:=A&X       
LAX  $A7 $B7$A3$B3$AF $BF  A,X:={adr}*    * 
DCP  $C7$D7 $C3$D3$CF$DF$DB  {adr}:={adr}-1 A-{adr}*    **
ISC  $E7$F7 $E3$F3$EF$FF$FB  {adr}:={adr}+1 A:=A-{adr}**   **
ANC $0B          A:=A&#{imm}*    **
ANC $2B          A:=A&#{imm}*    **
ALR $4B          A:=(A&#{imm})/2*    **
ARR $6B          A:=(A&#{imm})/2**   **
XAA² $8B          A:=X&#{imm}*    * 
LAX² $AB          A,X:=#{imm}*    * 
AXS $CB          X:=A&X-#{imm}*    **
SBC $EB          A:=A-#{imm}**   **
AHX¹      $93  $9F  {adr}:=A&X&H       
SHY¹        $9C   {adr}:=Y&H       
SHX¹         $9E  {adr}:=X&H       
TAS¹         $9B  S:=A&X {adr}:=S&H       
LAS         $BB  A,X,S:={adr}&S*    * 
+ +

+¹ = unstable in certain matters
+² = highly unstable (results are not predictable on some machines)
+A = Akkumulator
+X = X-Register
+Y = Y-Register
+S = Stack-Pointer
+P = Status-Register
++(S) = Stack-Pointer relative with pre-increment
+(S)- = Stack-Pointer relative with post-decrement

+

+Combinations of two operations with the same addressing mode:
+
+SLO {adr} = ASL {adr} + ORA {adr}
+RLA {adr} = ROL {adr} + AND {adr}
+SRE {adr} = LSR {adr} + EOR {adr}
+RRA {adr} = ROR {adr} + ADC {adr}
+SAX {adr} = store A&X into {adr}
+LAX {adr} = LDA {adr} + LDX {adr}
+DCP {adr} = DEC {adr} + CMP {adr}
+ISC {adr} = INC {adr} + SBC {adr}

+
+note to SAX: the A&X operation is a result of A and X put onto the bus at the same time.
+

+Combinations of an immediate and an implied command:
+
+ANC #{imm} = AND #{imm} + (ASL)
+ANC #{imm} = AND #{imm} + (ROL)
+ALR #{imm} = AND #{imm} + LSR
+ARR #{imm} = AND #{imm} + ROR
+XAA #{imm} = TXA + AND #{imm}
+LAX #{imm} = LDA #{imm} + TAX
+AXS #{imm} = A&X minus #{imm} into X
+SBC #{imm} = SBC #{imm} + NOP

+
+note to ANC: this command performs an AND operation only, but bit 7 is put into the carry, as if the ASL/ROL would have been executed.
+note to ARR: part of this command are some ADC mechanisms. following effects appear after AND but before ROR: +the V-Flag is set according to (A and #{imm})+#{imm}, bit 0 does NOT go into carry, but bit 7 is exchanged with the carry.
+note to XAA: DO NOT USE!!! Highly unstable!!!
+note to LAX: DO NOT USE!!! On my C128, this opcode is stable, but on my C64-II it loses bits so that the operation +looks like this: ORA #? AND #{imm} TAX.
+note to AXS: performs CMP and DEX at the same time, so that the MINUS sets the flag like CMP, not SBC.
+

+Combinations of STA/STX/STY:
+
+AHX {adr} = stores A&X&H into {adr}
+SHX {adr} = stores X&H into {adr}
+SHY {adr} = stores Y&H into {adr}

+
+note: sometimes the &H drops off. Also page boundary crossing will not work as expected (the bank where the value is stored may be equal to the value stored).
+

+Combinations of STA/TXS and LDA/TSX:
+
+TAS {adr} = stores A&X into S and A&X&H into {adr}
+LAS {adr} = stores {adr}&S into A, X and S

+
+note to LAS: is called as "propably unreliable" in one source.
+

+Bit configuration does not allow any operation on these ones:
+
+NOP = has no effects
+NOP #{imm} = fetches #{imm} but has no effects
+NOP {adr} = fetches {adr} but has no effects
+

+KIL = halts the CPU. the data bus will be set to #$FF

+
+
+Aliases used in other illegal opcode sources:
+
+SLO = ASO
+SRE = LSE
+ISC = ISB
+ALR = ASR
+SHX = A11 (A11 was a result of only having tested this one on adress $1000)
+SHY = A11
+LAS = LAR
+KIL = JAM, HLT
+
+
+The 6502 bugs:
+
+Zeropage index will not leave zeropage when page boundary is crossed:
+
+LDX #$01
+LDA $FF,X

+
+...will fetch from adress $0000 and not $0100 as indexed.
+

+Indirect adressing modes are not able to fetch an adress which crosses the page boundary:
+
+Four examples to illustrate this:
+
+LDA ($FF),Y
+
+LDX #$00
+LDA ($FF,X)
+
+LDX #$FF
+LDA ($00,X)

+
+... will all fetch the low-byte from $00FF and the high-byte from $0000
+
+JMP ($12FF)
+
+... will fetch the low-byte from $12FF and the high-byte from $1200
+

+The N, V and Z flags do not work correctly in BCD mode:
+
+N will always carry bit 7.
+V will always be ((U eor N) nand (U eor V)) (while U is bit 7 of operand 1, V is bit 7 of operand 2 and N is the N flag after the ADC is performed).
+please note that SBC is truly ADC with an inverted operand!
+Z will be 0 when the non-BCD operation WOULD have resulted in $00, no matter what value the result of the BCD operation is.
+
+example to Z:
+
+SED
+CLC
+LDA #$80
+ADC #$80

+
+... results in A=$60, but the Z flag is 1.
+

+BCD and non BCD values:
+
+Since only nibble values from 0 to 9 are valid in BCD, it's interesting to see what happens when using A to F:
+
+$00+$0F=$15 (an easy way to convert a hex-digit into BCD...)
+$00+$1F=$25 (can be claimed as being "ok" since 10+$0F=25)
+$10+$1F=$35 ("ok")
+$05+$1F=$2A (a non-BCD result, still somewhat "ok" since 5+10+$0F=20+$0A)
+$0F+$0A=$1F ("ok", since $0F+$0A=$0F+10)
+$0F+$0B=$10 (now, this is plain bullshit!)

+

+ +Different versions of the 6502:
+
+In the C64/C128 series of computers, slightly modified versions of the 6502 +were used. The modifications did not affect the functional part of the processor +itself. Only a so-called processor port was added. +This port, in combination with an external PLA, was used to map ROM and I/O areas into the 64KB RAM of the C64. +Also, some bits of the port were used for the legendary Datasette.
+
+The port can be accessed through memory adresses $0000 and $0001, while $0001 +is the port itself, and $0000 is the data direction register for it.
+
+Explanation for the bits of $0001:
+
+7 - unused (Flash 8: 0=8MHz/1=1MHz)
+6 - unused (C128: ASCII/DIN sense/switch (1=ASCII/0=DIN))
+5 - Cassette motor control (0 = motor on)
+4 - Cassette switch sense (0 = PLAY pressed)
+3 - Cassette write line
+2 - CHAREN (0=Character ROM instead of I/O area)
+1 - HIRAM ($E000-$FFFF)
+0 - LORAM ($A000-$BFFF)
+
+If HIRAM or LORAM is set, the I/O area is mapped to $D000-$DFFF.
+
+$0000 should always be set to $2F (%00101111)
+
+Note to bit 6: This bit is used to select either the ASCII or the DIN +character ROM of a C128. When data direction is set to INPUT, the charset +is selected externally with the ASCII/DIN key.
+
+CPU versions:
+
+6502: NMOS, used in Commodore disk drives, PET, various other 8 bit computers
+6502C: 6502 with additional HALT pin, used in Atari 8 bit computer range
+6510: 6502 with additional processor port, used in C64
+8500: CMOS version of the 6510, used in C64C and C64G
+8502: 2 MHz version of the 8500, used in C128
+7501: HMOS-1 version of the 6502, used in C16/C116/Plus4
+8501: HMOS-2 version of the 6502, used in C16/C116/Plus4
+
+All of these processors are the same concerning the software-side.
+
+Some processors of the family which are not 100% compatible:
+
+65C02: Extension of the 6502.
+65SC02: Small version of the 65C02 which lost a few opcodes again.
+65CE02: Extension of the 65C02, used in the C65.
+65816: Extended 6502 with new opcodes and 16 bit operation modes.
+

+Zeropage/Stack:
+
+The first 256 bytes of adressable memory are called Zeropage. The 6502 +processor family offers a wide selection of adressing modes to work with +this part of the memory, which generally results in shorter and (even more +important) faster code.
+
+Following the Zeropage, the next 256 bytes (located at $0100-$01FF) are +used as processor stack. The stack function of this part is defined as +it is in most other CPU's: Writing to stack will automatically decrement +the stack pointer, while reading from it will increment it.
+

© 2002-2010 Graham. Last change on 15.12.2010. + + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes816.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes816.html new file mode 100644 index 00000000..51d3a8a5 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodes816.html @@ -0,0 +1,562 @@ + +65816 Opcodes + + +65816 Opcode matrix: + + +

+imm = #$00
+sr = $00,S
+dp = $00
+dpx = $00,X
+dpy = $00,Y
+idp = ($00)
+idx = ($00,X)
+idy = ($00),Y
+idl = [$00]
+idly = [$00],Y
+isy = ($00,S),Y
+abs = $0000
+abx = $0000,X
+aby = $0000,Y
+abl = $000000
+alx = $000000,X
+ind = ($0000)
+iax = ($0000,X)
+ial = [$000000]
+rel = $0000 (8 bits PC-relative)
+rell = $0000 (16 bits PC-relative)
+bm = $00,$00
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
x0x1x2x3x4x5x6x7x8x9xAxBxCxDxExF
0xBRK
7
ORA
idx 6
COP
imm 7
ORA
sr 4
TSB
dp 5
ORA
dp 3
ASL
dp 5
ORA
idl 6
PHP
3
ORA
imm 2
ASL
2
PHD
4
TSB
abs 6
ORA
abs 4
ASL
abs 6
ORA
abl 5
1xBPL
rel 2*
ORA
idy 5*
ORA
idp 5
ORA
isy 7
TRB
dp 5
ORA
dpx 4
ASL
dpx 6
ORA
idly 6
CLC
2
ORA
aby 4*
INC
2
TCS
2
TRB
abs 6
ORA
abx 4*
ASL
abx 7
ORA
alx 4
2xJSR
abs 6
AND
idx 6
JSR
abl 8
AND
sr 4
BIT
dp 3
AND
dp 3
ROL
dp 5
AND
idl 6
PLP
4
AND
imm 2
ROL
2
PLD
5
BIT
abs 4
AND
abs 4
ROL
abs 6
AND
abl 5
3xBMI
rel 2*
AND
idy 5*
AND
idp 5
AND
isy 7
BIT
dpx 4
AND
dpx 4
ROL
dpx 6
AND
idly 6
SEC
2
AND
aby 4*
DEC
2
TSC
2
BIT
abx 4*
AND
abx 4*
ROL
abx 7
AND
alx 5
4xRTI
6
EOR
idx 6
WDM
n/a
EOR
sr 4
MVP
bm 1#
EOR
dp 3
LSR
dp 5
EOR
idl 6
PHA
3
EOR
imm 2
LSR
2
PHK
3
JMP
abs 3
EOR
abs 4
LSR
abs 6
EOR
abl 5
5xBVC
rel 2*
EOR
idy 5*
EOR
idp 5
EOR
isy 6
MVN
bm 1#
EOR
dpx 4
LSR
dpx 6
EOR
idly 6
CLI
2
EOR
aby 4*
PHY
3
TCD
2
JMP
abl 4
EOR
abx 4*
LSR
abx 7
EOR
alx 5
6xRTS
6
ADC
idx 6
PER
rell 6
ADC
sr 4
STZ
dp 3
ADC
dp 3
ROR
zp 5
ADC
idl 6
PLA
4
ADC
imm 2
ROR
2
RTL
6
JMP
ind 5
ADC
abs 4
ROR
abs 6
ADC
abl 5
7xBVS
rel 2*
ADC
idy 5*
ADC
idp 5
ADC
isy 7
STZ
dpx 4
ADC
dpx 4
ROR
zpx 6
ADC
idly 6
SEI
2
ADC
aby 4*
PLY
4
TDC
2
JMP
ial 6
ADC
abx 4*
ROR
abx 7
ADC
alx 5
8xBRA
rel 3*
STA
idx 6
BRL
rell 4
STA
sr 4
STY
dp 3
STA
dp 3
STX
dp 3
STA
idl 6
DEY
2
BIT
imm 2
TXA
2
PHB
3
STY
abs 4
STA
abs 4
STX
abs 4
STA
abl 5
9xBCC
rel 2*
STA
idy 6
STA
idp 5
STA
isy 7
STY
dpx 4
STA
dpx 4
STX
dpy 4
STA
idly 6
TYA
2
STA
aby 5
TXS
2
TXY
2
STZ
abs 4
STA
abx 5
STZ
abx 5
STA
alx 5
AxLDY
imm 2
LDA
idx 6
LDX
imm 2
LDA
sr 4
LDY
dp 3
LDA
dp 3
LDX
dp 3
LDA
idl 6
TAY
2
LDA
imm 2
TAX
2
PLB
4
LDY
abs 4
LDA
abs 4
LDX
abs 4
LDA
abl 5
BxBCS
rel 2*
LDA
idy 5*
LDA
idp 5
LDA
isy 7
LDY
dpx 4
LDA
dpx 4
LDX
dpy 4
LDA
idly 6
CLV
2
LDA
aby 4*
TSX
2
TYX
2
LDY
abx 4*
LDA
abx 4*
LDX
aby 4*
LDA
alx 5
CxCPY
imm 2
CMP
idx 6
REP
imm 3
CMP
sr 4
CPY
dp 3
CMP
dp 3
DEC
dp 5
CMP
idl 6
INY
2
CMP
imm 2
DEX
2
WAI
3
CPY
abs 4
CMP
abs 4
DEC
abs 6
CMP
abl 5
DxBNE
rel 2*
CMP
idy 5*
CMP
idp 5
CMP
isy 7
PEI
idp 6
CMP
dpx 4
DEC
dpx 6
CMP
idly 6
CLD
2
CMP
aby 4*
PHX
3
STP
3
JMP
iax 6
CMP
abx 4*
DEC
abx 7
CMP
alx 5
ExCPX
imm 2
SBC
idx 6
SEP
imm 3
SBC
sr 4
CPX
dp 3
SBC
dp 3
INC
dp 5
SBC
idl 6
INX
2
SBC
imm 2
NOP
2
XBA
3
CPX
abs 4
SBC
abs 4
INC
abs 6
SBC
abl 5
FxBEQ
rel 2*
SBC
idy 5*
SBC
idp 5
SBC
isy 7
PEA
abs 5
SBC
dpx 4
INC
dpx 6
SBC
idly 6
SED
2
SBC
aby 4*
PLX
4
XCE
2
JSR
iax 8
SBC
abx 4*
INC
abx 7
SBC
alx 5
+
+"*" : add 1 cycle if page boundary is crossed.
+"#" : add 7 cycles for every byte moved and 1 cycle if page boundary is crossed.
+add 1 cycle if m=0: ADC, AND, BIT, CMP, EOR, LDA, ORA, PHA, PLA, SBC, STA, STZ
+add 2 cycles if m=0 (NOT the implied ones): ASL, DEC, INC, LSR, ROL, ROR, TRB, TSB
+add 1 cycle if x=0: CPX, CPY, LDX, LDY, STX, STY, PLX, PLY, PHX, PHY
+add 1 cycle if e=0: BRK, RTI
+add 1 cycle if direct page register is non zero on direct page adressing modes.
+add 1 cycle on conditional branches if taken.
+native 65816 only: branches do not take one additional cycle when page boundary is crossed.
+
+
+A = Akkumulator
+B = Akkumulator, upper 8 bits
+C = Akkumulator (but always 16 bit not depending on M)
+X = X-Register
+Y = Y-Register
+S = Stack-Pointer
+P = Status-Register
++(S) = Stack-Pointer relative with pre-increment
+(S)- = Stack-Pointer relative with post-decrement
+DBR = Data Bank Register (all data movements with 16 bit adresses will refer to this bank)
+DPR = Direct Page Register (all direct page accesses will use this as adress base)
+PBR = Program Bank Register (the bank the actual code is executed)

+
+Please note that 3 new flags have been added to the P: E, M and X.
+
+While E=1 the 65816 is in 6502 emulation mode and will act like a 6502 in all legal matters.
+DBR, DPR and PBR are still active! Take care: irq's will force PBR=0 without saving the PBR!
+While E=0 the 65816 is in native mode.
+While M=1 the Akku is 8 bits wide.
+While M=0 the Akku is 16 bits wide.
+While X=1 the X and Y registers are 8 bits wide.
+While X=0 the X and Y registers are 16 bits wide.
+
+Also note that the P has virtually lost the B flag in some matters.
+
+
+65816 opcodes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeFunctionENVMXDIZC
BRAbranch always         
BRLbranch always (long)         
COPcoprocessor enable     **  
MVNmove block backward         
MVPmove block forward         
PEA(S)-:=adr         
PEI(S)-:=adr         
PER(S)-:=adr         
PHB(S)-:=DBR         
PHD(S)-:=DPR         
PHK(S)-:=PBR         
PHX(S)-:=X         
PHY(S)-:=Y         
PLBDBR:=+(S) *     * 
PLDDPR:=+(S) *     * 
PLXX:=+(S) *     * 
PLYY:=+(S) *     * 
REPP:=P nand #{imm} ????????
SEPP:=P or #{imm} ????????
RTLPC:=+(S) (long)         
STPstop CPU         
WAIwait for IRQ         
STZ{adr}:=0         
TCDDPR:=C         
TDCC:=DPR *     * 
TCSS:=C         
TSCC:=S *     * 
TXYY:=X *     * 
TYXX:=Y *     * 
XBAexchange B with A *     * 
XCEexchange C with E*       *
TRB{adr}:={adr} nand A       * 
TSB{adr}:={adr} or A       * 
WDMn/a         
+

+note to STP, WAI: these opcodes need 3 cycles to shut down the CPU.
+
+ + + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodesc02.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodesc02.html new file mode 100644 index 00000000..c3a09d70 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodesc02.html @@ -0,0 +1,492 @@ + + + + + + +65C02 Opcodes + + + + +

65C02 Opcode matrix:

+ +imm = #$00
+zp = $00
+zpx = $00,X
+zpy = $00,Y
+izp = ($00)
+izx = ($00,X)
+izy = ($00),Y
+abs = $0000
+abx = $0000,X
+aby = $0000,Y
+ind = ($0000)
+iax = ($0000,X)
+rel = $0000 (PC-relative) +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 x0x1x2x3x4x5x6x7x8x9xAxBxCxDxExF
0xBRK
7
ORA
izx 6
NOP
imm 2
NOPTSB
zp 5
ORA
zp 3
ASL
zp 5
RMB0
zp 5
PHP
3
ORA
imm 2
ASL
2
NOPTSB
abs 6
ORA
abs 4
ASL
abs 6
BBR0
rel 2*
1xBPL
rel 2*
ORA
izy 5*
ORA
izp 5
NOPTRB
zp 5
ORA
zpx 4
ASL
zpx 6
RMB1
zp 5
CLC
2
ORA
aby 4*
INC
2
NOPTRB
abs 6
ORA
abx 4*
ASL
abx 6*
BBR1
rel 2*
2xJSR
abs 6
AND
izx 6
NOP
imm 2
NOPBIT
zp 3
AND
zp 3
ROL
zp 5
RMB2
zp 5
PLP
4
AND
imm 2
ROL
2
NOPBIT
abs 4
AND
abs 4
ROL
abs 6
BBR2
rel 2*
3xBMI
rel 2*
AND
izy 5*
AND
izp 5
NOPBIT
zpx 4
AND
zpx 4
ROL
zpx 6
RMB3
zp 5
SEC
2
AND
aby 4*
DEC
2
NOPBIT
abx 4*
AND
abx 4*
ROL
abx 6*
BBR3
rel 2*
4xRTI
6
EOR
izx 6
NOP
imm 2
NOPNOP
zp 3
EOR
zp 3
LSR
zp 5
RMB4
zp 5
PHA
3
EOR
imm 2
LSR
2
NOPJMP
abs 3
EOR
abs 4
LSR
abs 6
BBR4
rel 2*
5xBVC
rel 2*
EOR
izy 5*
EOR
izp 5
NOPNOP
zpx 4
EOR
zpx 4
LSR
zpx 6
RMB5
zp 5
CLI
2
EOR
aby 4*
PHY
3
NOPNOPEOR
abx 4*
LSR
abx 6*
BBR5
rel 2*
6xRTS
6
ADC
izx 6
NOP
imm 2
NOPSTZ
zp 3
ADC
zp 3
ROR
zp 5
RMB6
zp 5
PLA
4
ADC
imm 2
ROR
2
NOPJMP
ind 6
ADC
abs 4
ROR
abs 6
BBR6
rel 2*
7xBVS
rel 2*
ADC
izy 5*
ADC
izp 5
NOPSTZ
zpx 4
ADC
zpx 4
ROR
zpx 6
RMB7
zp 5
SEI
2
ADC
aby 4*
PLY
4
NOPJMP
iax 6
ADC
abx 4*
ROR
abx 6*
BBR7
rel 2*
8xBRA
rel 3*
STA
izx 6
NOP
imm 2
NOPSTY
zp 3
STA
zp 3
STX
zp 3
SMB0
zp 5
DEY
2
BIT
imm 2
TXA
2
NOPSTY
abs 4
STA
abs 4
STX
abs 4
BBS0
rel 2*
9xBCC
rel 2*
STA
izy 6
STA
izp 5
NOPSTY
zpx 4
STA
zpx 4
STX
zpy 4
SMB1
zp 5
TYA
2
STA
aby 5
TXS
2
NOPSTZ
abs 4
STA
abx 5
STZ
abx 5
BBS1
rel 2*
AxLDY
imm 2
LDA
izx 6
LDX
imm 2
NOPLDY
zp 3
LDA
zp 3
LDX
zp 3
SMB2
zp 5
TAY
2
LDA
imm 2
TAX
2
NOPLDY
abs 4
LDA
abs 4
LDX
abs 4
BBS2
rel 2*
BxBCS
rel 2*
LDA
izy 5*
LDA
izp 5
NOPLDY
zpx 4
LDA
zpx 4
LDX
zpy 4
SMB3
zp 5
CLV
2
LDA
aby 4*
TSX
2
NOPLDY
abx 4*
LDA
abx 4*
LDX
aby 4*
BBS3
rel 2*
CxCPY
imm 2
CMP
izx 6
NOP
imm 2
NOPCPY
zp 3
CMP
zp 3
DEC
zp 5
SMB4
zp 5
INY
2
CMP
imm 2
DEX
2
STP ¹
3
CPY
abs 4
CMP
abs 4
DEC
abs 6
BBS4
rel 2*
DxBNE
rel 2*
CMP
izy 5*
CMP
izp 5
NOPNOP
zpx 4
CMP
zpx 4
DEC
zpx 6
SMB5
zp 5
CLD
2
CMP
aby 4*
PHX
3
WAI ¹
3
NOPCMP
abx 4*
DEC
abx 6*
BBS5
rel 2*
ExCPX
imm 2
SBC
izx 6
NOP
imm 2
NOPCPX
zp 3
SBC
zp 3
INC
zp 5
SMB6
zp 5
INX
2
SBC
imm 2
NOP
2
NOPCPX
abs 4
SBC
abs 4
INC
abs 6
BBS6
rel 2*
FxBEQ
rel 2*
SBC
izy 5*
SBC
izp 5
NOPNOP
zpx 4
SBC
zpx 4
INC
zpx 6
SMB7
zp 5
SED
2
SBC
aby 4*
PLX
4
NOPNOPSBC
abx 4*
INC
abx 6*
BBS7
rel 2*
+ +
+¹ - Only available on WDC 65C02
+
+"*" : add 1 cycle if page boundary is crossed.
+add 1 cycle if direct page register is non zero on direct page adressing modes.
+add 1 cycle on conditional branches if taken.
+add 1 cycle on these commands if D=1: ADC, SBC
+

+A = Akkumulator
+X = X-Register
+Y = Y-Register
+S = Stack-Pointer
+P = Status-Register
++(S) = Stack-Pointer relative with pre-increment
+(S)- = Stack-Pointer relative with post-decrement

+

+These things have changed from 6502 to 65C02:
+
+- new instructions.
+- new adressing modes for a few instrucions.
+- one adressing mode for fetch-modify-write instructions has been optimized, so it takes 1 less cycle in some cases.
+- flags now work correctly in BCD mode (takes 1 additional cycle).
+- absolute-indirect adressing mode now also works on a page boundary (also 1 additional cycle).
+- BRK also affects the decimal flag now.
+- illegal opcodes perform a NOP.
+
+ +65C02 opcodes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeimpimmzpzpxzpyizxizyabsabxabyindrelFunctionNVBDIZC
BRA           $80branch always       
PHX$DA           (S)-:=X       
PHY$5A           (S)-:=Y       
PLX$FA           X:=+(S)*    * 
PLY$7A           Y:=+(S)*    * 
STZ  $64$74   $9C$9E   {adr}:=0       
TRB  $14    $1C    {adr}:={adr} nand A     * 
TSB  $04    $0C    {adr}:={adr} or A     * 
BBRn           $xFbranch on bit n reset       
BBSn           $xFbranch on bit n set       
RMBn  $x7         {adr}:={adr} nand 2^n     * 
SMBn  $x7         {adr}:={adr} or 2^n     * 
+ +
+The WDC 65C02 also includes the STP and WAI instructions known from the 65816.
+Another version of this processor, called 65SC02, doesn't have the single-bit instructions BBR/BBS/RMB/SMB.
+

+Changes on instructions of the 6502 instruction set:
+
+INC and DEC now have implied adressing modes.
+All major instructions (ORA, ADC, STA...) now also feature the indirect-zeropage adressing mode without index.
+JMP features a new absolute-indirect-indexed adressing mode.
+BIT now works with a few more adressing modes.
+

© 2009-2010 Graham. Last change on 17.12.2010. + + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodesce02.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodesce02.html new file mode 100644 index 00000000..3f3c485a --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/opcodesce02.html @@ -0,0 +1,560 @@ + + + + + + +65CE02 Opcodes + + + + +

65CE02 Opcode matrix:

+ + +imm = #$00
+imw = #$0000
+zp = $00
+zpx = $00,X
+zpy = $00,Y
+izx = ($00,X)
+izy = ($00),Y
+izz = ($00),Z
+isy = ($00,S),Y
+abs = $0000
+abx = $0000,X
+aby = $0000,Y
+ind = ($0000)
+iax = ($0000,X)
+rel = $0000 (8 bits PC-relative)
+rell = $0000 (16 bits PC-relative)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 x0x1x2x3x4x5x6x7x8x9xAxBxCxDxExF
0xBRK
7
ORA
izx 6
CLE
2
SEE
2
TSB
zp 5
ORA
zp 3
ASL
zp 5
RMB0
zp 5
PHP
3
ORA
imm 2
ASL
2
TSY
2
TSB
abs 6
ORA
abs 4
ASL
abs 6
BBR0
rel 2*
1xBPL
rel 2*
ORA
izy 5*
ORA
izz 5
BPL
rell 3*
TRB
zp 5
ORA
zpx 4
ASL
zpx 6
RMB1
zp 5
CLC
2
ORA
aby 4*
INC
2
INZ
2
TRB
abs 6
ORA
abx 4*
ASL
abx 6*
BBR1
rel 2*
2xJSR
abs 6
AND
izx 6
JSR
iab 6
JSR
iax 6
BIT
zp 3
AND
zp 3
ROL
zp 5
RMB2
zp 5
PLP
4
AND
imm 2
ROL
2
TYS
2
BIT
abs 4
AND
abs 4
ROL
abs 6
BBR2
rel 2*
3xBMI
rel 2*
AND
izy 5*
AND
izz 5
BMI
rell 3*
BIT
zpx 4
AND
zpx 4
ROL
zpx 6
RMB3
zp 5
SEC
2
AND
aby 4*
DEC
2
DEZ
2
BIT
abx 4*
AND
abx 4*
ROL
abx 6*
BBR3
rel 2*
4xRTI
6
EOR
izx 6
NEG
2
ASR
2
ASR
zp 5
EOR
zp 3
LSR
zp 5
RMB4
zp 5
PHA
3
EOR
imm 2
LSR
2
TAZ
2
JMP
abs 3
EOR
abs 4
LSR
abs 6
BBR4
rel 2*
5xBVC
rel 2*
EOR
izy 5*
EOR
izz 5
BVC
rell 3*
ASR
zpx 6
EOR
zpx 4
LSR
zpx 6
RMB5
zp 5
CLI
2
EOR
aby 4*
PHY
3
TAB
2
MAP
2
EOR
abx 4*
LSR
abx 6*
BBR5
rel 2*
6xRTS
6
ADC
izx 6
RTS
imm 6
BSR
rell 3*
STZ
zp 3
ADC
zp 3
ROR
zp 5
RMB6
zp 5
PLA
4
ADC
imm 2
ROR
2
TZA
2
JMP
ind 6
ADC
abs 4
ROR
abs 6
BBR6
rel 2*
7xBVS
rel 2*
ADC
izy 5*
ADC
izz 5
BVS
rell 3*
STZ
zpx 4
ADC
zpx 4
ROR
zpx 6
RMB7
zp 5
SEI
2
ADC
aby 4*
PLY
4
TBA
2
JMP
iax 6
ADC
abx 4*
ROR
abx 6*
BBR7
rel 2*
8xBRA
rel 3*
STA
izx 6
STA
isy 7
BRA
rell 4
STY
zp 3
STA
zp 3
STX
zp 3
SMB0
zp 5
DEY
2
BIT
imm 2
TXA
2
STY
abx 5
STY
abs 4
STA
abs 4
STX
abs 4
BBS0
rel 2*
9xBCC
rel 2*
STA
izy 6
STA
izz 5
BCC
rell 3*
STY
zpx 4
STA
zpx 4
STX
zpy 4
SMB1
zp 5
TYA
2
STA
aby 5
TXS
2
STX
aby 5
STZ
abs 4
STA
abx 5
STZ
abx 5
BBS1
rel 2*
AxLDY
imm 2
LDA
izx 6
LDX
imm 2
LDZ
imm 2
LDY
zp 3
LDA
zp 3
LDX
zp 3
SMB2
zp 5
TAY
2
LDA
imm 2
TAX
2
LDZ
abs 4
LDY
abs 4
LDA
abs 4
LDX
abs 4
BBS2
rel 2*
BxBCS
rel 2*
LDA
izy 5*
LDA
izz 5
BCS
rell 3*
LDY
zpx 4
LDA
zpx 4
LDX
zpy 4
SMB3
zp 5
CLV
2
LDA
aby 4*
TSX
2
LDZ
abx 4*
LDY
abx 4*
LDA
abx 4*
LDX
aby 4*
BBS3
rel 2*
CxCPY
imm 2
CMP
izx 6
CPZ
imm 2
DEW
zp 6
CPY
zp 3
CMP
zp 3
DEC
zp 5
SMB4
zp 5
INY
2
CMP
imm 2
DEX
2
ASW
abs 7
CPY
abs 4
CMP
abs 4
DEC
abs 6
BBS4
rel 2*
DxBNE
rel 2*
CMP
izy 5*
CMP
izz 5
BNE
rell 3*
CPZ
zp 3
CMP
zpx 4
DEC
zpx 6
SMB5
zp 5
CLD
2
CMP
aby 4*
PHX
3
PHZ
3
CPZ
abs 4
CMP
abx 4*
DEC
abx 6*
BBS5
rel 2*
ExCPX
imm 2
SBC
izx 6
LDA
isy 7
INW
zp 6
CPX
zp 3
SBC
zp 3
INC
zp 5
SMB6
zp 5
INX
2
SBC
imm 2
NOP
2
ROW
abs 7
CPX
abs 4
SBC
abs 4
INC
abs 6
BBS6
rel 2*
FxBEQ
rel 2*
SBC
izy 5*
SBC
izz 5
BEQ
rell 3*
PHW
imw 4
SBC
zpx 4
INC
zpx 6
SMB7
zp 5
SED
2
SBC
aby 4*
PLX
4
PLZ
4
PHW
abs 4
SBC
abx 4*
INC
abx 6*
BBS7
rel 2*
+ +
+"*" : add 1 cycle if page boundary is crossed.
+add 1 cycle if direct page register is non zero on direct page adressing modes.
+add 1 cycle on conditional branches if taken.
+add 1 cycle on these commands if D=1: ADC, SBC
+

+A = Akkumulator
+B = Bank-Register
+X = X-Register
+Y = Y-Register
+Z = Z-Register
+S = Stack-Pointer
+P = Status-Register
++(S) = Stack-Pointer relative with pre-increment
+(S)- = Stack-Pointer relative with post-decrement

+

+These things have changed from 65C02 to 65CE02:
+
+- new instructions.
+- new adressing modes for a few instrucions.
+- Z-Register is introduced.
+- with the introduction of the Z-Register, also the IZP adressing mode has been changed to IZZ.
+
+ +65CE02 opcodes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OpcodeFunctionNVBDIZC
CPZZ-{adr}*    **
DECA:=A-1*    * 
DEZZ:=Z-1*    * 
DEW{adr}:={adr}-1 (16 bit)*    * 
INCA:=A+1*    * 
INZZ:=Z+1*    * 
INW{adr}:={adr}+1 (16 bit)*    * 
NEGA:=0-A*    * 
ASR{adr}:={adr}/2*    **
ASW{adr}:={adr}*2 (16 bit)*    **
ROW{adr}:={adr}*2+C (16 bit)*    **
LDZZ:={adr}*    * 
STZ{adr}:=Z       
TABB:=A*    * 
TBAA:=B*    * 
TAZZ:=A*    * 
TZAA:=Z*    * 
TSYY:=S*    * 
TYSS:=Y       
PHW(S)-:={adr} (16 bit)       
PHX(S)-:=X       
PHY(S)-:=Y       
PHZ(S)-:=Z       
PLXX:=+(S)*    * 
PLYY:=+(S)*    * 
PLZZ:=+(S)*    * 
TRB{adr}:={adr} nand A     * 
TSB{adr}:={adr} or A     * 
CLEE:=0 ?       
SEEE:=1 ?       
BRAbranch always       
BSR(S)-:=PC / branch always       
BBRnbranch on bit n reset       
BBSnbranch on bit n set       
RMBn{adr}:={adr} nand 2^n     * 
SMBn{adr}:={adr} or 2^n     * 
MAP?       
+ +
+Warning! Some information is based on 65C02 documents, others is just +guessed. There might be some mistakes in here.
+

© 2009 Graham. Last change on 30.1.2009. + + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_antic.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_antic.html new file mode 100644 index 00000000..86bf0ed9 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_antic.html @@ -0,0 +1,298 @@ + + + + + + +ANTIC reference + + + +

ANTIC (AlphaNumeric Television Interface Controller) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ANTIC register set:
  76543210
$D400 (W)DMACTLunusedDL DMAPM Res.P DMAM DMAPlayfield Width
$D401 (W)CHACTLunusedTurnedInverseOpaque
$D402 (W)DLISTLDisplay List Low (A7-A0)
$D403 (W)DLISTHDisplay List High (A15-A8)
$D404 (W)HSCROLunusedHorizontal Scrolling
$D405 (W)VSCROLunusedVertical Scrolling
$D406 (W) unused
$D407 (W)PMBASEPlayer/Missile Base High (A15-A10)unused
$D408 (W) unused
$D409 (W)CHBASECharacter Set Base High (A15-A9)unused
$D40A (W)WSYNCunused
$D40B (R)VCOUNTVertical Line Counter / 2
$D40C (R)PENHHorizontal Light Pen Trigger Position
$D40D (R)PENVVertical Light Pen Trigger Position
$D40E (W)NMIENDLIVBIResetunused
$D40F (R)NMISTDLIVBIResetunused ¹
$D40F (W)NMIRESunused
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ANTIC instruction set:
 76543210
Blank LineDLINumber of scan lines to skip - 10
Mode LineDLILMSVScrollHScrollANTIC Mode (2-15)
JumpDLI0unused$1
Jump/Wait for VBLDLI1unused$1
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ANTIC display modes:
ModeColorsResolutionMode HeightPixel Height
22 (Monochrome)32081
32 (Monochrome)320101
45 (4 per char)16081
55 (4 per char)160162
65 (2 per char)16081
75 (2 per char)160162
844088
92 (Monochrome)8044
1048044
112 (Monochrome)16022
122 (Monochrome)16011
13416022
14416011
152 (Monochrome)32011
GTIA 1 (15)16 Lumas/1 Chroma8011
GTIA 2 (15)98011
GTIA 3 (15)1 Luma/16 Chromas8011
+
+¹ - Unused bits read back 1
+

+© 2009-2010 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_gtia.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_gtia.html new file mode 100644 index 00000000..051f4f9a --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_gtia.html @@ -0,0 +1,314 @@ + + + + + + +CTIA/GTIA reference + + + +

CTIA/GTIA (Graphic Television Interface Adapter) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CTIA/GTIA register set:
  76543210
$D000 (R)M0PFunused ¹Missile 0 to playfield collision
$D000 (W)HPOSP0Horizontal position of player 0
$D001 (R)M1PFunused ¹Missile 1 to playfield collision
$D001 (W)HPOSP1Horizontal position of player 1
$D002 (R)M2PFunused ¹Missile 2 to playfield collision
$D002 (W)HPOSP2Horizontal position of player 2
$D003 (R)M3PFunused ¹Missile 3 to playfield collision
$D003 (W)HPOSP3Horizontal position of player 3
$D004 (R)P0PFunused ¹Player 0 to playfield collision
$D004 (W)HPOSM0Horizontal position of missile 0
$D005 (R)P1PFunused ¹Player 1 to playfield collision
$D005 (W)HPOSM1Horizontal position of missile 1
$D006 (R)P2PFunused ¹Player 2 to playfield collision
$D006 (W)HPOSM2Horizontal position of missile 2
$D007 (R)P3PFunused ¹Player 3 to playfield collision
$D007 (W)HPOSM3Horizontal position of missile 3
$D008 (R)M0PLunused ¹Missile 0 to player collision
$D008 (W)SIZEP0unusedPlayer 0 size
$D009 (R)M1PLunused ¹Missile 1 to player collision
$D009 (W)SIZEP1unusedPlayer 1 size
$D00A (R)M2PLunused ¹Missile 2 to player collision
$D00A (W)SIZEP2unusedPlayer 2 size
$D00B (R)M3PLunused ¹Missile 3 to player collision
$D00B (W)SIZEP3unusedPlayer 3 size
$D00C (R)P0PLunused ¹Player 0 to player collision
$D00C (W)SIZEMMissile 3 sizeMissile 2 sizeMissile 1 sizeMissile 0 size
$D00D (R)P1PLunused ¹Player 1 to player collision
$D00D (W)GRAFP0Player 0 graphic data
$D00E (R)P2PLunused ¹Player 2 to player collision
$D00E (W)GRAFP1Player 1 graphic data
$D00F (R)P3PLunused ¹Player 3 to player collision
$D00F (W)GRAFP2Player 2 graphic data
$D010 (R)TRIG0unused ¹/Trigger
$D010 (W)GRAFP3Player 3 graphic data
$D011 (R)TRIG1unused ¹/Trigger
$D011 (W)GRAFMMissiles graphic data
$D012 (R)TRIG2unused ¹/Trigger
$D012 (W)COLPM0ChromaLumaunused
$D013 (R)TRIG3unused ¹/Trigger
$D013 (W)COLPM1ChromaLumaunused
$D014 (R)PALunused ¹$F (NTSC) / $1 (PAL)
$D014 (W)COLPM2ChromaLumaunused
$D015 (W)COLPM3ChromaLumaunused
$D016 (W)COLPF0ChromaLumaunused
$D017 (W)COLPF1ChromaLumaunused
$D018 (W)COLPF2ChromaLumaunused
$D019 (W)COLPF3ChromaLumaunused
$D01A (W)COLBKChromaLumaunused
$D01B (W)PRIORGTIA mode ²Overlap5th Pl.Priority
$D01C (W)VDELAYP3P2P1P0M3M2M1M0
$D01D (W)GRACTLunusedLatch Tr.enable Penable M
$D01E (W)HITCLRunused
$D01F (R)CONSOLunused ¹/Option/Select/Start
$D01F (W)CONSOLunusedReset?unknown
+
+¹ - Unused bits read back 0
+² - Only available on GTIA
+

+© 2009-2010 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_pokey.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_pokey.html new file mode 100644 index 00000000..e4d129a6 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_pokey.html @@ -0,0 +1,196 @@ + + + + + + +POKEY reference + + + +

POKEY (POtentiometer and KEYboard Integrated Circuit) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
POKEY register set:
  76543210
$D200 (R)POT0Potentiometer 0 input
$D200 (W)AUDF1Frequency
$D201 (R)POT1Potentiometer 1 input
$D201 (W)AUDC1Sound DistortionOn/OffVolume Control
$D202 (R)POT2Potentiometer 2 input
$D202 (W)AUDF2Frequency
$D203 (R)POT3Potentiometer 3 input
$D203 (W)AUDC2Sound DistortionOn/OffVolume Control
$D204 (R)POT4Potentiometer 4 input
$D204 (W)AUDF3Frequency
$D205 (R)POT5Potentiometer 5 input
$D205 (W)AUDC3Sound DistortionOn/OffVolume Control
$D206 (R)POT6Potentiometer 6 input
$D206 (W)AUDF4Frequency
$D207 (R)POT7Potentiometer 7 input
$D207 (W)AUDC4Sound DistortionOn/OffVolume Control
$D208 (R)POTSTPOT7POT6POT5POT4POT3POT2POT1POT0
$D208 (W)AUDCTLDistortionC1 clockC3 clockC1+C2C3+C4C1 filterC2 filterbase clock
$D209 (R)KBCODECTRLSHIFTKey code
$D209 (W)STIMERunused
$D20A (R)RANDOMRandom generator value
$D20A (W)SKSTRESunused
$D20B (W)POTGOunused
$D20D (R)SERINData
$D20D (W)SEROUTData
$D20E (R)IRQSTBREAKKBCODESERINSEROUTEOTTimer 4Timer 2Timer 1
$D20E (W)IRQENBREAKKBCODESERINSEROUTEOTTimer 4Timer 2Timer 1
$D20F (R)SKCTLSPACESerial speedtwo-toneADC spdPOKEY ctrl
$D20F (W)SKSTATFrameESERIN EKB ESER ESHIFTKEYTransferunused
+
+© 2009-2010 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_rec.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_rec.html new file mode 100644 index 00000000..0d5b8763 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_rec.html @@ -0,0 +1,113 @@ + + + + + + +REC reference + + + +

REC 8726 (RAM Expansion Controller) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
REC register set:
  76543210
$DF00 (R)SRIRQEOBVerifySizeVersion
$DF01 (R/W)CRExecutereserved ²LoadFF00reserved ²Transfer Type
$DF02 (R/W) C64/C128 start address (A7-A0)
$DF03 (R/W) C64/C128 start address (A15-A8)
$DF04 (R/W) REU start address (A7-A0)
$DF05 (R/W) REU start address (A15-A8)
$DF06 (R/W) unused ¹REU start address (A18-A16)
$DF07 (R/W) Transfer length (D7-D0)
$DF08 (R/W) Transfer length (D15-D8)
$DF09 (R/W) IRQEEOBEVerifyEunused ¹
$DF0A (R/W)ACRC64 fixedREU fixedunused ¹
+
+¹ - Unused bits read back 1
+² - Reserved bits read back 0
+

+ + + + + + + + + + + + + + + + + + +
Transfer Types:
0C64 to REU
1REU to C64
2Swap
3Verify
+

+© 2010 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_sid.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_sid.html new file mode 100644 index 00000000..678789cc --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_sid.html @@ -0,0 +1,254 @@ + + + + + + +SID reference + + + +

SID 6581/8580 (Sound Interface Device) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SID register set:
  76543210
$D400 (W)FREQLO1Channel 1 Frequency Low-Byte
$D401 (W)FREQHI1Channel 1 Frequency High-Byte
$D402 (W)PWLO1Channel 1 Pulse Width (PW7-0)
$D403 (W)PWHI1unusedChannel 1 Pulse Width (PW11-8)
$D404 (W)CR1NOISEPULSESAWTRITESTRINGSYNCGATE
$D405 (W)AD1Channel 1 AttackChannel 1 Decay
$D406 (W)SR1Channel 1 SustainChannel 1 Release
$D407 (W)FREQLO2Channel 2 Frequency Low-Byte
$D408 (W)FREQHI2Channel 2 Frequency High-Byte
$D409 (W)PWLO2Channel 2 Pulse Width (PW7-0)
$D40A (W)PWHI2unusedChannel 2 Pulse Width (PW11-8)
$D40B (W)CR2NOISEPULSESAWTRITESTRINGSYNCGATE
$D40C (W)AD2Channel 2 AttackChannel 2 Decay
$D40D (W)SR2Channel 2 SustainChannel 2 Release
$D40E (W)FREQLO3Channel 3 Frequency Low-Byte
$D40F (W)FREQHI3Channel 3 Frequency High-Byte
$D410 (W)PWLO3Channel 3 Pulse Width (PW7-0)
$D411 (W)PWHI3unusedChannel 3 Pulse Width (PW11-8)
$D412 (W)CR3NOISEPULSESAWTRITESTRINGSYNCGATE
$D413 (W)AD3Channel 3 AttackChannel 3 Decay
$D414 (W)SR3Channel 3 SustainChannel 3 Release
$D415 (W)FCLOunusedFilter Cutoff Low (FC2-FC0)
$D416 (W)FCHIFilter Cutoff High (FC10-FC3)
$D417 (W)Res/FiltFilter ResonanceFilt ExFilt 3Filt 2Filt 1
$D418 (W)Mode/VolChan 3 OffHigh PassBand PassLow PassVolume
$D419 (R)POTXPotentiometer X
$D41A (R)POTYPotentiometer Y
$D41B (R)OSC3Channel 3 Oscillator
$D41C (R)ENV3Channel 3 Envelope
+
+
+Frequency:
+
+The frequency registers of the SID are written with a 16 bit value which is added to the oscillator every +clock cycle. Calculating a frequency register value for a given frequency can be done using these formulas:
+
+PAL:  x = f * (18*2^24)/17734475 (0 - 3848 Hz)
+NTSC: x = f * (14*2^24)/14318318 (0 - 3995 Hz)
+
+
+Waveforms:
+
+The SID offers four basic waveforms which can be selected by the control register of each channel.
+
+ + + + + + + + + + + + + + + + +
Triangle:Triangle
 
Sawtooth:Sawtooth
 
Pulse:Pulse
 
Noise:Noise
+
+
+Pulse width:
+
+The pulse waveform offers additional control via the 12 bit pulse width registers which are available for +every channel. A value of $800 will result in a square wave.
+
+ + + + + + + + + + + + +
$400Pulse width 25%
 
$800Pulse width 50%
 
$C00Pulse width 75%
+
+
+Noise waveform LFSR:
+
+The noise waveform of the SID is generated by a simple 23 bit LFSR. On shifting, bit 0 is filled with bit 22 EXOR bit 17.
+Unlike the other waveforms, the noise waveform does not use the topmost 8 bits for output. The 8 bits of the noise waveform +are assembled from bits 20, 18, 14, 11, 9, 5, 2 and 0 of the oscillator.
+
+
+Playing a sound:
+
+1. Set frequency and ADSR, and in case you use the pulse wave also set the pulse width.
+2. Set the waveform and the GATE bit in the control register. Setting the gate bit will start the ADSR envelope generator.
+3. Wait for as long as you wish the note to be played.
+4. Clear gate bit. This will start the release phase of the ADSR.
+
+
+© 2009-2012 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_ted.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_ted.html new file mode 100644 index 00000000..8f1fa916 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_ted.html @@ -0,0 +1,254 @@ + + + + + + +TED reference + + + +

TED 7360 (Text Editing Device) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TED register set:
  76543210
$FF00 (R/W) Timer 1 High-Byte
$FF01 (R/W) Timer 1 Low-Byte
$FF02 (R/W) Timer 2 High-Byte
$FF03 (R/W) Timer 2 Low-Byte
$FF04 (R/W) Timer 3 High-Byte
$FF05 (R/W) Timer 3 Low-Byte
$FF06 (R/W)CR1TESTECMBMMDENRSELYSCROLL
$FF07 (R/W)CR2REVERSEPAL/NTSCSTOPMCMCSELXSCROLL
$FF08 (R/W) Keyboard Input Latch
$FF09 (R/W)IRQSTIRQICNT3unused ¹ICNT2ICNT1ILPIRSTunused ¹
$FF0A (R/W)IRQENunused ¹ECNT3unused ¹ECNT2ECNT1ELPERSTRST8
$FF0B (R)RASTERRaster Counter bits 7-0
$FF0B (W)RSTCMPRaster Comparator bits 7-0
$FF0C (R/W) unused ¹Cursor Position bits 9-8
$FF0D (R/W) Cursor Position Low-Byte
$FF0E (R/W) Channel 1 Frequency Low-Byte
$FF0F (R/W) Channel 2 Frequency Low-Byte
$FF10 (R/W) unused ¹Chan. 1 Freq. bits 9-8
$FF11 (R/W) D/A modeC2 noiseC2 squareC1 enableVolume (0-8)
$FF12 (R/W) unused ¹Bitmap Address (A15-A13)CHARROMChan. 2 Freq. bits 9-8
$FF13 (R/W) Charset Address (A15-A10)1 MHzROM
$FF14 (R/W) Video RAM Address (A15-A11)unused ¹
$FF15 (R/W)B0Cunused ¹LumaChroma
$FF16 (R/W)B1Cunused ¹LumaChroma
$FF17 (R/W)B2Cunused ¹LumaChroma
$FF18 (R/W)B3Cunused ¹LumaChroma
$FF19 (R/W)ECunused ¹LumaChroma
$FF1A (R/W) unused ¹V. RAM Pos (A9-A8)
$FF1B (R/W) Video RAM Position (A7-A0)
$FF1C (R/W) unused ¹VSCAN8
$FF1D (R/W) Vertical Scan Position Low-Byte
$FF1E (R/W) Horizontal Scan Position
$FF1F (R/W) unused ¹Flash CounterVertical Character Scan Position
$FF3E (W)ROMENunused
$FF3F (W)RAMENunused
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TED video modes:
ECMBMMMCMMode
000Hires character mode (40x25)
001Multicolor character mode (40x25)
010Hires bitmap mode (320x200)
011Multicolor bitmap mode (160x200)
100Hires character mode with extended background colors (40x25)
+
+¹ - Unused bits read back 1
+

+© 2009-2011 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_vic1.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_vic1.html new file mode 100644 index 00000000..efefcd6c --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_vic1.html @@ -0,0 +1,107 @@ + + + + + + +VIC reference + + + +

VIC 6560/6561 (Video Interface Chip) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VIC register set:
  76543210
$9000 (R/W) InterlaceScreen Origin X
$9001 (R/W) Screen Origin Y
$9002 (R/W) Screen A9Number of Video Columns
$9003 (R/W) Raster b0Number of Video RowsChar Size
$9004 (R/W) Raster Value bits 8-1
$9005 (R/W) Screen Memory Location (A13-A10)Char Memory Location (A13-A10)
$9006 (R/W) Lightpen X
$9007 (R/W) Lightpen Y
$9008 (R/W) Paddle X
$9009 (R/W) Paddle Y
$900A (R/W) SwitchBass Frequency
$900B (R/W) SwitchAlto Frequency
$900C (R/W) SwitchSoprano Frequency
$900D (R/W) SwitchNoise Frequency
$900E (R/W) Auxiliary ColorVolume Control
$900F (R/W) Screen ColorReverseBorder Color
+

+© 2011 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_vic2.html b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_vic2.html new file mode 100644 index 00000000..4064c07f --- /dev/null +++ b/com.wudsn.ide.asm/help/www.oxyron.de/html/registers_vic2.html @@ -0,0 +1,385 @@ + + + + + + +VIC-II reference + + + +

VIC-II 6567/6569/856x (Video Interface Chip 2) reference

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VIC-II register set:
  76543210
$D000 (R/W)M0XSprite 0 X-position
$D001 (R/W)M0YSprite 0 Y-position
$D002 (R/W)M1XSprite 1 X-position
$D003 (R/W)M1YSprite 1 Y-position
$D004 (R/W)M2XSprite 2 X-position
$D005 (R/W)M2YSprite 2 Y-position
$D006 (R/W)M3XSprite 3 X-position
$D007 (R/W)M3YSprite 3 Y-position
$D008 (R/W)M4XSprite 4 X-position
$D009 (R/W)M4YSprite 4 Y-position
$D00A (R/W)M5XSprite 5 X-position
$D00B (R/W)M5YSprite 5 Y-position
$D00C (R/W)M6XSprite 6 X-position
$D00D (R/W)M6YSprite 6 Y-position
$D00E (R/W)M7XSprite 7 X-position
$D00F (R/W)M7YSprite 7 Y-position
$D010 (R/W)M?X8M7X8M6X8M5X8M4X8M3X8M2X8M1X8M0X8
$D011 (R/W)CR1RST8ECMBMMDENRSELYSCROLL
$D012 (R)RASTERRaster Counter bits 7-0
$D012 (W)RSTCMPRaster Comparator bits 7-0
$D013 (R)LPXLight Pen X-position
$D014 (R)LPYLight Pen Y-position
$D015 (R/W)M?EM7EM6EM5EM4EM3EM2EM1EM0E
$D016 (R/W)CR2unused ¹RESMCMCSELXSCROLL
$D017 (R/W)M?YEM7YEM6YEM5YEM4YEM3YEM2YEM1YEM0YE
$D018 (R/W)VM/CBScreen Pointer (A13-A10)Bitmap/Charset Pointer (A13-A11)unused ¹
$D019 (R/W)IRQSTIRQunused ¹ILPIMMCIMBCIRST
$D01A (R/W)IRQENunused ¹ELPEMMCEMBCERST
$D01B (R/W)M?DPM7DPM6DPM5DPM4DPM3DPM2DPM1DPM0DP
$D01C (R/W)M?MCM7MCM6MCM5MCM4MCM3MCM2MCM1MCM0MC
$D01D (R/W)M?XEM7XEM6XEM5XEM4XEM3XEM2XEM1XEM0XE
$D01E (R/W)M?MM7MM6MM5MM4MM3MM2MM1MM0M
$D01F (R/W)M?DM7DM6DM5DM4DM3DM2DM1DM0D
$D020 (R/W)ECunused ¹Border Color
$D021 (R/W)B0Cunused ¹Background Color 0
$D022 (R/W)B1Cunused ¹Background Color 1
$D023 (R/W)B2Cunused ¹Background Color 2
$D024 (R/W)B3Cunused ¹Background Color 3
$D025 (R/W)MM0unused ¹Sprite Multicolor 0
$D026 (R/W)MM1unused ¹Sprite Multicolor 1
$D027 (R/W)M0Cunused ¹Sprite 0 Color
$D028 (R/W)M1Cunused ¹Sprite 1 Color
$D029 (R/W)M2Cunused ¹Sprite 2 Color
$D02A (R/W)M3Cunused ¹Sprite 3 Color
$D02B (R/W)M4Cunused ¹Sprite 4 Color
$D02C (R/W)M5Cunused ¹Sprite 5 Color
$D02D (R/W)M6Cunused ¹Sprite 6 Color
$D02E (R/W)M7Cunused ¹Sprite 7 Color
$D02F (R/W)KCR ²unused ¹Keyboard Interface ²
$D030 (R/W)FAST ²unused ¹TEST ²2 MHz ²
+
+¹ - Unused bits read back 1
+² - Only available on C128
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VIC-II video modes:
ECMBMMMCMMode
000Hires character mode (40x25)
001Multicolor character mode (40x25)
010Hires bitmap mode (320x200)
011Multicolor bitmap mode (160x200)
100Hires character mode with extended background colors (40x25)
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
VIC-II color palette:
01234567
VIC-II color palette
89ABCDEF
+

+© 2009-2011 Graham + diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/noise.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/noise.gif new file mode 100644 index 00000000..f29b6a90 Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/noise.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse.gif new file mode 100644 index 00000000..d80230dd Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse1.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse1.gif new file mode 100644 index 00000000..8058ebaa Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse1.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse2.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse2.gif new file mode 100644 index 00000000..be454a95 Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse2.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse3.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse3.gif new file mode 100644 index 00000000..2f2412c9 Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/pulse3.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/sawtooth.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/sawtooth.gif new file mode 100644 index 00000000..8530ad3c Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/sawtooth.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/triangle.gif b/com.wudsn.ide.asm/help/www.oxyron.de/pics/triangle.gif new file mode 100644 index 00000000..14c3b8bd Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/triangle.gif differ diff --git a/com.wudsn.ide.asm/help/www.oxyron.de/pics/vic2palette.png b/com.wudsn.ide.asm/help/www.oxyron.de/pics/vic2palette.png new file mode 100644 index 00000000..c2b683d8 Binary files /dev/null and b/com.wudsn.ide.asm/help/www.oxyron.de/pics/vic2palette.png differ diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt new file mode 100644 index 00000000..ef4fe19b --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt @@ -0,0 +1,712 @@ +************************************************************** +************************************************************** +* * +* ATARI 2600 ADVANCED PROGRAMMING GUIDE * +* * +* Updated 05-21-04 * +* Compiled and edited by Paul Slocum * +* Written by the Atari 2600 programming community * +* * +* version 1.0 / 05-19-04 / first release * +* version 1.1 / 05-21-04 / added multi-sprite trick * +* * +************************************************************** +************************************************************** + +This guide is intended to be a supplement to the standard Stella Programmer's +Guide. Some of the sections assume a working knowledge of 6502 assembly and +Atari 2600 registers. + +If you would like to write new sections or update existing sections of this +document, contact me: paul at treewave dot com + + + +************************************************************** +************************************************************** +* * +* TABLE OF CONTENTS * +* * +************************************************************** +************************************************************** + +- Bankswitching +- BRK Subroutine Trick +- Checking the Number of Scanlines +- Constant Cycle Count to Avoid WSYNC +- Counting Down when Looping +- Illegal Opcodes +- Insurance Against Too Many VBlank/Overscan Cycles +- Multi-Sprite Trick +- Paddles +- Showing Missiles/Ball using PHP +- Skipdraw +- Sound and Music +- Using BRK with RESXX +- Wasting Cycles +- HMOVE Timing Chart + + +============================================================== +============================================================== +Bankswitching +============================================================== +============================================================== + +For a bankswitching reference you'll want Kevin Horton's sizes.txt reference: +http://www.tripoint.org/kevtris/files/sizes.txt + +One thing you'll probably want to know for any kind of bankswitching is the RORG +assembler directive. RORG is like ORG except it only affects the way label +addresses are handled, not where the code is placed in the ROM. So let's say +you're working on an 8K ROM. The first bank will start with ORG $1000 and all +the code and data for the first bank will follow. At the start of the second 4K +bank, you'll want: + + ORG $2000 + RORG $1000 + +If you don't use RORG, all your label addresses in the second bank will be in +the $2000-$2FFF range which won't work. With RORG, they will continue to +address the $1000-$1FFF range. + +Here's a basic template for doing F8 bankswitching. This can easily be modified +to work with similar bankswitching methods (F4,F6,etc). This code allows you to +call "Bank2Subroutine" from bank 1 using jsr CallBank2Subroutine. + + ;------------------------------------ + ;This code in bank 1 + ;Switches to bank 2 where the subroutine it called. + org $1FE0 + CallBank2Subroutine + ldx $1FF9 ; switch to bank 2 + nop ; 1FE3 jsr Bank2Subroutine + nop ; . + nop ; . + nop ; 1FE6 lda $1FF8 (Switch back to bank 1) + nop ; . + nop ; . + rts + + ;------------------------------------ + ;This code in bank 2: + ;Calls the subroutine and returns to bank 1 + org $2FE3 + rorg $1FE3 + + jsr Bank2Subroutine + ldx $1FF8 ;(Switch back to bank 1) + + +It's good practice to assume that your multi-bank ROM could start up in any bank +. In each bank, set up the startup vector so it points to code that switches to +the correct startup bank and then jumps to the start of your program. + + + +============================================================== +============================================================== +BRK Subroutine Trick +-------------------------------------------------------------- +Mark Lesser, Thomas Jentzch +============================================================== +============================================================== + +Thomas found this trick in Mark Lesser's Lord of the Rings prototype: You can +use BRK to call a subroutine that needs to be called often and save ROM space. +If you aren't familiar with BRK, it pushes the flags and PC on the stack and +jumps to wherever the vector $FFFE is pointing. + +Thomas found BRK commands like this scattered through Lord of the Rings: + + brk + .byte $0e ; id-byte + lda $e3 ; <- here we will continue + ora #$04 + +And the BRK vector was pointing to this routine: + +BrkRoutine: + plp ; remove flags from stack (not needed) + tsx ; load x with stackpointer + inx ; x++ + dec $00,x ; adjust return address + lda ($00,x) ; read break-id... + tay ; ...and store in y +Subroutine: + [subroutine code...] + rts + +So it ended up being the equivalent of passing a value to a subroutine similar +to this: + + ldy #value + jsr Subroutine + +But it saves 3 bytes with each call and the overhead is only 8 bytes. +After only 3 subroutine calls (Lord of the Rings has about 20) you are saving +ROM space. + +In the case of Lord of the Rings, the subroutine was sound related code that +selected the sound effect to be played based on a priority system. + + + +============================================================== +============================================================== +Checking the Number of Scanlines +-------------------------------------------------------------- +Eckhard Strolberg +============================================================== +============================================================== + +You'll want to verify that your program is drawing the desired number of +scanlines (around 262 NTSC and 312 PAL) and is not varying that number while +running. To do this, use the Z26 emulator in video modeo 9. While Z26 is +running, press ALT-9 to enter this mode which will display the number of +scanlines in the upper right corner. The -v9 switch will start Z26 in this +mode. + + + +============================================================== +============================================================== +Constant Cycle Count to Avoid WSYNC +-------------------------------------------------------------- +============================================================== +============================================================== + +Normally you use STA WSYNC towards the end of each scanline in the kernal to +stay in sync with the TV. But by carefully programming your kernal so each line +consistently takes exactly 76 cycles, you can avoid using STA WSYNC at all in +your kernal loop. This will save you at least 3 cycles per scanline. + + + +============================================================== +============================================================== +Counting Down When Looping +-------------------------------------------------------------- +============================================================== +============================================================== + +Have loops count down whenever possible since this can save a lot of cycles and +a little bit of ROM too. + +------------------------------------------ + + Counting up requires a compare: + + ldx #0 + Loop + [your code...] + inx + cpx #20 + bne Loop + +------------------------------------------ + + Counting down allows you to get rid of the compare: + + ldx #20 + Loop + [your code...] + dex + bne Loop + + + +============================================================== +============================================================== +Illegal Opcodes +-------------------------------------------------------------- +Thomas Jentzch +============================================================== +============================================================== + +These are the most commonly used illegal opcodes in 2600 programming and are +considered safe. + +Decrement and Compare: + Decrements the memory location and compares it to the accumulator. + DCP Opcode:$C3 M <- (M)-1, (A-M) -> NZC (Ind,X) 2/8 + +Double NOP: + No operation command that takes 3 cycles and uses two bytes. + DOP Opcode:$04 [no operation] (Z-Page) 2/3 + +Load A and X: + Loads both A and X with the memory. + LAX Opcode:$AF A <- M, X <- M (Absolute) 3/4 + + + +============================================================== +============================================================== +Insurance Against Too Many VBLANK/Overscan Cycles +-------------------------------------------------------------- +Paul Slocum +============================================================== +============================================================== + +It's difficult to make sure that there is no unusual case where your game logic +in VBlank and Overscan will use too many cycles and cause the number of +scanlines in a frame to fluctuate. To insure against this problem, pad your +VBlank and Overscan with a few STA WSYNCs to add extra cycles during development +. Optimize your game so that it runs fine with these in. Then when the game is +finished and ready for release, comment out those extra WSYNCs. This is also an +easy way to estimate how much time you have left in VBlank and Overscan: keep +adding WSYNCs (each line is 76 cycles) until the screen jumps. + + + +============================================================== +============================================================== +The Multi-Sprite Trick +-------------------------------------------------------------- +Christopher Tumber +Todo: Add ASCII Images +============================================================== +============================================================== + +The multi-sprite trick is a technique which allows a programmer to put more sprites on a scanline than would normally be +allowable. Using this trick, up to 18 [?] sprites may be displayed on a scanline. + +This trick may be used with Player0, Player1, Missile0 and/or Missile1 in any combination. The most common use is with +P0 and P1 to create a row of sprites. + +There are important limitations with this trick. This trick essentially allows you to create more than the normal limit +of 3 copies of a sprite. However, in doing so you probably will not have time to change the bitmap, colour or size of the +sprite (as applicable). So the kinds of displays created with this technique will usually be repetitive patterns. + +The trick is accomplished by first setting NUSIZ0 and/or NUSIZ1 to display 2 or more copies of the sprites you want to +display [Is the exact setting significant?]. Your program then must strobe RESP0, RESP1, RESM0 and/or RESM1 repeatedly. +If this is timed correctly so that it occurs after the first copy is drawn but before the second then the TIA is tricked +into thinking it's just started drawing sprites and contiues drawing the sprite as if it's the first copy. You just +continue this for every copy you need. + +The tightest formation of sprites possible with this trick is: + +[image] + +which is done by the following code: + + +sta RESP0 +sta RESP1 +sta RESP0 +sta RESP1 +sta RESP0 +sta RESP1 +sta RESP0 +sta RESP1 +sta RESP0 +sta RESP1 + +However, this formation has one problem in that the last sprite on a row is shifted one pixel to the left. There is no +known soloution to this problem. If you can work this "glitch" into your game (for example as part of an asynchronous +display) then you can use this layout. If not, the closest "stable" formation is: + +[image] + +which is done by the following code: + +sta RESP0,x +sta RESP1,x +sta RESP0,x +sta RESP1,x +sta RESP0,x +sta RESP1,x +sta RESP0,x +sta RESP1,x +sta RESP0,x +sta RESP1,x + +or + +sta.w RESP0 +sta.w RESP1 +sta.w RESP0 +sta.w RESP1 +sta.w RESP0 +sta.w RESP1 +sta.w RESP0 +sta.w RESP1 +sta.w RESP0 +sta.w RESP1 + +The ,x and .w in this example are "dummies". They're only used to increased the length of time taken by each instruction +by 1 cycle. The tradeoff being that the former requires the X register be set to zero and the latter results in slightly +larger code. + +If you need to turn off some of the sprites, you can do this by simply skipping their spot by inserting a dummy command. +For example: + +[image] + +sta RESP0 +sta RESP1 +sta Dummy +sta RESP1 +sta RESP0 +sta Dummy +sta RESP0 +sta RESP1 +sta RESP0 +sta RESP1 + +Where Dummy is an unsued (or scratch or temporary) RAM location. + +This trick is quite easy to use to generate static displays. However, if you want a fully dynamic display things get +considerably more complicated. + +Since there is no time available while drawing the sprites to do any calculations, if you want a variable number of +sprites on and off you must predetermine which sprites to display. A simple way to do this is to create a subroutine +for every possible combination of on/off sprites. Then your program just needs to call the appropriate subroutine. +The problem with this approach is that if you have a lot of sprites, the number of subroutines becomes very large. +An alternative method is to place you drawing routine in RAM, adjusted for the current display - In the above example, +copy all the STA RESP0 and STA RESP1 commands into RAM and then where sprites don't appear, substitue in a dummy RAM +location for the relevant RESP0 or RESP1. + +One thing you must be aware of if you are allowing individual sprites to be switched on and off. There are a number +of cominations which you must treat as an exception. They cannot be displayed using the general display as above. +For example, if either RESP0 or RESP1 needs to display only 1 copy of a sprite (because all other copies are off) +then you must reset NUSIZ0/NUSIZ1. This would also be the case when two copies of a sprite are set too far apart +for the second STA RESPn to occur before a "normal" copy is drawn. + +In addition, these sprites are not positioned vertically like normal sprites. Rather their position is determined by which +display cycle the first STA RESPn or RESPMn occurs. So if you want to be able to reposition your sprites verically, +you will either need to add more subroutines (as above) or adjust your RAM routine further. Or some combination of the +two. Space Instigators uses a different subroutine for each possible vertical position, copies that routine into RAM +and then modifies the STA RESPn commands to turn off dead Instigators. The new, single scanline repositioning routine may +be of help here, however the multi-sprite trick tends to use up so much of your scanline time that if you're +trying to do other things (ie: display other sprites) on that scanline you may not have the luxury of enough +cycles for general purpose positioning code. + +The multi-sprite trick has a side effect in that the formation of sprites is shifted [?exact number?] pixels right +as compared to where a normal sprite would appear with an STA RESPn at that cycle. This results in a left margin +that's not at the left edge of the screen. "Illegal" HMOVE/HMMn combination tricks may be used to fix this but with a +corresponding increase in complexity. + +[Some more example code here] + +References: The multi-sprite trick was originally used in Galaxian and was pioneered by Eckhard Stolberg, John +Saeger, Erik Mooney and Thomas Jentzsch. Search Stella List under "Grid demo","trick18","trick12" and "inv3". + + +============================================================== +============================================================== +Paddles +-------------------------------------------------------------- +Thomas Jentzch + Todo: Explain and include how to discharge cap +============================================================== +============================================================== + +Assumes Y is your kernal line counter. + + lda INPT0 ;3 + bmi paddles1 ;2 or 3 + .byte $2c ;1 bit abs opcode + paddles1: + sty padVal1 ;3 + + + +============================================================== +============================================================== +Showing Missiles/Ball using PHP +============================================================== +============================================================== + +This trick is originally from Combat and is probably the most efficient way to +display the missiles and/or ball. This trick just requires that you don't use +the stack during your kernal. Recall that: + + ENABL = $1F + ENAM1 = $1E + ENAM0 = $1D + +In this example I'll show how to use the trick for both missiles. You can +easily adapt it for the ball too. To set the trick up, before your kernal save +the stack pointer and set the top of the stack to ENAM1+1. + + tsx ; Transfer stack pointer to X + stx SavedStackPointer ; Store it in RAM + ldx #ENAM1+1 + txs ; Set the top of the stack to ENAM1+1 + +Now during the kernal you can compare your scanline counter to your missile +position register and this will set the zero flag in the processor. Then to +enable/disable the missile for that scanline, just push the processor flags onto +the stack. The ENAxx registers use bit 1 to enable/disable which corresponds +with the zero flag in the processor, so the enable/disable will be automatic. +It takes few cycles and doesn't vary the number of cycles depending on the +result like branching usually does. + + ; On each line of your the kernal... + cpy MissilePos1 ; Assumes Y is your kernal line counter + php + cpy MissilePos0 + php + +Then before you do it again, somewhere on each scanline you need to pull off the +stack again using two PLA's or PLP's, or you can manually reset the stack +pointer with ldx #ENAM1+1, txs. + +After your kernal, restore the stack pointer: + + ldx SavedStackPointer + txs + + +============================================================== +============================================================== +Skipdraw +-------------------------------------------------------------- +Thomas Jentzch + Todo: Explain and clean up +============================================================== +============================================================== + +The best way, i knew until now, was (if y contains linecounter): + + tya ; 2 +; sec ; 2) <- this can sometimes be avoided + sbc SpriteEnd ; 3 + adc #SPRITEHEIGHT ; 2 + bcx .skipDraw ; 2 = 9-11 cycles + ... + +---------- or --------- + +If you like using illegal opcodes, you can use dcp (dec,cmp) here: + + lda #SPRITEHEIGHT ; 2 + dcp SpriteEnd ; 5 initial value has to be adjusted + bcx .skipDraw ; 2 = 9 + ... + +Advantages: +- state of carry flag doesn't matter anymore (may save 2 cycles) +- a remains constant, could be useful for a 2nd sprite +- you could use the content of SpriteEnd instead of y for accesing sprite data + +;================================== +;An Example: +; + ; skipDraw routine for right player + TXA ; 2 A-> Current scannline + SEC ; 2 Set Carry + SBC slowP1YCoordFromBottom+1 ; 3 + ADC #SPRITEHEIGHT+1 ; 2 calc if sprite is drawn + BCC skipDrawRight ; 2/3 To skip or not to skip? + TAY ; 2 + lda P1Graphic,y ; 4 +continueRight: + STA GRP0 + +;----- this part outside of kernel + +skipDrawRight ; 3 from BCC + LDA #0 ; 2 + BEQ continueRight ; 3 Return... + + + +============================================================== +============================================================== +Sound and Music +-------------------------------------------------------------- +============================================================== +============================================================== + +Atari 2600 Music Programming Guide and music driver code: + +http://qotile.net/sequencer.html + +Eckhard Strolberg's Frequency and Waveform Guide: + +http://buerger.metropolis.de/estolberg + + + +============================================================== +============================================================== +Using BRK with RESXX +-------------------------------------------------------------- +Eckhard Strolberg +============================================================== +============================================================== + +(I'm not sure what you'd do with this trick but it's pretty interesting. Maybe +somebody will figure out how to use it.) + +Pole Position puts the stack pointer over the RESxx registers and then does a +BRK. There are three write cycles in a BRK instruction, so the three position +registers for the objects that make up the road in PP, get accessed in three +consecutive cycles. This is how PP managed to get the road to meet so closely in +the horizon. + + + +============================================================== +============================================================== +Wasting Cycles +-------------------------------------------------------------- +Christopher Tumbler, Chris Wilkson, Andrew Davie +============================================================== +============================================================== + +These are the most efficient ways to waste processor cycles. + +Note that locations $2D-$3F do nothin and aren't decoded, and so they are used +often here. In some bankswitching schemes this could cause problems though. + +----------------- +1 Cycle (0 or 1 byte) + .w (Change a zero page instruction to absolute, adds 1 byte of code) + ,x (Change a zero page or absolute instruction to an indexed instruction. + Make sure x=0. Can also use Y) +----------------- +2 Cycles (1 byte) + nop +----------------- +3 Cycles (2 bytes) + sta $2D + - or - + lda $2D + - or - + dop (Double NOP illegal opcode) +----------------- +4 Cycles (2 bytes) + nop + nop +----------------- +5 Cycles (2 bytes) + dec $2D + - or - + sta $1800,X ; asssumes you can write to ROM without problems +----------------- +6 Cycles (2 bytes) + lda ($80,X) ; assumes possible reads from 0-$7f have no effect + +6 Cycles (3 bytes) + nop + nop + nop +----------------- +7 Cycles (2 bytes, need 1 byte free on stack) + pha + pla +----------------- +8 Cycles (3 bytes) + lda ($80,X) ; assumes possible reads from 0-$7f have no effect + nop +----------------- +9 Cycles (3 bytes, need 1 byte free on stack) + pha + pla + nop + +9 Cycles (4 bytes) + dec $2D + nop + nop +----------------- +10 Cycles (4 bytes) + dec $2D + dec $2D + - or - + rol $80 + rol $80 ; leaves $80 unchanged +----------------- +11 Cycles (4 bytes) + ASSUMING we can safely write to ROM and have nothing disasterous... + STA $8000,X + LDA ($80,X) ; assumes possible reads from 0-$7f have no effect +----------------- +12 Cycles (3 bytes, need 2 bytes free on stack) + jsr return + ; somewhere else + return: + rts +----------------- +12 Cycles (4 bytes) + LDA ($80,X) ; assumes possible reads from 0-$7f have no effect + LDA ($80,X) ; assumes possible reads from 0-$7f have no effect + +Also: +You can use PHA/PHP (1 byte 3 cycles) or PLA/PLP (1 byte 4 cycles) alone but +you have to be carefull not to mess up your stack (PLP/PHA would be usefull if +you have no stack! + + + +============================================================== +============================================================== +HMOVE Timing Chart +-------------------------------------------------------------- +Brad Mott +============================================================== +============================================================== + +Typically HMOVE is executed right after WSYNC, but hitting HMOVE at other times +during the line has effects that can sometimes be useful. It's possible to move +objects without the black HMOVE bars and/or move objects farther than would +normally be possible with HMPx registers. This test was performed using player +graphics, but will probably work with the ball and missiles as well. + + + HMPx values + 0 1 2 3 4 5 6 7 8 9 a b c d e f +Cyc +10 0 -1 -2 -2 -2 -2 -2 -2 8 7 6 5 4 3 2 1 ** HBLANK +11 0 -1 -1 -1 -1 -1 -1 -1 8 7 6 5 4 3 2 1 HBLANK +12 0 0 0 0 0 0 0 0 8 7 6 5 4 3 2 1 HBLANK +13 1 1 1 1 1 1 1 1 8 7 6 5 4 3 2 1 HBLANK +14 1 1 1 1 1 1 1 1 8 7 6 5 4 3 2 1 ** HBLANK +15 2 2 2 2 2 2 2 2 8 7 6 5 4 3 2 2 HBLANK +16 3 3 3 3 3 3 3 3 8 7 6 5 4 3 3 3 HBLANK +17 4 4 4 4 4 4 4 4 8 7 6 5 4 4 4 4 HBLANK +18 4 4 4 4 4 4 4 4 8 7 6 5 4 4 4 4 ** HBLANK +19 5 5 5 5 5 5 5 5 8 7 6 5 5 5 5 5 HBLANK +20 6 6 6 6 6 6 6 6 8 7 6 6 6 6 6 6 HBLANK +21 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + . + . +53 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +54 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +55 0 0 0 0 0 0 0 -1 0 0 0 0 0 0 0 0 +56 0 0 0 0 0 0 -1 -2 0 0 0 0 0 0 0 0 +57 0 0 0 0 0 -1 -2 -3 0 0 0 0 0 0 0 0 +58 0 0 0 0 0 -1 -2 -3 0 0 0 0 0 0 0 0 ** +59 0 0 0 0 -1 -2 -3 -4 0 0 0 0 0 0 0 0 +60 0 0 0 -1 -2 -3 -4 -5 0 0 0 0 0 0 0 0 +61 0 0 -1 -2 -3 -4 -5 -6 0 0 0 0 0 0 0 0 +62 0 0 -1 -2 -3 -4 -5 -6 0 0 0 0 0 0 0 0 ** +63 0 -1 -2 -3 -4 -5 -6 -7 0 0 0 0 0 0 0 0 +64 -1 -2 -3 -4 -5 -6 -7 -8 0 0 0 0 0 0 0 0 +65 -2 -3 -4 -5 -6 -7 -8 -9 0 0 0 0 0 0 0 -1 +66 -2 -3 -4 -5 -6 -7 -8 -9 0 0 0 0 0 0 0 -1 ** +67 -3 -4 -5 -6 -7 -8 -9 -10 0 0 0 0 0 0 -1 -2 +68 -4 -5 -6 -7 -8 -9 -10 -11 0 0 0 0 0 -1 -2 -3 +69 -5 -6 -7 -8 -9 -10 -11 -12 0 0 0 0 -1 -2 -3 -4 +70 -5 -6 -7 -8 -9 -10 -11 -12 0 0 0 0 -1 -2 -3 -4 ** +71 -6 -7 -8 -9 -10 -11 -12 -13 0 0 0 -1 -2 -3 -4 -5 +72 -7 -8 -9 -10 -11 -12 -13 -14 0 0 -1 -2 -3 -4 -5 -6 +73 -8 -9 -10 -11 -12 -13 -14 -15 0 -1 -2 -3 -4 -5 -6 -7 +74 -8 -9 -10 -11 -12 -13 -14 -15 0 -1 -2 -3 -4 -5 -6 -7 ** +75 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK +76 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK +77 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK +78 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK +79 0 -1 -2 -3 -4 -5 -6 -7 8 7 6 5 4 3 2 1 HBLANK +80 0 -1 -2 -3 -4 -5 -6 -6 8 7 6 5 4 3 2 1 HBLANK +81 0 -1 -2 -3 -4 -5 -5 -5 8 7 6 5 4 3 2 1 HBLANK +82 0 -1 -2 -3 -4 -5 -5 -5 8 7 6 5 4 3 2 1 ** HBLANK +83 0 -1 -2 -3 -4 -4 -4 -4 8 7 6 5 4 3 2 1 HBLANK +84 0 -1 -2 -3 -3 -3 -3 -3 8 7 6 5 4 3 2 1 HBLANK +85 0 -1 -2 -2 -2 -2 -2 -2 8 7 6 5 4 3 2 1 HBLANK +( table repeats at this point) \ No newline at end of file diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_mem_map.txt b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_mem_map.txt new file mode 100644 index 00000000..9a7635f5 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_mem_map.txt @@ -0,0 +1,632 @@ +*************************************************** +* $0000-$003F = TIA Addresses $00-$3F (zero page) * +* ----------------------------------------------- * +* * +* mirror: $xyz0 * +* * +* x = {even} * +* y = {anything} * +* z = {0, 4} * +* * +*************************************************** + +************************************** +* $0080-$00FF = RIOT RAM (zero page) * +* ---------------------------------- * +* * +* mirror: $xy80 * +* * +* x = {even} * +* y = {0,1,4,5,8,9,$C,$D} * +* * +************************************** + +**************************************** +* $0280-$029F = RIOT Addresses $00-$1F * +* ------------------------------------ * +* * +* mirror: $xyz0 * +* * +* x = {even} * +* y = {2,3,6,7,$A,$B,$E,$F} * +* z = {8,$A,$C,$E} * +* * +**************************************** + +***************************************** +* $1000-$1FFF = ROM Addresses $000-$FFF * +* ------------------------------------- * +* * +* mirror: $x000 * +* * +* x = {odd} * +* * +***************************************** + + +Mirrors: + +$0000-$003F = TIA Addresses $00-$3F (zero page) +$0040-$007F = TIA Addresses $00-$3F (mirror) +$0080-$00FF = RIOT RAM (zero page) +$0100-$013F = TIA Addresses $00-$3F (mirror) +$0140-$017F = TIA Addresses $00-$3F (mirror) +$0180-$01FF = RIOT RAM (mirror) +$0200-$023F = TIA Addresses $00-$3F (mirror) +$0240-$027F = TIA Addresses $00-$3F (mirror) +$0280-$029F = RIOT Addresses $00-$1F +$02A0-$02BF = RIOT Addresses $00-$1F (mirror) +$02C0-$02DF = RIOT Addresses $00-$1F (mirror) +$02E0-$02FF = RIOT Addresses $00-$1F (mirror) +$0300-$033F = TIA Addresses $00-$3F (mirror) +$0340-$037F = TIA Addresses $00-$3F (mirror) +$0380-$039F = RIOT Addresses $00-$1F (mirror) +$03A0-$03BF = RIOT Addresses $00-$1F (mirror) +$03C0-$03DF = RIOT Addresses $00-$1F (mirror) +$03E0-$03FF = RIOT Addresses $00-$1F (mirror) +$0400-$043F = TIA Addresses $00-$3F (mirror) +$0440-$047F = TIA Addresses $00-$3F (mirror) +$0480-$04FF = RIOT RAM (mirror) +$0500-$053F = TIA Addresses $00-$3F (mirror) +$0540-$057F = TIA Addresses $00-$3F (mirror) +$0580-$05FF = RIOT RAM (mirror) +$0600-$063F = TIA Addresses $00-$3F (mirror) +$0640-$067F = TIA Addresses $00-$3F (mirror) +$0680-$069F = RIOT Addresses $00-$1F (mirror) +$06A0-$06BF = RIOT Addresses $00-$1F (mirror) +$06C0-$06DF = RIOT Addresses $00-$1F (mirror) +$06E0-$06FF = RIOT Addresses $00-$1F (mirror) +$0700-$073F = TIA Addresses $00-$3F (mirror) +$0740-$077F = TIA Addresses $00-$3F (mirror) +$0780-$079F = RIOT Addresses $00-$1F (mirror) +$07A0-$07BF = RIOT Addresses $00-$1F (mirror) +$07C0-$07DF = RIOT Addresses $00-$1F (mirror) +$07E0-$07FF = RIOT Addresses $00-$1F (mirror) +$0800-$083F = TIA Addresses $00-$3F (mirror) +$0840-$087F = TIA Addresses $00-$3F (mirror) +$0880-$08FF = RIOT RAM (mirror) +$0900-$093F = TIA Addresses $00-$3F (mirror) +$0940-$097F = TIA Addresses $00-$3F (mirror) +$0980-$09FF = RIOT RAM (mirror) +$0A00-$0A3F = TIA Addresses $00-$3F (mirror) +$0A40-$0A7F = TIA Addresses $00-$3F (mirror) +$0A80-$0A9F = RIOT Addresses $00-$1F (mirror) +$0AA0-$0ABF = RIOT Addresses $00-$1F (mirror) +$0AC0-$0ADF = RIOT Addresses $00-$1F (mirror) +$0AE0-$0AFF = RIOT Addresses $00-$1F (mirror) +$0B00-$0B3F = TIA Addresses $00-$3F (mirror) +$0B40-$0B7F = TIA Addresses $00-$3F (mirror) +$0B80-$0B9F = RIOT Addresses $00-$1F (mirror) +$0BA0-$0BBF = RIOT Addresses $00-$1F (mirror) +$0BC0-$0BDF = RIOT Addresses $00-$1F (mirror) +$0BE0-$0BFF = RIOT Addresses $00-$1F (mirror) +$0C00-$0C3F = TIA Addresses $00-$3F (mirror) +$0C40-$0C7F = TIA Addresses $00-$3F (mirror) +$0C80-$0CFF = RIOT RAM (mirror) +$0D00-$0D3F = TIA Addresses $00-$3F (mirror) +$0D40-$0D7F = TIA Addresses $00-$3F (mirror) +$0D80-$0DFF = RIOT RAM (mirror) +$0E00-$0E3F = TIA Addresses $00-$3F (mirror) +$0E40-$0E7F = TIA Addresses $00-$3F (mirror) +$0E80-$0E9F = RIOT Addresses $00-$1F (mirror) +$0EA0-$0EBF = RIOT Addresses $00-$1F (mirror) +$0EC0-$0EDF = RIOT Addresses $00-$1F (mirror) +$0EE0-$0EFF = RIOT Addresses $00-$1F (mirror) +$0F00-$0F3F = TIA Addresses $00-$3F (mirror) +$0F40-$0F7F = TIA Addresses $00-$3F (mirror) +$0F80-$0F9F = RIOT Addresses $00-$1F (mirror) +$0FA0-$0FBF = RIOT Addresses $00-$1F (mirror) +$0FC0-$0FDF = RIOT Addresses $00-$1F (mirror) +$0FE0-$0FFF = RIOT Addresses $00-$1F (mirror) +$1000-$1FFF = ROM Addresses $000-$FFF +$2000-$203F = TIA Addresses $00-$3F (mirror) +$2040-$207F = TIA Addresses $00-$3F (mirror) +$2080-$20FF = RIOT RAM (mirror) +$2100-$213F = TIA Addresses $00-$3F (mirror) +$2140-$217F = TIA Addresses $00-$3F (mirror) +$2180-$21FF = RIOT RAM (mirror) +$2200-$223F = TIA Addresses $00-$3F (mirror) +$2240-$227F = TIA Addresses $00-$3F (mirror) +$2280-$229F = RIOT Addresses $00-$1F (mirror) +$22A0-$22BF = RIOT Addresses $00-$1F (mirror) +$22C0-$22DF = RIOT Addresses $00-$1F (mirror) +$22E0-$22FF = RIOT Addresses $00-$1F (mirror) +$2300-$233F = TIA Addresses $00-$3F (mirror) +$2340-$237F = TIA Addresses $00-$3F (mirror) +$2380-$239F = RIOT Addresses $00-$1F (mirror) +$23A0-$23BF = RIOT Addresses $00-$1F (mirror) +$23C0-$23DF = RIOT Addresses $00-$1F (mirror) +$23E0-$23FF = RIOT Addresses $00-$1F (mirror) +$2400-$243F = TIA Addresses $00-$3F (mirror) +$2440-$247F = TIA Addresses $00-$3F (mirror) +$2480-$24FF = RIOT RAM (mirror) +$2500-$253F = TIA Addresses $00-$3F (mirror) +$2540-$257F = TIA Addresses $00-$3F (mirror) +$2580-$25FF = RIOT RAM (mirror) +$2600-$263F = TIA Addresses $00-$3F (mirror) +$2640-$267F = TIA Addresses $00-$3F (mirror) +$2680-$269F = RIOT Addresses $00-$1F (mirror) +$26A0-$26BF = RIOT Addresses $00-$1F (mirror) +$26C0-$26DF = RIOT Addresses $00-$1F (mirror) +$26E0-$26FF = RIOT Addresses $00-$1F (mirror) +$2700-$273F = TIA Addresses $00-$3F (mirror) +$2740-$277F = TIA Addresses $00-$3F (mirror) +$2780-$279F = RIOT Addresses $00-$1F (mirror) +$27A0-$27BF = RIOT Addresses $00-$1F (mirror) +$27C0-$27DF = RIOT Addresses $00-$1F (mirror) +$27E0-$27FF = RIOT Addresses $00-$1F (mirror) +$2800-$283F = TIA Addresses $00-$3F (mirror) +$2840-$287F = TIA Addresses $00-$3F (mirror) +$2880-$28FF = RIOT RAM (mirror) +$2900-$293F = TIA Addresses $00-$3F (mirror) +$2940-$297F = TIA Addresses $00-$3F (mirror) +$2980-$29FF = RIOT RAM (mirror) +$2A00-$2A3F = TIA Addresses $00-$3F (mirror) +$2A40-$2A7F = TIA Addresses $00-$3F (mirror) +$2A80-$2A9F = RIOT Addresses $00-$1F (mirror) +$2AA0-$2ABF = RIOT Addresses $00-$1F (mirror) +$2AC0-$2ADF = RIOT Addresses $00-$1F (mirror) +$2AE0-$2AFF = RIOT Addresses $00-$1F (mirror) +$2B00-$2B3F = TIA Addresses $00-$3F (mirror) +$2B40-$2B7F = TIA Addresses $00-$3F (mirror) +$2B80-$2B9F = RIOT Addresses $00-$1F (mirror) +$2BA0-$2BBF = RIOT Addresses $00-$1F (mirror) +$2BC0-$2BDF = RIOT Addresses $00-$1F (mirror) +$2BE0-$2BFF = RIOT Addresses $00-$1F (mirror) +$2C00-$2C3F = TIA Addresses $00-$3F (mirror) +$2C40-$2C7F = TIA Addresses $00-$3F (mirror) +$2C80-$2CFF = RIOT RAM (mirror) +$2D00-$2D3F = TIA Addresses $00-$3F (mirror) +$2D40-$2D7F = TIA Addresses $00-$3F (mirror) +$2D80-$2DFF = RIOT RAM (mirror) +$2E00-$2E3F = TIA Addresses $00-$3F (mirror) +$2E40-$2E7F = TIA Addresses $00-$3F (mirror) +$2E80-$2E9F = RIOT Addresses $00-$1F (mirror) +$2EA0-$2EBF = RIOT Addresses $00-$1F (mirror) +$2EC0-$2EDF = RIOT Addresses $00-$1F (mirror) +$2EE0-$2EFF = RIOT Addresses $00-$1F (mirror) +$2F00-$2F3F = TIA Addresses $00-$3F (mirror) +$2F40-$2F7F = TIA Addresses $00-$3F (mirror) +$2F80-$2F9F = RIOT Addresses $00-$1F (mirror) +$2FA0-$2FBF = RIOT Addresses $00-$1F (mirror) +$2FC0-$2FDF = RIOT Addresses $00-$1F (mirror) +$2FE0-$2FFF = RIOT Addresses $00-$1F (mirror) +$3000-$3FFF = ROM Addresses $000-$FFF (mirror) +$4000-$403F = TIA Addresses $00-$3F (mirror) +$4040-$407F = TIA Addresses $00-$3F (mirror) +$4080-$40FF = RIOT RAM (mirror) +$4100-$413F = TIA Addresses $00-$3F (mirror) +$4140-$417F = TIA Addresses $00-$3F (mirror) +$4180-$41FF = RIOT RAM (mirror) +$4200-$423F = TIA Addresses $00-$3F (mirror) +$4240-$427F = TIA Addresses $00-$3F (mirror) +$4280-$429F = RIOT Addresses $00-$1F (mirror) +$42A0-$42BF = RIOT Addresses $00-$1F (mirror) +$42C0-$42DF = RIOT Addresses $00-$1F (mirror) +$42E0-$42FF = RIOT Addresses $00-$1F (mirror) +$4300-$433F = TIA Addresses $00-$3F (mirror) +$4340-$437F = TIA Addresses $00-$3F (mirror) +$4380-$439F = RIOT Addresses $00-$1F (mirror) +$43A0-$43BF = RIOT Addresses $00-$1F (mirror) +$43C0-$43DF = RIOT Addresses $00-$1F (mirror) +$43E0-$43FF = RIOT Addresses $00-$1F (mirror) +$4400-$443F = TIA Addresses $00-$3F (mirror) +$4440-$447F = TIA Addresses $00-$3F (mirror) +$4480-$44FF = RIOT RAM (mirror) +$4500-$453F = TIA Addresses $00-$3F (mirror) +$4540-$457F = TIA Addresses $00-$3F (mirror) +$4580-$45FF = RIOT RAM (mirror) +$4600-$463F = TIA Addresses $00-$3F (mirror) +$4640-$467F = TIA Addresses $00-$3F (mirror) +$4680-$469F = RIOT Addresses $00-$1F (mirror) +$46A0-$46BF = RIOT Addresses $00-$1F (mirror) +$46C0-$46DF = RIOT Addresses $00-$1F (mirror) +$46E0-$46FF = RIOT Addresses $00-$1F (mirror) +$4700-$473F = TIA Addresses $00-$3F (mirror) +$4740-$477F = TIA Addresses $00-$3F (mirror) +$4780-$479F = RIOT Addresses $00-$1F (mirror) +$47A0-$47BF = RIOT Addresses $00-$1F (mirror) +$47C0-$47DF = RIOT Addresses $00-$1F (mirror) +$47E0-$47FF = RIOT Addresses $00-$1F (mirror) +$4800-$483F = TIA Addresses $00-$3F (mirror) +$4840-$487F = TIA Addresses $00-$3F (mirror) +$4880-$48FF = RIOT RAM (mirror) +$4900-$493F = TIA Addresses $00-$3F (mirror) +$4940-$497F = TIA Addresses $00-$3F (mirror) +$4980-$49FF = RIOT RAM (mirror) +$4A00-$4A3F = TIA Addresses $00-$3F (mirror) +$4A40-$4A7F = TIA Addresses $00-$3F (mirror) +$4A80-$4A9F = RIOT Addresses $00-$1F (mirror) +$4AA0-$4ABF = RIOT Addresses $00-$1F (mirror) +$4AC0-$4ADF = RIOT Addresses $00-$1F (mirror) +$4AE0-$4AFF = RIOT Addresses $00-$1F (mirror) +$4B00-$4B3F = TIA Addresses $00-$3F (mirror) +$4B40-$4B7F = TIA Addresses $00-$3F (mirror) +$4B80-$4B9F = RIOT Addresses $00-$1F (mirror) +$4BA0-$4BBF = RIOT Addresses $00-$1F (mirror) +$4BC0-$4BDF = RIOT Addresses $00-$1F (mirror) +$4BE0-$4BFF = RIOT Addresses $00-$1F (mirror) +$4C00-$4C3F = TIA Addresses $00-$3F (mirror) +$4C40-$4C7F = TIA Addresses $00-$3F (mirror) +$4C80-$4CFF = RIOT RAM (mirror) +$4D00-$4D3F = TIA Addresses $00-$3F (mirror) +$4D40-$4D7F = TIA Addresses $00-$3F (mirror) +$4D80-$4DFF = RIOT RAM (mirror) +$4E00-$4E3F = TIA Addresses $00-$3F (mirror) +$4E40-$4E7F = TIA Addresses $00-$3F (mirror) +$4E80-$4E9F = RIOT Addresses $00-$1F (mirror) +$4EA0-$4EBF = RIOT Addresses $00-$1F (mirror) +$4EC0-$4EDF = RIOT Addresses $00-$1F (mirror) +$4EE0-$4EFF = RIOT Addresses $00-$1F (mirror) +$4F00-$4F3F = TIA Addresses $00-$3F (mirror) +$4F40-$4F7F = TIA Addresses $00-$3F (mirror) +$4F80-$4F9F = RIOT Addresses $00-$1F (mirror) +$4FA0-$4FBF = RIOT Addresses $00-$1F (mirror) +$4FC0-$4FDF = RIOT Addresses $00-$1F (mirror) +$4FE0-$4FFF = RIOT Addresses $00-$1F (mirror) +$5000-$5FFF = ROM Addresses $000-$FFF (mirror) +$6000-$603F = TIA Addresses $00-$3F (mirror) +$6040-$607F = TIA Addresses $00-$3F (mirror) +$6080-$60FF = RIOT RAM (mirror) +$6100-$613F = TIA Addresses $00-$3F (mirror) +$6140-$617F = TIA Addresses $00-$3F (mirror) +$6180-$61FF = RIOT RAM (mirror) +$6200-$623F = TIA Addresses $00-$3F (mirror) +$6240-$627F = TIA Addresses $00-$3F (mirror) +$6280-$629F = RIOT Addresses $00-$1F (mirror) +$62A0-$62BF = RIOT Addresses $00-$1F (mirror) +$62C0-$62DF = RIOT Addresses $00-$1F (mirror) +$62E0-$62FF = RIOT Addresses $00-$1F (mirror) +$6300-$633F = TIA Addresses $00-$3F (mirror) +$6340-$637F = TIA Addresses $00-$3F (mirror) +$6380-$639F = RIOT Addresses $00-$1F (mirror) +$63A0-$63BF = RIOT Addresses $00-$1F (mirror) +$63C0-$63DF = RIOT Addresses $00-$1F (mirror) +$63E0-$63FF = RIOT Addresses $00-$1F (mirror) +$6400-$643F = TIA Addresses $00-$3F (mirror) +$6440-$647F = TIA Addresses $00-$3F (mirror) +$6480-$64FF = RIOT RAM (mirror) +$6500-$653F = TIA Addresses $00-$3F (mirror) +$6540-$657F = TIA Addresses $00-$3F (mirror) +$6580-$65FF = RIOT RAM (mirror) +$6600-$663F = TIA Addresses $00-$3F (mirror) +$6640-$667F = TIA Addresses $00-$3F (mirror) +$6680-$669F = RIOT Addresses $00-$1F (mirror) +$66A0-$66BF = RIOT Addresses $00-$1F (mirror) +$66C0-$66DF = RIOT Addresses $00-$1F (mirror) +$66E0-$66FF = RIOT Addresses $00-$1F (mirror) +$6700-$673F = TIA Addresses $00-$3F (mirror) +$6740-$677F = TIA Addresses $00-$3F (mirror) +$6780-$679F = RIOT Addresses $00-$1F (mirror) +$67A0-$67BF = RIOT Addresses $00-$1F (mirror) +$67C0-$67DF = RIOT Addresses $00-$1F (mirror) +$67E0-$67FF = RIOT Addresses $00-$1F (mirror) +$6800-$683F = TIA Addresses $00-$3F (mirror) +$6840-$687F = TIA Addresses $00-$3F (mirror) +$6880-$68FF = RIOT RAM (mirror) +$6900-$693F = TIA Addresses $00-$3F (mirror) +$6940-$697F = TIA Addresses $00-$3F (mirror) +$6980-$69FF = RIOT RAM (mirror) +$6A00-$6A3F = TIA Addresses $00-$3F (mirror) +$6A40-$6A7F = TIA Addresses $00-$3F (mirror) +$6A80-$6A9F = RIOT Addresses $00-$1F (mirror) +$6AA0-$6ABF = RIOT Addresses $00-$1F (mirror) +$6AC0-$6ADF = RIOT Addresses $00-$1F (mirror) +$6AE0-$6AFF = RIOT Addresses $00-$1F (mirror) +$6B00-$6B3F = TIA Addresses $00-$3F (mirror) +$6B40-$6B7F = TIA Addresses $00-$3F (mirror) +$6B80-$6B9F = RIOT Addresses $00-$1F (mirror) +$6BA0-$6BBF = RIOT Addresses $00-$1F (mirror) +$6BC0-$6BDF = RIOT Addresses $00-$1F (mirror) +$6BE0-$6BFF = RIOT Addresses $00-$1F (mirror) +$6C00-$6C3F = TIA Addresses $00-$3F (mirror) +$6C40-$6C7F = TIA Addresses $00-$3F (mirror) +$6C80-$6CFF = RIOT RAM (mirror) +$6D00-$6D3F = TIA Addresses $00-$3F (mirror) +$6D40-$6D7F = TIA Addresses $00-$3F (mirror) +$6D80-$6DFF = RIOT RAM (mirror) +$6E00-$6E3F = TIA Addresses $00-$3F (mirror) +$6E40-$6E7F = TIA Addresses $00-$3F (mirror) +$6E80-$6E9F = RIOT Addresses $00-$1F (mirror) +$6EA0-$6EBF = RIOT Addresses $00-$1F (mirror) +$6EC0-$6EDF = RIOT Addresses $00-$1F (mirror) +$6EE0-$6EFF = RIOT Addresses $00-$1F (mirror) +$6F00-$6F3F = TIA Addresses $00-$3F (mirror) +$6F40-$6F7F = TIA Addresses $00-$3F (mirror) +$6F80-$6F9F = RIOT Addresses $00-$1F (mirror) +$6FA0-$6FBF = RIOT Addresses $00-$1F (mirror) +$6FC0-$6FDF = RIOT Addresses $00-$1F (mirror) +$6FE0-$6FFF = RIOT Addresses $00-$1F (mirror) +$7000-$7FFF = ROM Addresses $000-$FFF (mirror) +$8000-$803F = TIA Addresses $00-$3F (mirror) +$8040-$807F = TIA Addresses $00-$3F (mirror) +$8080-$80FF = RIOT RAM (mirror) +$8100-$813F = TIA Addresses $00-$3F (mirror) +$8140-$817F = TIA Addresses $00-$3F (mirror) +$8180-$81FF = RIOT RAM (mirror) +$8200-$823F = TIA Addresses $00-$3F (mirror) +$8240-$827F = TIA Addresses $00-$3F (mirror) +$8280-$829F = RIOT Addresses $00-$1F (mirror) +$82A0-$82BF = RIOT Addresses $00-$1F (mirror) +$82C0-$82DF = RIOT Addresses $00-$1F (mirror) +$82E0-$82FF = RIOT Addresses $00-$1F (mirror) +$8300-$833F = TIA Addresses $00-$3F (mirror) +$8340-$837F = TIA Addresses $00-$3F (mirror) +$8380-$839F = RIOT Addresses $00-$1F (mirror) +$83A0-$83BF = RIOT Addresses $00-$1F (mirror) +$83C0-$83DF = RIOT Addresses $00-$1F (mirror) +$83E0-$83FF = RIOT Addresses $00-$1F (mirror) +$8400-$843F = TIA Addresses $00-$3F (mirror) +$8440-$847F = TIA Addresses $00-$3F (mirror) +$8480-$84FF = RIOT RAM (mirror) +$8500-$853F = TIA Addresses $00-$3F (mirror) +$8540-$857F = TIA Addresses $00-$3F (mirror) +$8580-$85FF = RIOT RAM (mirror) +$8600-$863F = TIA Addresses $00-$3F (mirror) +$8640-$867F = TIA Addresses $00-$3F (mirror) +$8680-$869F = RIOT Addresses $00-$1F (mirror) +$86A0-$86BF = RIOT Addresses $00-$1F (mirror) +$86C0-$86DF = RIOT Addresses $00-$1F (mirror) +$86E0-$86FF = RIOT Addresses $00-$1F (mirror) +$8700-$873F = TIA Addresses $00-$3F (mirror) +$8740-$877F = TIA Addresses $00-$3F (mirror) +$8780-$879F = RIOT Addresses $00-$1F (mirror) +$87A0-$87BF = RIOT Addresses $00-$1F (mirror) +$87C0-$87DF = RIOT Addresses $00-$1F (mirror) +$87E0-$87FF = RIOT Addresses $00-$1F (mirror) +$8800-$883F = TIA Addresses $00-$3F (mirror) +$8840-$887F = TIA Addresses $00-$3F (mirror) +$8880-$88FF = RIOT RAM (mirror) +$8900-$893F = TIA Addresses $00-$3F (mirror) +$8940-$897F = TIA Addresses $00-$3F (mirror) +$8980-$89FF = RIOT RAM (mirror) +$8A00-$8A3F = TIA Addresses $00-$3F (mirror) +$8A40-$8A7F = TIA Addresses $00-$3F (mirror) +$8A80-$8A9F = RIOT Addresses $00-$1F (mirror) +$8AA0-$8ABF = RIOT Addresses $00-$1F (mirror) +$8AC0-$8ADF = RIOT Addresses $00-$1F (mirror) +$8AE0-$8AFF = RIOT Addresses $00-$1F (mirror) +$8B00-$8B3F = TIA Addresses $00-$3F (mirror) +$8B40-$8B7F = TIA Addresses $00-$3F (mirror) +$8B80-$8B9F = RIOT Addresses $00-$1F (mirror) +$8BA0-$8BBF = RIOT Addresses $00-$1F (mirror) +$8BC0-$8BDF = RIOT Addresses $00-$1F (mirror) +$8BE0-$8BFF = RIOT Addresses $00-$1F (mirror) +$8C00-$8C3F = TIA Addresses $00-$3F (mirror) +$8C40-$8C7F = TIA Addresses $00-$3F (mirror) +$8C80-$8CFF = RIOT RAM (mirror) +$8D00-$8D3F = TIA Addresses $00-$3F (mirror) +$8D40-$8D7F = TIA Addresses $00-$3F (mirror) +$8D80-$8DFF = RIOT RAM (mirror) +$8E00-$8E3F = TIA Addresses $00-$3F (mirror) +$8E40-$8E7F = TIA Addresses $00-$3F (mirror) +$8E80-$8E9F = RIOT Addresses $00-$1F (mirror) +$8EA0-$8EBF = RIOT Addresses $00-$1F (mirror) +$8EC0-$8EDF = RIOT Addresses $00-$1F (mirror) +$8EE0-$8EFF = RIOT Addresses $00-$1F (mirror) +$8F00-$8F3F = TIA Addresses $00-$3F (mirror) +$8F40-$8F7F = TIA Addresses $00-$3F (mirror) +$8F80-$8F9F = RIOT Addresses $00-$1F (mirror) +$8FA0-$8FBF = RIOT Addresses $00-$1F (mirror) +$8FC0-$8FDF = RIOT Addresses $00-$1F (mirror) +$8FE0-$8FFF = RIOT Addresses $00-$1F (mirror) +$9000-$9FFF = ROM Addresses $000-$FFF (mirror) +$A000-$A03F = TIA Addresses $00-$3F (mirror) +$A040-$A07F = TIA Addresses $00-$3F (mirror) +$A080-$A0FF = RIOT RAM (mirror) +$A100-$A13F = TIA Addresses $00-$3F (mirror) +$A140-$A17F = TIA Addresses $00-$3F (mirror) +$A180-$A1FF = RIOT RAM (mirror) +$A200-$A23F = TIA Addresses $00-$3F (mirror) +$A240-$A27F = TIA Addresses $00-$3F (mirror) +$A280-$A29F = RIOT Addresses $00-$1F (mirror) +$A2A0-$A2BF = RIOT Addresses $00-$1F (mirror) +$A2C0-$A2DF = RIOT Addresses $00-$1F (mirror) +$A2E0-$A2FF = RIOT Addresses $00-$1F (mirror) +$A300-$A33F = TIA Addresses $00-$3F (mirror) +$A340-$A37F = TIA Addresses $00-$3F (mirror) +$A380-$A39F = RIOT Addresses $00-$1F (mirror) +$A3A0-$A3BF = RIOT Addresses $00-$1F (mirror) +$A3C0-$A3DF = RIOT Addresses $00-$1F (mirror) +$A3E0-$A3FF = RIOT Addresses $00-$1F (mirror) +$A400-$A43F = TIA Addresses $00-$3F (mirror) +$A440-$A47F = TIA Addresses $00-$3F (mirror) +$A480-$A4FF = RIOT RAM (mirror) +$A500-$A53F = TIA Addresses $00-$3F (mirror) +$A540-$A57F = TIA Addresses $00-$3F (mirror) +$A580-$A5FF = RIOT RAM (mirror) +$A600-$A63F = TIA Addresses $00-$3F (mirror) +$A640-$A67F = TIA Addresses $00-$3F (mirror) +$A680-$A69F = RIOT Addresses $00-$1F (mirror) +$A6A0-$A6BF = RIOT Addresses $00-$1F (mirror) +$A6C0-$A6DF = RIOT Addresses $00-$1F (mirror) +$A6E0-$A6FF = RIOT Addresses $00-$1F (mirror) +$A700-$A73F = TIA Addresses $00-$3F (mirror) +$A740-$A77F = TIA Addresses $00-$3F (mirror) +$A780-$A79F = RIOT Addresses $00-$1F (mirror) +$A7A0-$A7BF = RIOT Addresses $00-$1F (mirror) +$A7C0-$A7DF = RIOT Addresses $00-$1F (mirror) +$A7E0-$A7FF = RIOT Addresses $00-$1F (mirror) +$A800-$A83F = TIA Addresses $00-$3F (mirror) +$A840-$A87F = TIA Addresses $00-$3F (mirror) +$A880-$A8FF = RIOT RAM (mirror) +$A900-$A93F = TIA Addresses $00-$3F (mirror) +$A940-$A97F = TIA Addresses $00-$3F (mirror) +$A980-$A9FF = RIOT RAM (mirror) +$AA00-$AA3F = TIA Addresses $00-$3F (mirror) +$AA40-$AA7F = TIA Addresses $00-$3F (mirror) +$AA80-$AA9F = RIOT Addresses $00-$1F (mirror) +$AAA0-$AABF = RIOT Addresses $00-$1F (mirror) +$AAC0-$AADF = RIOT Addresses $00-$1F (mirror) +$AAE0-$AAFF = RIOT Addresses $00-$1F (mirror) +$AB00-$AB3F = TIA Addresses $00-$3F (mirror) +$AB40-$AB7F = TIA Addresses $00-$3F (mirror) +$AB80-$AB9F = RIOT Addresses $00-$1F (mirror) +$ABA0-$ABBF = RIOT Addresses $00-$1F (mirror) +$ABC0-$ABDF = RIOT Addresses $00-$1F (mirror) +$ABE0-$ABFF = RIOT Addresses $00-$1F (mirror) +$AC00-$AC3F = TIA Addresses $00-$3F (mirror) +$AC40-$AC7F = TIA Addresses $00-$3F (mirror) +$AC80-$ACFF = RIOT RAM (mirror) +$AD00-$AD3F = TIA Addresses $00-$3F (mirror) +$AD40-$AD7F = TIA Addresses $00-$3F (mirror) +$AD80-$ADFF = RIOT RAM (mirror) +$AE00-$AE3F = TIA Addresses $00-$3F (mirror) +$AE40-$AE7F = TIA Addresses $00-$3F (mirror) +$AE80-$AE9F = RIOT Addresses $00-$1F (mirror) +$AEA0-$AEBF = RIOT Addresses $00-$1F (mirror) +$AEC0-$AEDF = RIOT Addresses $00-$1F (mirror) +$AEE0-$AEFF = RIOT Addresses $00-$1F (mirror) +$AF00-$AF3F = TIA Addresses $00-$3F (mirror) +$AF40-$AF7F = TIA Addresses $00-$3F (mirror) +$AF80-$AF9F = RIOT Addresses $00-$1F (mirror) +$AFA0-$AFBF = RIOT Addresses $00-$1F (mirror) +$AFC0-$AFDF = RIOT Addresses $00-$1F (mirror) +$AFE0-$AFFF = RIOT Addresses $00-$1F (mirror) +$B000-$BFFF = ROM Addresses $000-$FFF (mirror) +$C000-$C03F = TIA Addresses $00-$3F (mirror) +$C040-$C07F = TIA Addresses $00-$3F (mirror) +$C080-$C0FF = RIOT RAM (mirror) +$C100-$C13F = TIA Addresses $00-$3F (mirror) +$C140-$C17F = TIA Addresses $00-$3F (mirror) +$C180-$C1FF = RIOT RAM (mirror) +$C200-$C23F = TIA Addresses $00-$3F (mirror) +$C240-$C27F = TIA Addresses $00-$3F (mirror) +$C280-$C29F = RIOT Addresses $00-$1F (mirror) +$C2A0-$C2BF = RIOT Addresses $00-$1F (mirror) +$C2C0-$C2DF = RIOT Addresses $00-$1F (mirror) +$C2E0-$C2FF = RIOT Addresses $00-$1F (mirror) +$C300-$C33F = TIA Addresses $00-$3F (mirror) +$C340-$C37F = TIA Addresses $00-$3F (mirror) +$C380-$C39F = RIOT Addresses $00-$1F (mirror) +$C3A0-$C3BF = RIOT Addresses $00-$1F (mirror) +$C3C0-$C3DF = RIOT Addresses $00-$1F (mirror) +$C3E0-$C3FF = RIOT Addresses $00-$1F (mirror) +$C400-$C43F = TIA Addresses $00-$3F (mirror) +$C440-$C47F = TIA Addresses $00-$3F (mirror) +$C480-$C4FF = RIOT RAM (mirror) +$C500-$C53F = TIA Addresses $00-$3F (mirror) +$C540-$C57F = TIA Addresses $00-$3F (mirror) +$C580-$C5FF = RIOT RAM (mirror) +$C600-$C63F = TIA Addresses $00-$3F (mirror) +$C640-$C67F = TIA Addresses $00-$3F (mirror) +$C680-$C69F = RIOT Addresses $00-$1F (mirror) +$C6A0-$C6BF = RIOT Addresses $00-$1F (mirror) +$C6C0-$C6DF = RIOT Addresses $00-$1F (mirror) +$C6E0-$C6FF = RIOT Addresses $00-$1F (mirror) +$C700-$C73F = TIA Addresses $00-$3F (mirror) +$C740-$C77F = TIA Addresses $00-$3F (mirror) +$C780-$C79F = RIOT Addresses $00-$1F (mirror) +$C7A0-$C7BF = RIOT Addresses $00-$1F (mirror) +$C7C0-$C7DF = RIOT Addresses $00-$1F (mirror) +$C7E0-$C7FF = RIOT Addresses $00-$1F (mirror) +$C800-$C83F = TIA Addresses $00-$3F (mirror) +$C840-$C87F = TIA Addresses $00-$3F (mirror) +$C880-$C8FF = RIOT RAM (mirror) +$C900-$C93F = TIA Addresses $00-$3F (mirror) +$C940-$C97F = TIA Addresses $00-$3F (mirror) +$C980-$C9FF = RIOT RAM (mirror) +$CA00-$CA3F = TIA Addresses $00-$3F (mirror) +$CA40-$CA7F = TIA Addresses $00-$3F (mirror) +$CA80-$CA9F = RIOT Addresses $00-$1F (mirror) +$CAA0-$CABF = RIOT Addresses $00-$1F (mirror) +$CAC0-$CADF = RIOT Addresses $00-$1F (mirror) +$CAE0-$CAFF = RIOT Addresses $00-$1F (mirror) +$CB00-$CB3F = TIA Addresses $00-$3F (mirror) +$CB40-$CB7F = TIA Addresses $00-$3F (mirror) +$CB80-$CB9F = RIOT Addresses $00-$1F (mirror) +$CBA0-$CBBF = RIOT Addresses $00-$1F (mirror) +$CBC0-$CBDF = RIOT Addresses $00-$1F (mirror) +$CBE0-$CBFF = RIOT Addresses $00-$1F (mirror) +$CC00-$CC3F = TIA Addresses $00-$3F (mirror) +$CC40-$CC7F = TIA Addresses $00-$3F (mirror) +$CC80-$CCFF = RIOT RAM (mirror) +$CD00-$CD3F = TIA Addresses $00-$3F (mirror) +$CD40-$CD7F = TIA Addresses $00-$3F (mirror) +$CD80-$CDFF = RIOT RAM (mirror) +$CE00-$CE3F = TIA Addresses $00-$3F (mirror) +$CE40-$CE7F = TIA Addresses $00-$3F (mirror) +$CE80-$CE9F = RIOT Addresses $00-$1F (mirror) +$CEA0-$CEBF = RIOT Addresses $00-$1F (mirror) +$CEC0-$CEDF = RIOT Addresses $00-$1F (mirror) +$CEE0-$CEFF = RIOT Addresses $00-$1F (mirror) +$CF00-$CF3F = TIA Addresses $00-$3F (mirror) +$CF40-$CF7F = TIA Addresses $00-$3F (mirror) +$CF80-$CF9F = RIOT Addresses $00-$1F (mirror) +$CFA0-$CFBF = RIOT Addresses $00-$1F (mirror) +$CFC0-$CFDF = RIOT Addresses $00-$1F (mirror) +$CFE0-$CFFF = RIOT Addresses $00-$1F (mirror) +$D000-$DFFF = ROM Addresses $000-$FFF (mirror) +$E000-$E03F = TIA Addresses $00-$3F (mirror) +$E040-$E07F = TIA Addresses $00-$3F (mirror) +$E080-$E0FF = RIOT RAM (mirror) +$E100-$E13F = TIA Addresses $00-$3F (mirror) +$E140-$E17F = TIA Addresses $00-$3F (mirror) +$E180-$E1FF = RIOT RAM (mirror) +$E200-$E23F = TIA Addresses $00-$3F (mirror) +$E240-$E27F = TIA Addresses $00-$3F (mirror) +$E280-$E29F = RIOT Addresses $00-$1F (mirror) +$E2A0-$E2BF = RIOT Addresses $00-$1F (mirror) +$E2C0-$E2DF = RIOT Addresses $00-$1F (mirror) +$E2E0-$E2FF = RIOT Addresses $00-$1F (mirror) +$E300-$E33F = TIA Addresses $00-$3F (mirror) +$E340-$E37F = TIA Addresses $00-$3F (mirror) +$E380-$E39F = RIOT Addresses $00-$1F (mirror) +$E3A0-$E3BF = RIOT Addresses $00-$1F (mirror) +$E3C0-$E3DF = RIOT Addresses $00-$1F (mirror) +$E3E0-$E3FF = RIOT Addresses $00-$1F (mirror) +$E400-$E43F = TIA Addresses $00-$3F (mirror) +$E440-$E47F = TIA Addresses $00-$3F (mirror) +$E480-$E4FF = RIOT RAM (mirror) +$E500-$E53F = TIA Addresses $00-$3F (mirror) +$E540-$E57F = TIA Addresses $00-$3F (mirror) +$E580-$E5FF = RIOT RAM (mirror) +$E600-$E63F = TIA Addresses $00-$3F (mirror) +$E640-$E67F = TIA Addresses $00-$3F (mirror) +$E680-$E69F = RIOT Addresses $00-$1F (mirror) +$E6A0-$E6BF = RIOT Addresses $00-$1F (mirror) +$E6C0-$E6DF = RIOT Addresses $00-$1F (mirror) +$E6E0-$E6FF = RIOT Addresses $00-$1F (mirror) +$E700-$E73F = TIA Addresses $00-$3F (mirror) +$E740-$E77F = TIA Addresses $00-$3F (mirror) +$E780-$E79F = RIOT Addresses $00-$1F (mirror) +$E7A0-$E7BF = RIOT Addresses $00-$1F (mirror) +$E7C0-$E7DF = RIOT Addresses $00-$1F (mirror) +$E7E0-$E7FF = RIOT Addresses $00-$1F (mirror) +$E800-$E83F = TIA Addresses $00-$3F (mirror) +$E840-$E87F = TIA Addresses $00-$3F (mirror) +$E880-$E8FF = RIOT RAM (mirror) +$E900-$E93F = TIA Addresses $00-$3F (mirror) +$E940-$E97F = TIA Addresses $00-$3F (mirror) +$E980-$E9FF = RIOT RAM (mirror) +$EA00-$EA3F = TIA Addresses $00-$3F (mirror) +$EA40-$EA7F = TIA Addresses $00-$3F (mirror) +$EA80-$EA9F = RIOT Addresses $00-$1F (mirror) +$EAA0-$EABF = RIOT Addresses $00-$1F (mirror) +$EAC0-$EADF = RIOT Addresses $00-$1F (mirror) +$EAE0-$EAFF = RIOT Addresses $00-$1F (mirror) +$EB00-$EB3F = TIA Addresses $00-$3F (mirror) +$EB40-$EB7F = TIA Addresses $00-$3F (mirror) +$EB80-$EB9F = RIOT Addresses $00-$1F (mirror) +$EBA0-$EBBF = RIOT Addresses $00-$1F (mirror) +$EBC0-$EBDF = RIOT Addresses $00-$1F (mirror) +$EBE0-$EBFF = RIOT Addresses $00-$1F (mirror) +$EC00-$EC3F = TIA Addresses $00-$3F (mirror) +$EC40-$EC7F = TIA Addresses $00-$3F (mirror) +$EC80-$ECFF = RIOT RAM (mirror) +$ED00-$ED3F = TIA Addresses $00-$3F (mirror) +$ED40-$ED7F = TIA Addresses $00-$3F (mirror) +$ED80-$EDFF = RIOT RAM (mirror) +$EE00-$EE3F = TIA Addresses $00-$3F (mirror) +$EE40-$EE7F = TIA Addresses $00-$3F (mirror) +$EE80-$EE9F = RIOT Addresses $00-$1F (mirror) +$EEA0-$EEBF = RIOT Addresses $00-$1F (mirror) +$EEC0-$EEDF = RIOT Addresses $00-$1F (mirror) +$EEE0-$EEFF = RIOT Addresses $00-$1F (mirror) +$EF00-$EF3F = TIA Addresses $00-$3F (mirror) +$EF40-$EF7F = TIA Addresses $00-$3F (mirror) +$EF80-$EF9F = RIOT Addresses $00-$1F (mirror) +$EFA0-$EFBF = RIOT Addresses $00-$1F (mirror) +$EFC0-$EFDF = RIOT Addresses $00-$1F (mirror) +$EFE0-$EFFF = RIOT Addresses $00-$1F (mirror) +$F000-$FFFF = ROM Addresses $000-$FFF (mirror) diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_riot_map.txt b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_riot_map.txt new file mode 100644 index 00000000..6abfc0aa --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_riot_map.txt @@ -0,0 +1,140 @@ +-------------------------------- +RIOT Addresses: names taken from +Atari VCS Stella Documentation +-------------------------------- +$0280 = (RIOT $00) - SWCHA (read/write) +$0281 = (RIOT $01) - SWACNT (read/write) +$0282 = (RIOT $02) - SWCHB (read/write) (*) +$0283 = (RIOT $03) - SWBCNT (read/write) (*) +$0284 = (RIOT $04) - INTIM (read), edge detect control (write) +$0285 = (RIOT $05) - read interrupt flag (read), edge detect control (write) +$0286 = (RIOT $06) - INTIM (read), edge detect control (write) +$0287 = (RIOT $07) - read interrupt flag (read), edge detect control (write) +$0288 = (RIOT $08) - SWCHA (read/write) +$0289 = (RIOT $09) - SWACNT (read/write) +$028A = (RIOT $0A) - SWCHB (read/write) (*) +$028B = (RIOT $0B) - SWBCNT (read/write) (*) +$028C = (RIOT $0C) - INTIM (read) , edge detect control (write) +$028D = (RIOT $0D) - read interrupt flag (read), edge detect control (write) +$028E = (RIOT $0E) - INTIM (read) , edge detect control (write) +$028F = (RIOT $0F) - read interrupt flag (read), edge detect control (write) +$0290 = (RIOT $10) - SWCHA (read/write) +$0291 = (RIOT $11) - SWACNT (read/write) +$0292 = (RIOT $12) - SWCHB (read/write) (*) +$0293 = (RIOT $13) - SWBCNT (read/write) (*) +$0294 = (RIOT $14) - INTIM (read), TIM1T (write) +$0295 = (RIOT $15) - read interrupt flag (read), TIM8T (write) +$0296 = (RIOT $16) - INTIM (read), TIM64T (write) +$0297 = (RIOT $17) - read interrupt flag (read), TIM1024T (write) +$0298 = (RIOT $18) - SWCHA (read/write) +$0299 = (RIOT $19) - SWACNT (read/write) +$029A = (RIOT $1A) - SWCHB (read/write) (*) +$029B = (RIOT $1B) - SWBCNT (read/write) (*) +$029C = (RIOT $1C) - INTIM (read), TIM1T (write) +$029D = (RIOT $1D) - read interrupt flag (read), TIM8T (write) +$029E = (RIOT $1E) - INTIM (read), TIM64T (write) +$029F = (RIOT $1F) - read interrupt flag (read), TIM1024T (write) + +(*) The Stella documentation states that Port B is hardwired for input + only. In reality, Port B is fully configurable. Configuring Port B + as an output, however, will cause direct hardware conflicts with the + console switches. DO NOT ATTEMPT THIS. + +------------------------------------------------------------------------------ + +********************** +* RIOT Documentation * +********************** + +--------------------------------------- +RIOT Addressing Notes for the Atari VCS +--------------------------------------- +A12 is connected to /CS2 (Chip Select 2 - active low) +A7 is connected to CS1 (Chip Select 1 - active high) +A9 is connected to /RS (RAM Select - active low) +A11, A10, and A8 are not connected to the RIOT + +------------------------------------- +$0080 - $00FF = RIOT RAM (read/write) +------------------------------------- +$0080 - $00FF = RIOT RAM, Addresses $00-$7F + +--------------------------------------------- +$0280 - $029F = RIOT Read Addresses (not RAM) +--------------------------------------------- +$0280 = (RIOT $00) - Read DRA +$0281 = (RIOT $01) - Read DDRA +$0282 = (RIOT $02) - Read DRB +$0283 = (RIOT $03) - Read DDRB +$0284 = (RIOT $04) - Read timer, disable interrupt (2) +$0285 = (RIOT $05) - Read interrupt flag +$0286 = (RIOT $06) - Read timer, disable interrupt (2) +$0287 = (RIOT $07) - Read interrupt flag +$0288 = (RIOT $08) - Read DRA +$0289 = (RIOT $09) - Read DDRA +$028A = (RIOT $0A) - Read DRB +$028B = (RIOT $0B) - Read DDRB +$028C = (RIOT $0C) - Read timer, enable interrupt (2) +$028D = (RIOT $0D) - Read interrupt flag +$028E = (RIOT $0E) - Read timer, enable interrupt (2) +$028F = (RIOT $0F) - Read interrupt flag +$0290 = (RIOT $10) - Read DRA +$0291 = (RIOT $11) - Read DDRA +$0292 = (RIOT $12) - Read DRB +$0293 = (RIOT $13) - Read DDRB +$0294 = (RIOT $14) - Read timer, disable interrupt (2) +$0295 = (RIOT $15) - Read interrupt flag +$0296 = (RIOT $16) - Read timer, disable interrupt (2) +$0297 = (RIOT $17) - Read interrupt flag +$0298 = (RIOT $18) - Read DRA +$0299 = (RIOT $19) - Read DDRA +$029A = (RIOT $1A) - Read DRB +$029B = (RIOT $1B) - Read DDRB +$029C = (RIOT $1C) - Read timer, enable interrupt (2) +$029D = (RIOT $1D) - Read interrupt flag +$029E = (RIOT $1E) - Read timer, enable interrupt (2) +$029F = (RIOT $1F) - Read interrupt flag + +---------------------------------------------- +$0280 - $029F = RIOT Write Addresses (not RAM) +---------------------------------------------- +$0280 = (RIOT $00) - Write DRA +$0281 = (RIOT $01) - Write DDRA +$0282 = (RIOT $02) - Write DRB +$0283 = (RIOT $03) - Write DDRB +$0284 = (RIOT $04) - Write edge detect control - negative edge, disable int (1) +$0285 = (RIOT $05) - Write edge detect control - positive edge, disable int (1) +$0286 = (RIOT $06) - Write edge detect control - negative edge, enable int (1) +$0287 = (RIOT $07) - Write edge detect control - positive edge, enable int (1) +$0288 = (RIOT $08) - Write DRA +$0289 = (RIOT $09) - Write DDRA +$028A = (RIOT $0A) - Write DRB +$028B = (RIOT $0B) - Write DDRB +$028C = (RIOT $0C) - Write edge detect control - negative edge, disable int (1) +$028D = (RIOT $0D) - Write edge detect control - positive edge, disable int (1) +$028E = (RIOT $0E) - Write edge detect control - negative edge, enable int (1) +$028F = (RIOT $0F) - Write edge detect control - positive edge, enable int (1) +$0290 = (RIOT $10) - Write DRA +$0291 = (RIOT $11) - Write DDRA +$0292 = (RIOT $12) - Write DRB +$0293 = (RIOT $13) - Write DDRB +$0294 = (RIOT $14) - Write timer (div by 1) - disable int (2) +$0295 = (RIOT $15) - Write timer (div by 8) - disable int (2) +$0296 = (RIOT $16) - Write timer (div by 64) - disable int (2) +$0297 = (RIOT $17) - Write timer (div by 1024) - disable int (2) +$0298 = (RIOT $18) - Write DRA +$0299 = (RIOT $19) - Write DDRA +$029A = (RIOT $1A) - Write DRB +$029B = (RIOT $1B) - Write DDRB +$029C = (RIOT $1C) - Write timer (div by 1) - enable int (2) +$029D = (RIOT $1D) - Write timer (div by 8) - enable int (2) +$029E = (RIOT $1E) - Write timer (div by 64) - enable int (2) +$029F = (RIOT $1F) - Write timer (div by 1024) - enable int (2) + +(1) A0 = 0 for negative edge detect + A0 = 1 for positive edge detect + A1 = 0 to disable interrupt from PA7 to /IRQ + A1 = 1 to enable interrupt from PA7 to /IRQ + +(2) A3 = 0 to disable interrupt from timer to /IRQ + A3 = 1 to enable interrupt from timer to /IRQ diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_tia_map.txt b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_tia_map.txt new file mode 100644 index 00000000..6305383d --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/2600_tia_map.txt @@ -0,0 +1,81 @@ +********************* +* TIA Documentation * +********************* + +-------------------------------------- +TIA Addressing Notes for the Atari VCS +-------------------------------------- +A12 is connected to /CS0 (Chip Select 0 - active low) +A7 is connected to /CS3 (Chip Select 3 - active low) +A[11:8] and A6 are not connected to the TIA + +VCC is connected to CS1 (Chip Select 1 - active high) +GND is connected to /CS2 (Chip Select 2 - active low) + +-------------------------------------------- +$0000 - $003F = TIA.......(write)......(read) +-------------------------------------------- +$0000 = TIA Address $00 - (VSYNC)......(CXM0P) +$0001 = TIA Address $01 - (VBLANK).....(CXM1P) +$0002 = TIA Address $02 - (WSYNC)......(CXP0FB) +$0003 = TIA Address $03 - (RSYNC)......(CXP1FB) +$0004 = TIA Address $04 - (NUSIZ0).....(CXM0FB) +$0005 = TIA Address $05 - (NUSIZ1).....(CXM1FB) +$0006 = TIA Address $06 - (COLUP0).....(CXBLPF) +$0007 = TIA Address $07 - (COLUP1).....(CXPPMM) +$0008 = TIA Address $08 - (COLUPF).....(INPT0) +$0009 = TIA Address $09 - (COLUBK).....(INPT1) +$000A = TIA Address $0A - (CTRLPF).....(INPT2) +$000B = TIA Address $0B - (REFP0)......(INPT3) +$000C = TIA Address $0C - (REFP1)......(INPT4) +$000D = TIA Address $0D - (PF0)........(INPT5) +$000E = TIA Address $0E - (PF1)........(UNDEFINED) +$000F = TIA Address $0F - (PF2)........(UNDEFINED) +$0010 = TIA Address $10 - (RESP0)......(CXM0P) +$0011 = TIA Address $11 - (RESP1)......(CXM1P) +$0012 = TIA Address $12 - (RESM0)......(CXP0FB) +$0013 = TIA Address $13 - (RESM1)......(CXP1FB) +$0014 = TIA Address $14 - (RESBL)......(CXM0FB) +$0015 = TIA Address $15 - (AUDC0)......(CXM1FB) +$0016 = TIA Address $16 - (AUDC1)......(CXBLPF) +$0017 = TIA Address $17 - (AUDF0)......(CXPPMM) +$0018 = TIA Address $18 - (AUDF1)......(INPT0) +$0019 = TIA Address $19 - (AUDV0)......(INPT1) +$001A = TIA Address $1A - (AUDV1)......(INPT2) +$001B = TIA Address $1B - (GRP0).......(INPT3) +$001C = TIA Address $1C - (GRP1).......(INPT4) +$001D = TIA Address $1D - (ENAM0)......(INPT5) +$001E = TIA Address $1E - (ENAM1)......(UNDEFINED) +$001F = TIA Address $1F - (ENABL)......(UNDEFINED) +$0020 = TIA Address $20 - (HMP0).......(CXM0P) +$0021 = TIA Address $21 - (HMP1).......(CXM1P) +$0022 = TIA Address $22 - (HMM0).......(CXP0FB) +$0023 = TIA Address $23 - (HMM1).......(CXP1FB) +$0024 = TIA Address $24 - (HMBL).......(CXM0FB) +$0025 = TIA Address $25 - (VDELP0).....(CXM1FB) +$0026 = TIA Address $26 - (VDELP1).....(CXBLPF) +$0027 = TIA Address $27 - (VDELBL).....(CXPPMM) +$0028 = TIA Address $28 - (RESMP0).....(INPT0) +$0029 = TIA Address $29 - (RESMP1).....(INPT1) +$002A = TIA Address $2A - (HMOVE)......(INPT2) +$002B = TIA Address $2B - (HMCLR)......(INPT3) +$002C = TIA Address $2C - (CXCLR)......(INPT4) +$002D = TIA Address $2D - (UNDEFINED)..(INPT5) +$002E = TIA Address $2E - (UNDEFINED)..(UNDEFINED) +$002F = TIA Address $2F - (UNDEFINED)..(UNDEFINED) +$0030 = TIA Address $30 - (UNDEFINED)..(CXM0P) +$0031 = TIA Address $31 - (UNDEFINED)..(CXM1P) +$0032 = TIA Address $32 - (UNDEFINED)..(CXP0FB) +$0033 = TIA Address $33 - (UNDEFINED)..(CXP1FB) +$0034 = TIA Address $34 - (UNDEFINED)..(CXM0FB) +$0035 = TIA Address $35 - (UNDEFINED)..(CXM1FB) +$0036 = TIA Address $36 - (UNDEFINED)..(CXBLPF) +$0037 = TIA Address $37 - (UNDEFINED)..(CXPPMM) +$0038 = TIA Address $38 - (UNDEFINED)..(INPT0) +$0039 = TIA Address $39 - (UNDEFINED)..(INPT1) +$003A = TIA Address $3A - (UNDEFINED)..(INPT2) +$003B = TIA Address $3B - (UNDEFINED)..(INPT3) +$003C = TIA Address $3C - (UNDEFINED)..(INPT4) +$003D = TIA Address $3D - (UNDEFINED)..(INPT5) +$003E = TIA Address $3E - (UNDEFINED)..(UNDEFINED) +$003F = TIA Address $3F - (UNDEFINED)..(UNDEFINED) diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/Stella Programmer's Guide (Unofficial HTML version).html b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/Stella Programmer's Guide (Unofficial HTML version).html new file mode 100644 index 00000000..d07bfafd --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/Stella Programmer's Guide (Unofficial HTML version).html @@ -0,0 +1,2375 @@ + + + + + Stella Programmer's Guide (Unofficial HTML version) + + + +
+

+STELLA
+PROGRAMMER'S
+GUIDE
+

+by Steve Wright 12/03/79

+Reconstructed by Charles Sinnett 6/11/93
+ Email: cas@mentor.cc.purdue.edu

+HTMLified by B. Watson 9/14/2001
+Email: atari@hardcoders.org
+(Editor's notes --BW) +

+ +

+ +

TABLE OF CONTENTS

+ + +
+ + + +

TELEVISION PROTOCOL

+ (The TV picture according to Atari)
+

+ +For the purposes of Stella programming, a single television "frame" consists +of 262 horizontal lines, and each line is divided by 228 clock counts +(3.58MHz). The actual TV picture is drawn line by line from the top down +60 times a second, and actaully consists of only a portion of the entire +"frame" (see diag. #1). A typical frame will consists of 3 vertical sync +(VSYNC) lines*, 37 vertical blank (VBLANK) lines, 192 TV picture lines, +and 30 overscan lines. Atari's research has shown that this pattern will +work on all types of TV sets. Each scan lines starts with 68 clock counts of +horizontal blank (not seen on the TV screen) followed by 160 clock counts to +fully scan one line of TV picture. When the electron beam reaches the end +of a scan line, it returns to the left side of the screen, waits for the 68 +horizontal blank clock counts, and proceeds to draw the next line below. +

+All horizontal timing is taken care of by hardware, but the microprocessor +must "manually" control vertical timing to signal the start of the next +frame. When the last line of the previous frame is detected, the +microprocessor must generate 3 lines of VSYNC, 37 lines of VBLANK, 192 +lines of actual TV picture, and 30 lines of overscan. Fortunately, both +VSYNC and VBLANK can simply be turned on and off at the appropriate +times, freeing the microprocessor for other activities during their execution. + +

+* (to signal the TV set to start a new frame) + +

+The actual TV picture is drawn one line at a time by having the +microprocessor enter the data for that line into the Television Interface +Adaptor (TIA) chip, which then converts the data into video signals. The +TIA can only have data in it that pertains to the line being currently drawn, +so the microprocessor must be "one step ahead" of the electron beam on each +line. Since one microprocessor machine cycle occurs every 3 clock counts, +the programmer has only 76 machine cycles per line (228/3 = 76) to +construct the actual picture (actually less because the microprocessor must +be ahead of the raster). To allow more time for the software, it is customary +(but not required) to update the TIA every two scan lines. The portion of +the program that constructs this TV picture is referred to as the "Kernel", as +it is the essence or kernel of the game. + +

+In general, the remaining 70 scan lines (3 for VSYNC, 37 for VBLANK, and +30 for overscan) will provides 5,320 machine cycles (70 lines x 76 machine +cycles) for housekeeping and game logic. Such activities as calculating the +new position of a player, updating the score, and checking for new inputs +are typically done during this time. + +

+ +Diagram 1 - Atari TV Frame +

+
+ + +

The TIA (as seen by the programmer)

+ + +1.0 General Description

+

+The TIA is a custom IC designed to create the TV picture and sound from +the instructions sent to it by the microprocessor. It converts the 8 bit +parallel data from the microprocessor into signals that are sent to video +modulation circuits which combine and shape those signals to be compatible +with ordinary TV reception. A "playfield" and 5 moveable objects can be +created and manipulated by software. +

+A playfield consisting of walls, clouds, barriers, and other seldom moved +objects can be created over a colored background. The 5 moveable objects +can be positioned anywhere, and consists of 2 players, 2 missiles, and a ball. +The playfield, players, missiles, and ball are created and manipulated by a +series of registers in the TIA that the microprocessor can address and write +into. Each type of object has certain defined capabilities. For example, a +player can be moved with one instruction, but the playfield must be +completely re-drawn in order to make it "move". +

+Color and luminosity (brightness) can be assigned to the background, +playfield, and 5 moveable objects. Sound can also be generated and +controlled for volume, pitch, and type of sound. Collisions between the +various objects on the TV screen are detected by the TIA and can be read by +the microprocessor . Input ports which can be read by the microprocessor +give the status of some of the various hand held controllers. +

+

+ +2.0 The Registers +

+

+All instructions to the TIA are achieved by addressing and writing to +various registers in the chip. A key point to remember is data written in a +register is latched an retained until altered by another write operation into +that register. For example, if the color register for a player is set for red, +that player will be red every time it is drawn until that color register is +changed. All of the registers are addressed by the microprocessor as part of +the overall RAM/ROM memory space. +

+All registers have fixed address locations and pre-assigned address names for handy +reference. Many registers do not use all 8 data bits, and some registers are used to +"strobe" or trigger events. A "strobe" register executes its function the instant it is +written to (the data written is ignored). The only registers the microprocessor can read +are the collision registers and input port registers. These registers are conveniently +arranged so that the data bits of interest always appear as data bits 6 or 7 for easy access. +

+ +

+ + +3.0 Synchronization

+

+

+ +3.1 Horizontal Timing +
+ When the electron beam scans across the TV screen and reaches the right + edge, it must be turned off and moved back to the left edge of the screen to + begin the next scan line. The TIA takes care of this automatically, + independent of the microprocessor. A 3.58 MHz oscillator generates clock + pulses called "color clocks" which go into a pulse counter in the TIA. This + counter allows 160 color clocks for the beam to reach the right edge, then + generates a horizontal sync signal (HSYNC) to return the beam to the left + edge. It also generates the signal to turn the beam off (horizontal blanking) + during its return time of 68 color clocks. Total round trip for the electron + beam is 160 + 68 = 228 color clocks. Again, all the horizontal timing is + taken care of by the TIA without assistance from the microprocessor. +

+

+ +3.2 Microprocessor Synchronization +
+ The microprocessor's clock is the 3.58 MHz oscillator divided by 3, so one + machine cycle is 3 color clocks. Therefore, one complete scan line of 228 + color clocks allows only 76 machine cycles (228/3 = 76) per scan line. The + microprocessor must be synchronized with the TIA on a line-by-line basis, + but program loops and branches take unpredictable lengths of time. To + solve this software sync. problem, the programmer can use the WSYNC + (Wait for SYNC) strobe register. Simply writing to the WSYNC causes the + microprocessor to halt until the electron beam reaches the right edge of the + screen, then the microprocessor resumes operation at the beginning of the + 68 color clocks for horizontal blanking. Since the TIA latches all + instructions until altered by another write operation, it could be updated + every 2 or 3 lines. The advantage is the programmer gains more time to + execute software, but at a price paid with lower vertical resolution in the + graphics. +

+ NOTE: WSYNC and all the following addresses' bit structures are itemized + in the TIA hardware manual. The purpose of this document is to make + them understandable. +

+

+ +3.3 Vertical timing +
+ When the electron beam has scanned 262 lines, the TV set must be signaled + to blank the beam and position it at the top of the screen to start a new + frame. This signal is called vertical sync, and the TIA must transmit this + signal for at least 3 scan lines. This is accomplished by writing a "1" in D1 + of VSYNC to turn it on, count at least 2 scan lines, then write a "0" to D1 of + VSYNC to turn it off. +

+ To physically turn the beam off during its repositioning time, the TV set + needs 37 scan lines of vertical blanks signal from the TIA. This is + accomplished by writing a "1" in D1 of VBLANK to turn it on, count 37 + lines, then write a "0" to D1 of VBLANK to turn it off. The microprocessor + is of course free to execute other software during the vertical timing + commands, VSYNC and VBLANK. +

+

+
+ +4.0 Color and Luminosity +

+

+ Color and luminosity can be assigned to the background (BK), playfield + (PF), ball (BL), player 0 (P0), player 1(P1), missile 0 (M0), and missile 1 + (M1). There are only four color-lum registers for these 7 objects, so the + objects are paired to share the same register according to the following list: +

+

+ + + + + + + +
color-lum register Objects colored
COLUMP0 P0, M0 (player 0, missile 0)
COLUMP1 P1, M1 (player 1, missile 1)
COLUMPF PF, BL (playfield, ball)
COLUMBK BK (background)
+
+

+ For example, if the COLUMP0 register is set for light red, both P0 and M0 + will be light red when drawn. +

+ A color-lum register is set for both color and luminosity by writing a single 7 + bit instruction to that register. Four of the bits select one of the 16 + available colors, and the other 3 bits select one of 8 levels of luminosity + (brightness). The specific codes required to create specific color and lum are + listed in the Detailed Address List of the TIA hardware manual. As with all + registers (except the "strobe" registers), the data written to them is latched + until altered by another write operation. +

+(These registers are referred to here as COLUMxx, but everywhere else +they are COLUxx, including later in this document. --BW) +

+ +5.0 Playfield +

+

+ The PF register is used to create a playfield of walls, clouds, barriers, etc., that are + seldom moved. This low resolution register is written into to draw the left half of the + TV screen only. The right half of the screen is drawn by software selection of whether a + duplication or a reflection of the right half. +

+ The PF register is 20 bits wide, so the 20 bits are written into 3 addresses: + PF0, PF1, and PF2. PF0 is only 4 bits wide and constructs the first 4 "bits" + of the playfield, starting at the left edge of the TV screen. PF1 constructs + the next 8 "bits", and PF2 the last 8 bits" which end at the center of the + screen. The PF register is scanned from left to right and where a "1" is + found the PF color is drawn, and where a "0" is found, the BK color is + drawn. To clear the playfield, obviously zeros must be written into PF0, + PF1, and PF2. +

+ To make the right half of the playfield into a duplication or copy of the left + half, a "0" is written to D0 of the CTLPF (control playfield) register. + Writing a "1" will cause the reflection to be displayed. +

+

+ +6.0 The Moveable Objects Graphics +

+

+ All 5 moveable objects (P0, M0, P1, M1, BL) can be assigned a horizontal + location on the screen and moved left or right relative to their location. + Vertical positions, however, are treated in an entirely different manner. In + principle, these objects appear at whatever scan lines their graphics + registers are enabled. For example, let us assume the ball is to be + positioned vertically in the center of the screen. The screen has 192 scan + lines and we want the ball to be 2 scan lines "thick". The ball graphics + would be disabled until scan line 96, enabled for 2 scan lines, then disabled + for the rest of the frame. Each type of object (players, missiles, and ball) + has its own characteristics and limitations. +

+

+ +6.1 Missile Graphics (M0, M1) +
+ The two missile graphics registers will draw a missile on any scan line by + writing a "1" to the one bit enable missile registers (ENAM0, ENAM1). + Writing a "0" to these registers will disable the graphics. The missiles' left + edge is positioned by a horizontal position register, but the right edge is a + function of how wide the missile is make. Width of a missile is controlled by + writing into bits D4 and D5 of the number-size registers (NUSIZ0, + NUSIZ1). This has the effect of "stretching" the missile out over 1,2,4, or 8 + color clock counts (a full scan line is 160 color clocks). +

+

+ +6.2 Ball Graphics (BL) +
+ The ball graphics register works just like the missile registers. Writing a + "1" to the enable ball register (ENABL) enables the ball graphics until the + register is disabled. The ball can also be "stretched" to widths of 1, 2, 4, or 8 + color clock counts by writing to bits D4 and D5 of the CTRLPF register. +

+ The ball can also be vertically delayed one can line. For example, if the ball + graphics were enabled on scan line 95, it could be delayed to not display on + the screen until scan line 96 by writing a "1" to D0 of the vertical delay + (VDELBL) register. The reason for having a vertical delay capability is + because most programs will update the TIA every 2 lines. This confines all + vertical movements of objects to 2 scan line "jumps". The use of vertical + delay allows the objects to move one scan line at a time. +

+

+ +6.3 Player Graphics (P0, P1) +
+ The player graphics are the most sophisticated of all the moveable objects. + They have all the capabilities of the missile and ball graphics, plus three + move capabilities. Players can take on a "shape" such as a man or an + airplane, and the player can easily be flipped over horizontally to display + the mirror image (reflection) instead of the original image, plus multiple + copies of the players can be created. +

+ The player graphics are drawn line-by-line like all other graphics. The + difference here is each scan line of the player is 8 "bits" wide, whereas the + missiles and ball are one "bit" wide. Therefore, a player can be though of as + being drawn of graph paper 8 squares wide and as tall as desired. To "color + in the squares" of this imaginary graph paper, 8 data bits are written into + the players graphics registers (GP0, GP1). This 8 bit register is scanned + from D7 to D0, and wherever a "1" is found that "square" gets the players' + color (from the color-lum register) and where a "0" is found that "square" + gets the background color. To position a player vertically, simply leave all + "0's" in the graphics registers (GP0, GP1) until the electron beam is on the + scan line desired, write to the graphics register line-by-line describing the + player, then write all "0's" to turn off the players' graphics until the end of + that frame. +

+ To display a mirror image (reflection) instead of the original figure, write a + "1" to D3 of the one bit reflection register (REFP0, REFP1). A "0" written to + these registers restores the original figure. +

+ Multiple copies of players as well as their size are controlled by writing 3 + bits (D0, D1, D2) into the number-size registers (NUSIZ0, NUSIZ1). These + three bits select from 1 to 3 copies of the player, spacing of those copies, as + well as the size of the player (each "square" of the player can be 1, 2, or 4 + clocks wide). Whenever multiple copies are selected, the TIA automatically + creates the same number of copies of the missile for that player. Again, the + specifics of all this are laid out in the TIA hardware manual. +

+ Vertical delay for the players works exactly like the ball by writing a "1" to + D0 in the players' vertical delay registers (VDELP0, VDELP1). Writing a + "0" to these locations disables the vertical delay. +

+

+ +7.0 Horizontal Positioning +
+

+ The horizontal position of each object is set by writing to its associated reset + register (RESP0, RESP1, RESM0, RESM1, RESBL) which are all "strobe" + registers (they trigger their function as soon as they are addressed). That + causes the object to be positioned wherever the electron bean was in its + sweep across the screen when the register was reset. for example, if the + electron beam was 60 color clocks into a scan line when RESP0 was written + to, player 0 would be positioned 60 color clocks "in" on the next scan line. + Whether or not P0 is actually drawn on the screen is a function of the data + in the GP0 register, but if it were drawn, it would show up at 60. Resets to + these registers anywhere during horizontal blanking will position objects at + the left edge of the screen (color clock 0). Since there are 3 color clocks per + machine cycle, and it can take up to 5 machine cycles to write the register, + the programmer is confined to positioning the objects at 15 color clock + intervals across the screen. This "course" positioning is "fine tuned" by the + Horizontal Motion, explained in section 8.0. +

+ Missiles have an additional positioning command. Writing a "1" to D1 of + the reset missile-to-player register (RESMP0, RESMP1) disables that + missiles' graphics (turns it off) and repositions it horizontally to the center + of its associated player. Until a "0" is written to the register, the missile's + horizontal position is locked to the center of its player in preparation to be + fired again. +

+

+ +8.0 Horizontal Motion +
+

+ Horizontal motion allows the programmer to move any of the 5 graphics + objects relative to their current horizontal position. Each object has a 4 bit + horizontal motion register (HMP0, HMP1, HMM0, HMM1, HMBL) that can + be loaded with a value in the range of +7 to -8 (negative values are + expressed in two's complement from). This motion is not executed until the + HMOVE register is written to, at which time all motion registers move their + respective objects. Objects can be moved repeatedly by simply executing + HMOVE. Any object that is not to move must have a 0 in its motion + register. With the horizontal positioning command confined to positioning + objects at 15 color clock intervals, the motion registers fills in the gaps by + moving objects +7 to -8 color clocks. Objects can not be placed at any color + clock position across the screen. All 5 motion registers can be set to zero + simultaneously by writing to the horizontal motion clear register (HMCLR). +

+ There are timing constraints for the HMOVE command. The HMOVE + command must immediately follow a WSYNC (Wait for SYNC) to insure the + HMOVE operation occurs during horizontal blanking. This is to allow + sufficient time for the motion registers to do their thing before the electron + beam starts drawing the next scan line. Also, for mysterious internal + hardware considerations, the motion registers should not be modified for at + least 24 machine cycles after an HMOVE command. +

+

+ +9.0 Object Priorities +
+

+ Each object is assigned a priority so when any two objects overlap the one + with the highest priority will appear to move in front of the other. To + simplify hardware logic, the missiles have the same priority as their + associated player, and the ball has the same priority as the playfield. The + background, of course, has the lowest priority. The following table + illustrates the normal (default) priority assignments. +

+

+ + + + + + +
Priority Objects
1 P0, M0
2 P1, M1
3 BL, PF
4 BK
+
+

+ This priority assignment means that players and missiles will move in front + of the playfield. To make the players and missiles move behind the + playfield, a "1" must be written to D2 of the CTRLPF register. The + following table illustrates how the priorities are affected: +

+

+ + + + + + +
Priority Objects
1 BL, PF
2 P0, M0
3 P1, M1
4 BK
+
+

+ One more priority control is available to be used for displaying the score. + When a "1" is written to D1 of the CTRLPF register, the left half of the + playfield takes on the color of player 0, and the right half the color of player + 1. The game score can now be displayed using the PF graphics register, and + the score will be in the same color as its associated player. +

+

+ +10.0 Collisions +
+

+ The TIA detects collisions between any of the 6 objects it generates (the + playfield and 5 moveable objects). There are 15 possible two-object + collisions which are stored in 15 one bit latches. Each collision register + contains two of these latches which are read by the microprocessor on D6 + and D7 of the data bus for easy access. A "1" on the data line indicates the + collision it records has occurred. The collision registers could be read at any + time but is usually done during vertical blank after all possible collisions + have occurred. The collision registers are all reset simultaneously by + writing to the collision reset register (CXCLR). +

+

+ +11.0 Sound +
+

+ There are two audio circuits for generating sound. They are identical but + completely independent and can be operated simultaneously to produce + sound effects through the TV speaker. Each audio circuit has three + registers that control a noise-tone generator (what kind of sound), a + frequency selection (high or low pitch of the sound), and a volume control. +

+ +11.1 Tone +

+ The noise-tone generator is controlled by writing to the 4 bit audio control + registers (AUDC0, AUDC1). The values written cause different kinds of + sounds to be generated. Some are pure tones like a flute, others have + various "noise" content like a rocket motor or explosion. Even though the + TIA hardware manual lists the sounds created by each value, some + experimentation will be necessary to find "your sound". +

+

+ +11.2 Frequency +
+ Frequency selection is controlled by writing to a 5 bit audio frequency + register (AUDF0, AUDF1). The value written is used to divide a 30KHz + reference frequency creating higher or lower pitch of whatever type of sound + is created by the noise-tone generator. By combining the pure tones + available from the noise-tone generator with frequency selection a wide + range of tones can be generated. +

+

+ +11.3 Volume +
+ Volume is controlled by writing to a 4 bit audio volume register (AUDV0, + AUDV1). Writing 0 to these registers turns sound off completely, and + writing any value up to 15 increases the volume accordingly. +

+

+
+ +12.0 Input Ports +
+

+ There are six input ports whose logic states can be read on D7 by reading + the input port addresses (INPT0, thru INPT5). These six ports are divided + into two types, "dumped" and "latched". +

+ +12.1 Dumped Input Ports (INPT0 thru INPT3) +

+ These four ports are used to read up to four paddle controllers. Each paddle controller + contains an adjustable pot controlled by the knob on the controller. The output of the + pot is used to charge a capacitor in the console, and when the capacitor is charged the + input port goes HI. The microprocessor discharges this capacitor by writing a "1" to D7 + of VBLANK then measures the time it takes to detect a logic one at that port. This + information can be used to position objects on the screen based on the position of the + knob on the paddle controller. +

+

+ +12.2 Latched Input Ports (INPT4, INPT5) +
+ These two ports have latches that are both enabled by writing a "1" or + disabled by writing a "0" to D6 of VBLANK. When disabled the + microprocessor reads the logic level of the port directly. When enabled, the + latch is set for logic one and remains that way until its port goes LO. When + the port goes LO the latch goes LO and remains that way regardless of what + the port does. The trigger buttons of the joystick controllers connect to + these ports. +

+

+
+
+ +

THE PIA (6532)

+

+ +1.0 General +

+

+ The PIA chip is an off-the-shelf 6532 Peripheral Interface Adaptor which + has three functions: a programmable timer, 128 bytes of RAM, and two 8 + bit parallel I/O ports. +

+

+ +2.0 Interval timer +
+

+ The PIA uses the same clock as the microprocessor so that one PIA cycle + occurs for each machine cycle. The PIA can be set for one of four different + "intervals", where each interval is some multiple of the clock (and therefore + machine cycles). A value from 1 to 255 is loaded into the PIA which will be + decremented by one at each interval. The timer can now be read by the + microprocessor to determine elapsed time for timing various software + operations and keep them synchronized with the hardware (TIA chip). +

+ +2.1 Setting the timer +

+ The timer is set by writing a value or count (from 1 to 255) to the address of + the desired interval setting according to the following table : +

+

+ + + + + + +
Hex Address Interval Mnemonic
294 1 clock TIM1T
295 8 clocks TIM8T
296 64 clocks TIM64T
297 1024 clocks T1024T
+
+

+ For example, if the value of 100 were written to TIM64T (HEX address 296) + the timer would decrement to 0 in 6400 clocks (64 clocks per interval x 100 + intervals) which would also be 6400 microprocessor machine cycles. +

+

+ +2.2 Reading the timer +
+ The timer may be read any number of times after it is loaded of course, but + the programmer is usually interested in whether or not the timer has + reached 0. The timer is read by reading INTIM at hex address 284. +

+

+ +2.3 When the timer reaches zero +
+ The PIA decrements the value or count loaded into it once each interval + until it reaches 0. It holds that 0 counts for one interval, then the counter + flips to FF(HEX) and decrements once each clock cycle, rather than once per + interval. The purpose of this feature is to allow the programmer to + determine how long ago the timer zeroed out in the event the timer was + read after it passed zero. +
+
+

+ +3.0 RAM +

+

+ The PIA has 128 bytes of RAM located in the Stella memory map from HEX + address 80 to FF. The microprocessor stack is normally located from FF on + down, and variables are normally located from 80 on up (hoping the two + never meet). +

+

+ +4.0 The I/O ports +
+

+ The two ports (Port A and Port B) are 8 bits wide and can be set for either + input or output. Port A is used to interface to carious hand-held controllers + but Port B is dedicated to reading the status of the Stella console switches. +

+ +4.1 Port B - Console Switches (read only) +

+ Port B is hardwired to be an input port only that is read by addressing + SWCHB (HEX 282) to determine the status of all the console switches + according to the following table: +

+

+ + + + + + + + + +
Data Bit Switch Bit Meaning
D7 P1 difficulty 0 = amateur (B), 1 = pro (A)
D6 P0 difficulty 0 = amateur (B), 1 = pro (A)
D5/D4 (not used)
D3 color - B/W 0 = B/W, 1 = color
D2 (not used)
D1 game select 0 = switch pressed
D0 game reset 0 = switch pressed
+
+
+

+

+ +5.0 Port A - Hand Controllers +
+

+ Port A is under full software control to be configured as an input or an + output port. It can then be used to read or control various hand-head + controllers with the data bits defined differently depending on the type of + controller used. +

+ +5.1 Setting for input or output +

+ Port A has an 8 bit wide Data Direction Register (DDR) that is written to at + SWACNT (HEX 281) to set each individual pin of Port A to either input or + output. The Port A pins are labeled PA0 thru PA7, and writing a "0" to a + pins' DDR bit sets that pin as input, and a "1" sets it as an output. For + example, writing all 0's to SWACNT (the DDR for Port A) sets PA0 thru + PA7 (all 8 pins of Port A) as inputs. If F0 (11110000) were written to + SWACNT then PA7, PA6, PA5 & PA4 would be outputs, and PA3, PA2, PA1 + & PA0 would be inputs. +
+

+ +5.2 Inputting and Outputting +

+ Once the DDR has set the pins of Port A for input or output they may be + read or written to by addressing SWCHA (HEX 280). +
+

+ +5.3 Joystick Controllers +

+ Two joysticks can be read by configuring the entire port as input and + reading the data at SWCHA according to the following table: +

+

+ + + + + + + + + + +
Data BitDirectionPlayer
D7rightP0
D6leftP0
D5downP0
D4upP0
D3rightP1
D2leftP1
D1downP1
D0upP1
+ (P0 = left player, P1 = right player) +

+

+ A "0" in a data bit indicates the joystick has been moved to close that + switch. All "1's" in a player's nibble indicates that joystick is not moving. +

+

+ + 5.4 Paddle (pot) controllers +
+ Only the paddle triggers are read from the PIA. The paddles themselves + are read at INP0 thru INPT3 of the TIA. The paddle triggers can be read at + SWCHA according to the following table : +

+

+ + + + + + + + +
Data Bit Paddle #
D7 P0
D6 P1
D5/D4 (not used)
D3 P2
D2 P3
D1/D0 (not used)
+
+

+

+ +5.5 Keyboard controllers +
+ The keyboard controller has 12 buttons arranged into 4 rows and 3 columns. + A signal is sent to a row, then the columns are checked to see if a button is + pushed, then the next row is signaled and all columns sensed, etc. until the + entire keyboard is scanned and sensed. The PIA sends the signals to the + rows, and the columns are sensed by reading INPT0, INPT1, and INPT4 of + the TIA. With Port A configured as an output port, the data bits will send a + signal to the keyboard controller rows according to the following table : +

+

+ + + + + + + + + + +
Data BitKeyboard RowPlayer
D7bottomP0
D6thirdP0
D5secondP0
D4topP0
D3bottomP1
D2thirdP1
D1secondP1
D0topP1
+(P0 = left player, P1 = right player) +

+ NOTE : a delay of 400 microseconds is necessary between writing to this + port and reading the TIA input ports. +

+
+
+

+ +6.0 Address summary table +

+

+ + + + + + + + + + + +
Hex Address Mnemonic Purpose
280 SWCHA Port A; input or output (read or write)
281 SWACNT Port A DDR, 0= input, 1=output
282 SWCHB Port B; console switches (read only)
283 SWBCNT Port B DDR (hardwired as input)
284 INTIM Timer output (read only)
294 TIM1T set 1 clock interval (838 nsec/interval)
295 TIM8T set 8 clock interval (6.7 usec/interval)
296 TIM64T set 64 clock interval (53.6 usec/interval)
297 T1024T set 1024 clock interval (858.2 usec/interval)
+

+ NOTE: one clock is also one microprocessor machine cycle +

+
+

+ +

PAL/SECAM CONVERSIONS

+

+PAL +

+1. The number of scan lines, and therefore the frame time increases from + NTSC to PAL according to the following table: +

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NTSCPAL
scanlinesmicrosecondsscanlinesmicroseconds
VBLANK402548483085
KERNEL1921222822814656
OVERSCAN301910362314
FRAME2621668631220055
+
+ +

+ 2. Sounds will drop a little in pitch (frequency) because of a slower crystal + clock. Some sounds may need the AUDF0/AUDF1 touched up. +

+ 3. PAL operates at 50 Hz compared to NTSC 60Hz, a 17% reduction. If + game play speed is based on frames per second, it will slow down by 17%. + This can be disastrous for most skill/action carts. If the NTSC version is + designed with 2 byte fractional addition techniques (or anything not based + on frames per second) to move objects, then PAL conversion can be as + simple as changing the fraction tables, avoiding major surgery on the + program. +

+

+SECAM +
+1. SECAM is a little weird. It takes the PAL software, but the console + color/black & white switch is hardwired as black & white. Therefore, it + reads the PAL black & white tables in software and assigns a fixed color to + each lum of black & white according to the following table: +

+

+ + + + + + + + + + +
Lum Color
0 Black
2 Blue
4 Red
6 Magenta
8 green
A cyan
C yellow
E white
+
+

+ There is a trap here: the manual is the same for NTSC, PAL, & SECAM. + This means that the descriptions for black & white must jive between NTSC + & PAL. If you make major changes to PAL black & white to achieve good + SECAM color, NTSC black & white must be made similar. +

+ 2. PAL sounds work fine on SECAM with one exception: when a sound is to + be turned off, it must be done by setting AUDV0/AUDV1 to 0, not by setting + AUDC0/AUDC1 to 0. Otherwise, you get an obnoxious background sound. +

+

+
+ +

TIA 1A - TELEVISION INTERFACE ADAPTOR (MODEL 1A)

+

+ +GENERAL DESCRIPTION +

+

+ The TIA is an MOS integrated circuit designed to interface between an + eight (8) bit microprocessor and a television video modulator and to convert + eight (8) bit parallel data into serial outputs for the color, luminosity, and + composite sync required by a video modulator. +

+ This circuit operates on a line by line basis, always outputting the same + information every television line unless new data is written into it by the + microprocessor. +

+ A hardware sync counter produces horizontal sync timing independent of + the microprocessor. Vertical sync timing is supplied to this circuit by the + microprocessor and combined into composite sync. +

+ Horizontal position counters are used to trigger the serial output of five (5) + horizontally movable objects; two players, two missiles and a ball. The + microprocessor can add or subtract from these position counters to move + these objects right or left. +

+ The microprocessor determines all vertical position and motion by writing + zeros or ones into object registers before each appropriate horizontal line. +

+ Walls, clouds and other seldom moved objects are produced by a low + resolution data register called the playfield register. +

+ A fifteen (15) bit collision register detects all fifteen possible two object + collisions between these six (6) objects (five moveable and one playfield). + This collision register can be read and reset by the microprocessor. Six input + ports are also provided on this chip that can be read by the microprocessor. + These input ports and the collision register are the only chip addresses that + can be read by the microprocessor. All other addresses are write only. +

+ Color luminosity registers are included that can be programmed by the + microprocessor with eight (8) luminosity and fifteen (15) color values. A + digital phase shifter is included on this chip to provide a single color output + with fifteen (15) phase angles. +

+ Two (2) independent audio generating circuits are included, each with + programmable frequency, noise content, and volume control registers. +

+

+ +DETAILED DESCRIPTION +
+

+ +1. Data and addressing +

+

+ Registers on this chip are addressed by the microprocessor as part of its + overall RAM-ROM memory space. The attached table of read-write + addresses summarizes the addressable functions. There are no registers + that are both read and write. Some addresses however are both read and + write, with write data going into one register and read data returning from + a different register. +

+ If the read-write line is low, the data bits indicated in this table will be + written into the addressed write location when the 02 clock goes from high + to low. Some registers are eight bits wide, some only one bit, and some + (strobes) have no bits, performing only control functions (such as resets) + when their address is written. +

+ If the read-write line is high, the addressed location can be read by the + microprocessor on data lines 6 and 7 while the 02 clock is high. +

+ The addresses given in the table refer only to the six (6) real address lines. + If any of the four (4) chip select lines are used for addressing, the addresses + must be modified accordingly. +

+

+ +2. Synchronization +
+

+ A. Horizontal Timing +

+ A hardware counter on this chip produces all horizontal timing (such as + sync, blank, burst) independent of the microprocessor, This counter is + driven from an external 3.58 Mhz oscillator and has a total count of 228. + Blank is decoded as 68 counts and sync and color burst as 16 counts. +
+

+ B. Vertical Timing +

+ There are one bit, addressable registers on this chip for vertical sync and + vertical blank. The timing for these functions is established by the + microprocessor by writing zero or one into these bits. (VSYNC, VBLANK ) +
+

+ C. Composite Sync +

+ Horizontal sync and the output of the vertical sync bit are combined + together to produce composite sync. This composite sync signal drives a + chip output pad to an external composite video resistor network. +
+

+ D. Microprocessor Synchronization +

+ The 3.58 MHz oscillator also clocks a divide by three counter on this chip + whose output (1.19 Mhz) is buffered to drive an output pad called 00. This + pad provides the input phase zero clock to the microprocessor which then + produces the system 02 clock (1.19 Mhz). + Software program loops require different lengths of time to run depending + on branch decisions made within the program. Additional synchronization + between the software and hardware. This is done with a one bit latch called + WSYNC (wait for sync). When the microprocessor finishes a routine such + as loading registers for a horizontal line, or computing new vertical + locations during vertical blank, it can address WSYNC, setting this latch + high. When this latch is high, it drives an output pad to zero connected to + the microprocessor ready line (RDY). A zero on this line causes the + microprocessor to halt and wait. As shown in figure 2, WSYNC latch is + automatically reset to zero by the leading edge of the next horizontal blank + timing signal, releasing the RDY line, allowing the microprocessor to begin + its computation and register writing for this horizontal television line or + line pair. +

+

+
+ +3. Playfield graphics Register +

+

+ A. Description +
+ Objects such as walls, clouds, and score) which are not required to move, are + written into a 20 bit register called the playfield register. This register + (Figure 5) is loaded from the data bus by three separate write addresses + (PF0, PFl, PF2). Playfield may be loaded at any time. To clear the playfield, + zeros must be written into all three addresses. +

+

+ B. Normal Serial Output +
+ The playfield register is automatically scanned (and converted to serial + output) by a bi-directional shift register clocked at a rate which spreads the + twenty (20) bits out over the left half of a horizontal line. This scanning is + initiated by the end of horizontal blank (left edge of television screen). + Normally the same scan is then repeated, duplicating the same twenty (20) + bit sequence over the right half of the horizontal line. +

+

+ C. Reflected Serial Output +
+ A reflected playfield may be requested by writing a one into bit zero of the + playfield control register (CTRLPF). When this bit is true the scanning + shift register will scan the opposite direction during the right half of the + horizontal line, reversing the twenty (20) bit sequence. +

+

+ D. Timing Constraints +
+ Even though the playfield bytes (PF0, PFl, PF2) may be written to any time, + if one of them is changed while being serially scanned, part of the new value + may both show up on the television horizontal line. +

+

+
+ +4. Horizontal Position Counters +
+

+ A. Description +

+ The playfield is a fixed graphics register, always starting its serial output + when triggered by the beginning of each television line. This chip also + includes five "moveable" graphics registers, whose serial outputs are + triggered by five separate horizontal position counters every time these + counters pass through zero count. These position counters are clocked + continuously during the unblanked portion of every horizontal line and + their count length is exactly equal to the normal number of clocks supplied + to them during this time. They will therefore pass through zero at the same + time during each horizontal television line and the triggered outputs will + have no horizontal motion. A typical horizontal counter is shown in figure 4. +

+ If extra clocks are supplied to these counters (or normal clocks suppressed) + the zero crossing time will shift and the object will have moved left (extra + clocks) or right (fewer clocks). Some position counters have extra decoders + (in addition to a zero decode) to trigger multiple copies of the same object + across a horizontal line. +

+ All position counters can be reset to zero count by the microprocessor at any + time, by a write instruction to the reset addresses (RESBL, RESM0, RESMl, + RESP0, RESPl). If reset occurs during horizontal blank, the object will + appear at the left side of the television screen. Properly timed resets may + position an object at any horizontal location consistent with the + microprocessor cycle time. +

+

+ B. Ball position Counter +
+ The ball position counter has only the zero crossing decode and therefore + cannot trigger multiple copies of the ball graphics. +

+

+ C. Player Position Counters +
+ Each player position counter has three decodes in addition to the zero + crossing decode. These decodes are controlled by bits 0,1,2 of the number + size control registers (NUSIZ0, NUSIZ1), and trigger 1,2, or 3 copies of the + player (at various spacings) across a horizontal line as shown on page ___. + These same control bits are used for the decodes on the missile position + counter, insuring an equal number of players and missiles. +
+ D. Missile Position Counters +
+ Missile position counters are identical to player position counters except + that they have another type of reset in addition to the previously discussed + horizontal position reset. These extra reset addresses (RESMP0, RESMP1) + write data bit 1 into a one bit register whose output is used to position the + missile (horizontally) directly on top of its corresponding player, and to + disable the missile serial output. +

+

+
+ +5. Horizontal Motion Registers +

+

+ A. General Description +
+ There are five write only registers on this chip that contain the horizontal + motion values for each of the five moving objects. A typical horizontal + motion register is shown in figure 4 . The data bus (bits 4 through 7) is + written into these addresses (HMP0, HMPl, HMM0, HMMl, HMBL) to load + these registers with motion values. These registers supply extra (or fewer) + clocks to the horizontal position counters only when commanded to do so by + an HMOVE address from the microprocessor. These registers may all be + cleared to zero (no motion) simultaneously by an HMCLR command from + the microprocessor, or individually by loading zeros into each register. + These registers are each four bits in length and may be loaded with positive + (left motion), negative (right motion) or zero (no motion) values. Negative + values are represented in twos complement format. +

+

+ B. Timing constraints +
+ These registers may be loaded or cleared at almost any time. The motion + values they contain will be used only when an HMOVE command is + addressed, and then all five motion values will be used simultaneously into + all five horizontal position counters once. The only timing constraint on this + operation involves the HMOVE command. The HMOVE command must be + located in the microprocessor program immediately after a wait for sync + (WSYNC) command. This assures that the HMOVE operation begins at the + leading edge of horizontal blank, and has the full blank time to supply extra + or fewer clocks to the horizontal position counters. These registers should + not be modified for at least 24 Computer cycles after the HMOVE command. +

+

+
+ +6. Moving Object Graphics Registers +

+

+ A. General Description +
+ There are five graphics registers for moving objects on this chip. These + graphics registers are loaded (written) in parallel by the microprocessor and + like the playfield register are scanned and converted to serial output. + Unlike the playfield register, which is always scanned beginning at the left + side of each horizontal line, moving object graphics registers are scanned + only when triggered by a start decode from their horizontal position + counter. A typical graphics register is shown in figure 4 . +

+

+ B. Missile Graphics +
+ The graphics registers for both missiles are identical and very simple. They + each consist of a one bit register called missile enable (ENAM0, ENAM1). + This graphics bit is scanned (outputted) only when triggered by its + corresponding position counter. There are control bits (bits 4, 5, of NUSIZ0, + NUSIZ1) that can stretch this single graphics bit out over widths of 1, 2, 4, + or 8 clocks of horizontal line time. (A full line is 160 clocks). +

+

+ C. Player Graphics +
+ The graphics registers for both players are identical and are rather complex. + They each consist of eight bit parallel registers (GRP0, GRP1) and a bi- + directional parallel to serial scan counter that converts the parallel data + into serial output. A one bit control register (REFP0, REFP1) determines + the direction (reflection) of the parallel to serial scan, outputing either D7 + through D0, or D0 though D7. This allows reflection (horizontal flipping) of + player serial graphics data without having to flip the microprocessor data. +

+ The clock into the scan counter can be controlled (three bits of NUSIZ0 and + NUSIZ1) to slow the scan rate and stretch the eight bits of serial graphics + out over widths of 8, 16, or 32 clocks of horizontal line time. These same + control bits are used in the player-missile motion counters to control + multiple copies, so only three player widths ( scan + rates) are available. +

+

+ D. Vertical Delay +
+ Each of the player graphics registers actually consists of two 8 bit parallel + registers. The first (GRP0, GRP1) is loaded (written) from the + microprocessor 8 bit data bus. The second is automatically loaded from the + output of the first. The reason for this is a complex subject called vertical + delay. A large amount of microprocessor time is required to generate + player, missile and playfield graphics (table look up, masking, comparisons, + etc.) and load these into this chip's registers. For most game programs this + time is just too large to fit into one horizontal line time. In fact for most + games it will barely fit into two line times (127 microseconds). Therefore, + individual graphics registers are loaded (written) every two lines, and used + twice for serial output between loads. This type of programing will + obviously limit the vertical height resolution of objects to multiples of two + lines. It also will limit the resolution of vertical motion to two lines jumps. + Nothing can be done about the vertical height resolution; however, vertical + motion can be resolved to a single line by addition of a second graphics + register that is automatically parallel loaded from the output of the first, + one line time after the first was loaded from the data bus. This second + graphics register output is therefore always delayed vertically by one line. A + control bit called vertical delay (VDEL0, VDEL1) selects which of these two + registers is to be used for serial output. If this control bit is set by the + microprocessor between picture frames, the object will be moved down + (delayed) by one line during the next frame. In most programming + applications player 0 graphics and player 1 graphics are loaded (written) + alternately, during the blank time just prior to each line as shown in (figure + 1). Since GRP0 and GRP1 addresses from the microprocessor alternate, + they are delayed by one line from each other. The GRP0 address decode can + therefore be used to load the delayed graphics register for player 1, and + GRP1 likewise to load the delayed graphics register for player 0. The two + vertical delay bits (VDEL0, VDELl) then select delayed or undelayed + registers for player 0 and player 1 as serial outputs. +

+

+ E. Ball Graphics +
+ The ball graphics register is almost identical to the missile graphics + register. It also consists of a single enable bit (ENABL) whose output is + triggered by the ball position counter. It also has two control bits (bits 4, 5 + of CTRLPF) that can stretch this single graphics bit out over widths of 1, 2, + 4, or 8 clocks of horizontal line time. Unlike the missile graphics; however, + the ball graphics register has capability for vertical delay similar to the + player graphics. A second graphics (enable) bit is alternately loaded from + the output of the first, one line after the first was loaded from the data bus. + A ball vertical delay bit (VDELBL) selects which of these two graphics bits + is used for the ball serial output. The first graphics bit (ENABL) should be + loaded during the same horizontal blank time as player 0 (GRP0), because + GRP1 is used to load the second enable bit from the output of the first on + alternate lines. +

+

+
+ +7. Collision Detection Latches +

+

+ A. Definitions +
+ The serial outputs from all the graphics registers represent real time + horizontal location of objects on the television screen. If any of these outputs + occur at the same time, they will overlap (collide) on the screen. There are + six objects generated on this chip (five moving and playfield) allowing + fifteen possible two object collisions. These overlaps (collisions) are detected + by fifteen "and" gates whenever they occur, and are stored in fifteen + individual latch register bits, as shown in figure 6. +

+

+ B. Reading Collision +
+ The microprocessor can read these fifteen collision bits on data lines 6 and 7 + by addressing them two at a time. This could be done at any time but is + usually done between frames (during vertical blank) after all possible + collisions have serially occurred. +

+

+ C. Reset +
+ All collision bits are reset simultaneously by the microprocessor using the + reset address CXCLR. This is usually done near the end of vertical blank, + after collisions have been tested. +

+

+
+

+ +8. Input ports +

+

+ A. General Description +
+ There are 6 input ports on this chip whose logic state may be read on data + line 7 with read addresses INPT0 through INPT5. These 6 ports are + divided into two types, "dumped" and "latched". See Figure 8. +

+

+ B. Dumped Input Ports (I0 through I3) +
+ These 4 input ports are normally used to read paddle position from an + external potentiometer-capacitor circuit. In order to discharge these + capacitors each of these input ports has a large transistor, which may be + turned on (grounding the input ports) by writing into bit 7 of the register + VBLANK. When this control bit is cleared the potentiometers begin to + recharge the capacitors and the microprocessor measures the time required + to detect a logic 1 at each input port. +

+ As long as bit 7 of register VBLANK is zero, these four ports are general + purpose high impedance input ports. When this bit is a 1 these ports are + grounded. +

+

+ C. Latched Input ports (I4, I5) +
+ These two input ports have latches which can be enabled or disabled by + writing into bit 6 of register VBLANK. +

+ When disabled, these latches are removed from the circuit completely and + these ports become two general purpose input ports, whose present logic + state can be read directly by the microprocessor. +

+ When enabled, these latches will store negative (zero logic level) signals + appearing on these two input ports, and the input port addresses will read + the latches instead of the input ports. +

+ When first enabled these latches will remain positive as long as the input + ports remain positive (logic one). A zero input port signal will clear a latch + value to zero, where it will remain (even after the port returns positive) + until disabled. Both latches may be simultaneously disabled by writing a + zero into bit 6 of register VBLANK. +

+

+
+ +8.5 Priority Encoder +

+

+ A. Purpose +
+ As discussed in the section on collisions, simultaneous serial outputs from + the graphics registers represent overlap on the television screen. In order to + have color-luminosity values assigned to individual objects it is necessary to + establish priorities between objects when overlapped. The priority encoder + is shown in figure 3. +

+

+ B. Priority Assignment +
+ The lack of any objects results in a color-lum value called the background. + The background (BK) has lowest priority and only appears when no objects + are outputing. In order to simplify the logic, each missile is given the same + color-lum value and priority as its corresponding player (P0, M0) and the + ball is given the same color-lum value and priority as the playfield (PF, BL). +

+ The following table illustrates the normal priority assignment: +

+

+ + + + + +
Highest Priority P0, M0
Second Highest P1, M1
Third Highest PF, BL
Lowest Priority BK
+
+

+ Objects with higher priority will appear to move in front of objects with + lower priority. Players will therefore move in front of playfield (clouds, + walls, etc.). +

+

+ C. Priority Control +
+ There are two playfield control bits that affect priority, one called playfield + priority (PFP) (bit 2 of CTRLPF) and one called score (bit 1 of CTRLPF). + When a one is written into the PFP bit the priority assignment is modified + as shown below. +

+

+ + + + + +
Highest Priority PF, BL
Second Highest P0, M0
Third Highest P1, M1
Lowest Priority BK
+
+

+ Players will then move behind playfield (clouds, wall, etc.). When a one is + written into the score control bit, the playfield is forced to take the color-lum + of player 0 in the left half of the screen and player 1 in the right half of the + screen. This is used when displaying score and identifies the score with the + correct player. The priority encoder produces 4 register select lines shown + in figure 3) that are mutually exclusive. These 4 lines select either + background, player 0, player 1 or playfield, and only one of them can be + true at a time. +

+

+
+ +9. Color Luminance Registers +

+

+ A. Description +
+ There are four registers (shown in figure 3) that contain color-lum codes. + Four bits of color code and three its of luminance code may be written into + each of these registers (COLUP0, COLUP1, COLUPF, COLUBK) by the + microprocessor at any time. These codes (representing 16 color values and 8 + luminance values) are given in the Detailed Address List. +

+

+ B. Multiplexing +
+ The serial graphics output from all six objects is examined by the priority + encoder which activates one of the four select lines into a 4 x 7 multiplexer. + This multiplexer (shown in figure 3) then selects one of the four color-lum + registers as a 7 line output. Three of these lines are binary coded + luminosity and go directly to chip output pads. The other four lines go to the + color phase shifter. +

+

+
+ +10. Color Phase Shifter +

+

+ This portion of the chip (shown in figure 3) produces a reference color + output (color burst) during horizontal blank and then during the unblanked + portion of the line it produces a color output shifted in phase with respect to + the color burst. The amount of phase shift determines the color and is + selected by the four color code lines from the Color-lum multiplexer. Binary + code 0 selects no color. Code 1 selects gold (same phase as color burst). + Codes 2 (0010) through 15 (1111) shift the phase from zero through almost + 360 degrees allowing selection of 15 total colors around the television color + wheel. +

+

+ +11. Audio Circuits +

+

+ Two audio circuits are incorporated on this chip. They are identical and + completely independent, although their outputs could be combined + externally into one speaker. Each audio circuit consists of parts described + below, and in figure 7. +

+ A. Frequency Select +

+ Clock pulses (at approximately 30 KHz) from the horizontal sync counter + pass through a divide by N circuit which is controlled by the output code + from a five bit frequency register (AUDF). This register can be loaded + (written) by the microprocessor at any time, and causes the 30 KHz clocks + to be divided by 1 (code 00000) through 32 (code 11111). This produces + pulses that are digitally adjustable from approximately 30 KHz to 1 KHz + and are used to clock the noise-tone generator. +
+ B. Noise-Tone Generator +
+ This circuit contains a nine bit shift counter which may be controlled by the + output code from a four bit audio control register(AUDC), and is clocked by + the frequency select circuit. The control register can be loaded by the + microprocessor at any time, and selects different shift counter feedback taps + and count lengths to produce a variety of noise and tone qualities. +

+

+ C. Volume Select +
+ The shift counter output is used to drive the audio output pad through four + driver transistors that are graduated in size. Each transistor is twice as + large as the previous one and is enable by one bit from the audio volume + register (AUDV). This audio volume register may be loaded by the + microprocessor at any time. As binary codes 0 through 15 are loaded, the + pad drive transistors are enabled in a binary sequence. The shift counter + output therefore can pull down on the audio output pad with 16 selectable + impedance levels. +
+
+

+


+

+Figure 1. Vertical Delay

+Figure 2. Synchronization
Figure 3. Color-Luminance

+Figure 4. Typical Horizontal Motion Circuit

+Figure 5. Playfield Graphics

+Figure 6. Collision Detection

+Figure 7. Audio Circuit

+Figure 8. Input Ports

+Figure 9. Game System

+

+


+

+ +

Write Address Detailed Functions

+

+ +WSYNC (wait for sync) +

+ This address halts microprocessor by clearing RDY latch to zero. RDY is set true again + by the leading edge of horizontal blank. +

+ Data bits not used +

+ +RSYNC (reset sync) +
+ This address resets the horizontal sync counter to define the beginning of horizontal + blank time, and is used in chip testing. +

+ Data bits not used. +

+ +VSYNC +
+This address controls vertical sync time by writing D1 into the VSYNC latch +
+ + + + +
D1
0Stop vertical sync
1Start vertical sync
+
+
+ +VBLANK +
+ This address controls vertical blank and the latches and dumping transistors on the input + ports by writing into bits D7, D6 and D1 of the VBLANK register. +
+ + + + + + + + +
Data BitValueEffect
D10Stop vert. blank
1Start vert. blank
D60Disable INPT4, INPT5 latches
1Enable INPT4, INPT5 latches
D70Remove INPT1,2,3,6 dump path to ground
1Dump INPT1,2,3,6 to ground
+ Note : Disable latches (D6 = 0) also resets latches to logic true +
+
+ +PF0 (PF1, PF2) +
+ These addresses are used to write into playfield registers +
+ + + + +
PF0D7D6D5D4(not used)
PF1D7D6D5D4D3D2D1D0
PF2D7D6D5D4D3D2D1D0
+
+
+ + PLAYFIELD REGISTERS SERIAL OUTPUT +
+ + + + + +
1 horizontal line ( 160 clocks)Playfield Reflect Control
4..7 7......0 0......7 4..7 7......0 0......7 REF = 0
PF0PF1PF2PF0PF1PF2
center
+ + + +
4..7 7......0 0......7 7......0 0......7 7..4 REF = 1
PF0 PF1 PF2 PF2 PF1 PF0
+

+ each bit = 4 clocks +

+ +CTRLPF +
+ This address is used to write into the playfield control register +(a logic 1 causes action as described below) +
+ + + + + + +
D0REF (reflect playfield)
D1SCORE (left half of playfield gets color of player 0, right half gets color of player
D2PFP (playfield gets priority over players so they can move behind the playfield)
D4Ball Size (see next table)
D5
+

+ + D4 & D5 = BALL SIZE + + + + + + +
D5D4Width
001 clock
012 clocks
104 clocks
118 clocks
+

+
+ +NUSIZ0 (NUSIZ1) +
+ These addresses control the number and size of players and missiles. +
+ Missile Size + + + + + + +
D5D4Width
001 clock
012 clocks
104 clocks
118 clocks
+

+Player-Missile number & player size + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
D2D1D01/2 television line (80 clocks)
8 clocks per square
Description
000X        one copy
001X X      two copies - close
010X   X    two copies - med
011X X X    three copies - close
100X       Xtwo copies - wide
101XX       double size player
110X   X   X3 copies medium
111XXXX     quad sized player
+

+
+ + + +RESP0 (RESP1, RESM0, RESM1, RESBL) +
+ These addresses are used to reset players, missiles and the ball. The object will begin its + serial graphics at the time of a horizontal line at which the reset address occurs. +

+ No data bits are used +

+ +RESMP0 (RESMP1) +
+ These addresses are used to reset the horiz. location of a missile to the center of its + corresponding player. As long as this control bit is true (1) the missile will remain + locked to the center of its player and the missile graphics will be disabled. When a + zero is written into this location, the missile is enabled, and can be moved independently + from the player. +

+

+ D1 = RESMP (missile-player reset) +
+
+ +HMOVE +
+ This address causes the horizontal motion register values to be acted upon during the + horizontal blank time in which it occurs. It must occur at the beginning of horiz. blank in + order to allow time for generation of extra clock pulses into the horizontal position + counters if motion is desired this command must immediately follow a WSYNC + command in the program. +

+ No data bits are used. +

+ +HMCLR +
+This address clears all horizontal motion registers to zero (no motion). + No data bits are used. +
+ + + +HMP0 (HMP1, HMM0, HMM1, HMBL) +
+ These addresses write data (horizontal motion values) into the horizontal motion + registers. These registers will cause horizontal motion only when commanded to do so + by the horiz. move command HMOVE. + The motion values are coded as shown below : +
+ + + + + + + + + + + + + + + + + + +
D7D6D5D4ClocksEffect
0111+7Move left indicated number of clocks
0110+6
0101+5
0100+4
0011+3
0010+2
0001+1
00000No Motion
1111-1Move right indicated number of clocks
1110-2
1101-3
1100-4
1011-5
1010-6
1001-7
1000-8
+
+ WARNING : These motion registers should not be modified during the 24 computer + cycles immediately following an HMOVE command. Unpredictable motion values may + result. +
+ + +ENAM0 (ENAM1, ENABL) +
+ These addresses write D1 into the 1 bit missile or ball graphics registers. +
+ + + + +
D1
0Disables object
1Enables object
+
+
+ +GRP0 (GRP1) +
+ These addresses write data into the player graphics registers. +
+ + + +
D7D6D5D4D3D2D1D0
Graphics Data
+
+ Note: serial output begins with D7, unless REFP0 (REFP1) = 1 +
+ +REFP0 (REFP1) +
+ These addesses write D3 into the 1 bit player reflect registers +
+ + + + +
D3
0no reflect, D7 of GRPx on left
1reflect, D0 of GRPx on left
+
+
+ + +VDELP0 (VDELP1, VDELBL) +
+ These addresses write D0 into the 1 bit vertical delay registers, to delay players or ball by + one vertical line.

+

+ + + + +
D0
0no delay
1delay
+
+
+ +CXCLR +
+This adderess clears all collision latches to zero (no collision). + No data bits are used. +
+ +COLUP0 (COLUP1, COLUPF, COLUBK) +
+ These addresses write data into the player, playfield, and background color-luminance + registers +

+

+ + + + + + + + + + + + + + + + + + +
COLORD7D6D5D4D3D2D1LUM
grey - gold0000000black
 0001001dark grey
orange, brt-org0010010
 0011011grey
pink - purple0100100
 0101101
purp-blue, blue0110110light grey
 0111111white
blue - lt. blue1000
 1001
torq. - grn. blue1010
 1011
grn. - yel. grn.1100
 1101
org. grn - lt org.1110
 1111
+
+
+ +AUDF0 (AUDF1) +
+ These addresses write data into the audio frequency divider registers. +

+

+ + + + + + + + +
D4 D3 D2 D1 D0 30KHz divided by
00000no division
00001divide by 2
00010divide by 3
..................
11110divide by 31
11111divide by 32
+
+
+ +AUDC0 (AUDC1) +
+ These addresses write data into the audio control registers which control the noise + content and additional division of the audio output. +

+

+ + + + + + + + + + + + + + + + + + +
D3 D2 D1 D0 Type of noise or division
0000set to 1
00014 bit poly
0010div 15 -> 4 bit poly
00115 bit poly -> 4 bit poly
0100div 2 : pure tone
0101div 2 : pure tone
0110div 31 : pure tone
01115 bit poly -> div 2
10009 bit poly (white noise)
10015 bit poly
1010div 31 : pure tone
1011set last 4 bits to 1
1100div 6 : pure tone
1101div 6 : pure tone
1110div 93 : pure tone
11115 bit poly div 6
+
+
+ +AUDV0 (AUDV1) +
+ These addresses write data into the audio volume registers which set the pull down + impedance driving the audio output pads. +

+

+ + + + + + + + +
D3 D2 D1 D0 Audio Output Pull down current
0 0 0 0 No output current
0 0 0 1 lowest
0 0 1 0
... ... ... ...
1 1 1 0
1 1 1 1 highest
+
+
+

+


+

+ +

TIA WRITE ADDRESS SUMMARY

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
6-bit AddressAddress Name76543210Function
00VSYNC ......1. vertical sync set-clear
01VBLANK 11....1. vertical blank set-clear
02WSYNC strobe wait for leading edge of horizontal blank
03RSYNC strobe reset horizontal sync counter
04NUSIZ0 ..111111 number-size player-missile 0
05NUSIZ1 ..111111 number-size player-missile 1
06COLUP0 1111111. color-lum player 0
07COLUP1 1111111. color-lum player 1
08COLUPF 1111111. color-lum playfield
09COLUBK 1111111. color-lum background
0ACTRLPF ..11.111 control playfield ball size & collisions
0BREFP0 ....1... reflect player 0
0CREFP1 ....1... reflect player 1
0DPF0 1111.... playfield register byte 0
0EPF1 11111111 playfield register byte 1
0FPF2 11111111 playfield register byte 2
10RESP0 strobe reset player 0
11RESP1 strobe reset player 1
12RESM0 strobe reset missile 0
13RESM1 strobe reset missile 1
14RESBL strobe reset ball
15AUDC0 ....1111 audio control 0
16AUDC1 ...11111 audio control 1
17AUDF0 ...11111 audio frequency 0
18AUDF1 ....1111 audio frequency 1
19AUDV0 ....1111 audio volume 0
1AAUDV1 ....1111 audio volume 1
1BGRP0 11111111 graphics player 0
1CGRP1 11111111 graphics player 1
1DENAM0 ......1. graphics (enable) missile 0
1EENAM1 ......1. graphics (enable) missile 1
1FENABL ......1. graphics (enable) ball
20HMP0 1111.... horizontal motion player 0
21HMP1 1111.... horizontal motion player 1
22HMM0 1111.... horizontal motion missile 0
23HMM1 1111.... horizontal motion missile 1
24HMBL 1111.... horizontal motion ball
25VDELP0 .......1 vertical delay player 0
26VDELP1 .......1 vertical delay player 1
27VDELBL .......1 vertical delay ball
28RESMP0 ......1. reset missile 0 to player 0
29RESMP1 ......1. reset missile 1 to player 1
2AHMOVE strobe apply horizontal motion
2BHMCLR strobe clear horizontal motion registers
2CCXCLR strobe clear collision latches
+
+

+


+

+ +

TIA READ ADDRESS SUMMARY

+ +
+ + + + + + + + + + + + + + + + +
6-bit AddressAddress Name 76543210 FunctionD7D6
0CXM0P 11...... read collision M0 P1 M0 P0
1CXM1P 11...... read collision M1 P0 M1 P1
2CXP0FB 11...... read collision P0 PF P0 BL
3CXP1FB 11...... read collision P1 PF P1 BL
4CXM0FB 11...... read collision M0 PF M0 BL
5CXM1FB 11...... read collision M1 PF M1 BL
6CXBLPF 1....... read collision BL PF unused
7CXPPMM 11...... read collision P0 P1 M0 M1
8INPT0 1....... read pot port
9INPT1 1....... read pot port
AINPT2 1....... read pot port
BINPT3 1....... read pot port
CINPT4 1....... read input
DINPT5 1....... read input
+ Note : I0, I2, I2, I3 can be grounded + under software control. + I4, I5 can be converted to latched + inputs under software control +
+

+(For PIA I/O addresses, see section 6,0 --BW) +

+


+

+ +Editor's Notes

+(This section is *not* part of the manual, so you can ignore it if +you want! --BW) +

+I did this HTML version of the Stella Programming Guide for a couple of +reasons: +
    +
  • +The existing PDF version is great, but I really hate having to use a PDF +reader to read it. HTML is (supposedly) cross-platform, and since I already +keep 10-15 browser windows open at any given time, I'd rather use the same +GUI for reading the Stella docs as I do for everything else that's graphical. +
  • +The existing plain-text version I found is ok, but it's missing all the +diagrams (of course), and some of the text formatting is a bit strange. +
  • +I tried numerous programs that claim to convert PDF (or PostScript) to HTML, +but they all produced ugly, unusable results. +
  • +I wanted internal links in the document, such as the table of contents. The +existing PDF version doesn't seem to have them. The next version of this +document will probably have links to external `how-to' docs, with code +snippets and such. +
  • +The Stella/Atari development community are some of the nicest people I've +ever met, and this is my attempt to give back to the community in some small +way. +
  • +Finally, I needed an excuse to procrastinate... my poor 2600 game is in a +nasty state of disrepair, and I'm at the point where I'd be better starting +from scratch. Taking a break from 6502 asm and writing in a `language' like +HTML gives me a much-needed breather. +
+

+The original PDF version has a few typos. I don't know if those are in the +original paper version, or mistakes in the transcription, but I fixed as many +of them as I could find (most of them were silly, like it's instead of +its, or sidable for disable). Also, I didn't try to +format the tables exactly as they appear in the PDF version, since HTML isn't +much good for that sort of thing anyway. I did my best to preserve the content, +but took a few liberties with the presentation.

+ +Although I have tried to avoid it, I'm sure I introduced a few errors and +typos of my own. If anybody spots one, let me know, and I'll fix it ASAP.

+ +As far as copyright goes, who knows what the official, legal status of the +Stella Programmer's Guide is? I sure don't. But this HTML version is free for +anyone to use and/or redistribute, as much as the original version is. If +you modify this version, I'd really appreciate it if you'd clearly mark your +new version as modified. Better yet, if you fix some of my mistakes, show me +what I did wrong so I can learn from them!

+ +I looked at this in Netscape 4.7, Opera 5.0b6, and Konqueror 2.1 on Linux, and IE 5.0 on Win98; it looks OK on all of them. +If you have any problems with the HTML formatting, please let me know. I tried to +use `bog standard' HTML as much as possible, so it should work OK on any +browser, on any OS, but if it doesn't, I'll fix it the best I am able.

+ +B. Watson
+atari@hardcoders.org
+09/15/2001
+ +

\ No newline at end of file diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/sizes.txt b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/sizes.txt new file mode 100644 index 00000000..2aabc284 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/sizes.txt @@ -0,0 +1,1966 @@ + + Cart Information + ---- ----------- + + + + Info about cart sizes and bankswitching methods + + + + By Kevin Horton + khorton@iquest.net + + +V6.00 +Last modified on 4/18/97 +(614 images) + +Copyright 1997 K Horton, all rights reserved! You may copy and distribute +this file as long as it remains intact and un-modified. + + +This text has been modified to be machine-readable by Bankzilla's database +generator. This includes the '####Data Start####' and '####Data End####' +flags, as well as including an 'overflow' category and including controller +type. + + + How Bankswitching Works + --- ------------- ----- + +Bankswitching allows game programmers to include more data into a cartridge, +therefore making (hopefully) a better game with more graphics/ levels. + +Bankswitching works under similar principals in all cases. Basically, by +reading a certain location in ROM switches banks. + + +(this is the F8-style of bankswitching) + +Bank #1 Bank #2 +-------------------------------------------------------------- +1000 JSR $1800 (do subroutine) . +1003 (program continues) 1200 _subroutine goes here_ +. 1209 RTS +. . +1800 LDA $1FF9 (switch to bank 2) 1802 (rest of program) +1803 NOP 1803 JSR $1200 +1804 NOP . +1805 NOP . +1806 NOP 1806 LDA $1FF8 (Switch back to bank 1) +1807 NOP . +1808 NOP . +1809 RTS (We're done w/ routine) 1809 (rest of program) + +OK, we start out in bank #1 and we want to run a subroutine in bank #2. + +What happens is this- the processor starts at 1000h in bank #1. We call +our subroutine from here. 1800h: We do a read to change us to bank #2. +Remember that when we change banks, we are basically doing a ROM swap. +(You can think of bankswitching as 'hot-swapping' ROMs) Now that we're +in bank #2, the processor sees that JSR to $1200, which is the subroutine +that we wanted to execute. We execute the subroutine and exit it with an +RTS. This brings us back to 1806h. We then do another read to select bank +#1. After this instruction finishes, the processor is now in bank #1, with +the program counter pointing to 1809, which is an RTS which will take us +back to 1003 and let us continue on with our program. + + + Extra RAM in Carts + ----- --- -- ----- + +Some carts have extra RAM; There are three known formats for this: + +Atari's 'Super Chip' is nothing more than a 128-byte RAM chip that maps +itsself in the first 256 bytes of cart memory. (1000-10FFh) +The first 128 bytes is the write port, while the second 128 bytes is the +read port. This is needed, because there is no R/W line to the cart. + +CBS RAM Plus (RAM+) This maps in 256 bytes of RAM in the first 512 bytes +of the cart; 1000-11FF. The lower 256 addresses are the write port, while +the upper 256 addresses are the read port. To store a byte and retrieve it: + +LDA #$69 ; byte to store +STA $1000 ; store it +. +. ; rest of program goes here +. +LDA $1100 ; read it back +. ; acc=$69, which is what we stored here earlier. + + +M-network (AFAIK it has no name) + +OK, the RAM setup in these carts is very complex. There is a total of 2K +of RAM broken up into 2 1K pieces. One 1K piece goes into 1000-17FF +if the bankswitch is set to $1FE7. The other is broken up into 4 256-byte +parts. + +You select which part to use by issuing a fake read to 1FE8-1FEB. The +RAM is then available for use by all banks at 1800-19FF. + +Similar to other schemes, 1800-18FF is write while 1900-19FF is read. +Low RAM uses 1000-13FF for write and 1400-17FF for read. + + +Note that the 256-byte banks and the large 1K bank are seperate entities. +The M-Network carts are about as complex as it gets. + + + Descriptions of the Various Bankswitch Modes + -------------------------------------------- + +2K: + + -These carts are not bankswitched, however the data repeats twice in the + 4K address space. You'll need to manually double-up these images to 4K + if you want to put these in say, a 4K cart. + +4K: + + -These images are not bankswitched. + +6K: + + -AR: The Arcadia (aka Starpath) Supercharger uses 6K of RAM to store the + games loaded from tape. + +8K: + + -F8: This is the 'standard' method to implement 8K carts. There are two + addresses which select between two unique 4K sections. They are 1FF8 + and 1FF9. Any access to either one of these locations switches banks. + Accessing 1FF8 switches in the first 4K, and accessing 1FF9 switches in + the last 4K. Note that you can only access one 4K at a time! + + -FE: Used only on two carts (Robot Tank and Decathlon). You select banks + via accesses to the stack. You set the stack pointer to FF, and then a + JSR switches banks one way, while RTS switches you back to the original + bank (both banks are 4K). This allows the programmers to perform + 'automatic' bankswitching. All the subroutines are in one bank, while + all the game code is in another. When you perform a JSR; you switch banks + to the bank containg the subroutines. Upon encoutering an RTS, the bank + is switched back to the original calling bank. Pretty spiffy! + + -E0: Parker Brothers was the main user of this method. This cart is + segmented into 4 1K segments. Each segment can point to one 1K slice of + the ROM image. You select the desired 1K slice by accessing 1FE0 to 1FE7 + for the first 1K (1FE0 selects slice 0, 1FE1 selects slice 1, etc). + 1FE8 to 1FEF selects the slice for the second 1K, and 1FF0 to 1FF8 selects + the slice for the third 1K. The last 1K always points to the last 1K + of the ROM image so that the cart always starts up in the exact same place. + + -3F: Tigervision was the only user of this intresting method. This works + in a similar fashion to the above method; however, there are only 4 2K + segments instead of 4 1K ones, and the ROM image is broken up into 4 2K + slices. As before, the last 2K always points to the last 2K of the image. + You select the desired bank by performing an STA $3F instruction. The + accumulator holds the desired bank number (0-3; only the lower two bits + are used). Any STA in the $00-$3F range will change banks. This appears to + interfere with the TIA addresses, which it does; however you just use + $40 to $7F instead! :-) $3F does not have a corresponding TIA register, so + writing here has no effect other than switching banks. Very clever; + especially since you can implement this with only one chip! (a 74LS173) + +12K: + + -FA: Used only by CBS. Similar to F8, except you have three 4K banks + instead of two. You select the desired bank via 1FF8, 1FF9, and 1FFA. + These carts also have 256 bytes of RAM mapped in at 1000-11FF. 1000-10FF + is the write port while 1100-11FF is the read port. + +16K: + + -F6: The 'standard' method for implementing 16K of data. It is identical + to the F8 method above, except there are 4 4K banks. You select which + 4K bank by accessing 1FF6, 1FF7, 1FF8, and 1FF9. + + -E7: Only M-Network used this scheme. This has to be the most complex + method used in any cart! :-) It allows for the capability of 2K of RAM; + although it doesn't have to be used (in fact, only one cart used it- + Burgertime). This is similar to the 3F type with a few changes. There are + now 8 2K banks, instead of 4. The last 2K in the cart always points to + the last 2K of the ROM image, while the first 2K is selectable. You + access 1FE0 to 1FE6 to select which 2K bank. Note that you cannot select + the last 2K of the ROM image into the lower 2K of the cart! Accessing + 1FE7 selects 1K of RAM at 1000-17FF instead of ROM! The 2K of RAM is + broken up into two 1K sections. One 1K section is mapped in at 1000-17FF + if 1FE7 has been accessed. 1000-13FF is the write port, while 1400-17FF + is the read port. The second 1K of RAM appears at 1800-19FF. 1800-18FF + is the write port while 1900-19FF is the read port. You select which + 256 byte block appears here by accessing 1FF8 to 1FFB. + +32K + + -F4: The 'standard' method for implementing 32K. Only one cart is known + to use it- Fatal Run. Like the F6 method, however there are 8 4K + banks instead of 4. You use 1FF4 to 1FFB to select the desired bank. + +64K + + -F0: Only used one cart, AFAIK. (the 'Megaboy' cart from Dynacom) It + has 16 4K banks. Accessing 1FF0 will increment the current bank. The + program uses location 1FEC to tell it which bank it's in. There's a + little loop at 1FE0 that checks this location against the accumulator, + and if they're equal it does an RTS. Otherwise it does an STA 1FF0 + and repeats the loop. + + + /-----------------------------------------------------------------------\ + | KEY | + | --- | + | Name - Game Name | + | Part # - Part Number of the actual cart | + | RA - Rarity, according to VGR's guide and my observations | + | SZ - Size of the ROM image in K | + | SC - If the cart has a Special Chip | + | BS - Bankswitch method used (see below) | + | IM - 'X'ed if I have the image | + | SP - Special Attribute (See the end of a section for details) | + | CT - Controller type (See below) | + | Filename - The filename of the ROM image | + | | + | Bankswitch Types: | + | ----------------- | + | (See above for full descriptions) | + | | + | - (nothing); Not bankswitched (2K and 4K only) | + | F8 - 'Standard' 8K; uses 1FF8 and 1FF9 | + | F6 - 'Standard' 16K; uses 1FF6 to 1FF9 | + | F4 - 'Standard' 32K; uses 1FF4 to 1FFB | + | F0 - Megaboy 64K; uses 1FF0 to increment bank # | + | SC - Superchip; 128 bytes of RAM @ 1000-10FF (i.e. F8+SC, F4+SC) | + | FA - 'RAM+' 12K; uses 1FF8 to 1FFA; 256 bytes of RAM @ 1000 to 11FF | + | FE - 'Activision' 8K; uses 01FE and 01FF to determine bank | + | E0 - 'Parker Brothers' 8K; uses 1FE0 to 1FF7 | + | E7 - 'M-Network' 16K; Uses 1FE0 to 1FE7 and 2K of RAM at 1800-19FF | + | 3F - 'Tigervision' 8K; Uses STA $3F to determine bank # | + | AR - 'Arcadia' 6K; Used on the Supercharger | + | ?? - Unknown at this time | + | | + | Controller Types: | + | ----------------- | + | | + | - (nothing); Unknown at this time | + | J - Joystick | + | P - Paddles | + | K - Keypad | + | JK - Joystick and keypad (Star Raiders) | + | D - Driving Controllers | + | B - Joystick plus Booster Grip (Omega Race) | + | T - Track & Field controller | + | O - Other | + | L - Light Gun | + | | + | | + | I have added two new categories to the rarity rating: | + | | + | PR - This was only available as a prototype | + | DM - This image is only a demo, and not really a game | + | | + \-----------------------------------------------------------------------/ + + + + + Hot Wants (that I know I probably won't get :-) + ----------------------------------------------- + + +Tempest (Atari) +Good Luck, Charlie Brown (Atari) +Miss Piggy's Wedding (exists?) (Atari) +Wizard (Atari) +BMX Airmaster (atari ver) (Atari) +White Water Madness (exists?) (Atari) +Rodeo (Atari) +Rabbit Transit (Atari) +Nightmare Manor (Atari) +Pink Panther (Probe 2000) +The Impossible Game (Telesys) +Ewoks Adventure (Parker Bros) +Thwoker (Activision) +Out of Control (Avalon Hill) +Berenstein Bears (Coleco) +Video Life (Commavid) +Aerial Ace (exist?) (Imagic) +Lady in Wading (Playaround) +Snowplow (Sunrise) +Noah and the Ark (Sunrise) +Meltdown (exist?) (20th cent.) +Tomarc the Barabrrian (exist?) (Xonox) +Motocross Racer (exist?) (Xonox) + + + +Anything by Action Hi-tech + + + + + +####Data Start#### + + +*********************************** +* Atari * +*********************************** + +[If SC is marked, cart uses a 'Super Chip'; aka CO20231] + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Combat CX2601 C 2 X J COMBAT +Air-Sea Battle CX2602 U 2 X J AIR_SEA +Star Ship CX2603 R 2 X STARSHIP +Space War CX2604 U 2 X SPACEWAR +Outlaw CX2605 U 2 X J OUTLAW +Slot Racers CX2606 U 2 X SLOTRACE +Canyon Bomber CX2607 U 2 X CANYONB +Super Breakout CX2608 U 4 X P SUPERB +Defender CX2609 C 4 X J DEFENDER +Warlords CX2610 U 4 X P WARLORDS +Indy 500 CX2611 U 2 X D INDY_500 +Street Racer CX2612 U 2 X STRTRACE +Adventure CX2613 C 4 X J ADVNTURE +Steeple Chase CX2614 NR 2 X P STEPLCHS +Demons to Diamonds CX2615 U 4 X DEMONDIM +Hot Rox CX2615 NR 4 X DEMONDIM +Pele's Soccer CX2616 C 4 X PELE +Backgammon CX2617 U 4 X BACKGAM +3D- Tic-Tac-Toe CX2618 U 2 X J 3D_TIC +Stellar Track CX2619 NR 4 X STELRTRK +BASIC Programming CX2620 R 4 X K BASIC +Video Olympics CX2621 C 2 X P VID_OLYM +Breakout CX2622 C 2 X P BREAKOUT +Homerun CX2623 C 2 X HOMERUN +Basketball CX2624 C 2 X BASKETBL +Football CX2625 C 2 X FOOTBALL +Minature Golf CX2626 U 2 X MIN_GOLF +Human Cannonball CX2627 U 2 X HUMAN_CB +Bowling CX2628 C 2 X J BOWLING +Sky Diver CX2629 U 2 X SKYDIVER +Circus Atari CX2630 C 4 X P CIRCATRI +Superman CX2631 C 4 X J SUPRMAN1 +Space Invaders CX2632 C 4 X J SPCINVAD +Night Driver CX2633 C 2 X NIGHTDRV +Golf CX2634 C 2 X GOLF +Maze Craze CX2635 U 4 X J MAZECRZ +Video Checkers CX2636 R 4 X J CHECKERS +Dodge 'Em CX2637 U 4 X DODGE_EM +Missile Command CX2638 C 4 X J MISSCOMM +Othello CX2639 R 2 X OTHELLO +Realsports Baseball CX2640 U 8 F8 X RS_BASEB +Surround CX2641 C 2 X J SURROUND +A Game of Concentration CX2642 C 2 X CONCENTR +Code Breaker CX2643 U 2 X CODEBRK +Flag Capture CX2644 U 2 X FLAGCAP +Video Chess CX2645 U 4 X VIDCHESS +Pac-Man CX2646 C 4 X J PACMAN +Submarine Commander CX2647 PR 4 X J SUBCOMDR +Video Pinball CX2648 U 4 X J VIDPIN +Asteroids CX2649 C 8 F8 X J ASTEROID +Berzerk CX2650 C 4 X J BERZERK +Blackjack CX2651 R 2 X BLACK_J +Casino CX2652 U 4 X CASINO +Slot Machine CX2653 R 2 X SLOTMACH +Haunted House CX2654 C 4 X J HAUNTHSE +Yar's Revenge CX2655 C 4 X J YAR_REV +Swordquest Earthworld CX2656 C 8 F8 X J SQ_EARTH +Swordquest Fireworld CX2657 C 8 F8 X J SQ_FIRE +Math Gran Prix CX2658 C 4 X J MATH_GPX +Raiders of the Lost Ark CX2659 C 8 F8 X J RAIDERS +Star Raiders CX2660 U 8 F8 X JK STARRAID +Basic Math CX2661 C 2 X J BASMATH +Hangman CX2662 U 4 X J HANGMAN +Road Runner CX2663 ER 16 F6 X J ROADRUNR +Brain Games CX2664 U 2 X BRAINGMS +Frog Pond CX2665 PR 8 F8 X J FROGPOND +Realsports Volleyball CX2666 U 4 X RS_VOLLY +Realsports Soccer CX2667 U 8 F8 X RSSOCCER +Realsports Football CX2668 C 8 F8 X RS_FOOTB +Vanguard CX2669 C 8 F8 X VANGUARD +Atari Video Cube CX2670 R 4 X J VIDCUBE +Swordquest Waterworld CX2671 UR 8 F8 X J SQ_WATER +Swordquest Airworld CX2672 NR ?? ---No Known Copies Exist--- +Phoenix CX2673 C 8 F8 X J PHOENIX +E.T. The Extra-Terrestrial CX2674 C 8 F8 X J E_T +Ms. Pac-Man CX2675 C 8 F8 X J MSPACMAN +Centipede CX2676 C 8 F8 X J CENTIPED +Dig Dug CX2677 U 16 X F6 X J DIGDUG +Dukes of Hazzard CX2678 PR 16 F6 X J DUKES +Realsports Basketball CX2679 NR ?? ---No Known Copies Exist--- +Realsports Tennis CX2680 U 8 F8 X J RSTENNIS +Battlezone CX2681 U 8 F8 X J BATLZONE +Krull CX2682 R 8 F8 X J KRULL +Crazy Climber CX2683 ER 8 F8 X J CRAZCLMB +Galaxian CX2684 U 8 F8 X J GALAXIAN +Gravitar CX2685 U 8 F8 X J GRAVITAR +Quadrun CX2686 ER 8 F8 X J QUADRUN +Tempest CX2687 PR ?? ---A Few Proto's Exist--- +Junglehunt CX2688 U 8 F8 X J JNGLHUNT +Kangaroo CX2689 U 8 F8 X J KANGAROO +Pengo CX2690 ER 8 F8 X J PENGO +Joust CX2691 C 8 F8 X J JOUST +Moon Patrol CX2692 U 8 F8 X J MOONPTRL +Food Fight CX2693 NR ?? ---No Known Copies Exist--- +Pole Position CX2694 C 8 F8 X J POLEPSN +Xevious CX2695 PR 8 F8 X J XEVIOUS +Asterix CX2696 ER 8 F8 X J ASTERPAL +Mario Bros. CX2697 U 8 F8 X J MARIOBRO +Rubik's Cube CX2698 ER 4 X J RUBIKS +Taz CX2699 R 8 F8 X J TAZ +Oscar's Trash Race CX26101 R 8 F8 X K OSCAR +Cookie Monster Crunch CX26102 R 8 F8 X K COOKMONS +Alpha-Beam with Ernie CX26103 R 8 F8 X K ALPHBEAM +Big Bird's Egg Catch CX26104 R 8 F8 X K EGGCATCH +3-D Asteroids CX26105 NR ?? +Grover's Music Maker CX26106 PR 8 F8 X GROVER +Snow White CX26107 NR ?? +Donald Duck's Speedboat CX26108 PR 8 F8 X J DDUCKSBT +Sourcerer's Apprentice CX26109 R 8 F8 X J SORCAPRN +Crystal Castles CX26110 U 16 X F6 X J XTALCAST +Snoopy and the Red Barron CX26111 R 8 F8 X J SNOOPY +Good Luck; Charlie Brown CX26112 PR ?? ---One Proto Exists--- +Miss Piggie's Wedding CX26113 UR ?? +Pigs in Space CX26114 ER 8 F8 X J PIGSPACE +Dumbo's Flying Circus CX26115 PR 8 F8 X J DUMBO_N +Galaga CX26116 NR ?? +Obelix CX26117 ER 8 F8 X J OBELIX +Millipede CX26118 R 16 X F6 X J MILLIPED +Saboteur CX26119 PR 8 F8 X J SABOTEUR +Star Gate CX26120 U 8 X F8 X J STARGATE +Defender ][ CX26120 R 8 X F8 X J DEFENDR2 +Zookeeper CX26121 NR ?? +Sinistar CX26122 PR 8 F8 X J SINISTAR +Jr. Pac-Man CX26123 U 16 X F6 X J JRPACMAN +Choplifter CX26124 NR ?? +Track & Field CX26125 R 16 F6 X T TRACK +Elevator Action CX26126 NR ?? +Gremlins CX26127 ER 8 F8 X J GREMLINS +Boing CX26128 NR ?? +Midnight Magic CX26129 R 16 F6 X J MIDNIGHT +Honker Bonker CX26130 NR ?? +Monstercise CX26131 PR 8 F8 X MONS +Garfield CX26132 NR ?? +The A-Team CX26133 PR 8 F8 X J ATEAM +The Last Starfighter CX26134 NR ?? +Star Raiders ][ CX26134 NR ?? +Realsports Boxing CX26135 U 16 F6 X J RSBOXING +Solaris CX26136 U 16 F6 X J SOLARIS +Peek-A-Boo CX26137 PR 4 X PEEKABOO +Super Soccer CX26138 NR ?? +Crossbow CX26139 U 16 F6 X J CROSSBOW +Desert Falcon CX26140 C 16 X F6 X J DSRTFALC +Motor Psycho CX26141 NR ?? +Crack'ed CX26142 ?? +Donkey Kong CX26143 U 4 X J DK +Donkey Kong Jr. CX26144 R 8 F8 X J DKJR +Venture CX26145 R 4 X J VENTURE +Mousetrap CX26146 R 4 X J MOUSETRP +Frogger CX26147 NR 4 X J FROGGER +Turbo CX26148 NR ?? +Zaxxon CX26149 NR ?? +Q*Bert CX26150 R 4 X J QBERT_PB +Dark Chambers CX26151 U 16 X F6 X X3 J DARKC +Super Baseball CX26152 U 16 F6 X J SUPBBALL +Super Football CX26154 U 16 X F6 X J SPRFOOTB +Sprintmaster CX26155 R 16 X F6 X J SPRNMAST +Combat II (Wizard?) CX26156 NR ?? + CX26157 ?? +Surround II CX26158 NR ?? +Double Dunk CX26159 R 16 F6 X J DOUBDUNK + CX26160 ?? + CX26161 ?? +Fatal Run CX26162 UR 32 X F4 X J FATALRUN +32-in-1 CX26163 ER 64 32 banks of 2K -- 32IN1 + CX26164 ?? +Jinks CX26165 NR ?? + CX26166 ?? +Street Fight CX26167 ?? +Off the Wall CX26168 ER 16 X F6 X J OFTHWALL +Shooting Arcade CX26169 PR 16 X F6 X L SHOOTING +Secret Quest CX26170 R 16 X F6 X J SECRETQ +Motorodeo CX26171 UR 16 F6 X J MOTOR +Xenophobe CX26172 ER 16 F6 X J XENOPHOB + CX26173 ?? + CX26174 ?? + CX26175 ?? +Radar Lock CX26176 R 16 X F6 X J RADARLOK +Ikari Warriors CX26177 R 16 F6 X J IKARIWAR +Save Mary! CX26178 PR 16 X F6 X J SAVEMARY + CX26179 ?? + CX26180 ?? + CX26181 ?? + CX26182 ?? +Sentinel CX26183 R 16 F6 X L SENTINEL +White Water Madness CX26184 UR ?? + CX26185 ?? + CX26186 ?? + CX26187 ?? + CX26188 ?? + CX26189 ?? +BMX Airmaster CX26190 ER ?? + CX26191 ?? +KLAX CX26192 ER 16 X F6 X X2 J KLAXNTSC + + +Aquaventure CX26??? PR 8 F8 X J AQUAVENT +Bionic Breakthrough CX26??? PR 8 F8 X O MINDLINK +Bugs Bunny CX26??? PR 8 F8 X J BUGSBUN +Coke Wins! CX26??? UR 4 X J COKEWINS +Holy Moley CX26??? PR 8 F8 X HOLEMOLE +Polo CX26??? PR 2 X J POLO +Rodeo CX26??? PR ?? +Rabbit Transit CX26??? PR ?? +Standalone Test Tape MAO17600 DM 2 X -- MAO17600 +Nightmare Manor CX26??? PR ?? +Super Stunt Cycle PR 2 X J STUNT-1 +Dukes of Hazzard (not CX2678) PR 2 X J STUNT-2 + +X2: Special Best Prototype NTSC version. + +X3: Doesn't like my test cart; have to disable SC for it to start. I + can then re-enable the SC and it'll work. Also, the cart itsself + doesn't work on my 7800 (or my test cart for that matter). + + +*********************************** +* Action Hi-Tech * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Crab Control 605077 UR ?? +F-18 vs. Aliens ???? UR ?? +Galaxy Invader ???? UR ?? +Space Grid ???? UR ?? +Tank City ???? UR ?? +War Zone ???? UR ?? + + +*********************************** +* Activision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Dragster AG-001 U 2 X J DRAGSTER +Boxing AG-002 U 2 X J BOXING +Checkers AG-003 ER 2 X J CHECKERA +Fishing Derby AG-004 U 2 X J FISHDRBY +Skiing AG-005 U 2 X J SKIING +Bridge AX-006 U 4 X J BRIDGE +Tennis AG-007 C 2 X J TENNIS +Laser Blast AG-008 C 2 X J LASRBLST +Freeway AG-009 U 2 X J FREEWAY +Kaboom AG-010 U 2 X P KABOOM +Stampede AG-011 U 2 X J STAMPEDE +Ice hockey AX-012 U 4 X J ICEHOCKY +Barnstroming AX-013 U 4 X J BARNSTRM +Gran Prix AX-014 U 4 X J GRANDPRX +Chopper Command AX-015 U 4 X J CHOPRCMD +Starmaster AX-016 U 4 X J STARMAST +Megamania AX-017 U 4 X J MEGAMAN +Pitfall AX-018 C 4 X J PITFALL +Sky Jinks AG-019 R 2 X J SKYJINKS +River Raid AX-020 U 4 X J RIVERAID +Spider Fighter AX-021 U 4 X J SPIDRFTR +Seaquest AX-022 R 4 X J SEAQUEST +Oink! AX-023 R 4 X J OINK +Dolphin AX-024 R 4 X J DOLPHIN +Keystone Kapers AX-025 U 4 X J KEYSTONE +Enduro AX-026 U 4 X J ENDURO_A +Plaque Attack AX-027 R 4 X J PLAQATTK +Robot Tank AZ-028 R 8 F8 X X2 J ROBO_FIX +Crackpots AX-029 R 4 X J CRACKPOT +Decathlon AZ-030 R 8 FE X J DECATHLN +Frostbite AX-031 R 4 X J FROSTBIT +Pressure Cooker AZ-032 R 8 F8 X J PRESSURE +Space Shuttle AZ-033 R 8 F8 X J SPCSHUTL +Private Eye AG-034-04 R 8 F8 X J PRIVEYE +Pitfall ][: Lost Caverns AB-035-04 R 8 F8 X X1 J PITFALL2 +H.E.R.O. AZ-036-04 R 8 F8 X J HERO +Beamrider AZ-037-04 R 8 F8 X J BEAMRIDE +Cosmic Commuter AG-038 R 4 X J CSMCOMTR +Kung-Fu Master AX-039 R 8 F8 X J KUNG_FU +Contenders AK-041 ---Never Released by Activision--- +Commando AK-043 R 16 F6 X J COMMANDO +Fighter Pilot AK-046 UR 16 F6 X J FIGHTERP +River Raid ][ AK-048-04 ER 16 F6 X J RIVRAID2 +Rampage AK-049 R 16 F6 X J RAMPAGE +Double Dragon AK-050-04 R 16 F6 X J DBLDRAGN +Ghostbusters AZ-108-04 R 8 F8 X J GHOSTBST +Ghostbusters ][ A?-??? NR ?? ---See Salu--- +Dreadnaught Factor A?-??? NR ?? +Wing War A?-??? NR ?? ---See Imagic--- +Thwocker A?-??? PR ?? ---Proto Exists--- +Zenji A?-??? NR ?? + + +X1: Uses Activision's version of the 'Super Chip' No extra RAM this time, + however it does have extra ROM! Does three-channel sound, and even + includes several random # generators. + + +X2: This used to be an FE cart, but has been fixed to run as an F8. + +*********************************** +* Absolute Entertainment * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Title Match Pro Wresting AG-041 R 8 F8 X J PROWREST +Skateboardin' AG-042 R 8 F8 X J SKATEBRD +Pete Rose Baseball AK-045 R 16 F6 X J PETEROSE +Tomcat F-14 Simulator AK-046 ER 16 F6 X X1 J FIGHTERP +My Golf A?-??? ER 8 F8 X J MYGOLF + +X1: This is identical to the Activision one. I read both in and compared; +they are byte-for-byte identical. + +*********************************** +* American Videogame * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Tax Avoiders ???? R 8 F8 X J TAXAVOID + + +*********************************** +* Amiga * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Mogul Maniac 3120 ER 4 X J MOGULMAN +Surf's Up 3125 PR 8 F8 X J SURF_FIX +Off your Rocker 3130 PR 4 X J OFFROCKR + + +*********************************** +* Answer Software * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Malagai ASC1001 UR 4 X J MALAGAI +Gauntlet ASC1002 UR 4 X J GAUNTLET +Confrontation ASC2001 UR ?? +Personal Game Programmer PGP-1 UR XX ---Hardware--- + + +*********************************** +* Artic * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Space Robot SM8001 UR 4 X J SPCROBOT +Astrowar SM8002 UR 4 X J ASTROWAR + + +*********************************** +* Apollo * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Skeet Shoot AP 1001 R 2 X SKEETSHT +Spacechase AP 2001 U 4 X SPACHASE +Space Cavern AP 2002 U 4 X SPACECAV +Racquetball AP 2003 U 4 X RACQUETB +Lost Luggage AP 2004 U 4 X J LOSTLUGG +Lochjaw AP 2005 ER 4 X J LOCHJAW +Shark Attack AP 2005 U 4 X J SHARKATK +Infiltrate AP 2006 U 4 X J INFILTRT +Kyphus AP 2007 R ?? +Guardian AP 2008 R 4 X GUARDIAN +Final Approach AP 2009 R 4 X J FINLAPCH +Wabbit AP 2010 R 4 X J WABBIT +Pompeii AP 2011 R ?? +Squoosh AP 2012 PR ?? + + +*********************************** +* Avalon Hill * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Death Trap 50010 UR 4 X J DETHTRAP +London Blitz 50020 ER 4 X J LONDBLTZ +Wall Ball 50030 ER 4 X J WALLBALL +Shuttle Orbiter 50040 UR 4 X J SHTLORBT +Out of Control 50050 UR ?? Need! + + +*********************************** +* Bit Corp. * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Sea Monster PG201 R 4 X SEAMNSTR +Space Tunnel PG202 R 4 X SPACT_TW +Phantom Tank PG203 R 4 X PHANTOMT +Open Sesame PG204 R 4 X OPENSESM +Dancing Plate PG205 R 4 X DANCPLAT +Bobby is Going Home PG206 R 4 X J BOBBY +Mission 3000 AD PG207 R 4 X MISN3000 +Snail Against Squirrel PG208 R 4 X SNALSQRL +Mr. Postman PG209 R 4 X MRPOSTMN +Superman (CCE) ???? ?? 4 X SUPERCCE + +*********************************** +* Bob Colbert * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Okie Dokie (Lights Out) ???? -- 2 X J OKIEDOKE + + +*********************************** +* Bomb * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Assault CA281 ER 4 X J ASSAULT +Great Escape CA282 ER 4 X J GRESCAPE +Z-Tack CA283 ER 4 X J Z_TACK +Wall Defender CA285 ER 4 X J WALLDFND + + +*********************************** +* CBS Electronics * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Wizard of Wor M8774 R 4 X J WIZRDWOR +Gorf M8793 U 4 X J GORF +Blueprint 4L-2486 U 8 F8 X J BLUEPRNT +Solar Fox 4L-2487 U 8 F8 X J SOLARFOX +Tunnel Runner 4L-2520 R 12 FA X J TUNLRUNR +Omega Race 4L-2737 U 12 FA X B OMEGARAC +Mountain King 4L-2738 R 12 FA X J MTNKING +Wings ???? NR ?? ?? + +'SC' in this case refers to RAM+ + +*********************************** +* Coleco * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Donkey Kong 2451 C 4 X J DK +Zaxxon 2454 U 8 F8 X J ZAXXON +Venture 2457 C 4 X J VENTURE +Mouse Trap 2459 U 4 X J MOUSETRP +Lady Bug 2463 UR ?? +Cosmic Avenger 2464 UR ?? +Smurf: RIGC 2465 U 8 F8 X J SMURFRES +Carnival 2468 U 4 X J CARNIVAL +Smurfs Save the Day 2511 UR 8 F8 X J SMRFSAVE +Donkey Kong Jr. 2653 R 8 F8 X J DKJR +Mr. Do! 2656 R 8 F8 X J MRDO +Berinstein Bears 2658 UR ?? +Time Pilot 2663 R 8 F8 X J TIMEPLT +Front Line 2665 R 8 F8 X J FRNTLINE +Roc 'N Rope 2667 R 8 F8 X J ROCNROPE + + + +*addendum* +Got Mr. Do! to read out, and Time Pilot seems to have read out correctly, +yet it doesn't play on the emu. I'm going to pull the EPROM off the board +and read it directly! +*end* + +*addendum2* +Pulled the EPROM and copied it. Just as I suspected, it did indeed read out +correctly. Guess the emu isn't as good as the Real Thing. :-) I'll try it +out on the real hardware RSN. +*end* + +*addendum3* +The saga continues. I tried the ROM image of Time Pilot out on the Real +Thing, and it behaved the exact same way that it did on the emu. It appears +that the RC delay in the cart is required so it doesn't switch banks +immediately. I hope I can fix it so it can work as a normal F8 cart. +*end* + +*addendum4* +Yes! I got Time Pilot to work! This is intresting. The bank is *only* +flipped in two parts of the cart- once at the beginning of bank0 and once +at the beginning of bank1. The tip-off was bank #0 had a BIT $1FF8 +instruction and bank #1 had a BIT $1FF9 instruction! This of course will +*not* flip banks!!! I changed the 4K blocks around, ran it on the emu, and +it worked perfectly! +*end* + +*addendum5* +Finally figured out what was wrong with Smurfs Save the Day. The places +in the code that switch banks was right on top of each other. (the +STA $1FFx instructions were at the same addresses in diffrent banks) +As a result, there were only STA $1FF8 instructions rather than both +STA $1FF8 and STA $1FF9 instructions. Fixing these resulted in a working +ROM image!!!!!! Now all I need is Berinstein Bears to round out the +Coleco Collection. +*end* + +*********************************** +* CommaVid * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +MagiCard CM-001 UR ?? ----Hardware---- +Video Life CM-002 UR ?? +Cosmic Swarm CM-003 R 2 X J COSMSWRM +Room of Doom CM-004 ER 4 X J ROOMDOOM +Mines of Minos CM-005 ER 4 X MINEMNOS +Cakewalk CM-008 ER 4 X J CAKEWALK +Stronghold CM-009 ER 4 X STRNGHLD + + +*********************************** +* Data Age * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Encounter at L-5 DA 1001 U 4 X ENCONTL5 +Warplock DA 1002 C 4 X WARPLOCK +SSSnake DA 1003 C 4 X SSSNAKE +Airlock DA 1004 C 4 X AIRLOCK +Bugs DA 1005 U 4 X P BUGS +Journey Escape 112-006 C 4 X J JRNYESCP +Rock 'n Roll Escape 112-006 R 4 X X1 J JRNYESCP +Bermuda Triangle 112-007 R 4 X J BERMDTRI +Frankenstein's Monster 112-008 R 4 X J FRANKMON +Secret Agent ???? UR ?? + + +X1: This is the same as Journey Escape. + + +*********************************** +* DSD/Camelot * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Tooth Protectors ???? UR 8 E0 X X1 J TOOTHPRO + + +X1: Intresting!!! This cart uses the Parker Bros. 8K bankswitch!!! It's +the only non-PB cart to use this format. + + + +*********************************** +* Dynacom * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Mega Boy ???? ?? 64 F0 X J MEGABOY + + +This is a very intresting cart. It's designed as an educational product! +It was only test-marketed in Brazil, and most of it is in Portugese. It +contains several different learning tools- Math, English, Games, and Music! +This cart goes with a hand-held 2600 called the 'Mega Boy'... it's similar +to a TV Boy except it can accept regular 2600 carts! It runs on batteries +and transmits through the air to a TV in a similar fashion to a TV Boy. All +in all a very cool device. + + + +*********************************** +* Ed Fendermyer * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- +SoundX ???? ER 4 X J SOUNDX +EdTris ???? ER 4 X J EDTRIS + + +*********************************** +* Emag * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +I Want My Mommy GN-010 ER 4 X IWANTMOM +Dishaster GN-020 ER 4 X J DISHASTR +Tanks But No Tanks GN-030 ER 4 X TANKSBUT +Cosmic Corridor GN-040 ER 4 X COSMCORR +Pizza Chef GN-050 ER 4 X J PIZZA +Immies & Aggies GN-060 ER 4 X IMMIES +A Mysterious Thief GN-070 ER ?? +Fire Spinner GN-080 ER 4 X FIRESPIN + + +*********************************** +* Epyx * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Summer Games 8056100250 R 16 F6 X J SUMMERGA +Winter Games 8056100251 R 16 F6 X J WINTERGA +California Games 8056100286 R 16 F6 X J CALIFGMS +Super Cycle ???? ?? + + +*********************************** +* Exus * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Video Jogger ???? ?? 4 X O VIDJOGGR +Video Reflex ???? ?? 4 X O VIDREFLX + +*********************************** +* First Star Software * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Boing! ???? ER 4 X J BOING + +*********************************** +* Froggo * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Karate FG 1001 R 4 X J KARATE +Spiderdroid FG 1002 R 4 X J SPIDROID +Task Force FG 1003 R 4 X J TASKFORC +Cruise Missile FG 1007 R 4 X J CRUSMISL +Sea Hawk FG 1008 R 4 X J SEAHWK_F +Sea Hunt FG 1009 R 4 X J SEA_HUNT + + +*********************************** +* Funvision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Ocean City Defender ???? ?? 4 X J OCEANCTY +Spider Maze ???? ?? 4 X J SPDRMAZE + + +*********************************** +* HES * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Challenge ???? ?? 8 F8 X J CHALLANG +My Golf 535 ?? 8 F8 X J MYGOLF +Pigs 'n Wolf ???? ?? ?? +Star Warrior ???? ?? ?? + +Go for the Gold Pak (really two carts) + -Winter Games ?? 16 F6 X J WINTERG2 + -Summer Games ?? 16 F6 X J SUMMERG2 + +**** +Special menued multicarts: +**** + +Super Action Pak 223 ?? 16 F6 X J SUPERACT + -Pitfall + -Grand Prix + -Laser Blast + -Barnstorming +Smash Hit Pak 498 ?? 16 F6 X J SMASHHIT + -Frogger + -Stampede + -Seaquest + -Boxing + -Skiing +Hot Action Pak 542 ?? 16 F6 X J HOTPAK + -Ghostbusters + -Plaque Attack + -Tennis +Rad Action Pak 559 ?? 16 F6 X J RADACT + -Kung-Fu Master + -Frostbite + -Freeway +Mega Fun Pak ???? ?? ?? F6 X J MEGAPAK + -Gorf + -Planet Patrol + -Pac-Man + -Skeet Shoot +Sports Action Pak ???? ?? 16 F6 X J SPORTACT + -Enduro + -Ice hockey + -Fishing Derby + -Dragster +Super Hit Pak ???? ?? 16 F6 X J SUPERHIT + -River Raid + -Grand Prix + -Fishing Derby + -Jink + -Checkers +2 Pak Special #1 ???? ?? 16 F6 X J P0 + -Dungeon Master - Venture + -Creature Strike - Demon Attack +2 Pak Special #2 ???? ?? 16 F6 X J P1 + -Star Warrior - Starwars: Empire Strikes Back + -Frogger +2 Pak Special #3 ???? ?? 16 F6 X J P2 + -Wall Defender + -Planet Patrol +2 Pak Special #4 ???? ?? 16 F6 X J P3 + -Space Voyage - Starmaster + -Fire Alert - Fire Fighter +2 Pak Special #5 ???? ?? ?? + -Alien Force + -Hoppy +2 Pak Special #6 ???? ?? ?? + -Cavern Blaster + -City War +2 Pak Special #7 ???? ?? ?? + -Challenge + -Surfing +2 Pak Special #8 ???? ?? ?? + -Dolphin + -Pigs 'n Wolf +2 Pak Special #9 ???? ?? ?? + -Motocross + -Boom Bang + + +Notes about menued carts: These are very intresting! They consist of +several games in seperate banks of a 16K F6 bankswitched ROM. There's a +very slick looking menu that comes up displaying the co's logo (HES), and +to press the fire button. After doing so, the user is given a choice of +what game to play. The choices are actually written out onto the screen in +hi-res text! You highlight the desired game and hit the button. The tech +behind it is pretty simple, yet clever. On startup, the bank is pointed to +the menu system's bank, and then is run just like any other F6 cart. The +games are stored in seperate banks, or the upper 2K of a 4K block with the +lower 2K being the menuing program. When the user selects a game, a small +'stub' of code is written to RAM then executed; this stub is usually +something like this: + +0080: LDA $1FF8 ;change banks +0083: JMP $1000 ;run game + +So that when 0080 is called, the cart is switched to bank #2, and then the +game in said bank will be run. Pretty nifty! Note that since this is +already a bankswitched game, 8K bankswitched games can be run in it, as well +as non-bankswitched games. Check out the game linup on the 'Hot Action Pak'. +It is: + +Ghost Busters 8K +Plaque Attack 4K +Tennis 2K + +Total: 14K + +That gives us the extra 2K for the menuing system. + +On those '2 Pak Special' carts, they are still 16K, but almost half of this +goes to waste; they could've made '3 Pak Specials' to use up most of the +space no problem. I still don't know why they didn't do this. Examining the +ROM shows almost half of it is 'FF'. What a waste! :-) + + +*********************************** +* Homevision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + + +Robot Fight 1 ER 4 X J ROBOFGHT +War 2000 2 ?? +Gogo? Home Monster 3 ?? +World? Trap? 9 ?? +Asteroid Fire 11 ?? +Sky Alien 12 ?? +Base Attack 13 4 X BASEATTK +Wall Break 14 ?? +Lilly Adventure 17 4 X J LILLY +Col 'N' ???? 4 X J COLN +Cosmic War ???? ?? +Frisco ???? ?? +IQ 180 ???? 4 X J IQ180 +Magic Carpet ???? ?? +Panda Chase ???? 4 X J PANDCHSE +Parachute ???? 4 X PARCHUTE +Plate Mania ???? ?? +Racing Car ???? ?? +Repro Cart 83014 ?? +Tanks War ???? ?? +Teddy Apple ???? ?? +Tennis Topsy ???? ?? +Zoo Fun ???? 4 X J ZOOFUN + + +*********************************** +* Imagic * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Trick Shot IA3000 R 4 X J TRICKSHT +Demon Attack IA3200 C 4 X J DEMONATK +Star Voyager IA3201 C 4 X STARVYGR +Atlantis IA3203 U 4 X J ATLANTIS +Cosmic Ark IA3204 U 4 X COSMCARK +No Escape! IA3312 R 4 X NOESCAPE +Fire Fighter IA3400 R 4 X J FIREFITE +Aerial Ace IA3409 ER ?? +Shootin' Gallery IA3410 ER 4 X J SHOOTIN +Riddle of the Sphinx IA3600 U 4 X J RIDDLE +Dragon Fire IA3611 C 4 X J DRGNFIRE +Fathom O3205 R 8 F8 X J FATHOM +Solar Storm O3206 R 4 X SOLRSTRM +Moonsweeper O3207 ER 8 F8 X J MOONSWEP +Laser Gates O3208 U 4 X J LASRGATE +Quick Step O3211 R 4 X J QUICKSTP +Subterra O3213 R 8 F8 X J SUBTERRA +Wing War EIZ-002-04 ER 8 F8 X J WINGWAR +Cubicolour ???? UR 4 X J CUBICOL +Imagic Selector ???? DM 4 X -- IMAGSLCT + + +*********************************** +* ITT * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Aliens Return ???? ?? 4 X J ALIENRET +Fire Birds ???? ?? 4 X J FIREBIRD +Meteor Defence ???? ?? 4 X J METDEF + +*********************************** +* Konami * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Pooyan 001 ER 4 X POOYAN +Strategy X 010 ER 4 X STRATGYX +Marine Wars 011 ER 4 X MARINWAR + +Note: The part numbers are labelled in binary notation. :-) + +*********************************** +* M-Network * +*********************************** + +[If SC is marked, chip uses an extra RAM, #TMM2009P-25. It's +really a 6116 in disguise] + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Star Strike MT4313 R 4 X J STARSTRK +Adventures of TRON MT4317 U 4 X J ADVNTRON +MOTU: Power of He-Man MT4319 R 16 E7 X J HE_MAN +Burgertime MT4518 R 12 X E7 X J BURGTIME +Kool-Aid man MT4648 U 4 X J KOOLAIDE +SC Football MT5658 C 4 X J SUPRFOOT +Space Attack MT5659 C 4 X J SPACATTK +Armour Ambush MT5661 C 4 X J ARMAMBSH +TRON Deadly Discs MT5662 U 4 X J TRONDEAD +Lock 'n Chase MT5663 C 4 X J LOCKCHSE +Frogs and Flies MT5664 U 4 X J FROGFLYS +SC Baseball MT5665 C 4 X J SUPRBASE +Astroblast MT5666 U 4 X J ASTRBLST +Dark Cavern MT5667 U 4 X J DARKCVRN +International Soccer MT5687 U 4 X J INTRSCCR +Air Raiders MT5861 R 4 X J AIRAIDRS +Bump 'n Jump MT7045 R 8 E7 X J BNJ + + +'SC' in this case refers to extra RAM in the cart. + +Notes: + +All three E7 carts have been read in as 16K. This makes it much easier +to write emulators and build hardware, as there's just one standard +size. The RAM can still be included in any cart; however it has no +effect in Bump 'n Jump or He-Man. Fortunately, it doesn't hinder operation +either, so I chose to just include the extra RAM under the E7 label. + + + +*********************************** +* Milton Bradley * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Survival Run 4362 R 4 X J SURVLRUN +Spitfire Attack 4363 U 4 X J SPITFIRE + + +*********************************** +* Mystique (hehe!) * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Custer's Revenge 1001 ER 4 X J CUSTEREV +Bachelor Party 1002 ER 4 X P BACHELOR +Beat 'em and Eat 'em 1003 ER 4 X P BEATEM +Bachelorette Party 1004 ER 4 X P BACHLRTT +Gigolo 1009 ER 4 X J GIGOLO +Jungle Fever 1011 ER 4 X J JNGLFEVR +Burning Desire ???? ER 4 X J BURNDESR +Cathouse Blues ???? ER 4 X J CATHOUSE +Knight on the Town ???? ER 4 X J KNIGHTWN +Lady in Wading ???? ER ?? +Philly Flasher ???? ER 4 X P PHILLY + + +*********************************** +* Mythicon * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Sourcerer MA-1001 U 4 X J SORCERER +Fire Fly MA-1002 U 4 X J FIREFLY +Star Fox MA-1003 U 4 X J STARFOX + +*********************************** +* Panda Inc. * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Space Canyon 100 R 4 X J SPACANYN +Tank Brigade 101 R 4 X TANKBRIG +Scuba Diver 104 R 4 X J SCUDIV_P +Stuntman 105 R 4 X J STNTMAN +Dice Puzzle 106 R 4 X J DICEPUZL +Sea Hawk 108 R 4 X SEAHWK_P +Exocet 109 R 4 X EXOCET +Harbour Escape 110 R 4 X J HARBRESC + + +*********************************** +* Parker Bros. * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +SW: Jedi Arena PB5000 R 4 X P JEDIAREN +SW: Empire Strikes Back PB5050 C 4 X J STAREMPR +SW: Death Star Battle PB5060 R 8 E0 X J DETHSTAR +SW: Ewok Adventure PB5065 PR ?? ---Proto Exists--- +Gyruss PB5080 R 8 E0 X J GYRUSS +James Bond 007 PB5110 R 8 E0 X J JAMEBOND +Frogger PB5300 C 4 X J FROGGER +Amidar PB5310 U 4 X J AMIDAR +Super Cobra PB5320 U 8 E0 X J SPRCOBRA +Reactor PB5330 U 4 X J REACTOR +Tutankham PB5340 U 8 E0 X J TUTANK +Sky Skipper PB5350 R 4 X SKYSKIPR +Q*Bert PB5360 C 4 X J QBERT_PB +Popeye PB5370 C 8 E0 X J POPEYE +SW: Arcade Game PB5540 ER 8 E0 X J SWARCADE +Q*Bert's Qubes PB5550 ER 8 E0 X J QBRTQUBE +Frogger ][: Threeedeep PB5590 ER 8 E0 X J FROGGER2 +Circus Charlie PB5750 ?? ---No Known Copies Exist--- +Montezuma's Revenge PB5760 ER 8 E0 X J MONTZREV +Mr. Do's Castle PB5820 ER 8 E0 X J DOCASTLE +Spider-Man PB5900 R 4 X J SPIDRMAN +Strawberry Shortcake PB5910 U 4 X J STRWBERY +GI Joe: Cobra Strike PB5920 U 4 X J GIJOE +Action Force PB5920 ER 4 X J ACTIONMN +Lord of the Rings PB???? UR ?? ---No Known Copies Exist--- +McDonald's PB???? UR ?? ---No Known Copies Exist--- + + +*********************************** +* Playaround * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +See Mystique for #201-205 + +/General retreat 206 ER ?? +\Westward Ho 206 ER ?? + + +*********************************** +* Puzzy * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Seesaw ???? ER 4 X J SEESW_TW +Football ???? ER 4 X FB_PIR8 +Earth Attack ???? ER ?? +Puzzled World ???? ER 4 X PUZZL_TW +Chess ???? ER ?? +Boom Bang ???? ER 4 X J BOOMBANG +Pitfall ???? ER ?? +Spider ???? ER ?? +Tennis ???? ER ?? +Pyramid War ???? ER 4 X PYRMDWAR +Bobby is Going Home ???? ER 4 X J BOBBY +Mr. Postman ???? ER 4 X MRPOSTMN +Space Tunnel ???? ER 4 X SPACTUNL +Fancy Car ???? ER ?? +My Way ???? ER ?? +S.O.S. ???? ER ?? +Frogger ???? ER ?? +Fishing ???? ER ?? +Cross Force ???? ER 4 X CROSFRCE +Farmer Dan ???? ER ?? +Dancing Plate ???? ER 4 X DANCPLAT +Volley Ball ???? ER ?? +Little Bear ???? ER ?? + + +*********************************** +* Rainbow Vision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Pac-Kong 55-003 R 4 X PACKONG +Netmaker 55-006 R ?? +Mafia 55-010 R ?? +Hey! Stop! 55-012 R 4 X J HEY_STOP +Bi! Bi! 55-013 R 4 X BIBI +Catch Time 55-015 R ?? +Boom Bang 55-016 R 4 X BOOMBANG +Mariana 55-017 R ?? +Curtiss 55-019 R ?? +Tuby Bird 55-020 R 4 X TUBYBIRD +Tomboy 55-??? R 4 X J TOMBOY + + +*********************************** +* Salu * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Acid Drop ???? ER 16 F6 X J ACIDDROP +Ghostbusters ][ ???? ER 16 F6 X J GHOSTBS2 +Pick 'N' Pile ???? ER 16 F6 X J PICKPILE + + +*********************************** +* Sancho * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Exocet TEC001 ER ?? +Sea Hawk TEC002 ER ?? +Skin Diver TEC003 ER 4 X SKINDIVR +Nightmare TEC004 ER 4 X NGHTMARE +Dice Puzzle TEC005 ER 4 X DICEPUZL +Forest TEC006 ER 4 X FOREST + + +*********************************** +* Sega * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Tac-Scan 001-01 U 4 X TACSCAN +Sub Scan 002-01 U 4 X SUBSCAN +Thunderground 003-01 R 4 X THUNDGRD +Star Trek: SOS 004-01 R 8 F8 X J STARTREK +Buck Rodgers 005-01 U 8 F8 X J BUCKROG +Congo Bongo 006-01 R 8 F8 X J CONGBONG +Up 'n Down 009-01 ER 8 F8 X J UPNDOWN +Tapper 010-01 ER 8 F8 X J TAPPER +Spy Hunter 011-02 ER 8 F8 X J SPYHUNTR + + +*********************************** +* Selchow & Righter * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Glib ???? UR 4 X GLIB + + +*********************************** +* Simage * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Eli's Ladder ???? UR 4 X ELILADDR + + +*********************************** +* Sparrow * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Music Machine GCG 1001T UR 4 X MUSCMACH + + +*********************************** +* Spectravision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Gangster Alley SA-201 R 4 X J GANGALLY +Planet Patrol SA-202 R 4 X J PLANTPAT +Cross Force SA-203 R 4 X CROSFRCE +Tape Worm SA-204 R 4 X J TAPEWORM +China Syndrome SA-205 R 4 X CHINASYN +The Challange of... Nexar SA-206 R 4 X NEXAR +Master Builder SA-210 R 4 X J MASTBULD +Galactic Tactic SA-211 R ?? +Mangia SA-212 R 4 X J MANGIA +Gas Hog SA-217 ER 4 X J GASHOG +Bumper Bash SA-218 ER 4 X J BUMPER +Save the Whales SA-??? UR ?? +Cave-In SA-??? UR ?? +Chase the Chuckwagon SA-??? UR 4 X J CHUCKWGN + + +*********************************** +* StarPath * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Supercharger BIOS ROM 330030 -- 2 X STARPATH +Phasor Patrol RA-4000 R 6 AR X J PHASOR +Communist Mutants RA-4001 R 6 AR X COMMIE +Suicide Mission RA-4002 R 6 AR X J SUICIDE +Killer Satellites RA-4103 R 6 AR X J KILLRSAT +Rabbit Transit RA-4104 ER 6 AR X J RABBIT +Frogger RA-4105 ER 6 AR X J FROGGER +Escape from the Mindmaster RA-4200 ER 6*4 AR XXXX J MINDMAS1-4 +Sword of Saros RA-4201 ER 6 AR X SWOSAROS +Excalibur RA-4201 PR ?? +Fireball RA-4300 R 6 AR X FIREBALL +Party Mix RA-4302 ER 6*3 AR XXX PRTYMIX1-3 +Dragonstomper RA-4400 ER 6*3 AR XXX J DRAGON1-3 +Survival Island RA-4401 ER 6*3 AR XXX SURVIVAL1-3 +Sweat! RA-4??? PR 6*2 AR XX SWEAT1,2 +Comm. Mutants Demo ???? DM 6 AR X COMMDEMO +Dragonstomper Demo ???? DM 6 AR X DRAGDEMO +Fireball Demo ???? DM 6 AR X FIREDEMO +Frogger Demo ???? DM 6 AR X FROGDEMO +Killer Satellites Demo ???? DM 6 AR X KILLDEMO +Esc. from Mindmaster Demo ???? DM 6 AR X MINDDEMO +Party Mix Demo ???? DM 6 AR X PRTYDEMO +Rabbit Transit Demo ???? DM 6 AR X RABTDEMO +Suicide Mission Demo ???? DM 6 AR X SUICDEMO + + +*********************************** +* Sunrise * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Quest for Quinta Roo 1603 ER 8 F8 X J QUINTROO +Snowplow ???? ER ?? +Glacier Patrol ???? ER 4 X J GLACIER +Noah and the Ark ???? ?? + + +*********************************** +* Suntek * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Time Race 1 ER ?? +Galactic 2 ER ?? +Pac-Kong 3 ER 4 X PACKONG +Pyramid War 4 ER 4 X PYRMDWAR +Netmaker 6 ER ?? +Bermuda 9 ER 4 X BERMUDA + + +*********************************** +* Technovision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Adventures of GX-12 ???? ?? +Flipper ???? ?? +Formula I ???? ?? +Jungle Jim ???? ?? +Laser Raid ???? ?? +Moonbase ???? ?? +Motor Mouth ???? ?? +Mouse Highway ???? 4 X J CATMOUSE +Nuts ???? 4 X NUTS +Pharoah's Curse ???? 4 X PHARHCRS +Save Our Ship ???? 4 X J SAVESHIP +Silly Safari ???? ?? +Shoot-Out ???? ?? +Stone Age ???? ?? +Tachion Beam ???? ?? + + +*********************************** +* Tele-Games * +*********************************** + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + + +Bogey Blaster 5861 A030 R 4 X J BOGYBLST +Night Stalker ???? R 4 X J NIGHTSTK +Universal Chaos ???? 4 X J UNIVCHOS +Bump 'n Jump ???? 8 F8 X X1 J BUMPHUMP + + +X1: Intresting... uses F8 instead of E7 bankswitching! + + +*********************************** +* Telesys * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Coco Nuts 1001 R 4 X J COCONUTS +Cosmic Creeps 1002 R 4 X J COSMCREP +Fast Food 1003 R 4 X J FASTFOOD +Ram- It 1004 ER 4 X J RAMIT +Star Gunner 1005 ER 4 X STARGN +Demolition Herby 1006 ER 4 X J DEMOHRBY +Bouncing Baby Monkeys ???? UR ?? +The Impossible Game ???? UR ?? + + +*********************************** +* Tigervision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +King Kong 7-001 R 4 X J KINGKONG +Jawbreaker 7-002 R 4 X J JAWBREAK +Threshold 7-003 R 4 X J THRSHOLD +River Patrol 7-004 UR 8 3F X J RIVERP +Marauder 7-005 ER 4 X J MARAUDER +Springer 7-006 UR 8 3F X J SPRINGER +Polaris 7-007 ER 8 3F X J POLARIS +Miner 2049'er 7-008 ER 8 3F X J MNR2049R +Intuition 7-009 NR ?? +Scraper Caper 7-010 NR ?? +Miner 2049'er Volume ][ 7-011 ER 8 3F X J MINRVOL2 +Espial 7-012 ER 8 3F X J ESPIAL + +*********************************** +* TNT Games * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +BMX Airmaster 26192 ER 16 F6 X J BMX_TNT + +*********************************** +* 20th Century Fox * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Worm War I 11001 U 4 X J WORMWAR1 +Beany Bopper 11002 R 4 X J BEANYBOP +Fast Eddie 11003 U 4 X FASTEDIE +Deadly Duck 11004 R 4 X J DEADDUCK +Mega Force 11005 R 4 X MEGAFRCE +Alien 11006 R 4 X J ALIEN +Turmoil 11007 U 4 X J TURMOIL +Fantastic Voyage 11008 R 4 X FANTCVOY +Crypts of Chaos 11009 R 4 X CRPTCHOS +M*A*S*H 11011 U 4 X J MASH +Bank Heist 11012 R 4 X BANKHEST +Porky's 11013 R 8 F8 X J PORKYS +Flash Gordon 11015 R 4 X FLASHGRD +Revenge of the BS Tomatoes 11016 R 4 X J REVNGTOM +The Earth Dies Screaming 11020 R 4 X J EARTHDIE +Spacemaster X-7 11022 ER 4 X SPACMAST +Meltdown 11029 ER ?? Need! +Crash Dive 11031 ER 4 X CRSHDIVE +Alligator People ????? PR 4 X J ALIGPEPL + +*********************************** +* U.S. Games * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Space Jockey VC 1001 C 2 X J SPACJOCK +Sneak 'n Peek VC 1002 R 4 X SNEKPEEK +Word Zapper VC 1003 U 4 X J WORDZAPR +Commando Raid VC 1004 R 4 X COMANDRD +"Name This Game" VC 1007 R 4 X X1 J NAMEGAME +Octopus VC 1007 ER 4 X X1 J NAMEGAME +Towering Inferno VC 1009 U 4 X J TOWERINF +M.A.D VC 1012 R 4 X J M_A_D +Gopher VC 2001 R 4 X J GOPHER +Squeeze Box VC 2002 ER 4 X J SQUEEZBX +Eggomania VC 2003 R 4 X P EGGOMANA +Picnic VC 2004 ER 4 X P PICNIC +Piece 'o Cake VC 2005 ER 4 X P PIECECKE +Raft Rider VC 2006 ER 4 X J RAFTRIDR +Entombed VC 2007 ER 4 X J ENTOMBED + + +X1: These two carts are the same except the name. + + +*********************************** +* Universal Gamex * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +X-Man GX-001 UR 4 X J XMAN + + +*********************************** +* Venturevision * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Rescue Terra I VV2001 ER 4 X RESCTER1 +Inner Space ???? + + +*********************************** +* Video Gems * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Missile Control ???? ER 4 X MISLCONT +Mission Survive ???? ER +Steeple Chase ???? ER +Surfer's Paradise ???? ER 4 X SURFPRDS +Treasure Below ???? ER + + +*********************************** +* Wizard Video * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Texas Chainsaw Massacre 008 ER 4 X J TXSCHAIN +Halloween 007 ER 4 X J HALOWEEN + + +*********************************** +* Xonox * +*********************************** + +Note: No repeats are listed (i.e. when the same game is on two double-enders) + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Spike's Peak 99001 ER 8 F8 X J SPIKE_PK +Ghost Manor 99002 ER 8 F8 X J GHOSTMAN +Chuck Norris Super Kicks 99003 ER 8 F8 X J CHUCKICK +Artillery Duel 99004 ER 8 F8 X J ART_DUEL +Robin Hood 99005 ER 8 F8 X J ROBH_P +Sir Lancelot 99006 ER 8 F8 X J SIRL_N +Tomarc the Barbarrian 99007 R ?? +Motocross Racer 99008 R ?? + + +*********************************** +* Zellers * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Time Warp ???? ER 4 X TIMEWARP + +*********************************** +* Zimag * +*********************************** + + +See Emag + + +*********************************** +* Un-marked / Other * +*********************************** + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Dragon Defender TP-605 ER 4 X DRGNDFND +Hole Hunter TP-606 ER 4 X HOLEHUNT +Farmyard Fun TP-617 ER 4 X FARMYARD +16 Games in 1 ???? ER ?? +Brick Kick ???? ER 4 X J BRICKICK +Challange ???? ER 4 X CHALENGE +Clown Down Town ???? ER 4 X CLWNDOWN +Criminal Pursuit ???? ER 4 X CRIMLPUR +Dragon Treasure ???? ER ?? +Frontline ???? ER ?? +Inca Gold ???? ER ?? +laser Volley ???? ER 4 X LASRVOLY +Ski Run ???? ER 4 X SKI_RUN +Lie Low ???? ER ?? +Lost and Found ???? ER ?? +Missile Attack ???? ER ?? +Oops ???? ER ?? +Planet Protector ???? ER ?? +Ski Hunt ???? ER 4 X SKIHUNT +Super-Ferrari ???? ER 4 X SUPFERRI +Tom Boy ???? ER 4 X J TOMBOY +UFO Patrol ???? ER 4 X UFOPATRL +Wolf Fighting ???? ER ?? +Pink Panther / Probe 2000 ???? PR ?? ---One Proto Exists--- + + +********************************************** +* Odd proto's and other intresting things * +********************************************** + + Name / Description SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Air Raid (Not Air Raiders! By Men-A-Vision) 4 X AIR_RAID +Bira Bira (modified Burning Desire) 4 X J BIRABIRA +Challange of Nexar (Changed GFX) 4 X NEXAR-CH +Circus Atari (Uses joysticks) 4 X J CIRCUS-J +Colourbar Generator 4 X -- COLORBAR +Condor (Similar to Condor Attack) 4 X CONDOR +Donkey Kong (Changed GFX) 4 X J DK-CH +Duck Shot (Positively Weird!) 4 X DUCKSHOT +Elk Attack (Mark R. Hahn; 1987) 8 F8 X ELK +Fishing Derby (changed GFX; from 32-in-1) 2 X J FISHN-CH +Freeway (Changed GFX; from the 32-in-1) 2 X J FREWY-CH +Galaga (River Raid ripoff) 4 X J GALAGA +Joust Hack (#1) 8 F8 X J J_HCK1 +Joust Hack (#2) 8 F8 X J J_HCK2 +Joust; Super (modified) 8 F8 X J SJOUST +Marflegr (PAL version of Sea Hawk) 4 X MARFLEGR +MASH w/ subs (Modified GFX) 4 X J MASH_SUB +Missile Command (Changed GFX) 4 X J MC-CH +Pac-Kong (Changed GFX) 4 X J PK-CH +Snail Against Squirrel (Changed GFX) 4 X J SVS-CH +Space Raid (Vaguely Threshold-like) 4 X SPACRAID +Test (Neat little TIA test) 4 X -- TEST +Traffic (Dunno; intresting) 4 X J TRAFFIC +World End (I Believe this is World? Trap?) 4 X J WORLDEND +Galactic (Starsoft title; not a pirate) 4 X J GALACTIC +Air-sea Battle (changed GFX) 2 X J AIRSEA2 +Space Invaders (changed GFX) 4 X J SPACE2 +Magazine Demo 4 X -- MAGDEMO +4-game ROM... uses F6 bankswitch 16 F6 X J GORF_RIP + +*********************************** +* Overflow * +*********************************** + +Used to keep Bankzilla's database program happy (mainly PAL versions and +game revisions. Prevents cluttering up the main listing) + + + Name Part # RA SZ SC BS IM SP CT Filename +--------------------------------------------------------------------------- + +Pal version of Pele's Soc. CX2616 ?? 4 X J CX2616PL +Taiwanese Enduro ???? ?? 4 X J ENDRO_TW +Taiwanese Pele's ???? ?? 4 X J PELE_TW +CCE version of Pitfall ???? ?? 4 X J PITF_CCE +Taiwanese River Raid ???? ?? 4 X J RIVER_TW +Diagnostic Program ???? ?? 4 X -- SALTDIAG +Superman; revision 2 ???? ?? 4 X J SUPRMAN2 +Dunno ???? ?? 2 X -- XX +Pal version of KLAX ???? ?? 16 X F6 X J KLAX +Okie Dokie; Limited ???? ?? 4 X J OKIEDLIM +Robot Tank; F8-fixed ---- -- 8 F8 X J ROBO_FIX +Surf's Up; original ---- -- 8 F8 X J SURFSUP +Dumbo's F. Circ.; PAL ???? PR 8 F8 X J DUMBO_P +Sir Lancelot; NTSC ???? ?? 8 F8 X J SIRL_P +Smurf Rescue; PAL ???? ?? 8 F8 X J SMURF_PL +'Chess'; Taiwanese ???? ?? 2 X J CHESS_TW +Outerspace; Sears ???? ?? 2 X J OUTERSPC +Okie Dokie Proto ---- -- 2 X J LT +Taiwanese Bowling ???? ?? 2 X J BOWLG_TW + +*Not* a ROM image! **** ** ** ** ** X -- EMPTY + +####Data End#### + +*********************************** +* World of Dead ROM Images * +*********************************** + +Yeppers, some ROM images are better off dead. Either they're exact copies, +damaged, or just plain DOA. + +XTACK Same as Z-Tack by Bomb. +POLEPOS Pole Position... bad read. 5 bytes bad. +ASTROSMS Same as M-net's Astroblast + +*********************************** +* Proto's Read * +*********************************** + + +Thanks to many people, I have been able to read some prototype carts in! +However, the data contained on the carts is identical to the production +ones. + +Bermuda Triangle, #371 +Pac-Man, in an Imagic case!! The board has been wave-soldered; said board + is an actual Imagic board, and the chip is a ROM. diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/stella.pdf b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/stella.pdf new file mode 100644 index 00000000..5a76bc1b Binary files /dev/null and b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/stella.pdf differ diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/stella.txt b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/stella.txt new file mode 100644 index 00000000..e9eb92c9 --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/stella.txt @@ -0,0 +1,1938 @@ +TABLE OF CONTENTS 4 + + + + + + + STELLA + + PROGRAMMER'S + + GUIDE + + + + + + + + + + by + Steve Wright + 12/03/79 + + + + + + + + + + + + + + + + (Reconstructed by Charles Sinnett 6/11/93 Internet: + cas@mentor.cc.purdue.edu) + +Table of Contents + + +TELEVISION PROTOCOL 1 + Diagram 1 - Atari TV Frame 2 +The TIA (as seen by the programmer) 3 + 1.0 General Description 3 + 2.0 The Registers 3 + 3.0 Synchronization 4 + 3.1 Horizontal Timing 4 + 3.2 Microprocessor Synchronization 4 + 3.3 Vertical timing 4 + 4.0 Color and Luminosity 5 + 5.0 Playfield 5 + 6.0 The Moveable Objects Graphics 6 + 6.1 Missile Graphics (M0, M1) 6 + 6.2 Ball Graphics (BL) 6 + 6.3 Player Graphics (P0, P1) 7 + 7.0 Horizontal Positioning 8 + 8.0 Horizontal Motion 8 + 9.0 Object Priorities 9 + 10.0 Collisions 10 + 11.0 Sound 10 + 11.1 Tone 10 + 11.2 Frequency 10 + 11.3 Volume 10 + 12.0 Input Ports 11 + 12.1 Dumped Input Ports (INPT0 thru INPT3) 11 + 12.2 Latched Input Ports (INPT4, INPT5) 11 + +THE PIA (6532) 12 + 1.0 General 12 + 2.0 Interval timer 12 + 2.1 Setting the timer 12 + 2.2 Reading the timer 12 + 2.3 When the timer reaches zero 12 + 3.0 RAM 13 + 4.0 The I/O ports 13 + 4.1 Port B - Console Switches (read only) 13 + 5.0 Port A - Hand Controllers 13 + 5.1 Setting for input or output 13 + 5.2 Inputting and Outputting 14 + 5.3 Joystick Controllers 14 + 5.4 Paddle (pot) controllers 14 + 5.5 Keyboard controllers 15 + 6.0 Address summary table 15 + + +PAL/SECAM CONVERSIONS 16 + PAL 16 + SECAM 16 + + +TIA 1A - TELEVISION INTERFACE ADAPTOR (MODEL 1A) 17 + GENERAL DESCRIPTION 17 + DETAILED DESCRIPTION 18 + 1. Data and addressing 18 + 2. Synchronization 18 + A. Horizontal Timing 18 + B. Vertical Timing 18 + C. Composite Sync 18 + D. Microprocessor Synchronization 19 + 3. Playfield graphics Register 19 + A. Description 19 + B. Normal Serial Output 19 + C. Reflected Serial Output 19 + D. Timing Constraints 20 + 4. Horizontal Position Counters 20 + A. Description 20 + B. Ball position Counter 20 + C. Player Position Counters 20 + D. Missile Position Counters 21 + 5. Horizontal Motion Registers 21 + A. General Description 21 + B. Timing constraints 21 + + 6. Moving Object Graphics Registers 22 + A. General Description 22 + B. Missile Graphics 22 + C. Player Graphics 22 + D. Vertical Delay 23 + E. Ball Graphics 24 + 7. Collision Detection Latches 24 + A. Definitions 24 + B. Reading Collision 24 + C. Reset 24 + 8. Input ports 25 + A. General Description 25 + B. Dumped Input Ports (I0 through I3) 25 + C. Latched Input ports (I4, I5) 25 + 8.5 Priority Encoder 26 + A. Purpose 26 + B. Priority Assignment 26 + C. Priority Control 26 + 9 Color Luminance Registers 27 + A. Description 27 + B. Multiplexing 27 + 10. Color Phase Shifter 27 + 11. Audio Circuits 27 + A. Frequency Select 27 + B. Noise-Tone Generator 28 + C. Volume Select 28 + + Figure 1. Vertical Delay 29 + Figure 2. Synchronization 30 + Figure 3. Color-Luminance 30 + Figure 4. Typical Horizontal Motion Circuit 31 + Figure 5. Playfield Graphics 32 + Figure 6. Collision Detection 33 + Figure 7. Audio Circuit 34 + Figure 8. Input Ports 35 + Figure 9. Game System 36 + +Write Address Detailed Functions 37 + WSYNC (wait for sync) 37 + RSYNC (reset sync) 37 + VSYNC 37 + VBLANK 37 + PJ0 (PF1, PF2) 38 + PLAYFIELD REGISTERS SERIAL OUTPUT 38 + CTRLPF 38 + NUSIZ0 (NUSIZ1) 39 + RESP0 (RESP1, RESM0, RESM1, RESBL) 39 + RESMP0 (RESMP1) 39 + HMOVE 40 + HMCLR 40 + HMP0 (HMP1, HMM0, HMM1, HMBL) 40 + ENAM0 (ENAM1, ENABL) 41 + GRP0 (GRP1) 41 + REFP0 (REFP1) 41 + VDELP0 (VDELP1, VDELBL) 41 + CXCLR 41 + COLUP0 (COLUP1, COLUPF, COLUBK) 42 + AUDF0 (AUDF1) 42 + AUDC0 (AUDC1) 43 + AUDV0 (AUDV1) 43 + WRITE ADDRESS SUMMARY 44 + READ ADDRESS SUMMARY 45 + + TIA O0..02 AND LUM TIMING 46 + TIA WRITE TIMING CHARACTERISTICS 47 + TIA READ TIMING CHARACTERISTICS 48 + TIA COMP-SYN AND READY TIMING 49 + RSYNC, RES0O, H01, H02, SHB, 02, 0O 50 + TIA RSYNC AND BLANK AND READY TIMING 51 + + + +TELEVISION PROTOCOL + (The TV picture according to Atari) + + For the purposes of Stella programming, a single television + “frame” consists of 262 horizontal lines, and each line is + divided by 228 clock counts (3.58MHz). The actual TV + picture is drawn line by line from the top down 60 times a + second, and actaully consists of only a portion of the + entire “frame” (see diag. #1). A typical frame will + consists of 3 vertical sync (VSYNC) lines*, 37 vertical + blank (VBLANK) lines, 192 TV picture lines, and 30 overscan + lines. Atari’s research has shown that this pattern will + work on all types of TV sets. Each scan lines starts with + 68 clock counts of horizontal blank (not seen on the TV + screen) followed by 160 clock counts to fully scan one line + of TV picture. When the electron beam reaches the end of a + scan line, it returns to the left side of the screen, waits + for the 68 horizontal blank clock counts, and proceeds to + draw the next line below. + + All horizontal timing is taken care of by hardware, but the + microprocessor must “manually” control vertical timing to + signal the start of the next frame. When the last line of + the previous frame is detected, the microprocessor must + generate 3 lines of VSYNC, 37 lines of VBLANK, 192 lines of + actual TV picture, and 30 lines of overscan. Fortunately, + both VSYNC and VBLANK can simply be turned on and off at + the appropriate times, freeing the microprocessor for other + activities during their execution. + + * (to signal the TV set to start a new frame) + + The actual TV picture is drawn one line at a time by having + the microprocessor enter the data for that line into the + Television Interface Adaptor (TIA) chip, which then + converts the data into video signals. The TIA can only + have data in it that pertains to the line being currently + drawn, so the microprocessor must be “one step ahead” of + the electron beam on each line. Since one microprocessor + machine cycle occurs every 3 clock counts, the programmer + has only 76 machine cycles per line (228/3 = 76) to + construct the actual picture (actually less because the + microprocessor must be ahead of the raster). To allow more + time for the software, it is customary (but not required) + to update the TIA every two scan lines. The portion of the + program that constructs this TV picture is referred to as + the “Kernel”, as it is the essence or kernel of the game. + + In general, the remaining 70 scan lines (3 for VSYNC, 37 + for VBLANK, and 30 for overscan) will provides 5,320 + machine cycles (70 lines x 76 machine cycles) for + housekeeping and game logic. Such activities as + calculating the new position of a player, updating the + score, and checking for new inputs are typically done + during this time. +Diagram 1 - Atari TV Frame + + + + +The TIA (as seen by the programmer) +1.0 General Description + + The TIA is a custom IC designed to create the TV picture + and sound from the instructions sent to it by the + microprocessor. It converts the 8 bit parallel data from + the microprocessor into signals that are sent to video + modulation circuits which combine and shape those signals + to be compatible with ordinary TV reception. A “playfield” + and 5 moveable objects can be created and manipulated by + software. + + A playfield consisting of walls, clouds, barriers, and + other seldom moved objects can be created over a colored + background. The 5 moveable objects can be positioned + anywhere, and consists of 2 players, 2 missiles, and a + ball. The playfield, players, missiles, and ball are + created and manipulated by a series of registers in the TIA + that the microprocessor can address and write into. Each + type of object has certain defined capabilities. For + example, a player can be moved with one instruction, but + the playfield must be completely re-drawn in order to make + it “move”. + + Color and luminosity (brightness) can be assigned to the + background, playfield, and 5 moveable objects. Sound can + also be generated and controlled for volume, pitch, and + type of sound. Collisions between the various objects on + the TV screen are detected by the TIA and can be read by + the microprocessor . Input ports which can be read by the + microprocessor give the status of some of the various hand + held controllers. + +2.0 The Registers + + All instructions to the TIA are achieved by addressing and + writing to various registers in the chip. A key point to + remember is data written in a register is latched an + retained until altered by another write operation into that + register. For example, if the color register for a player + is set for red, that player will be red every time it is + drawn until that color register is changed. All of the + registers are addressed by the microprocessor as part of + the overall RAM/ROM memory space. + + All registers have fixed address locations and pre-assigned + address names for handy reference. Many registers do not + use all 8 data bits, and some registers are used to + “strobe” or trigger events. A “strobe” register executes + its function the instant it is written to (the data written + is ignored). The only registers the microprocessor can + read are the collision registers and input port registers. + These registers are conveniently arranged so that the data + bits of interest always appear as data bits 6 or 7 for easy + access. + +3.0 Synchronization + + 3.1 Horizontal Timing + When the electron beam scans across the TV screen and + reaches the right edge, it must be turned off and moved + back to the left edge of the screen to begin the next scan + line. The TIA takes care of this automatically, independent + of the microprocessor. A 3.58 MHz oscillator generates + clock pulses called “color clocks” which go into a pulse + counter in the TIA. This counter allows 160 color clocks + for the beam to reach the right edge, then generates a + horizontal sync signal (HSYNC) to return the beam to the + left edge. It also generates the signal to turn the beam + off (horizontal blanking) during its return time of 68 + color clocks. Total round trip for the electron beam is + 160 + 68 = 228 color clocks. Again, all the horizontal + timing is taken care of by the TIA without assistance from + the microprocessor. + + 3.2 Microprocessor Synchronization + The microprocessor’s clock is the 3.58 MHz oscillator + divided by 3, so one machine cycle is 3 color clocks. + Therefore, one complete scan line of 228 color clocks + allows only 76 machine cycles (228/3 = 76) per scan line. + The microprocessor must be synchronized with the TIA on a + line-by-line basis, but program loops and branches take + unpredictable lengths of time. To solve this software + sync. problem, the programmer can use the WSYNC (Wait for + SYNC) strobe register. Simply writing to the WSYNC causes + the microprocessor to halt until the electron beam reaches + the right edge of the screen, then the microprocessor + resumes operation at the beginning of the 68 color clocks + for horizontal blanking. Since the TIA latches all + instructions until altered by another write operation, it + could be updated every 2 or 3 lines. The advantage is the + programmer gains more time to execute software, but at a + price paid with lower vertical resolution in the graphics. + + NOTE: WSYNC and all the following addresses’ bit + structures are itemized in the TIA hardware manual. The + purpose of this document is to make them understandable. + + 3.3 Vertical timing + When the electron beam has scanned 262 lines, the TV set + must be signaled to blank the beam and position it at the + top of the screen to start a new frame. This signal is + called vertical sync, and the TIA must transmit this signal + for at least 3 scan lines. This is accomplished by writing + a “1” in D1 of VSYNC to turn it on, count at least 2 scan + lines, then write a “0” to D1 of VSYNC to turn it off. + + To physically turn the beam off during its repositioning + time, the TV set needs 37 scan lines of vertical blanks + signal from the TIA. This is accomplished by writing a “1” + in D1 of VBLANK to turn it on, count 37 lines, then write a + “0” to D1 of VBLANK to turn it off. The microprocessor is + of course free to execute other software during the + vertical timing commands, VSYNC and VBLANK. + +4.0 Color and Luminosity + + Color and luminosity can be assigned to the background + (BK), playfield (PF), ball (BL), player 0 (P0), player + 1(P1), missile 0 (M0), and missile 1 (M1). There are only + four color-lum registers for these 7 objects, so the + objects are paired to share the same register according to + the following list: + + color-lum register Objects colored + COLUMP0 P0, M0 (player 0, missile 0) + COLUMP1 P1, M1 (player 1, missile 1) + COLUMPF PF, BL (playfield, ball) + COLUMBK BK (background) + + For example, if the COLUMP0 register is set for light red, + both P0 and M0 will be light red when drawn. + + A color-lum register is set for both color and luminosity + by writing a single 7 bit instruction to that register. + Four of the bits select one of the 16 available colors, and + the other 3 bits select one of 8 levels of luminosity + (brightness). The specific codes required to create + specific color and lum are listed in the Detailed Address + List of the TIA hardware manual. As with all registers + (except the “strobe” registers), the data written to them + is latched until altered by another write operation. + +5.0 Playfield + + The PF register is used to create a playfield of walls, + clouds, barriers, etc., that are seldom moved. This low + resolution register is written into to draw the left half + of the TV screen only. The right half of the screen is + drawn by software selection of whether a duplication or a + reflection of the right half. + + The PF register is 20 bits wide, so the 20 bits are written + into 3 addresses: PF0, PF1, and PF2. PF0 is only 4 bits + wide and constructs the first 4 “bits” of the playfield, + starting at the left edge of the TV screen. PF1 constructs + the next 8 “bits”, and PF2 the last 8 bits” which end at + the center of the screen. The PF register is scanned from + left to right and where a “1” is found the PF color is + drawn, and where a “0” is found, the BK color is drawn. To + clear the playfield, obviously zeros must be written into + PF0, PF1, and PF2. + + To make the right half of the playfield into a duplication + or copy of the left half, a “0” is written to D0 of the + CTLPF (control playfield) register. Writing a “1” will + cause the reflection to be displayed. + +6.0 The Moveable Objects Graphics + + All 5 moveable objects (P0, M0, P1, M1, BL) can be assigned + a horizontal location on the screen and moved left or right + relative to their location. Vertical positions, however, + are treated in an entirely different manner. In principle, + these objects appear at whatever scan lines their graphics + registers are enabled. For example, let us assume the ball + is to be positioned vertically in the center of the screen. + The screen has 192 scan lines and we want the ball to be 2 + scan lines “thick”. The ball graphics would be disabled + until scan line 96, enabled for 2 scan lines, then disabled + for the rest of the frame. Each type of object (players, + missiles, and ball) has its’ own characteristics and + limitations. + + 6.1 Missile Graphics (M0, M1) + The two missile graphics registers will draw a missile on + any scan line by writing a “1” to the one bit enable + missile registers (ENAM0, ENAM1). Writing a “0” to these + registers will disable the graphics. The missiles’ left + edge is positioned by a horizontal position register, but + the right edge is a function of how wide the missile is + make. Width of a missile is controlled by writing into + bits D4 and D5 of the number-size registers (NUSIZ0, + NUSIZ1). This has the effect of “stretching” the missile + out over 1,2,4, or 8 color clock counts (a full scan line + is 160 color clocks). + + 6.2 Ball Graphics (BL) + The ball graphics register works just like the missile + registers. Writing a “1” to the enable ball register + (ENABL) enables the ball graphics until the register is + disabled. The ball can also be “stretched” to widths of 1, + 2, 4, or 8 color clock counts by writing to bits D4 and D5 + of the CTRLPF register. + + The ball can also be vertically delayed one can line. For + example, if the ball graphics were enabled on scan line 95, + it could be delayed to not display on the screen until scan + line 96 by writing a “1” to D0 of the vertical delay + (VDELBL) register. The reason for having a vertical delay + capability is because most programs will update the TIA + every 2 lines. This confines all vertical movements of + objects to 2 scan line “jumps”. The use of vertical delay + allows the objects to move one scan line at a time. + + 6.3 Player Graphics (P0, P1) + The player graphics are the most sophisticated of all the + moveable objects. They have all the capabilities of the + missile and ball graphics, plus three move capabilities. + Players can take on a “shape” such as a man or an airplane, + and the player can easily be flipped over horizontally to + display the mirror image (reflection) instead of the + original image, plus multiple copies of the players can be + created. + + The player graphics are drawn line-by-line like all other + graphics. The difference here is each scan line of the + player is 8 “bits” wide, whereas the missiles and ball are + one “bit” wide. Therefore, a player can be though of as + being drawn of graph paper 8 squares wide and as tall as + desired. To “color in the squares” of this imaginary graph + paper, 8 data bits are written into the players graphics + registers (GP0, GP1). This 8 bit register is scanned from + D7 to D0, and wherever a “1” is found that “square” gets + the players’ color (from the color-lum register) and where + a “0” is found that “square” gets the background color. To + position a player vertically, simply leave all “0’s” in the + graphics registers (GP0, GP1) until the electron beam is on + the scan line desired, write to the graphics register line- + by-line describing the player, then write all “0’s” to turn + off the players’ graphics until the end of that frame. + + To display a mirror image (reflection) instead of the + original figure, write a “1” to D3 of the one bit + reflection register (REFP0, REFP1). A “0” written to these + registers restores the original figure. + + Multiple copies of players as well as their size are + controlled by writing 3 bits (D0, D1, D2) into the number- + size registers (NUSIZ0, NUSIZ1). These three bits select + from 1 to 3 copies of the player, spacing of those copies, + as well as the size of the player (each “square” of the + player can be 1, 2, or 4 clocks wide). Whenever multiple + copies are selected, the TIA automatically creates the same + number of copies of the missile for that player. Again, + the specifics of all this are laid out in the TIA hardware + manual. + + Vertical delay for the players works exactly like the ball + by writing a “1” to D0 in the players’ vertical delay + registers (VDELP0, VDELP1). Writing a “0” to these + locations disables the vertical delay. + +7.0 Horizontal Positioning + + The horizontal position of each object is set by writing to + its’ associated reset register (RESP0, RESP1, RESM0, RESM1, + RESBL) which are all “strobe” registers (they trigger their + function as soon as they are addressed). That causes the + object to be positioned wherever the electron bean was in + its sweep across the screen when the register was reset. + for example, if the electron beam was 60 color clocks into + a scan line when RESP0 was written to, player 0 would be + positioned 60 color clocks "in” on the next scan line. + Whether or not P0 is actually drawn on the screen is a + function of the data in the GP0 register, but if it were + drawn, it would show up at 60. Resets to these registers + anywhere during horizontal blanking will position objects + at the left edge of the screen (color clock 0). Since + there are 3 color clocks per machine cycle, and it can take + up to 5 machine cycles to write the register, the + programmer is confined to positioning the objects at 15 + color clock intervals across the screen. This “course” + positioning is “fine tuned” by the Horizontal Motion, + explained in section 8.0. + + Missiles have an additional positioning command. Writing a + “1” to D1 of the reset missile-to-player register (RESMP0, + RESMP1) disables that missiles’ graphics (turns it off) and + repositions it horizontally to the center of its’ + associated player. Until a “0” is written to the register, + the missiles’ horizontal position is locked to the center + of its’ player in preparation to be fired again. + +8.0 Horizontal Motion + + Horizontal motion allows the programmer to move any of the + 5 graphics objects relative to their current horizontal + position. Each object has a 4 bit horizontal motion + register (HMP0, HMP1, HMM0, HMM1, HMBL) that can be loaded + with a value in the range of +7 to -8 (negative values are + expressed in two’s complement from). This motion is not + executed until the HMOVE register is written to, at which + time all motion registers move their respective objects. + Objects can be moved repeatedly by simply executing HMOVE. + Any object that is not to move must have a 0 in its motion + register. With the horizontal positioning command confined + to positioning objects at 15 color clock intervals, the + motion registers fills in the gaps by moving objects +7 to + -8 color clocks. Objects can not be placed at any color + clock position across the screen. All 5 motion registers + can be set to zero simultaneously by writing to the + horizontal motion clear register (HMCLR). + + There are timing constraints for the HMOVE command. The + HMOVE command must immediately follow a WSYNC (Wait for + SYNC) to insure the HMOVE operation occurs during + horizontal blanking. This is to allow sufficient time for + the motion registers to do their thing before the electron + beam starts drawing the next scan line. Also, for + mysterious internal hardware considerations, the motion + registers should not be modified for at least 24 machine + cycles after an HMOVE command. + +9.0 Object Priorities + + Each object is assigned a priority so when any two objects + overlap the one with the highest priority will appear to + move in front of the other. To simplify hardware logic, + the missiles have the same priority as their associated + player, and the ball has the same priority as the + playfield. The background, of course, has the lowest + priority. The following table illustrates the normal + (default) priority assignments. + + Priority Objects + 1 P0, M0 + 2 P1, M1 + 3 BL, PF + 4 BK + + This priority assignment means that players and missiles + will move in front of the playfield. To make the players + and missiles move behind the playfield, a "1" must be + written to D2 of the CTRLPF register. The following table + illustrates how the priorities are affected: + + Priority Objects + 1 PF, BL + 2 P0, M0 + 3 P1, M1 + 4 BK + + One more priority control is available to be used for + displaying the score. When a "1" is written to D1 of the + CTRLPF register, the left half of the playfield takes on + the color of player 0, and the right half the color of + player 1. The game score can now be displayed using the PF + graphics register, and the score will be in the same color + as its associated player. + +10.0 Collisions + + The TIA detects collisions between any of the 6 objects it + generates (the playfield and 5 moveable objects). There + are 15 possible two-object collisions which are stored in + 15 one bit latches. Each collision register contains two + of these latches which are read by the microprocessor on D6 + and D7 of the data bus for easy access. A "1" on the data + line indicates the collision it records has occurred. The + collision registers could be read at any time but is + usually done during vertical blank after all possible + collisions have occurred. The collision registers are all + reset simultaneously by writing to the collision reset + register (CXCLR). + +11.0 Sound + + There are two audio circuits for generating sound. They + are identical but completely independent and can be + operated simultaneously to produce sound effects through + the TV speaker. Each audio circuit has three registers + that control a noise-tone generator (what kind of sound), a + frequency selection (high or low pitch of the sound), and a + volume control. + + 11.1 Tone + The noise-tone generator is controlled by writing to the 4 + bit audio control registers (AUDC0, AUDC1). The values + written cause different kinds of sounds to be generated. + Some are pure tones like a flute, others have various + "noise" content like a rocket motor or explosion. Even + though the TIA hardware manual lists the sounds created by + each value, some experimentation will be necessary to find + "your sound". + + 11.2 Frequency + Frequency selection is controlled by writing to a 5 bit + audio frequency register (AUDF0, AUDF1). The value written + is used to divide a 30KHz reference frequency creating + higher or lower pitch of whatever type of sound is created + by the noise-tone generator. By combining the pure tones + available from the noise-tone generator with frequency + selection a wide range of tones can be generated. + + 11.3 Volume + Volume is controlled by writing to a 4 bit audio volume + register (AUDV0, AUDV1). Writing 0 to these registers + turns sound off completely, and writing any value up to 15 + increases the volume accordingly. + +12.0 Input Ports + + There are six input ports whose logic states can be read on + D7 by reading the input port addresses (INPT0, thru INPT5). + These six ports are divided into two types, "dumped" and + "latched". + + 12.1 Dumped Input Ports (INPT0 thru INPT3) + These four ports are used to read up to four paddle + controllers. Each paddle controller contains an adjustable + pot controlled by the knob on the controller. The output + of the pot is used to charge a capacitor in the console, + and when the capacitor is charged the input port goes HI. + The microprocessor discharges this capacitor by writing a + "1" to D7 of VBLANK then measures the time it takes to + detect a logic one at that port. This information can be + used to position objects on the screen based on the + position of the knob on the paddle controller. + + 12.2 Latched Input Ports (INPT4, INPT5) + These two ports have latches that are both enabled by + writing a "1" or disabled by writing a "0" to D6 of VBLANK. + When disabled the microprocessor reads the logic level of + the port directly. When enabled, the latch is set for + logic one and remains that way until its' port goes LO. + When the port goes LO the latch goes LO and remains that + way regardless of what the port does. The trigger buttons + of the joystick controllers connect to these ports. + +THE PIA (6532) + +1.0 General + + The PIA chip is an off-the-shelf 6532 Peripheral Interface + Adaptor which has three functions: a programmable timer, + 128 bytes of RAM, and two 8 bit parallel I/O ports. + +2.0 Interval timer + + The PIA uses the same clock as the microprocessor so that + one PIA cycle occurs for each machine cycle. The PIA can + be set for one of four different "intervals", where each + interval is some multiple of the clock (and therefore + machine cycles). A value from 1 to 255 is loaded into the + PIA which will be decremented by one at each interval. The + timer can now be read by the microprocessor to determine + elapsed time for timing various software operations and + keep them synchronized with the hardware (TIA chip). + + 2.1 Setting the timer + The timer is set by writing a value or count (from 1 to + 255) to the address of the desired interval setting + according to the following table : + + Hex Address Interval Mnemonic + 294 1 clock TIM1T + 295 8 clocks TIM8T + 296 64 clocks TIM64T + 297 1024 clocks T1024T + + For example, if the value of 100 were written to TIM64T + (HEX address 296) the timer would decrement to 0 in 6400 + clocks (64 clocks per interval x 100 intervals) which would + also be 6400 microprocessor machine cycles. + + 2.2 Reading the timer + The timer may be read any number of times after it is + loaded of course, but the programmer is usually interested + in whether or not the timer has reached 0. The timer is + read by reading INTIM at hex address 284. + + 2.3 When the timer reaches zero + The PIA decrements the value or count loaded into it once + each interval until it reaches 0. It holds that 0 counts + for one interval, then the counter flips to FF(HEX) and + decrements once each clock cycle, rather than once per + interval. The purpose of this feature is to allow the + programmer to determine how long ago the timer zeroed out + in the event the timer was read after it passed zero. + +3.0 RAM + + The PIA has 128 bytes of RAM located in the Stella memory + map from HEX address 80 to FF. The microprocessor stack is + normally located from FF on down, and variables are + normally located from 80 on up (hoping the two never meet). + +4.0 The I/O ports + + The two ports (Port A and Port B) are 8 bits wide and can + be set for either input or output. Port A is used to + interface to carious hand-held controllers but Port B is + dedicated to reading the status of the Stella console + switches. + + 4.1 Port B - Console Switches (read only) + Port B is hardwired to be an input port only that is read + by addressing SWCHB (HEX 282) to determine the status of + all the console switches according to the following table: + + Data Bit Switch Bit Meaning + D7 P1 difficulty 0 = amateur (B), 1 = pro (A) + D6 P0 difficulty 0 = amateur (B), 1 = pro + (A) + D5/D4 (not used) + D3 color - B/W 0 = B/W, 1 = color + D2 (not used) + D1 game select 0 = switch pressed + D0 game reset 0 = switch pressed + +5.0 Port A - Hand Controllers + + Port A is under full software control to be configured as + an input or an output port. It can then be used to read or + control various hand-head controllers with the data bits + defined differently depending on the type of controller + used. + + 5.1 Setting for input or output + Port A has an 8 bit wide Data Direction Register (DDR) that + is written to at SWACNT (HEX 281) to set each individual + pin of Port A to either input or output. The Port A pins + are labeled PA0 thru PA7, and writing a "0" to a pins' DDR + bit sets that pin as input, and a "1" sets it as an output. + For example, writing all 0's to SWACNT (the DDR for Port A) + sets PA0 thru PA7 (all 8 pins of Port A) as inputs. If F0 + (11110000) were written to SWACNT then PA7, PA6, PA5 & PA4 + would be outputs, and PA3, PA2, PA1 & PA0 would be inputs. + + 5.2 Inputting and Outputting + Once the DDR has set the pins of Port A for input or output + they may be read or written to by addressing SWCHA (HEX + 280). + + 5.3 Joystick Controllers + Two joysticks can be read by configuring the entire port as + input and reading the data at SWCHA according to the + following table: + + Data Bit Direction Player + D7 right P0 + D6 left P0 + D5 down P0 + D4 up P0 + D3 right P1 + D2 left P1 + D1 down P1 + D0 up P1 + + (P0 = left player, P1 = right player) + + A "0" in a data bit indicates the joystick has been moved + to close that switch. All "1's" in a player's nibble + indicates that joystick is not moving. + + 5.4 Paddle (pot) controllers + Only the paddle triggers are read from the PIA. The + paddles themselves are read at INP0 thru INPT3 of the TIA. + The paddle triggers can be read at SWCHA according to the + following table : + + Data Bit Paddle # + D7 P0 + D6 P1 + D5/D4 (not used) + D3 P2 + D2 P3 + D1/D0 (not used) + + 5.5 Keyboard controllers + The keyboard controller has 12 buttons arranged into 4 rows + and 3 columns. A signal is sent to a row, then the columns + are checked to see if a button is pushed, then the next row + is signaled and all columns sensed, etc. until the entire + keyboard is scanned and sensed. The PIA sends the signals + to the rows, and the columns are sensed by reading INPT0, + INPT1, and INPT4 of the TIA. With Port A configured as an + output port, the data bits will send a signal to the + keyboard controller rows according to the following table : + + Data Bit Keyboard Row Player + D7 bottom P0 + D6 third P0 + D5 second P0 + D4 top P0 + D3 bottom P1 + D2 third P1 + D1 second P1 + D0 top P1 + (P0 = left player, P1 = right player) + + NOTE : a delay of 400 microseconds is necessary between + writing to this port and reading the TIA input ports. + +6.0 Address summary table + + Hex Address Mnemonic Purpose + 280 SWCHA Port A; input or output (read or + write) + 281 SWACNT Port A DDR, 0= input, 1=output + 282 SWCHB Port B; console switches (read only) + 283 SWBCNT Port B DDR (hardwired as input) + 284 INTIM Timer output (read only) + 294 TIM1T set 1 clock interval (838 + nsec/interval) + 295 TIM8T set 8 clock interval (6.7 + usec/interval) + 296 TIM64T set 64 clock interval (53.6 + usec/interval) + 297 T1024T set 1024 clock interval (858.2 + usec/interval) + + NOTE: one clock is also one microprocessor machine cycle + +PAL/SECAM CONVERSIONS + +PAL + 1. The number of scan lines, and therefore the frame time + increases from NTSC to PAL according to the following + table: + + NTSC PAL + scan micro scan micro + lines seconds lines seconds + VBLANK 40 2548 48 3085 + KERNAL 192 12228 228 14656 + OVERSCAN 30 1910 36 2314 + FRAME 262 16686 312 20055 + + 2. Sounds will drop a little in pitch (frequency) because + of a slower crystal clock. Some sounds may need the + AUDF0/AUDF1 touched up. + + 3. PAL operates at 50 Hz compared to NTSC 60Hz, a 17% + reduction. If game play speed is based on frames per + second, it will slow down by 17%. This can be disastrous + for most skill/action carts. If the NTSC version is + designed with 2 byte fractional addition techniques (or + anything not based on frames per second) to move objects, + then PAL conversion can be as simple as changing the + fraction tables, avoiding major surgery on the program. + +SECAM + 1. SECAM is a little weird. It takes the PAL software, but + the console color/black & white switch is hardwired as + black & white. Therefore, it reads the PAL black & white + tables in software and assigns a fixed color to each lum of + black & white according to the following table: + + Lum Color + 0 Black + 2 Blue + 4 Red + 6 Magenta + 8 green + A cyan + C yellow + E white + + There is a trap here: the manual is the same for NTSC, + PAL, & SECAM. This means that the descriptions for black & + white must jive between NTSC & PAL. If you make major + changes to PAL black & white to achieve good SECAM color, + NTSC black & white must be made similar. + + 2. PAL sounds work fine on SECAM with one exception: when a + sound is to be turned off, it must be one by setting + AUDV0/AUDV1 to 0, not by setting AUDC0/AUDC1 to 0. + Otherwise, you get an obnoxious background sound. + + +TIA 1A - TELEVISION INTERFACE ADAPTOR (MODEL 1A) + +GENERAL DESCRIPTION + + The TIA is an MOS integrated circuit designed to interface + between an eight (8) bit microprocessor and a television + video modulator and to convert eight (8) bit parallel data + into serial outputs for the color, luminosity, and + composite sync required by a video modulator. + + This circuit operates on a line by line basis, always + outputting the same information every television line + unless new data is written into it by the microprocessor. + + A hardware sync counter produces horizontal sync timing + independent of the microprocessor. Vertical sync timing is + supplied to this circuit by the microprocessor and combined + into composite sync. + + Horizontal position counters are used to trigger the serial + output of five (5) horizontally movable objects; two + players, two missiles and a ball. The microprocessor can + add or subtract from these position counters to move these + objects right or left. + + The microprocessor determines all vertical position and + motion by writing zeros or ones into object registers + before each appropriate horizontal line. + + Walls, clouds and other seldom moved objects are produced + by a low resolution data register called the playfield + register. + + A fifteen (15) bit collision register detects all fifteen + possible two object collisions between these six (6) + objects (five moveable and one playfield). This collision + register can be read and reset by the microprocessor. Six + input ports are also provided on this chip that can be read + by the microprocessor. These input ports and the collision + register are the only chip addresses that can be read by + the microprocessor. All other addresses are write only. + + Color luminosity registers are included that can be + programmed by the microprocessor with eight (8) luminosity + and fifteen (15) color values. A digital phase shifter is + included on this chip to provide a single color output with + fifteen (15) phase angles. + + Two (2) independent audio generating circuits are included, + each with programmable frequency, noise content, and volume + control registers. + + +DETAILED DESCRIPTION + +1. Data and addressing + + Registers on this chip are addressed by the microprocessor + as part of its overall RAM-ROM memory space. The attached + table of read-write addresses summarizes the addressable + functions. There are no registers that are both read and + write. Some addresses however are both read and write, + with write data going into one register and read data + returning from a different register. + + If the read-write line is low, the data bits indicated in + this table will be written into the addressed write + location when the 02 clock goes from high to low. Some + registers are eight bits wide, some only one bit, and some + (strobes) have no bits, performing only control functions + (such as resets) when their address is written. + + If the read-write line is high, the addressed location can + be read by the microprocessor on data lines 6 and 7 while + the 02 clock is high. + + The addresses given in the table refer only to the six (6) + real address lines. If any of the four (4) chip select + lines are used for addressing, the addresses must be + modified accordingly. + +2. Synchronization + + A. Horizontal Timing + A hardware counter on this chip produces all horizontal + timing (such as sync, blank, burst) independent of the + microprocessor, This counter is driven from an external + 3.58 Mhz oscillator and has a total count of 228. Blank is + decoded as 68 counts and sync and color burst as 16 counts. + + B. Vertical Timing + There are one bit, addressable registers on this chip for + vertical sync and vertical blank. The timing for these + functions is established by the microprocessor by writing + zero or one into these bits. (VSYNC, VBLANK ) + + C. Composite Sync + Horizontal sync and the output of the vertical sync bit are + combined together to produce composite sync. This + composite sync signal drives a chip output pad to an + external composite video resistor network. + + D. Microprocessor Synchronization + The 3.58 MHz oscillator also clocks a divide by three + counter on this chip whose output (1.19 Mhz) is buffered to + drive an output pad called 00. This pad provides the input + phase zero clock to the microprocessor which then produces + the system 02 clock (1.19 Mhz). + Software program loops require different lengths of time to + run depending on branch decisions made within the program. + Additional synchronization between the software and + hardware. This is done with a one bit latch called WSYNC + (wait for sync). When the microprocessor finishes a + routine such as loading registers for a horizontal line, or + computing new vertical locations during vertical blank, it + can address WSYNC, setting this latch high. When this + latch is high, it drives an output pad to zero connected to + the microprocessor ready line (RDY). A zero on this line + causes the microprocessor to halt and wait. As shown in + figure 2, WSYNC latch is automatically reset to zero by the + leading edge of the next horizontal blank timing signal, + releasing the RDY line, allowing the microprocessor to + begin its computation and register writing for this + horizontal television line or line pair. + +3. Playfield graphics Register + + A. Description + Objects such as walls, clouds, and score) which are not + required to move, are written into a 20 bit register called + the playfield register. This register (Figure 5) is loaded + from the data bus by three separate write addresses (PF0, + PFl, PF2). Playfield may be loaded at any time. To clear + the playfield, zeros must be written into all three + addresses. + + B. Normal Serial Output + The playfield register is automatically scanned (and + converted to serial output) by a bi-directional shift + register clocked at a rate which spreads the twenty (20) + bits out over the left half of a horizontal line. This + scanning is initiated by the end of horizontal blank (left + edge of television screen). Normally the same scan is then + repeated, duplicating the same twenty (20) bit sequence + over the right half of the horizontal line. + + C. Reflected Serial Output + A reflected playfield may be requested by writing a one + into bit zero of the playfield control register (CTRLPF). + When this bit is true the scanning shift register will scan + the opposite direction during the right half of the + horizontal line, reversing the twenty (20) bit sequence. + + D. Timing Constraints + Even though the playfield bytes (PF0, PFl, PF2) may be + written to any time, if one of them is changed while being + serially scanned, part of the new value may both show up on + the television horizontal line. + +4. Horizontal Position Counters + + A. Description + The playfield is a fixed graphics register, always starting + its serial output when triggered by the beginning of each + television line. This chip also includes five "moveable" + graphics registers, whose serial outputs are triggered by + five separate horizontal position counters every time these + counters pass through zero count. These position counters + are clocked continuously during the unblanked portion of + every horizontal line and their count length is exactly + equal to the normal number of clocks supplied to them + during this time. They will therefore pass through zero at + the same time during each horizontal television line and + the triggered outputs will have no horizontal motion. A + typical horizontal counter is shown in figure 4. + + If extra clocks are supplied to these counters (or normal + clocks suppressed) the zero crossing time will shift and + the object will have moved left (extra clocks) or right + (fewer clocks). Some position counters have extra decoders + (in addition to a zero decode) to trigger multiple copies + of the same object across a horizontal line. + + All position counters can be reset to zero count by the + microprocessor at any time, by a write instruction to the + reset addresses (RESBL, RESM0, RESMl, RESP0, RESPl). If + reset occurs during horizontal blank, the object will + appear at the left side of the television screen. Properly + timed resets may position an object at any horizontal + location consistent with the microprocessor cycle time. + + B. Ball position Counter + The ball position counter has only the zero crossing decode + and therefore cannot trigger multiple copies of the ball + graphics. + + C. Player Position Counters + Each player position counter has three decodes in addition + to the zero crossing decode. These decodes are controlled + by bits 0,1,2 of the number size control registers (NUSIZ0, + NUSIZ1), and trigger 1,2, or 3 copies of the player (at + various spacings) across a horizontal line as shown on page + ___. These same control bits are used for the decodes on + the missile position counter, insuring an equal number of + players and missiles. + + D. Missile Position Counters + Missile position counters are identical to player position + counters except that they have another type of reset in + addition to the previously discussed horizontal position + reset. These extra reset addresses (RESMP0, RESMP1) write + data bit 1 into a one bit register whose output is used to + position the missile (horizontally) directly on top of its + corresponding player, and to disable the missile serial + output. + +5. Horizontal Motion Registers + + A. General Description + There are five write only registers on this chip that + contain the horizontal motion values for each of the five + moving objects. A typical horizontal motion register is + shown in figure 4 . The data bus (bits 4 through 7) is + written into these addresses (HMP0, HMPl, HMM0, HMMl, HMBL) + to load these registers with motion values. These registers + supply extra (or fewer) clocks to the horizontal position + counters only when commanded to do so by an HMOVE address + from the microprocessor. These registers may all be cleared + to zero (no motion) simultaneously by an HMCLR command from + the microprocessor, or individually by loading zeros into + each register. These registers are each four bits in + length and may be loaded with positive (left motion), + negative (right motion) or zero (no motion) values. + Negative values are represented in twos complement format. + + B. Timing constraints + These registers may be loaded or cleared at almost any + time. The motion values they contain will be used only when + an HMOVE command is addressed, and then all five motion + values will be used simultaneously into all five horizontal + position counters once. The only timing constraint on this + operation involves the HMOVE command. The HMOVE command + must be located in the microprocessor program immediately + after a wait for sync (WSYNC) command. This assures that + the HMOVE operation begins at the leading edge of + horizontal blank, and has the full blank time to supply + extra or fewer clocks to the horizontal position counters. + These registers should not be modified for at least 24 + Computer cycles after the HMOVE command. + +6. Moving Object Graphics Registers + + A. General Description + There are five graphics registers for moving objects on + this chip. These graphics registers are loaded (written) in + parallel by the microprocessor and like the playfield + register are scanned and converted to serial output. + Unlike the playfield register, which is always scanned + beginning at the left side of each horizontal line, moving + object graphics registers are scanned only when triggered + by a start decode from their horizontal position counter. + A typical graphics register is shown in figure 4 . + + B. Missile Graphics + The graphics registers for both missiles are identical and + very simple. They each consist of a one bit register called + missile enable (ENAM0, ENAM1). This graphics bit is scanned + (outputted) only when triggered by its corresponding + position counter. There are control bits (bits 4, 5, of + NUSIZ0, NUSIZ1) that can stretch this single graphics bit + out over widths of 1, 2, 4, or 8 clocks of horizontal line + time. (A full line is 160 clocks). + + C. Player Graphics + The graphics registers for both players are identical and + are rather complex. They each consist of eight bit parallel + registers (GRP0, GRP1) and a bi-directional parallel to + serial scan counter that converts the parallel data into + serial output. A one bit control register (REFP0, REFP1) + determines the direction (reflection) of the parallel to + serial scan, outputing either D7 through D0, or D0 though + D7. This allows reflection (horizontal flipping) of player + serial graphics data without having to flip the + microprocessor data. + + The clock into the scan counter can be controlled (three + bits of NUSIZ0 and NUSIZ1) to slow the scan rate and + stretch the eight bits of serial graphics out over widths + of 8, 16, or 32 clocks of horizontal line time. These same + control bits are used in the player-missile motion counters + to control multiple copies, so only three player widths ( + scan + rates) are available. + + D. Vertical Delay + Each of the player graphics registers actually consists of + two 8 bit parallel registers. The first (GRP0, GRP1) is + loaded (written) from the microprocessor 8 bit data bus. + The second is automatically loaded from the output of the + first. The reason for this is a complex subject called + vertical delay. A large amount of microprocessor time is + required to generate player, missile and playfield graphics + (table look up, masking, comparisons, etc.) and load these + into this chip's registers. For most game programs this + time is just too large to fit into one horizontal line + time. In fact for most games it will barely fit into two + line times (127 microseconds). Therefore, individual + graphics registers are loaded (written) every two lines, + and used twice for serial output between loads. This type + of programing will obviously limit the vertical height + resolution of objects to multiples of two lines. It also + will limit the resolution of vertical motion to two lines + jumps. Nothing can be done about the vertical height + resolution; however, vertical motion can be resolved to a + single line by addition of a second graphics register that + is automatically parallel loaded from the output of the + first, one line time after the first was loaded from the + data bus. This second graphics register output is + therefore always delayed vertically by one line. A control + bit called vertical delay (VDEL0, VDEL1) selects which of + these two registers is to be used for serial output. If + this control bit is set by the microprocessor between + picture frames, the object will be moved down (delayed) by + one line during the next frame. In most programming + applications player 0 graphics and player 1 graphics are + loaded (written) alternately, during the blank time just + prior to each line as shown in (figure 1). Since GRP0 and + GRP1 addresses from the microprocessor alternate, they are + delayed by one line from each other. The GRP0 address + decode can therefore be used to load the delayed graphics + register for player 1, and GRP1 likewise to load the + delayed graphics register for player 0. The two vertical + delay bits (VDEL0, VDELl) then select delayed or undelayed + registers for player 0 and player 1 as serial outputs. + + E. Ball Graphics + The ball graphics register is almost identical to the + missile graphics register. It also consists of a single + enable bit (ENABL) whose output is triggered by the ball + position counter. It also has two control bits (bits 4, 5 + of CTRLPF) that can stretch this single graphics bit out + over widths of 1, 2, 4, or 8 clocks of horizontal line + time. Unlike the missile graphics; however, the ball + graphics register has capability for vertical delay similar + to the player graphics. A second graphics (enable) bit is + alternately loaded from the output of the first, one line + after the first was loaded from the data bus. A ball + vertical delay bit (VDELBL) selects which of these two + graphics bits is used for the ball serial output. The first + graphics bit (ENABL) should be loaded during the same + horizontal blank time as player 0 (GRP0), because GRP1 is + used to load the second enable bit from the output of the + first on alternate lines. + +7. Collision Detection Latches + + A. Definitions + The serial outputs from all the graphics registers + represent real time horizontal location of objects on the + television screen. If any of these outputs occur at the + same time, they will overlap (collide) on the screen. + There are six objects generated on this chip (five moving + and playfield) allowing fifteen possible two object + collisions. These overlaps (collisions) are detected by + fifteen "and" gates whenever they occur, and are stored in + fifteen individual latch register bits, as shown in figure + 6. + + B. Reading Collision + The microprocessor can read these fifteen collision bits on + data lines 6 and 7 by addressing them two at a time. This + could be done at any time but is usually done between + frames (during vertical blank) after all possible + collisions have serially occurred. + + C. Reset + All collision bits are reset simultaneously by the + microprocessor using the reset address CXCLR. This is + usually done near the end of vertical blank, after + collisions have been tested. + +8. Input ports + + A. General Description + There are 6 input ports on this chip whose logic state may + be read on data line 7 with read addresses INPT0 through + INPT5. These 6 ports are divided into two types, "dumped" + and "latched". See Figure 8. + + B. Dumped Input Ports (I0 through I3) + These 4 input ports are normally used to read paddle + position from an external potentiometer-capacitor circuit. + In order to discharge these capacitors each of these input + ports has a large transistor, which may be turned on + (grounding the input ports) by writing into bit 7 of the + register VBLANK. When this control bit is cleared the + potentiometers begin to recharge the capacitors and the + microprocessor measures the time required to detect a logic + 1 at each input port. + + As long as bit 7 of register VBLANK is zero, these four + ports are general purpose high impedance input ports. When + this bit is a 1 these ports are grounded. + + C. Latched Input ports (I4, I5) + These two input ports have latches which can be enabled or + disabled by writing into bit 6 of register VBLANK. + + When disabled, these latches are removed from the circuit + completely and these ports become two general purpose input + ports, whose present logic state can be read directly by + the microprocessor. + + When enabled, these latches will store negative (zero logic + level) signals appearing on these two input ports, and the + input port addresses will read the latches instead of the + input ports. + + When first enabled these latches will remain positive as + long as the input ports remain positive (logic one). A zero + input port signal will clear a latch value to zero, where + it will remain (even after the port returns positive) until + disabled. Both latches may be simultaneously disabled by + writing a zero into bit 6 of register VBLANK. + +8.5 Priority Encoder + + A. Purpose + As discussed in the section on collisions, simultaneous + serial outputs from the graphics registers represent + overlap on the television screen. In order to have color- + luminosity values assigned to individual objects it is + necessary to establish priorities between objects when + overlapped. The priority encoder is shown in figure 3. + + B. Priority Assignment + The lack of any objects results in a color-lum value called + the background. The background (BK) has lowest priority and + only appears when no objects are outputing. In order to + simplify the logic, each missile is given the same color- + lum value and priority as it's corresponding player (P0, + M0) and the ball is given the same color-lum value and + priority as the playfield (PF, BL). + + The following table illustrates the normal priority + assignment: + + Highest Priority P0, M0 + Second Highest P1, M1 + Third Highest PF, BL + Lowest Priority BK + + Objects with higher priority will appear to move in front + of objects with lower priority. Players will therefore move + in front of playfield (clouds, walls, etc.). + + C. Priority Control + There are two playfield control bits that affect priority, + one called playfield priority (PFP) (bit 2 of CTRLPF) and + one called score (bit 1 of CTRLPF). When a one is written + into the PFP bit the priority assignment is modified as + shown below. + + Highest Priority PF, BL + Second Highest P0, M0 + Third Highest P1, M1 + Lowest Priority BK + + + Players will then move behind playfield (clouds, wall, + etc.). When a one is written into the score control bit, + the playfield is forced to take the color-lum of player 0 + in the left half of the screen and player 1 in the right + half of the screen. This is used when displaying score and + identifies the score with the correct player. The priority + encoder produces 4 register select lines shown in figure 3) + that are mutually exclusive. These 4 lines select either + background, player 0, player 1 or playfield, and only one + of them can be true at a time. + +9 Color Luminance Registers + + A. Description + There are four registers (shown in figure 3) that contain + color-lum codes. Four bits of color code and three its of + luminance code may be written into each of these registers + (COLUP0, COLUP1, COLUPF, COLUBK) by the microprocessor at + any time. These codes (representing 16 color values and 8 + luminance values) are given in the Detailed Address List. + + B. Multiplexing + The serial graphics output from all six objects is examined + by the priority encoder which activates one of the four + select lines into a 4 x 7 multiplexer. This multiplexer + (shown in figure 3) then selects one of the four color-lum + registers as a 7 line output. Three of these lines are + binary coded luminosity and go directly to chip output + pads. The other four lines go to the color phase shifter. + +10. Color Phase Shifter + + This portion of the chip (shown in figure 3) produces a + reference color output (color burst) during horizontal + blank and then during the unblanked portion of the line it + produces a color output shifted in phase with respect to + the color burst. The amount of phase shift determines the + color and is selected by the four color code lines from the + Color-lum multiplexer. Binary code 0 selects no color. + Code 1 selects gold (same phase as color burst). Codes 2 + (0010) through 15 (1111) shift the phase from zero through + almost 360 degrees allowing selection of 15 total colors + around the television color wheel. + +11. Audio Circuits + + Two audio circuits are incorporated on this chip. They are + identical and completely independent, although their + outputs could be combined externally into one speaker. + Each audio circuit consists of parts described below, and + in figure 7. + + A. Frequency Select + Clock pulses (at approximately 30 KHz) from the horizontal + sync counter pass through a divide by N circuit which is + controlled by the output code from a five bit frequency + register (AUDF). This register can be loaded (written) by + the microprocessor at any time, and causes the 30 KHz + clocks to be divided by 1 (code 00000) through 32 (code + 11111). This produces pulses that are digitally adjustable + from approximately 30 KHz to 1 KHz and are used to clock + the noise-tone generator. + + B. Noise-Tone Generator + This circuit contains a nine bit shift counter which may be + controlled by the output code from a four bit audio control + register(AUDC), and is clocked by the frequency select + circuit. The control register can be loaded by the + microprocessor at any time, and selects different shift + counter feedback taps and count lengths to produce a + variety of noise and tone qualities. + + C. Volume Select + The shift counter output is used to drive the audio output + pad through four driver transistors that are graduated in + size. Each transistor is twice as large as the previous one + and is enable by one bit from the audio volume register + (AUDV). This audio volume register may be loaded by the + microprocessor at any time. As binary codes 0 through 15 + are loaded, the pad drive transistors are enabled in a + binary sequence. The shift counter output therefore can + pull down on the audio output pad with 16 selectable + impedance levels. + + + + +Figure 1. Vertical Delay + + + + + + +Figure 2. Synchronization + + + +Figure 3. Color-Luminance + + + + +Figure 4. Typical Horizontal Motion Circuit + + + + + + + + +Figure 5. Playfield Graphics + + + + + +Figure 6. Collision Detection + + + + + + +Figure 7. Audio Circuit + + + + + +Figure 8. Input Ports + + + + + +Figure 9. Game System + + + +Write Address Detailed Functions + + +WSYNC (wait for sync) + This address halts microporcessor by clearing RDY latch to + zero. RDY is set true again by the leading edge of + horizontal blank. + + Data bits not used + + +RSYNC (reset sync) + This address resets the horizontal sync counter to define + the beginning of horizontal blank time, and is used in chip + testing. + + Data bits not used + + +VSYNC + This address controls vertical sync time by writing D1 into + the VSYNC latch + + D1 [1 = start vert sync, 0 = stop vertical sync] + +VBLANK + This address controls vertical blank and the latches and + dumping transistors on the input ports by writing into bits + D7, D6 and D1 of the VBLANK register. + + D1 [ 1 = start vert. blank, 0 = stop vert. blank] + D6 [ 1 = Enable I4 I5 latches, 0 = disable I4 I5 latches] + D7 [ 1 = dump I6I1I2I3 ports to ground, 0 = remove dump + path to ground] + Note : Disable latches (D6 = 0) also resets latches to + logic true + +PJ0 (PF1, PF2) + These addresses are used to write into playfield registers + + + PLAYFIELD REGISTERS SERIAL OUTPUT + + 1 horizontal line ( 160 clocks) + Playfield + Reflect Control + PF0 PF1 PF2 PF0 PF1 PF2 + + center + + PF0 PF1 PF2 PF2 PF1 + PF0 + + each bit = 4 clocks + +CTRLPF + This address is uded to write into the playfield control + register (a logic 1 causes action as described below) + + D0 = REF (reflect playfield) + D1 = SCORE (left half of playfield gets color of player 0, + right half gets color of player 1) + D2 = PFP (playfield gets priority over players so they can + move behind the playfield) + D4 & D5 = BALL SIZE + D5 D4 Width + 0 0 1 clock + 0 1 2 clocks + 1 0 4 clocks + 1 1 8 clocks + +NUSIZ0 (NUSIZ1) + These addresses control the number and size of players and + missiles. + + Missile Size D5 D4 Width + 0 0 1 clock + 0 1 2 clocks + 1 0 4 clocks + 1 1 8 clocks + + Player-Missile number & player size + + 1/2 television line (80 clocks) + 8 clocks per square + + +RESP0 (RESP1, RESM0, RESM1, RESBL) + These addresses are used to reset players, missiles and the + ball. The object will begin its serial graphics at the + time of a horizontal line at which the reset address + occurs. + + No data bits are used + + +RESMP0 (RESMP1) + These addresses are used to reset the hoiz. location of a + missile to the center of it’s corresponding player. As + long as this control bit is true (1) the missile will + remain locked to the center of it’s player and the missile + graphics will be siddabled. When a zero is written into + this location, the missile is enabled, and can be moved + independently from the player. + + D1 = RESMP (missile-player reset) +HMOVE + This address causes the horizontal motion register values + to be acted upon during the horizontal blank time in which + it occurs. It must occur at the beginning of horiz. blank + in order to allow time for generation of extra clock pulses + into the horizontal position counters if motion is desired + this command must immediately follow a WSYNC command in the + program. + + No data bits are used + + +HMCLR + This address clears all horizontal motion registers to zero + (no motion) + + No data bits are used + + +HMP0 (HMP1, HMM0, HMM1, HMBL) + These addresses write data (horizontal motion values) into + the horizontal motion registers. These registers will + cause horizontal motion only when commanded to do so by the + horiz. move command HMOVE. + The motion values are coded as shown below : + + D7 D6 D5 D4 + 0 1 1 1 +7 + 0 1 1 0 +6 + 0 1 0 1 +5 Move left + 0 1 0 0 +4 indicated number + 0 0 1 1 +3 of clocks + 0 0 1 0 +2 + 0 0 0 1 +1 + 0 0 0 0 0 No Motion + 1 1 1 1 -1 + 1 1 1 0 -2 + 1 1 0 1 -3 + 1 1 0 0 -4 move right + 1 0 1 1 -5 indicated number + 1 0 1 0 -6 of clocks + 1 0 0 1 -7 + 1 0 0 0 -8 + + WARNING : These motion registers should not be modified + during the 24 computer cycles immediately following an + HMOVE command. Unpredictable motion values may result. +ENAM0 (ENAM1, ENABL) + These addresses write D1 into the 1 bit missile or ball + graphics registers. + + D1 - [0 = dissables object, 1 = enables object] + + +GRP0 (GRP1) + These addresses write data into the player graphics + registers. + + Note: serial output begins with D7, unless REFP0 (REFP1) = + 1 + +REFP0 (REFP1) + These addesses write D3 into the 1 bit player reflect + registers + + D3 - [ 0 = no reflect, D7 of GRP on left, 1 = reflect, D0 + of GRP on left] + +VDELP0 (VDELP1, VDELBL) + These addresses write D0 into the 1 bit vertical delay + registers, to delay players or ball by one vertical line. + + D0 - [0 = no delay, 1 = delay] + +CXCLR + This adderess clears all collision latches to zero (no + collision) + + No data bits are used + +COLUP0 (COLUP1, COLUPF, COLUBK) + These addresses write data into the player, playfield, adn + background color-luminance registers + + COLOR D7 D6 D5 D4 D3 D2 D1 LUM + grey - gold 0 0 0 0 0 0 0 black + 0 0 0 1 0 0 1 dark grey + orange, brt-0 0 1 0 0 1 0 + org + 0 0 1 1 0 1 1 grey + pink - 0 1 0 0 1 0 0 + purple + 0 1 0 1 1 0 1 + purp-blue, 0 1 1 0 1 1 0 light gret + blue + 0 1 1 1 1 1 1 white + blue - lt. 1 0 0 0 + blue + 1 0 0 1 + torq. - 1 0 1 0 + grn. blue + 1 0 1 1 + grn. - yel. 1 1 0 0 + grn. + 1 1 0 1 + org. grn - 1 1 1 0 + lt org. + 1 1 1 1 + +AUDF0 (AUDF1) + These addresses write data into the audio frequency divider + registers. + + D4 D3 D2 D1 D0 30KHz divided + by + 0 0 0 0 0 no division + 0 0 0 0 1 divide by 2 + 0 0 0 1 0 divide by 3 + .. .. .. .. .. ... + . . . . . + 1 1 1 1 0 divide by 31 + 1 1 1 1 1 divide by 32 + +AUDC0 (AUDC1) + These addresses write data into the audio control registers + which control the noise content and additional division of + the audio output. + + D3 D2 D1 D0 Type of noise + or division + 0 0 0 0 set to 1 + 0 0 0 1 4 bit poly + 0 0 1 0 div 15 -> 4 + bit poly + 0 0 1 1 5 bit poly -> + 4 bit poly + 0 1 0 0 div 2 : pure + tone + 0 1 0 1 div 2 : pure + tone + 0 1 1 0 div 31 : pure + tone + 0 1 1 1 5 bit poly -> + div 2 + 1 0 0 0 9 bit poly + (white noise) + 1 0 0 1 5 bit poly + 1 0 1 0 div 31 : pure + tone + 1 0 1 1 set last 4 + bits to 1 + 1 1 0 0 div 6 : pure + tone + 1 1 0 1 div 6 : pure + tone + 1 1 1 0 div 93 : pure + tone + 1 1 1 1 5 bit poly div + 6 + + +AUDV0 (AUDV1) + These addresses write data into the audio volume registers + which set the pull down impedance driving the audio output + pads. + + D3 D2 D1 D0 Audio Output + Pull down + current + 0 0 0 0 No output + current + 0 0 0 1 lowest + 0 0 1 0 + .. .. .. .. + . . . . + 1 1 1 0 + 1 1 1 1 highest + + +WRITE ADDRESS SUMMARY + 6 bit Address 7 6 5 4 3 2 1 0 Function + addres Name + s + 00 VSYNC 1 vertical sync set-clear + 01 VBLANK 1 1 1 vertical blank set- + clear + 02 WSYNC s t r o b e wait for leading edge + of horizontal blank + 03 RSYNC s t r o b e reset horizontal sync + counter + 04 NUSIZ0 1 1 1 1 1 1 number-size player- + missile 0 + 05 NUSIZ1 1 1 1 1 1 1 number-size player- + missile 1 + 06 COLUP0 1 1 1 1 1 1 1 color-lum player 0 + 07 COLUP1 1 1 1 1 1 1 1 color-lum player 1 + 08 COLUPF 1 1 1 1 1 1 1 color-lum playfield + 09 COLUBK 1 1 1 1 1 1 1 color-lum background + 0A CTRLPF 1 1 1 1 1 control playfield ball + size & collisions + 0B REFP0 1 reflect player 0 + 0C REFP1 1 reflect player 1 + 0D PF0 1 1 1 1 playfield register byte + 0 + 0E PF1 1 1 1 1 1 1 1 1 playfield register byte + 1 + 0F PF2 1 1 1 1 1 1 1 1 playfield register byte + 2 + 10 RESP0 s t r o b e reset player 0 + 11 RESP1 s t r o b e reset player 1 + 12 RESM0 s t r o b e reset missile 0 + 13 RESM1 s t r o b e reset missile 1 + 14 RESBL s t r o b e reset ball + 15 AUDC0 1 1 1 1 audio control 0 + 16 AUDC1 1 1 1 1 1 audio control 1 + 17 AUDF0 1 1 1 1 1 audio frequency 0 + 18 AUDF1 1 1 1 1 audio frequency 1 + 19 AUDV0 1 1 1 1 audio volume 0 + 1A AUDV1 1 1 1 1 audio volume 1 + 1B GRP0 1 1 1 1 1 1 1 1 graphics player 0 + 1C GRP1 1 1 1 1 1 1 1 1 graphics player 1 + 1D ENAM0 1 graphics (enable) + missile 0 + 1E ENAM1 1 graphics (enable) + missile 1 + 1F ENABL 1 graphics (enable) ball + 20 HMP0 1 1 1 1 horizontal motion + player 0 + 21 HMP1 1 1 1 1 horizontal motion + player 1 + 22 HMM0 1 1 1 1 horizontal motion + missile 0 + 23 HMM1 1 1 1 1 horizontal motion + missile 1 + 24 HMBL 1 1 1 1 horizontal motion ball + 25 VDELP0 1 vertical delay player 0 + 26 VDEL01 1 vertical delay player 1 + 27 VDELBL 1 vertical delay ball + 28 RESMP0 1 reset missile 0 to + player 0 + 29 RESMP1 1 reset missile 1 to + player 1 + 2A HMOVE s t r o b e apply horizontal motion + 2B HMCLR s t r o b e clear horizontal motion + registers + 2C CXCLR s t r o b e clear collision latches + +READ ADDRESS SUMMARY + + 6 bit Address 7 6 5 4 3 2 1 0 Function + addres Name + s D7 D6 + 0 CXM0P 1 1 read MO P1 M0 P0 + collision + 1 CXM1P 1 1 read M1 P0 M1 P1 + collision + 2 CXP0FB 1 1 read P0 PF P0 BL + collision + 3 CXP1FB 1 1 read P1 PF P1 BL + collision + 4 CXM0FB 1 1 read M0 PF M0 BL + collision + 5 CXM1FB 1 1 read M1 PF M1 BL + collision + 6 CXBLPF 1 read BL PF unuse + collision d + 7 CXPPMM 1 1 read P0 P1 M0 M1 + collision + 8 INPT0 1 read pot port + 9 INPT1 1 read pot port + A INPT2 1 read pot port + B INPT3 1 read pot port + C INPT4 1 read input + D INPT5 1 read input + Note : I0, I2, I2, I3 + can be grounded under + software control. + I4, I5 can be converted + to latched inputs under + software control + +TIA O0..02 AND LUM TIMING +TIA WRITE TIMING CHARACTERISTICS +TIA READ TIMING CHARACTERISTICS + +TIA COMP-SYN AND READY TIMING + +RSYNC, RES0O, H01, H02, SHB, 02, 0O +TIA RSYNC AND BLANK AND READY TIMING + diff --git a/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/tia_color.html b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/tia_color.html new file mode 100644 index 00000000..75bfc32a --- /dev/null +++ b/com.wudsn.ide.asm/help/www.qotile.net/minidig/docs/tia_color.html @@ -0,0 +1,898 @@ + +TIA Color Charts + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NTSC TIA colors (128 unique colors)
HUE
L
U
M
0123456789ABCDEF
0-1                
2-3                
4-5                
6-7                
8-9                
A-B                
C-D                
E-F                
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PAL TIA colors (104 unique colors)
HUE
L
U
M
0123456789ABCDEF
0-1                
2-3                
4-5                
6-7                
8-9                
A-B                
C-D                
E-F                
+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SECAM TIA colors (8 unique colors)
HUE
L
U
M
0123456789ABCDEF
0-1                
2-3                
4-5                
6-7                
8-9                
A-B                
C-D                
E-F                

diff --git a/com.wudsn.ide.asm/icons/brkp_obj.gif b/com.wudsn.ide.asm/icons/brkp_obj.gif new file mode 100644 index 00000000..a831fe72 Binary files /dev/null and b/com.wudsn.ide.asm/icons/brkp_obj.gif differ diff --git a/com.wudsn.ide.asm/icons/brkpd_obj.gif b/com.wudsn.ide.asm/icons/brkpd_obj.gif new file mode 100644 index 00000000..74b26c31 Binary files /dev/null and b/com.wudsn.ide.asm/icons/brkpd_obj.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-apple2-16x16.gif b/com.wudsn.ide.asm/icons/hardware-apple2-16x16.gif new file mode 100644 index 00000000..9755078c Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-apple2-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-atari2600-16x16.gif b/com.wudsn.ide.asm/icons/hardware-atari2600-16x16.gif new file mode 100644 index 00000000..ab28795f Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-atari2600-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-atari7800-16x16.gif b/com.wudsn.ide.asm/icons/hardware-atari7800-16x16.gif new file mode 100644 index 00000000..2079858c Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-atari7800-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-atari8bit-16x16.gif b/com.wudsn.ide.asm/icons/hardware-atari8bit-16x16.gif new file mode 100644 index 00000000..c93689bf Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-atari8bit-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-atari8bit-32x32.gif b/com.wudsn.ide.asm/icons/hardware-atari8bit-32x32.gif new file mode 100644 index 00000000..1845df70 Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-atari8bit-32x32.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-c64-16x16.gif b/com.wudsn.ide.asm/icons/hardware-c64-16x16.gif new file mode 100644 index 00000000..c58eaa56 Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-c64-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-generic-16x16.gif b/com.wudsn.ide.asm/icons/hardware-generic-16x16.gif new file mode 100644 index 00000000..b58a71de Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-generic-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-nes-16x16.gif b/com.wudsn.ide.asm/icons/hardware-nes-16x16.gif new file mode 100644 index 00000000..3269e0a1 Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-nes-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/hardware-test-16x16.gif b/com.wudsn.ide.asm/icons/hardware-test-16x16.gif new file mode 100644 index 00000000..a5bb5a8c Binary files /dev/null and b/com.wudsn.ide.asm/icons/hardware-test-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/help-16x16.gif b/com.wudsn.ide.asm/icons/help-16x16.gif new file mode 100644 index 00000000..86550fe9 Binary files /dev/null and b/com.wudsn.ide.asm/icons/help-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/help-topic-pdf-small.gif b/com.wudsn.ide.asm/icons/help-topic-pdf-small.gif new file mode 100644 index 00000000..9591e124 Binary files /dev/null and b/com.wudsn.ide.asm/icons/help-topic-pdf-small.gif differ diff --git a/com.wudsn.ide.asm/icons/help-topic-small.gif b/com.wudsn.ide.asm/icons/help-topic-small.gif new file mode 100644 index 00000000..1cfe6f3a Binary files /dev/null and b/com.wudsn.ide.asm/icons/help-topic-small.gif differ diff --git a/com.wudsn.ide.asm/icons/instruction-type-directive-16x16.gif b/com.wudsn.ide.asm/icons/instruction-type-directive-16x16.gif new file mode 100644 index 00000000..12d90bea Binary files /dev/null and b/com.wudsn.ide.asm/icons/instruction-type-directive-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/instruction-type-illegal-opcode-16x16.gif b/com.wudsn.ide.asm/icons/instruction-type-illegal-opcode-16x16.gif new file mode 100644 index 00000000..da0e61ca Binary files /dev/null and b/com.wudsn.ide.asm/icons/instruction-type-illegal-opcode-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/instruction-type-legal-opcode-16x16.gif b/com.wudsn.ide.asm/icons/instruction-type-legal-opcode-16x16.gif new file mode 100644 index 00000000..ef6f72a7 Binary files /dev/null and b/com.wudsn.ide.asm/icons/instruction-type-legal-opcode-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/instruction-type-pseudo-opcode-16x16.gif b/com.wudsn.ide.asm/icons/instruction-type-pseudo-opcode-16x16.gif new file mode 100644 index 00000000..92d2f6f7 Binary files /dev/null and b/com.wudsn.ide.asm/icons/instruction-type-pseudo-opcode-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-binary-include-16x16.gif b/com.wudsn.ide.asm/icons/outline-binary-include-16x16.gif new file mode 100644 index 00000000..b7db9168 Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-binary-include-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-binary-output-16x16.gif b/com.wudsn.ide.asm/icons/outline-binary-output-16x16.gif new file mode 100644 index 00000000..a61ee2d3 Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-binary-output-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-constant-definition-16x16.gif b/com.wudsn.ide.asm/icons/outline-constant-definition-16x16.gif new file mode 100644 index 00000000..bb6ec29c Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-constant-definition-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-default-16x16.gif b/com.wudsn.ide.asm/icons/outline-default-16x16.gif new file mode 100644 index 00000000..7d24707e Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-default-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-definition-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-definition-section-16x16.gif new file mode 100644 index 00000000..7d24707e Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-definition-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-diamond.gif b/com.wudsn.ide.asm/icons/outline-diamond.gif new file mode 100644 index 00000000..36b7561b Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-diamond.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-enum-definition-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-enum-definition-section-16x16.gif new file mode 100644 index 00000000..3f8a0dba Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-enum-definition-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-equate-definition-16x16.gif b/com.wudsn.ide.asm/icons/outline-equate-definition-16x16.gif new file mode 100644 index 00000000..a610743c Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-equate-definition-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-implementation-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-implementation-section-16x16.gif new file mode 100644 index 00000000..49ccefda Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-implementation-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-label-definition-16x16.gif b/com.wudsn.ide.asm/icons/outline-label-definition-16x16.gif new file mode 100644 index 00000000..8adce954 Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-label-definition-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-local-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-local-section-16x16.gif new file mode 100644 index 00000000..86df7aae Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-local-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-macro-definition-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-macro-definition-section-16x16.gif new file mode 100644 index 00000000..05f3a129 Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-macro-definition-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-pages-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-pages-section-16x16.gif new file mode 100644 index 00000000..53ec9919 Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-pages-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-procedure-definition-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-procedure-definition-section-16x16.gif new file mode 100644 index 00000000..cbb53ccb Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-procedure-definition-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-repeat-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-repeat-section-16x16.gif new file mode 100644 index 00000000..572f08fc Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-repeat-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-sort.gif b/com.wudsn.ide.asm/icons/outline-sort.gif new file mode 100644 index 00000000..6311cc00 Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-sort.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-source-include-16x16.gif b/com.wudsn.ide.asm/icons/outline-source-include-16x16.gif new file mode 100644 index 00000000..ea94702c Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-source-include-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-structure-definition-section-16x16.gif b/com.wudsn.ide.asm/icons/outline-structure-definition-section-16x16.gif new file mode 100644 index 00000000..871c78cf Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-structure-definition-section-16x16.gif differ diff --git a/com.wudsn.ide.asm/icons/outline-variable-16x16 - Copy.gif b/com.wudsn.ide.asm/icons/outline-variable-16x16 - Copy.gif new file mode 100644 index 00000000..61c98fcf Binary files /dev/null and b/com.wudsn.ide.asm/icons/outline-variable-16x16 - Copy.gif differ diff --git a/com.wudsn.ide.asm/icons/sample.gif b/com.wudsn.ide.asm/icons/sample.gif new file mode 100644 index 00000000..34fb3c9d Binary files /dev/null and b/com.wudsn.ide.asm/icons/sample.gif differ diff --git a/com.wudsn.ide.asm/icons/topic.gif b/com.wudsn.ide.asm/icons/topic.gif new file mode 100644 index 00000000..fbd1394c Binary files /dev/null and b/com.wudsn.ide.asm/icons/topic.gif differ diff --git a/com.wudsn.ide.asm/plugin.properties b/com.wudsn.ide.asm/plugin.properties new file mode 100644 index 00000000..959f4286 --- /dev/null +++ b/com.wudsn.ide.asm/plugin.properties @@ -0,0 +1,52 @@ +com.wudsn.ide.asm.Hardware.GENERIC=Generic +com.wudsn.ide.asm.Hardware.APPLE2=Apple 2 +com.wudsn.ide.asm.Hardware.ATARI2600=Atari 2600 +com.wudsn.ide.asm.Hardware.ATARI7800=Atari 7800 +com.wudsn.ide.asm.Hardware.ATARI8BIT=Atari 8-bit +com.wudsn.ide.asm.Hardware.C64=C64 +com.wudsn.ide.asm.Hardware.NES=NES +com.wudsn.ide.asm.Hardware.TEST=Test + +com.wudsn.ide.asm.CPU.MOS6502=6502 +com.wudsn.ide.asm.CPU.MOS6502_ILLEGAL=6502 with illegal opcodes +com.wudsn.ide.asm.CPU.MOS65C02=65C02 +com.wudsn.ide.asm.CPU.MOS6502_DTV=6502 DTV +com.wudsn.ide.asm.CPU.MOS65816=65816 + +com.wudsn.ide.asm.compiler.AssemblerSourceFile.name=Assembler Source File + +com.wudsn.ide.asm.editor.AssemblerEditorAssemblerMenu.label=Assembler +com.wudsn.ide.asm.editor.AssemblerEditorAssemblerMenu.mnemonic=A + +com.wudsn.ide.asm.editor.AssemblerEditorAssemblerToolbar.label=Assembler + +com.wudsn.ide.asm.editor.AssemblerEditorCommands.name=Assembler Editor Commands +com.wudsn.ide.asm.editor.AssemblerEditorOpenSourceFolderCommand.name=Open Source Folder +com.wudsn.ide.asm.editor.AssemblerEditorOpenSourceFolderCommand.mnemonic=S +com.wudsn.ide.asm.editor.AssemblerEditorOpenOutputFolderCommand.name=Open Output Folder +com.wudsn.ide.asm.editor.AssemblerEditorOpenOutputFolderCommand.mnemonic=O +com.wudsn.ide.asm.editor.AssemblerEditorCompileCommand.name=Compile +com.wudsn.ide.asm.editor.AssemblerEditorCompileCommand.mnemonic=C +com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunCommand.name=Compile and Run +com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunWithCommand.name=Compile and Run with... +com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunCommand.mnemonic=R +com.wudsn.ide.asm.editor.AssemblerEditorCompilerHelpCommand.name=Compiler Help +com.wudsn.ide.asm.editor.AssemblerEditorCompilerHelpCommand.mnemonic=H +com.wudsn.ide.asm.editor.AssemblerEditorToggleCommentCommand.name=Toggle Comment +com.wudsn.ide.asm.editor.AssemblerEditorToggleBreakpointCommand.name=Toggle Breakpoint +com.wudsn.ide.asm.editor.AssemblerEditorOpenDeclarationCommand.name=Open Declaration +com.wudsn.ide.asm.editor.AssemblerEditorEnableDisableBreakpointCommand.name=Enable/Disable Breakpoint + +com.wudsn.ide.asm.editor.AssemblerHyperlinkDetector.name=Hyperlink Detector +com.wudsn.ide.asm.editor.AssemblerHyperlinkDetectorEditorTarget.name=Assembler Editor + +com.wudsn.ide.asm.editor.AssemblerBreakpoint.name=Breakpoint + +com.wudsn.ide.asm.editor.CompilerSymbolsView.name=Symbols + +# Preferences +com.wudsn.ide.asm.preferences.AssemblerPreferences.name=Assembler Preferences +com.wudsn.ide.asm.preferences.AssemblerPreferencesPage.name=Assembler + +com.wudsn.ide.asm.runner.DefaultApplication.name=Operating System Default Application +com.wudsn.ide.asm.runner.UserDefinedApplication.name=User Defined Application diff --git a/com.wudsn.ide.asm/plugin.xml b/com.wudsn.ide.asm/plugin.xml new file mode 100644 index 00000000..46fb9ff9 --- /dev/null +++ b/com.wudsn.ide.asm/plugin.xml @@ -0,0 +1,425 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + label="%com.wudsn.ide.asm.editor.AssemblerEditorAssemblerMenu.label" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm/plugin_de_DE.properties b/com.wudsn.ide.asm/plugin_de_DE.properties new file mode 100644 index 00000000..6c148013 --- /dev/null +++ b/com.wudsn.ide.asm/plugin_de_DE.properties @@ -0,0 +1,52 @@ +com.wudsn.ide.asm.Hardware.GENERIC=Generisch +com.wudsn.ide.asm.Hardware.APPLE2=Apple 2 +com.wudsn.ide.asm.Hardware.ATARI2600=Atari 2600 +com.wudsn.ide.asm.Hardware.ATARI7800=Atari 7800 +com.wudsn.ide.asm.Hardware.ATARI8BIT=Atari 8-bit +com.wudsn.ide.asm.Hardware.C64=C64 +com.wudsn.ide.asm.Hardware.NES=NES +com.wudsn.ide.asm.Hardware.TEST=Test + +com.wudsn.ide.asm.CPU.MOS6502=6502 +com.wudsn.ide.asm.CPU.MOS6502_ILLEGAL=6502 mit illegalen Opcodes +com.wudsn.ide.asm.CPU.MOS65C02=65C02 +com.wudsn.ide.asm.CPU.MOS6502_DTV=6502 DTV +com.wudsn.ide.asm.CPU.MOS65816=65816 + +com.wudsn.ide.asm.compiler.AssemblerSourceFile.name=Assembler Quell-Datei + +com.wudsn.ide.asm.editor.AssemblerEditorAssemblerMenu.label=Assembler +com.wudsn.ide.asm.editor.AssemblerEditorAssemblerMenu.mnemonic=A + +com.wudsn.ide.asm.editor.AssemblerEditorAssemblerToolbar.label=Assembler + +com.wudsn.ide.asm.editor.AssemblerEditorCommands.name=Assembler Editor Befehle +com.wudsn.ide.asm.editor.AssemblerEditorOpenSourceFolderCommand.name=Quell-Ordner öffnen +com.wudsn.ide.asm.editor.AssemblerEditorOpenSourceFolderCommand.mnemonic=Q +com.wudsn.ide.asm.editor.AssemblerEditorOpenOutputFolderCommand.name=Ausgabe-Ordner öffnen +com.wudsn.ide.asm.editor.AssemblerEditorOpenOutputFolderCommand.mnemonic=A +com.wudsn.ide.asm.editor.AssemblerEditorCompileCommand.name=Kompilieren +com.wudsn.ide.asm.editor.AssemblerEditorCompileCommand.mnemonic=K +com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunCommand.name=Kompilieren und Ausführen +com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunWithCommand.name=Kompilieren und Ausführen mit ... +com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunCommand.mnemonic=A +com.wudsn.ide.asm.editor.AssemblerEditorCompilerHelpCommand.name=Kompiler Hilfe +com.wudsn.ide.asm.editor.AssemblerEditorCompilerHelpCommand.mnemonic=H +com.wudsn.ide.asm.editor.AssemblerEditorToggleCommentCommand.name=Kommentar umschalten +com.wudsn.ide.asm.editor.AssemblerEditorToggleBreakpointCommand.name=Breakpoint umschalten +com.wudsn.ide.asm.editor.AssemblerEditorOpenDeclarationCommand.name=Deklaration öffnen +com.wudsn.ide.asm.editor.AssemblerEditorEnableDisableBreakpointCommand.name=Breakpoint ein/ausschalten + +com.wudsn.ide.asm.editor.AssemblerHyperlinkDetector.name=Hyperlink Detector +com.wudsn.ide.asm.editor.AssemblerHyperlinkDetectorEditorTarget.name=Assembler Editor + +com.wudsn.ide.asm.editor.AssemblerBreakpoint.name=Unterbrechungspunkt + +com.wudsn.ide.asm.editor.CompilerSymbolsView.name=Symbole + +# Preferences +com.wudsn.ide.asm.preferences.AssemblerPreferences.name=Assembler Einstellungen +com.wudsn.ide.asm.preferences.AssemblerPreferencesPage.name=Assembler + +com.wudsn.ide.asm.runner.DefaultApplication.name=Standardanwendung des Betriebssystems +com.wudsn.ide.asm.runner.UserDefinedApplication.name=Benutzerdefinierte Anwendung \ No newline at end of file diff --git a/com.wudsn.ide.asm/schema/compilers.exsd b/com.wudsn.ide.asm/schema/compilers.exsd new file mode 100644 index 00000000..fb88ff2b --- /dev/null +++ b/com.wudsn.ide.asm/schema/compilers.exsd @@ -0,0 +1,197 @@ + + + + + + + + + This extension point allows for adding new 6502 assembler compilers to the WUDSN IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The technical id of the compiler. Must consist of lower case letters or digits. + + + + + + + Translatable name of the compiler. Typically contains only upper case letters. + + + + + + + + + + The name of the compiler class. + + + + + + + + + + Supported (minimum) version of the compiler. All compiler directives and major compiler features up to this version have been incorporated into this version of WUDSN IDE. + + + + + + + The absolute URL pointing to the homepage of the compiler where the latest version can be downloaded. + + + + + + + The relative paths to the help documents. Multiple paths are separated by comma. Each path is evluated based on the absolute path of the compiler executable in the preferences. + + + + + + + The default compiler parameters which are use if not other parameters are specified in the preferences. + + + + + + + The default hardware which is assumed, if no source annotation is found. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WUDSN IDE 1.1.0 + + + + + + + + + + To implement this extension point, you have to create a sub-class of com.wudsn.ide.asm.compiler.Compiler, a sub-class of com.wudsn.ide.asm.compiler.parser.CompilerSourceParser and a sub-class of com.wudsn.ide.asm.compiler.CompilerLogParser. In the constructor of the compiler you have to set the source parser instance. In the "createLogParser" method of the compiler you have to create a new instance of the corresponding log parser. + + + + + + + + + + (c) 2009 Peter Dell + + + + diff --git a/com.wudsn.ide.asm/schema/runners.exsd b/com.wudsn.ide.asm/schema/runners.exsd new file mode 100644 index 00000000..e7703290 --- /dev/null +++ b/com.wudsn.ide.asm/schema/runners.exsd @@ -0,0 +1,144 @@ + + + + + + + + + This extension point allows for adding new runner, for example runners, to the WUDSN IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Optional implementation class to provide runner specific breakouts, for example for creating breakpoint files. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WUDSN IDE 1.3.0 + + + + + + + + + + + + (c) 2009 Peter Dell + + + + diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Actions.properties b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Actions.properties new file mode 100644 index 00000000..b9deb5e4 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Actions.properties @@ -0,0 +1,3 @@ +# Used by AssemblerEditor +com.wudsn.ide.asm.editor.ContentAssistProposal_label=Content Assist +com.wudsn.ide.asm.editor.AssemblerEditorToggleCommentCommand_label=Toggle Comment2 \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/AssemblerPlugin.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/AssemblerPlugin.java new file mode 100644 index 00000000..735bfd0d --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/AssemblerPlugin.java @@ -0,0 +1,305 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.jface.preference.JFacePreferences; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.osgi.framework.BundleContext; + +import com.wudsn.ide.asm.compiler.CompilerConsole; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesChangeListener; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesConstants; +import com.wudsn.ide.asm.runner.RunnerRegistry; +import com.wudsn.ide.base.common.AbstractIDEPlugin; + +/** + * The main plugin class to be used in the desktop. + * + * @author Peter Dell + */ +public final class AssemblerPlugin extends AbstractIDEPlugin { + + /** + * The plugin id. + */ + public static final String ID = "com.wudsn.ide.asm"; + + /** + * The shared instance. + */ + private static AssemblerPlugin plugin; + + /** + * The preferences. + */ + private AssemblerPreferences preferences; + private ListenerList preferencesChangeListeners; + + /** + * The compiler registry. + */ + private CompilerRegistry compilerRegistry; + + /** + * The compiler console. + */ + private CompilerConsole compilerConsole; + + /** + * The runner registry. + */ + private RunnerRegistry runnerRegistry; + + /** + * The UI properties. + */ + private Map properties; + + /** + * Creates a new instance. Must be public for dynamic instantiation. + */ + public AssemblerPlugin() { + preferences = null; + preferencesChangeListeners = new ListenerList(ListenerList.IDENTITY); + compilerRegistry = new CompilerRegistry(); + compilerConsole = null; + runnerRegistry = new RunnerRegistry(); + properties = new HashMap(10); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getPluginId() { + return ID; + } + + /** + * {@inheritDoc} + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + preferences = new AssemblerPreferences(getPreferenceStore()); + plugin = this; + try { + compilerRegistry.init(); + } catch (Exception ex) { + logError("Cannot initialize compiler registry", null, ex); + throw ex; + } + compilerConsole = new CompilerConsole(); + try { + runnerRegistry.init(); + } catch (Exception ex) { + logError("Cannot initialize runner registry", null, ex); + throw ex; + } + + // Register for global JFace preferences that also affect the editors. + JFacePreferences.getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() { + final static String BLOCK_SELECTION_MODE_FONT = "org.eclipse.ui.workbench.texteditor.blockSelectionModeFont"; + + @Override + public void propertyChange(PropertyChangeEvent event) { + if (event.getProperty().equals(JFaceResources.TEXT_FONT) + || event.getProperty().equals(BLOCK_SELECTION_MODE_FONT)) { + firePreferencesChangeEvent(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTES); + } + + } + }); + + } + + /** + * {@inheritDoc} + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + plugin = null; + } + + /** + * Gets the shared plugin instance. + * + * @return The plug-in, not null. + */ + public static AssemblerPlugin getInstance() { + if (plugin == null) { + throw new IllegalStateException("Plugin not initialized or already stopped"); + } + return plugin; + } + + /** + * Gets the compiler registry for this plugin. + * + * @return The compiler registry, not null. + */ + public CompilerRegistry getCompilerRegistry() { + if (compilerRegistry == null) { + throw new IllegalStateException("Field 'compilerRegistry' must not be null."); + } + return compilerRegistry; + } + + /** + * Gets the compiler console for this plugin. + * + * @return The compiler console, not null. + */ + public final CompilerConsole getCompilerConsole() { + if (compilerConsole == null) { + throw new IllegalStateException("Field 'compilerConsole' must not be null."); + } + return compilerConsole; + } + + /** + * Gets the runner registry for this plugin. + * + * @return The runner registry, not null. + */ + public RunnerRegistry getRunnerRegistry() { + if (runnerRegistry == null) { + throw new IllegalStateException("Field 'runnerRegistry' must not be null."); + } + return runnerRegistry; + } + + /** + * Gets the preferences for this plugin. + * + * @return The preferences, not null. + */ + public AssemblerPreferences getPreferences() { + if (preferences == null) { + throw new IllegalStateException("Field 'preferences' must not be null."); + } + return preferences; + } + + /** + * Adds a listener for immediate preferences changes. + * + * @param listener + * The listener, not null. + * @since 1.6.3 + */ + public void addPreferencesChangeListener(AssemblerPreferencesChangeListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Parameter 'listener' must not be null."); + } + preferencesChangeListeners.add(listener); + } + + /** + * Removes a listener for immediate preferences changes. + * + * @param listener + * The listener, not null. + * @since 1.6.3 + */ + public void removePreferencesChangeListener(AssemblerPreferencesChangeListener listener) { + if (listener == null) { + throw new IllegalArgumentException("Parameter 'listener' must not be null."); + } + preferencesChangeListeners.remove(listener); + } + + /** + * Fire the change events for all registered listeners. + * + * @param changedPropertyNames + * The set of property changed property names, not + * null. + * + * @since 1.6.3 + */ + public void firePreferencesChangeEvent(Set changedPropertyNames) { + if (changedPropertyNames == null) { + throw new IllegalArgumentException("Parameter 'changedPropertyNames' must not be null."); + } + if (!changedPropertyNames.isEmpty()) { + + for (Object listener : preferencesChangeListeners.getListeners()) { + ((AssemblerPreferencesChangeListener) listener).preferencesChanged(preferences, changedPropertyNames); + } + } + } + + /** + * Gets a UI property. + * + * @param key + * The property key, not null. + * + * @return The UI property, may be empty, not null. + */ + public String getProperty(QualifiedName key) { + if (key == null) { + throw new IllegalArgumentException("Parameter 'key' must not be null."); + } + + String result; + synchronized (properties) { + result = properties.get(key); + } + if (result == null) { + result = ""; + } + return result; + } + + /** + * Set a UI property. + * + * @param key + * The property key, not null. + * + * @param value + * The UI property, may be empty, not null. + */ + public void setProperty(QualifiedName key, String value) { + if (key == null) { + throw new IllegalArgumentException("Parameter 'key' must not be null."); + } + if (value == null) { + throw new IllegalArgumentException("Parameter 'value' must not be null."); + } + synchronized (properties) { + properties.put(key, value); + } + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/AssemblerProperties.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/AssemblerProperties.java new file mode 100644 index 00000000..ff051fc2 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/AssemblerProperties.java @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm; + +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.resources.IMarker; + +import com.wudsn.ide.base.common.StringUtility; + +public final class AssemblerProperties { + + /** + * A single key value pair. + * + * @since 1.6.1 + */ + public static final class AssemblerProperty { + public final String key; + public final String value; + public final int lineNumber; + + AssemblerProperty(String key, String value, int lineNumber) { + this.key = key; + this.value = value; + this.lineNumber = lineNumber; + } + + @Override + public String toString() { + return key + "=" + value + " in line " + lineNumber; + } + } + + @SuppressWarnings("serial") + public final static class InvalidAssemblerPropertyException extends Exception { + public final AssemblerProperty property; + public final IMarker marker; + + public InvalidAssemblerPropertyException(AssemblerProperty property, IMarker marker) { + if (property == null) { + throw new IllegalArgumentException("Parameter 'property' must not be null."); + } + this.property = property; + this.marker = marker; + } + } + + /** + * Source code constants. + */ + public final static String PREFIX = "@com.wudsn.ide.asm."; + public final static String HARDWARE = "@com.wudsn.ide.asm.hardware"; + public final static String MAIN_SOURCE_FILE = "@com.wudsn.ide.asm.mainsourcefile"; + public final static String OUTPUT_FOLDER_MODE = "@com.wudsn.ide.asm.outputfoldermode"; + public final static String OUTPUT_FOLDER = "@com.wudsn.ide.asm.outputfolder"; + public final static String OUTPUT_FILE_EXTENSION = "@com.wudsn.ide.asm.outputfileextension"; + public final static String OUTPUT_FILE = "@com.wudsn.ide.asm.outputfile"; + + private Map properties; + + /** + * Creation is public. + */ + public AssemblerProperties() { + properties = new TreeMap(); + } + + /** + * Puts a new value into the properties provided not other value is already + * there. + * + * @param key + * The property key, not empty and not null. + * @param value + * The property value, may be empty, not null. + * @param lineNumber + * The line number, a positive integer or 0 if the line number is + * undefined. + * @since 1.6.1 + */ + public void put(String key, String value, int lineNumber) { + if (key == null) { + throw new IllegalArgumentException("Parameter 'key' must not be null."); + } + if (StringUtility.isEmpty(key)) { + throw new IllegalArgumentException("Parameter 'key' must not be empty."); + } + if (value == null) { + throw new IllegalArgumentException("Parameter 'value' must not be null."); + } + if (lineNumber < 0l) { + throw new IllegalArgumentException("Parameter 'lineNumber' must not be negative. Specified value is " + + lineNumber + "."); + } + if (!properties.containsKey(key)) { + AssemblerProperty property = new AssemblerProperty(key, value, lineNumber); + properties.put(key, property); + } + } + + /** + * Gets a property from the properties map. + * + * @param key + * The property key, not empty and not null. + * @return The property or null if the property is not defined. + * + * @since 1.6.1 + */ + public AssemblerProperty get(String key) { + if (key == null) { + throw new IllegalArgumentException("Parameter 'key' must not be null."); + } + if (StringUtility.isEmpty(key)) { + throw new IllegalArgumentException("Parameter 'key' must not be empty."); + } + return properties.get(key); + } + + @Override + public String toString() { + return properties.toString(); + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/CPU.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/CPU.java new file mode 100644 index 00000000..2c0b38d5 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/CPU.java @@ -0,0 +1,31 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm; + +/** + * Enum for the supported CPUs. Used for restricting the visible instructions. + * + * @author Peter Dell + * + * @sinbce 1.6.1 + */ +public enum CPU { + + MOS6502, MOS6502_ILLEGAL, MOS65C02, MOS6502_DTV, MOS65816 +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Hardware.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Hardware.java new file mode 100644 index 00000000..7478f01a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Hardware.java @@ -0,0 +1,31 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm; + +/** + * Enum for the supported hardware platforms. Used for classifying compilers, + * emulators and converters. + * + * @author Peter Dell + * + */ +public enum Hardware { + + GENERIC, APPLE2, ATARI2600, ATARI7800, ATARI8BIT, C64, NES, TEST +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/HardwareUtility.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/HardwareUtility.java new file mode 100644 index 00000000..73b76cff --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/HardwareUtility.java @@ -0,0 +1,170 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm; + +import org.eclipse.jface.resource.ImageDescriptor; + +import com.wudsn.ide.asm.compiler.CompilerFileWriter; +import com.wudsn.ide.asm.compiler.writer.AppleFileWriter; + +/** + * Map value of {@link Hardware} to icon paths and descriptors. + * + * @author Peter Dell + * + */ +public final class HardwareUtility { + + /** + * Creation is private. + */ + private HardwareUtility() { + } + + /** + * Gets the default file extension for executable files on a hardware. + * + * @param hardware + * The hardware, not null. + * @return The default file extension, may be empty, not null. + * + * @since 1.6.1 + */ + public static String getDefaultFileExtension(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + switch (hardware) { + case APPLE2: + // AppleDos 3.3 binary file + // start-lo,start-hi,length-lo,length-hi,data + return ".b"; + case ATARI2600: + // Atari VCS ROM cartridge + return ".bin"; + case ATARI7800: + // Atari 7800 ROM cartridge + return ".bin"; + case ATARI8BIT: + // AtariDOS 2.5 compound file, + // $ff,$ff,start-lo,start-hi,end-lo,end-hi,data + return ".xex"; + case C64: + // C64 program file + // start-lo,start-hi,data + return ".prg"; + case NES: + // NES ROM file + return ".nes"; + case TEST: + return ".tst"; + default: + return ""; + } + } + + /** + * Gets the image path for a hardware image. + * + * @param hardware + * The hardware, not null. + * @return The image path for the hardware image, not empty and not + * null. + */ + public static String getImagePath(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + String path; + switch (hardware) { + case GENERIC: + path = "hardware-generic-16x16.gif"; + break; + case APPLE2: + path = "hardware-apple2-16x16.gif"; + break; + case ATARI2600: + path = "hardware-atari2600-16x16.gif"; + break; + case ATARI7800: + path = "hardware-atari7800-16x16.gif"; + break; + case ATARI8BIT: + path = "hardware-atari8bit-16x16.gif"; + break; + case C64: + path = "hardware-c64-16x16.gif"; + break; + case NES: + path = "hardware-nes-16x16.gif"; + break; + case TEST: + path = "hardware-test-16x16.gif"; + break; + default: + throw new IllegalArgumentException("Unknown hardware " + hardware + + "."); + } + return path; + } + + /** + * Gets the image descriptor for a hardware image. + * + * @param hardware + * The hardware, not null. + * @return The image descriptor for the hardware image, not + * null. + */ + public static ImageDescriptor getImageDescriptor(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + ImageDescriptor result; + result = AssemblerPlugin.getInstance().getImageDescriptor( + getImagePath(hardware)); + return result; + } + + /** + * Gets the compiler file writer a hardware. + * + * @param hardware + * The hardware, not null. + * @return The image descriptor for the hardware image, not + * null. + * @since 1.6.4 + */ + public static CompilerFileWriter getCompilerFileWriter(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + CompilerFileWriter compilerFileWriter; + if (hardware.equals(Hardware.APPLE2)) { + compilerFileWriter = new AppleFileWriter(); + } else { + compilerFileWriter = new CompilerFileWriter(); + } + return compilerFileWriter; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts.java new file mode 100644 index 00000000..931ac98b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts.java @@ -0,0 +1,288 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm; + +import org.eclipse.osgi.util.NLS; + +/** + * Class which holds the localized text constants. + * + * @author Peter Dell + */ +public final class Texts extends NLS { + + public static String TOC_ASSEMBLER_SYNTAX_YES; + public static String TOC_ASSEMBLER_SYNTAX_NO; + public static String TOC_ASSEMBLER_SYNTAX_BLOCK_DEFINITION_CHARACTERS; + public static String TOC_ASSEMBLER_SYNTAX_COMPLETION_PROPOSAL_AUTO_ACTIVATION_CHARACTERS; + public static String TOC_ASSEMBLER_SYNTAX_IDENTIFIER_PART_CHARACTERS; + public static String TOC_ASSEMBLER_SYNTAX_IDENTIFIER_SEPARATOR_CHARACTER; + public static String TOC_ASSEMBLER_SYNTAX_IDENTIFIER_START_CHARACTERS; + public static String TOC_ASSEMBLER_SYNTAX_IDENTIFIERS_CASE_SENSITIVE; + public static String TOC_ASSEMBLER_SYNTAX_INSTRUCTIONS_CASE_SENSITIVE; + public static String TOC_ASSEMBLER_SYNTAX_LABEL_DEFINITION_SUFFIX_CHARACTER; + public static String TOC_ASSEMBLER_SYNTAX_MACRO_USAGE_PREFIX_CHARACTER; + public static String TOC_ASSEMBLER_SYNTAX_MULTIPLE_LINES_COMMENT_DELIMITERS; + public static String TOC_ASSEMBLER_SYNTAX_SINGLE_LINE_COMMENT_DELIMITERS; + public static String TOC_ASSEMBLER_SYNTAX_SOURCE_INCLUDE_DEFAULT_EXTENSION; + public static String TOC_ASSEMBLER_SYNTAX_STRING_DELIMITERS; + + /** + * Compiler console. + */ + public static String COMPILER_CONSOLE_TITLE; + + /** + * Compiler source parser tree. + */ + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFAULT; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFINITION_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_IMPLEMENTATION_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_EQUATE_DEFINITION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LABEL_DEFINITION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_ENUM_DEFINITION_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_STRUCTURE_DEFINITION_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LOCAL_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_MACRO_DEFINITION_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PAGES_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PROCEDURE_DEFINITION_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_REPEAT_SECTION; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_SOURCE_INCLUDE; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_INCLUDE; + public static String COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_OUTPUT; + + /** + * Compiler syntax. + */ + public static String COMPILER_SYNTAX_INSTRUCTION_DIRECTIVE; + public static String COMPILER_SYNTAX_LEGAL_OPCODE; + public static String COMPILER_SYNTAX_ILLEGAL_OPCODE; + public static String COMPILER_SYNTAX_PSEUDO_OPCODE; + public static String COMPILER_SYNTAX_W65816_ONLY; + + /** + * Assembler toolbar and menu. + * + */ + public static String ASSEMBLER_TOOLBAR_RUN_WITH_DEFAULT_LABEL; + + /** + * Assembler content outline. + */ + public static String ASSEMBLER_CONTENT_OUTLINE_SORT_BUTTON_TOOL_TIP; + public static String ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFAULT; + public static String ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFINITION_SECTION; + public static String ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_IMPLEMENTATION_SECTION; + + /** + * Assembler hyperlink detector. + */ + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_SOURCE_WITH_ASSEMBLER_EDITOR; + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_HEX_EDITOR; + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_GRAPHICS_EDITOR; + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_DEFAULT_EDITOR; + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_SYSTEM_EDITOR; + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER; + public static String ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER_IN_INCLUDE; + public static String ASSEMBLER_HYPERLINK_FILE_NOT_EXISTS; + + /** + * Assembler breakpoints. + */ + public static String ASSEMBLER_BREAKPOINT_MARKER_MESSAGE; + public static String ASSEMBLER_BREAKPOINT_TOGGLE_TYPE_MENU_TEXT; + + /** + * Compiler symbols + */ + public static String COMPILER_SYMBOLS_VIEW_FILTER_TOOLTIP; + public static String COMPILER_SYMBOLS_VIEW_SOURCE_LABEL; + public static String COMPILER_SYMBOLS_VIEW_SOURCE_NONE; + public static String COMPILER_SYMBOLS_VIEW_SOURCE_TOTAL_COUNT; + public static String COMPILER_SYMBOLS_VIEW_SOURCE_FILTERED_COUNT; + public static String COMPILER_SYMBOLS_VIEW_TYPE_COLUMN_LABEL; + public static String COMPILER_SYMBOLS_VIEW_BANK_COLUMN_LABEL; + public static String COMPILER_SYMBOLS_VIEW_NAME_COLUMN_LABEL; + public static String COMPILER_SYMBOLS_VIEW_HEX_VALUE_COLUMN_LABEL; + public static String COMPILER_SYMBOLS_VIEW_DECIMAL_VALUE_COLUMN_LABEL; + public static String COMPILER_SYMBOLS_VIEW_STRING_VALUE_COLUMN_LABEL; + + /** + * Preferences: syntax highlighting. + */ + public static String PREFERENCES_SYNTAX_HIGHLIGHTING_GROUP_TITLE; + + public static String PREFERENCES_TEXT_ATTRIBUTE_COMMENT_NAME; + public static String PREFERENCES_TEXT_ATTRIBUTE_STRING_NAME; + public static String PREFERENCES_TEXT_ATTRIBUTE_NUMBER_NAME; + public static String PREFERENCES_TEXT_ATTRIBUTE_DIRECTIVE; + public static String PREFERENCES_TEXT_ATTRIBUTE_OPCODE_LEGAL_NAME; + public static String PREFERENCES_TEXT_ATTRIBUTE_OPCODE_ILLEGAL_NAME; + public static String PREFERENCES_TEXT_ATTRIBUTE_OPCODE_PSEUDO_NAME; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LABEL; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION; + public static String PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION; + + public static String PREFERENCES_FOREGROUND_COLOR_LABEL; + public static String PREFERENCES_BOLD_LABEL; + public static String PREFERENCES_ITALIC_LABEL; + + /** + * Preferences: editor content assist and parsing. + */ + public static String PREFERENCES_EDITOR_GROUP_TITLE; + + public static String PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LABEL; + public static String PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_UPPER_CASE_TEXT; + public static String PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LOWER_CASE_TEXT; + + public static String PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_LABEL; + public static String PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_OR_WARNING_TEXT; + public static String PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_TEXT; + + /** + * Preferences: compiler and runner + */ + public static String PREFERENCES_DOWNLOAD_LINK; + public static String PREFERENCES_DOWNLOAD_LINK_TOOL_TIP; + + /** + * Preferences: compiler. + */ + public static String PREFERENCES_COMPILER_CPU_LABEL; + + public static String PREFERENCES_COMPILER_EXECUTABLE_PATH_LABEL; + public static String PREFERENCES_COMPILER_HARDWARE_ACTIVE_LABEL; + public static String PREFERENCES_COMPILER_DEFAULT_PARAMETERS_LABEL; + public static String PREFERENCES_COMPILER_PARAMETERS_LABEL; + public static String PREFERENCES_COMPILER_PARAMETERS_HELP; + public static String PREFERENCES_COMPILER_PARAMETERS_VARIABLES; + + public static String PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_LABEL; + public static String PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_SOURCE_FOLDER_TEXT; + public static String PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_TEMP_FOLDER_TEXT; + public static String PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_FIXED_FOLDER_TEXT; + public static String PREFERENCES_COMPILER_OUTPUT_FOLDER_PATH_LABEL; + public static String PREFERENCES_COMPILER_OUTPUT_FILE_EXTENSION_LABEL; + + public static String PREFERENCES_COMPILER_RUNNER_ID_LABEL; + public static String PREFERENCES_COMPILER_RUNNER_EXECUTABLE_PATH_LABEL; + public static String PREFERENCES_COMPILER_RUNNER_DEFAULT_COMMAND_LINE_LABEL; + public static String PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_LABEL; + public static String PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_HELP; + public static String PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_VARIABLES; + public static String PREFERENCES_COMPILER_RUNNER_WAIT_FOR_COMPLETION_LABEL; + + /** + * Help table of contents + */ + public static String TOC_WUDSN_IDE_LABEL; + + public static String TOC_IDE_TOPIC_LABEL; + + public static String TOC_ASSEMBLERS_TOPIC_LABEL; + public static String TOC_ASSEMBLER_GENERAL_TOPIC_LABEL; + public static String TOC_ASSEMBLER_NAME_LABEL; + public static String TOC_ASSEMBLER_HOME_PAGE_LABEL; + public static String TOC_ASSEMBLER_DEFAULT_HARDWARE_LABEL; + public static String TOC_ASSEMBLER_SUPPORTED_CPUS_LABEL; + public static String TOC_ASSEMBLER_DEFAULT_PARAMETERS_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTIONS_TOPIC_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_TYPE_DIRECTIVES_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_TYPE_LEGAL_OPCODES_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_TYPE_PSEUDO_OPCODES_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_TYPE_ILLEGAL_OPCODES_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_TYPE_W65816_OPCODES_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_TYPE_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_NAME_LABEL; + public static String TOC_ASSEMBLER_INSTRUCTION_DESCRIPTION_LABEL; + public static String TOC_ASSEMBLER_MANUAL_TOPIC_LABEL; + + public static String TOC_HARDWARES_TOPIC_LABEL; + public static String TOC_HARDWARE_NAME_LABEL; + public static String TOC_HARDWARE_ID_LABEL; + public static String TOC_HARDWARE_ICON_LABEL; + public static String TOC_HARDWARE_DEFAULT_FILE_EXTENSION_LABEL; + public static String TOC_HARDWARE_EMULATOR_LABEL; + public static String TOC_HARDWARE_HOME_PAGE_LABEL; + public static String TOC_HARDWARE_DEFAULT_PARAMETERS_LABEL; + + public static String TOC_CPUS_TOPIC_LABEL; + public static String TOC_CPU_NAME_LABEL; + public static String TOC_CPU_OPCODE_LABEL; + + /** + * Messages for the Assembler plugin. + */ + public static String MESSAGE_E100; + public static String MESSAGE_E101; + public static String MESSAGE_E102; + public static String MESSAGE_E103; + public static String MESSAGE_E104; + public static String MESSAGE_E105; + public static String MESSAGE_E106; + public static String MESSAGE_E107; + public static String MESSAGE_E108; + public static String MESSAGE_I109; + public static String MESSAGE_I110; + public static String MESSAGE_E111; + public static String MESSAGE_E112; + public static String MESSAGE_E113; + public static String MESSAGE_E114; + public static String MESSAGE_E115; + public static String MESSAGE_E116; + public static String MESSAGE_E117; + public static String MESSAGE_I118; + public static String MESSAGE_E119; + public static String MESSAGE_W120; + public static String MESSAGE_I121; + public static String MESSAGE_E122; + public static String MESSAGE_E123; + public static String MESSAGE_E124; + public static String MESSAGE_E125; + public static String MESSAGE_E126; + public static String MESSAGE_E127; + public static String MESSAGE_E128; + public static String MESSAGE_E129; + public static String MESSAGE_E130; + public static String MESSAGE_E131; + public static String MESSAGE_E132; + public static String MESSAGE_E133; + public static String MESSAGE_E134; + public static String MESSAGE_E135; + public static String MESSAGE_E136; + public static String MESSAGE_E137; + public static String MESSAGE_E138; + public static String MESSAGE_E139; + public static String MESSAGE_E140; + public static String MESSAGE_E141; + public static String MESSAGE_E142; + public static String MESSAGE_S143; + /** + * Initializes the constants. + */ + static { + NLS.initializeMessages(Texts.class.getName(), Texts.class); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts.properties b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts.properties new file mode 100644 index 00000000..6dd13a77 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts.properties @@ -0,0 +1,206 @@ +COMPILER_CONSOLE_TITLE=Compiler Console + +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFAULT=Default +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFINITION_SECTION=Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_IMPLEMENTATION_SECTION=Implementation Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_EQUATE_DEFINITION=Equate Definition +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LABEL_DEFINITION=Label Definition +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_ENUM_DEFINITION_SECTION=Enum Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_STRUCTURE_DEFINITION_SECTION=Structure Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LOCAL_SECTION=Local Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_MACRO_DEFINITION_SECTION=Macro Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PAGES_SECTION=Pages Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PROCEDURE_DEFINITION_SECTION=Procedure Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_REPEAT_SECTION=Repeat Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_SOURCE_INCLUDE=Source Include +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_INCLUDE=Binary Include +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_OUTPUT=Binary Output + +COMPILER_SYNTAX_INSTRUCTION_DIRECTIVE=Directive +COMPILER_SYNTAX_LEGAL_OPCODE=Legal Opcode +COMPILER_SYNTAX_ILLEGAL_OPCODE=Illegal Opcode +COMPILER_SYNTAX_PSEUDO_OPCODE=Pseudo Opcode +COMPILER_SYNTAX_W65816_ONLY=W65816 only + +ASSEMBLER_TOOLBAR_RUN_WITH_DEFAULT_LABEL=(default) + +ASSEMBLER_CONTENT_OUTLINE_SORT_BUTTON_TOOL_TIP=Sort +ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFAULT=Default +ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFINITION_SECTION=Definition Section +ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_IMPLEMENTATION_SECTION=Implementation Section + +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_SOURCE_WITH_ASSEMBLER_EDITOR=Open with Assembler Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_HEX_EDITOR=Open with Hex Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_GRAPHICS_EDITOR=Open with Graphics Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_DEFAULT_EDITOR=Open with Default Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_SYSTEM_EDITOR=Open with System Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER={0} {1} in line {2} +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER_IN_INCLUDE={0} {1} in line {2} of {3} +ASSEMBLER_HYPERLINK_FILE_NOT_EXISTS=Target file '{0}' not exists. Do you want to create the file now? + +ASSEMBLER_BREAKPOINT_MARKER_MESSAGE={0} [line: {1}] - {2} +ASSEMBLER_BREAKPOINT_TOGGLE_TYPE_MENU_TEXT=Assembler Breakpoints + +COMPILER_SYMBOLS_VIEW_FILTER_TOOLTIP=Type one or more substrings separated by spaces to filter the result list +COMPILER_SYMBOLS_VIEW_SOURCE_LABEL=Source: +COMPILER_SYMBOLS_VIEW_SOURCE_NONE=None +COMPILER_SYMBOLS_VIEW_SOURCE_TOTAL_COUNT={0} symbols +COMPILER_SYMBOLS_VIEW_SOURCE_FILTERED_COUNT={0} of {1} symbols +COMPILER_SYMBOLS_VIEW_SOURCE_NONE=Symbols +COMPILER_SYMBOLS_VIEW_TYPE_COLUMN_LABEL=Type +COMPILER_SYMBOLS_VIEW_BANK_COLUMN_LABEL=Bank +COMPILER_SYMBOLS_VIEW_NAME_COLUMN_LABEL=Name +COMPILER_SYMBOLS_VIEW_HEX_VALUE_COLUMN_LABEL=Hex Value +COMPILER_SYMBOLS_VIEW_DECIMAL_VALUE_COLUMN_LABEL=Decimal Value +COMPILER_SYMBOLS_VIEW_STRING_VALUE_COLUMN_LABEL=String Value + +PREFERENCES_SYNTAX_HIGHLIGHTING_GROUP_TITLE=Syntax Highlighting + +PREFERENCES_TEXT_ATTRIBUTE_COMMENT_NAME=Comments +PREFERENCES_TEXT_ATTRIBUTE_STRING_NAME=Strings +PREFERENCES_TEXT_ATTRIBUTE_NUMBER_NAME=Numbers +PREFERENCES_TEXT_ATTRIBUTE_DIRECTIVE=Directives +PREFERENCES_TEXT_ATTRIBUTE_OPCODE_LEGAL_NAME=Opcodes +PREFERENCES_TEXT_ATTRIBUTE_OPCODE_ILLEGAL_NAME=Illegal Opcodes +PREFERENCES_TEXT_ATTRIBUTE_OPCODE_PSEUDO_NAME=Pseudo Opcodes +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE=Identifiers of Equates +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LABEL=Identifiers of Labels +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION=Identifiers of Enums +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION=Identifiers of Structures +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION=Identifiers of Local Sections +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION=Identifiers of Macros +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION=Identifiers of Procedures + +PREFERENCES_EDITOR_GROUP_TITLE=Editor + +PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LABEL=Default Case for Content Assist +PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LOWER_CASE_TEXT=Lower Case Instructions +PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_UPPER_CASE_TEXT=Upper Case Instructions + +PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_LABEL=Position after compiling +PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_OR_WARNING_TEXT=To first error or warning +PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_TEXT=To first error + + +PREFERENCES_FOREGROUND_COLOR_LABEL=&Color: +PREFERENCES_BOLD_LABEL=&Bold +PREFERENCES_ITALIC_LABEL=&Italic + +PREFERENCES_DOWNLOAD_LINK=Download +PREFERENCES_DOWNLOAD_LINK_TOOL_TIP=Open download page {0} + +PREFERENCES_COMPILER_CPU_LABEL=CP&U +PREFERENCES_COMPILER_EXECUTABLE_PATH_LABEL=Path to &Compiler +PREFERENCES_COMPILER_HARDWARE_ACTIVE_LABEL=Use for {0} +PREFERENCES_COMPILER_DEFAULT_PARAMETERS_LABEL=&Default Parameters +PREFERENCES_COMPILER_PARAMETERS_LABEL=&Parameters +PREFERENCES_COMPILER_PARAMETERS_HELP=Available variables are: +PREFERENCES_COMPILER_PARAMETERS_VARIABLES=${sourceFolderPath} The absolute path to the source folder\n${sourceFilePath} The absolute path to the source file\n${outputFolderPath} The absolute path to the output folder\n${outputFilePath} The absolute path to the output file\n${outputFilePathWithoutExtension} The absolute path to the output file without extension\n${outputFileName} The name of the output file including its extension\n${outputFileNameWithoutExtension} The name of the output file without extension\n${outputFileNameShortWithoutExtension} The name of the output file without extension shortened to 8 alphanumeric characters +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_LABEL=&Output Folder +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_SOURCE_FOLDER_TEXT=&Source Folder +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_TEMP_FOLDER_TEXT=&Temporary Folder +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_FIXED_FOLDER_TEXT=&Fixed Folder +PREFERENCES_COMPILER_OUTPUT_FOLDER_PATH_LABEL=&Output Folder +PREFERENCES_COMPILER_OUTPUT_FILE_EXTENSION_LABEL=Output File E&xtension +PREFERENCES_COMPILER_RUNNER_ID_LABEL=Default &Application to open Output File +PREFERENCES_COMPILER_RUNNER_EXECUTABLE_PATH_LABEL=Path to A&pplication +PREFERENCES_COMPILER_RUNNER_DEFAULT_COMMAND_LINE_LABEL=&Default Command Line +PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_LABEL=Command &Line +PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_HELP=Available variables are: +PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_VARIABLES=${runnerExecutablePath} The absolute path to the application\n${sourceFolderPath} The absolute path to the source folder\n${sourceFilePath} The absolute path to the source file\n${outputFolderPath} The absolute path to the output folder\n${outputFilePath} The absolute path to the output file\n${outputFilePathWithoutExtension} The absolute path to the output file without extension\n${outputFileName} The name of the output file including its extension\n${outputFileNameWithoutExtension} The name of the output file without extension\n${outputFileNameShortWithoutExtension} The name of the output file without extension shortened to 8 alphanumeric characters +PREFERENCES_COMPILER_RUNNER_WAIT_FOR_COMPLETION_LABEL=Wait for end of application + +TOC_WUDSN_IDE_LABEL=WUDSN IDE Guide + +TOC_IDE_TOPIC_LABEL=WUDSN IDE + +TOC_ASSEMBLERS_TOPIC_LABEL=Assemblers +TOC_ASSEMBLER_GENERAL_TOPIC_LABEL=General +TOC_ASSEMBLER_NAME_LABEL=Name +TOC_ASSEMBLER_HOME_PAGE_LABEL=Home Page +TOC_ASSEMBLER_DEFAULT_HARDWARE_LABEL=Default Hardware +TOC_ASSEMBLER_SUPPORTED_CPUS_LABEL=Supported CPUs +TOC_ASSEMBLER_DEFAULT_PARAMETERS_LABEL=Default Parameters +TOC_ASSEMBLER_INSTRUCTIONS_TOPIC_LABEL=Instructions +TOC_ASSEMBLER_INSTRUCTION_TYPE_DIRECTIVES_LABEL=Directives +TOC_ASSEMBLER_INSTRUCTION_TYPE_LEGAL_OPCODES_LABEL=Legal Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_PSEUDO_OPCODES_LABEL=Pseudo Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_ILLEGAL_OPCODES_LABEL=Illegal Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_W65816_OPCODES_LABEL=W65816 Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_LABEL=Type +TOC_ASSEMBLER_INSTRUCTION_NAME_LABEL=Name +TOC_ASSEMBLER_INSTRUCTION_DESCRIPTION_LABEL=Description +TOC_ASSEMBLER_SYNTAX_YES=Yes +TOC_ASSEMBLER_SYNTAX_NO=No +TOC_ASSEMBLER_SYNTAX_BLOCK_DEFINITION_CHARACTERS=Block Definition Characters +TOC_ASSEMBLER_SYNTAX_COMPLETION_PROPOSAL_AUTO_ACTIVATION_CHARACTERS=Completion Proposal Characters +TOC_ASSEMBLER_SYNTAX_IDENTIFIER_PART_CHARACTERS=Identifier Part Characters +TOC_ASSEMBLER_SYNTAX_IDENTIFIER_SEPARATOR_CHARACTER=Identifier Separator Character +TOC_ASSEMBLER_SYNTAX_IDENTIFIER_START_CHARACTERS=Identifier Start Characters +TOC_ASSEMBLER_SYNTAX_IDENTIFIERS_CASE_SENSITIVE=Identifiers Case Sensitive +TOC_ASSEMBLER_SYNTAX_INSTRUCTIONS_CASE_SENSITIVE=Instructions Case Sensitive +TOC_ASSEMBLER_SYNTAX_LABEL_DEFINITION_SUFFIX_CHARACTER=Label Definition Suffix Character +TOC_ASSEMBLER_SYNTAX_MACRO_USAGE_PREFIX_CHARACTER=Macro Usage Prefix Character +TOC_ASSEMBLER_SYNTAX_MULTIPLE_LINES_COMMENT_DELIMITERS=Multiple Lines Comment Delimiters +TOC_ASSEMBLER_SYNTAX_SINGLE_LINE_COMMENT_DELIMITERS=Single Line Comment Delimiters +TOC_ASSEMBLER_SYNTAX_SOURCE_INCLUDE_DEFAULT_EXTENSION=Source Include Default Extension +TOC_ASSEMBLER_SYNTAX_STRING_DELIMITERS=String Delimiters +TOC_ASSEMBLER_MANUAL_TOPIC_LABEL=Manual + +TOC_HARDWARES_TOPIC_LABEL=Hardwares +TOC_HARDWARE_NAME_LABEL=Hardware +TOC_HARDWARE_ID_LABEL=Identifier +TOC_HARDWARE_ICON_LABEL=Icon +TOC_HARDWARE_DEFAULT_FILE_EXTENSION_LABEL=Default File Extension +TOC_HARDWARE_EMULATOR_LABEL=Emulator +TOC_HARDWARE_HOME_PAGE_LABEL=Home Page +TOC_HARDWARE_DEFAULT_PARAMETERS_LABEL=Default Parameters + +TOC_CPUS_TOPIC_LABEL=CPUs +TOC_CPU_NAME_LABEL=CPU +TOC_CPU_OPCODE_LABEL=Opcode + +MESSAGE_E100=Path to '{0}' compiler executable is not set in the 'Assembler' preferences. +MESSAGE_E101=The compiler '{0}' does not specify default parameters. +MESSAGE_E102=The compiler '{0}' does not specify a help file path. +MESSAGE_E103=Path to '{0}' compiler executable in the preferences points to non-existing file '{1}'. +MESSAGE_E104=Output file extension must be set in the preferences of compiler '{0}' or via the annotation '{1}'. +MESSAGE_E105=Cannot execute compiler process '{0}' in working directory '{1}'. System error: {2} +MESSAGE_E106=Output file '{0}' cannot be opened for writing. End all applications which may keep the file open. +MESSAGE_E107=Output file not created. Check the error messages and the console log. +MESSAGE_E108=Output file not updated. Check the error messages and the console log. +MESSAGE_I109=Output file '{0}' created or updated with {1} (${2}) bytes. +MESSAGE_I110=Symbols file '{0}' created with {1} symbols. +MESSAGE_E111=Cannot open symbols file '{0}' for output. System error: {1} +MESSAGE_E112=Path to application executable is not set in the preferences of application '{0}'. +MESSAGE_E113=Cannot execute application '{0}' process '{1}' in working directory '{2}'. System error: {3} +MESSAGE_E114=Path to '{0}' application executable in the preferences points to non-existing file '{1}'. +MESSAGE_E115=Cannot open output file '{0}' with the standard application since no application is registered for the file extension '{1}'. +MESSAGE_E116=Definition for application '{0}' from the preferences of hardware '{1}' is not registered. +MESSAGE_E117=Cannot delete empty symbols file '{0}'. +MESSAGE_I118=Opening output file '{0}' with application '{1}'. +MESSAGE_E119=Cannot open output file '{0}' with application '{1}'. +MESSAGE_W120=Breakpoints will be ignored because the application '{0}' does not support passing source level breakpoints. +MESSAGE_I121=Breakpoints file '{0}' created with {1} active breakpoints. +MESSAGE_E122=Cannot open breakpoints file '{0}' for output. System error: {1} +MESSAGE_E123=Cannot delete empty breakpoints file '{0}'. +MESSAGE_E124=Unknown hardware '{0}'. Specify one of the following valid values '{1}'. +MESSAGE_E125=Main source file '{0}' does not exist. +MESSAGE_E126=Output file created but empty. Check the error messages and the console log. +MESSAGE_E127=Compiler process ended with return code {0}. Check the error messages and the console log. +MESSAGE_E128=Hardware not specified. Specify one of the following valid values '{0}'. +MESSAGE_E129=Main source file specifies or defaults to hardware '{0}' while include file specifies or defaults to hardware '{1}'. +MESSAGE_E130=Help for the '{0}' compiler cannot be displayed because the path to the compiler executable is not set in the preferences. +MESSAGE_E131=Help for the '{0}' compiler cannot be displayed because no help file was found in the paths '{1}' relative to the compiler executable path '{0}'. +MESSAGE_E132=Disk image file '{0}' does not exist. Create a bootable disk image where the output file '{1}' can be stored. +MESSAGE_E133=Disk image file '{0}' is not writeable. Make the disk image file is writeable, so the output file '{1}' can be stored. +MESSAGE_E134=Disk image file '{0}' cannot be opened for reading. System error: {1} +MESSAGE_E135=Disk image file '{0}' does not contain a valid file system. Make sure the disk image is properly formatted, so the output file '{1}' can be stored. +MESSAGE_E136=Disk image file '{0}' is full. System error: {1} +MESSAGE_E137=Disk image file '{0}' cannot be opened for writing. System error: {1} +MESSAGE_E138=Output file {0} has an unsupported file extension or invalid content. Supported file extensions are ".b" (Apple II binary file with format [start, length, code]), ".prg" (C64 program file with format [start, code]) and ".xex" (Atari compound file with format [$ffff, start,end,code]). +MESSAGE_E139=Output file extension '{0}' must start with ".". +MESSAGE_E140=Output folder mode be set in the preferences of compiler '{0}' or via the annotation '{1}'. +MESSAGE_E141=Unknown output folder mode '{0}'. Specify one of the following valid values '{1}'. +MESSAGE_E142=Include statement for file '{0}' uses a file name that has a different case different from real file system name '{1}'. Correct the file name in the include statement. +MESSAGE_S143=In include file '{0}', line {1}. \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts_de_DE.properties b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts_de_DE.properties new file mode 100644 index 00000000..ad857909 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/Texts_de_DE.properties @@ -0,0 +1,204 @@ +COMPILER_CONSOLE_TITLE=Kompiler Konsole + +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFAULT=Default +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFINITION_SECTION=Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_IMPLEMENTATION_SECTION=Implementation Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_EQUATE_DEFINITION=Equate Definition +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LABEL_DEFINITION=Label Definition +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_ENUM_DEFINITION_SECTION=Enum Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_STRUCTURE_DEFINITION_SECTION=Structure Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LOCAL_SECTION=Local Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_MACRO_DEFINITION_SECTION=Macro Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PAGES_SECTION=Pages Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PROCEDURE_DEFINITION_SECTION=Procedure Definition Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_REPEAT_SECTION=Repeat Section +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_SOURCE_INCLUDE=Source Include +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_INCLUDE=Binary Include +COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_OUTPUT=Binary Output + +COMPILER_SYNTAX_INSTRUCTION_DIRECTIVE=Direktive +COMPILER_SYNTAX_LEGAL_OPCODE=Legaler Opcode +COMPILER_SYNTAX_ILLEGAL_OPCODE=Illegaler Opcode +COMPILER_SYNTAX_PSEUDO_OPCODE=Pseudo Opcode +COMPILER_SYNTAX_W65816_ONLY=nur W65816 + +ASSEMBLER_TOOLBAR_RUN_WITH_DEFAULT_LABEL=(Standard) + +ASSEMBLER_CONTENT_OUTLINE_SORT_BUTTON_TOOL_TIP=Sortieren +ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFAULT=Standard +ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFINITION_SECTION=Definitionsabschnitt +ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_IMPLEMENTATION_SECTION=Implementierungsabschnitt + +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_SOURCE_WITH_ASSEMBLER_EDITOR=Öffnen mit Assembler Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_HEX_EDITOR=Öffnen mit Hex Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_GRAPHICS_EDITOR=Öffnen mit Grafik Editor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_DEFAULT_EDITOR=Öffnen mit Standardeditor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_SYSTEM_EDITOR=Öffnen mit Systemeditor +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER={0} {1} in Zeile {2} +ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER_IN_INCLUDE={0} {1} in Zeile {2} von {3} +ASSEMBLER_HYPERLINK_FILE_NOT_EXISTS=Ziel-Datei '{0}' existiert nicht. Möchten Sie die Ziel-Datei nun anlegen? + +ASSEMBLER_BREAKPOINT_MARKER_MESSAGE={0} [Zeile: {1}] - {2} +ASSEMBLER_BREAKPOINT_TOGGLE_TYPE_MENU_TEXT=Assembler Breakpoints + +COMPILER_SYMBOLS_VIEW_FILTER_TOOLTIP=Geben sie eine oder mehrere Teilzeichenfolgen durch Leerzeichen getrennt an +COMPILER_SYMBOLS_VIEW_SOURCE_LABEL=Quelle: +COMPILER_SYMBOLS_VIEW_SOURCE_NONE=Keine +COMPILER_SYMBOLS_VIEW_SOURCE_TOTAL_COUNT={0} Symbole +COMPILER_SYMBOLS_VIEW_SOURCE_FILTERED_COUNT={0} von {1} Symbolen +COMPILER_SYMBOLS_VIEW_TYPE_COLUMN_LABEL=Typ +COMPILER_SYMBOLS_VIEW_BANK_COLUMN_LABEL=Bank +COMPILER_SYMBOLS_VIEW_NAME_COLUMN_LABEL=Name +COMPILER_SYMBOLS_VIEW_HEX_VALUE_COLUMN_LABEL=Hexadezimalwert +COMPILER_SYMBOLS_VIEW_DECIMAL_VALUE_COLUMN_LABEL=Dezimalwert +COMPILER_SYMBOLS_VIEW_STRING_VALUE_COLUMN_LABEL=Textwert + +PREFERENCES_SYNTAX_HIGHLIGHTING_GROUP_TITLE=Syntax Hervorhebung + +PREFERENCES_TEXT_ATTRIBUTE_COMMENT_NAME=Kommentare +PREFERENCES_TEXT_ATTRIBUTE_STRING_NAME=Textkonstanten +PREFERENCES_TEXT_ATTRIBUTE_NUMBER_NAME=Zahlen +PREFERENCES_TEXT_ATTRIBUTE_DIRECTIVE=Direktive +PREFERENCES_TEXT_ATTRIBUTE_OPCODE_LEGAL_NAME=Opcodes +PREFERENCES_TEXT_ATTRIBUTE_OPCODE_ILLEGAL_NAME=Illegale Opcodes +PREFERENCES_TEXT_ATTRIBUTE_OPCODE_PSEUDO_NAME=Pseudo Opcodes +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE=Namen von Equates +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LABEL=Namen von Labels +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION=Namen von Enums +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION=Namen von von Strukturen +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION=Namen von Lokalen Bereichen +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION=Namen von Macros +PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION=Namen von Prozeduren + +PREFERENCES_EDITOR_GROUP_TITLE=Editor + +PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LABEL=Standard Schreibweise für Befehlsvorschläge +PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LOWER_CASE_TEXT=Kleingeschriebene Befehle +PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_UPPER_CASE_TEXT=Großgeschriebene Befehle + +PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_LABEL=Positionieren nach Kompilieren +PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_OR_WARNING_TEXT=Auf ersten Fehler oder erste Warnung +PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_TEXT=Auf ersten Fehler + +PREFERENCES_FOREGROUND_COLOR_LABEL=&Farbe: +PREFERENCES_BOLD_LABEL=F&ett +PREFERENCES_ITALIC_LABEL=&Kursiv + +PREFERENCES_DOWNLOAD_LINK=Download +PREFERENCES_DOWNLOAD_LINK_TOOL_TIP=Webseite {0} für Download öffnen + +PREFERENCES_COMPILER_CPU_LABEL=CP&U +PREFERENCES_COMPILER_EXECUTABLE_PATH_LABEL=Pfad zum &Kompiler +PREFERENCES_COMPILER_HARDWARE_ACTIVE_LABEL=Verwenden für {0} +PREFERENCES_COMPILER_DEFAULT_PARAMETERS_LABEL=&Standardparameter +PREFERENCES_COMPILER_PARAMETERS_LABEL=&Parameter +PREFERENCES_COMPILER_PARAMETERS_HELP=Verfügbare Variablen sind: +PREFERENCES_COMPILER_PARAMETERS_VARIABLES=${sourceFolderPath} Absoluter Pfad zum Quell-Ordner\n${sourceFilePath} Absoluter Pfad zur Quell-Datei\n${outputFolderPath} Absoluter Pfad zum Ausgabe-Ordner\n${outputFilePath} Absoluter Pfad der Ausgabe-Datei\n${outputFilePathWithoutExtension} Absoluter Pfad der Ausgabe-Datei ohne ihre Dateierweiterung\n${outputFileName} Name der Ausgabe-Datei inklusive ihrer Dateierweiterung\n${outputFileNameWithoutExtension} Name der Ausgabe-Datei ohne ihre Dateierweiterung\n${outputFileNameShortWithoutExtension} Name der Ausgabe-Datei ohne ihre Dateierweiterung, gekürzt auf 8 alpha-numerische Zeichen +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_LABEL=&Ausgabe-Ordner +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_SOURCE_FOLDER_TEXT=&Quell-Ordner +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_TEMP_FOLDER_TEXT=&Temporärer Ordner +PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_FIXED_FOLDER_TEXT=&Fester Ordner +PREFERENCES_COMPILER_OUTPUT_FOLDER_PATH_LABEL=Ausgabe-O&rdner +PREFERENCES_COMPILER_OUTPUT_FILE_EXTENSION_LABEL=Ausgabe-&Dateierweiterung +PREFERENCES_COMPILER_RUNNER_ID_LABEL=Standardan&wendung zum Öffnen der Ausgabe-Datei +PREFERENCES_COMPILER_RUNNER_EXECUTABLE_PATH_LABEL=P&fad zur Anwendung +PREFERENCES_COMPILER_RUNNER_DEFAULT_COMMAND_LINE_LABEL=S&tandard-Kommandozeile +PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_LABEL=Kommando&zeile +PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_HELP=Verfügbare Variablen sind: +PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_VARIABLES=${runnerExecutablePath} Absoluter Pfad zur Anwendung\n${sourceFolderPath} Absoluter Pfad zum Quell-Ordner\n${sourceFilePath} Absoluter Pfad zur Quell-Datei\n${outputFolderPath} Absoluter Pfad zum Ausgabe-Ordner\n${outputFilePath} Absoluter Pfad der Ausgabe-Datei\n${outputFilePathWithoutExtension} Absoluter Pfad der Ausgabe-Datei ohne ihre Dateierweiterung\n${outputFileName} Name der Ausgabe-Datei inklusive ihrer Dateierweiterung\n${outputFileNameWithoutExtension} Name der Ausgabe-Datei ohne ihre Dateierweiterung\n${outputFileNameShortWithoutExtension} Name der Ausgabe-Datei ohne ihre Dateierweiterung, gekürzt auf 8 alpha-numerische Zeichen +PREFERENCES_COMPILER_RUNNER_WAIT_FOR_COMPLETION_LABEL=Auf Ende der Anwendung warten + +TOC_WUDSN_IDE_LABEL=WUDSN IDE Handbuch + +TOC_IDE_TOPIC_LABEL=WUDSN IDE + +TOC_ASSEMBLERS_TOPIC_LABEL=Assembler +TOC_ASSEMBLER_GENERAL_TOPIC_LABEL=Allgemein +TOC_ASSEMBLER_NAME_LABEL=Name +TOC_ASSEMBLER_HOME_PAGE_LABEL=Home Page +TOC_ASSEMBLER_DEFAULT_HARDWARE_LABEL=Standardhardware +TOC_ASSEMBLER_SUPPORTED_CPUS_LABEL=Unterstütze CPUs +TOC_ASSEMBLER_DEFAULT_PARAMETERS_LABEL=Standardparameter +TOC_ASSEMBLER_INSTRUCTIONS_TOPIC_LABEL=Befehle +TOC_ASSEMBLER_INSTRUCTION_TYPE_DIRECTIVES_LABEL=Direktiven +TOC_ASSEMBLER_INSTRUCTION_TYPE_LEGAL_OPCODES_LABEL=Legale Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_PSEUDO_OPCODES_LABEL=Pseudo Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_ILLEGAL_OPCODES_LABEL=Illegale Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_W65816_OPCODES_LABEL=W65816 Opcodes +TOC_ASSEMBLER_INSTRUCTION_TYPE_LABEL=Typ +TOC_ASSEMBLER_INSTRUCTION_NAME_LABEL=Name +TOC_ASSEMBLER_INSTRUCTION_DESCRIPTION_LABEL=Beschreibung +TOC_ASSEMBLER_SYNTAX_YES=Ja +TOC_ASSEMBLER_SYNTAX_NO=Nein +TOC_ASSEMBLER_SYNTAX_BLOCK_DEFINITION_CHARACTERS=Zeichen für Block Definitionen +TOC_ASSEMBLER_SYNTAX_COMPLETION_PROPOSAL_AUTO_ACTIVATION_CHARACTERS=Zeichen für Vervollständigungsvorschlag +TOC_ASSEMBLER_SYNTAX_IDENTIFIER_PART_CHARACTERS=Zeichen für Namen +TOC_ASSEMBLER_SYNTAX_IDENTIFIER_SEPARATOR_CHARACTER=Trennzeichen für Namen +TOC_ASSEMBLER_SYNTAX_IDENTIFIER_START_CHARACTERS=Anfangszeichen für Namen +TOC_ASSEMBLER_SYNTAX_IDENTIFIERS_CASE_SENSITIVE=Namen unterscheiden Groß-/Kleinschreibung +TOC_ASSEMBLER_SYNTAX_INSTRUCTIONS_CASE_SENSITIVE=Instruktionen unterscheiden Groß-/Kleinschreibung +TOC_ASSEMBLER_SYNTAX_LABEL_DEFINITION_SUFFIX_CHARACTER=Suffixzeichen für Label Definitionen +TOC_ASSEMBLER_SYNTAX_MACRO_USAGE_PREFIX_CHARACTER=Zeichen für Verwendung von Makros +TOC_ASSEMBLER_SYNTAX_MULTIPLE_LINES_COMMENT_DELIMITERS=Trennzeichen für mehrzeilige Kommentare +TOC_ASSEMBLER_SYNTAX_SINGLE_LINE_COMMENT_DELIMITERS=Trennzeichen für einzeilige Kommentare +TOC_ASSEMBLER_SYNTAX_SOURCE_INCLUDE_DEFAULT_EXTENSION=Standard Datei-Erweiterung für Quell-Dateien +TOC_ASSEMBLER_SYNTAX_STRING_DELIMITERS=Trennzeichen für Textkonstanten +TOC_ASSEMBLER_MANUAL_TOPIC_LABEL=Handbuch + +TOC_HARDWARES_TOPIC_LABEL=Hardware Platformen +TOC_HARDWARE_NAME_LABEL=Hardware +TOC_HARDWARE_ID_LABEL=Identifikation +TOC_HARDWARE_ICON_LABEL=Ikone +TOC_HARDWARE_DEFAULT_FILE_EXTENSION_LABEL=Standard-Dateierweiterung +TOC_HARDWARE_EMULATOR_LABEL=Emulator +TOC_HARDWARE_HOME_PAGE_LABEL=Home Page +TOC_HARDWARE_DEFAULT_PARAMETERS_LABEL=Standardparameter + +TOC_CPUS_TOPIC_LABEL=CPUs +TOC_CPU_NAME_LABEL=CPU +TOC_CPU_OPCODE_LABEL=Opcode + +MESSAGE_E100=Pfad zur ausführbaren Datei des Kompilers '{0}' ist in den 'Assembler' Voreinstellungen nicht angegeben. +MESSAGE_E101=Der Kompiler '{0}' definiert keine Standardparameter. +MESSAGE_E102=Der Kompiler '{0}' getHelpFile keine Pfade zu Hilfe-Dateien. +MESSAGE_E103=Pfad zur ausführbaren Datei des Kompilers '{0}' in den Voreinstellungen verweist auf die nicht existierende Datei '{1}'. +MESSAGE_E104=Ausgabe-Dateierweiterung muss in den Voreinstellungen des Kompilers '{0}' oder mit der Annotation '{1}' angegeben werden. +MESSAGE_E105=Kompilerprozess '{0}' kann im Arbeitsverzeichnis '{1}' nicht ausgeführt werden. Systemfehler: {2} +MESSAGE_E106=Ausgabe-Datei '{0}' kann nicht zum Schreiben geöffnet werden. Beenden Sie alle Anwendungen welche die Datei geöffnet haben könnten. +MESSAGE_E107=Ausgabe-Datei wurde nicht erzeugt. Prüfen Sie die Fehlermeldungen und die Konsolenausgabe. +MESSAGE_E108=Ausgabe-Datei wurde nicht aktualisiert. Prüfen Sie die Fehlermeldungen und die Konsolenausgabe. +MESSAGE_I109=Ausgabe-Datei '{0}' mit einer Größe von {1} (${2}) Bytes erzeugt. +MESSAGE_I110=Symbol-Datei '{0}' mit {1} Symbolen erzeugt. +MESSAGE_E111=Symbol-Datei '{0}' kann nicht zum Schreiben geöffnet werden. Systemfehler: {1} +MESSAGE_E112=Pfad zur ausführbaren Datei des Emulators ist in den Voreinstellungen des Emulators '{0}' nicht angegeben. +MESSAGE_E113=Prozess '{1}' for den Emulator '{0}' kann im Arbeitsverzeichnis '{1}' nicht ausgeführt werden. Systemfehler: {2} +MESSAGE_E114=Pfad zur ausführbaren Datei des Anwendung '{0}' in der Voreinstellungen verweist auf die nicht existierende Datei '{1}'. +MESSAGE_E115=Ausgabe-Datei '{0}' kann nicht geöffnet werden, da keine Standardanwendung für die Dateierweiterung '{1}' registiert ist. +MESSAGE_E116=Definition für die Anwendung '{0}' aus den Voreinstellungen für die Hardware '{1}' ist nicht registriert. +MESSAGE_E117=Symbol-Datei '{0}' ist leer, aber kann nicht gelöscht werden. +MESSAGE_I118=Ausgabe-Datei '{0}' wird mit Anwendung '{1}' geöffnet. +MESSAGE_E119=Ausgabe-Datei '{0}' kann nicht mit Anwendung '{1}' geöffnet werden. +MESSAGE_W120=Unterbrechungspunkte werden ignoriert, da die Anwendung '{0}' die Übergabe von Quelltext-Unterbrechungspunkte nicht unterstützt. +MESSAGE_I121=Unterbrechungspunkte-Datei '{0}' mit {1} aktiven Unterbrechungspunkten erzeugt. +MESSAGE_E122=Unterbrechungspunkte-Datei '{0}' kann nicht zum Schreiben geöffnet werden. Systemfehler: {1} +MESSAGE_E123=Unterbrechungspunkte-Datei '{0}' ist leer, aber kann nicht gelöscht werden. +MESSAGE_E124=Unbekannte Hardware '{0}'. Geben Sie einen der folgenden gültigen Werte an '{1}'. +MESSAGE_E125=Haupt-Quell-Datei '{0}' existiert nicht. +MESSAGE_E126=Ausgabe-Datei erzeugt is aber leer. Prüfen Sie die Fehlermeldungen und die Konsolenausgabe. +MESSAGE_E127=Kompilerprozess endete mit dem Rückgabewert {0}. Prüfen Sie die Fehlermeldungen und die Konsolenausgabe. +MESSAGE_E128=Hardware nicht angegeben. Gültige Werte sind '{0}'. +MESSAGE_E129=Haupt-Quell-Datei spezifiziert oder verwendet die Hardware '{0}' während die Include-Quell-Datei die Hardware '{1}' spezifiziert oder verwendet. +MESSAGE_E130=Handbuch zum Kompiler '{0}' kann nicht angezeigt werden, da der Pfad zur ausführbaren Datei des Kompilers in den Voreinstellungen nicht angegeben ist. +MESSAGE_E131=Handbuch zum Kompiler '{0}' kann nicht angezeigt werden, da keine Hilfe-Datei in den Pfaden '{1}' relativ zur ausführbaren Datei '{2}' des Kompilers gefunden wurde. +MESSAGE_E132=Disketten-Image-Datei '{0}' existiert nicht. Lege ein bootfähiges Disketten-Image an, auf dem die Ausgabe-Datei '{1}' gespeichert werden kann. +MESSAGE_E133=Disketten-Image-Datei '{0}' ist nicht schreibbar. Mache das Diskettenimage schreibbbar, damit die Ausgabe-Datei '{1}' gespeichert werden kann. +MESSAGE_E134=Disketten-Image-Datei '{0}' kann not nicht zum Lesen geöffnet werden. Systemfehler: {1} +MESSAGE_E135=Disketten-Image-Datei '{0}' enthält kein gültiges Dateisystem. Stelle sicher, dass das Diskettenimage korrekt formatiert ist, damit die Ausgabe-Datei '{1}' gespeichert werden kann. +MESSAGE_E136=Disketten-Image-Datei '{0}' ist voll. Systemfehler: {1} +MESSAGE_E137=Disketten-Image-Datei '{0}' kann nicht zum Schreiben geöffnet werden. Systemfehler: {1} +MESSAGE_E138=Ausgabe-Datei '{0}' has eine nicht unterstütze Dateierweiterunge oder ungültigen Inhalt. Unterstützte Dateierweiterungen sind ".b" (Apple II Binär-Datei im Format [start, length, code]), ".prg" (C64 Programm-Datei im Format [start, code]) und ".xex" (Atari Compound-Datei im Format [$ffff, start,end,code]). +MESSAGE_E139=Ausgabe-Dateierweiterung '{0}' muss mit "." beginnen. +MESSAGE_E140=Ausgabe-Ordnermodus muss in den Voreinstellungen des Kompilers '{0}' oder mit der Annotation '{1}' angegeben werden. +MESSAGE_E141=Unbekannter Ausgabe-Ordnermodus '{0}'. Geben Sie einen der folgenden gültigen Werte an '{1}'. +MESSAGE_E142=Include Anweisung für die Datei '{0}' verwendet im Dateinamen eine andere Groß-Kleinschreibung als der echte Dateiname {1} auf dem Dateisystem. Korrigieren Sie den Dateinamen in der Include Anweisung. +MESSAGE_S143=In Include-Datei '{0}', Zeile {1}. \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/Compiler.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/Compiler.java new file mode 100644 index 00000000..671ef52f --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/Compiler.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; + +/** + * Base class for compiler implementations. Sub classes have to be stateless. + * + * @author Peter Dell + */ +public abstract class Compiler { + + // See {@link CompilerId} for predefined ids. + private CompilerDefinition definition; + + /** + * Creation is protected. + */ + protected Compiler() { + + } + + /** + * Sets the definition of the compiler. Called by {@link CompilerRegistry} + * only. + * + * @param definition + * The definition if the compiler, not null. + */ + final void setDefinition(CompilerDefinition definition) { + if (definition == null) { + throw new IllegalArgumentException("Parameter 'type' must not be null."); + } + this.definition = definition; + } + + /** + * Gets the definition of the compiler. + * + * @return The definition of the compiler, not null. + */ + public final CompilerDefinition getDefinition() { + if (definition == null) { + throw new IllegalStateException("Field 'definition' must not be null."); + } + return definition; + } + + /** + * Creates a compiler source parser. + * + * @return The compiler source parser, not null. + */ + public abstract CompilerSourceParser createSourceParser(); + + /** + * Checks if the exit code of the compiler process represents success. By + * default 0 is interpreted as success, but a compiler may + * override this. + * + * @param exitValue + * The exit code of the compiler process. + * @return true if the exit code represents success (only + * information and warning messages) or a failure (at least one error + * message). + * + * @since 1.7.0 + */ + public boolean isSuccessExitValue(int exitValue) { + return exitValue == 0; + } + + /** + * Creates the parser to for the compiler output. + * + * @return The parser to for the compiler output, not null. + */ + public abstract CompilerProcessLogParser createLogParser(); + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/Compiler.xml b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/Compiler.xml new file mode 100644 index 00000000..9ca3c1ab --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/Compiler.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerConsole.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerConsole.java new file mode 100644 index 00000000..adcfa0da --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerConsole.java @@ -0,0 +1,102 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import java.io.PrintStream; + +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; +import org.eclipse.ui.console.IConsoleManager; +import org.eclipse.ui.console.IConsoleView; +import org.eclipse.ui.console.MessageConsole; +import org.eclipse.ui.console.MessageConsoleStream; + +import com.wudsn.ide.asm.Texts; + +/** + * The console to show the user the output from the compiler. + * + * @author Peter Dell + * @author Daniel Mitte + */ +public final class CompilerConsole { + + private IConsoleManager consoleManager; + public MessageConsole console; + + private MessageConsoleStream messageStream; + private PrintStream printStream; + + /** + * Create a new console-window. + * + */ + public CompilerConsole() { + consoleManager = ConsolePlugin.getDefault().getConsoleManager(); + console = new MessageConsole(Texts.COMPILER_CONSOLE_TITLE, null); + consoleManager.addConsoles(new IConsole[] { console }); + + messageStream = console.newMessageStream(); + messageStream.setActivateOnWrite(false); + messageStream.print(""); + printStream = new PrintStream(messageStream); + } + + /** + * Brings this console view instance to front in the console view editor + * part. + * + * @param consoleView + * The console view editor part, not null. + */ + + public void display(IConsoleView consoleView) { + if (consoleView == null) { + throw new IllegalArgumentException( + "Parameter 'consoleView' must not be null."); + } + consoleView.display(console); + + } + + /** + * Add a line to console. + * + * @param message + * The message to print, not null. + */ + public void println(String message) { + if (message == null) { + throw new IllegalArgumentException( + "Parameter 'message' must not be null."); + } + messageStream.println(message); + } + + /** + * Gets a print messageStream to write to this console. + * + * @return The print messageStream, not null. + */ + public PrintStream getPrintStream() { + return printStream; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerDefinition.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerDefinition.java new file mode 100644 index 00000000..ec6cb7e3 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerDefinition.java @@ -0,0 +1,436 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.StringUtility; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Definition of a compiler. The definition contains all static meta information + * about the compiler. It is normally defined via an extension. The id of a + * compiler must be unique across all compilers. + * + * @author Peter Dell + */ +public final class CompilerDefinition implements Comparable { + + // Id + private String id; + private String name; + private String className; + + // Installation and use. + private String helpFilePaths; + private String homePageURL; + + // Editing and source parsing. + private List supportedCPUs; + private CompilerSyntax syntax; + + // Compiling. + private String defaultParameters; + + // Assignment to default hardware type. + private Hardware defaultHardware; + + /** + * Creation is package local. Called by {@link CompilerRegistry} only. + */ + CompilerDefinition() { + + } + + /** + * Sets the id of the compiler. Called by {@link CompilerRegistry} only. + * + * @param id + * The id of the compiler, not empty and not null. + */ + final void setId(String id) { + if (id == null) { + throw new IllegalArgumentException("Parameter 'id' must not be null."); + } + if (StringUtility.isEmpty(id)) { + throw new IllegalArgumentException("Parameter 'id' must not be empty."); + } + this.id = id; + } + + /** + * Gets the id of the compiler. + * + * @return The id of the compiler, not empty and not null. + */ + public final String getId() { + if (id == null) { + throw new IllegalStateException("Field 'id' must not be null."); + } + return id; + } + + /** + * Sets the localized name of the compiler. Called by + * {@link CompilerRegistry} only. + * + * @param name + * The localized name of the compiler, not empty and not + * null. + */ + final void setName(String name) { + if (name == null) { + throw new IllegalArgumentException("Parameter 'name' must not be null."); + } + if (StringUtility.isEmpty(id)) { + throw new IllegalArgumentException("Parameter 'name' must not be empty."); + } + this.name = name; + } + + /** + * Gets the localized name of the compiler. + * + * @return The localized name of the compiler, not empty and not + * null. + */ + public final String getName() { + if (name == null) { + throw new IllegalStateException("Field 'name' must not be null."); + } + return name; + } + + /** + * Sets the class name of the compiler. Called by {@link CompilerRegistry} + * only. + * + * @param className + * The class name of the compiler, not empty and not + * null. + */ + final void setClassName(String className) { + if (className == null) { + throw new IllegalArgumentException("Parameter 'className' must not be null."); + } + this.className = className; + } + + /** + * Gets the class name of the compiler. + * + * @return The class name of the compiler, not empty and not + * null. + */ + public final String getClassName() { + if (className == null) { + throw new IllegalStateException("Field 'className' must not be null."); + } + return className; + } + + /** + * Sets the absolute URL of the home page where the compiler can be + * downloaded. Called by {@link CompilerRegistry} only. + * + * @param homePageURL + * The absolute URL of the home page where the compiler can be + * downloaded. May be empty or null. + */ + final void setHomePageURL(String homePageURL) { + if (homePageURL == null) { + homePageURL = ""; + } + this.homePageURL = homePageURL; + } + + /** + * Gets the absolute URL of the home page where the compiler can be + * downloaded. + * + * @return The absolute URL of the home page where the compiler can be + * downloaded. The result may be empty or null. + */ + public final String getHomePageURL() { + if (homePageURL == null) { + homePageURL = ""; + } + return homePageURL; + } + + /** + * Sets the help file paths to locate the help file for the compiler. Called + * by {@link CompilerRegistry} only. + * + * @param helpFilePaths + * The relative file path to locate the help file for the + * compiler based on the folder of the executable. ".", ".." and + * "/" may be used to specify the path. A path may end with a + * language in the form "(en)". Multiple paths are separated by + * ",". May be empty or null. + */ + final void setHelpFilePaths(String helpFilePaths) { + if (helpFilePaths == null) { + helpFilePaths = ""; + } + this.helpFilePaths = helpFilePaths; + } + + /** + * Gets the help file paths to locate the help file for the compiler. + * + * @return The relative file path to locate the help file for the compiler + * based on the folder of the executable. ".", ".." and "/" may be + * used to specify the path. A path may end with a language in the + * form "(en)".Multiple paths are separated by ",". The result may + * be empty, not null. + */ + public final String getHelpFilePaths() { + if (helpFilePaths == null) { + throw new IllegalStateException("Field 'helpFilePaths' must not be null."); + } + return helpFilePaths; + } + + /** + * Determines if this compiler offers a help file at all. + * + * @return true if this compiler offers a help file, + * false otherwise. + */ + public final boolean hasHelpFile() { + return StringUtility.isSpecified(helpFilePaths); + } + + /** + * Gets the help file for the compiler. This includes locating the most + * appropriate file in the file system based on the current locale. + * + * @param compilerExecutablePath + * the compiler executable path, may be empty, not + * null. + * @return The help file, or null not help file could be found. + * + * @throws CoreException + * if the compilerExecutablePath is empty, the compiler does not + * specify a help path or no help file can be found. + */ + public final File getHelpFile(String compilerExecutablePath) throws CoreException { + if (compilerExecutablePath == null) { + throw new IllegalArgumentException("Parameter 'compilerExecutablePath' must not be null."); + } + if (StringUtility.isEmpty(compilerExecutablePath)) { + // ERROR: Help for the '{0}' compiler cannot be + // displayed because the path to the compiler executable + // is not set in the preferences. + throw new CoreException(new Status(IStatus.ERROR, AssemblerPlugin.ID, TextUtility.format( + Texts.MESSAGE_E130, name))); + } + if (!hasHelpFile()) { + // ERROR: The compiler '{0}' does not specify a help file path. + throw new CoreException(new Status(IStatus.ERROR, AssemblerPlugin.ID, TextUtility.format( + Texts.MESSAGE_E102, name))); + } + + String localeLanguage = Locale.getDefault().getLanguage(); + File firstFile = null; + File firstLanguageFile = null; + StringTokenizer tokenizer = new StringTokenizer(helpFilePaths, ","); + while (tokenizer.hasMoreTokens()) { + String helpFilePath = tokenizer.nextToken().trim(); + String helpFileLanguage = ""; + int index = helpFilePath.lastIndexOf("("); + if (index > 0) { + helpFileLanguage = helpFilePath.substring(index + 1, index + 3); + helpFilePath = helpFilePath.substring(0, index - 1).trim(); + } + File file = FileUtility.getCanonicalFile(new File(new File(compilerExecutablePath).getParent(), + helpFilePath)); + if (file.exists()) { + if (firstFile == null) { + firstFile = file; + } + if (firstLanguageFile == null && helpFileLanguage.equals(localeLanguage)) { + firstLanguageFile = file; + } + } + } + // Use language specific file if present, use first file otherwise. + File result = firstLanguageFile; + if (result == null) { + result = firstFile; + } + if (result == null) { + // ERROR: Help for the '{0}' compiler cannot be displayed because no + // help file was found in the paths '{1}' for the compiler + // executable path '{0}'. + throw new CoreException(new Status(IStatus.ERROR, AssemblerPlugin.ID, TextUtility.format( + Texts.MESSAGE_E131, name, helpFilePaths, compilerExecutablePath))); + } + return result; + + } + + /** + * Sets the list of supported CPUs Called by {@link CompilerRegistry} only. + * + * @param supportedCPUs + * The unmodifiable list of supported CPUs, not empty and not + * null. + * @since 1.6.1 + */ + final void setSupportedCPUs(List supportedCPUs) { + if (supportedCPUs == null) { + throw new IllegalArgumentException("Parameter 'supportedCPUs' must not be null."); + } + if (supportedCPUs.isEmpty()) { + throw new IllegalArgumentException("Parameter 'supportedCPUs' must not be empty."); + } + this.supportedCPUs = supportedCPUs; + } + + /** + * Gets the unmodifiable list of CPUs supported by this compiler. The first + * entry defines the default CPU. + * + * @return The unmodifiable list of CPUs supported by this compiler, not + * empty and, not null. + * + * @since 1.6.1 + */ + public final List getSupportedCPUs() { + if (supportedCPUs == null) { + throw new IllegalStateException("Field 'supportedCPUs' must not be null."); + } + return supportedCPUs; + } + + /** + * Sets the compiler syntax. Called by {@link CompilerRegistry} only. + * + * @param syntax + * The compiler syntax, not null. + */ + final void setSyntax(CompilerSyntax syntax) { + if (syntax == null) { + throw new IllegalArgumentException("Parameter 'syntax' must not be null."); + } + this.syntax = syntax; + } + + /** + * Gets the compiler syntax. + * + * @return The compiler syntax, not null. + */ + public final CompilerSyntax getSyntax() { + if (syntax == null) { + throw new IllegalStateException("Field 'syntax' must not be null."); + } + return syntax; + } + + /** + * Sets the compiler default parameters. Called by {@link CompilerRegistry} + * only. + * + * @param defaultParameters + * The compiler default parameters, not null. + */ + final void setDefaultParameters(String defaultParameters) { + if (defaultParameters == null) { + throw new IllegalArgumentException("Parameter 'defaultParameters' must not be null."); + } + this.defaultParameters = defaultParameters; + } + + /** + * Gets the compiler default parameters. + * + * @return The compiler default parameters, not null. + */ + public final String getDefaultParameters() { + if (defaultParameters == null) { + throw new IllegalStateException("Field 'defaultParameters' must not be null."); + } + return defaultParameters; + } + + /** + * Sets the default hardware to be assumed for this compiler. Called by + * {@link CompilerRegistry} only. + * + * @param hardware + * The default hardware, not null. + */ + final void setDefaultHardware(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + this.defaultHardware = hardware; + } + + /** + * Gets the default hardware for this compiler. + * + * @return The default hardwares, not null. + */ + public final Hardware getDefaultHardware() { + if (defaultHardware == null) { + throw new IllegalStateException("Field 'defaultHardware' must not be null."); + } + return defaultHardware; + } + + /** + * See {@link Comparable}. + */ + @Override + public int compareTo(CompilerDefinition o) { + if (o == null) { + throw new IllegalArgumentException("Parameter 'o' must not be null."); + } + if (id == null || o.id == null) { + if (id == null) { + throw new IllegalStateException("Field 'id' must not be null for this or for argument."); + } + } + return id.compareTo(o.id); + } + + @Override + public String toString() { + return id; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerFileWriter.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerFileWriter.java new file mode 100644 index 00000000..2ed2727a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerFileWriter.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +public class CompilerFileWriter { + /** + * Creates or updates a disk image file with the output file. + * + * @param files The compiler files, not null. + * + * @return true if no disk image is required or it is required and has been updated. + * + * @since 1.6.3 + */ + public boolean createOrUpdateDiskImage(CompilerFiles files) { + if (files == null) { + throw new IllegalArgumentException( + "Parameter 'outputFile' must not be null."); + } + return true; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerFiles.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerFiles.java new file mode 100644 index 00000000..cde61fe2 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerFiles.java @@ -0,0 +1,256 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import java.io.File; + +import org.eclipse.core.resources.IFile; + +import com.wudsn.ide.asm.AssemblerProperties; +import com.wudsn.ide.asm.AssemblerProperties.AssemblerProperty; +import com.wudsn.ide.asm.preferences.CompilerPreferences; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Container class for the folder, file names and paths of the source file, the + * output file and the symbols file. + * + * @author Peter Dell + * + */ +public final class CompilerFiles { + + public final class SourceFile { + + public final IFile iFile; + + public final File folder; + public final String folderPath; + + public final File file; + public final String filePath; + public final String fileName; + public final String fileNameWithoutExtension; + + public final AssemblerProperties assemblerProperties; + + SourceFile(IFile iFile, AssemblerProperties assemblerProperties) { + + if (iFile == null) { + throw new IllegalArgumentException("Parameter 'iFile' must not be null."); + } + if (assemblerProperties == null) { + throw new IllegalArgumentException("Parameter 'assemblerProperties' must not be null."); + } + + this.iFile = iFile; + this.assemblerProperties = assemblerProperties; + + // Source file. + filePath = iFile.getLocation().toOSString(); + file = FileUtility.getCanonicalFile(new File(filePath)); + fileName = file.getName(); + + String extension = iFile.getFileExtension(); + if (extension == null) { + extension = ""; + } + fileNameWithoutExtension = fileName.substring(0, fileName.length() - extension.length() - 1); + + // Source folder. + folder = file.getParentFile(); + folderPath = folder.getPath(); + } + } + + /** + * The actual source file which is currently open. + */ + public final SourceFile sourceFile; + + /** + * The main source file which is either the current file or the file + * indicated by the property "@com.wudsn.ide.asm.editor.MainSourceFile". + */ + public final SourceFile mainSourceFile; + + public final AssemblerProperty outputFolderModeProperty; + public final String outputFolderMode; + + public final AssemblerProperty outputFolderProperty; + public final File outputFolder; + public final String outputFolderPath; + + public final File outputFile; + public final String outputFilePath; + public final String outputFilePathWithoutExtension; + + public final AssemblerProperty outputFileProperty; + public final String outputFileName; + public final String outputFileNameWithoutExtension; + public final AssemblerProperty outputFileExtensionProperty; + public final String outputFileExtension; + public final String outputFileNameShortWithoutExtension; + + public final File symbolsFile; + public final String symbolsFilePath; + public final String symbolsFileName; + + public CompilerFiles(IFile mainSourceIFile, AssemblerProperties mainSourceFileAssemblerProperties, + IFile sourceIFile, AssemblerProperties sourceFileAssemblerProperties, + CompilerPreferences compilerPreferences) { + + if (mainSourceIFile == null) { + throw new IllegalArgumentException("Parameter 'mainSourceIFile' must not be null."); + } + if (sourceIFile == null) { + throw new IllegalArgumentException("Parameter 'sourceIFile' must not be null."); + } + if (compilerPreferences == null) { + throw new IllegalArgumentException("Parameter 'compilerPreferences' must not be null."); + } + this.mainSourceFile = new SourceFile(mainSourceIFile, mainSourceFileAssemblerProperties); + this.sourceFile = new SourceFile(sourceIFile, sourceFileAssemblerProperties); + + // Output folder mode + // Can be overridden via annotation property in main source file + String localOutputFolderPath = compilerPreferences.getOutputFolderPath(); + String localOutputFolderMode = compilerPreferences.getOutputFolderMode(); + String localOutputFileExtension = compilerPreferences.getOutputFileExtension(); + + // Properties which override the preferences + outputFolderModeProperty = mainSourceFileAssemblerProperties.get(AssemblerProperties.OUTPUT_FOLDER_MODE); + outputFolderProperty = mainSourceFileAssemblerProperties.get(AssemblerProperties.OUTPUT_FOLDER); + outputFileExtensionProperty = mainSourceFileAssemblerProperties.get(AssemblerProperties.OUTPUT_FILE_EXTENSION); + outputFileProperty = mainSourceFileAssemblerProperties.get(AssemblerProperties.OUTPUT_FILE); + + // The following sequence sets the instance fields "outputFolder" and + // "outputFileNameWithoutExtension" as well as the + // "outputFileNameWithoutExtension". + // If the output file is specified explicitly, it overrides all output + // properties. + if (outputFileProperty != null) { + + // Make the file an absolute file. + File file = new File(outputFileProperty.value); + if (!file.isAbsolute()) { + file = new File(mainSourceFile.file.getParentFile(), file.getPath()); + } + file = FileUtility.getCanonicalFile(file); + + outputFolderMode = CompilerOutputFolderMode.FIXED_FOLDER; + outputFolder = file.getParentFile(); + + // Split the file name and file extension. + String fileName = file.getName(); + int index = fileName.lastIndexOf('.'); + if (index > 0) { + outputFileNameWithoutExtension = fileName.substring(0, index); + localOutputFileExtension = fileName.substring(index); + } else { + outputFileNameWithoutExtension = fileName; + localOutputFileExtension = ""; + } + } else { + // The output file extension is independent of the rest. + if (outputFileExtensionProperty != null) { + localOutputFileExtension = outputFileExtensionProperty.value; + } + // If the output folder mode is specified explicitly, it overrides + // the output + // folder mode preferences. + if (outputFolderModeProperty != null) { + localOutputFolderMode = outputFolderModeProperty.value; + } + + // If the output folder is specified explicitly, it overrides the + // output folder mode and folder preferences. + if (outputFolderProperty != null) { + localOutputFolderMode = CompilerOutputFolderMode.FIXED_FOLDER; + localOutputFolderPath = outputFolderProperty.value; + } + + if (localOutputFolderMode.equals(CompilerOutputFolderMode.SOURCE_FOLDER)) { + localOutputFolderPath = mainSourceFile.folderPath; + } else if (localOutputFolderMode.equals(CompilerOutputFolderMode.FIXED_FOLDER)) { + // Fallback + if (StringUtility.isEmpty(localOutputFolderPath)) { + localOutputFolderPath = System.getProperty("java.io.tmpdir"); + } + } else { + localOutputFolderPath = System.getProperty("java.io.tmpdir"); + } + + File file = new File(localOutputFolderPath); + if (!file.isAbsolute()) { + file = new File(sourceFile.file, file.getPath()); + } + file = FileUtility.getCanonicalFile(file); + + outputFolderMode = localOutputFolderMode; + outputFolder = file; + + // Output file. + outputFileNameWithoutExtension = mainSourceFile.fileNameWithoutExtension; + } + + // Common output parts. + outputFolderPath = outputFolder.getPath(); + outputFileNameShortWithoutExtension = getFileNameShort(outputFileNameWithoutExtension); + outputFileName = outputFileNameWithoutExtension + localOutputFileExtension; + outputFileExtension = localOutputFileExtension; + outputFile = new File(outputFolder, outputFileName); + outputFilePath = outputFile.getPath(); + outputFilePathWithoutExtension = new File(outputFolder, outputFileNameWithoutExtension).getPath(); + + // Symbols file. + symbolsFileName = mainSourceFile.fileNameWithoutExtension + ".lbl"; + symbolsFile = new File(outputFolder, symbolsFileName); + symbolsFilePath = symbolsFile.getPath(); + + } + + /** + * Computes a short file consisting of at most 8 ASCII letters and digits + * which starts with a letter. + * + * @param fileName + * The file name, may be empty, not null. + * @return The short file, name, not empty and not null. + */ + private String getFileNameShort(String fileName) { + if (fileName == null) { + throw new IllegalArgumentException("Parameter 'fileName' must not be null."); + } + StringBuilder builder = new StringBuilder(fileName); + for (int i = 0; i < fileName.length() && builder.length() < 8; i++) { + char c = fileName.charAt(i); + c = Character.toUpperCase(c); + if (c >= 'A' && c <= 'Z' && (builder.length() > 0) || c >= '0' && c <= '9') { + builder.append(c); + } + } + if (builder.length() == 0) { + return "UNKNOWN"; + } + return builder.toString(); + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerOutputFolderMode.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerOutputFolderMode.java new file mode 100644 index 00000000..7991b23e --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerOutputFolderMode.java @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import com.wudsn.ide.base.common.StringUtility; + +/** + * The target directory mode controls which folder is use to create the binary + * file in. + * + * @author Peter Dell + */ +public final class CompilerOutputFolderMode { + + /** + * Creation is private. + */ + private CompilerOutputFolderMode() { + } + + public final static String SOURCE_FOLDER = "SOURCE_FOLDER"; + public final static String TEMP_FOLDER = "TEMP_FOLDER"; + public final static String FIXED_FOLDER = "FIXED_FOLDER"; + + /** + * Determines if the output folder mode is defined. + * + * @param outputFolderMode + * The output folder mode, not null. + * @return true if the output folder mode is not specified or + * defined. + */ + public static boolean isDefined(String outputFolderMode) { + if (outputFolderMode == null) { + throw new IllegalArgumentException("Parameter 'outputFolderMode' must not be null."); + } + if (StringUtility.isEmpty(outputFolderMode)) { + return true; + } + if (outputFolderMode.equals(SOURCE_FOLDER) || outputFolderMode.equals(TEMP_FOLDER) + || outputFolderMode.equals(FIXED_FOLDER)) { + return true; + } + return false; + } + + /** + * Gets the comma separated list of allowed values. + * + * @return The comma separated list of allowed values, not null + * . + */ + public static String getAllowedValues() { + return SOURCE_FOLDER + ", " + TEMP_FOLDER + ", " + FIXED_FOLDER; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerProcessLogParser.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerProcessLogParser.java new file mode 100644 index 00000000..69dbc158 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerProcessLogParser.java @@ -0,0 +1,286 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import java.io.File; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Base class for compiler process log parsing. + * + * @author Peter Dell + */ +public abstract class CompilerProcessLogParser { + + /** + * Proxy class for creating instances of {@link IMarker}. + * + * @since 1.6.1 + */ + public static final class Marker { + private IFile iFile; + private int lineNumber; + private int severity; + private String message; + private Marker detailMarker; + + Marker(IFile iFile, int lineNumber, int severity, String message, Marker detailMarker) { + if (iFile == null) { + throw new IllegalArgumentException("Parameter 'iFile' must not be null."); + } + if (message == null) { + throw new IllegalArgumentException("Parameter 'message' must not be null."); + } + this.iFile = iFile; + this.lineNumber = lineNumber; + this.severity = severity; + this.message = message; + this.detailMarker = detailMarker; + } + + /** + * Gets the iFile the marker refers to. + * + * @return The iFile, not null. + */ + public IFile getIFile() { + return iFile; + } + + /** + * Gets the line number. + * + * @return The liner number or 0, if there is no known line number. + */ + public int getLineNumber() { + return lineNumber; + } + + /** + * Gets the severity. + * + * @return The severity, see {@link IMarker#SEVERITY}. + */ + public int getSeverity() { + return severity; + } + + /** + * Gets the message. + * + * @return The message, may be empty, not null. + */ + public String getMessage() { + return message; + } + + /** + * Gets the detail marker, describing this marker in more detail. + * + * @return The detail marker or null. + */ + public Marker getDetailMarker() { + return detailMarker; + } + + @Override + public String toString() { + return iFile.getFullPath() + ":" + lineNumber + ":" + severity + ":" + message; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof Marker)) { + return false; + } + Marker other = (Marker) o; + return iFile.getFullPath().equals(other.iFile.getFullPath()) + && lineNumber == other.lineNumber + && severity == other.severity + && message.equals(other.message) + && ((detailMarker == null && other.detailMarker == null) || detailMarker.equals(other.detailMarker)); + + } + + @Override + public int hashCode() { + return lineNumber; + + } + } + + private boolean initialized; + protected CompilerFiles files; + protected String mainSourceFilePath; + protected String outputLog; + protected String errorLog; + protected boolean markerAvailable; + protected String filePath; + protected int lineNumber; + protected int severity; + protected String message; + + protected CompilerProcessLogParser() { + } + + public final void setLogs(CompilerFiles files, String outputLog, String errorLog) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + if (outputLog == null) { + throw new IllegalArgumentException("Parameter 'outputLog' must not be null."); + } + if (errorLog == null) { + throw new IllegalArgumentException("Parameter 'errorLog' must not be null."); + } + this.files = files; + this.mainSourceFilePath = files.mainSourceFile.filePath; + this.outputLog = outputLog; + this.errorLog = errorLog; + initialize(); + initialized = true; + markerAvailable = false; + return; + } + + protected void initialize() { + } + + public final boolean nextMarker() { + filePath = ""; + lineNumber = 0; + severity = 0; + message = ""; + markerAvailable = false; + findNextMarker(); + return markerAvailable; + } + + protected abstract void findNextMarker(); + + /** + * Adds the compiler symbols from the process output to the specified list. + * + * @param list + * The modifiable list to which the compiler symbols shall be + * added, not null. + * + * @throws CoreException + * if the symbols information is present, but cannot be read or + * parsed. + */ + public void addCompilerSymbols(List list) throws CoreException { + } + + /** + * Creates a new marker proxy for a file. + * + * @return The marker proxy, not null. + * + * @since 1.6.1 + */ + public final Marker getMarker() { + + if (!initialized) { + throw new IllegalStateException("No log set."); + } + if (!markerAvailable) { + throw new IllegalStateException("No marker available."); + } + IFile mainSourceIFile = files.mainSourceFile.iFile; + IFile iFile = mainSourceIFile; + String normalizedFilePath = filePath.replace(File.separatorChar, '/'); + String normalizedMainSourceFilePath = mainSourceFilePath.replace(File.separatorChar, '/'); + Marker detailMarker = null; + if (normalizedFilePath.length() > 0 && !normalizedFilePath.equals(normalizedMainSourceFilePath)) { + String folderPath = mainSourceIFile.getParent().getLocation().toOSString(); + String normalizedFolderPath = folderPath.replace(File.separatorChar, '/'); + + String relativePath; + + // Absolute include path, may even be a brother or parent path? + if (normalizedFilePath.startsWith(normalizedFolderPath)) { + relativePath = normalizedFilePath.substring(folderPath.length() + 1); + } else { + // Simple relative path. + relativePath = normalizedFilePath; + } + + // Create absolute iFile. + iFile = mainSourceIFile.getParent().getFile(new Path(relativePath)); + + // Check if it exists. This requires the file path to be in exactly + // the right case, even if the file system is case insensitive. + if (iFile == null || !iFile.exists()) { + + // If the file exists, but the include was specified with a + // different case, an additional detail error message is issued. + IFile caseInsenstiveIFile = null; + if (iFile != null) { + try { + IResource[] members = iFile.getParent().members(); + for (int i = 0; i < members.length && caseInsenstiveIFile == null; i++) { + if (members[i] instanceof IFile && members[i].getName().equalsIgnoreCase(iFile.getName())) { + caseInsenstiveIFile = (IFile) members[i]; + } + } + } catch (CoreException ex) { + AssemblerPlugin.getInstance().logError("Could not retrieve members of {0}", + new Object[] { iFile }, ex); + } + } + + // Use the case insensitive file if found and the main file + // otherwise. + if (iFile != null && caseInsenstiveIFile != null) { + // ERROR: Include statement for file '{0}' uses a file name + // that has a different case different from file system name + // {1}. Correct the file name in the include statement. + String caseMessage = TextUtility.format(Texts.MESSAGE_E142, iFile.getName(), + caseInsenstiveIFile.getName()); + detailMarker = new Marker(mainSourceIFile, 0, IMarker.SEVERITY_ERROR, caseMessage, null); + + iFile = caseInsenstiveIFile; + } else { + iFile = mainSourceIFile; + // INFO: In include file '{0}', line {1}. + message += " " + + TextUtility.format(Texts.MESSAGE_S143, filePath, + NumberUtility.getLongValueDecimalString(lineNumber)); + lineNumber = 0; + } + } + + } + + return new Marker(iFile, lineNumber, severity, message.trim(), detailMarker); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerRegistry.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerRegistry.java new file mode 100644 index 00000000..6ff56bcc --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerRegistry.java @@ -0,0 +1,244 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; + +/** + * Registry for compilers, based on the extension points + * {@value CompilerRegistry#COMPILERS}. + * + * @author Peter Dell + * + */ +public final class CompilerRegistry { + + /** + * The id of the extension point which provides the compilers. + */ + private static final String COMPILERS = "com.wudsn.ide.asm.compilers"; + + /** + * The registered compiler definition. + */ + private List compilerDefinitionList; + + /** + * The cached map of compiler instances. + */ + private Map compilerMap; + + /** + * Creation is public. + */ + public CompilerRegistry() { + compilerDefinitionList = Collections.emptyList(); + compilerMap = Collections.emptyMap(); + + } + + /** + * Initializes the list of available compilers. + */ + public void init() { + + compilerDefinitionList = new ArrayList(); + compilerMap = new TreeMap(); + + IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); + IExtensionPoint extensionPoint = extensionRegistry + .getExtensionPoint(COMPILERS); + if (extensionPoint == null) { + throw new IllegalStateException("Extension point '" + COMPILERS + + "' is not defined."); + } + + IExtension[] extensions = extensionPoint.getExtensions(); + + for (IExtension extension : extensions) { + IConfigurationElement[] configurationElements = extension + .getConfigurationElements(); + for (IConfigurationElement configurationElement : configurationElements) { + + try { + CompilerDefinition compilerDefinition; + compilerDefinition = new CompilerDefinition(); + compilerDefinition.setId(configurationElement + .getAttribute("id")); + compilerDefinition.setName(configurationElement + .getAttribute("name")); + compilerDefinition.setClassName(configurationElement + .getAttribute("class")); + compilerDefinition.setHelpFilePaths(configurationElement + .getAttribute("helpFilePaths")); + compilerDefinition.setHomePageURL(configurationElement + .getAttribute("homePageURL")); + compilerDefinition + .setDefaultParameters(configurationElement + .getAttribute("defaultParameters")); + + configurationElement.getChildren("supportedCPU"); + IConfigurationElement[] supportedCPUArray; + supportedCPUArray = configurationElement + .getChildren("supportedCPU"); + List supportedCPUs = new ArrayList( + supportedCPUArray.length); + for (IConfigurationElement supportedCPU : supportedCPUArray) { + supportedCPUs.add(CPU.valueOf(supportedCPU + .getAttribute("cpu"))); + } + supportedCPUs = Collections.unmodifiableList(supportedCPUs); + compilerDefinition.setSupportedCPUs(supportedCPUs); + compilerDefinition.setDefaultHardware(Hardware + .valueOf(configurationElement + .getAttribute("defaultHardware"))); + + compilerDefinitionList.add(compilerDefinition); + + addCompiler(configurationElement, compilerDefinition); + } catch (RuntimeException ex) { + throw new RuntimeException( + "Error during registration of compiler '" + + configurationElement.getAttribute("id") + + "'.", ex); + } + } + } + + compilerDefinitionList = new ArrayList( + compilerDefinitionList); + Collections.sort(compilerDefinitionList); + compilerDefinitionList = Collections + .unmodifiableList(compilerDefinitionList); + compilerMap = Collections.unmodifiableMap(compilerMap); + } + + /** + * Adds a new compiler. + * + * @param configurationElement + * The configuration element used as class instance factory, not + * null. + * + * @param compilerDefinition + * The compiler definition, not null. + */ + private void addCompiler(IConfigurationElement configurationElement, + CompilerDefinition compilerDefinition) { + if (configurationElement == null) { + throw new IllegalArgumentException( + "Parameter 'configurationElement' must not be null."); + } + if (compilerDefinition == null) { + throw new IllegalArgumentException( + "Parameter 'compilerDefinition' must not be null."); + } + + String id = compilerDefinition.getId(); + Compiler compiler; + try { + // The class loading must be delegated to the framework. + compiler = (Compiler) configurationElement + .createExecutableExtension("class"); + } catch (CoreException ex) { + throw new RuntimeException( + "Cannot create compiler instance for id '" + id + "'.", ex); + } + + // Build the list of common and specific syntax definition files. + List> compilerClasses = new ArrayList>(2); + compilerClasses.add(compiler.getClass()); + compilerClasses.add(Compiler.class); + + CompilerSyntax syntax; + syntax = new CompilerSyntax(id); + + syntax.loadXMLData(compilerClasses); + + compilerDefinition.setSyntax(syntax); + + compiler.setDefinition(compilerDefinition); + + compiler = compilerMap.put(id, compiler); + if (compiler != null) { + throw new RuntimeException("Compiler id '" + id + + "' is already registered to class '" + + compiler.getClass().getName() + "'."); + } + + } + + /** + * Gets the unmodifiable list of compiler definitions, sorted by their id. + * + * + * @return The unmodifiable list of compiler definitions, sorted by their + * id, may be empty, not null + * + * @since 1.6.1 + */ + public List getCompilerDefinitions() { + return compilerDefinitionList; + } + + /** + * Gets the compiler for a given id. Instances of compiler are stateless + * singletons within the plugin. + * + * @param compilerId + * The compiler id, not null. + * + * @return The compiler, not null. + */ + public Compiler getCompiler(String compilerId) { + if (compilerId == null) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be null."); + } + Compiler result; + synchronized (compilerMap) { + + result = compilerMap.get(compilerId); + } + if (result == null) { + + throw new IllegalArgumentException("Unknown compiler id '" + + compilerId + "'."); + } + + return result; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerSymbol.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerSymbol.java new file mode 100644 index 00000000..63771105 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerSymbol.java @@ -0,0 +1,156 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * A symbols in the symbol table. A symbol may be an equate or a label and it + * may represent a numeric numericValue or an address. + * + * @author Peter Dell + */ +public final class CompilerSymbol { + + public static final int NUMBER = 1; + public static final int STRING = 2; + + private final int type; + private final String name; + private final String nameUpperCase; + private final String bankString; + private final int valueType; + private final String numberValueHexString; + private final String numberValueHexStringUpperCase; + private final String numberValueDecimalString; + private final String stringValue; + private final String stringValueUpperCase; + + public static final int UNDEFINED_BANK = -1; + + private CompilerSymbol(int type, String name, int bank, int valueType, long numberValue, String stringValue) { + if (name == null) + throw new IllegalArgumentException("Parameter 'name' must not be null."); + if (StringUtility.isEmpty(name)) { + throw new IllegalArgumentException("Parameter 'name' must not be empty."); + } + if (stringValue == null) { + throw new IllegalArgumentException("Parameter 'stringValue' must not be null."); + } + this.type = type; + this.name = name.trim(); + this.nameUpperCase = name.toUpperCase(); + this.bankString = bank != UNDEFINED_BANK ? NumberUtility.getLongValueDecimalString(bank) : ""; + this.valueType = valueType; + switch (valueType) { + case NUMBER: + numberValue = numberValue & 0xfffffff; + int length = (HexUtility.getLongValueHexLength(numberValue) + 1) & 0xffffffe; + this.numberValueHexString = HexUtility.getLongValueHexString(numberValue, length); + this.numberValueHexStringUpperCase = numberValueHexString.toUpperCase(); + this.numberValueDecimalString = NumberUtility.getLongValueDecimalString(numberValue); + this.stringValue = ""; + this.stringValueUpperCase = stringValue; + break; + case STRING: + this.numberValueHexString = ""; + this.numberValueHexStringUpperCase = ""; + this.numberValueDecimalString = ""; + this.stringValue = stringValue; + this.stringValueUpperCase = stringValue.toUpperCase(); + break; + default: + throw new IllegalArgumentException("Value type '" + valueType + "' is not supported."); + } + } + + public static CompilerSymbol createNumberSymbol(int type, String name, int bank, long numberValue) { + return new CompilerSymbol(type, name, bank, NUMBER, numberValue, ""); + } + + public static CompilerSymbol createNumberHexSymbol(String name, String hexValue) throws NumberFormatException { + return new CompilerSymbol(CompilerSymbolType.DEFAULT, name, UNDEFINED_BANK, NUMBER, + Long.parseLong(hexValue, 16), ""); + } + + public static CompilerSymbol createStringSymbol(String name, String stringValue) { + return new CompilerSymbol(CompilerSymbolType.DEFAULT, name, UNDEFINED_BANK, STRING, 0, stringValue); + } + + public int getType() { + return type; + } + + public String getName() { + return name; + } + + public String getNameUpperCase() { + return nameUpperCase; + } + + public String getBankString() { + return bankString; + } + + public String getValueAsHexString() { + return numberValueHexString; + } + + /** + * Upper case version of the hex string for fuzzy search. + * + * @return The upper case version of the hex string. + */ + public String getValueAsHexStringUpperCase() { + return numberValueHexStringUpperCase; + } + + public String getValueAsDecimalString() { + return numberValueDecimalString; + } + + public String getValueAsString() { + return stringValue; + } + + /** + * Upper case version of the hex string for fuzzy search. + * + * @return The upper case version of the hex string. + */ + public String getValueAsStringUpperCase() { + return stringValueUpperCase; + } + + @Override + public String toString() { + switch (valueType) { + case NUMBER: + return name + "=" + getValueAsHexString(); + case STRING: + return name + "=" + getValueAsString(); + } + throw new IllegalStateException(); + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerSymbolType.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerSymbolType.java new file mode 100644 index 00000000..5c4c0493 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerSymbolType.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.compiler; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObjectType; + +/** + * Constant values for symbol types. Symbol types are a subset of the source + * tree object types. + * + * @author Peter Dell + */ +public final class CompilerSymbolType { + + /** + * Creation is private. + */ + private CompilerSymbolType() { + } + + /** + * Default-type + */ + public static final int DEFAULT = CompilerSourceParserTreeObjectType.DEFAULT; + + public static final int EQUATE_DEFINITION = CompilerSourceParserTreeObjectType.EQUATE_DEFINITION; + public static final int LABEL_DEFINITION = CompilerSourceParserTreeObjectType.LABEL_DEFINITION; + + public static final int ENUM_DEFINITION_SECTION = CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION; + public static final int STRUCTURE_DEFINITION_SECTION = CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION; + public static final int MACRO_DEFINITION_SECTION = CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION; + public static final int LOCAL_SECTION = CompilerSourceParserTreeObjectType.LOCAL_SECTION; + public static final int PROCEDURE_DEFINITION_SECTION = CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION; + + /** + * Gets the localized text for a compiler symbol type. + * + * @param type + * The type, see constants of this class. + * @return The localized text, may be empty but not null. + */ + public static String getText(int type) { + String result; + switch (type) { + case DEFAULT: + case EQUATE_DEFINITION: + case LABEL_DEFINITION: + case ENUM_DEFINITION_SECTION: + case STRUCTURE_DEFINITION_SECTION: + case MACRO_DEFINITION_SECTION: + case LOCAL_SECTION: + case PROCEDURE_DEFINITION_SECTION: + result = CompilerSourceParserTreeObjectType.getText(type); + break; + default: + throw new IllegalArgumentException("Unknown type " + type + "."); + } + return result; + + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerVariables.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerVariables.java new file mode 100644 index 00000000..bc0ac64e --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/CompilerVariables.java @@ -0,0 +1,75 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler; + +/** + * Utility class to handle dynamic variables. + * + * @author Peter Dell + */ +public final class CompilerVariables { + + /** + * Creation is private. + */ + private CompilerVariables() { + } + + public static final String SOURCE_FOLDER_PATH = "${sourceFolderPath}"; + public static final String SOURCE_FILE_PATH = "${sourceFilePath}"; + public static final String OUTPUT_FOLDER_PATH = "${outputFolderPath}"; + public static final String OUTPUT_FILE_PATH = "${outputFilePath}"; + public static final String OUTPUT_FILE_PATH_WITHOUT_EXTENSION = "${outputFilePathWithoutExtension}"; + public static final String OUTPUT_FILE_NAME = "${outputFileName}"; + public static final String OUTPUT_FILE_NAME_WITHOUT_EXTENSION = "${outputFileNameWithoutExtension}"; + public static final String OUTPUT_FILE_NAME_SHORT_WITHOUT_EXTENSION = "${outputFileNameShortWithoutExtension}"; + + public static String replaceVariables(String parameter, + CompilerFiles files) { + if (parameter == null) { + throw new IllegalArgumentException( + "Parameter 'parameter' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException( + "Parameter 'files' must not be null."); + } + + // When referring to the source file and folder at compiler time, + // this always means the main source file. + parameter = parameter.replace(SOURCE_FOLDER_PATH, + files.mainSourceFile.folderPath); + parameter = parameter.replace(SOURCE_FILE_PATH, + files.mainSourceFile.filePath); + parameter = parameter.replace(OUTPUT_FOLDER_PATH, + files.outputFolderPath); + parameter = parameter.replace(OUTPUT_FILE_PATH, files.outputFilePath); + parameter = parameter.replace(OUTPUT_FILE_PATH_WITHOUT_EXTENSION, + files.outputFilePathWithoutExtension); + parameter = parameter.replace(OUTPUT_FILE_NAME, files.outputFileName); + parameter = parameter.replace(OUTPUT_FILE_NAME_WITHOUT_EXTENSION, + files.outputFileNameWithoutExtension); + parameter = parameter.replace(OUTPUT_FILE_NAME_SHORT_WITHOUT_EXTENSION, + files.outputFileNameShortWithoutExtension); + + return parameter; + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceFile.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceFile.java new file mode 100644 index 00000000..89b28816 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceFile.java @@ -0,0 +1,460 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.parser; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; + +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Container for recursive parsing of source files using + * {@link CompilerSourceParser#parse(CompilerSourceFile, CompilerSourceParserLineCallback)} + * . + * + * @author Peter Dell + */ +public final class CompilerSourceFile { + + private final static class FoldingStackEntry { + public final int startOffset; + public final boolean forSection; + + public FoldingStackEntry(int startOffset, boolean forSection) { + this.startOffset = startOffset; + this.forSection = forSection; + } + + @Override + public String toString() { + return "startOffset=" + startOffset + ", forSection=" + forSection; + } + } + + private CompilerSyntax compilerSyntax; + private File documentFile; + private File documentDirectory; + private IDocument document; + + private List foldingPositions; + + /** + * Temporary data during the parse process. + */ + private List foldingStack; + + /** + * The result of the last parse process. + */ + private CompilerSourceParserTreeObject definitionSection; + private List implementationSections; + private List sectionStack; + + /** + * Creates a new compiler source file. Instances are only created by + * {@link CompilerSourceParser#createCompilerSourceFile(File, IDocument)}. + * + * @param compilerSyntax + * The compiler syntax used to parse this file, not + * null. + * @param documentFile + * The file in the file system. Its directory is used to resolve + * relative file paths, or null if the document is + * not yet persistent. + * @param document + * The document, not null. + */ + CompilerSourceFile(CompilerSyntax compilerSyntax, File documentFile, IDocument document) { + if (compilerSyntax == null) { + throw new IllegalArgumentException("Parameter 'compilerSyntax' must not be null."); + } + if (document == null) { + throw new IllegalArgumentException("Parameter 'document' must not be null."); + } + this.compilerSyntax = compilerSyntax; + if (documentFile != null) { + documentFile = FileUtility.getCanonicalFile(documentFile); + this.documentFile = documentFile; + this.documentDirectory = documentFile.getParentFile(); + } + + this.document = document; + + // Folding. + foldingPositions = new ArrayList(); + foldingStack = new ArrayList(); + + // Sections. + definitionSection = new CompilerSourceParserTreeObject(this, 0, + CompilerSourceParserTreeObjectType.DEFINITION_SECTION, "DefinitionSection", + Texts.ASSEMBLER_CONTENT_OUTLINE_TREE_TYPE_DEFINITION_SECTION, ""); + implementationSections = new ArrayList(); + sectionStack = new ArrayList(); + } + + /** + * Gets the compiler syntax user to parse this file. + * + * @return The compiler syntax, not null. + * + * @since 1.6.1 + */ + public CompilerSyntax getCompilerSyntax() { + return compilerSyntax; + } + + /** + * Gets the canonical document file. + * + * @return The file where the document is located, or null if + * the document is not yet persistent. + */ + public File getDocumentFile() { + return documentFile; + } + + /** + * Gets the document directory. + * + * @return The directory which is used to resolved relative file paths, or + * null if the document is not yet persistent. + */ + public File getDocumentDirectory() { + return documentDirectory; + } + + /** + * Gets the document. + * + * @return The document, not null. + */ + public IDocument getDocument() { + return document; + } + + /** + * Gets the sections of this file. + * + * @return The unmodifiable list of section, may be empty, not + * null. + */ + public List getSections() { + List result; + result = new ArrayList(); + if (definitionSection != null && definitionSection.hasChildren()) { + result.add(definitionSection); + } + + result.addAll(implementationSections); + result = Collections.unmodifiableList(result); + return result; + } + + final CompilerSourceParserTreeObject getDefinitionSection() { + return definitionSection; + } + + final List getImplementationSections() { + return implementationSections; + } + + /** + * Gets the list of foldingPositions for folding after parsed has completed. + * + * @return The non-modifiable sorted list of foldingPositions, may be empty, + * not null. + */ + public List getFoldingPositions() { + return Collections.unmodifiableList(foldingPositions); + } + + /** + * Determines if the currently active folding was started for a section. + * + * @return true if the folding was started for a section, + * false if not. + */ + final boolean isFoldingForSection() { + if (foldingStack.isEmpty()) { + return false; // Ignore wrong nesting. + } + FoldingStackEntry entry = foldingStack.get(foldingStack.size() - 1); + return entry.forSection; + } + + /** + * Starts a new active folding level. + * + * @param startOffset + * The start offset, a non-negative integer. + * @param forSection + * true if the folding is started for a section, + * false if not. + */ + final void beginFolding(int startOffset, boolean forSection) { + if (startOffset < 0) { + throw new IllegalArgumentException("Parameter 'startOffset' must not be negative. Specified value is " + + startOffset + "."); + } + foldingStack.add(new FoldingStackEntry(startOffset, forSection)); + } + + /** + * Ends the currently active folding level. + * + * @param endOffset + * The end offset, a non-negative integer. This must be the + * actual offset of the last character in the line, including the + * line end delimiter, if present. + */ + final void endFolding(int endOffset) { + if (endOffset < 0) { + throw new IllegalArgumentException("Parameter 'endOffset' must not be negative. Specified value is " + + endOffset + "."); + } + + if (foldingStack.isEmpty()) { + return; // Ignore wrong nesting. + } + FoldingStackEntry entry = foldingStack.remove(foldingStack.size() - 1); + int length = endOffset - entry.startOffset; + if (length < 0) { + throw new IllegalArgumentException("End offset " + endOffset + " is less than the start offset " + + entry.startOffset + "."); + } + // Add only non-empty positions. + if (length > 0) { + foldingPositions.add(new Position(entry.startOffset, length)); + } + } + + /** + * Ends all currently active folding levels. + * + */ + final void endAllFoldings() { + + while (!foldingStack.isEmpty()) { + endFolding(document.getLength() > 0 ? document.getLength() - 1 : 0); + + } + } + + /** + * Ends all currently active sections. + * + */ + public void endAllSections() { + while (!sectionStack.isEmpty()) { + endSection(document.getLength() > 0 ? document.getLength() - 1 : 0); + } + } + + /** + * Starts a new active section. + * + * @param startOffset + * The start offset, a non-negative integer. + * @param section + * The new section, not null. + * @param withFolding + * true if the section is also a folding section, + * false otherwise. + */ + final void beginSection(int startOffset, CompilerSourceParserTreeObject section, boolean withFolding) { + if (startOffset < 0) { + throw new IllegalArgumentException("Parameter 'startOffset' must not be negative. Specified value is " + + startOffset + "."); + } + if (section == null) { + throw new IllegalArgumentException("Parameter 'section' must not be null."); + } + + if (!sectionStack.isEmpty()) { + CompilerSourceParserTreeObject parent = sectionStack.get(sectionStack.size() - 1); + parent.addChild(section); + } + sectionStack.add(section); + if (withFolding) { + beginFolding(startOffset, true); + } + + } + + /** + * Ends the currently active section. + * + * @param endOffset + * The end offset, a non-negative integer. This must be the + * actual offset of the last character in the line, including the + * line end delimiter, if present. + * + * @return The new top of the section stack, may be null. + */ + final CompilerSourceParserTreeObject endSection(int endOffset) { + if (endOffset < 0) { + throw new IllegalArgumentException("Parameter 'endOffset' must not be negative. Specified value is " + + endOffset + "."); + } + + CompilerSourceParserTreeObject result; + result = null; + + // Remove top of stack. + if (!sectionStack.isEmpty()) { + sectionStack.remove(sectionStack.size() - 1); + + // Make top of stack the new active section, accepting illegal pops. + if (!sectionStack.isEmpty()) { + result = sectionStack.get(sectionStack.size() - 1); + } + endFolding(endOffset); + } + return result; + } + + public List getIdentifiers() { + List foundElements; + foundElements = new ArrayList(); + getIdentifiers(getSections(), foundElements); + foundElements = Collections.unmodifiableList(foundElements); + return foundElements; + } + + private void getIdentifiers(List allElements, + List foundElements) { + if (allElements == null) { + throw new IllegalArgumentException("Parameter 'allElements' must not be null."); + } + if (foundElements == null) { + throw new IllegalArgumentException("Parameter 'foundElements' must not be null."); + } + CompilerSourceParserTreeObject element = null; + for (int i = 0; i < allElements.size(); i++) { + element = allElements.get(i); + switch (element.getType()) { + case CompilerSourceParserTreeObjectType.EQUATE_DEFINITION: + case CompilerSourceParserTreeObjectType.LABEL_DEFINITION: + case CompilerSourceParserTreeObjectType.LOCAL_SECTION: + case CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION: + + foundElements.add(element); + break; + } + if (element.hasChildren()) { + getIdentifiers(element.getChildren(), foundElements); + } + + } + } + + /** + * Find the definition elements for a given identifier. + * + * @param identifier + * The identifier to search for, not empty and not + * null. + * @return The unmodifiable list of compiler source tree object with the + * label, may be empty, not null. The result may + * contain entries from source include files. + */ + public List getIdentifierDefinitionElements(String identifier) { + + if (identifier == null) { + throw new IllegalArgumentException("Parameter 'identifier' must not be null."); + } + if (StringUtility.isEmpty(identifier)) { + throw new IllegalArgumentException("Parameter 'identifier' must not be empty."); + } + List foundElements; + foundElements = new ArrayList(); + getIdentifierDefinitionElements(getSections(), identifier, foundElements); + foundElements = Collections.unmodifiableList(foundElements); + return foundElements; + + } + + private void getIdentifierDefinitionElements(List allElements, String identifier, + List foundElements) { + if (allElements == null) { + throw new IllegalArgumentException("Parameter 'allElements' must not be null."); + } + if (identifier == null) { + throw new IllegalArgumentException("Parameter 'identifier' must not be null."); + } + if (foundElements == null) { + throw new IllegalArgumentException("Parameter 'foundElements' must not be null."); + } + String compoundIdentifierSuffix = null; + char c = compilerSyntax.getIdentifierSeparatorCharacter(); + if (c != CompilerSyntax.NO_CHARACTER) { + compoundIdentifierSuffix = c + identifier; + } + + boolean identifiersCaseSensitive = compilerSyntax.areIdentifiersCaseSensitive(); + CompilerSourceParserTreeObject element = null; + for (int i = 0; i < allElements.size(); i++) { + element = allElements.get(i); + switch (element.getType()) { + case CompilerSourceParserTreeObjectType.EQUATE_DEFINITION: + case CompilerSourceParserTreeObjectType.LABEL_DEFINITION: + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.LOCAL_SECTION: + case CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION: + // Check if name equals the identifier, compound name equals the + // identifier or compound name ends with + // compoundIdentifierSuffix. + if (identifiersCaseSensitive) { + if (element.getName().equals(identifier) + || element.getCompoundName().equals(identifier) + || (compoundIdentifierSuffix != null && element.getCompoundName().endsWith( + compoundIdentifierSuffix))) { + foundElements.add(element); + } + } else { + if (element.getName().equalsIgnoreCase(identifier) + || element.getCompoundName().equals(identifier) + || (compoundIdentifierSuffix != null && element.getCompoundName().regionMatches(true, + element.getCompoundName().length() - compoundIdentifierSuffix.length(), + compoundIdentifierSuffix, 0, compoundIdentifierSuffix.length()))) { + foundElements.add(element); + } + } + break; + } + + if (element.hasChildren()) { + getIdentifierDefinitionElements(element.getChildren(), identifier, foundElements); + } + + } + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParser.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParser.java new file mode 100644 index 00000000..fb06d661 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParser.java @@ -0,0 +1,1214 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.parser; + +import java.io.File; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.AssemblerProperties; +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.asm.compiler.syntax.Instruction; +import com.wudsn.ide.asm.compiler.syntax.InstructionSet; +import com.wudsn.ide.asm.compiler.syntax.InstructionType; +import com.wudsn.ide.base.BasePlugin; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Source parser for creating {@link CompilerSourceParserTreeObject} instances. + * The performance is so good, that no caching for the results of source include + * parsing is required. + * + * @author Peter Dell + * + */ +public abstract class CompilerSourceParser { + + // The sections of a single line + private static final class LineSection { + public static final int NONE = 0; + public static final int SYMBOL = 1; + public static final int INSTRUCTION = 2; + public static final int OPERAND = 3; + } + + // The compiler syntax and instruction set. + private CompilerSyntax compilerSyntax; + private InstructionSet instructionSet; + + // Fields set once for parsing a file. + private CompilerSourceFile compilerSourceFile; + + // Fields modified during parsing. + private CompilerSourceParserTreeObject section; + + // Fields modified per line. Preserved to and restored from local variables + // during recursive parsing of SOURCE_INCLUDE_DIRECTIVE instructions. + private CompilerSourceParserTreeObject child; + private CompilerSourceParserTreeObject labelChild; + private boolean blockStarting; + private boolean blockEnding; + + // For debugging. + private boolean logEnabled = false; + + /** + * Extract all {@link AssemblerProperties} properties from a document. + * + * @param document + * The document, not null. + * @return The properties, may be empty, not null. + */ + public static AssemblerProperties getDocumentProperties(IDocument document) { + if (document == null) { + throw new IllegalArgumentException( + "Parameter 'document' must not be null."); + } + String content = document.get(); + AssemblerProperties properties = new AssemblerProperties(); + + int index1 = content.indexOf(AssemblerProperties.PREFIX); + while (index1 >= 0) { + + int indexEqualSign = content.indexOf('=', index1); + int indexNewLine = content.indexOf('\n', index1); + if (indexNewLine < 0) { + indexNewLine = content.indexOf('\r', index1); + } + if (indexNewLine < 0) { + indexNewLine = content.length(); + } + + if (indexEqualSign >= 0 && indexEqualSign < indexNewLine) { + String key = content.substring(index1, indexEqualSign).trim(); + String value = content.substring(indexEqualSign + 1, + indexNewLine).trim(); + int lineNumber; + try { + lineNumber = document.getLineOfOffset(index1) + 1; + } catch (BadLocationException ex) { + lineNumber = 0; + } + properties.put(key, value, lineNumber); + } + index1 = content.indexOf(AssemblerProperties.PREFIX, indexNewLine); + } + return properties; + } + + /** + * Creation is protected. + */ + protected CompilerSourceParser() { + + } + + /** + * Gets the compiler syntax for this parser. + * + * @return The compiler syntax, not null. + */ + public final CompilerSyntax getCompilerSyntax() { + if (compilerSyntax == null) { + throw new IllegalStateException( + "Field 'compilerSyntax' must not be null."); + } + return compilerSyntax; + } + + /** + * Gets the instruction for the currently active CPU. + * + * @return The instruction set, not null. + * + * @since 1.6.1 + */ + public final InstructionSet getInstructionSet() { + if (instructionSet == null) { + throw new IllegalStateException( + "Field 'instructionSet' must not be null."); + } + return instructionSet; + } + + /** + * Logs a message to the error log in case logging is enabled. + * + * @param message + * The message with place holders, may be empty, not + * null. + * @param parameters + * The parameters for the place holders, may be empty or + * null. + */ + private void log(String message, Object... parameters) { + if (logEnabled) { + BasePlugin.getInstance().log(message, parameters); + } + + } + + /** + * Called by {@link Compiler} to link the parser to the compile syntax. + * + * @param instructionSet + * The instruction set, not null. + */ + public final void init(InstructionSet instructionSet) { + if (instructionSet == null) { + throw new IllegalArgumentException( + "Parameter 'instructionSet' must not be null."); + } + this.instructionSet = instructionSet; + this.compilerSyntax = instructionSet.getCompilerSyntax(); + + } + + /** + * Detects a file references in the given source line. This method is + * stateless. + * + * @param line + * The source line, may be empty, not null. + * @param include + * The modifiable include statement description, not + * null. + */ + public final void detectFileReference(String line, + CompilerSourceParserFileReference include) { + if (line == null) { + throw new IllegalArgumentException( + "Parameter 'line' must not be null."); + } + if (include == null) { + throw new IllegalArgumentException( + "Parameter 'include' must not be null."); + } + + boolean caseSenstive = instructionSet.areInstructionsCaseSensitive(); + if (!caseSenstive) { + line = line.toUpperCase(); + } + + // Find next (possible) quote. + int quoteOffset = -1; + Iterator i = compilerSyntax.getStringDelimiters().iterator(); + while (i.hasNext() && quoteOffset == -1) { + String quote = i.next(); + quoteOffset = line.indexOf(quote); + } + + for (Instruction instruction : instructionSet + .getFileReferenceInstructions()) { + int instructionOffset; + String instructionName; + + if (caseSenstive) { + instructionName = instruction.getName(); + } else { + instructionName = instruction.getUpperCaseName(); + } + instructionOffset = line.indexOf(instructionName); + + // Key word found before the quote? + if (quoteOffset > -1 && instructionOffset > -1 + && instructionOffset < quoteOffset) { + if (instruction.getType() == InstructionType.SOURCE_INCLUDE_DIRECTIVE) { + include.setType(CompilerSourceParserFileReferenceType.SOURCE); + } else if (instruction.getType() == InstructionType.BINARY_INCLUDE_DIRECTIVE + || instruction.getType() == InstructionType.BINARY_OUTPUT_DIRECTIVE) { + include.setType(CompilerSourceParserFileReferenceType.BINARY); + } else { + throw new IllegalStateException("Include instruction '" + + instructionName + "' has the unsupported type '" + + instruction.getType() + "'"); + } + + include.setDirectiveEndOffset(instructionOffset + + instructionName.length()); + return; + } + + } + } + + /** + * Enhances the file path of an include, for example add a default extension + * for source includes. + * + * @param type + * The type of include, see + * {@link CompilerSourceParserFileReferenceType}. + * + * @param documentDirectory + * The current directory which act as the basis for relative + * paths or not null if it is not known. + * + * @param filePath + * The possibly relative file path of the include file in OS + * specific notation, not empty and not null. + * @return The enhanced absolute file path of the include file in OS + * specific notation, not empty and not null. + */ + public final String getIncludeAbsoluteFilePath(int type, + File documentDirectory, String filePath) { + + if (filePath == null) { + throw new IllegalArgumentException( + "Parameter 'filePath' must not be null."); + } + + String relativeCurrentPrefix; + String relativeParentPrefix; + File absoluteFile; + relativeCurrentPrefix = "."; + relativeParentPrefix = ".."; + + // Ensure documentDirectory is a canonical file. + if (documentDirectory != null) { + documentDirectory = FileUtility.getCanonicalFile(documentDirectory); + } + + // Check double or single dots followed by path delimiter first. + int relativeParentPrefixLength = relativeParentPrefix.length() + 1; + int relativeCurrentPrefixLength = relativeCurrentPrefix.length() + 1; + if (filePath.startsWith(relativeParentPrefix) + && filePath.length() >= relativeParentPrefixLength) { + if (documentDirectory == null) { + return null; + } + absoluteFile = new File(documentDirectory.getParentFile(), + filePath.substring(relativeParentPrefixLength)); + + } else if (filePath.startsWith(relativeCurrentPrefix) + && filePath.length() >= relativeCurrentPrefixLength) { + if (documentDirectory == null) { + return null; + } + absoluteFile = new File(documentDirectory, + filePath.substring(relativeCurrentPrefixLength)); + } else { + + // If there is no file separator in the file name, we can assume a + // relative path based on the current directory. + File file = new File(filePath); + if (file.exists()) { + absoluteFile = file; + } else { + absoluteFile = new File(documentDirectory, filePath); + } + } + + // Ensure the complete file path is in OS notation. + absoluteFile = FileUtility.getCanonicalFile(absoluteFile); + String absoluteFilePath = absoluteFile.getPath(); + if (type == CompilerSourceParserFileReferenceType.SOURCE) { + int index = absoluteFilePath.lastIndexOf(File.separator); + if (index < 1) { + index = 0; + } + String fileName = absoluteFilePath.substring(index); + index = fileName.lastIndexOf('.'); + if (index == -1) { + String extension = compilerSyntax + .getSourceIncludeDefaultExtension(); + if (extension.length() > 0) { + absoluteFilePath = absoluteFilePath + "." + extension; + } + } + } + return absoluteFilePath; + } + + /** + * Creates a new, yet empty compiler source file. + * + * @param file + * The file, not null. + * @param document + * The document, not null. + * @return The compiler source file for use in + * {@link #parse(CompilerSourceFile, CompilerSourceParserLineCallback)} + * , not null. + * @since 1.6.1 + */ + public final CompilerSourceFile createCompilerSourceFile(File file, + IDocument document) { + if (compilerSyntax == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSyntax' must not be null."); + } + if (document == null) { + throw new IllegalArgumentException( + "Parameter 'document' must not be null."); + } + return new CompilerSourceFile(compilerSyntax, file, document); + } + + /** + * Creates a new compiler source file for persistent file. + * + * @param filePath + * The absolute file path, not empty and not null. + * @return The compiler source file, not null. + * + * @since 1.6.3 + */ + private CompilerSourceFile createCompilerSourceFile(String filePath) { + if (filePath == null) { + throw new IllegalArgumentException( + "Parameter 'filePath' must not be null."); + } + if (StringUtility.isEmpty(filePath)) { + throw new IllegalArgumentException( + "Parameter 'filePath' must not be empty."); + } + File newDocumentFile = new File(filePath); + String newDocumentContent; + try { + newDocumentContent = FileUtility.readString(newDocumentFile, + FileUtility.MAX_SIZE_UNLIMITED); + } catch (CoreException ex) { + newDocumentContent = compilerSyntax + .getSingleLineCommentDelimiters().get(0) + + " " + + ex.getMessage(); + } + IDocument newDocument = new Document(newDocumentContent); + CompilerSourceFile newSourceFile = createCompilerSourceFile( + newDocumentFile, newDocument); + return newSourceFile; + } + + /** + * Parse the new input and builds up the parse tree. + * + * @param compilerSourceFile + * The file to be parsed, not null. + * @param compilerSourceParserLineCallback + * The callback to be notified when a certain line is encountered + * or null. + */ + public final void parse(CompilerSourceFile compilerSourceFile, + CompilerSourceParserLineCallback compilerSourceParserLineCallback) { + if (compilerSourceFile == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSourceFile' must not be null."); + } + Map parsedFiles; + parsedFiles = new HashMap(); + parseInternal(compilerSourceFile, parsedFiles, + compilerSourceParserLineCallback); + return; + } + + /** + * Parse the new input and builds up the parse tree recursively with + * collecting already parsed includes. + * + * @param compilerSourceFile + * The file to be parsed, not null. + * @param parsedFiles + * The list of already parsed file names to prevent recursion, + * not null. + * @param compilerSourceParserLineCallback + * The callback to be notified when a certain line is encountered + * or null. + * @return true if the file was parsed now, false + * if the file is already in the list of parsed files. + */ + private boolean parseInternal(CompilerSourceFile compilerSourceFile, + Map parsedFiles, + CompilerSourceParserLineCallback compilerSourceParserLineCallback) { + if (compilerSourceFile == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSourceFile' must not be null."); + } + if (parsedFiles == null) { + throw new IllegalArgumentException( + "Parameter 'parsedFiles' must not be null."); + } + + // Ensure every persistent file is parsed only once to prevent infinite + // recursions cause by circular includes. Non-persistent files cannot + // cause recursions since they cannot be included yet. + if (compilerSourceFile.getDocumentFile() != null) { + String key = compilerSourceFile.getDocumentFile().getPath(); + if (parsedFiles.containsKey(key)) { + return false; + } + parsedFiles.put(key, compilerSourceFile); + } + + this.compilerSourceFile = compilerSourceFile; + + // To allow folding for introduction comment at the begin of the source, + // the definition section is always open already, even if it does not + // contain any definitions. + child = compilerSourceFile.getDefinitionSection(); + beginSection(0, true); + + IDocument document = compilerSourceFile.getDocument(); + int lines = document.getNumberOfLines(); + int lineOffset, lineLength, startOffset, endOffset; + String stringLine = ""; + + // Prepare line and document offsets. + lineOffset = 0; + lineLength = 0; + startOffset = 0; + endOffset = 0; + + // Prepare line section buffers. + StringBuilder symbolBuffer; + StringBuilder instructionBuffer; + StringBuilder operandBuffer; + StringBuilder commentBuffer; + char blockDefinitonStartCharacter; + char blockDefinitonEndCharacter; + + symbolBuffer = new StringBuilder(100); + instructionBuffer = new StringBuilder(100); + operandBuffer = new StringBuilder(100); + commentBuffer = new StringBuilder(100); + blockDefinitonStartCharacter = compilerSyntax + .getBlockDefinitionStartCharacter(); + blockDefinitonEndCharacter = compilerSyntax + .getBlockDefinitionEndCharacter(); + + for (int lineNumber = 0; lineNumber < lines; lineNumber++) { + + /** + * Part 1: Parse line segments from line strin. + */ + + int symbolOffset = 0; + boolean symbolOffsetFound = false; + symbolBuffer.setLength(0); + int instructionOffset = 0; + boolean instructionOffsetFound = false; + instructionBuffer.setLength(0); + int operandOffset = 0; + boolean operandOffsetFound = false; + operandBuffer.setLength(0); + int commentOffset = 0; + boolean commentOffsetFound = false; + commentBuffer.setLength(0); + + try { + IRegion region = document.getLineInformation(lineNumber); + lineOffset = region.getOffset(); + lineLength = region.getLength(); + stringLine = document.get(lineOffset, lineLength); + + int pos = 0; + char lastChar = 0; + int lineSection = LineSection.NONE; + while (pos < lineLength) { + char ch = stringLine.charAt(pos); + boolean whiteSpace = Character.isWhitespace(ch); + // Find the next word. + if (pos == 0 + || (!whiteSpace && Character.isWhitespace(lastChar))) { + + // Does the current section allow instructions? + if (CompilerSourceParserTreeObjectType + .areInstructionsAllowed(section.getType())) { + if (lineSection == LineSection.NONE) { + lineSection = LineSection.SYMBOL; + } else if (lineSection == LineSection.SYMBOL) { + lineSection = LineSection.INSTRUCTION; + if (symbolBuffer.length() > 0) { + String possibleInstruction = symbolBuffer + .toString().toUpperCase(); + if (isInstruction(possibleInstruction)) { + + instructionOffset = symbolOffset; + instructionOffsetFound = true; + instructionBuffer.append(symbolBuffer); + symbolOffset = 0; + symbolBuffer.setLength(0); + lineSection = LineSection.OPERAND; + } + } + } else if (lineSection == LineSection.INSTRUCTION) { + lineSection = LineSection.OPERAND; + } + } else { + // No instructions allowed. + if (!symbolOffsetFound) { + if (!whiteSpace + && lineSection == LineSection.NONE) { + lineSection = LineSection.SYMBOL; + } + } else { + lineSection = LineSection.OPERAND; + } + } + + } + String type = document.getPartition(lineOffset + pos) + .getType(); + if (type.equals(IDocument.DEFAULT_CONTENT_TYPE)) { + if (lineSection == LineSection.SYMBOL) { + + // TODO: Does not work with kernel equates + // if (symbolBuffer.length() == 0 && + // compilerSyntax.isIdentifierStartCharacter(ch) + // || symbolBuffer.length() > 0 && + // compilerSyntax.isIdentifierPartCharacter(ch)) + if (compilerSyntax.isIdentifierCharacter(ch)) { + if (!symbolOffsetFound) { + symbolOffsetFound = true; + symbolOffset = pos; + } + symbolBuffer.append(ch); + + } + } else if (lineSection == LineSection.INSTRUCTION) { + if (!whiteSpace) { + if (!instructionOffsetFound) { + instructionOffsetFound = true; + instructionOffset = pos; + } + instructionBuffer.append(ch); + } + } else { + if (!operandOffsetFound) { + operandOffsetFound = true; + operandOffset = pos; + } + operandBuffer.append(ch); + } + } else if (type + .equals(CompilerSourcePartitionScanner.PARTITION_COMMENT_SINGLE)) { + if (!commentOffsetFound) { + commentOffsetFound = true; + commentOffset = pos; + } + // Keep spaces within comments and convert tabs to + // spaces. + if (ch == 0x9) { + ch = ' '; + } + if (ch != 0xa && ch != 0xd) { + commentBuffer.append(ch); + } + } else if (type + .equals(CompilerSourcePartitionScanner.PARTITION_STRING)) { + operandBuffer.append(ch); + } + + lastChar = ch; + pos++; + } + + } catch (BadLocationException ex) { + throw new RuntimeException(ex); + } + + /** + * Part 2: Post processing of line segments + */ + startOffset = lineOffset; + try { + endOffset = startOffset + document.getLineLength(lineNumber); + } catch (BadLocationException ex) { + throw new RuntimeException(ex); + } + + // Check if the single symbol in the line is actually an + // instruction. + String possibleInstruction = symbolBuffer.toString().toUpperCase(); + if (isInstruction(possibleInstruction)) { + + instructionOffset = symbolOffset; + instructionBuffer.append(symbolBuffer); + symbolOffset = 0; + symbolBuffer.setLength(0); + } + + String symbol = symbolBuffer.toString(); + String instruction = instructionBuffer.toString(); + if (!instructionSet.areInstructionsCaseSensitive()) { + instruction = instruction.toUpperCase(); + } + + // Refine operand and detect block start and end. + String operand = operandBuffer.toString().trim(); + blockStarting = false; + if (operandOffsetFound) { + int blockStartOffset = operand + .indexOf(blockDefinitonStartCharacter); + if (blockStartOffset > -1) { + operand = operand.substring(0, blockStartOffset); + blockStarting = true; + } + } + blockEnding = false; + int blockEndOffset = stringLine.indexOf(blockDefinitonEndCharacter); + if (blockEndOffset > -1) { + if (!commentOffsetFound || blockEndOffset < commentOffset) { + blockEnding = true; + } + } + + // Refine comment. Strip leading single comment sign. + String comment = commentBuffer.toString(); + if (comment.length() > 0) { + for (String singleLineCommentDelimiter : compilerSyntax + .getSingleLineCommentDelimiters()) { + if (comment.startsWith(singleLineCommentDelimiter)) { + comment = comment.substring(singleLineCommentDelimiter + .length()); + } + } + comment = comment.trim(); + } + + /** + * Part 3: Parse labels or equates, either directly or via + * delegation. + */ + // This is an instance variable modified per line. + labelChild = null; + + // Depending on the current context parsing is delegated or not. + switch (section.getType()) { + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + if (symbol.length() > 0) { + String value = operand.trim(); + + if (value.startsWith("=")) { + value = value.substring(1).trim(); + } + if (value.length() == 0) { + value = "(auto)"; + } + createEquateDefinitionChild(startOffset, startOffset + + symbolOffset, symbol, value, comment); + } + break; + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + if (symbol.length() > 0) { + createLabelDefinitionChild(startOffset, startOffset + + symbolOffset, symbol, comment); + } + break; + default: + parseLine(startOffset, symbol, symbolOffset, instruction, + instructionOffset, operand, comment); + break; + } + + /** + * Part 4: Instruction based parsing + */ + int positionStartOffset = startOffset + instructionOffset; + parseInstruction(startOffset, endOffset, positionStartOffset, + symbol, instruction, operand, comment, parsedFiles, + compilerSourceParserLineCallback); + // Label not yet consumed by instruction, so it is a single label. + if (labelChild != null) { + section.addChild(labelChild); + } + + /** + * Part 6: Handle generic block starts and ends. + */ + if (blockStarting && child == null) { + // Block started but not yet converted into a child like a macro + // or procedure. + beginFolding(startOffset); + } + if (blockEnding) { + // Arbitrary block end, can be section or folding. + if (compilerSourceFile.isFoldingForSection()) { + endSection(startOffset + blockEndOffset); + } else { + endFolding(startOffset + blockEndOffset); + } + } + + /** + * Part 7: Callback for dedicated source file lines. + */ + // Was the current file and line relevant for the callback? + if (compilerSourceParserLineCallback != null + && compilerSourceParserLineCallback.getSourceFilePath() + .equals(compilerSourceFile.getDocumentFile() + .getPath()) + && lineNumber == compilerSourceParserLineCallback + .getLineNumber()) { + compilerSourceParserLineCallback.processLine(this, + compilerSourceFile, lineNumber, startOffset, + symbolOffset, isInstruction(instruction), + instructionOffset, instruction, operandOffset, section); + } + } + + // End last section. + endSection(document.getLength() > 0 ? document.getLength() - 1 : 0); + + // End incomplete sections. + compilerSourceFile.endAllSections(); + + // End incomplete sections. + compilerSourceFile.endAllFoldings(); + + return true; + } + + private boolean isInstruction(String instructionName) { + if (instructionName == null) { + throw new IllegalArgumentException( + "Parameter 'instructionName' must not be null."); + } + boolean result; + result = instructionSet.getInstruction(instructionName) != null; + return result; + } + + protected void parseLine(int startOffset, String symbol, int symbolOffset, + String instruction, int instructionOffset, String operand, + String comment) { + return; + } + + protected final void createEquateDefinitionChild(int startOffset, + int positionStartOffset, String symbol, String operand, + String comment) { + ensureDefinitionSection(startOffset, positionStartOffset); + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.EQUATE_DEFINITION, symbol, + symbol + " = " + operand, comment); + section.addChild(child); + + } + + protected final void createLabelDefinitionChild(int startOffset, + int positionStartOffset, String symbol, String comment) { + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.LABEL_DEFINITION, symbol, + symbol, comment); + // Remember the label child. It will be added only if the label is not + // consumed by some other instruction. + labelChild = child; + } + + protected final void beginImplementationSection(int startOffset, + int positionStartOffset, String operand, String comment) { + + if (section.getParent() != null) { + return; + } + int endOffset = startOffset - 1; + if (endOffset < 0) { + endOffset = 0; + } + endSection(endOffset); + + // Folding for an implementation section should only bebe active if + // there is´a name section in the code. + boolean withFolding = (StringUtility.isSpecified(operand)); + if (StringUtility.isEmpty(operand)) { + operand = "Implementation Section"; + } + + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.IMPLEMENTATION_SECTION, + operand, operand, comment); + + // This will always be a top level section. + beginSection(startOffset, withFolding); + compilerSourceFile.getImplementationSections().add(section); + labelChild = null; + } + + /** + * Parse the instruction in a single line. + * + * @param startOffset + * The start offset of the line. + * @param endOffset + * The end offset of the line, including its last character and + * the line delimiter is available. + * @param positionStartOffset + * The start offset for positioning the cursor. + * @param symbol + * The symbol name, may be empty, not null. + * @param instructionName + * The instruction name, may be empty, not null. + * @param operand + * The operand, may be empty, not null. + * @param comment + * The comment, may be empty, not null. + * @param parsedFiles + * The parsed files, not null. + * @param compilerSourceParserLineCallback + * The callback to be notified when a certain line is encountered + * or null. + */ + private void parseInstruction(int startOffset, int endOffset, + int positionStartOffset, String symbol, String instructionName, + String operand, String comment, + Map parsedFiles, + CompilerSourceParserLineCallback compilerSourceParserLineCallback) { + + if (StringUtility.isEmpty(instructionName)) { + return; + } + Instruction instruction = instructionSet + .getInstruction(instructionName); + + if (instruction == null) { + return; + } + + if (logEnabled) { + log("parseInstruction: startOffset={0} endOffset={1} positionStartOffset={2}, symbol={3} instructionName={4} symbol={5} instructionType={6} labelChild={7}", + Integer.toString(startOffset), Integer.toString(endOffset), + Integer.toString(positionStartOffset), symbol, + instructionName, operand, + Integer.toString(instruction.getType()), labelChild); + } + + String symbolOrOperand; + String symbolOrOperandFirstWord; + symbolOrOperand = symbol; + if (StringUtility.isEmpty(symbolOrOperand)) { + symbolOrOperand = operand; + } + symbolOrOperandFirstWord = symbolOrOperand; + int i = 0; + for (; i < symbolOrOperandFirstWord.length(); i++) { + if (Character.isWhitespace(symbolOrOperandFirstWord.charAt(i))) { + break; + } + } + symbolOrOperandFirstWord = symbolOrOperandFirstWord.substring(0, i); + + switch (instruction.getType()) { + case InstructionType.BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE: + beginImplementationSection(startOffset, positionStartOffset, + operand, comment); + break; + case InstructionType.DIRECTIVE: + if (blockStarting) { + beginFolding(startOffset); + } + break; + case InstructionType.BEGIN_FOLDING_BLOCK_DIRECTIVE: + beginFolding(startOffset); + break; + case InstructionType.END_FOLDING_BLOCK_DIRECTIVE: + endFolding(endOffset); + break; + case InstructionType.END_SECTION_DIRECTIVE: + endSection(endOffset); + break; + + case InstructionType.BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE: + ensureDefinitionSection(startOffset, positionStartOffset); + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION, + symbolOrOperandFirstWord, symbolOrOperand, comment); + beginSection(startOffset, true); + break; + + case InstructionType.BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE: + ensureDefinitionSection(startOffset, positionStartOffset); + createChild( + positionStartOffset, + CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION, + symbolOrOperandFirstWord, symbolOrOperand, comment); + beginSection(startOffset, true); + break; + + case InstructionType.BEGIN_LOCAL_SECTION_DIRECTIVE: + ensureImplementationSection(startOffset, positionStartOffset); + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.LOCAL_SECTION, + symbolOrOperandFirstWord, symbolOrOperand, comment); + beginSection(startOffset, true); + + break; + case InstructionType.BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE: + ensureDefinitionSection(startOffset, positionStartOffset); + createChild( + positionStartOffset, + CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION, + symbolOrOperandFirstWord, symbolOrOperand, comment); + beginSection(startOffset, true); + break; + case InstructionType.BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE: + ensureImplementationSection(startOffset, positionStartOffset); + createChild( + positionStartOffset, + CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION, + symbolOrOperandFirstWord, symbolOrOperand, comment); + beginSection(startOffset, true); + break; + case InstructionType.BEGIN_PAGES_SECTION_DIRECTIVE: + ensureImplementationSection(startOffset, positionStartOffset); + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.PAGES_SECTION, "", + symbolOrOperand, comment); + beginSection(startOffset, true); + break; + case InstructionType.BEGIN_REPEAT_SECTION_DIRECTIVE: + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.REPEAT_SECTION, "", + symbolOrOperand, comment); + beginSection(startOffset, true); + break; + + case InstructionType.SOURCE_INCLUDE_DIRECTIVE: + // Remove leading and trailing string delimiters. + String filePath = operand; + for (String stringDelimiter : compilerSyntax.getStringDelimiters()) { + if (filePath.startsWith(stringDelimiter)) { + filePath = filePath.substring(stringDelimiter.length()); + break; + } + } + for (String stringDelimiter : compilerSyntax.getStringDelimiters()) { + if (filePath.endsWith(stringDelimiter)) { + filePath = filePath.substring(0, filePath.length() + - stringDelimiter.length()); + break; + } + } + filePath = getIncludeAbsoluteFilePath( + CompilerSourceParserFileReferenceType.SOURCE, + compilerSourceFile.getDocumentDirectory(), filePath); + + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.SOURCE_INCLUDE, "", + operand, comment); + + // If there is no file, the include is a normal child. + if (filePath == null) { + if (labelChild == null) { + section.addChild(child); + } else { + labelChild.addChild(child); + } + } else { + // If there is a file, the include is a section. + beginSection(startOffset, true); + + // Preserve current line specific state into local variables. + CompilerSourceFile oldSourceFile = compilerSourceFile; + CompilerSourceParserTreeObject oldSection = section; + CompilerSourceParserTreeObject oldChild = child; + CompilerSourceParserTreeObject oldLabelChild = labelChild; + boolean oldBlockStarting = blockStarting; + boolean oldBlockEnding = blockEnding; + + CompilerSourceFile newSourceFile = createCompilerSourceFile(filePath); + + // This might be moved to the createCompilerSourceFile() method. + CompilerSourcePartitionScanner partitionScanner = new CompilerSourcePartitionScanner( + compilerSyntax); + partitionScanner.createDocumentPartitioner(newSourceFile + .getDocument()); + boolean parsed = parseInternal(newSourceFile, parsedFiles, + compilerSourceParserLineCallback); + + if (parsed) { + // Restore old line specific state from local variables. + section = oldSection; + compilerSourceFile = oldSourceFile; + child = oldChild; + labelChild = oldLabelChild; + blockStarting = oldBlockStarting; + blockEnding = oldBlockEnding; + + section.setIncludedCompilerSourceFile(newSourceFile); + List newSourceFileSections = newSourceFile + .getSections(); + if (newSourceFileSections.size() == 1 + && newSourceFileSections.get(0).getType() == CompilerSourceParserTreeObjectType.SOURCE_INCLUDE) { + newSourceFileSections = newSourceFileSections.get(0) + .getChildren(); + } + for (CompilerSourceParserTreeObject newChild : newSourceFileSections) { + section.addChild(newChild); + } + } else { + AssemblerPlugin + .getInstance() + .log("Include file '{0}' was already parsed. Stopping recursion.", + new Object[] { newSourceFile + .getDocumentFile().getPath() }); + } + endSection(endOffset); + } + break; + case InstructionType.BINARY_INCLUDE_DIRECTIVE: + ensureImplementationSection(startOffset, positionStartOffset); + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.BINARY_INCLUDE, "", + operand, comment); + if (labelChild == null) { + section.addChild(child); + } else { + labelChild.addChild(child); + } + + break; + case InstructionType.BINARY_OUTPUT_DIRECTIVE: + + createChild(positionStartOffset, + CompilerSourceParserTreeObjectType.BINARY_OUTPUT, "", + operand, comment); + if (labelChild == null) { + section.addChild(child); + } else { + labelChild.addChild(child); + } + + break; + } + + } + + private void ensureDefinitionSection(int startOffset, + int positionStartOffset) { + // To allow folding for introduction comment at the begin of the source, + // the definition section is always open already. + } + + private void ensureImplementationSection(int startOffset, + int positionStartOffset) { + if (section.getType() == CompilerSourceParserTreeObjectType.DEFINITION_SECTION) { + beginImplementationSection(startOffset, positionStartOffset, "", ""); + } + } + + private void createChild(int startOffset, int type, String name, + String displayName, String comment) { + if (startOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'startOffset' must not be negative. Specified value is " + + startOffset + "."); + } + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'name' must not be null."); + } + if (displayName == null) { + throw new IllegalArgumentException( + "Parameter 'displayName' must not be null."); + } + if (comment == null) { + throw new IllegalArgumentException( + "Parameter 'comment' must not be null."); + } + + child = new CompilerSourceParserTreeObject(compilerSourceFile, + startOffset, type, name, displayName, comment); + } + + /** + * Starts a new active section. + * + * @param startOffset + * The start offset, a non-negative integer. + * @param withFolding + * true if the section is also a folding section, + * false otherwise. + */ + private void beginSection(int startOffset, boolean withFolding) { + if (startOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'startOffset' must not be negative. Specified value is " + + startOffset + "."); + } + if (child == null) { + throw new IllegalStateException("Field 'child' must not be null."); + } + section = child; + compilerSourceFile.beginSection(startOffset, section, withFolding); + labelChild = null; + blockStarting = false; + } + + /** + * Ends the currently active section. + * + * @param endOffset + * The end offset, a non-negative integer. + */ + private void endSection(int endOffset) { + if (section == null) { + throw new IllegalStateException( + "Variable 'section' must not be null."); + } + if (endOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'endOffset' must not be negative. Specified value is " + + endOffset + "."); + } + CompilerSourceParserTreeObject section; + section = compilerSourceFile.endSection(endOffset); + if (section != null) { + this.section = section; + } + labelChild = null; + blockEnding = false; + } + + /** + * Starts a new active folding which does not belong to a section. + * + * @param startOffset + * The start offset, a non-negative integer. + */ + private void beginFolding(int startOffset) { + compilerSourceFile.beginFolding(startOffset, false); + blockStarting = false; + } + + /** + * Ends the currently active folding which does not belong to a section. + * + * @param endOffset + * The end offset, a non-negative integer. This must be the + * actual offset of the last character in the line, including the + * line end delimiter, if present. + */ + private void endFolding(int endOffset) { + compilerSourceFile.endFolding(endOffset); + blockEnding = false; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserFileReference.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserFileReference.java new file mode 100644 index 00000000..a3c191c6 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserFileReference.java @@ -0,0 +1,72 @@ +package com.wudsn.ide.asm.compiler.parser; + +public final class CompilerSourceParserFileReference { + + private int type; + private int directiveEndOffset; + + /** + * Creation is public. + */ + public CompilerSourceParserFileReference() { + type = CompilerSourceParserFileReferenceType.NONE; + directiveEndOffset = 0; + + } + + /** + * Sets the type of the include. + * + * @param type + * The type of the include, see + * {@link CompilerSourceParserFileReferenceType}. + */ + public void setType(int type) { + switch (type) { + case CompilerSourceParserFileReferenceType.SOURCE: + case CompilerSourceParserFileReferenceType.BINARY: + break; + + default: + throw new IllegalArgumentException("Illegal include type " + type + + "."); + } + this.type = type; + } + + /** + * Get the type of the include. + * + * @return The type of the include, see + * {@link CompilerSourceParserFileReferenceType}. + */ + public int getType() { + return type; + } + + /** + * Sets the include directive end offset. The value must be the offset of + * first character after the directive. + * + * @param directiveEndOffset + * The include directive end offset, a non-negative integer. + */ + public void setDirectiveEndOffset(int directiveEndOffset) { + if (directiveEndOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'directiveEndOffset' must not be be negative. Specified value was " + + directiveEndOffset + "."); + } + this.directiveEndOffset = directiveEndOffset; + } + + /** + * Gets the include directive end offset. + * + * @return The include directive end offset, a non-negative integer. + */ + public int getDirectiveEndOffset() { + return directiveEndOffset; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserFileReferenceType.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserFileReferenceType.java new file mode 100644 index 00000000..de62c379 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserFileReferenceType.java @@ -0,0 +1,38 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.parser; + +/** + * The possible types of referenced files. + * + * @author Peter Dell + */ +public final class CompilerSourceParserFileReferenceType { + + /** + * Creation is private. + */ + private CompilerSourceParserFileReferenceType() { + } + + public final static int NONE=1; + public final static int SOURCE = 2; + public final static int BINARY = 3; +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserLineCallback.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserLineCallback.java new file mode 100644 index 00000000..630a287b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserLineCallback.java @@ -0,0 +1,85 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.parser; + +/** + * Callback interface to react on the parsing of certain source file lines. + * + * @author Peter Dell + * + * @since 1.6.0 + */ +public abstract class CompilerSourceParserLineCallback { + private String filePath; + private int lineNumber; + + /** + * Creation is protected. + * + * @param filePath + * The absolute path of the source file, not empty and not + * null. + * @param lineNumber + * The line number, a non-negative integer or -1 to + * indicate that no line number is relevant. + */ + protected CompilerSourceParserLineCallback(String filePath, int lineNumber) { + if (filePath == null) { + throw new IllegalArgumentException( + "Parameter 'filePath' must not be null."); + } + if (lineNumber < 0) { + throw new IllegalArgumentException( + "Parameter 'lineNumber' must not be negative. Specified value is " + + lineNumber + "."); + } + this.filePath = filePath; + this.lineNumber = lineNumber; + } + + /** + * Gets the path of the source file for which for which the callback shall + * be triggered. + * + * @return The absolute path of the source file, not empty and not + * null. + */ + public final String getSourceFilePath() { + return filePath; + } + + /** + * Gets the line number for which the callback shall be triggered. + * + * @return The line number, a non-negative integer or -1 to + * indicate that no line number is relevant. + */ + public final int getLineNumber() { + return lineNumber; + } + + // Most of the parameters are currently not used by the consumer. + public abstract void processLine(CompilerSourceParser compilerSourceParser, + CompilerSourceFile compilerSourceFile, int lineNumber, + int startOffset, int symbolOffset, boolean instructionFound, + int instructionOffset, String instruction, int operandOffset, + CompilerSourceParserTreeObject section); + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserTreeObject.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserTreeObject.java new file mode 100644 index 00000000..b2faaa1f --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserTreeObject.java @@ -0,0 +1,411 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.base.common.StringUtility; + +/** + * The object representing a node in the content outline tree. + * + * @author Peter Dell + * @author Andy Reek + */ +public final class CompilerSourceParserTreeObject { + + private final CompilerSourceFile compilerSourceFile; + + private final int startOffset; + + private final int type; + + private final String name; + + private final String displayName; + + private final String description; + + private final StyledString styledString; + + private final List children; + + private CompilerSourceParserTreeObject parent; + + private String treePath; + + private String compoundName; + + private CompilerSourceFile includedCompilerSourceFile; + + /** + * Create a new instance. + * + * @param compilerSourceFile + * The source file to which this parser tree object belongs. + * + * @param startOffset + * The start offset of the instance, a non-negative integer. + * @param type + * The type of a instance, see + * {@link CompilerSourceParserTreeObjectType}. + * @param name + * The name of the tree object, not null. It is used + * to build the full tree path, + * @param displayName + * The display name of the tree object, not null. It + * is used in the tree view. + * @param description + * The description of the tree object, not null. + */ + CompilerSourceParserTreeObject(CompilerSourceFile compilerSourceFile, + int startOffset, int type, String name, String displayName, + String description) { + if (compilerSourceFile == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSourceFile' must not be null."); + } + if (startOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'startOffset' must not be negative. Specified value is " + + startOffset + "."); + } + this.compilerSourceFile = compilerSourceFile; + this.startOffset = startOffset; + + switch (type) { + case CompilerSourceParserTreeObjectType.DEFAULT: + + case CompilerSourceParserTreeObjectType.DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.IMPLEMENTATION_SECTION: + + case CompilerSourceParserTreeObjectType.EQUATE_DEFINITION: + case CompilerSourceParserTreeObjectType.LABEL_DEFINITION: + + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.LOCAL_SECTION: + case CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.PAGES_SECTION: + case CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.REPEAT_SECTION: + + case CompilerSourceParserTreeObjectType.SOURCE_INCLUDE: + case CompilerSourceParserTreeObjectType.BINARY_INCLUDE: + case CompilerSourceParserTreeObjectType.BINARY_OUTPUT: + + break; + + default: + throw new IllegalArgumentException("Unknown type " + type + "."); + } + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'name' must not be null."); + } + if (displayName == null) { + throw new IllegalArgumentException( + "Parameter 'displayName' must not be null."); + } + if (description == null) { + throw new IllegalArgumentException( + "Parameter 'description' must not be null."); + } + this.type = type; + this.name = name; + this.displayName = displayName; + this.description = description; + + styledString = new StyledString(displayName); + if (description.length() > 0) { + styledString.append(" "); + styledString.append(description, StyledString.QUALIFIER_STYLER); + } + this.children = new ArrayList(); + } + + /** + * Gets the compiler source file, this parser tree object belongs to. + * + * @return The compiler source file, this parser tree object belongs to, not + * null. + */ + public CompilerSourceFile getCompilerSourceFile() { + return compilerSourceFile; + } + + /** + * Gets the start offset of this parser tree object in the compiler source + * file. + * + * @return The start offset of this parser tree object in the compiler + * source file, a non-negative integer. + */ + public int getStartOffset() { + return startOffset; + } + + /** + * Gets the type of the object. + * + * @return The type, see {@link CompilerSourceParserTreeObjectType}. + */ + public int getType() { + return type; + } + + /** + * Gets the name of the object. + * + * @return The name, not null. + */ + public String getName() { + return name; + } + + /** + * Gets the compound name of the object. + * + * @return The compound name, not null. + */ + public String getCompoundName() { + if (compoundName == null) { + char identifierSeparatorCharacter = compilerSourceFile + .getCompilerSyntax().getIdentifierSeparatorCharacter(); + switch (type) { + case CompilerSourceParserTreeObjectType.DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.IMPLEMENTATION_SECTION: + case CompilerSourceParserTreeObjectType.REPEAT_SECTION: + case CompilerSourceParserTreeObjectType.SOURCE_INCLUDE: + case CompilerSourceParserTreeObjectType.BINARY_INCLUDE: + case CompilerSourceParserTreeObjectType.BINARY_OUTPUT: + if (parent != null) { + compoundName = parent.getCompoundName(); + } else { + compoundName = ""; + } + return compoundName; + } + + compoundName = name; + + // Compound identifiers supported? + if (identifierSeparatorCharacter != CompilerSyntax.NO_CHARACTER) { + + // Find next named parent. + CompilerSourceParserTreeObject namedParent = parent; + while (namedParent != null + // && namedParent.getType() != + // CompilerSourceParserTreeObjectType.DEFINITION_SECTION + // && namedParent.getType() != + // CompilerSourceParserTreeObjectType.IMPLEMENTATION_SECTION + // && namedParent.getType() != + // CompilerSourceParserTreeObjectType.REPEAT_SECTION + // && namedParent.getType() != + // CompilerSourceParserTreeObjectType.SOURCE_INCLUDE + // && namedParent.getType() != + // CompilerSourceParserTreeObjectType.BINARY_INCLUDE + // && namedParent.getType() != + // CompilerSourceParserTreeObjectType.BINARY_OUTPUT + + && namedParent.getType() != CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION + && namedParent.getType() != CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION + && namedParent.getType() != CompilerSourceParserTreeObjectType.LOCAL_SECTION + && namedParent.getType() != CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION + && namedParent.getType() != CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION) { + namedParent = namedParent.getParent(); + } + if (namedParent != null + && StringUtility.isSpecified(compoundName)) { + String parentCompoundName; + parentCompoundName = parent.getCompoundName(); + if (StringUtility.isSpecified(parentCompoundName)) { + compoundName = parentCompoundName + + identifierSeparatorCharacter + compoundName; + } + } + } + } + + return compoundName; + + } + + /** + * Gets the display name of the object. + * + * @return The display name, not null. + */ + public String getDisplayName() { + return displayName; + } + + /** + * Gets the unique tree path of this tree object within the tree. + * + * @return The unique tree path, not empty and not null. + */ + public String getTreePath() { + if (treePath == null) { + treePath = "\"" + type + "/" + name + "\""; + if (parent != null) { + treePath = parent.getTreePath() + "/" + treePath; + } + } + return treePath; + } + + /** + * Gets the description of the object. + * + * @return The description, not null. + */ + public String getDescription() { + return description; + } + + /** + * Gets the styled string of the object. + * + * @return The styled string, not null. + */ + public StyledString getStyledString() { + return styledString; + } + + /** + * Gets the parent object of the tree object. + * + * @return The parent, may be null. + */ + public CompilerSourceParserTreeObject getParent() { + return parent; + } + + /** + * Sets a new parent. + * + * @param parent + * The new parent, may be null. + */ + final void setParent(CompilerSourceParserTreeObject parent) { + this.parent = parent; + this.treePath = null; + } + + /** + * Determines if this tree object has children. + * + * @return true if this tree object has children, + * false otherwise. + */ + public boolean hasChildren() { + return !children.isEmpty(); + } + + /** + * Gets all children of the tree object. + * + * @return The array of children, not null.. + */ + public Object[] getChildrenAsArray() { + return children.toArray(new Object[0]); + } + + /** + * Gets all children of the tree object. + * + * @return The array of children, not null. Do not modify. + */ + public List getChildren() { + return children; + } + + /** + * Adds a new Child. + * + * @param child + * The new child, not null. + */ + final void addChild(CompilerSourceParserTreeObject child) { + if (child == null) { + throw new IllegalArgumentException( + "Parameter 'child' must not be null."); + } + if (child == this) { + throw new IllegalArgumentException( + "Parameter 'child' must not be this: " + toString()); + } + children.add(child); + child.setParent(this); + + } + + /** + * Gets the included compiler source file for a SOURCE_INCLUDE tree object. + * + * @return The included compiler source file or null. + */ + final CompilerSourceFile setIncludedCompilerSourceFile() { + return includedCompilerSourceFile; + } + + /** + * Sets the included compiler source file for a SOURCE_INCLUDE tree object. + * + * @param includedCompilerSourceFile + * The included compiler source file, may be null. + */ + final void setIncludedCompilerSourceFile( + CompilerSourceFile includedCompilerSourceFile) { + if (type != CompilerSourceParserTreeObjectType.SOURCE_INCLUDE) { + throw new IllegalStateException("The type of this tree object is " + + type + " and not SOURCE_INCLUDE"); + } + this.includedCompilerSourceFile = includedCompilerSourceFile; + + } + + + @Override + public boolean equals(Object object) { + if (!(object instanceof CompilerSourceParserTreeObject)) { + return false; + } + CompilerSourceParserTreeObject other; + other = (CompilerSourceParserTreeObject) object; + return getTreePath().equals(other.getTreePath()); + } + + @Override + public int hashCode() { + return getTreePath().hashCode(); + + } + + @Override + public String toString() { + return getTreePath(); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserTreeObjectType.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserTreeObjectType.java new file mode 100644 index 00000000..6f74f2af --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourceParserTreeObjectType.java @@ -0,0 +1,143 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ +package com.wudsn.ide.asm.compiler.parser; + +import com.wudsn.ide.asm.Texts; + +/** + * Constant values for tree object types. + * + * @author Peter Dell + */ +public final class CompilerSourceParserTreeObjectType { + + /** + * Creation is private. + */ + private CompilerSourceParserTreeObjectType() { + } + + /** + * Default-type + */ + public static final int DEFAULT = 0; + + public static final int DEFINITION_SECTION = 1; + public static final int IMPLEMENTATION_SECTION = 2; + + public static final int EQUATE_DEFINITION = 3; + public static final int LABEL_DEFINITION = 4; + + public static final int ENUM_DEFINITION_SECTION = 5; + public static final int STRUCTURE_DEFINITION_SECTION = 6; + public static final int LOCAL_SECTION = 7; + public static final int MACRO_DEFINITION_SECTION = 8; + public static final int PAGES_SECTION = 9; + public static final int PROCEDURE_DEFINITION_SECTION = 10; + public static final int REPEAT_SECTION = 11; + + public static final int SOURCE_INCLUDE = 12; + public static final int BINARY_INCLUDE = 13; + public static final int BINARY_OUTPUT = 14; + + /** + * Gets the localized text for a compiler source parser tree object type. + * + * @param type + * The type, see constants of this class. + * @return The localized text, may be empty but not null. + */ + public static String getText(int type) { + String result; + switch (type) { + case DEFAULT: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFAULT; + break; + case DEFINITION_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_DEFINITION_SECTION; + break; + case IMPLEMENTATION_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_IMPLEMENTATION_SECTION; + break; + case EQUATE_DEFINITION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_EQUATE_DEFINITION; + break; + case LABEL_DEFINITION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LABEL_DEFINITION; + break; + case ENUM_DEFINITION_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_ENUM_DEFINITION_SECTION; + break; + case STRUCTURE_DEFINITION_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_STRUCTURE_DEFINITION_SECTION; + break; + case LOCAL_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_LOCAL_SECTION; + break; + case MACRO_DEFINITION_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_MACRO_DEFINITION_SECTION; + break; + case PAGES_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PAGES_SECTION; + break; + case PROCEDURE_DEFINITION_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_PROCEDURE_DEFINITION_SECTION; + break; + case REPEAT_SECTION: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_REPEAT_SECTION; + break; + case SOURCE_INCLUDE: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_SOURCE_INCLUDE; + break; + case BINARY_INCLUDE: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_INCLUDE; + break; + case BINARY_OUTPUT: + result = Texts.COMPILER_SOURCE_PARSER_TREE_OBJECT_TYPE_BINARY_OUTPUT; + break; + default: + throw new IllegalArgumentException("Unknown type " + type + "."); + } + return result; + + } + + /** + * Determines if instructions are allowed in the given type of section. + * + * @param type + * The type of the tree object, {link + * {@link CompilerSourceParserTreeObjectType} + * @return true if instructions are allowed, false + * otherwise. + * + * @since 1.6.0 + */ + public static boolean areInstructionsAllowed(int type) { + // Within ENUM_DEFINITION_SECTION everything is an equate. + // Within STRUCTURE_DEFINITION_SECTION everything is a label. + switch (type) { + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + return false; + default: + return true; + } + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourcePartitionScanner.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourcePartitionScanner.java new file mode 100644 index 00000000..8e3e08c0 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/parser/CompilerSourcePartitionScanner.java @@ -0,0 +1,141 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.parser; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.rules.EndOfLineRule; +import org.eclipse.jface.text.rules.FastPartitioner; +import org.eclipse.jface.text.rules.IPredicateRule; +import org.eclipse.jface.text.rules.IRule; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.MultiLineRule; +import org.eclipse.jface.text.rules.RuleBasedPartitionScanner; +import org.eclipse.jface.text.rules.SingleLineRule; +import org.eclipse.jface.text.rules.Token; + +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.asm.editor.AssemblerEditor; + +/** + * A partition scanner for the comments and strings. + * + * @author Peter Dell + * @author Andy Reek + */ +public final class CompilerSourcePartitionScanner extends + RuleBasedPartitionScanner { + + /** + * Name for the single line comment partition. + */ + public static final String PARTITION_COMMENT_SINGLE = "partition.comment.single"; //$NON-NLS-1$ + + /** + * Name for the multiple lines comment partition. + */ + public static final String PARTITION_COMMENT_MULTIPLE = "partition.comment.multiple"; //$NON-NLS-1$ + + /** + * Name for the string partition. + */ + public static final String PARTITION_STRING = "partition.string"; //$NON-NLS-1$ + + /** + * Creates a new instance. + * + * Called by + * {@link AssemblerEditor#init(org.eclipse.ui.IEditorSite, org.eclipse.ui.IEditorInput)} + * . + * + * @param compilerSyntax + * The compiler syntax, not null. + */ + public CompilerSourcePartitionScanner(CompilerSyntax compilerSyntax) { + if (compilerSyntax == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSyntax' must not be null."); + } + IToken commentSingleToken = new Token(PARTITION_COMMENT_SINGLE); + IToken commentMultipleToken = new Token(PARTITION_COMMENT_MULTIPLE); + IToken stringToken = new Token(PARTITION_STRING); + + List rules = new ArrayList(); + for (String singleLineCommentDelimiter : compilerSyntax + .getSingleLineCommentDelimiters()) { + + // A "*" is only a comment start token if it is followed by a space + // or a tab. + // It is allowed as part of an expression to refer to the program + // counter. + if (singleLineCommentDelimiter.equals("*")) { + rules.add(new EndOfLineRule(singleLineCommentDelimiter + " ", + commentSingleToken)); + rules.add(new EndOfLineRule(singleLineCommentDelimiter + "\t", + commentSingleToken)); + } else { + rules.add(new EndOfLineRule(singleLineCommentDelimiter, + commentSingleToken)); + } + } + List multipleLinesCommentDelimiters = compilerSyntax + .getMultipleLinesCommentDelimiters(); + for (int i = 0; i < multipleLinesCommentDelimiters.size();) { + String startSequence = multipleLinesCommentDelimiters.get(i++); + String endSequence = multipleLinesCommentDelimiters.get(i++); + rules.add(new MultiLineRule(startSequence, endSequence, + commentMultipleToken)); + } + + for (String stringDelimiter : compilerSyntax.getStringDelimiters()) { + rules.add(new SingleLineRule(stringDelimiter, stringDelimiter, + stringToken)); + } + + + IPredicateRule[] rulesArray = new IPredicateRule[rules.size()]; + rules.toArray(rulesArray); + setPredicateRules(rulesArray); + } + + /** + * Creates a new FastPartitioner based on this partition scanner and + * connects it to the document. + * + * @param document + * The document, not null. + */ + public void createDocumentPartitioner(IDocument document) { + + if (document == null) { + throw new IllegalArgumentException( + "Parameter 'document' must not be null."); + } + FastPartitioner partitioner = new FastPartitioner(this, new String[] { + CompilerSourcePartitionScanner.PARTITION_COMMENT_SINGLE, + CompilerSourcePartitionScanner.PARTITION_COMMENT_MULTIPLE, + CompilerSourcePartitionScanner.PARTITION_STRING }); + partitioner.connect(document); + document.setDocumentPartitioner(partitioner); + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntax.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntax.java new file mode 100644 index 00000000..c01c76f5 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntax.java @@ -0,0 +1,936 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.helpers.DefaultHandler; + +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Container for a set of directives, legal, illegal and pseudo opcodes and + * their properties. + * + * @author Peter Dell + * @author Daniel Mitte + * + */ +public final class CompilerSyntax { + + public static final char NO_CHARACTER = (char) 0; + + private static final class XMLHandler extends DefaultHandler { + + // Completion proposal auto activation characters, possibly empty array + // of characters. + String completionProposalAutoActivationCharactersText; + + // Single line delimiters, list of strings separated by spaces. + String singleLineCommentDelimitersText; + + // Single line delimiters, list of strings separated by spaces. + String multipleLinesCommentDelimitersText; + + // Single line delimiters, non empty array of characters. + String stringDelimiterCharactersText; + + // The (possibly empty) string of character pairs which define the begin + // and end of a named or unnamed (folding) block. + String blockDefinitionCharactersText; + + // The boolean value indicating if identifiers are case sensitive or + // not. + boolean identifiersCaseSensitive; + + // The string containing all allowed identifier start characters. + String identifierStartCharactersText; + + // The string containing all allowed identifier start characters. + String identifierPartCharactersText; + + // The string containing the character which separates two parts of a + // compound identifier or + // the empty string if compound identifiers are not supported. + String identifierSeparatorCharacterText; + + // The string containing the character which end a label definition or + // the empty string if label definitions end at the first white space. + String labelDefinitionSuffixCharacterText; + + // The string containing the character which start a macro usage or + // the empty string if macro usages are not prefixed with a character. + String macroUsagePrefixCharacterText; + + // The boolean value indicating if instructions are case sensitive or + // not. + boolean instructionsCaseSensitive; + + // Default file extension to be added if it is missing for source + // include directives. + String sourceIncludeDefaultExtension; + + List instructionsList; + + public XMLHandler() { + instructionsList = new ArrayList(); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + if (qName == null) { + throw new IllegalArgumentException("Parameter 'qName' must not be null."); + } + if (attributes == null) { + throw new IllegalArgumentException("Parameter 'attributes' must not be null."); + } + + if (qName.equals("instructionset")) { + + // Completion proposal auto activation characters + completionProposalAutoActivationCharactersText = attributes + .getValue("completionProposalAutoActivationCharacters"); + if (completionProposalAutoActivationCharactersText == null) { + throw new SAXException("No completionProposalAutoActivationCharacters specified."); + } + + // Single lines comments. + singleLineCommentDelimitersText = attributes.getValue("singleLineCommentDelimiters"); + if (singleLineCommentDelimitersText == null) { + throw new SAXException("No singleLineCommentDelimiters specified."); + } + if (StringUtility.isEmpty(singleLineCommentDelimitersText)) { + throw new SAXException("Attribute singleLineCommentDelimiterst must not be empty."); + } + + // Multiple lines comments. + multipleLinesCommentDelimitersText = attributes.getValue("multipleLinesCommentDelimiters"); + if (multipleLinesCommentDelimitersText == null) { + throw new SAXException("No multipleLinesCommentDelimiters specified."); + } + + // Strings. + stringDelimiterCharactersText = attributes.getValue("stringDelimiterCharacters"); + if (stringDelimiterCharactersText == null) { + throw new SAXException("No stringDelimiterCharacters specified."); + } + if (StringUtility.isEmpty(stringDelimiterCharactersText)) { + throw new SAXException("Attribute stringDelimiterCharacters must not be empty."); + } + + // Block definition characters + blockDefinitionCharactersText = attributes.getValue("blockDefinitionCharacters"); + if (blockDefinitionCharactersText == null) { + throw new SAXException("No blockDefinitionCharacters specified."); + } + if (blockDefinitionCharactersText.length() % 2 != 0) { + throw new SAXException("Attribute blockDefinitionCharacters must and even number of characters."); + } + + // Identifiers: Case sensitive. + String value = attributes.getValue("identifiersCaseSensitive"); + if (value == null) { + throw new SAXException("No identifiersCaseSensitive specified."); + } + if (!value.equals("true") && !value.equals("false")) { + throw new SAXException("Attribute identifiersCaseSensitive must be \"true\" or \"false\"."); + } + identifiersCaseSensitive = Boolean.parseBoolean(value); + + // Identifiers: Start characters. + identifierStartCharactersText = attributes.getValue("identifierStartCharacters"); + if (identifierStartCharactersText == null) { + throw new SAXException("No identifierStartCharacters specified."); + } + if (StringUtility.isEmpty(identifierStartCharactersText)) { + throw new SAXException("Attribute identifierStartCharacters must not be empty."); + } + + // Identifiers: Start characters. + identifierPartCharactersText = attributes.getValue("identifierPartCharacters"); + if (identifierPartCharactersText == null) { + throw new SAXException("No identifierPartCharacters specified."); + } + if (StringUtility.isEmpty(identifierPartCharactersText)) { + throw new SAXException("Attribute identifierPartCharacters must not be empty."); + } + + // Identifiers: Separator character. + identifierSeparatorCharacterText = attributes.getValue("identifierSeparatorCharacter"); + if (identifierSeparatorCharacterText == null) { + throw new SAXException("No identifierSeparatorCharacter specified."); + } + if (identifierSeparatorCharacterText.length() > 1) { + throw new SAXException( + "Attribute identifierSeparatorCharacter must not contain more than 1 characters."); + } + + // Identifiers: Label definition suffix character. + labelDefinitionSuffixCharacterText = attributes.getValue("labelDefinitionSuffixCharacter"); + if (labelDefinitionSuffixCharacterText == null) { + throw new SAXException("No labelDefinitionSuffixCharacter specified."); + } + if (labelDefinitionSuffixCharacterText.length() > 1) { + throw new SAXException( + "Attribute labelDefinitionSuffixCharacter must not contain more than 1 characters."); + } + + // Identifiers: Macro usage prefix character. + macroUsagePrefixCharacterText = attributes.getValue("macroUsagePrefixCharacter"); + if (macroUsagePrefixCharacterText == null) { + throw new SAXException("No macroUsagePrefixCharacter specified."); + } + if (macroUsagePrefixCharacterText.length() > 1) { + throw new SAXException( + "Attribute macroUsagePrefixCharacter must not contain more than 1 characters."); + } + + // Instructions case sensitive. + value = attributes.getValue("instructionsCaseSensitive"); + if (value == null) { + throw new SAXException("No instructionsCaseSensitive specified."); + } + if (!value.equals("true") && !value.equals("false")) { + throw new SAXException("Attribute instructionsCaseSensitive must be \"true\" or \"false\"."); + } + instructionsCaseSensitive = Boolean.parseBoolean(value); + + // Source include default extension. + sourceIncludeDefaultExtension = attributes.getValue("sourceIncludeDefaultExtension"); + if (sourceIncludeDefaultExtension == null) { + throw new SAXException("No sourceIncludeDefaultExtension specified."); + } + + } else if (qName.equals("opcodes")) { + // Nothing to do. + } else { + + // Begin parsing of instructions. + String cpusString; + Set cpus; + String name; + String title; + String proposal; + String typeString; + int type; + + name = attributes.getValue("name"); + if (name == null) { + throw new SAXException("No name specified for '" + qName + "'."); + } + + cpusString = attributes.getValue("cpus"); + if (cpusString == null) { + throw new SAXException("No CPUs specified for '" + name + "'."); + } + cpus = new TreeSet(); + StringTokenizer tokenizer = new StringTokenizer(cpusString, ","); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + boolean found = false; + + // Wild card? + if (token.endsWith("*")) { + token = token.substring(0, token.length() - 1); + for (CPU cpu : CPU.values()) { + if (cpu.name().startsWith(token) || cpu == CPU.MOS65C02 && token.equals("MOS6502")) { + cpus.add(cpu); + found = true; + } + } + } else { + // Exact match + for (CPU cpu : CPU.values()) { + if (cpu.name().equals(token)) { + cpus.add(cpu); + found = true; + } + } + } + if (!found) { + throw new SAXException("No cpu matches the cpus '" + cpusString + "' for '" + name + "'."); + } + } + + title = attributes.getValue("title"); + if (title == null) { + throw new SAXException("No title specified for '" + name + "'."); + } + + typeString = attributes.getValue("type"); + if (typeString == null) { + typeString = ""; + } + + proposal = attributes.getValue("proposal"); + if (qName.equals("constant")) { + // Constants always have a simple proposal + proposal = name + "_"; + } + if (proposal == null) { + throw new SAXException("No proposal specified for '" + name + "'."); + } + + // TODO Have constant as own instruction class + if (qName.equals("constant")) { + qName = "directive"; + typeString = "DIRECTIVE"; + } + + if (qName.equals("directive")) { + + // Default and default nesting for folding blocks and + // sections. + if (typeString.equals("DIRECTIVE")) { + type = InstructionType.DIRECTIVE; + } else if (typeString.equals("BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_FOLDING_BLOCK_DIRECTIVE")) { + type = InstructionType.BEGIN_FOLDING_BLOCK_DIRECTIVE; + } else if (typeString.equals("END_FOLDING_BLOCK_DIRECTIVE")) { + type = InstructionType.END_FOLDING_BLOCK_DIRECTIVE; + } else if (typeString.equals("END_SECTION_DIRECTIVE")) { + type = InstructionType.END_SECTION_DIRECTIVE; + } + + // Begin of sections. + else if (typeString.equals("BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_LOCAL_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_LOCAL_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_PAGES_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_PAGES_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE; + } else if (typeString.equals("BEGIN_REPEAT_SECTION_DIRECTIVE")) { + type = InstructionType.BEGIN_REPEAT_SECTION_DIRECTIVE; + } + + // File references. + else if (typeString.equals("SOURCE_INCLUDE_DIRECTIVE")) { + type = InstructionType.SOURCE_INCLUDE_DIRECTIVE; + } else if (typeString.equals("BINARY_INCLUDE_DIRECTIVE")) { + type = InstructionType.BINARY_INCLUDE_DIRECTIVE; + } else if (typeString.equals("BINARY_OUTPUT_DIRECTIVE")) { + type = InstructionType.BINARY_OUTPUT_DIRECTIVE; + } else if (typeString.equals("")) { + throw new SAXException("No directive type specified for directive '" + name + "'."); + } else { + throw new SAXException("Unknown directive type '" + typeString + "' for directive '" + name + + "'."); + } + instructionsList.add(new Directive(cpus, type, instructionsCaseSensitive, name, title, proposal)); + } else if (qName.equals("opcode") || qName.equals("illegalopcode") || qName.equals("pseudoopcode")) { + + if (qName.equals("opcode")) { + type = InstructionType.LEGAL_OPCODE; + } else if (qName.equals("illegalopcode")) { + type = InstructionType.ILLEGAL_OPCODE; + } else if (qName.equals("pseudoopcode")) { + type = InstructionType.PSEUDO_OPCODE; + } else { + throw new SAXException("Unknown qName '" + qName + "'."); + } + + String flags = attributes.getValue("flags"); + if (flags == null) { + flags = "@todo flags"; + } + String modes = attributes.getValue("modes"); + if (modes == null) { + modes = "todo=$00"; + } + + // Currently only KickAss uses case sensitive instructions + // and they are lower case. There this logic is applied here + // instead of having an additional XML attribute to control + // the lower/upper case setting for opcodes. + if (instructionsCaseSensitive) { + name = name.toLowerCase(); + proposal = proposal.toLowerCase(); + } + instructionsList.add(new Opcode(cpus, type, instructionsCaseSensitive, name, title, proposal, cpus + .contains(CPU.MOS65816), flags, modes)); + } + } + } + } + + private String compilerId; + + private char[] completionProposalAutoActivationCharacters; + + private List singleLineCommentDelimiters; + + private List multipleLinesCommentDelimiters; + + private List stringDelimiters; + + private String blockDefinitionCharacters; + private char blockDefinitionStartCharacter; + private char blockDefinitionEndCharacter; + + private boolean identifiersCaseSensitive; + + private String identifierStartCharacters; + private boolean identifierStartCharactersArray[]; + + private String identifierPartCharacters; + private boolean identifierPartCharactersArray[]; + + private char identifierSeparatorCharacter; + + private char labelDefinitionSuffixCharacter; + + private char macroUsagePrefixCharacter; + + private boolean instructionsCaseSensitive; + + private String sourceIncludeDefaultExtension; + + private List instructionList; + private Map instructionSets; + + /** + * Creates new instance. Called by {@link CompilerRegistry}. + * + * @param compilerId + * The compiler id, not empty and null. + */ + public CompilerSyntax(String compilerId) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (StringUtility.isEmpty(compilerId)) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be empty."); + } + this.compilerId = compilerId; + } + + /** + * Load XML data. + * + * @param compilerClasses + * The list of compiler classes for which the XML files shall be + * loaded, not empty and not null. The content of + * subsequent files will extend the content of previous files. + */ + public void loadXMLData(List> compilerClasses) { + + if (compilerClasses == null) { + throw new IllegalArgumentException("Parameter 'compilerClasses' must not be null."); + } + if (compilerClasses.isEmpty()) { + throw new IllegalArgumentException("Parameter 'compilerClasses' must not be empty."); + } + XMLHandler xmlHandler; + xmlHandler = new XMLHandler(); + for (int i = 0; i < compilerClasses.size(); i++) { + Class compilerClass = compilerClasses.get(i); + if (compilerClass == null) { + throw new IllegalArgumentException("Parameter 'compilerClasses[" + i + "]' must not be null."); + } + + SAXParser parser; + try { + parser = SAXParserFactory.newInstance().newSAXParser(); + } catch (ParserConfigurationException ex) { + throw new RuntimeException( + "Cannot create parser for compiler class '" + compilerClass.getName() + "'.", ex); + } catch (SAXException ex) { + throw new RuntimeException( + "Cannot create parser for compiler class '" + compilerClass.getName() + "'.", ex); + } + + String syntaxFileName = "/" + compilerClass.getName().replace('.', '/') + ".xml"; + try { + + InputStream inputStream = compilerClass.getResourceAsStream(syntaxFileName); + parser.parse(inputStream, xmlHandler); + } catch (SAXParseException ex) { + throw new RuntimeException("Cannot create parser for file '" + syntaxFileName + "'. Error in line " + + ex.getLineNumber() + ", column " + ex.getColumnNumber() + ".", ex); + } catch (SAXException ex) { + throw new RuntimeException("Cannot create parser for file '" + syntaxFileName + "'.", ex); + } catch (IOException ex) { + throw new RuntimeException("Cannot create parser for file '" + syntaxFileName + "'.", ex); + } + } + + // Completion proposal auto activation characters + completionProposalAutoActivationCharacters = xmlHandler.completionProposalAutoActivationCharactersText + .toCharArray(); + + // Single line comments. + String delimiterText = xmlHandler.singleLineCommentDelimitersText; + if (delimiterText == null) { + throw new IllegalStateException("Attribute 'singleLineCommentDelimiters' is no set."); + } + StringTokenizer tokenizer = new StringTokenizer(delimiterText, " "); + singleLineCommentDelimiters = new ArrayList(tokenizer.countTokens()); + while (tokenizer.hasMoreTokens()) { + String token = tokenizer.nextToken(); + singleLineCommentDelimiters.add(token); + } + singleLineCommentDelimiters = Collections.unmodifiableList(singleLineCommentDelimiters); + + // Multiple lines comments. + delimiterText = xmlHandler.multipleLinesCommentDelimitersText; + if (delimiterText == null) { + throw new IllegalStateException("Attribute 'singleLineCommentDelimiters' is no set."); + } + tokenizer = new StringTokenizer(delimiterText, " "); + int tokenCount = tokenizer.countTokens(); + if ((tokenCount & 1) == 1) { + throw new IllegalStateException("Attribute 'multipleLinesCommentDelimiters' has an odd number of tokens: '" + + delimiterText + "'."); + } + multipleLinesCommentDelimiters = new ArrayList(tokenCount); + while (tokenizer.hasMoreTokens()) { + multipleLinesCommentDelimiters.add(tokenizer.nextToken()); + } + multipleLinesCommentDelimiters = Collections.unmodifiableList(multipleLinesCommentDelimiters); + + // Strings. + delimiterText = xmlHandler.stringDelimiterCharactersText; + if (delimiterText == null) { + throw new IllegalStateException("Attribute 'stringDelimiters' is not set."); + } + stringDelimiters = new ArrayList(delimiterText.length()); + for (int i = 0; i < delimiterText.length(); i++) { + stringDelimiters.add(delimiterText.substring(i, i + 1)); + } + stringDelimiters = Collections.unmodifiableList(stringDelimiters); + + // Block definitions. + if (xmlHandler.blockDefinitionCharactersText == null) { + throw new IllegalArgumentException("Attribute 'blockDefinitionCharacters' is not set."); + } + blockDefinitionCharacters = xmlHandler.blockDefinitionCharactersText; + if (blockDefinitionCharacters.length() > 0) { + if (blockDefinitionCharacters.length() == 2) { + blockDefinitionStartCharacter = blockDefinitionCharacters.charAt(0); + blockDefinitionEndCharacter = blockDefinitionCharacters.charAt(1); + } else { + throw new IllegalArgumentException("Attribute 'blockDefinitionCharacters' has the value '" + + blockDefinitionCharacters + "' and does not have 2 characters."); + } + } else { + blockDefinitionStartCharacter = NO_CHARACTER; + blockDefinitionEndCharacter = NO_CHARACTER; + } + + // Identifiers: Case sensitive. + identifiersCaseSensitive = xmlHandler.identifiersCaseSensitive; + + // Identifiers: Start characters. + if (xmlHandler.identifierStartCharactersText == null) { + throw new IllegalArgumentException("Attribute 'identifierStartCharacters' is not set."); + } + identifierStartCharacters = xmlHandler.identifierStartCharactersText; + identifierStartCharactersArray = createBooleanArray(identifierStartCharacters); + + // Identifiers: Start characters. + if (xmlHandler.identifierPartCharactersText == null) { + throw new IllegalArgumentException("Attribute 'identifierPartCharacters' is not set."); + } + identifierPartCharacters = xmlHandler.identifierPartCharactersText; + identifierPartCharactersArray = createBooleanArray(identifierPartCharacters); + + // Identifiers: Separator characters. + if (xmlHandler.identifierSeparatorCharacterText == null) { + throw new IllegalArgumentException("Attribute 'identifierSeparatorCharacter' is not set."); + } + if (xmlHandler.identifierSeparatorCharacterText.length() == 0) { + identifierSeparatorCharacter = NO_CHARACTER; + } else { + identifierSeparatorCharacter = xmlHandler.identifierSeparatorCharacterText.charAt(0); + } + + // Identifiers: Label definition suffix character. + if (xmlHandler.labelDefinitionSuffixCharacterText == null) { + throw new IllegalArgumentException("Attribute 'labelDefinitionSuffixCharacterText' is not set."); + } + if (xmlHandler.labelDefinitionSuffixCharacterText.length() == 0) { + labelDefinitionSuffixCharacter = NO_CHARACTER; + } else { + labelDefinitionSuffixCharacter = xmlHandler.labelDefinitionSuffixCharacterText.charAt(0); + } + + // Identifiers: Macro usage prefix character + if (xmlHandler.macroUsagePrefixCharacterText == null) { + throw new IllegalArgumentException("Attribute 'macroUsagePrefixCharacterText' is not set."); + } + if (xmlHandler.macroUsagePrefixCharacterText.length() == 0) { + macroUsagePrefixCharacter = NO_CHARACTER; + } else { + macroUsagePrefixCharacter = xmlHandler.macroUsagePrefixCharacterText.charAt(0); + } + + // Instructions case sensitive. + instructionsCaseSensitive = xmlHandler.instructionsCaseSensitive; + + // Source include default extension. + sourceIncludeDefaultExtension = xmlHandler.sourceIncludeDefaultExtension; + + instructionList = Collections.unmodifiableList(xmlHandler.instructionsList); + + // Create instruction set map. + instructionSets = new TreeMap(); + } + + static boolean[] createBooleanArray(String string) { + int length = string.length(); + int max = 0; + for (int i = 0; i < length; i++) { + char c = string.charAt(i); + if (c > max) { + max = c; + } + } + boolean[] result = new boolean[max + 1]; + + for (int i = 0; i < length; i++) { + char c = string.charAt(i); + result[c] = true; + } + return result; + } + + /** + * Gets the completion proposal auto activation characters. + * + * @return The array of completion proposal auto activation characters, may + * be empty, not null. + */ + public char[] getCompletionProposalAutoActivationCharacters() { + if (completionProposalAutoActivationCharacters == null) { + throw new IllegalStateException("Attribute 'completionProposalAutoActivationCharacters' is not set."); + } + return completionProposalAutoActivationCharacters; + } + + /** + * Gets the delimiter prefixes for single line comments. + * + * @return The unmodifiable list of delimiter prefixes for single line + * comments, not empty and not null. + */ + public List getSingleLineCommentDelimiters() { + if (singleLineCommentDelimiters == null) { + throw new IllegalStateException("Attribute 'singleLineCommentDelimiters' is not set."); + } + return singleLineCommentDelimiters; + } + + /** + * Gets the delimiter prefixes for multiple lines comments. + * + * @return The unmodifiable list of delimiter prefixes for single line + * comments, not empty and not null. The list contains + * an even number of entries where two entries constitute the start + * sequence and the end sequence of the multiple lines rules. + */ + public List getMultipleLinesCommentDelimiters() { + if (multipleLinesCommentDelimiters == null) { + throw new IllegalStateException("Attribute 'multipleLinesCommentDelimiters' is not set."); + } + return multipleLinesCommentDelimiters; + } + + /** + * Gets the delimiter characters for strings. + * + * @return The unmodifiable list of delimiter characters for strings, not + * empty and not null. + */ + public List getStringDelimiters() { + if (stringDelimiters == null) { + throw new IllegalStateException("Attribute 'stringDelimiters' is not set."); + } + return stringDelimiters; + } + + /** + * Gets the (possibly empty) string of character pairs which define the + * begin and end of a named or unnamed (folding) block. + * + * @return The string of character pairs which define the begin and end of a + * named or unnamed (folding) block, may be empty, not + * null. + * @since 1.6.1 + */ + public String getBlockDefinitionCharacters() { + return blockDefinitionCharacters; + } + + /** + * Gets the block definition start character if defined. + * + * @return The block definition start character or {@link #NO_CHARACTER}. + */ + public char getBlockDefinitionStartCharacter() { + return blockDefinitionStartCharacter; + } + + /** + * Gets the block definition start character if defined. + * + * @return The block definition start character or {@link #NO_CHARACTER}. + */ + public char getBlockDefinitionEndCharacter() { + return blockDefinitionEndCharacter; + } + + /** + * Determines if identifiers are case sensitive. + * + * @return true if identifiers are case sensitive, + * false otherwise. + * + * @since 1.6.1 + */ + public boolean areIdentifiersCaseSensitive() { + return identifiersCaseSensitive; + } + + /** + * Determines if a character can be the start or part of an identifier. + * + * @param c + * The character to be checked. + * @return true if the character can be the start of an + * identifier, false otherwise. + */ + public boolean isIdentifierCharacter(char c) { + return isIdentifierStartCharacter(c) || isIdentifierPartCharacter(c) + || (c == identifierSeparatorCharacter && c != NO_CHARACTER); + } + + /** + * Gets the valid identifier start characters. + * + * @return The non empty string of identifier start characters, not + * null. + * + * @since 1.6.3 + */ + public String getIdentifierStartCharacters() { + return identifierStartCharacters; + } + + /** + * Determines if a character can be the start of an identifier. + * + * @param c + * The character to be checked. + * @return true if the character can be the start of an + * identifier, false otherwise. + * + * @since 1.6.1 + */ + public boolean isIdentifierStartCharacter(char c) { + return c < identifierStartCharactersArray.length && identifierStartCharactersArray[c]; + } + + /** + * Gets the valid identifier part characters. + * + * @return The non empty string of identifier part characters, not + * null. + * + * @since 1.6.3 + */ + public String getIdentifierPartCharacters() { + return identifierPartCharacters; + } + + /** + * Determines if a character can be the part of an identifier. + * + * @param c + * The character to be checked. + * @return true if the character can be part of an identifier, + * false otherwise. + * @since 1.6.1 + */ + public boolean isIdentifierPartCharacter(char c) { + return c < identifierPartCharactersArray.length && identifierPartCharactersArray[c]; + } + + /** + * Gets the character which separates two parts of a compound identifier. + * + * @return The character which separates two parts of a compound identifier + * or {@link #NO_CHARACTER} if compound identifiers are not + * supported. + */ + public char getIdentifierSeparatorCharacter() { + return identifierSeparatorCharacter; + } + + /** + * Determines if a character can be part of an identifier and separates two + * parts of a compound identifier characters. + * + * @param c + * The character to be checked. + * @return true if the character can be part of an identifier + * and separates two parts of a compound identifier, + * false otherwise. + * @since 1.6.1 + */ + public boolean isIdentifierSeparatorCharacter(char c) { + return (c != NO_CHARACTER) && (c == identifierSeparatorCharacter); + } + + /** + * Gets the character which end a label definition, e.g. ':'. + * + * @return The character which ends a label definition or + * {@link #NO_CHARACTER} if label definitions end at the first white + * space. + * + * @since 1.6.1 + */ + public char getLabelDefinitionSuffixCharacter() { + return labelDefinitionSuffixCharacter; + } + + /** + * Gets the character which separates two parts of a compound identifier. + * + * @return The character which start a macro usage or {@link #NO_CHARACTER} + * if macro usages are not prefixed with a character. + * + * @since 1.6.1 + */ + public char getMacroUsagePrefixCharacter() { + return macroUsagePrefixCharacter; + } + + /** + * Determines if instructions are case sensitive. + * + * @return true if instructions are case sensitive, + * false otherwise. + * + * @since 1.6.1 + */ + public boolean areInstructionsCaseSensitive() { + return instructionsCaseSensitive; + } + + /** + * Determines if a character can be the start of a number. + * + * @param c + * The character to be checked. + * @return true if the character can be the start of a number, + * false otherwise. + * + * @since 1.6.1 + */ + public boolean isNumberStartCharacter(char c) { + return c == '$' || c == '%' || c == '~' || (c >= '0' && c <= '9'); + } + + /** + * Determines if a character can be the part of a number. + * + * @param c + * The character to be checked. + * @return true if the character can be part of a number, + * false otherwise. + * @since 1.6.1 + */ + public boolean isNumberPartCharacter(char c) { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); + } + + /** + * Gets the default extension (excluding the period) to use in case source + * include file name does not end with an extension. + * + * @return Gets the default extension (excluding the period) to use in case + * source include file name does not end with an extension, may be + * empty, not null. + */ + public String getSourceIncludeDefaultExtension() { + if (sourceIncludeDefaultExtension == null) { + throw new IllegalStateException("Variable 'sourceIncludeDefaultExtension' not yet initialized."); + } + return sourceIncludeDefaultExtension; + } + + /** + * Gets the instruction set for a CPU. + * + * @param cpu + * The CPU this which the allowed list of instructions shall be + * returned, not null. + * @return The set of instructions supported by the compiler for the + * specified CPU, not null. + * + * @since 1.6.1 + */ + public InstructionSet getInstructionSet(CPU cpu) { + if (cpu == null) { + throw new IllegalArgumentException("Parameter 'cpu' must not be null."); + } + InstructionSet result; + synchronized (instructionSets) { + result = instructionSets.get(cpu); + if (result == null) { + result = new InstructionSet(this, instructionList, cpu); + instructionSets.put(cpu, result); + } + } + + return result; + } + + @Override + public String toString() { + return compilerId; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntaxUtility.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntaxUtility.java new file mode 100644 index 00000000..3da119b0 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/CompilerSyntaxUtility.java @@ -0,0 +1,110 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import com.wudsn.ide.asm.Texts; + +/** + * Utility class for compiler syntax and instructions. + * + * @author Peter Dell + * @since 1.6.1 + */ +public final class CompilerSyntaxUtility { + + /** + * Creation private. + */ + private CompilerSyntaxUtility() { + + } + + /** + * Gets the image path for an instruction type image. + * + * @param instruction + * The instruction, not null. + * @return The image path for the instruction type image, not empty and not + * null. + */ + public static String getTypeImagePath(Instruction instruction) { + if (instruction == null) { + throw new IllegalArgumentException( + "Parameter 'instruction' must not be null."); + } + String path; + + if (instruction instanceof Directive) { + path = "instruction-type-directive-16x16.gif"; + } else { + Opcode opcode = (Opcode) instruction; + switch (opcode.getType()) { + case InstructionType.LEGAL_OPCODE: + path = "instruction-type-legal-opcode-16x16.gif"; + break; + case InstructionType.ILLEGAL_OPCODE: + path = "instruction-type-illegal-opcode-16x16.gif"; + break; + case InstructionType.PSEUDO_OPCODE: + path = "instruction-type-pseudo-opcode-16x16.gif"; + break; + default: + throw new IllegalStateException("Unknown opcode type " + + opcode.getType() + "."); + } + + } + return path; + } + + /** + * Gets the text for an instruction type. + * + * @param instruction + * The instruction, not null. + * @return The text for the instruction type image, may be empty, not + * null. + */ + public static String getTypeText(Instruction instruction) { + String text; + + if (instruction instanceof Directive) { + text = Texts.COMPILER_SYNTAX_INSTRUCTION_DIRECTIVE; + } else { + Opcode opcode = (Opcode) instruction; + switch (opcode.getType()) { + case InstructionType.LEGAL_OPCODE: + text = Texts.COMPILER_SYNTAX_LEGAL_OPCODE; + break; + case InstructionType.ILLEGAL_OPCODE: + text = Texts.COMPILER_SYNTAX_ILLEGAL_OPCODE; + break; + case InstructionType.PSEUDO_OPCODE: + text = Texts.COMPILER_SYNTAX_PSEUDO_OPCODE; + break; + default: + throw new IllegalStateException("Unknown opcode type " + + opcode.getType() + "."); + } + + } + return text; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Directive.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Directive.java new file mode 100644 index 00000000..eb982852 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Directive.java @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import java.util.Set; + +import com.wudsn.ide.asm.CPU; + +public final class Directive extends Instruction { + + Directive(Set cpus, int type, boolean caseSensitive, + String name, String title, String proposal) { + super(cpus, type, caseSensitive, name, title, proposal); + + switch (type) { + case InstructionType.DIRECTIVE: + case InstructionType.BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE: + case InstructionType.BEGIN_FOLDING_BLOCK_DIRECTIVE: + case InstructionType.END_FOLDING_BLOCK_DIRECTIVE: + case InstructionType.END_SECTION_DIRECTIVE: + + case InstructionType.BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE: + case InstructionType.BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE: + case InstructionType.BEGIN_LOCAL_SECTION_DIRECTIVE: + case InstructionType.BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE: + case InstructionType.BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE: + case InstructionType.BEGIN_PAGES_SECTION_DIRECTIVE: + case InstructionType.BEGIN_REPEAT_SECTION_DIRECTIVE: + + case InstructionType.SOURCE_INCLUDE_DIRECTIVE: + case InstructionType.BINARY_INCLUDE_DIRECTIVE: + case InstructionType.BINARY_OUTPUT_DIRECTIVE: + break; + + default: + throw new IllegalArgumentException("Unknown type " + type + + " for directive '" + name + "'."); + } + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Instruction.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Instruction.java new file mode 100644 index 00000000..456755dd --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Instruction.java @@ -0,0 +1,205 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.base.common.NumberFactory; + +public abstract class Instruction implements Comparable { + + public static final char CURSOR = '_'; + public static final char NEWLINE = '\n'; + + private Set cpus; + private int type; + private String name; + private String upperCaseName; + private String lowerCaseName; + private String title; + private String formattedTitle; + private String styledTitle; + private int[] styledTitleOffsets; + private String proposal; + + protected Instruction(Set cpus, int type, boolean caseSensitive, + String name, String title, String proposal) { + if (cpus == null) { + throw new IllegalArgumentException( + "Parameter 'cpus' must not be null."); + } + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'name' must not be null."); + } + if (title == null) { + throw new IllegalArgumentException( + "Parameter 'title' must not be null for instruction '" + + name + "'."); + } + if (proposal == null) { + throw new IllegalArgumentException( + "Parameter 'proposal' must not be null for instruction '" + + name + "'."); + } + this.cpus = cpus; + this.type = type; + this.name = name; + if (caseSensitive) { + this.upperCaseName = name; + this.lowerCaseName = name; + } else { + this.upperCaseName = name.toUpperCase(); + this.lowerCaseName = name.toLowerCase(); + } + this.title = title; + + int length = title.length(); + StringBuilder mnemonicBuilder = new StringBuilder(3); + StringBuilder formattedTitleBuilder = new StringBuilder(length); + StringBuilder styledTitleBuilder = new StringBuilder(length); + List styledTitleOffsetsList = new ArrayList(10); + for (int i = 0; i < length; i++) { + char c = title.charAt(i); + char fc; + if (c == '_') { + i++; + if (i >= title.length()) { + throw new RuntimeException("Instruction '" + name + + "' has invalid title '" + title + "'."); + } + c = title.charAt(i); + fc = Character.toUpperCase(c); + mnemonicBuilder.append(fc); + styledTitleOffsetsList.add(NumberFactory + .getInteger(styledTitleBuilder.length())); + } else { + fc = c; + } + formattedTitleBuilder.append(fc); + styledTitleBuilder.append(c); + } + this.formattedTitle = formattedTitleBuilder.toString(); + this.styledTitle = styledTitleBuilder.toString(); + int size = styledTitleOffsetsList.size(); + this.styledTitleOffsets = new int[size]; + for (int i = 0; i < size; i++) { + this.styledTitleOffsets[i] = styledTitleOffsetsList.get(i) + .intValue(); + } + + proposal = proposal.replace("\\n", "" + NEWLINE); + if (!proposal.startsWith(name)) { + throw new RuntimeException("Proposal '" + proposal + + "' of instruction '" + name + "' does not start with '" + + name + "'."); + } + if (proposal.indexOf(CURSOR) == -1) { + + throw new RuntimeException("Proposal '" + proposal + + "' of instruction '" + name + + "' does not contain cursor positioning via '_'."); + } + + this.proposal = proposal; + + // Remove all special characters like + StringBuilder mnemonicNameBuilder = new StringBuilder(upperCaseName); + for (int i = 0; i < mnemonicNameBuilder.length(); i++) { + if (!Character.isLetter(mnemonicNameBuilder.charAt(i)) + && !Character.isDigit(mnemonicNameBuilder.charAt(i))) { + mnemonicNameBuilder.deleteCharAt(i); + } + } + + String mnemonic = mnemonicBuilder.toString(); + String mnemonicName = mnemonicNameBuilder.toString(); + if (!mnemonicName.equalsIgnoreCase(mnemonic)) { + throw new RuntimeException("Menmonic '" + mnemonic + + "' derived from title '" + title + + "' with of instruction '" + name + + "' does match mnemonic '" + mnemonicName + + " derived from the name'."); + + } + } + + public Set getCPUs() { + return cpus; + } + + public int getType() { + return type; + } + + public final String getName() { + return name; + } + + public final String getUpperCaseName() { + return upperCaseName; + } + + public final String getLowerCaseName() { + return lowerCaseName; + } + + public final String getTitle() { + return title; + } + + public final String getFormattedTitle() { + return formattedTitle; + } + + public final String getStyledTitle() { + return styledTitle; + } + + public final int[] getStyledTitleOffsets() { + return styledTitleOffsets; + } + + /** + * Gets the upper case proposal text for the instruction. By default the + * proposal is the same as the {@link #getName()}. In case the instruction + * has a mandatory parameter, a space is added. In case the proposal span + * several line, they are separated via {@link #NEWLINE}. The position of + * the cursor is defined by the character {@link #CURSOR}. + * + * @return The proposal, not empty, not null. + */ + public final String getProposal() { + return proposal; + } + + @Override + public final int compareTo(Instruction o) { + return name.compareToIgnoreCase(o.name); + } + + @Override + public final String toString() { + return "type=" + type + ", name=" + name; + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/InstructionSet.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/InstructionSet.java new file mode 100644 index 00000000..4c1eec09 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/InstructionSet.java @@ -0,0 +1,273 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.compiler.syntax.Opcode.OpcodeAddressingMode; + +/** + * Container for a set of directives, legal, illegal and pseudo opcodes and + * their properties. + * + * TODO Maintain address modes for all illegal opcodes for online help + * + * @author Peter Dell + * @since 1.6.1 + */ +public final class InstructionSet { + + private CompilerSyntax compilerSyntax; + + private boolean instructionStartCharactersArray[]; + private boolean instructionPartCharactersArray[]; + + private List instructionsList; + + private Map instructionsMap; + + /** + * Array list of opcode addressing modes. + * + * @since 1.7.0 + */ + private List> opcodeAddessingModesList; + + private List fileReferenceInstructionsList; + + /** + * Creates new instance. Called by {@link CompilerRegistry}. + * + * @param compilerSyntax + * The compiler syntax, not null. + * + * @param instructionsList + * The non-filtered list of compiler instructions. + * @param cpu + * The cpu to filter by, not null. + */ + InstructionSet(CompilerSyntax compilerSyntax, List instructionsList, CPU cpu) { + + if (compilerSyntax == null) { + throw new IllegalArgumentException("Parameter 'compilerSyntax' must not be null."); + } + if (instructionsList == null) { + throw new IllegalArgumentException("Parameter 'instructionsList' must not be null."); + } + if (cpu == null) { + throw new IllegalArgumentException("Parameter 'cpu' must not be null."); + } + // Compute the list of all include instructions. + this.compilerSyntax = compilerSyntax; + this.instructionsList = new ArrayList(instructionsList.size()); + instructionsMap = new TreeMap(); + + opcodeAddessingModesList = new ArrayList>(Opcode.MAX_OPCODES); + for (int i = 0; i < Opcode.MAX_OPCODES; i++) { + opcodeAddessingModesList.add(new ArrayList()); + } + fileReferenceInstructionsList = new ArrayList(10); + + // Collect all start and part characters. + boolean caseSenstive = compilerSyntax.areIdentifiersCaseSensitive(); + StringBuilder instructionStartCharacters = new StringBuilder(512); + StringBuilder instructionPartCharacters = new StringBuilder(2048); + + for (Instruction instruction : instructionsList) { + if (!instruction.getCPUs().contains(cpu)) { + continue; + } + this.instructionsList.add(instruction); + + // If not case sensitive, the upper case and lower case + // representation is allowed + if (caseSenstive) { + instructionStartCharacters.append(instruction.getName().substring(0, 1)); + instructionPartCharacters.append(instruction.getName().substring(1)); + instructionsMap.put(instruction.getName(), instruction); + + } else { + instructionStartCharacters.append(instruction.getUpperCaseName().substring(0, 1)); + instructionPartCharacters.append(instruction.getUpperCaseName().substring(1)); + instructionStartCharacters.append(instruction.getLowerCaseName().substring(0, 1)); + instructionPartCharacters.append(instruction.getLowerCaseName().substring(1)); + instructionsMap.put(instruction.getUpperCaseName(), instruction); + } + + switch (instruction.getType()) { + case InstructionType.LEGAL_OPCODE: + case InstructionType.ILLEGAL_OPCODE: + case InstructionType.PSEUDO_OPCODE: + Opcode opcode = (Opcode) instruction; + for (OpcodeAddressingMode opcodeAddressingMode : opcode.getAddressingModes()) { + // Even if an instruction is supported by all CPUs, not all + // addressing modes may be supported by the CPU, + if (opcodeAddressingMode.getCPUs().contains(cpu)) { + List list = opcodeAddessingModesList.get(opcodeAddressingMode + .getOpcodeValue()); + if (list == null) { + list = new ArrayList(); + } + list.add(opcodeAddressingMode); + } + } + break; + + case InstructionType.SOURCE_INCLUDE_DIRECTIVE: + case InstructionType.BINARY_INCLUDE_DIRECTIVE: + case InstructionType.BINARY_OUTPUT_DIRECTIVE: + fileReferenceInstructionsList.add(instruction); + break; + + } + + } + + instructionStartCharactersArray = CompilerSyntax.createBooleanArray(instructionStartCharacters.toString()); + instructionPartCharactersArray = CompilerSyntax.createBooleanArray(instructionPartCharacters.toString()); + + this.instructionsList = Collections.unmodifiableList(this.instructionsList); + instructionsMap = Collections.unmodifiableMap(instructionsMap); + fileReferenceInstructionsList = Collections.unmodifiableList(fileReferenceInstructionsList); + } + + /** + * Gets the compiler syntax. + * + * @return The compiler syntax, not null. + */ + public CompilerSyntax getCompilerSyntax() { + return compilerSyntax; + } + + /** + * Determines if instructions are case sensitive. + * + * @return true if instructions are case sensitive, + * false otherwise. + * + * @since 1.6.1 + */ + public boolean areInstructionsCaseSensitive() { + return compilerSyntax.areInstructionsCaseSensitive(); + } + + /** + * Determines if a character can be the start of an instruction. + * + * @param c + * The character to be checked. + * @return true if the character can be the start of an + * instruction, false otherwise. + * + * @since 1.6.1 + */ + public boolean isInstructionStartCharacter(char c) { + return c < instructionStartCharactersArray.length && instructionStartCharactersArray[c]; + } + + /** + * Determines if a character can be the part of an instruction. + * + * @param c + * The character to be checked. + * @return true if the character can be part of an instruction, + * false otherwise. + * @since 1.6.1 + */ + public boolean isInstructionPartCharacter(char c) { + return c < instructionPartCharactersArray.length && instructionPartCharactersArray[c]; + } + + /** + * Gets the list of all instructions. + * + * @return The unmodifiable list of instructions, not null. + */ + public List getInstructions() { + + if (instructionsList == null) { + throw new IllegalStateException("Variable 'instructionsList' not yet initialized."); + } + return instructionsList; + } + + /** + * Gets list of opcode address modes for the given opcode value. Only + * instances that are support by the CPU of the instruction set are + * returned. + * + * @param opcodeValue + * The opcode value. + * @return The list of opcode address modes, may be empty, not + * null. + * + * @since 1.7.0 + */ + public List getOpcodeAddressingModes(int opcodeValue) { + if (opcodeAddessingModesList == null) { + throw new IllegalStateException("Variable 'opcodeAddessingModesList' not yet initialized."); + } + List result = null; + if (opcodeValue > 0 && opcodeValue < opcodeAddessingModesList.size()) { + result = opcodeAddessingModesList.get(opcodeValue); + } + if (result == null) { + result = Collections.emptyList(); + } + return result; + } + + /** + * Gets an instruction by its upper case name. + * + * @param instructionName + * The upper case name + * + * @return The instruction or null. + */ + public Instruction getInstruction(String instructionName) { + return instructionsMap.get(instructionName); + } + + /** + * Gets the list of all include instructions. + * + * @return The unmodifiable list of include instructions, not + * null. + */ + public List getFileReferenceInstructions() { + if (fileReferenceInstructionsList == null) { + throw new IllegalStateException("Variable 'fileReferenceInstructionsList' not yet initialized."); + } + return fileReferenceInstructionsList; + } + + @Override + public String toString() { + return compilerSyntax.toString() + ": " + instructionsMap.keySet().toString(); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/InstructionType.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/InstructionType.java new file mode 100644 index 00000000..b11b608d --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/InstructionType.java @@ -0,0 +1,63 @@ +/** +* Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObjectType; + +/** + * Instruction types as defined in the XML syntax definition. See also + * {@link CompilerSourceParserTreeObjectType}. + * + * @author Peter Dell + * + */ +public final class InstructionType { + /** + * Creation is private. + */ + private InstructionType() { + + } + + // Types of directives. + public static final int DIRECTIVE = 100; + public static final int BEGIN_IMPLEMENTATION_SECTION_DIRECTIVE = 101; + public static final int BEGIN_FOLDING_BLOCK_DIRECTIVE = 102; + public static final int END_FOLDING_BLOCK_DIRECTIVE = 103; + public static final int END_SECTION_DIRECTIVE = 104; + + public static final int BEGIN_ENUM_DEFINITION_SECTION_DIRECTIVE = 105; + public static final int BEGIN_STRUCTURE_DEFINITION_SECTION_DIRECTIVE = 106; + public static final int BEGIN_LOCAL_SECTION_DIRECTIVE = 107; + public static final int BEGIN_MACRO_DEFINITION_SECTION_DIRECTIVE = 108; + public static final int BEGIN_PROCEDURE_DEFINITION_SECTION_DIRECTIVE = 109; + public static final int BEGIN_PAGES_SECTION_DIRECTIVE = 110; + public static final int BEGIN_REPEAT_SECTION_DIRECTIVE = 111; + + public static final int SOURCE_INCLUDE_DIRECTIVE = 120; + public static final int BINARY_INCLUDE_DIRECTIVE = 121; + public static final int BINARY_OUTPUT_DIRECTIVE = 122; + + + // Types of opcodes. + public static final int LEGAL_OPCODE = 200; + public static final int ILLEGAL_OPCODE = 201; + public static final int PSEUDO_OPCODE = 202; +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Opcode.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Opcode.java new file mode 100644 index 00000000..55fcd537 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/syntax/Opcode.java @@ -0,0 +1,199 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.syntax; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import com.wudsn.ide.asm.CPU; + +public final class Opcode extends Instruction { + + public static final int MAX_OPCODES = 256; + + public final static class OpcodeAddressingMode { + private Opcode opcode; + private Set cpus; + private String addressingMode; + private int opcodeValue; + + OpcodeAddressingMode(Opcode opcode, Set cpus, String addressingMode, int opcodeValue) { + if (opcode == null) { + throw new IllegalArgumentException("Parameter 'opcode' must not be null."); + } + if (cpus == null) { + throw new IllegalArgumentException("Parameter 'cpus' must not be null for opcode '" + opcode.getName() + + "'."); + } + if (addressingMode == null) { + throw new IllegalArgumentException("Parameter 'addressingMode' must not be nullfor opcode '" + + opcode.getName() + "'."); + } + if (opcodeValue < 0 || opcodeValue > 255) { + throw new IllegalArgumentException("Parameter 'opcodeValue' has value " + opcodeValue + " for opcode '" + + opcode.getName() + "' but must be between $00 and $ff."); + + } + this.opcode = opcode; + this.cpus = cpus; + this.addressingMode = addressingMode; + this.opcodeValue = opcodeValue; + } + + public Opcode getOpcode() { + return opcode; + } + + public Set getCPUs() { + return cpus; + } + + public String getAddressingMode() { + return addressingMode; + } + + public String getFormattedText() { + + StringBuffer result = new StringBuffer(opcode.getName()); + if (addressingMode.equals("imp")) { + + } else if (addressingMode.equals("imm")) { + result.append(" #$nn"); + } else if (addressingMode.equals("zp")) { + result.append(" zp"); + } else if (addressingMode.equals("zpx")) { + result.append(" zp,x"); + } else if (addressingMode.equals("zpy")) { + result.append(" zp,y"); + } else if (addressingMode.equals("izx")) { + result.append(" (zp,x)"); + } else if (addressingMode.equals("izy")) { + result.append(" (zp),y"); + } else if (addressingMode.equals("abs")) { + result.append(" abs"); + } else if (addressingMode.equals("abx")) { + result.append(" abs,x"); + } else if (addressingMode.equals("aby")) { + result.append(" abs,y"); + } else if (addressingMode.equals("ind")) { + result.append(" (abs)"); + } else if (addressingMode.equals("rel")) { + result.append(" rel"); + } else + + // 65816 modes + if (addressingMode.equals("abl")) { + result.append(" adr (long)"); + } else if (addressingMode.equals("bm")) { + result.append(" $nn,$nn"); + } else if (addressingMode.equals("dp")) { + result.append(" (zp)"); + } else if (addressingMode.equals("ial")) { + result.append(" abs (long)"); + } else if (addressingMode.equals("iax")) { + result.append(" (abs,x)"); + } else if (addressingMode.equals("idp")) { + result.append(" (zp)"); + } else if (addressingMode.equals("rell")) { + result.append(" rel (long)"); + } else { + throw new RuntimeException("Unmapped addressing mode " + addressingMode + " for opcode " + + opcode.getName()); + } + + return result.toString(); + } + + public int getOpcodeValue() { + return opcodeValue; + } + } + + private boolean w65816; + private String flags; + private String modes; + private List addressingModes; + + Opcode(Set cpus, int type, boolean instructionsCaseSensitive, String name, String title, String proposal, + boolean w65816, String flags, String modes) { + + super(cpus, type, instructionsCaseSensitive, name, title, proposal); + switch (type) { + case InstructionType.LEGAL_OPCODE: + case InstructionType.ILLEGAL_OPCODE: + case InstructionType.PSEUDO_OPCODE: + break; + + default: + throw new IllegalArgumentException("Unknown type " + type + " for opcode '" + name + "'."); + } + if (flags == null) { + throw new IllegalArgumentException("Parameter 'flags' must not be null for opcode '" + name + "'."); + } + if (modes == null) { + throw new IllegalArgumentException("Parameter 'modes' must not be null for opcode '" + name + "'."); + + } + this.w65816 = w65816; + this.flags = flags; + this.modes = modes; + addressingModes = new ArrayList(); + Set addressingModeCPUs = cpus; + for (String mode : modes.split(",")) { + mode = mode.trim(); + String values[] = mode.split("="); + String addressingMode = values[0]; + String value = values[1].substring(1); + int index = value.indexOf("["); + if (index >= 0) { + String[] cpuNameList = value.substring(index + 1, value.length() - 1).split(";"); + value = value.substring(0, index); + addressingModeCPUs = new TreeSet(); + for (String cpuName : cpuNameList) { + addressingModeCPUs.add(CPU.valueOf(cpuName)); + } + } + int opcode = Integer.parseInt(value, 16); + OpcodeAddressingMode addressingModeInstance = new OpcodeAddressingMode(this, addressingModeCPUs, + addressingMode, opcode); + addressingModes.add(addressingModeInstance); + } + + } + + public boolean isW65816() { + return w65816; + } + + public String getFlags() { + return flags; + } + + public String getModes() { + return modes; + } + + public List getAddressingModes() { + return addressingModes; + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/writer/AppleFileWriter.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/writer/AppleFileWriter.java new file mode 100644 index 00000000..8db967c1 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/compiler/writer/AppleFileWriter.java @@ -0,0 +1,343 @@ +/** + * Copyright (C) 2009 - 2014 Peter Dell + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see . + */ + +package com.wudsn.ide.asm.compiler.writer; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; + +import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.DiskFullException; +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FormattedDisk; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.CompilerFileWriter; +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.MarkerUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Compiler file writer for Apple II + * + * @author Peter Dell + * @since 1.6.3 + */ +public final class AppleFileWriter extends CompilerFileWriter { + + private final static String HELLO_ENTRY_NAME = "HELLO"; + private final static String OUTPUT_IMAGE_ENTRY_NAME = "WORLD"; + + private final class AppleSoftTokens { + public final static byte PRINT = (byte) 0xba; + public final static byte CHRS = (byte) 0xe7; + public final static byte CALL = (byte) 0x8c; + + // public String[] tokenNames = { "END", "FOR ", "NEXT ", "DATA ", + // "INPUT ", "DEL", "DIM ", "READ ", "GR", "TEXT", "PR#", "IN#", + // "CALL ", "PLOT", "HLIN ", "VLIN ", "HGR2", "HGR", "HCOLOR=", + // "HPLOT ", "DRAW ", "XDRAW ", "HTAB ", "HOME", "ROT=", "SCALE=", + // "SHLOAD", "TRACE", "NOTRACE", "NORMAL", "INVERSE", "FLASH", + // "COLOR=", "POP", "VTAB ", "HIMEM:", "LOMEM:", "ONERR ", + // "RESUME", "RECALL", "STORE", "SPEED=", "LET ", "GOTO ", "RUN", + // "IF ", "RESTORE", "& ", "GOSUB ", "RETURN", "REM ", "STOP", + // "ON ", "WAIT", "LOAD", "SAVE", "DEF", "POKE ", "PRINT ", + // "CONT", "LIST", "CLEAR", "GET ", "NEW", "TAB(", "TO ", "FN", + // "SPC(", "THEN ", "AT ", "NOT ", "STEP ", "+ ", "- ", "* ", + // "/ ", "^ ", "AND ", "OR ", "> ", "= ", "< ", "SGN", "INT", + // "ABS", "USR", "FRE", "SCRN(", "PDL", "POS ", "SQR", "RND", + // "LOG", "EXP", "COS", "SIN", "TAN", "ATN", "PEEK", "LEN", + // "STR$", "VAL", "ASC", "CHR$", "LEFT$", "RIGHT$", "MID$" }; + + // public int[] tokenAddresses = { 0xD870, 0xD766, 0xDCF9, 0xD995, + // 0xDBB2, + // 0xF331, 0xDFD9, 0xDBE2, 0xF390, 0xF399, 0xF1E5, 0xF1DE, 0xF1D5, + // 0xF225, 0xF232, 0xF241, 0xF3D8, 0xF3E2, 0xF6E9, 0xF6FE, 0xF769, + // 0xF76F, 0xF7E7, 0xFC58, 0xF721, 0xF727, 0xF775, 0xF26D, 0xF26F, + // 0xF273, 0xF277, 0xF280, 0xF24F, 0xD96B, 0xF256, 0xF286, 0xF2A6, + // 0xF2CB, 0xF318, 0xF3BC, 0xF39F, 0xF262, 0xDA46, 0xD93E, 0xD912, + // 0xD9C9, 0xD849, 0x03F5, 0xD921, 0xD96B, 0xD9DC, 0xD86E, 0xD9EC, + // 0xE784, 0xD8C9, 0xD8B0, 0xE313, 0xE77B, 0xFDAD5, 0xD896, + // 0xD6A5, 0xD66A, 0xDBA0, 0xD649, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // 0, 0, 0, 0, 0, 0, 0, 0, 0xEB90, 0xEC23, 0xEBAF, 0x000A, 0xE2DE, + // 0xD412, 0xDFCD, 0xE2FF, 0xEE8D, 0xEFAE, 0xE941, 0xEF09, 0xEFEA, + // 0xEFF1, 0xF03A, 0xF09E, 0xE764, 0xE6D6, 0xE3C5, 0xE707, 0xE6E5, + // 0xE646, 0xE65A, 0xE686, 0xE691 }; + } + + @Override + public boolean createOrUpdateDiskImage(CompilerFiles files) { + String imageFilePath = files.outputFilePathWithoutExtension + ".dsk"; + String outputImageEntryName = OUTPUT_IMAGE_ENTRY_NAME; + String outputImageEntryTitle = "Loading " + files.outputFileNameWithoutExtension; + boolean autoCreate = true; + + if (StringUtility.isSpecified(imageFilePath)) { + File imageFile = new File(imageFilePath); + if (!imageFile.exists()) { + + // When auto creation is active, we copy a template file. + // This is the only way to get a disk image which is not only + // formatted but also has a boot loader which runs "HELLO". + if (autoCreate) { + String resource = "/lib/AppleDos.dsk"; + InputStream inputStream = Disk.class.getClassLoader().getResourceAsStream(resource); + if (inputStream == null) { + throw new RuntimeException("Cannot get input stream for '" + resource + "'"); + } + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + int count; + do { + try { + count = inputStream.read(buffer); + } catch (IOException ex) { + throw new RuntimeException("Cannot read input stream for '" + resource + "'", ex); + } + + if (count > 0) { + bos.write(buffer, 0, count); + + } + } while (count > -1); + + try { + bos.close(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + try { + FileUtility.writeBytes(imageFile, bos.toByteArray()); + } catch (CoreException ex) { + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, ex.getStatus().getSeverity(), "{0}", + ex.getStatus().getMessage()); + return false; + } + + } else { // no auto creation + + // ERROR: Disk image file '{0}' does not exist. Create a + // bootable disk image where the output file '{1}' can be + // stored. + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E132, imageFilePath, outputImageEntryName); + return false; + } + } + if (!imageFile.canWrite()) { + // ERROR: Disk image file '{0}' is not writeable. Make the disk + // image file writeable, so the output file '{1}' can be stored. + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E133, + imageFilePath, outputImageEntryName); + return false; + } + } + Disk disk; + try { + disk = new Disk(imageFilePath); + } catch (IOException ex) { + // ERROR: Disk image file '{0}' cannot be opened for reading. System + // error: {1} + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E134, + imageFilePath, ex.getMessage()); + return false; + } + + FormattedDisk[] formattedDisks = disk.getFormattedDisks(); + if (formattedDisks == null || formattedDisks.length == 0) { + // ERROR: Disk image file '{0}' does not contain a valid file + // system. Make sure the disk image is properly formatted, so + // the output file '{1}' can be stored. + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E135, + imageFilePath, outputImageEntryName); + return false; + } + + byte[] outputFileContent; + try { + outputFileContent = FileUtility.readBytes(files.outputFile, 65536, true); + } catch (CoreException ex) { + // ERROR: Cannot read output file. + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, ex.getStatus().getSeverity(), "{0}", ex + .getStatus().getMessage()); + return false; + } + + FormattedDisk formattedDisk; + try { + formattedDisk = formattedDisks[0]; + // Create HELLO with dummy address. + createHello(formattedDisk, outputImageEntryName, outputImageEntryTitle, 1234); + + FileEntry entry = formattedDisk.getFile(outputImageEntryName); + if (entry == null) { + entry = formattedDisk.createFile(); + // TODO This is required due to a BUG in AppleCommander + entry.setFilename(outputImageEntryName); + } + entry.setFiletype("B"); + + String fileName = files.outputFileName.toLowerCase(); + int length = outputFileContent.length; + byte[] content = outputFileContent; + if (entry.needsAddress()) { + int address; + if (fileName.endsWith(".b") && length > 4) { + // // AppleDos 3.3 binary file: + // start-lo,start-hi,length-lo,length-hi,data + address = getWord(outputFileContent, 0); + length = length - 4; + content = getData(outputFileContent, 4); + } else if (fileName.endsWith(".prg") && length > 2) { + // C64 program file + // start-lo,start-hi,data + address = getWord(outputFileContent, 0); + length = length - 2; + content = getData(outputFileContent, 2); + } else if (fileName.endsWith(".xex") && length > 6 + && ((getWord(outputFileContent, 0) & 0xffff) == 0xffff)) { + // AtariDOS 2.5 binary file: + // $ff,$ff,start-lo,start-hi,end-lo,end-hi,data + address = getWord(outputFileContent, 2); + length = length - 6; + content = getData(outputFileContent, 6); + } else { + // ERROR: Output file {0} has unknown executable file + // extension or content. File extensions ".b", ".prg" and + // ".xex" are allowed. + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E138, files.outputFilePath); + return false; + } + // Method setAddress must be called after method setFileData! + entry.setFileData(content); + entry.setAddress(address); + // Update HELLO with acual start address. + createHello(formattedDisk, outputImageEntryName, outputImageEntryTitle, address); + } else { + entry.setFileData(outputFileContent); + + } + + } catch (DiskFullException ex) { + // ERROR: Disk image file '{0}' is full. System + // error: {1} + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E136, + imageFilePath, ex.getMessage()); + return false; + + } + + try { + formattedDisk.save(); + + } catch (IOException ex) { + + // ERROR: Disk image file '{0}' cannot be opened for writing. System + // error: {1} + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E137, + imageFilePath, ex.getMessage()); + return false; + } + + return true; + + } + + private static FileEntry createHello(FormattedDisk formattedDisk, String outputImageEntryName, + String outputImageEntryTitle, int runAddress) throws DiskFullException { + if (formattedDisk == null) { + throw new IllegalArgumentException("Parameter 'formattedDisk' must not be null."); + } + if (outputImageEntryName == null) { + throw new IllegalArgumentException("Parameter 'OUTPUT_IMAGE_ENTRY_NAME' must not be null."); + } + if (StringUtility.isEmpty(outputImageEntryName)) { + throw new IllegalArgumentException("Parameter 'OUTPUT_IMAGE_ENTRY_NAME' must not be empty."); + } + if (outputImageEntryTitle == null) { + throw new IllegalArgumentException("Parameter 'outputImageEntryTitle' must not be null."); + } + FileEntry entry; + + entry = formattedDisk.getFile(HELLO_ENTRY_NAME); + if (entry == null) { + entry = formattedDisk.createFile(); + entry.setFilename(HELLO_ENTRY_NAME); + } + entry.setFiletype("A"); + byte[] program; + try { + // See "Beneath Apple DOS.pdf", 4-11/12 for the binary and AppleSoft + // file format. + // See http://www.textfiles.com/apple/ANATOMY/cmd.brun.bload.txt for + // the bug in the BRUN routine. Instead of BRUN now BLOAD is sent to + // DOS via PRINT CHR$(4) and the program is started via CALL. + // 10 PRINT "LOADING " : PRINT CHR$(4);"BRUN WORLD" : CALL + // <address> + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bos.write(new byte[] { 0x00, 0x08, 0x0A, 0x00 }); + bos.write(new byte[] { AppleSoftTokens.PRINT, '"' }); + bos.write(outputImageEntryTitle.getBytes("US-ASCII")); + bos.write(new byte[] { '"', ':' }); + bos.write(new byte[] { AppleSoftTokens.PRINT, AppleSoftTokens.CHRS, '(', '4', ')', ';', '"', 'B', 'L', 'O', + 'A', 'D', ' ' }); + bos.write(outputImageEntryName.getBytes("US-ASCII")); + bos.write(new byte[] { '"', ':', AppleSoftTokens.CALL }); + bos.write(Integer.toString(runAddress).getBytes("US-ASCII")); + bos.write(new byte[] { 0, 0, 0 }); + program = bos.toByteArray(); + program[0] = (byte) (program.length); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + + // Byte is the line length in bytes + entry.setFileData(program); + return entry; + } + + private static int getWord(byte[] bytes, int index) { + if (bytes == null) { + throw new IllegalArgumentException("Parameter 'bytes' must not be null."); + } + + return (0xff & bytes[index]) + 256 * (0xff & bytes[index + 1]); + } + + private static byte[] getData(byte[] bytes, int index) { + if (bytes == null) { + throw new IllegalArgumentException("Parameter 'bytes' must not be null."); + } + int length = bytes.length - index; + byte[] result = new byte[length]; + System.arraycopy(bytes, index, result, 0, length); + return result; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpoinDebugModelPresentation.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpoinDebugModelPresentation.java new file mode 100644 index 00000000..70a473d3 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpoinDebugModelPresentation.java @@ -0,0 +1,114 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.ui.IDebugModelPresentation; +import org.eclipse.debug.ui.IValueDetailListener; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.ide.IDE; + +import com.wudsn.ide.asm.AssemblerPlugin; + +/** + * Implementation class for extension + * "com.wudsn.ide.asm.editor.AssemblerBreakpoinDebugModelPresentation". This is + * the binding logic which enables navigation from transient and persistent + * break point markers to the corresponding editor. + * + * @author Peter Dell + */ +public class AssemblerBreakpoinDebugModelPresentation implements IDebugModelPresentation { + + @Override + public void dispose() { + } + + @Override + public boolean isLabelProperty(Object element, String property) { + return false; + } + + @Override + public void addListener(ILabelProviderListener listener) { + } + + @Override + public void removeListener(ILabelProviderListener listener) { + + } + + @Override + public IEditorInput getEditorInput(Object element) { + AssemblerBreakpoint assemblerBreakpoint = (AssemblerBreakpoint) element; + IEditorInput result = assemblerBreakpoint.getEditorInput(); + if (result == null) { + IWorkbenchWindow activeWindow = AssemblerPlugin.getInstance().getWorkbench().getActiveWorkbenchWindow(); + if (activeWindow == null) { + return null; + } + IWorkbenchPage activePage = activeWindow.getActivePage(); + if (activePage == null) { + return null; + } + IEditorPart part; + try { + part = IDE.openEditor(activePage, assemblerBreakpoint.getMarker(), false); + } catch (PartInitException ex) { + return null; + } + return part.getEditorInput(); + } + return result; + } + + @Override + public String getEditorId(IEditorInput input, Object element) { + AssemblerBreakpoint assemblerBreakpoint = (AssemblerBreakpoint) element; + return assemblerBreakpoint.getEditorId(); + } + + @Override + public void setAttribute(String attribute, Object value) { + + } + + @Override + public Image getImage(Object element) { + return null; + } + + @Override + public String getText(Object element) { + return null; + } + + @Override + public void computeDetail(IValue value, IValueDetailListener listener) { + + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpoint.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpoint.java new file mode 100644 index 00000000..72e00810 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpoint.java @@ -0,0 +1,172 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRunnable; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.LineBreakpoint; +import org.eclipse.ui.IEditorInput; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Implementation class for assembler breakpoints. See + * http://eclipse.org/articles/Article-Debugger/how-to.html for details. + * + * @author Peter Dell + * @since 1.6.1 + * + */ + +public final class AssemblerBreakpoint extends LineBreakpoint { + + /** + * Attributes stored with the marker. + */ + public static final String EDITOR_ID = "editorId"; + + /** + * Grouping ID for all breakpoint of this type. + */ + public static final String DEBUG_MODEL_ID = AssemblerPlugin.ID; + + /** + * Marker type as defined by the extension + * "org.eclipse.core.resources.markers" + */ + public static final String MARKER_TYPE = "org.eclipse.debug.core.lineBreakpointMarker"; + + private IEditorInput editorInput; + + /** + * Default constructor is required for the breakpoint manager to re-create + * persisted breakpoints. After instantiating a breakpoint, the + * <code>setMarker(...)</code> method is called to restore this breakpoint's + * attributes. + */ + public AssemblerBreakpoint() { + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public Object getAdapter(Class adapter) { + if (adapter.isAssignableFrom(IMarker.class)) { + return getMarker(); + } + if (adapter.isAssignableFrom(IResource.class)) { + // IResource result = getMarker().getResource(); + // System.out.println(adapter.getName() + ":" + result); + // return result; + } + return super.getAdapter(adapter); + } + + /** + * Constructs a line breakpoint on the given resource at the given line + * number. The line number is 1-based (i.e. the first line of a file is line + * number 1). + * + * @param editorId + * The editor id, not <code>null</code>. + * + * @param editorInput + * The editor input, not <code>null</code>. + * @param resource + * The file on which to set the breakpoint, not <code>null</code> + * . + * @param lineNumber + * The line number of the breakpoint, a positive integer. + * @param description + * The description of the break point, may be empty not + * <code>null</code>. + * @throws CoreException + * if unable to create the breakpoint + */ + public AssemblerBreakpoint(final String editorId, IEditorInput editorInput, final IResource resource, + final int lineNumber, final String description) throws CoreException { + if (editorId == null) { + throw new IllegalArgumentException("Parameter 'editorId' must not be null."); + } + if (editorInput == null) { + throw new IllegalArgumentException("Parameter 'editorInput' must not be null."); + } + if (resource == null) { + throw new IllegalArgumentException("Parameter 'resource' must not be null."); + } + if (lineNumber < 1) { + throw new IllegalArgumentException("Parameter 'lineNumber' must be positive. Specified value is " + + lineNumber + "."); + } + if (description == null) { + throw new IllegalArgumentException("Parameter 'description' must not be null."); + } + this.editorInput = editorInput; + IWorkspaceRunnable runnable = new IWorkspaceRunnable() { + @Override + public void run(IProgressMonitor monitor) throws CoreException { + IMarker marker = resource.createMarker(MARKER_TYPE); + // This must be the first operation before setting marker + // attributes. + setMarker(marker); + + marker.setAttribute(EDITOR_ID, editorId); + marker.setAttribute(IBreakpoint.ENABLED, Boolean.TRUE); + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + marker.setAttribute(IBreakpoint.ID, getModelIdentifier()); + marker.setAttribute( + IMarker.MESSAGE, + TextUtility.format(Texts.ASSEMBLER_BREAKPOINT_MARKER_MESSAGE, resource.getName(), + NumberUtility.getLongValueDecimalString(lineNumber), description)); + + } + }; + run(getMarkerRule(resource), runnable); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IBreakpoint#getModelIdentifier() + */ + @Override + public String getModelIdentifier() { + return DEBUG_MODEL_ID; + } + + final String getEditorId() { + return getMarker().getAttribute(EDITOR_ID, null); + } + + final void setEditorInput(IEditorInput editorInput) { + this.editorInput = editorInput; + } + + final IEditorInput getEditorInput() { + return editorInput; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpointAdapterFactory.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpointAdapterFactory.java new file mode 100644 index 00000000..8a600f65 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpointAdapterFactory.java @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTargetFactory; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IWorkbenchPart; + +import com.wudsn.ide.asm.Texts; + +/** + * Factory for {@AssemblerBreakpointsTarget} + * instances. Used by extension + * "org.eclipse.debug.ui.toggleBreakpointsTargetFactories" + * + * @author Peter Dell + * @since 1.6.1 + */ +public final class AssemblerBreakpointAdapterFactory implements + IToggleBreakpointsTargetFactory { + + private String TARGET_ID = AssemblerBreakpointsTarget.class.getName(); + private Set<String> defaultSet; + + public AssemblerBreakpointAdapterFactory() { + defaultSet = new HashSet<String>(); + defaultSet.add(TARGET_ID); + } + + @Override + public Set<String> getToggleTargets(IWorkbenchPart part, + ISelection selection) { + if (part instanceof AssemblerEditor) { + return defaultSet; + } + return Collections.emptySet(); + } + + @Override + public String getDefaultToggleTarget(IWorkbenchPart part, + ISelection selection) { + if (part instanceof AssemblerEditor) { + return TARGET_ID; + } + return null; + } + + @Override + public IToggleBreakpointsTarget createToggleTarget(String targetID) { + if (TARGET_ID.equals(targetID)) { + return new AssemblerBreakpointsTarget(); + } + return null; + } + + @Override + public String getToggleTargetName(String targetID) { + return Texts.ASSEMBLER_BREAKPOINT_TOGGLE_TYPE_MENU_TEXT; + } + + @Override + public String getToggleTargetDescription(String targetID) { + return TARGET_ID; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpointsTarget.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpointsTarget.java new file mode 100644 index 00000000..c27045ef --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerBreakpointsTarget.java @@ -0,0 +1,168 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.debug.core.model.ILineBreakpoint; +import org.eclipse.debug.ui.actions.IToggleBreakpointsTarget; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.texteditor.IDocumentProvider; + +import com.wudsn.ide.base.common.StringUtility; + +/** + * Target which creates {@link AssemblerBreakpoint} instances. Used by + * {@link AssemblerBreakpointAdapterFactory}. + */ +public final class AssemblerBreakpointsTarget implements IToggleBreakpointsTarget { + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleLineBreakpoints + * (org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public void toggleLineBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + AssemblerEditor assemblerEditor = getEditor(part); + if (assemblerEditor != null) { + IBreakpointManager breakPointManager = DebugPlugin.getDefault().getBreakpointManager(); + String editorId=assemblerEditor.getClass().getName(); + IEditorInput editorInput = assemblerEditor.getEditorInput(); + IResource resource = (IResource) editorInput.getAdapter(IResource.class); + ITextSelection textSelection = (ITextSelection) selection; + int lineNumber = textSelection.getStartLine(); + IBreakpoint[] breakpoints = breakPointManager.getBreakpoints(AssemblerBreakpoint.DEBUG_MODEL_ID); + for (int i = 0; i < breakpoints.length; i++) { + IBreakpoint breakpoint = breakpoints[i]; + if (resource.equals(breakpoint.getMarker().getResource())) { + if (((ILineBreakpoint) breakpoint).getLineNumber() == (lineNumber + 1)) { + // Remove existing breakpoint + breakpoint.delete(); + return; + } + } + } + // Create line breakpoint (doc line numbers start at 0) + String description; + IDocumentProvider provider = assemblerEditor.getDocumentProvider(); + IDocument document = provider.getDocument(assemblerEditor.getEditorInput()); + try { + int startOffset = document.getLineOffset(lineNumber); + int lineLength = document.getLineLength(lineNumber); + description = document.get(startOffset, lineLength).trim(); + description = description.replace('\t', ' '); + } catch (BadLocationException ex) { + throw new RuntimeException(ex); + } + + // No break points on empty lines + if (StringUtility.isEmpty(description)) { + return; + } + AssemblerBreakpoint breakpoint = new AssemblerBreakpoint(editorId, editorInput, resource, lineNumber + 1, description); + breakPointManager.addBreakpoint(breakpoint); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget# + * canToggleLineBreakpoints(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public boolean canToggleLineBreakpoints(IWorkbenchPart part, ISelection selection) { + return getEditor(part) != null; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleMethodBreakpoints + * (org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public void toggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.actions.IToggleBreakpointsTarget# + * canToggleMethodBreakpoints(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public boolean canToggleMethodBreakpoints(IWorkbenchPart part, ISelection selection) { + return false; + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#toggleWatchpoints + * (org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public void toggleWatchpoints(IWorkbenchPart part, ISelection selection) throws CoreException { + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.debug.ui.actions.IToggleBreakpointsTarget#canToggleWatchpoints + * (org.eclipse.ui.IWorkbenchPart, org.eclipse.jface.viewers.ISelection) + */ + @Override + public boolean canToggleWatchpoints(IWorkbenchPart part, ISelection selection) { + return false; + } + + /** + * Determines of the specified workbench part is an assembler editor with a + * valid resource. + * + * @param part + * The editor part or <code>null</code>. + * @return The assembler editor or <code>null</code>. + */ + private AssemblerEditor getEditor(IWorkbenchPart part) { + if (part instanceof AssemblerEditor) { + AssemblerEditor assemblerEditor = (AssemblerEditor) part; + if (assemblerEditor.getCurrentFile() != null) { + return assemblerEditor; + } + } + return null; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentAssistProcessor.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentAssistProcessor.java new file mode 100644 index 00000000..3e76dbad --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentAssistProcessor.java @@ -0,0 +1,573 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.StyledString.Styler; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.TextStyle; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceFile; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserLineCallback; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.asm.compiler.syntax.Directive; +import com.wudsn.ide.asm.compiler.syntax.Instruction; +import com.wudsn.ide.asm.compiler.syntax.InstructionSet; +import com.wudsn.ide.asm.compiler.syntax.InstructionType; +import com.wudsn.ide.asm.compiler.syntax.Opcode; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Class for content assist. Creates the content assist list. + * + * @author Peter Dell + * @author Daniel Mitte + */ +final class AssemblerContentAssistProcessor implements IContentAssistProcessor { + + /** + * Empty styler + */ + private final static class InstructionStyler extends Styler { + + InstructionStyler() { + } + + @Override + public void applyStyles(TextStyle textStyle) { + + } + } + + /** + * Underline styler + */ + private final static class HighlightStyler extends Styler { + + HighlightStyler() { + } + + @Override + public void applyStyles(TextStyle textStyle) { + textStyle.underline = true; + + } + } + + /** + * Callback to find out if a given line already contains an instruction. + * + * @since 1.6.0 + */ + private static final class SourceParserCallback extends + CompilerSourceParserLineCallback { + private boolean instructionFound; + private int instructionEndOffset; + + /** + * Create a new callback. + * + * @param filePath + * The absolute path of the source file, not empty and not + * <code>null</code>. + * @param lineNumber + * The line number, a non-negative integer or <code>-1</code> + * to indicate that no line number is relevant. + */ + public SourceParserCallback(String filePath, int lineNumber) { + super(filePath, lineNumber); + } + + @Override + public void processLine(CompilerSourceParser compilerSourceParser, + CompilerSourceFile compilerSourceFile, int lineNumber, + int startOffset, int symbolOffset, boolean instructionFound, + int instructionOffset, String instruction, int operandOffset, + CompilerSourceParserTreeObject section) { + + this.instructionFound = instructionFound; + if (instructionFound) { + instructionEndOffset = instructionOffset + instruction.length(); + } else { + instructionEndOffset = -1; + } + } + + /** + * Determines if the specified line in the source file already contains + * an instruction. + * + * @return <code>true</code> if the specified line in the source file + * already contains an instruction, <code>false</code> + * otherwise. + */ + public boolean wasInstructionFound() { + return instructionFound; + } + + /** + * Gets the offset of the last character of the instruction if an + * instruction was found. + * + * @return The offset or -1 if no instruction was found. + */ + public int getInstructionEndOffset() { + return instructionEndOffset; + } + } + + private AssemblerEditor editor; + + private Image directiveImage; + private Image legalOpcodeImage; + private Image illegalOpcodeImage; + private Image pseudoOpcodeImage; + private Styler instructionStyler; + private Styler highlightStyler; + + /** + * Creates a new instance. + * + * Called by + * {@link AssemblerSourceViewerConfiguration#getContentAssistant(org.eclipse.jface.text.source.ISourceViewer)} + * . + * + * @param editor + * The assembler editor for which this instance is created, not + * <code>null</code>. + */ + AssemblerContentAssistProcessor(AssemblerEditor editor) { + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + + this.editor = editor; + + AssemblerPlugin plugin = editor.getPlugin(); + directiveImage = plugin + .getImage("instruction-type-directive-16x16.gif"); + legalOpcodeImage = plugin + .getImage("instruction-type-legal-opcode-16x16.gif"); + illegalOpcodeImage = plugin + .getImage("instruction-type-illegal-opcode-16x16.gif"); + pseudoOpcodeImage = plugin + .getImage("instruction-type-pseudo-opcode-16x16.gif"); + instructionStyler = new InstructionStyler(); + highlightStyler = new HighlightStyler(); + } + + /** + * {@inheritDoc} + */ + @Override + public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, + int offset) { + if (viewer == null) { + throw new IllegalArgumentException( + "Parameter 'viewer' must not be null."); + } + ITextSelection selection = (ITextSelection) viewer + .getSelectionProvider().getSelection(); + + int selectionOffset = offset; + + if (selection.getOffset() != offset) { + selectionOffset = selection.getOffset(); + } + + List<ICompletionProposal> proposalList = new ArrayList<ICompletionProposal>(); + + // Convert offset into line number. + int lineNumber; + int lineOffset; + try { + lineNumber = viewer.getDocument().getLineOfOffset(offset); + lineOffset = viewer.getDocument().getLineOffset(lineNumber); + } catch (BadLocationException ex) { + lineNumber = -1; + lineOffset = -1; + } + + // Parse the current assembler file and try to find the line in the + // correct source file. + CompilerFiles files = AssemblerEditorFilesLogic.createInstance(editor).createCompilerFiles(); + if (files==null){ + return null; + } + SourceParserCallback compilerSourceCallback = new SourceParserCallback( + files.sourceFile.filePath, lineNumber); + CompilerSourceParser compilerSourceParser = editor + .createCompilerSourceParser(); + CompilerSourceFile compilerSourceFile = compilerSourceParser + .createCompilerSourceFile(files.sourceFile.file, + viewer.getDocument()); + compilerSourceParser.parse(compilerSourceFile, compilerSourceCallback); + + // If there is no instruction in the line yet or the cursor is exactly + // at the last character of that instruction, propose one. + if (!compilerSourceCallback.wasInstructionFound() + || selectionOffset == lineOffset + + compilerSourceCallback.getInstructionEndOffset()) { + String prefix = getPrefix(viewer, + compilerSourceParser.getCompilerSyntax(), selectionOffset, + false); + Region region = new Region(selectionOffset - prefix.length(), + prefix.length() + selection.getLength()); + addInstructionProposals(region, prefix, proposalList); + } else { + // Otherwise propose to use an identifier as operand. + String prefix = getPrefix(viewer, + compilerSourceParser.getCompilerSyntax(), selectionOffset, + true); + Region region = new Region(selectionOffset - prefix.length(), + prefix.length() + selection.getLength()); + addIdentifierProposals(region, prefix, compilerSourceFile, + proposalList); + } + + // If there is no proposal entry, return null instead of an empty array. + int size = proposalList.size(); + if (proposalList.size() == 0) { + return null; + } + + return proposalList.toArray(new ICompletionProposal[size]); + } + + /** + * Gets the prefix of the document starting at a given offset to the start + * of the document until a space or control character is found. + * + * @param viewer + * The viewer, not <code>null</code>. + * @param compilerSyntax + * The compiler syntax, not <code>null</code>. + * @param offset + * The offset, a non-negative integer. + * @param onlyIdentifiers + * <code>true</code> if only identifier characters shall be + * considered as part of the prefix. + * + * @return The prefix, may be empty, not <code>null</code>. + */ + private String getPrefix(ITextViewer viewer, CompilerSyntax compilerSyntax, + int offset, boolean onlyIdentifiers) { + if (viewer == null) { + throw new IllegalArgumentException( + "Parameter 'viewer' must not be null."); + } + if (compilerSyntax == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSyntax' must not be null."); + } + int i = offset; + IDocument document = viewer.getDocument(); + + int l = document.getLength(); + if (i > l) { + return ""; + } + + try { + while (i > 0) { + char ch = document.getChar(i - 1); + + if (onlyIdentifiers) { + if (!compilerSyntax.isIdentifierCharacter(ch)) { + break; + } + } else { + if (Character.isWhitespace(ch)) { + break; + } + } + + i--; + } + + return document.get(i, offset - i); + } catch (BadLocationException ex) { + throw new RuntimeException(ex); + } + } + + private void addInstructionProposals(Region region, String prefix, + List<ICompletionProposal> proposalList) { + if (region == null) { + throw new IllegalArgumentException( + "Parameter 'region' must not be null."); + } + if (prefix == null) { + throw new IllegalArgumentException( + "Parameter 'prefix' must not be null."); + } + if (proposalList == null) { + throw new IllegalArgumentException( + "Parameter 'proposalList' must not be null."); + } + AssemblerPreferences assemblerPreferences = editor.getPlugin() + .getPreferences(); + + int offset = region.getOffset(); + boolean lowerCase; + + // Prefix is empty or prefix does not end with a letter but for + // example "." + if (StringUtility.isEmpty(prefix) + || !Character.isLetter(prefix.charAt(prefix.length() - 1))) { + String defaultCase; + defaultCase = assemblerPreferences + .getEditorContentAssistProcessorDefaultCase(); + lowerCase = AssemblerContentAssistProcessorDefaultCase.LOWER_CASE + .equals(defaultCase); + } else { + char lastchar = prefix.charAt(prefix.length() - 1); + lowerCase = ((lastchar < 'a') || (lastchar > 'z')) ? false : true; + } + + CompilerSourceParser compilerSourceParser = editor + .createCompilerSourceParser(); + InstructionSet instructionSet = compilerSourceParser + .getInstructionSet(); + + boolean caseSenstive = instructionSet.areInstructionsCaseSensitive(); + if (!caseSenstive) { + prefix = prefix.toUpperCase(); + } + + List<Instruction> instructions = instructionSet.getInstructions(); + for (int i = 0; i < instructions.size(); i++) { + Instruction instruction = instructions.get(i); + + String name = null; + if (caseSenstive) { + if (instruction.getName().indexOf(prefix) == 0) { + name = instruction.getName(); + } + } else { + if (instruction.getUpperCaseName().indexOf(prefix) == 0) { + + name = lowerCase ? instruction.getLowerCaseName() + : instruction.getUpperCaseName(); + } + } + + if (name != null) { + Image image; + + if (instruction instanceof Directive) { + image = directiveImage; + } else { + Opcode opcode = (Opcode) instruction; + switch (opcode.getType()) { + case InstructionType.LEGAL_OPCODE: + image = legalOpcodeImage; + break; + case InstructionType.ILLEGAL_OPCODE: + image = illegalOpcodeImage; + + break; + case InstructionType.PSEUDO_OPCODE: + image = pseudoOpcodeImage; + break; + default: + throw new IllegalStateException("Unknown opcode type " + + opcode.getType() + "."); + } + } + + String separator = " - "; + String displayString = name + separator + + instruction.getTitle(); + StyledString styledDisplayString = new StyledString(); + styledDisplayString.append(name); + styledDisplayString.append(separator); + int start = styledDisplayString.length(); + styledDisplayString.append(instruction.getStyledTitle()); + styledDisplayString.setStyle(0, name.length(), + instructionStyler); + int[] offsets = instruction.getStyledTitleOffsets(); + + for (int j = 0; j < offsets.length; j++) { + styledDisplayString.setStyle(start + offsets[j], 1, + highlightStyler); + } + + // Adapt proposal. + String proposal = instruction.getProposal(); + proposal = lowerCase ? proposal.toLowerCase() : proposal; + int proposalIndex; + int newCursorOffset; + // Must be positive. + proposalIndex = proposal.indexOf('_'); + // Remove cursor positioning. + proposal = proposal.replace("_", ""); + // Apply leading tabulator. + proposal = proposal.replace("\n", "\n\t"); + newCursorOffset = offset + proposalIndex; + + proposalList.add(new AssemblerInstructionCompletionProposal( + proposal, offset, region.getLength(), newCursorOffset, + image, displayString, styledDisplayString, null)); + } + } + } + + // TODO Handle prefixes which contain "." or end with it. + // TODO Handle identifier case sensitivity correctly + private void addIdentifierProposals(Region region, String prefix, + CompilerSourceFile compilerSourceFile, + List<ICompletionProposal> proposalList) { + if (region == null) { + throw new IllegalArgumentException( + "Parameter 'region' must not be null."); + } + if (prefix == null) { + throw new IllegalArgumentException( + "Parameter 'prefix' must not be null."); + } + if (compilerSourceFile == null) { + throw new IllegalArgumentException( + "Parameter 'compilerSourceFile' must not be null."); + } + if (proposalList == null) { + throw new IllegalArgumentException( + "Parameter 'proposalList' must not be null."); + } + + CompilerSourceParserTreeObjectLabelProvider imageProvider = new CompilerSourceParserTreeObjectLabelProvider(); + IStyledLabelProvider styledStringProvider = imageProvider + .getStyledStringProvider(); + int regionOffset = region.getOffset(); + int regionLength = region.getLength(); + String lowerCasePrefix = prefix.toLowerCase(); + + // Find last separator as basis for the prefix. + char identifierSeparatorCharacter = editor.getCompilerDefinition() + .getSyntax().getIdentifierSeparatorCharacter(); + if (identifierSeparatorCharacter != CompilerSyntax.NO_CHARACTER) { + int index = lowerCasePrefix + .lastIndexOf(identifierSeparatorCharacter); + if (index >= 0) { + regionOffset += index + 1; + regionLength -= index + 1; + lowerCasePrefix = lowerCasePrefix.substring(index + 1); + } + } + List<CompilerSourceParserTreeObject> identifiers = compilerSourceFile + .getIdentifiers(); + String separator = " - "; + for (int i = 0; i < identifiers.size(); i++) { + CompilerSourceParserTreeObject element = identifiers.get(i); + String lowerCaseName = element.getName().toLowerCase(); + if (lowerCaseName.indexOf(lowerCasePrefix) == 0) { + String proposal = element.getName(); + Image image = imageProvider.getImage(element); + String displayName; + String description; + String displayString; + displayName = element.getDisplayName(); + description = element.getDescription(); + if (StringUtility.isSpecified(description)) { + displayString = displayName + separator + description; + } else { + displayString = displayName; + + } + StyledString styledDisplayString = styledStringProvider + .getStyledText(element); + + int newCursorOffset = regionOffset + proposal.length(); + + proposalList.add(new AssemblerInstructionCompletionProposal( + proposal, regionOffset, regionLength, newCursorOffset, + image, displayString, styledDisplayString, null)); + } + } + + } + + /** + * {@inheritDoc} + */ + @Override + public IContextInformation[] computeContextInformation(ITextViewer viewer, + int offset) { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public char[] getCompletionProposalAutoActivationCharacters() { + CompilerSyntax compilerSyntax = editor.getCompilerDefinition() + .getSyntax(); + char[] result = compilerSyntax + .getCompletionProposalAutoActivationCharacters(); + if (result.length == 0) { + result = null; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public char[] getContextInformationAutoActivationCharacters() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getErrorMessage() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public IContextInformationValidator getContextInformationValidator() { + return null; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentAssistProcessorDefaultCase.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentAssistProcessorDefaultCase.java new file mode 100644 index 00000000..a8e3c450 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentAssistProcessorDefaultCase.java @@ -0,0 +1,39 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +/** + * Constant definition for completion proposals. + * + * @author Peter Dell + * + */ +public final class AssemblerContentAssistProcessorDefaultCase { + + /** + * Creation is private. + */ + private AssemblerContentAssistProcessorDefaultCase() { + + } + + public static final String UPPER_CASE = "UPPER_CASE"; + public static final String LOWER_CASE = "LOWER_CASE"; +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentOutlinePage.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentOutlinePage.java new file mode 100644 index 00000000..a8d9d14b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentOutlinePage.java @@ -0,0 +1,590 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.TextSelection; +import org.eclipse.jface.viewers.ContentViewer; +import org.eclipse.jface.viewers.IBaseLabelProvider; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.swt.custom.BusyIndicator; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.ui.views.contentoutline.ContentOutlinePage; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceFile; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObjectType; +import com.wudsn.ide.base.common.Profiler; +import com.wudsn.ide.base.common.RunnableWithLogging; + +/** + * Outline page for the assembler editor. + * + * @author Peter Dell. + * @author Andy Reek + */ +final class AssemblerContentOutlinePage extends ContentOutlinePage { + + /* + * Toggle action to toggle the sorting in the outline tree. The state of the + * action will be persisted along with the file in the editor. If there is + * no state store yet along with in the file, the default is taken from the + * last file which was opened. + */ + private static final class OutlineViewerSortAction extends Action { + private static final QualifiedName CHECKED = new QualifiedName( + "OutlineViewerSortAction", "Checked"); + + final AssemblerEditor editor; + final TreeViewer treeViewer; + + /** + * Creates a new sort action. + * + * @param editor + * The editor which holds the respective file, not + * <code>null</code>. + * @param treeViewer + * The tree viewer which displays the outline. + */ + public OutlineViewerSortAction(AssemblerEditor editor, + TreeViewer treeViewer) { + super("", AS_CHECK_BOX); + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + if (treeViewer == null) { + throw new IllegalArgumentException( + "Parameter 'treeViewer' must not be null."); + } + setToolTipText(Texts.ASSEMBLER_CONTENT_OUTLINE_SORT_BUTTON_TOOL_TIP); + ImageDescriptor imageDescriptor = AbstractUIPlugin + .imageDescriptorFromPlugin(AssemblerPlugin.ID, + "icons/outline-sort.gif"); + setImageDescriptor(imageDescriptor); + this.editor = editor; + this.treeViewer = treeViewer; + + String checkedProperty; + try { + IFile iFile = editor.getCurrentIFile(); + if (iFile != null) { + checkedProperty = iFile.getPersistentProperty(CHECKED); + } else { + checkedProperty = ""; + } + } catch (CoreException ignore) { + checkedProperty = null; + } + if (checkedProperty == null) { + checkedProperty = editor.getPlugin().getProperty(CHECKED); + } + + boolean checked = Boolean.parseBoolean(checkedProperty); + setChecked(checked); + + } + + @Override + public void run() { + + // Get current state and update the UI. + boolean checked = isChecked(); + setChecked(checked); + + // Store the property. + String checkedProperty = Boolean.toString(checked); + try { + IFile iFile = editor.getCurrentIFile(); + if (iFile != null) { + iFile.setPersistentProperty(CHECKED, checkedProperty); + } + } catch (CoreException ex) { + editor.getPlugin().logError("Cannot set property {0}", + new Object[] { CHECKED }, ex); + } + editor.getPlugin().setProperty(CHECKED, checkedProperty); + + // Refresh the tree viewer. + BusyIndicator.showWhile(treeViewer.getControl().getDisplay(), + new RunnableWithLogging() { + @Override + protected void runWithLogging() { + treeViewer.refresh(false); + } + }); + } + } + + private static final class OutlineViewerComparator extends ViewerComparator { + private final OutlineViewerSortAction sortAction; + + OutlineViewerComparator(OutlineViewerSortAction sortAction) { + if (sortAction == null) { + throw new IllegalArgumentException( + "Parameter 'sortAction' must not be null."); + } + this.sortAction = sortAction; + } + + @Override + public int category(Object element) { + int result; + CompilerSourceParserTreeObject object = (CompilerSourceParserTreeObject) element; + + // Treat equate definition and label definition as equal. + result = object.getType(); + + switch (object.getType()) { + case CompilerSourceParserTreeObjectType.DEFAULT: + case CompilerSourceParserTreeObjectType.DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.IMPLEMENTATION_SECTION: + break; + + case CompilerSourceParserTreeObjectType.EQUATE_DEFINITION: + result = CompilerSourceParserTreeObjectType.EQUATE_DEFINITION; + break; + case CompilerSourceParserTreeObjectType.LABEL_DEFINITION: + result = CompilerSourceParserTreeObjectType.EQUATE_DEFINITION; + break; + + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.LOCAL_SECTION: + case CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.PAGES_SECTION: + case CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION: + case CompilerSourceParserTreeObjectType.REPEAT_SECTION: + break; + + case CompilerSourceParserTreeObjectType.SOURCE_INCLUDE: + case CompilerSourceParserTreeObjectType.BINARY_INCLUDE: + break; + + default: + throw new RuntimeException("Element '" + object.getName() + + "' has unknown type " + object.getType() + "."); + + } + return result; + } + + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + + if (!sortAction.isChecked()) { + return 0; + } + + int cat1 = category(e1); + int cat2 = category(e2); + + // Never sort definition or implementation sections. + if (cat1 == CompilerSourceParserTreeObjectType.DEFINITION_SECTION + && cat2 == CompilerSourceParserTreeObjectType.DEFINITION_SECTION) { + return 0; + } + + if (cat1 != cat2) { + return cat1 - cat2; + } + + String name1; + String name2; + + if (viewer == null || !(viewer instanceof ContentViewer)) { + name1 = e1.toString(); + name2 = e2.toString(); + } else { + IBaseLabelProvider prov = ((ContentViewer) viewer) + .getLabelProvider(); + if (prov instanceof ILabelProvider) { + ILabelProvider lprov = (ILabelProvider) prov; + name1 = lprov.getText(e1); + name2 = lprov.getText(e2); + } else { + name1 = e1.toString(); + name2 = e2.toString(); + } + } + if (name1 == null) { + name1 = "";//$NON-NLS-1$ + } + if (name2 == null) { + name2 = "";//$NON-NLS-1$ + } + + // Use direct comparison as identified are ASCII only. + return name1.compareTo(name2); + + } + } + + /** + * Editor updater for selection changes in the content outline page. + */ + private final static class EditorUpdater extends RunnableWithLogging { + private final Profiler profiler; + + private final AssemblerEditor editor; + private final AssemblerContentOutlinePage outlinePage; + private final TreeViewer viewer; + private final AssemblerContentOutlineTreeContentProvider contentProvider; + + EditorUpdater(AssemblerEditor editor, + AssemblerContentOutlinePage outlinePage, TreeViewer viewer) { + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + if (outlinePage == null) { + throw new IllegalArgumentException( + "Parameter 'outlinePage' must not be null."); + } + if (viewer == null) { + throw new IllegalArgumentException( + "Parameter 'viewer' must not be null."); + } + this.editor = editor; + this.outlinePage = outlinePage; + this.viewer = viewer; + this.contentProvider = (AssemblerContentOutlineTreeContentProvider) viewer + .getContentProvider(); + profiler = new Profiler(this); + + } + + /** + * Triggers a new + * {@link AssemblerContentOutlineTreeContentProvider#parse} run and + * updates the display. + */ + @Override + protected void runWithLogging() { + synchronized (outlinePage) { + try { + outlinePage.inputUpdateCounter++; + runSynchronized(); + } finally { + outlinePage.inputUpdateCounter--; + } + } + } + + private void runSynchronized() { + // Stop drawing the control. + Control control = viewer.getControl(); + + // Check if this call is caused by closing the editor. + if (control.isDisposed()) { + return; + } + + profiler.begin("runSynchronized"); + + profiler.begin("updateOutline"); + control.setRedraw(false); + + // Remember the currently selected tree object in the content + // outline tree viewer. + ISelection selection = viewer.getSelection(); + Object[] expandedElements = viewer.getExpandedElements(); + + // Trigger the the new parse run. + viewer.setInput(outlinePage.input); + // viewer.refresh(); Not required? + + profiler.begin("expandElements"); + if (expandedElements.length > 0) { + viewer.setExpandedElements(expandedElements); + } else { + viewer.expandToLevel(2); + } + profiler.end("expandElements"); + + restoreSelection(selection); + // Now that all changes are done, draw the control again. + control.setRedraw(true); + profiler.end("updateOutline"); + + // // Reselect the previous text selection in the editor. + // editor.getSelectionProvider().setSelection(textSelection); + + CompilerSourceFile compilerSourceFile; + compilerSourceFile = contentProvider.getCompilerSourceFile(); + + // Update the identifiers to be highlighted + profiler.begin("updateIdentifiers"); + editor.updateIdentifiers(compilerSourceFile); + profiler.end("updateIdentifiers"); + + // Update the folding structure. + profiler.begin("updateFoldingStructure"); + List<Position> foldingPositions; + if (compilerSourceFile != null) { + foldingPositions = compilerSourceFile.getFoldingPositions(); + } else { + foldingPositions = Collections.emptyList(); + } + editor.updateFoldingStructure(foldingPositions); + + profiler.end("updateFoldingStructure"); + + profiler.end("runSynchronized"); + + } + + private void restoreSelection(ISelection selection) { + if (selection instanceof TreeSelection) { + TreeSelection treeSelection = (TreeSelection) selection; + TreePath[] selectedTreePaths = treeSelection.getPaths(); + List<TreePath> reselectedTreePaths = new ArrayList<TreePath>( + selectedTreePaths.length); + for (int i = 0; i < selectedTreePaths.length; i++) { + TreePath treePath = selectedTreePaths[i]; + List<CompilerSourceParserTreeObject> treeObjects = contentProvider + .getCompilerSourceFile().getSections(); + + List<Object> segments = new ArrayList<Object>( + treePath.getSegmentCount()); + + for (int j = 0; j < treePath.getSegmentCount(); j++) { + CompilerSourceParserTreeObject oldTreeObject; + CompilerSourceParserTreeObject newTreeObject; + oldTreeObject = (CompilerSourceParserTreeObject) treePath + .getSegment(j); + newTreeObject = null; + + for (int k = 0; newTreeObject == null + && k < treeObjects.size(); k++) { + if (treeObjects.get(k).getTreePath() + .equals(oldTreeObject.getTreePath())) { + newTreeObject = treeObjects.get(k); + segments.add(newTreeObject); + treeObjects = newTreeObject.getChildren(); + } + } + + } + if (!segments.isEmpty()) { + reselectedTreePaths + .add(new TreePath(segments.toArray())); + } + + } + + TreePath[] reselectedTreePathsArray = new TreePath[reselectedTreePaths + .size()]; + reselectedTreePaths.toArray(reselectedTreePathsArray); + selection = new TreeSelection(reselectedTreePathsArray); + + // Reselect the previously selected tree object in the + // content outline tree viewer. + viewer.setSelection(selection); + } + } + } + + /** + * The owning editor. + */ + final AssemblerEditor editor; + + /** + * The visual components. + */ + private OutlineViewerSortAction treeViewerSortAction; + private OutlineViewerComparator treeViewerComparator; + + /** + * The current input. + */ + IEditorInput input; + int inputUpdateCounter; + + /** + * Creates a new instance. + * + * @param editor + * The assembler editor, not <code>null</code>. + */ + AssemblerContentOutlinePage(AssemblerEditor editor) { + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + this.editor = editor; + } + + /** + * Sets the input for the outline page. + * + * @param input + * The new input, not <code>null</code>. + */ + final void setInput(IEditorInput input) { + if (input == null) { + throw new IllegalArgumentException( + "Parameter 'input' must not be null."); + } + this.input = input; + + runEditorUpdater(); + } + + private void runEditorUpdater() { + final TreeViewer viewer = getTreeViewer(); + + if ((viewer != null) && (viewer.getContentProvider() != null)) { + editor.getSite().getShell().getDisplay() + .asyncExec(new EditorUpdater(editor, this, viewer)); + } + } + + /** + * Create the control and configures it. See code of + * org.eclipse.jdt.internal.ui.text.JavaOutlineInformationControl for + * similar use case. + * + * @param parent + * ´The parent, not <code>null</code>. + */ + @Override + public void createControl(Composite parent) { + super.createControl(parent); + + TreeViewer treeViewer = getTreeViewer(); + + // Configure the toolbar. + treeViewerSortAction = new OutlineViewerSortAction(editor, treeViewer); + treeViewerComparator = new OutlineViewerComparator(treeViewerSortAction); + IToolBarManager toolBarManager = getSite().getActionBars() + .getToolBarManager(); + + // Configure the content. + treeViewer + .setContentProvider(new AssemblerContentOutlineTreeContentProvider( + this)); + treeViewer + .setLabelProvider(new CompilerSourceParserTreeObjectLabelProvider()); + treeViewer.setComparator(treeViewerComparator); + treeViewer.addSelectionChangedListener(this); + + toolBarManager.add(treeViewerSortAction); + toolBarManager.update(true); + + if (input != null) { + runEditorUpdater(); + } + + } + + @Override + public void selectionChanged(SelectionChangedEvent event) { + super.selectionChanged(event); + + synchronized (this) { + if (inputUpdateCounter > 0) { + return; + } + } + + ISelection selection = event.getSelection(); + + if (selection.isEmpty()) { + editor.resetHighlightRange(); + } else { + if (selection instanceof IStructuredSelection) { + Object object = ((IStructuredSelection) selection) + .getFirstElement(); + + if (object instanceof CompilerSourceParserTreeObject) { + + CompilerSourceParserTreeObject treeObject; + AssemblerContentOutlineTreeContentProvider contentProvider; + contentProvider = (AssemblerContentOutlineTreeContentProvider) getTreeViewer() + .getContentProvider(); + treeObject = (CompilerSourceParserTreeObject) object; + + // If this is the tree object from another (source + // include) file, step off the tree to find the source + // include statement. + while (treeObject != null + && treeObject.getCompilerSourceFile() != contentProvider + .getCompilerSourceFile()) { + treeObject = treeObject.getParent(); + } + if (treeObject != null) { + try { + editor.setHighlightRange( + treeObject.getStartOffset(), 0, true); + editor.getSelectionProvider().setSelection( + new TextSelection(treeObject + .getStartOffset(), 1)); + } catch (IllegalArgumentException x) { + editor.resetHighlightRange(); + } + } + + } + } + } + } + + /** + * Gets the compiler source file of the last parse process. + * + * @return The compiler source file of the last parse process or + * <code>null</code>. + */ + final CompilerSourceFile getCompilerSourceFile() { + AssemblerContentOutlineTreeContentProvider contentProvider; + contentProvider = (AssemblerContentOutlineTreeContentProvider) getTreeViewer() + .getContentProvider(); + CompilerSourceFile compilerSourceFile = contentProvider + .getCompilerSourceFile(); + return compilerSourceFile; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentOutlineTreeContentProvider.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentOutlineTreeContentProvider.java new file mode 100644 index 00000000..a1cdb2d5 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerContentOutlineTreeContentProvider.java @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.List; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.IEditorInput; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceFile; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.base.common.Profiler; + +/** + * Tree content provider to {@link AssemblerContentOutlinePage}. + * + * @author Peter Dell + * @author Andy Reek + */ +final class AssemblerContentOutlineTreeContentProvider implements + ITreeContentProvider { + + /** + * The surrounding content outline page. + */ + private final AssemblerContentOutlinePage assemblerContentOutlinePage; + + /** + * The last editor input which was parsed. + */ + private IEditorInput input; + + /** + * The result of the last parse process. + */ + private CompilerSourceFile compilerSourceFile; + + /** + * Called by + * {@link AssemblerContentOutlinePage#createControl(org.eclipse.swt.widgets.Composite)} + * . + * + * @param assemblerContentOutlinePage + * The outline page, not <code>null</code>. + */ + AssemblerContentOutlineTreeContentProvider( + AssemblerContentOutlinePage assemblerContentOutlinePage) { + if (assemblerContentOutlinePage == null) { + throw new IllegalArgumentException( + "Parameter 'assemblerContentOutlinePage' must not be null."); + } + this.assemblerContentOutlinePage = assemblerContentOutlinePage; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getParent(Object element) { + if (element instanceof CompilerSourceParserTreeObject) { + return ((CompilerSourceParserTreeObject) element).getParent(); + } + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasChildren(Object element) { + if (element instanceof CompilerSourceParserTreeObject) { + return (((CompilerSourceParserTreeObject) element).hasChildren()); + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof CompilerSourceParserTreeObject) { + return ((CompilerSourceParserTreeObject) parentElement) + .getChildrenAsArray(); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Object[] getElements(Object inputElement) { + Object[] result; + if (inputElement == input && compilerSourceFile != null) { + List<CompilerSourceParserTreeObject> sections; + sections = compilerSourceFile.getSections(); + result = sections.toArray(new Object[sections.size()]); + } else { + result = new Object[0]; + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() { + input = null; + } + + /** + * Gets the compiler source file of the last parse process. + * + * @return The compiler source file of the last parse process or + * <code>null</code>. + */ + CompilerSourceFile getCompilerSourceFile() { + return compilerSourceFile; + } + + /** + * {@inheritDoc} + */ + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (oldInput instanceof IEditorInput) { + input = null; + } + + if (newInput instanceof IEditorInput) { + input = (IEditorInput) newInput; + parse(); + } + } + + /** + * Parses the new input and builds up the parse tree. + */ + private void parse() { + + AssemblerEditor editor = this.assemblerContentOutlinePage.editor; + IDocument document = editor.getDocumentProvider().getDocument( + editor.getEditorInput()); + + if (document != null) { + CompilerSourceParser parser = editor.createCompilerSourceParser(); + compilerSourceFile = parser.createCompilerSourceFile( + editor.getCurrentFile(), document); + Profiler profiler = new Profiler(parser); + profiler.begin("parse", editor.getTitle()); + parser.parse(compilerSourceFile, null); + profiler.end("parse"); + } + + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditor.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditor.java new file mode 100644 index 00000000..1ce697b7 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditor.java @@ -0,0 +1,558 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.ui.actions.ToggleBreakpointAction; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.Annotation; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.IVerticalRuler; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.text.source.projection.ProjectionAnnotation; +import org.eclipse.jface.text.source.projection.ProjectionAnnotationModel; +import org.eclipse.jface.text.source.projection.ProjectionSupport; +import org.eclipse.jface.text.source.projection.ProjectionViewer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.editors.text.TextEditor; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.part.FileEditorInput; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; +import org.eclipse.ui.texteditor.TextOperationAction; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.AssemblerProperties; +import com.wudsn.ide.asm.AssemblerProperties.InvalidAssemblerPropertyException; +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceFile; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.asm.compiler.parser.CompilerSourcePartitionScanner; +import com.wudsn.ide.asm.preferences.CompilerPreferences; +import com.wudsn.ide.base.common.Profiler; + +/** + * The assembler editor. + * + * @author Peter Dell + * @author Andy Reek + */ +public abstract class AssemblerEditor extends TextEditor { + + private AssemblerPlugin plugin; + private AssemblerEditorFilesLogic filesLogic; + + private Compiler compiler; + + private AssemblerContentOutlinePage contentOutlinePage; + private ProjectionAnnotationModel annotationModel; + + private Hardware hardware; + + /** + * Creates a new instance. Constructor parameters are not useful, because + * the super constructor inverts the flow of control, so + * {@link #initializeEditor} is called before the code in this constructor + * is executed. + */ + protected AssemblerEditor() { + filesLogic = AssemblerEditorFilesLogic.createInstance(this); + } + + /** + * Gets the files logic associated with this editor. + * + * @return The files logic, not <code>null</code>. + */ + public AssemblerEditorFilesLogic getFilesLogic() { + return filesLogic; + } + + /** + * Gets the hardware for this editor. + * + * @return The hardware for this editor, not <code>null</code>. + * + * @since 1.6.1 + */ + protected final Hardware getHardware() { + if (hardware != null) { + return hardware; + } + return compiler.getDefinition().getDefaultHardware(); + } + + /** + * Gets the compiler id for this editor. + * + * @return The compiler id for this editor, not empty and not + * <code>null</code>. + */ + protected abstract String getCompilerId(); + + @Override + protected final void initializeEditor() { + super.initializeEditor(); + + plugin = AssemblerPlugin.getInstance(); + compiler = plugin.getCompilerRegistry().getCompiler(getCompilerId()); + + setSourceViewerConfiguration(new AssemblerSourceViewerConfiguration(this, getPreferenceStore())); + + } + + /** + * Gets the plugin this compiler instance belongs to. + * + * @return The plugin this compiler instance belongs to, not + * <code>null</code>. + */ + public final AssemblerPlugin getPlugin() { + if (plugin == null) { + throw new IllegalStateException("Field 'plugin' must not be null."); + } + return plugin; + } + + /** + * Gets the compiler preferences. + * + * @return The compiler preferences, not <code>null</code>. + */ + public final CompilerPreferences getCompilerPreferences() { + return plugin.getPreferences().getCompilerPreferences(getCompilerId(), getHardware()); + } + + /** + * Gets the compiler for this editor. + * + * @return The compiler for this editor, not <code>null</code>. + */ + public final Compiler getCompiler() { + if (compiler == null) { + throw new IllegalStateException("Field 'compiler' must not be null."); + } + return compiler; + } + + /** + * Gets the compiler definition for this editor. + * + * @return The compiler definition for this editor, not <code>null</code>. + * + * @sine 1.6.1 + */ + public final CompilerDefinition getCompilerDefinition() { + if (compiler == null) { + throw new IllegalStateException("Field 'compiler' must not be null."); + } + return compiler.getDefinition(); + } + + /** + * Gets the compiler source parser for this editor and the currently + * selected instruction set. + * + * @return The compiler source parser for this editor, not <code>null</code> + * . + */ + public final CompilerSourceParser createCompilerSourceParser() { + CPU cpu; + CompilerSourceParser result; + if (compiler == null) { + throw new IllegalStateException("Field 'compiler' must not be null."); + } + cpu = getCompilerPreferences().getCPU(); + result = compiler.createSourceParser(); + result.init(compiler.getDefinition().getSyntax().getInstructionSet(cpu)); + return result; + } + + /** + * This method is called whenever the input changes, i.e. after loading and + * after saving as new file. + * + * @param input + * The new input, may be <code>null</code> + */ + @Override + protected final void doSetInput(IEditorInput input) throws CoreException { + super.doSetInput(input); + + hardware = null; + if (input != null) { + IDocument document = getDocumentProvider().getDocument(getEditorInput()); + + CompilerSourcePartitionScanner partitionScanner = new CompilerSourcePartitionScanner(compiler + .getDefinition().getSyntax()); + partitionScanner.createDocumentPartitioner(document); + + AssemblerProperties properties = CompilerSourceParser.getDocumentProperties(document); + + IFile iFile = getCurrentIFile(); + if (iFile != null) { + try { + hardware = filesLogic.getHardware(iFile, properties); + } catch (InvalidAssemblerPropertyException ex) { + // Do not use MarkerUtility.gotoMarker to make sure this + // editor instance is used. + IDE.gotoMarker(this, ex.marker); + hardware = null; + } + } + } + + } + + @Override + protected final void createActions() { + super.createActions(); + + ResourceBundle bundle = ResourceBundle.getBundle("com.wudsn.ide.asm.Actions", Locale.getDefault(), + AssemblerEditor.class.getClassLoader()); + + String actionDefintionId; + String actionId; + actionDefintionId = ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS; + actionId = "com.wudsn.ide.asm.editor.ContentAssistProposal"; + IAction action = new TextOperationAction(bundle, actionId + ".", this, ISourceViewer.CONTENTASSIST_PROPOSALS); + action.setActionDefinitionId(actionDefintionId); + setAction(actionId, action); + markAsStateDependentAction(actionId, true); + + SourceViewer sourceViewer = (SourceViewer) getSourceViewer(); + actionDefintionId = "com.wudsn.ide.asm.editor.AssemblerEditorToggleCommentCommand"; + actionId = actionDefintionId; + action = new AssemblerEditorToggleCommentAction(bundle, actionId + ".", this, sourceViewer); + action.setActionDefinitionId(actionId); + setAction(actionId, action); + markAsStateDependentAction(actionId, true); + + // Register rule double click. + ToggleBreakpointAction toggleBreakpointAction; + actionDefintionId = "org.eclipse.debug.ui.commands.ToggleBreakpoint"; + actionId = "RulerDoubleClick"; + action.setActionDefinitionId(actionId); + toggleBreakpointAction = new ToggleBreakpointAction(this, getDocumentProvider().getDocument(getEditorInput()), + getVerticalRuler()); + toggleBreakpointAction.setId(actionId); + setAction(actionId, toggleBreakpointAction); + markAsStateDependentAction(actionId, true); + toggleBreakpointAction.update(); + } + + final ISourceViewer getSourceViewerInternal() { + return getSourceViewer(); + } + + /** + * Refreshes the editor after changes to the text attributes or the text + * content. + * + * Called by {@link #updateIdentifiers(CompilerSourceFile)} and + * {@link AssemblerSourceViewerConfiguration#preferencesChanged(com.wudsn.ide.asm.preferences.AssemblerPreferences, java.util.Set)} + * . + */ + final void refreshSourceViewer() { + ISourceViewer isv = getSourceViewer(); + if (isv instanceof SourceViewer) { + ((SourceViewer) getSourceViewer()).invalidateTextPresentation(); + } + } + + @Override + public final void dispose() { + AssemblerSourceViewerConfiguration asvc; + asvc = (AssemblerSourceViewerConfiguration) getSourceViewerConfiguration(); + asvc.dispose(); + super.dispose(); + } + + @Override + public final Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + if (IContentOutlinePage.class.equals(adapter)) { + if (contentOutlinePage == null) { + contentOutlinePage = new AssemblerContentOutlinePage(this); + // This causes double parsing upon starting with a new file + // currently. + updateContentOutlinePage(); + } + return contentOutlinePage; + } + return super.getAdapter(adapter); + } + + /** + * Updates the content in view of the outline page. Called by + * {@link AssemblerReconcilingStategy#parse}. + */ + final void updateContentOutlinePage() { + if (contentOutlinePage != null) { + IEditorInput input = getEditorInput(); + + if (input != null) { + contentOutlinePage.setInput(input); + } + } + } + + /** + * Gets the compiler source file of the last parse process. + * + * @return The compiler source file of the last parse process or + * <code>null</code>. + */ + final CompilerSourceFile getCompilerSourceFile() { + CompilerSourceFile result; + if (contentOutlinePage != null) { + result = contentOutlinePage.getCompilerSourceFile(); + } else { + result = null; + } + return result; + } + + @Override + public final void createPartControl(Composite parent) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + super.createPartControl(parent); + ProjectionViewer viewer = (ProjectionViewer) getSourceViewer(); + + ProjectionSupport projectionSupport = new ProjectionSupport(viewer, getAnnotationAccess(), getSharedColors()); + projectionSupport.install(); + + // turn projection mode on + viewer.doOperation(ProjectionViewer.TOGGLE); + + annotationModel = viewer.getProjectionAnnotationModel(); + + } + + @Override + protected final ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + if (ruler == null) { + throw new IllegalArgumentException("Parameter 'ruler' must not be null."); + } + ISourceViewer viewer = new ProjectionViewer(parent, ruler, getOverviewRuler(), isOverviewRulerVisible(), styles); + + // Ensure decoration support has been created and configured. + getSourceViewerDecorationSupport(viewer); + + // The first single line comment delimiter is used as the default. + List<String> singleLineCommentDelimiters = compiler.getDefinition().getSyntax() + .getSingleLineCommentDelimiters(); + String[] array = singleLineCommentDelimiters.toArray(new String[singleLineCommentDelimiters.size()]); + viewer.setDefaultPrefixes(array, IDocument.DEFAULT_CONTENT_TYPE); + viewer.setDefaultPrefixes(array, CompilerSourcePartitionScanner.PARTITION_COMMENT_SINGLE); + + return viewer; + } + + /** + * Update the identifiers to be highlighted + * + * @param compilerSourceFile + * The compiler source file or <code>null</code>. + */ + final void updateIdentifiers(CompilerSourceFile compilerSourceFile) { + Profiler profiler = new Profiler(this.getClass()); + + AssemblerSourceViewerConfiguration asvc; + AssemblerSourceScanner ais; + asvc = (AssemblerSourceViewerConfiguration) getSourceViewerConfiguration(); + ais = asvc.getAssemblerInstructionScanner(); + + List<CompilerSourceParserTreeObject> newIdentifiers; + if (compilerSourceFile == null) { + newIdentifiers = Collections.emptyList(); + } else { + newIdentifiers = compilerSourceFile.getIdentifiers(); + } + + ais.setIdentifiers(newIdentifiers); + profiler.begin("refreshSourceViewer"); + // refreshSourceViewer(); + profiler.end("refreshSourceViewer"); + } + + /** + * Update the folding structure with a given list of foldingPositions. Used + * by the editor updater of {@link AssemblerReconcilingStategy}. + * + * @param foldingPositions + * The list of foldingPositions, may be empty, not + * <code>null</code>. + */ + final void updateFoldingStructure(List<Position> foldingPositions) { + if (foldingPositions == null) { + throw new IllegalArgumentException("Parameter 'foldingPositions' must not be null."); + } + + // Create a working copy. + foldingPositions = new ArrayList<Position>(foldingPositions); + List<ProjectionAnnotation> deletions = new ArrayList<ProjectionAnnotation>(); + Object annotationObject = null; + ProjectionAnnotation annotation = null; + Position position = null; + + // Access to the annotationModel is intentionally not synchronized, as + // otherwise deadlock would be the result. + for (@SuppressWarnings("rawtypes") + Iterator iter = annotationModel.getAnnotationIterator(); iter.hasNext();) { + annotationObject = iter.next(); + + if (annotationObject instanceof ProjectionAnnotation) { + annotation = (ProjectionAnnotation) annotationObject; + + position = annotationModel.getPosition(annotation); + + if (foldingPositions.contains(position)) { + foldingPositions.remove(position); + } else { + deletions.add(annotation); + } + } + + } + + Annotation[] removeAnnotations = deletions.toArray(new Annotation[deletions.size()]); + + // This will hold the new annotations along + // with their corresponding folding positions. + HashMap<ProjectionAnnotation, Position> newAnnotations = new HashMap<ProjectionAnnotation, Position>(); + + for (int i = 0; i < foldingPositions.size(); i++) { + annotation = new ProjectionAnnotation(); + newAnnotations.put(annotation, foldingPositions.get(i)); + } + + // Do not update anything if there is actual change to preserve the + // current cursor positioning. + if (removeAnnotations.length == 0 && newAnnotations.isEmpty()) { + return; + } + + annotationModel.modifyAnnotations(removeAnnotations, newAnnotations, new Annotation[] {}); + } + + /** + * Gets the directory of the current file. + * + * @return The directory of the current file or <code>null</code>. + */ + public final File getCurrentDirectory() { + File result; + result = getCurrentFile(); + if (result != null) { + result = result.getParentFile(); + } + return result; + } + + /** + * Gets the the current file. + * + * @return The current file or <code>null</code>. + */ + public final File getCurrentFile() { + File result; + IEditorInput editorInput = getEditorInput(); + if (editorInput instanceof FileEditorInput) { + FileEditorInput fileEditorInput = (FileEditorInput) editorInput; + result = new File(fileEditorInput.getPath().toOSString()); + } else { + result = null; + } + return result; + } + + /** + * Gets the the current file. + * + * @return The current file or <code>null</code>. + */ + public final IFile getCurrentIFile() { + IFile result; + IEditorInput editorInput = getEditorInput(); + if (editorInput instanceof FileEditorInput) { + FileEditorInput fileEditorInput = (FileEditorInput) editorInput; + result = fileEditorInput.getFile(); + + } else { + result = null; + } + return result; + } + + /** + * Position the cursor to the specified line in the document. + * + * @param line + * The line number, a positive integer. + * + * @return <code>true</code> if the positioning was successful. + */ + public final boolean gotoLine(int line) { + if (line < 1) { + throw new IllegalArgumentException("Parameter 'line' must be positive. Specified value is " + line + "."); + } + IDocumentProvider provider = getDocumentProvider(); + IDocument document = provider.getDocument(getEditorInput()); + boolean result = false; + try { + int startOffset = document.getLineOffset(line - 1); + int lineLength = document.getLineLength(line - 1); + if (lineLength > 0) { + lineLength = lineLength - 1; + } + selectAndReveal(startOffset, lineLength); + result = true; + } catch (BadLocationException ex) { + plugin.logError("Cannot position to line {0}.", new Object[] { String.valueOf(line) }, ex); + result = false; + } + return result; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileAndRunCommandMenu.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileAndRunCommandMenu.java new file mode 100644 index 00000000..4d5fc0d0 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileAndRunCommandMenu.java @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.Date; +import java.util.List; + +import org.eclipse.e4.ui.di.AboutToShow; +import org.eclipse.e4.ui.model.application.ui.menu.MDirectMenuItem; +import org.eclipse.e4.ui.model.application.ui.menu.MMenuElement; +import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.menus.WorkbenchWindowControlContribution; + +// TODO: Dynamic menu contribution is not working! +public final class AssemblerEditorCompileAndRunCommandMenu extends + WorkbenchWindowControlContribution { + + public AssemblerEditorCompileAndRunCommandMenu() { + new Exception().printStackTrace(); + } + + public AssemblerEditorCompileAndRunCommandMenu(String id) { + super(id); + } + + @AboutToShow + public void aboutToShow(List<MMenuElement> items) { + MDirectMenuItem dynamicItem = MMenuFactory.INSTANCE + .createDirectMenuItem(); + dynamicItem.setLabel("Dynamic Menu Item (" + new Date() + ")"); + dynamicItem + .setContributorURI("platform:/plugin/at.descher.eclipse.bug389063"); + dynamicItem + .setContributionURI("bundleclass://at.descher.eclipse.bug389063/at.descher.eclipse.bug389063.dynamic.DirectMenuItemAHandler"); + items.add(dynamicItem); + + } + + @Override + protected Control createControl(Composite parent) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommand.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommand.java new file mode 100644 index 00000000..7766909b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommand.java @@ -0,0 +1,802 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IBreakpointManager; +import org.eclipse.debug.core.model.IBreakpoint; +import org.eclipse.swt.program.Program; +import org.eclipse.ui.IPageLayout; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.console.IConsoleConstants; +import org.eclipse.ui.console.IConsoleView; +import org.eclipse.ui.ide.IDE; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.HardwareUtility; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerConsole; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerFileWriter; +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser; +import com.wudsn.ide.asm.compiler.CompilerProcessLogParser.Marker; +import com.wudsn.ide.asm.compiler.CompilerSymbol; +import com.wudsn.ide.asm.compiler.CompilerVariables; +import com.wudsn.ide.asm.preferences.CompilerRunPreferences; +import com.wudsn.ide.asm.runner.Runner; +import com.wudsn.ide.asm.runner.RunnerDefinition; +import com.wudsn.ide.asm.runner.RunnerId; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.MarkerUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.ProcessWithLogs; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Implementation of the "Compile" command. + * + * @author Peter Dell + */ +final class AssemblerEditorCompileCommand { + + /** + * Commands. + */ + public static final String COMPILE = "com.wudsn.ide.asm.editor.AssemblerEditorCompileCommand"; + public static final String COMPILE_AND_RUN = "com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunCommand"; + public static final String COMPILE_AND_RUN_WITH = "com.wudsn.ide.asm.editor.AssemblerEditorCompileAndRunWithCommand"; + + /** + * The owning plugin. + */ + private AssemblerPlugin plugin; + + /** + * Creation is private. + */ + private AssemblerEditorCompileCommand() { + plugin = AssemblerPlugin.getInstance(); + } + + /** + * Creates a message associated with the main source file of an + * {@link CompilerFiles} instance. The message is not bound to a line + * number. + * + * @param files + * The {@link CompilerFiles} not <code>null</code>. + * @param severity + * The message severity, see {@link IMarker#SEVERITY} + * @param message + * The message, may contain parameter "{0}" to "{9}". May be + * empty, not <code>null</code>. + * @param parameters + * The format parameters for the message, may be empty, not + * <code>null</code>. + */ + private void createMainSourceFileMessage(CompilerFiles files, int severity, String message, String... parameters) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + MarkerUtility.createMarker(files.mainSourceFile.iFile, 0, severity, message, parameters); + } + + /** + * Executes a compile command. + * + * @param assemblerEditor + * The assembler editor, not <code>null</code>. + * @param files + * The compiler files, not <code>null</code>. + * @param commandId + * The command id, see {@link #COMPILE}, {@link #COMPILE_AND_RUN} + * , {@link #COMPILE_AND_RUN_WITH}. + * @param runnerId + * The runner id, may be empty or <code>null</code>. + * + * @throws RuntimeException + */ + public static void execute(AssemblerEditor assemblerEditor, CompilerFiles files, String commandId, String runnerId) + throws RuntimeException { + + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + if (commandId == null) { + throw new IllegalArgumentException("Parameter 'commandId' must not be null."); + } + + // Ensure all current changes in all open editors are saved. + if (!IDE.saveAllEditors(new IResource[] { ResourcesPlugin.getWorkspace().getRoot() }, false)) { + return; + } + + assemblerEditor.doSave(null); + + IWorkbenchPage page = assemblerEditor.getSite().getPage(); + + AssemblerEditorCompileCommand instance; + instance = new AssemblerEditorCompileCommand(); + + try { + instance.executeInternal(assemblerEditor, files, commandId, runnerId); + } catch (RuntimeException ex) { + throw ex; + } + + try { + // Remember active editor. + IWorkbenchPart activePart = page.getActivePart(); + + // Show console. + CompilerConsole compilerConsole = instance.plugin.getCompilerConsole(); + IConsoleView consoleView = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW); + compilerConsole.display(consoleView); + + // Show problems view. + page.showView(IPageLayout.ID_PROBLEM_VIEW); + + // Reactivate previously active editor. + page.activate(activePart); + + } catch (PartInitException ex) { + throw new RuntimeException("Cannot show view.", ex); + } + } + + private boolean executeInternal(AssemblerEditor assemblerEditor, CompilerFiles files, String commandId, + String runnerId) { + + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + if (commandId == null) { + throw new IllegalArgumentException("Parameter 'commandId' must not be null."); + } + + AssemblerEditorFilesLogic assemblerEditorFilesLogic = AssemblerEditorFilesLogic.createInstance(assemblerEditor); + + // Remove existing problem markers from all files. + if (!assemblerEditorFilesLogic.removeMarkers(files)) { + return false; + } + + // Determine and check hardware. + Hardware hardware = assemblerEditorFilesLogic.getHardware(files); + if (hardware == null) { + return false; + } + + // Check files based on the compiler definition. + if (!assemblerEditorFilesLogic.validateOutputFile(files)) { + return false; + } + + // Create wrapper for run properties. + CompilerDefinition compilerDefinition = assemblerEditor.getCompilerDefinition(); + CompilerRunPreferences compilerRunPreferences = new CompilerRunPreferences( + assemblerEditor.getCompilerPreferences(), files.mainSourceFile.assemblerProperties); + + // Check if output file is modifiable in case it already exists. + long outputFileLastModified = -1; + if (files.outputFile.exists()) { + boolean canWrite = files.outputFile.canWrite(); + if (canWrite) { + try { + FileOutputStream fos = new FileOutputStream(files.outputFile, true); + try { + fos.close(); + } catch (IOException ex) { + plugin.logError("Cannot close file output stream", null, ex); + } + } catch (FileNotFoundException ex) { + canWrite = false; + } + + } + if (!canWrite) { + // ERROR: Output file '{0}' cannot be opened for writing. End + // all applications which may keep the file open. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E106, files.outputFileName); + return false; + } + outputFileLastModified = files.outputFile.lastModified(); + } + + // Get and check path to compiler executable. + String compilerExecutablePath = plugin.getPreferences().getCompilerExecutablePath(compilerDefinition.getId()); + if (StringUtility.isEmpty(compilerExecutablePath)) { + // ERROR: Path to '{0}' compiler executable is not set in the + // preferences. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E100, compilerDefinition.getName()); + return false; + } + File compilerExecutableFile = new File(compilerExecutablePath); + if (!compilerExecutableFile.exists()) { + // Path to '{0}' compiler executable in the preferences points + // to non-existing file '{1}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E103, + compilerDefinition.getName(), compilerExecutablePath); + return false; + } + + // Get and check compiler executable parameters. + String compilerParameters = compilerRunPreferences.getParameters(); + if (StringUtility.isEmpty(compilerParameters)) { + compilerParameters = compilerDefinition.getDefaultParameters(); + } + + // The parameters are first split and then substituted. + // This allows for parameters and file paths inner spaces to be used. + // In some case addition quotes must be places around parameters, for + // example for the "${sourceFilePath}". This can be used to avoid + // problems with absolute file path under Unix starting with "/" or path + // containing white spaces. + compilerParameters = compilerParameters.trim(); + String compilerParameterArray[] = compilerParameters.split(" "); + if (compilerParameterArray.length == 0) { + // ERROR: The compiler '{0}' does not specify default + // parameters. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E101); + return false; + } + + // From here on, the method is linear, i.e. there is no "return" until + // the end. + + // Special handling for direct execution of ".jar" files. + String[] fullCommandLineArray; + int offset; + if (compilerExecutablePath.toLowerCase().endsWith(".jar")) { + offset = 3; + fullCommandLineArray = new String[offset + compilerParameterArray.length]; + fullCommandLineArray[0] = "java"; + fullCommandLineArray[1] = "-jar"; + fullCommandLineArray[2] = compilerExecutablePath; + } else { + offset = 1; + fullCommandLineArray = new String[offset + compilerParameterArray.length]; + fullCommandLineArray[0] = compilerExecutablePath; + } + + // Map parameter with variables replacement. + for (int i = 0; i < compilerParameterArray.length; i++) { + String parameter = compilerParameterArray[i]; + parameter = CompilerVariables.replaceVariables(parameter, files); + fullCommandLineArray[i + offset] = parameter; + } + + ProcessWithLogs compilerProcess = new ProcessWithLogs(fullCommandLineArray, files.mainSourceFile.folder); + CompilerConsole compilerConsole = plugin.getCompilerConsole(); + compilerConsole.println(""); + compilerConsole.println("Compiling for hardware " + hardware.name() + " on " + + new SimpleDateFormat().format(new Date()) + ": " + compilerProcess.getCommandArrayString()); + + try { + compilerProcess.exec(System.out, System.err, true); + } catch (IOException ex) { + // ERROR: Cannot execute compiler process '{0}' in working directory + // '{1}'. System error: {2} + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E105, + compilerProcess.getCommandArrayString(), compilerProcess.getWorkingDirectory().getPath(), + ex.getMessage()); + } + + // Refresh the output and the symbols file resource. + if (files.outputFolderPath.equals(files.mainSourceFile.folderPath) + || files.outputFolderPath.equals(files.sourceFile.folderPath)) { + try { + IResource outputResource = files.mainSourceFile.iFile.getParent(); + if (outputResource != null) { + outputResource.refreshLocal(IResource.DEPTH_ONE, null); + } + outputResource = files.sourceFile.iFile.getParent(); + if (outputResource != null) { + outputResource.refreshLocal(IResource.DEPTH_ONE, null); + } + + } catch (CoreException ex) { + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, ex.getMessage()); + return false; + } + } + + compilerConsole.println(""); + compilerConsole.println("Compiler '" + compilerDefinition.getName() + "' output:"); + compilerConsole.println(compilerProcess.getErrorLog()); + compilerConsole.println(compilerProcess.getOutputLog()); + + // Compiling is over, check the result. + Compiler compiler = assemblerEditor.getCompiler(); + boolean compilerSuccess = compiler.isSuccessExitValue(compilerProcess.getExitValue()); + if (compilerSuccess) { + if (files.outputFile.exists()) { + if (files.outputFile.length() > 0) { + if (files.outputFile.lastModified() != outputFileLastModified) { + // INFO: Output file '{0}' created or updated with {1} + // (${2}) bytes. + long fileLength = files.outputFile.length(); + createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I109, + files.outputFilePath, Long.toString(fileLength), + HexUtility.getLongValueHexString(fileLength)); + + // Handle disk images + CompilerFileWriter compilerFileWriter = HardwareUtility + .getCompilerFileWriter(compilerRunPreferences.getHardware()); + if (!compilerFileWriter.createOrUpdateDiskImage(files)) { + return false; + } + + if (commandId.equals(COMPILE_AND_RUN) || commandId.equals(COMPILE_AND_RUN_WITH)) { + + openOutputFile(assemblerEditor, files, compilerRunPreferences, compilerConsole, runnerId); + + } + } else { + // ERROR: Output file not updated. Check the error + // messages and the console log. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E108); + + } + } else { + // ERROR: Output file created but empty. Check the error + // messages and the console log. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E126); + } + } else { + // ERROR: No output file created. Check the error messages and + // the + // console log. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E107); + } + } + + // Output an additional message if the reason for the compiler's exit + // value is not already contained in the error messages. + boolean errorFound = parseLogs(assemblerEditor, files, compilerProcess); + if (!compilerSuccess && !errorFound) { + // ERROR: Compiler process ended with error code {0}. Check the + // error messages and the console log. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E127, + NumberUtility.getLongValueDecimalString(compilerProcess.getExitValue())); + } + + return true; + } + + /** + * Creates or deletes the breakpoints file based on the runner. + * + * @param assemblerEditor + * The assembler editor, not <code>null</code>. + * @param files + * The assembler files, not <code>null</code>. + * @param runner + * The runner, not <code>null</code>. + * + * @since 1.6.1 + */ + private void createBreakpointsFile(AssemblerEditor assemblerEditor, CompilerFiles files, Runner runner) { + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + if (runner == null) { + throw new IllegalArgumentException("Parameter 'runner' must not be null."); + } + File breakpointsFile = runner.createBreakpointsFile(files); + IBreakpointManager breakpointManager = DebugPlugin.getDefault().getBreakpointManager(); + IBreakpoint breakpoints[] = breakpointManager.getBreakpoints(AssemblerBreakpoint.DEBUG_MODEL_ID); + if (breakpointsFile == null) { + if (breakpoints.length > 0) { + // WARNING: Breakpoints will be ignored because the application + // '{0}' does not support passing source level breakpoints. + createMainSourceFileMessage(files, IMarker.SEVERITY_WARNING, Texts.MESSAGE_W120, new String[] { runner + .getDefinition().getName() }); + } + return; + } + + // If breakpoints are present, a breakpoints file is generated. + if (breakpoints.length >= 0) { + AssemblerBreakpoint[] assemblerBreakpoints = new AssemblerBreakpoint[breakpoints.length]; + System.arraycopy(breakpoints, 0, assemblerBreakpoints, 0, breakpoints.length); + StringBuilder breakpointBuilder = new StringBuilder(); + int activeBreakpointCount = runner.createBreakpointsFileContent(assemblerBreakpoints, breakpointBuilder); + try { + FileUtility.writeString(breakpointsFile, breakpointBuilder.toString()); + } catch (CoreException ex) { + // ERROR: Cannot open breakpoints file '{0}' for output. System + // error: {1} + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E122, new String[] { + breakpointsFile.getPath(), ex.getMessage() }); + return; + } + // INFO: Breakpoints file '{0}' created with {1} active breakpoints. + createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I121, new String[] { + breakpointsFile.getPath(), NumberUtility.getLongValueDecimalString(activeBreakpointCount) }); + + } else { + // If no breakpoints are present, the breakpoints file is deleted. + if (breakpointsFile.exists()) { + if (!breakpointsFile.delete()) { + // ERROR: Cannot delete empty breakpoints file '{0}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E123, + new String[] { breakpointsFile.getPath() }); + } + } + } + } + + private void openOutputFile(AssemblerEditor assemblerEditor, CompilerFiles files, + CompilerRunPreferences compilerRunPreferences, CompilerConsole compilerConsole, String runnerId) { + + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + if (compilerRunPreferences == null) { + throw new IllegalArgumentException("Parameter 'compilerRunPreferences' must not be null."); + } + if (compilerConsole == null) { + throw new IllegalArgumentException("Parameter 'compilerConsole' must not be null."); + } + String[] fullCommandLineArray; + ProcessWithLogs runnerProcess; + + fullCommandLineArray = null; + + // If the runner id was no specified explicitly, the default from the + // preferences is used. + if (runnerId == null || StringUtility.isEmpty(runnerId)) { + runnerId = compilerRunPreferences.getRunnerId(); + } + Hardware hardware = compilerRunPreferences.getHardware(); + Runner runner; + runner = plugin.getRunnerRegistry().getRunner(hardware, runnerId); + if (runner == null) { + // ERROR: Definition for application '{0}' from the preferences of + // hardware '{1}' is not registered. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E116, runnerId, + hardware.toString()); + return; + } + + createBreakpointsFile(assemblerEditor, files, runner); + + String runnerCommandLine; + runnerCommandLine = compilerRunPreferences.getRunnerCommandLine(runnerId); + if (StringUtility.isEmpty(runnerCommandLine)) { + runnerCommandLine = runner.getDefinition().getDefaultCommandLine(); + } + runnerCommandLine = runnerCommandLine.trim(); + + // The parameters are first split and then substituted. + // This allows for parameters and file paths inner spaces to + // be used. In some case addition quotes must be places around + // parameters, for example for the "${outputFilePath}" for + // MADS. Otherwise using absolute file path under Unix starting + // "/" may cause conflicts. + String[] commandLineArray; + commandLineArray = runnerCommandLine.split(" "); + + // Execution type: DEFAULT_APPLICATION + if (runnerId.equals(RunnerId.DEFAULT_APPLICATION)) { + String extension = files.outputFileName; + int index = extension.lastIndexOf('.'); + if (index > 0) { + extension = extension.substring(index); + } + Program program = Program.findProgram(extension); + if (program == null) { + // ERROR: Cannot open output file '{0}' with the + // standard application since no application is + // registered for the file extension '{1}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E115, files.outputFilePath, + extension); + } else { + + if (Program.launch(files.outputFilePath)) { + // INFO: Opening output file '{0}' with + // application + // '{1}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I118, files.outputFilePath, + program.getName()); + + compilerConsole.println("Running '" + runner.getDefinition().getName() + "': " + program.getName() + + " " + files.outputFilePath); + } else { + // ERROR: Cannot open output file '{0}' with + // application '{1}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E119, + files.outputFilePath, program.getName()); + } + } + } + // Execution type: pre-defined or USER_DEFINED_APPLICATION + else { + boolean error = false; + + String runnerExecutablePath = compilerRunPreferences.getRunnerExecutablePath(runnerId); + if (runnerCommandLine.contains(RunnerDefinition.RUNNER_EXECUTABLE_PATH)) { + if (StringUtility.isEmpty(runnerExecutablePath)) { + // ERROR: Path to application executable is not + // set in the preferences of application '{0}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E112, runner + .getDefinition().getName()); + error = true; + } else { + File runnerExecutableFile = new File(runnerExecutablePath); + if (!runnerExecutableFile.exists()) { + // ERROR: Path to '{0}' application + // executable in the preferences points to + // non-existing file '{1}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E114, runner + .getDefinition().getName(), runnerExecutablePath); + error = true; + } + } + } + if (!error) { + + fullCommandLineArray = new String[commandLineArray.length]; + for (int i = 0; i < commandLineArray.length; i++) { + String parameter = commandLineArray[i]; + parameter = CompilerVariables.replaceVariables(parameter, files); + parameter = replaceRunnerParameters(parameter, runnerExecutablePath); + parameter = parameter.replace(RunnerDefinition.OUTPUT_FILE_PATH, files.outputFilePath); + fullCommandLineArray[i] = parameter; + } + + // INFO: Opening output file '{0}' with application '{1}'. + createMainSourceFileMessage(files, IMarker.SEVERITY_INFO, Texts.MESSAGE_I118, files.outputFilePath, + runner.getDefinition().getName()); + + runnerProcess = new ProcessWithLogs(fullCommandLineArray, files.outputFolder); + + compilerConsole.println("Running '" + runner.getDefinition().getName() + "': " + + runnerProcess.getCommandArrayString()); + + try { + boolean wait = compilerRunPreferences.isRunnerWaitForCompletion(runnerId); + runnerProcess.exec(compilerConsole.getPrintStream(), compilerConsole.getPrintStream(), wait); + compilerConsole + .println("Application returned with exit code " + runnerProcess.getExitValue() + "."); + } catch (IOException ex) { + // ERROR: Cannot execute application '{0}' process '{1}' in + // working directory '{2}'. System error: {3} + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, Texts.MESSAGE_E113, runner + .getDefinition().getName(), runnerProcess.getCommandArrayString(), runnerProcess + .getWorkingDirectory().getPath(), ex.getMessage()); + } + } + } + } + + private String replaceRunnerParameters(String parameter, String runnerExecutablePath) { + if (parameter == null) { + throw new IllegalArgumentException("Parameter 'parameter' must not be null."); + } + if (runnerExecutablePath == null) { + throw new IllegalArgumentException("Parameter 'runnerExecutablePath' must not be null."); + } + parameter = parameter.replace(RunnerDefinition.RUNNER_EXECUTABLE_PATH, runnerExecutablePath); + return parameter; + } + + private boolean parseLogs(AssemblerEditor assemblerEditor, CompilerFiles files, ProcessWithLogs compileProcess) { + + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + if (compileProcess == null) { + throw new IllegalArgumentException("Parameter 'compileProcess' must not be null."); + } + Compiler compiler = assemblerEditor.getCompiler(); + CompilerProcessLogParser logParser = compiler.createLogParser(); + + // Line parser with main source file and logs. + logParser.setLogs(files, compileProcess.getOutputLog(), compileProcess.getErrorLog()); + + Set<Marker> set = new HashSet<Marker>(); + List<IMarker> markers = new ArrayList<IMarker>(); + while (logParser.nextMarker()) { + Marker markerProxy = logParser.getMarker(); + while (markerProxy != null) { // Loop to add main marker and its detail markers + if (!set.contains(markerProxy)) { + set.add(markerProxy); + try { + IFile iFile = markerProxy.getIFile(); + IMarker marker = iFile.createMarker(IMarker.PROBLEM); + marker.setAttribute(IMarker.SEVERITY, markerProxy.getSeverity()); + marker.setAttribute(IMarker.MESSAGE, markerProxy.getMessage()); + int lineNumber = markerProxy.getLineNumber(); + if (lineNumber > 0) { + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + } + marker.setAttribute(IMarker.TRANSIENT, true); + markers.add(marker); + } catch (CoreException ex) { + throw new RuntimeException(ex); + } + } + markerProxy = markerProxy.getDetailMarker(); + } + } + boolean errorOccurred = positionToFirstErrorOrWarning(assemblerEditor, markers); + + parseCompilerSymbols(assemblerEditor, files, logParser); + + return errorOccurred; + } + + /** + * Positions to the first error or warning in any file for which markers + * have been created. + * + * @param assemblerEditor + * The assembler editor, not <code>null</code>. Used as basis for + * opening another editor when required. + * @param markers + * The modifiable list of marker, may be empty, not + * <code>null</code>. + * @return <code>true</code> if an error was found. + */ + private boolean positionToFirstErrorOrWarning(AssemblerEditor assemblerEditor, List<IMarker> markers) { + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (markers == null) { + throw new IllegalArgumentException("Parameter 'markers' must not be null."); + } + + Collections.sort(markers, new Comparator<IMarker>() { + + @Override + public int compare(IMarker o1, IMarker o2) { + int result = o1.getResource().getFullPath().toString() + .compareTo(o2.getResource().getFullPath().toString()); + if (result == 0) { + result = o1.getAttribute(IMarker.LINE_NUMBER, 0) - o2.getAttribute(IMarker.LINE_NUMBER, 0); + } + return result; + } + + }); + + String positioningMode = plugin.getPreferences().getEditorCompileCommandPositioningMode(); + boolean ignoreWarnings = positioningMode.equals(AssemblerEditorCompileCommandPositioningMode.FIRST_ERROR); + IMarker firstWarningMarker = null; + IMarker firstErrorMarker = null; + for (IMarker marker : markers) { + + switch (marker.getAttribute(IMarker.SEVERITY, 0)) { + case IMarker.SEVERITY_WARNING: + if (!ignoreWarnings) { + + if (firstWarningMarker == null) { + firstWarningMarker = marker; + } + + } + break; + case IMarker.SEVERITY_ERROR: + if (firstErrorMarker == null) { + firstErrorMarker = marker; + } + break; + } + + } + IMarker firstMarker = firstErrorMarker; + if (firstMarker == null) { + firstMarker = firstWarningMarker; + } + + if (firstMarker != null) { + MarkerUtility.gotoMarker(assemblerEditor, firstMarker); + } + return firstErrorMarker != null; + } + + private void parseCompilerSymbols(AssemblerEditor assemblerEditor, CompilerFiles files, + CompilerProcessLogParser logParser) { + + if (assemblerEditor == null) + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + if (files == null) + throw new IllegalArgumentException("Parameter 'files' must not be null."); + if (logParser == null) + throw new IllegalArgumentException("Parameter 'logParser' must not be null."); + + List<CompilerSymbol> compilerSymbols; + + compilerSymbols = new ArrayList<CompilerSymbol>(); + try { + logParser.addCompilerSymbols(compilerSymbols); + } catch (RuntimeException ex) { + String message = ex.getMessage(); + if (message == null) { + message = ex.getClass().getName(); + } + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, message); + AssemblerPlugin.getInstance().logError("Error in addCompilerSymbols()", null, ex); + + } catch (CoreException ex) { + String message = ex.getMessage(); + if (message == null) { + message = ex.getClass().getName(); + } + createMainSourceFileMessage(files, IMarker.SEVERITY_ERROR, message); + + } + + // Display symbols. + IViewReference[] references = assemblerEditor.getSite().getPage().getViewReferences(); + for (IViewReference reference : references) { + if (reference.getId().equals(CompilerSymbolsView.ID)) { + CompilerSymbolsView compilerSymbolsView = (CompilerSymbolsView) reference.getView(true); + if (compilerSymbolsView != null) { + compilerSymbolsView.setSymbols(files, compilerSymbols); + } + } + + } + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandDelegate.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandDelegate.java new file mode 100644 index 00000000..f9bd3968 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandDelegate.java @@ -0,0 +1,243 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.List; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IActionDelegate2; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.IWorkbenchWindowPulldownDelegate2; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.HardwareUtility; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.preferences.CompilerPreferences; +import com.wudsn.ide.asm.runner.RunnerDefinition; +import com.wudsn.ide.asm.runner.RunnerId; +import com.wudsn.ide.asm.runner.RunnerRegistry; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Delegate class to provide a dynamic drop-down menu for the toolbar based on + * the configured runners for the currently active compiler's hardware. + * + * @author Peter Dell + * + */ +public final class AssemblerEditorCompileCommandDelegate implements IActionDelegate2, IWorkbenchWindowPulldownDelegate2 { + + private IWorkbenchWindow window; + private Menu menu; + + /** + * Private action class. + * + * @author Peter Dell + */ + private final class CompileAndRunAction extends Action { + private String runnerId; + + public CompileAndRunAction(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + this.runnerId = runnerId; + } + + /** + * @see org.eclipse.jface.action.IAction#run() + */ + @Override + public void run() { + + } + + @Override + public void runWithEvent(Event event) { + compileAndRun(runnerId); + } + } + + /** + * Creation is public. + */ + public AssemblerEditorCompileCommandDelegate() { + } + + @Override + public void init(IWorkbenchWindow window) { + this.window = window; + } + + @Override + public Menu getMenu(Control parent) { + + AssemblerEditor assemblerEditor = getAssemblerEditor(); + if (assemblerEditor == null) { + return null; + } + + AssemblerPlugin assemblerPlugin = assemblerEditor.getPlugin(); + RunnerRegistry runnerRegistry = assemblerPlugin.getRunnerRegistry(); + Hardware hardware = assemblerEditor.getHardware(); + List<RunnerDefinition> runnerDefinitions = runnerRegistry.getDefinitions(hardware); + CompilerPreferences compilerPreferences = assemblerEditor.getCompilerPreferences(); + + Menu menu = new Menu(parent); + setMenu(menu); + + // Collect all runner definition for which the executable path is + // maintained correctly. + ImageDescriptor imageDescriptor = HardwareUtility.getImageDescriptor(hardware); + for (RunnerDefinition runnerDefinition : runnerDefinitions) { + String runnerId = runnerDefinition.getId(); + String runnerName = runnerDefinition.getName(); + // The system default application does not need an executable path. + if (!runnerId.equals(RunnerId.DEFAULT_APPLICATION)) { + if (StringUtility.isEmpty(compilerPreferences.getRunnerExecutablePath(runnerId))) { + continue; + } + } + + Action action = new CompileAndRunAction(runnerId); + action.setActionDefinitionId(AssemblerEditorCompileCommand.COMPILE_AND_RUN_WITH); + action.setImageDescriptor(imageDescriptor); + if (runnerId.equals(compilerPreferences.getRunnerId())) { + runnerName = runnerName + " " + Texts.ASSEMBLER_TOOLBAR_RUN_WITH_DEFAULT_LABEL; + } + action.setText(runnerName); + ActionContributionItem item = new ActionContributionItem(action); + item.fill(menu, -1); + } + + return menu; + } + + @Override + public Menu getMenu(Menu parent) { + return null; + } + + @Override + public void dispose() { + setMenu(null); + } + + @Override + public void init(IAction action) { + AssemblerEditor assemblerEditor = getAssemblerEditor(); + boolean enabled = assemblerEditor != null && assemblerEditor.getCurrentIFile() != null; + action.setEnabled(enabled); + + Hardware hardware; + if (assemblerEditor != null) { + hardware = assemblerEditor.getHardware(); + + } else { + hardware = Hardware.GENERIC; + } + ImageDescriptor imageDescriptor = HardwareUtility.getImageDescriptor(hardware); + action.setImageDescriptor(imageDescriptor); + } + + @Override + public void run(IAction action) { + // Not invoked + } + + @Override + public void runWithEvent(IAction action, Event event) { + try { + compileAndRun(null); + } catch (RuntimeException ex) { + AssemblerPlugin.getInstance().showError(window.getShell(), "Error in compileAndRun()", ex); + } + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + init(action); + // TODO: Check disabling of action based on current selection. + // System.out.println(action.getActionDefinitionId()); + // System.out.println(selection); + } + + /** + * Helper method to correctly retain and dispose menu instances. + * + * @param menu + * The menu to be retained or <code>null</code>. + */ + private void setMenu(Menu menu) { + if (this.menu != null) { + this.menu.dispose(); + } + this.menu = menu; + } + + /** + * Gets the currently active assembler editor. + * + * @return The currently active assembler editor or <code>null</code>. + */ + private AssemblerEditor getAssemblerEditor() { + if (window == null) { + return null; + } + + IWorkbenchPage workbenchPage = window.getActivePage(); + if (workbenchPage == null) { + return null; + } + IEditorPart editorPart = workbenchPage.getActiveEditor(); + if (!(editorPart instanceof AssemblerEditor)) { + return null; + } + return (AssemblerEditor) editorPart; + } + + /** + * Call the compiler and run command with the specified runner id. + * + * @param runnerId + * The runner id or <code>null</code> to use the default. + */ + final void compileAndRun(String runnerId) { + AssemblerEditor assemblerEditor = getAssemblerEditor(); + if (assemblerEditor == null) { + throw new IllegalStateException("Action is active but no assembler editor is active."); + } + AssemblerEditorCompileCommand.execute(assemblerEditor, AssemblerEditorFilesLogic + .createInstance(assemblerEditor).createCompilerFiles(), AssemblerEditorCompileCommand.COMPILE_AND_RUN, + runnerId); + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandHandler.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandHandler.java new file mode 100644 index 00000000..23eb8058 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandHandler.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; + +import com.wudsn.ide.asm.compiler.CompilerFiles; + +/** + * Event handler for the "Compile" command. + * + * @author Peter Dell + */ +public final class AssemblerEditorCompileCommandHandler extends + AssemblerEditorFilesCommandHandler { + + /** + * Creates a new instance. Called by the extension point + * "org.eclipse.ui.handlers". + */ + public AssemblerEditorCompileCommandHandler() { + + } + + @Override + protected void execute(ExecutionEvent event, + AssemblerEditor assemblerEditor, CompilerFiles files) + throws ExecutionException { + if (event == null) { + throw new IllegalArgumentException("Parameter 'event' must not be null."); + } + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + try { + AssemblerEditorCompileCommand.execute(assemblerEditor, files, event + .getCommand().getId(), null); + } catch (RuntimeException ex) { + throw new ExecutionException("Cannot execute event " + event, ex); + } + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandPositioningMode.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandPositioningMode.java new file mode 100644 index 00000000..2c1ee5fc --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompileCommandPositioningMode.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +/** + * Constant definition for positioning after compiling. + * + * @author Peter Dell + * + */ +public final class AssemblerEditorCompileCommandPositioningMode { + + /** + * Creation is private. + */ + private AssemblerEditorCompileCommandPositioningMode() { + + } + + public static final String FIRST_ERROR_OR_WARNING = "FIRST_ERROR_OR_WARNING"; + public static final String FIRST_ERROR = "FIRST_ERROR"; +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompilerHelpCommandHandler.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompilerHelpCommandHandler.java new file mode 100644 index 00000000..b0f122c7 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorCompilerHelpCommandHandler.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.io.File; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.program.Program; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; + +/** + * Event handler for the "Compiler Help" command. + * + * @author Peter Dell + */ +public final class AssemblerEditorCompilerHelpCommandHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + Shell shell; + shell = HandlerUtil.getActiveShell(event); + + IEditorPart editor; + editor = HandlerUtil.getActiveEditorChecked(event); + if (!(editor instanceof AssemblerEditor)) { + return null; + } + + AssemblerEditor assemblerEditor; + assemblerEditor = (AssemblerEditor) editor; + + CompilerDefinition compilerDefinition = assemblerEditor.getCompilerDefinition(); + AssemblerPreferences assemblerPreferences = AssemblerPlugin.getInstance().getPreferences(); + String compilerExecutablePath = assemblerPreferences.getCompilerExecutablePath(compilerDefinition.getId()); + + try { + File file = compilerDefinition.getHelpFile(compilerExecutablePath); + Program.launch(file.getPath()); + + } catch (CoreException ex) { + // ERROR: Display text from core exception. + MessageDialog.openInformation(shell, com.wudsn.ide.base.Texts.DIALOG_TITLE, ex.getMessage()); + } + + return null; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorFilesCommandHandler.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorFilesCommandHandler.java new file mode 100644 index 00000000..9b84c63a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorFilesCommandHandler.java @@ -0,0 +1,98 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.common.NotDefinedException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.compiler.CompilerFiles; + +/** + * Base class for commands which operate on the current file of an assembler + * editor, in case the file is within the work space. The base class ensures + * that the corresponding command is disabled, if there is no active assembler + * editor or the editor contains a file from outside of the work space. + * + * @author Peter Dell + */ +public abstract class AssemblerEditorFilesCommandHandler extends + AbstractHandler { + + public AssemblerEditorFilesCommandHandler() { + super(); + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IEditorPart editor; + editor = HandlerUtil.getActiveEditorChecked(event); + if (!(editor instanceof AssemblerEditor)) { + return null; + } + + AssemblerEditor assemblerEditor; + assemblerEditor = (AssemblerEditor) editor; + + CompilerFiles files; + files = AssemblerEditorFilesLogic.createInstance(assemblerEditor).createCompilerFiles(); + + if (files != null) { + execute(event, assemblerEditor, files); + } else { + try { + AssemblerPlugin + .getInstance() + .showError( + assemblerEditor.getSite().getShell(), + "Operation '" + + event.getCommand().getName() + + "' is not possible because the file in the editor is not located in the worksapce.", + new Exception()); + } catch (NotDefinedException ignore) { + // Ignore + } + } + return null; + } + + /** + * Perform the action on the current editor and file. + * + * @param event + * The event, not <code>null</code>. + * @param assemblerEditor + * The assembler editor, not <code>null</code> and with current + * files which are not <code>null</code>. + * @param files + * The current compiler files of the editor, not <code>null</code> + * . + * @throws ExecutionException + * if an exception occurred during execution. + */ + protected abstract void execute(ExecutionEvent event, + AssemblerEditor assemblerEditor, CompilerFiles files) + throws ExecutionException; + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorFilesLogic.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorFilesLogic.java new file mode 100644 index 00000000..818a8291 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorFilesLogic.java @@ -0,0 +1,372 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.io.File; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.AssemblerProperties; +import com.wudsn.ide.asm.AssemblerProperties.AssemblerProperty; +import com.wudsn.ide.asm.AssemblerProperties.InvalidAssemblerPropertyException; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.asm.compiler.CompilerOutputFolderMode; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.MarkerUtility; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Logic to handle the {@link CompilerFiles} of an {@link AssemblerEditor} + * + * @author Peter Dell + * + * @since 1.7.0 + */ +public final class AssemblerEditorFilesLogic { + + private AssemblerEditor assemblerEditor; + + /** + * Create a new instance of the logic. + * + * @param assemblerEditor + * The assembler editor, not <code>null</code>. + * + * @return The new instance, not <code>null</code>. + */ + static AssemblerEditorFilesLogic createInstance(AssemblerEditor assemblerEditor) { + if (assemblerEditor == null) { + throw new IllegalArgumentException("Parameter 'assemblerEditor' must not be null."); + } + + AssemblerEditorFilesLogic result = new AssemblerEditorFilesLogic(); + result.assemblerEditor = assemblerEditor; + return result; + } + + /** + * Gets the container with all files related to the current files. + * + * @return The container with all files related to the current files or + * <code>null</code>. + */ + CompilerFiles createCompilerFiles() { + IFile sourceIFile; + CompilerFiles result; + sourceIFile = assemblerEditor.getCurrentIFile(); + if (sourceIFile != null) { + + IDocument document = assemblerEditor.getDocumentProvider().getDocument(assemblerEditor.getEditorInput()); + AssemblerProperties sourceFileProperties = CompilerSourceParser.getDocumentProperties(document); + + IFile mainSourceIFile; + AssemblerProperties mainSourceFileProperties; + + mainSourceIFile = sourceIFile; + mainSourceFileProperties = sourceFileProperties; + + AssemblerProperty property = sourceFileProperties.get(AssemblerProperties.MAIN_SOURCE_FILE); + if (property != null) { + if (StringUtility.isSpecified(property.value)) { + IPath mainSourceFileIPath; + mainSourceFileIPath = sourceIFile.getFullPath().removeLastSegments(1).append(property.value); + mainSourceIFile = ResourcesPlugin.getWorkspace().getRoot().getFile(mainSourceFileIPath); + File mainSourceFile = new File(mainSourceIFile.getLocation().toOSString()); + + try { + String mainSource; + + mainSource = FileUtility.readString(mainSourceFile, FileUtility.MAX_SIZE_UNLIMITED); + document = new Document(mainSource); + mainSourceFileProperties = CompilerSourceParser.getDocumentProperties(document); + } catch (CoreException ex) { + AssemblerPlugin plugin = AssemblerPlugin.getInstance(); + + plugin.logError("Cannot read main source file '{0'}", + new Object[] { mainSourceFile.getPath() }, ex); + mainSourceFileProperties = new AssemblerProperties(); + } + + } + } + result = new CompilerFiles(mainSourceIFile, mainSourceFileProperties, sourceIFile, sourceFileProperties, + assemblerEditor.getCompilerPreferences()); + } else { + result = null; + } + return result; + } + + public boolean removeMarkers(CompilerFiles files) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + + // Remove markers from the current file. + try { + files.sourceFile.iFile.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); + } catch (CoreException ex) { + assemblerEditor.getPlugin().logError("Cannot remove markers", null, ex); + } + + // If the main source file is not there, the error shall be related to + // the include source file. In all other cases, the main source file + // exists and is the main message target. + if (!files.mainSourceFile.iFile.exists()) { + int lineNumber = files.sourceFile.assemblerProperties.get(AssemblerProperties.MAIN_SOURCE_FILE).lineNumber; + + // ERROR: Main source file '{0}' does not exist. + IMarker marker = MarkerUtility.createMarker(files.sourceFile.iFile, lineNumber, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E125, files.mainSourceFile.filePath); + MarkerUtility.gotoMarker(assemblerEditor, marker); + return false; + } + + // Remove markers from the other relevant files. + // TODO Use (only) include files parsed from main source file + try { + files.mainSourceFile.iFile.getParent().deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE); + } catch (CoreException ex) { + assemblerEditor.getPlugin().logError("Cannot remove markers", null, ex); + } + return true; + } + + /** + * Determine the hardware defined by the property + * {@link AssemblerProperties#HARDWARE}. + * + * @param iFile + * The iFIle to which error message will be associated, not + * <code>null</code>. + * @param properties + * The assembler properties, not <code>null</code>. + * + * @return The hardware or <code>null</code> if is the not defined in the + * properties. + * @throws InvalidAssemblerPropertyException + * If the hardware is specified but invalid. Error message will + * be assigned to the iFile in this case. + * + * @since 1.6.1 + */ + Hardware getHardware(IFile iFile, AssemblerProperties properties) throws InvalidAssemblerPropertyException { + if (iFile == null) { + throw new IllegalArgumentException("Parameter 'iFile' must not be null."); + } + Hardware hardware = null; + AssemblerProperty hardwareProperty = properties.get(AssemblerProperties.HARDWARE); + if (hardwareProperty != null) { + Map<String, Hardware> allowedValues = new TreeMap<String, Hardware>(); + StringBuilder allowedValuesBuilder = new StringBuilder(); + for (Hardware value : Hardware.values()) { + + if (value != Hardware.GENERIC) { + if (allowedValuesBuilder.length() > 0) { + allowedValuesBuilder.append(","); + } + allowedValues.put(value.name(), value); + allowedValuesBuilder.append(value.name()); + } + } + + String hardwarePropertyValue = hardwareProperty.value.toUpperCase(); + if (StringUtility.isEmpty(hardwarePropertyValue)) { + try { + iFile.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO); + } catch (CoreException ex) { + AssemblerPlugin.getInstance().logError("Cannot remove markers", null, ex); + } + // ERROR: Hardware not specified. Specify one of the + // following valid values '{0}'. + IMarker marker = MarkerUtility.createMarker(iFile, hardwareProperty.lineNumber, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E128, new String[] { allowedValuesBuilder.toString() }); + throw new InvalidAssemblerPropertyException(hardwareProperty, marker); + } + hardware = allowedValues.get(hardwarePropertyValue); + + if (hardware == null) { + try { + iFile.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO); + } catch (CoreException ex) { + assemblerEditor.getPlugin().logError("Cannot remove markers", null, ex); + } + // ERROR: Unknown hardware {0}. Specify one of the + // following valid values '{1}'. + IMarker marker = MarkerUtility.createMarker(iFile, hardwareProperty.lineNumber, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E124, new String[] { hardwarePropertyValue, allowedValuesBuilder.toString() }); + throw new InvalidAssemblerPropertyException(hardwareProperty, marker); + } + + } + return hardware; + } + + /** + * Determine the hardware of the main source file and makes sure that the + * include file uses the same hardware. + * + * @param files + * The compiler files, not <code>null</code>. + * @return The hardware to be used, or <code>null</code> if errors have + * occurred. + * + * @since 1.6.1 + */ + public Hardware getHardware(CompilerFiles files) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + Hardware mainSourceFileHardware; + Hardware sourceFileHardware; + try { + sourceFileHardware = getHardware(files.sourceFile.iFile, files.sourceFile.assemblerProperties); + } catch (InvalidAssemblerPropertyException ex) { + MarkerUtility.gotoMarker(assemblerEditor, ex.marker); + return null; + } + try { + mainSourceFileHardware = getHardware(files.mainSourceFile.iFile, files.mainSourceFile.assemblerProperties); + } catch (InvalidAssemblerPropertyException ex) { + MarkerUtility.gotoMarker(assemblerEditor, ex.marker); + return null; + } + Hardware defaultHardware = assemblerEditor.getCompilerDefinition().getDefaultHardware(); + int sourceFileLineNumber; + int mainSourceFileLineNumber; + if (sourceFileHardware == null) { + sourceFileHardware = defaultHardware; + sourceFileLineNumber = 0; + } else { + sourceFileLineNumber = files.sourceFile.assemblerProperties.get(AssemblerProperties.HARDWARE).lineNumber; + } + if (mainSourceFileHardware == null) { + mainSourceFileHardware = defaultHardware; + mainSourceFileLineNumber = 0; + } else { + mainSourceFileLineNumber = files.mainSourceFile.assemblerProperties.get(AssemblerProperties.HARDWARE).lineNumber; + } + + if (!sourceFileHardware.equals(mainSourceFileHardware)) { + // ERROR: Main source file specifies or defaults to hardware {0} + // while include file specifies or defaults to hardware '{1}'. + MarkerUtility.createMarker(files.mainSourceFile.iFile, mainSourceFileLineNumber, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E129, new String[] { mainSourceFileHardware.name(), sourceFileHardware.name() }); + MarkerUtility.createMarker(files.sourceFile.iFile, sourceFileLineNumber, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E129, new String[] { mainSourceFileHardware.name(), sourceFileHardware.name() }); + return null; + } + return mainSourceFileHardware; + } + + /** + * Validates the output file related settings of the compiler files. + * + * @param files + * The compiler files, not <code>null</code>. + * @return <code>true</code> if all settings are correct, <code>false</code> + * otherwise. + */ + public boolean validateOutputFile(CompilerFiles files) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + CompilerDefinition compilerDefinition = assemblerEditor.getCompilerDefinition(); + if (StringUtility.isEmpty(files.outputFileExtension)) { + // ERROR: Output file extension must be set in the preferences of + // compiler '{0}' or via the annotation '{1}'. + createMainSourceFileMessage(files, files.outputFileExtensionProperty, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E104, compilerDefinition.getName(), AssemblerProperties.OUTPUT_FILE_EXTENSION); + + return false; + } + if (!files.outputFileExtension.startsWith(".")) { + // ERROR: Output file extension {0} must start with ".". + createMainSourceFileMessage(files, files.outputFileExtensionProperty, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E139, files.outputFileExtension); + return false; + } + + if (StringUtility.isEmpty(files.outputFolderMode)) { + // ERROR: Output folder mode be set in the preferences of + // compiler '{0}' or via the annotation '{1}'. + createMainSourceFileMessage(files, files.outputFolderModeProperty, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E140, compilerDefinition.getName(), AssemblerProperties.OUTPUT_FOLDER_MODE); + + return false; + } + if (!CompilerOutputFolderMode.isDefined(files.outputFolderMode)) { + // ERROR: Unknown output folder mode {0}. Specify one of the + // following valid values '{1}'. + createMainSourceFileMessage(files, files.outputFolderModeProperty, IMarker.SEVERITY_ERROR, + Texts.MESSAGE_E141, files.outputFolderMode, CompilerOutputFolderMode.getAllowedValues()); + return false; + + } + + return true; + } + + /** + * Creates a message associated with the main source file of an + * {@link AssemblerEditorFilesLogic} instance. The message is is bound to + * the line number number of the property (if available). Also the editor is + * position to the marker. + * + * @param files + * The {@link AssemblerEditorFilesLogic} not <code>null</code>. + * @param property + * The assembler editor property to which the message belongs or + * <code>null</code>. + * @param severity + * The message severity, see {@link IMarker#SEVERITY} + * @param message + * The message, may contain parameter "{0}" to "{9}". May be + * empty, not <code>null</code>. + * @param parameters + * The format parameters for the message, may be empty, not + * <code>null</code>. + */ + private void createMainSourceFileMessage(CompilerFiles files, AssemblerProperty property, int severity, + String message, String... parameters) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + IMarker marker = MarkerUtility.createMarker(files.mainSourceFile.iFile, (property == null ? 0 + : property.lineNumber), severity, message, parameters); + MarkerUtility.gotoMarker(assemblerEditor, marker); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorOpenDeclarationCommandHandler.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorOpenDeclarationCommandHandler.java new file mode 100644 index 00000000..eaaf863d --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorOpenDeclarationCommandHandler.java @@ -0,0 +1,66 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * Handler to open the definition of an identifier or include file via "F3". + * + * @author Peter Dell + * + * @since 1.7.0 + */ +public final class AssemblerEditorOpenDeclarationCommandHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IEditorPart editor; + editor = HandlerUtil.getActiveEditorChecked(event); + if (!(editor instanceof AssemblerEditor)) { + return null; + } + + AssemblerEditor assemblerEditor; + assemblerEditor = (AssemblerEditor) editor; + ITextSelection textSelection = (ITextSelection) assemblerEditor.getSite().getSelectionProvider().getSelection(); + if (textSelection != null) { + IDocument document = assemblerEditor.getDocumentProvider().getDocument(assemblerEditor.getEditorInput()); + int offset = textSelection.getOffset(); + List<AssemblerHyperlink> hyperlinks = new ArrayList<AssemblerHyperlink>(); + AssemblerHyperlinkDetector.detectHyperlinks(assemblerEditor, document, offset, false, hyperlinks); + if (!hyperlinks.isEmpty()){ + AssemblerHyperlink hyperlink=hyperlinks.get(0); + hyperlink.open(); + } + } + return null; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorOpenFolderCommandHandler.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorOpenFolderCommandHandler.java new file mode 100644 index 00000000..3d874197 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorOpenFolderCommandHandler.java @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.swt.program.Program; + +import com.wudsn.ide.asm.compiler.CompilerFiles; + +/** + * Event handler for the "Open Source Folder" and "Open Output Folder" command. + * + * @author Peter Dell + */ +public final class AssemblerEditorOpenFolderCommandHandler extends + AssemblerEditorFilesCommandHandler { + + public static final String OPEN_SOURCE_FOLDER = "com.wudsn.ide.asm.editor.AssemblerEditorOpenSourceFolderCommand"; + public static final String OPEN_OUTPUT_FOLDER = "com.wudsn.ide.asm.editor.AssemblerEditorOpenOutputFolderCommand"; + + @Override + protected void execute(ExecutionEvent event, + AssemblerEditor assemblerEditor, CompilerFiles files) + throws ExecutionException { + + if (event == null) { + throw new IllegalArgumentException( + "Parameter 'event' must not be null."); + } + if (assemblerEditor == null) { + throw new IllegalArgumentException( + "Parameter 'assemblerEditor' must not be null."); + } + if (files == null) { + throw new IllegalArgumentException( + "Parameter 'files' must not be null."); + } + + String folderPath; + if (event.getCommand().getId().equals(OPEN_SOURCE_FOLDER)) { + folderPath = files.sourceFile.folderPath; + } else if (event.getCommand().getId().equals(OPEN_OUTPUT_FOLDER)) { + folderPath = files.outputFolderPath; + } else { + folderPath = null; + } + if (folderPath != null) { + Program.launch(folderPath); + } + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorToggleCommentAction.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorToggleCommentAction.java new file mode 100644 index 00000000..9f79d1f5 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerEditorToggleCommentAction.java @@ -0,0 +1,168 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.List; +import java.util.ResourceBundle; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.TextSelection; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.texteditor.TextEditorAction; + +import com.wudsn.ide.asm.AssemblerPlugin; + +/** + * Action to toggle a comment. + * + * @author Peter Dell + * @author Andy Reek + */ +final class AssemblerEditorToggleCommentAction extends TextEditorAction { + + /** + * The owning editor. + */ + private final AssemblerEditor editor; + + /** + * The editor's source viewer. + */ + private final SourceViewer sourceViewer; + + /** + * Creates a new instance. + * + * @param bundle + * The resource bundle, not <code>null</code>. + * @param prefix + * The resource bundle key prefix, not <code>null</code>. + * + * @param editor + * The assembler editor, not <code>null</code>. + * @param sourceViewer + * The assembler editor's source viewer. + * + */ + AssemblerEditorToggleCommentAction(ResourceBundle bundle, String prefix, + AssemblerEditor editor, SourceViewer sourceViewer) { + super(bundle, prefix, editor); + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + if (sourceViewer == null) { + throw new IllegalArgumentException( + "Parameter 'sourceViewer' must not be null."); + } + this.editor = editor; + this.sourceViewer = sourceViewer; + } + + @Override + public void update() { + setEnabled(canModifyEditor()); + } + + @Override + public void run() { + IDocument document = sourceViewer.getDocument(); + ISelection selection = sourceViewer.getSelection(); + TextSelection textSelection; + if (selection instanceof TextSelection) { + textSelection = (TextSelection) selection; + boolean isCommented = isCommented(document, textSelection); + if (isCommented) { + sourceViewer.doOperation(ITextOperationTarget.STRIP_PREFIX); + } else { + sourceViewer.doOperation(ITextOperationTarget.PREFIX); + } + } + } + + /** + * Checks, if the selection in the given document is commented fully using + * one of the single line comment delimiters. + * + * @param document + * The document, not <code>null</code>. + * @param selection + * The selection, not <code>null</code>. + * + * @return <code>true</code>, if commented. + */ + private boolean isCommented(IDocument document, TextSelection selection) { + if (document == null) { + throw new IllegalArgumentException( + "Parameter 'document' must not be null."); + } + if (selection == null) { + throw new IllegalArgumentException( + "Parameter 'selection' must not be null."); + } + try { + int startLine = selection.getStartLine(); + int endLine = selection.getEndLine(); + for (int line = startLine; line <= endLine; line++) { + int lineOffset = document.getLineOffset(line); + int lineLength = document.getLineLength(line); + String lineText = document.get(lineOffset, lineLength); + + if (!isCommented(lineText)) { + return false; + } + } + } catch (BadLocationException ex) { + AssemblerPlugin.getInstance().logError("Cannot find location", null, ex); + } + + return true; + } + + /** + * Checks, if the line is commented using one of the single line comment + * delimiters. + * + * @param lineText + * The line text, may be empty, not <code>null</code>. + * + * @return <code>true</code>, if commented. + */ + private boolean isCommented(String lineText) { + + if (lineText == null) { + throw new IllegalArgumentException( + "Parameter 'lineText' must not be null."); + } + + List<String> singleLineCommentDeliminters; + singleLineCommentDeliminters = editor.getCompilerDefinition() + .getSyntax().getSingleLineCommentDelimiters(); + for (String delimiter : singleLineCommentDeliminters) { + if (lineText.startsWith(delimiter)) { + return true; + } + } + return false; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerHyperlink.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerHyperlink.java new file mode 100644 index 00000000..f03bcb54 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerHyperlink.java @@ -0,0 +1,236 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.io.File; +import java.net.URI; + +import org.eclipse.core.filesystem.EFS; +import org.eclipse.core.filesystem.IFileStore; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.swt.program.Program; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.ide.IDE; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.StringUtility; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Hyperlink implementation for opening source or binary include files. + * + * @author Peter Dell + */ +final class AssemblerHyperlink implements IHyperlink { + + public static final String DEFAULT_EDITOR = "DEFAULT_EDITOR"; + public static final String SYSTEM_EDITOR = "SYSTEM_EDITOR"; + + private IRegion region; + private IWorkbenchPage workbenchPage; + private String absoluteFilePath; + private URI uri; + private String editorId; + private int lineNumber; + private String hyperlinkText; + + /** + * Creates a new hyper link. + * + * @param region + * The region of the text, not <code>null</code>. + * + * @param workbenchPage + * The active workbench page used for the new editor instance, + * not <code>null</code>. + * + * @param absoluteFilePath + * The absolute file path, not <code>null</code>. + * @param uri + * The uri to be opened, not <code>null</code>. + * + * @param editorId + * The id of the editor to be opened, not empty and not + * <code>null</code>. + * + * @param lineNumber + * The liner number to position to, in case the editor is an + * {@link AssemblerEditor}. The line numbers are starting at 1. + * The value 0 indicates that no positioning shall take place. + * + * @param hyperlinkText + * The localized text to display in case there is more than one + * hyperlink for the same location, may be empty, not + * <code>null</code>. + */ + AssemblerHyperlink(IRegion region, IWorkbenchPage workbenchPage, + String absoluteFilePath, URI uri, String editorId, int lineNumber, + String hyperlinkText) { + if (region == null) { + throw new IllegalArgumentException( + "Parameter 'region' must not be null."); + } + if (workbenchPage == null) { + throw new IllegalArgumentException( + "Parameter 'workbenchPage' must not be null."); + } + if (absoluteFilePath == null) { + throw new IllegalArgumentException( + "Parameter 'absoluteFilePath' must not be null."); + } + if (uri == null) { + throw new IllegalArgumentException( + "Parameter 'uri' must not be null."); + } + if (editorId == null) { + throw new IllegalArgumentException( + "Parameter 'editorId' must not be null."); + } + if (StringUtility.isEmpty(editorId)) { + throw new IllegalArgumentException( + "Parameter 'editorId' must not be empty."); + } + if (lineNumber < 0) { + throw new IllegalArgumentException( + "Parameter 'lineNumber' must not be negative. Specified value is " + + lineNumber + "."); + } + + if (hyperlinkText == null) { + throw new IllegalArgumentException( + "Parameter 'hyperlinkText' must not be null."); + } + this.region = region; + this.workbenchPage = workbenchPage; + this.absoluteFilePath = absoluteFilePath; + this.uri = uri; + this.editorId = editorId; + this.lineNumber = lineNumber; + this.hyperlinkText = hyperlinkText; + } + + /** + * {@inheritDoc} + */ + @Override + public IRegion getHyperlinkRegion() { + return region; + } + + /** + * {@inheritDoc} + */ + @Override + public String getTypeLabel() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public String getHyperlinkText() { + return hyperlinkText; + } + + /** + * {@inheritDoc} + */ + @Override + public void open() { + if (uri != null) { + File fileToOpen = new File(absoluteFilePath); + if (!fileToOpen.exists()) { + String message = TextUtility.format( + // ERROR: Target file '{0}' not exists. Do you + // want to create the file now? + Texts.ASSEMBLER_HYPERLINK_FILE_NOT_EXISTS, + absoluteFilePath); + boolean result = MessageDialog.openQuestion(workbenchPage + .getWorkbenchWindow().getShell(), + com.wudsn.ide.base.Texts.DIALOG_TITLE, message); + // Try to create the file, if OK was pressed. + if (result) { + try { + FileUtility.writeString(fileToOpen, ""); + } catch (CoreException ex) { + ErrorDialog.openError(workbenchPage + .getWorkbenchWindow().getShell(), + com.wudsn.ide.base.Texts.DIALOG_TITLE, null, ex + .getStatus()); + } + + } else { + return; + } + } + IEditorPart editorPart; + editorPart = null; + if (editorId.equals(DEFAULT_EDITOR)) { + + if (fileToOpen.exists() && fileToOpen.isFile()) { + IFileStore fileStore = EFS.getLocalFileSystem().getStore( + fileToOpen.toURI()); + + try { + editorPart = IDE.openEditorOnFileStore(workbenchPage, + fileStore); + } catch (PartInitException ex) { + + AssemblerPlugin.getInstance().logError( + "Cannot default editor editor for '{0}'.", + new Object[] { uri }, ex); + + } + } else { + // Do something if the file does not exist + } + + } else if (editorId.equals(SYSTEM_EDITOR)) { + Program.launch(absoluteFilePath); + } else { + + try { + + editorPart = IDE.openEditor(workbenchPage, uri, editorId, + true); + } catch (PartInitException ex) { + AssemblerPlugin.getInstance().logError( + "Cannot system editor editor for '{0}'.", + new Object[] { uri }, ex); + } + } + if (editorPart instanceof AssemblerEditor && lineNumber > 0) { + AssemblerEditor assemblerEditor = (AssemblerEditor) editorPart; + assemblerEditor.gotoLine(lineNumber); + } + + uri = null; + } + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerHyperlinkDetector.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerHyperlinkDetector.java new file mode 100644 index 00000000..bcacdcb3 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerHyperlinkDetector.java @@ -0,0 +1,332 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IWorkbenchPage; + +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceFile; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserFileReference; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserFileReferenceType; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObjectType; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.TextUtility; +import com.wudsn.ide.base.editor.hex.HexEditor; +import com.wudsn.ide.gfx.editor.GraphicsEditor; + +/** + * Hyperlink detector implementation for opening source or binary include files. + * + * @author Peter Dell + */ +public final class AssemblerHyperlinkDetector extends AbstractHyperlinkDetector { + + /** + * Hyperlink detector target as defined in the extension + * "org.eclipse.ui.workbench.texteditor.hyperlinkDetectorTargets". + */ + public static final String TARGET = "com.wudsn.ide.asm.editor.AssemblerHyperlinkDetectorEditorTarget"; + + /** + * Creates a new instance. Called by extension point + * "org.eclipse.ui.workbench.texteditor.hyperlinkDetectors". + * + */ + public AssemblerHyperlinkDetector() { + } + + /** + * Tries to detect hyperlinks for the given region in the given text viewer + * and returns them. + * <p> + * In most of the cases only one hyperlink should be returned. + * </p> + * + * @param textViewer + * the text viewer on which the hover popup should be shown + * @param region + * the text range in the text viewer which is used to detect the + * hyperlinks + * @param canShowMultipleHyperlinks + * tells whether the caller is able to show multiple links to the + * user. If <code>true</code> {@link IHyperlink#open()} should + * directly open the link and not show any additional UI to + * select from a list. If <code>false</code> this method should + * only return one hyperlink which upon {@link IHyperlink#open()} + * may allow to select from a list. + * @return The hyperlinks or <code>null</code> if no hyperlink was detected + */ + @Override + public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) { + + List<AssemblerHyperlink> hyperlinks; + hyperlinks = new ArrayList<AssemblerHyperlink>(2); + + if (region == null || textViewer == null) + return null; + + AssemblerEditor assemblerEditor; + + assemblerEditor = (AssemblerEditor) getAdapter(AssemblerEditor.class); + if (assemblerEditor == null) { + return null; + } + + IDocument document = textViewer.getDocument(); + + if (document == null) + return null; + + int offset = region.getOffset(); + + detectHyperlinks(assemblerEditor, document, offset, canShowMultipleHyperlinks, hyperlinks); + + if (!hyperlinks.isEmpty()) { + return hyperlinks.toArray(new IHyperlink[hyperlinks.size()]); + } + return null; + + } + + final static void detectHyperlinks(AssemblerEditor assemblerEditor, IDocument document, int offset, + boolean canShowMultipleHyperlinks, List<AssemblerHyperlink> hyperlinks) { + IRegion lineInfo; + int lineNumber; + String line; + try { + lineInfo = document.getLineInformationOfOffset(offset); + lineNumber = document.getLineOfOffset(offset) + 1; + line = document.get(lineInfo.getOffset(), lineInfo.getLength()); + } catch (BadLocationException ex) { + throw new RuntimeException("Region with offset " + offset + " no valid", ex); + } + + int offsetInLine = offset - lineInfo.getOffset(); + + if (offsetInLine >= line.length()) { + return; + } + detectInclude(assemblerEditor, lineInfo, lineNumber, line, offsetInLine, canShowMultipleHyperlinks, hyperlinks); + if (hyperlinks.isEmpty()) { + detectIdentifier(assemblerEditor, lineInfo, lineNumber, line, offsetInLine, canShowMultipleHyperlinks, + hyperlinks); + } + } + + private static void detectInclude(AssemblerEditor assemblerEditor, IRegion lineInfo, int lineNumber, String line, + int offsetInLine, boolean canShowMultipleHyperlinks, List<AssemblerHyperlink> hyperlinks) { + // Try to detect binary or source includes + CompilerSourceParser compilerSourceParser = assemblerEditor.createCompilerSourceParser(); + CompilerSourceParserFileReference fileReference; + fileReference = new CompilerSourceParserFileReference(); + compilerSourceParser.detectFileReference(line, fileReference); + if (fileReference.getType() != CompilerSourceParserFileReferenceType.NONE) { + + CompilerSyntax syntax = compilerSourceParser.getCompilerSyntax(); + int startQuoteOffset = 0; + String filePath = null; + Iterator<String> i = syntax.getStringDelimiters().iterator(); + while (i.hasNext() && filePath == null) { + String quote = i.next(); + startQuoteOffset = line.indexOf(quote, fileReference.getDirectiveEndOffset()); + if (startQuoteOffset == -1) { + continue; + } + int endQuoteOffset = line.indexOf(quote, startQuoteOffset + 1); + if (endQuoteOffset == -1) { + continue; + } + + if (startQuoteOffset < offsetInLine && offsetInLine < endQuoteOffset) { + filePath = line.substring(startQuoteOffset + 1, endQuoteOffset); + } else { + continue; + } + } + if (filePath == null) { + return; + } + + // Perform resolution of relative paths and compiler specific + // default extension. + File currentDirectory = assemblerEditor.getCurrentDirectory(); + String absoluteFilePath = compilerSourceParser.getIncludeAbsoluteFilePath(fileReference.getType(), + currentDirectory, filePath); + if (absoluteFilePath == null) { + return; + } + + URI uri; + uri = new File(absoluteFilePath).toURI(); + + IRegion linkRegion = new Region(lineInfo.getOffset() + startQuoteOffset + 1, filePath.length()); + + IEditorSite site = assemblerEditor.getEditorSite(); + if (site == null) { + return; + } + IWorkbenchPage workbenchPage = site.getWorkbenchWindow().getActivePage(); + + switch (fileReference.getType()) { + case CompilerSourceParserFileReferenceType.SOURCE: + hyperlinks + .add(new AssemblerHyperlink(linkRegion, workbenchPage, absoluteFilePath, uri, assemblerEditor + .getClass().getName(), 0, + Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_SOURCE_WITH_ASSEMBLER_EDITOR)); + break; + case CompilerSourceParserFileReferenceType.BINARY: + hyperlinks.add(new AssemblerHyperlink(linkRegion, workbenchPage, absoluteFilePath, uri, HexEditor.ID, + 0, Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_HEX_EDITOR)); + if (canShowMultipleHyperlinks) { + hyperlinks.add(new AssemblerHyperlink(linkRegion, workbenchPage, absoluteFilePath, uri, + GraphicsEditor.ID, 0, Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_GRAPHICS_EDITOR)); + hyperlinks.add(new AssemblerHyperlink(linkRegion, workbenchPage, absoluteFilePath, uri, + AssemblerHyperlink.DEFAULT_EDITOR, 0, + Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_DEFAULT_EDITOR)); + hyperlinks.add(new AssemblerHyperlink(linkRegion, workbenchPage, absoluteFilePath, uri, + AssemblerHyperlink.SYSTEM_EDITOR, 0, + Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_BINARY_WITH_SYSTEM_EDITOR)); + } + break; + default: + throw new IllegalStateException("Unknown include type " + fileReference.getType()); + + } + } + } + + private static void detectIdentifier(AssemblerEditor assemblerEditor, IRegion lineInfo, int lineNumber, + String line, int offsetInLine, boolean canShowMultipleHyperlinks, List<AssemblerHyperlink> hyperlinks) { + + CompilerSyntax compilerSyntax = assemblerEditor.createCompilerSourceParser().getCompilerSyntax(); + + int startIdentifierOffset = offsetInLine; + int endIdentifierOffset = offsetInLine; + + if (!compilerSyntax.isIdentifierCharacter(line.charAt(startIdentifierOffset))) { + return; + } + + while (startIdentifierOffset > 0 + && compilerSyntax.isIdentifierCharacter(line.charAt(startIdentifierOffset - 1))) { + startIdentifierOffset--; + } + while (endIdentifierOffset < line.length() + && compilerSyntax.isIdentifierCharacter(line.charAt(endIdentifierOffset))) { + // If we find an identifier separator character when moving right, + // we stop if we pass the cursor position. + if (endIdentifierOffset > offsetInLine + && compilerSyntax.isIdentifierSeparatorCharacter(line.charAt(endIdentifierOffset))) { + break; + } + endIdentifierOffset++; + } + String identifier = line.substring(startIdentifierOffset, endIdentifierOffset); + CompilerSourceFile compilerSourceFile = assemblerEditor.getCompilerSourceFile(); + if (compilerSourceFile == null) { + return; + } + + List<CompilerSourceParserTreeObject> foundElements; + foundElements = compilerSourceFile.getIdentifierDefinitionElements(identifier); + if (foundElements.isEmpty()) { + return; + } + + IEditorSite site = assemblerEditor.getEditorSite(); + if (site == null) { + return; + } + IWorkbenchPage workbenchPage = site.getWorkbenchWindow().getActivePage(); + + int size; + if (canShowMultipleHyperlinks) { + size = foundElements.size(); + } else { + size = 1; + } + for (int i = 0; i < size; i++) { + CompilerSourceParserTreeObject element = foundElements.get(i); + + String absoluteFilePath; + String fileName; + File documentFile = element.getCompilerSourceFile().getDocumentFile(); + if (documentFile != null) { + absoluteFilePath = documentFile.getPath(); + fileName = documentFile.getName(); + } else { + absoluteFilePath = ""; + fileName = ""; + } + int elementLineNumber; + try { + elementLineNumber = element.getCompilerSourceFile().getDocument() + .getLineOfOffset(element.getStartOffset()) + 1; + } catch (BadLocationException ex) { + continue; + } + + // Ignore if the found element is the start element only. + File currentFile = assemblerEditor.getCurrentFile(); + boolean inSameFile = foundElements.size() == 1 && currentFile != null + && currentFile.getPath().equals(absoluteFilePath); + + if (inSameFile && lineNumber == elementLineNumber) { + continue; + } + + URI uri; + uri = new File(absoluteFilePath).toURI(); + + IRegion linkRegion = new Region(lineInfo.getOffset() + startIdentifierOffset, identifier.length()); + + String hyperlinkText; + if (inSameFile) { + hyperlinkText = TextUtility.format(Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER, + CompilerSourceParserTreeObjectType.getText(element.getType()), element.getCompoundName(), + NumberUtility.getLongValueDecimalString(elementLineNumber)); + } else { + hyperlinkText = TextUtility.format(Texts.ASSEMBLER_HYPERLINK_DETECTOR_OPEN_IDENTIFIER_IN_INCLUDE, + CompilerSourceParserTreeObjectType.getText(element.getType()), element.getCompoundName(), + NumberUtility.getLongValueDecimalString(elementLineNumber), fileName); + } + hyperlinks.add(new AssemblerHyperlink(linkRegion, workbenchPage, absoluteFilePath, uri, assemblerEditor + .getClass().getName(), elementLineNumber, hyperlinkText)); + } + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerInstructionCompletionProposal.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerInstructionCompletionProposal.java new file mode 100644 index 00000000..5e5b08a9 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerInstructionCompletionProposal.java @@ -0,0 +1,167 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +/** + * The standard implementation of the <code>ICompletionProposal</code> + * interface. + * + * @author Peter Dell + */ +final class AssemblerInstructionCompletionProposal implements + ICompletionProposal, ICompletionProposalExtension6 { + + /** The string to be displayed in the completion proposal popup. */ + private String displayString; + private StyledString styledDisplayString; + + /** The replacement string. */ + private String replacementString; + /** The replacement offset. */ + private int replacementOffset; + /** The replacement length. */ + private int replacementLength; + /** The offset of the cursor after applying the replacement. */ + private int cursorOffset; + + /** The image to be displayed in the completion proposal popup. */ + private Image image; + /** The context information of this proposal. */ + private IContextInformation contextInformation; + + + /** + * Creates a new completion proposal. All fields are initialized based on + * the provided information. + * + * @param replacementString + * The actual string to be inserted into the document. + * @param replacementOffset + * The offset of the text to be replaced. + * @param replacementLength + * The length of the text to be replaced. + * @param cursorOffset + * The offset of the cursor after applying the replacement. + * @param image + * The image to display for this proposal. + * @param displayString + * The string to be displayed for the proposal. + * @param styledDisplayString + * The styles display string for the proposal. + * @param contextInformation + * The context information associated with this proposal. + */ + AssemblerInstructionCompletionProposal(String replacementString, + int replacementOffset, int replacementLength, int cursorOffset, Image image, + String displayString, StyledString styledDisplayString, + IContextInformation contextInformation) { + Assert.isNotNull(replacementString); + Assert.isNotNull(displayString); + Assert.isTrue(replacementOffset >= 0); + Assert.isTrue(replacementLength >= 0); + Assert.isTrue(cursorOffset >= 0); + + this.replacementString = replacementString; + this.replacementOffset = replacementOffset; + this.replacementLength = replacementLength; + this.cursorOffset=cursorOffset; + this.image = image; + this.displayString = displayString; + this.styledDisplayString = styledDisplayString; + this.contextInformation = contextInformation; + } + + /* + * @see ICompletionProposal#apply(IDocument) + */ + @Override + public void apply(IDocument document) { + try { + document.replace(replacementOffset, replacementLength, + replacementString); + } catch (BadLocationException ex) { + throw new RuntimeException("Replacement offset " + + replacementOffset + " no valid", ex); + + } + } + + /* + * @see ICompletionProposal#getSelection(IDocument) + */ + @Override + public Point getSelection(IDocument document) { + return new Point(cursorOffset, 0); + } + + /* + * @see ICompletionProposal#getContextInformation() + */ + @Override + public IContextInformation getContextInformation() { + return contextInformation; + } + + /* + * @see ICompletionProposal#getImage() + */ + @Override + public Image getImage() { + return image; + } + + /* + * @see ICompletionProposal#getDisplayString() + */ + @Override + public String getDisplayString() { + if (displayString != null) + return displayString; + return replacementString; + } + + /* + * @see ICompletionProposalExtension6#getStyledDisplayString() + */ + @Override + public StyledString getStyledDisplayString() { + + return styledDisplayString; + } + + /* + * @see ICompletionProposal#getAdditionalProposalInfo() + */ + @Override + public String getAdditionalProposalInfo() { + return ""; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerReconcilingStategy.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerReconcilingStategy.java new file mode 100644 index 00000000..0ddbbd5b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerReconcilingStategy.java @@ -0,0 +1,109 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.reconciler.DirtyRegion; +import org.eclipse.jface.text.reconciler.IReconcilingStrategy; +import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; + +/** + * The reconciling strategy for the AssemblerEditor. Builds the folding + * structure for folding and notifies the editor. + * + * @author Peter Dell + * @author Andy Reek + */ +final class AssemblerReconcilingStategy implements IReconcilingStrategy, + IReconcilingStrategyExtension { + + private final AssemblerEditor editor; + private IDocument document; + + /** + * Creates a new instance. Called by + * {@link AssemblerSourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer)} + * . + * + * * @param editor The underlying assembler editor, not <code>null</code>. + */ + AssemblerReconcilingStategy(AssemblerEditor editor) { + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + this.editor = editor; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDocument(IDocument document) { + this.document = document; + } + + /** + * {@inheritDoc} + */ + @Override + public final void setProgressMonitor(final IProgressMonitor monitor) { + // Not needed + } + + /** + * {@inheritDoc} + */ + @Override + public void initialReconcile() { + parse(); + } + + /** + * {@inheritDoc} + */ + @Override + public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) { + parse(); + } + + /** + * {@inheritDoc} + */ + @Override + public void reconcile(IRegion partition) { + parse(); + } + + /** + * Parses the current document for the content outline and the folding + * structure. + */ + private void parse() { + if (document == null) { + return; + } + + editor.updateContentOutlinePage(); + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerRuleBasedScanner.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerRuleBasedScanner.java new file mode 100644 index 00000000..4f0a986f --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerRuleBasedScanner.java @@ -0,0 +1,102 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.Set; + +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.rules.RuleBasedScanner; +import org.eclipse.jface.text.rules.Token; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; +import com.wudsn.ide.asm.preferences.TextAttributeConverter; + +/** + * Rule based scanner for comments and strings. + * + * @author Peter Dell + * @author Daniel Mitte + */ +final class AssemblerRuleBasedScanner extends RuleBasedScanner { + + /** Default Token for the text attributes * */ + private Token defaultToken; + + /** Key for preference store * */ + private String preferencesKey; + + /** + * Creates a new instance. Called by + * {@link AssemblerSourceViewerConfiguration}. + * + * @param preferencesKey + * The preference key to listen to for text attribute changes, + * not <code>null</code>.. + */ + AssemblerRuleBasedScanner(String preferencesKey) { + + if (preferencesKey == null) { + throw new IllegalArgumentException("Parameter 'preferencesKey' must not be null."); + } + + this.preferencesKey = preferencesKey; + + AssemblerPreferences preferences = AssemblerPlugin.getInstance().getPreferences(); + defaultToken = new Token(preferences.getEditorTextAttribute(preferencesKey)); + + super.setDefaultReturnToken(defaultToken); + } + + /** + * Dispose UI resources. + */ + final void dispose() { + TextAttributeConverter.dispose((TextAttribute) defaultToken.getData()); + } + + /** + * Update the token based on the preferences. Called by + * {@link AssemblerSourceViewerConfiguration}. + * + * @param preferences + * The preferences, not <code>null</code>. + * @param changedPropertyNames + * The set of changed property names, not <code>null</code>. + * + * @return <code>true</code> If the editor has to be refreshed. + */ + final boolean preferencesChanged(AssemblerPreferences preferences, Set<String> changedPropertyNames) { + if (preferences == null) { + throw new IllegalArgumentException("Parameter 'preferences' must not be null."); + } + if (changedPropertyNames == null) { + throw new IllegalArgumentException("Parameter 'changedPropertyNames' must not be null."); + } + boolean refresh = false; + if (changedPropertyNames.contains(preferencesKey)) { + TextAttributeConverter.dispose((TextAttribute) defaultToken.getData()); + defaultToken.setData(preferences.getEditorTextAttribute(preferencesKey)); + refresh = true; + } + return refresh; + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerSourceScanner.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerSourceScanner.java new file mode 100644 index 00000000..49dbe314 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerSourceScanner.java @@ -0,0 +1,516 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.text.rules.ICharacterScanner; +import org.eclipse.jface.text.rules.IRule; +import org.eclipse.jface.text.rules.IToken; +import org.eclipse.jface.text.rules.RuleBasedScanner; +import org.eclipse.jface.text.rules.Token; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParser; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObjectType; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.asm.compiler.syntax.Directive; +import com.wudsn.ide.asm.compiler.syntax.Instruction; +import com.wudsn.ide.asm.compiler.syntax.InstructionSet; +import com.wudsn.ide.asm.compiler.syntax.InstructionType; +import com.wudsn.ide.asm.compiler.syntax.Opcode; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesConstants; +import com.wudsn.ide.asm.preferences.TextAttributeConverter; + +/** + * A rule based scanner for instructions. + * + * @author Peter Dell + * @author Andy Reek + */ +final class AssemblerSourceScanner extends RuleBasedScanner { + + private final class AssemblerWordRule implements IRule { + public final class State { + public CompilerSourceParser compilerSourceParser; + + public CompilerSyntax compilerSyntax; + public InstructionSet instructionSet; + + public boolean instructionsCaseSensitive; + public Map<String, IToken> instructionWordTokens; + + public boolean identifiersCaseSensitive; + public Map<String, IToken> identifierWordTokens; + + public State() { + instructionWordTokens = new TreeMap<String, IToken>(); + identifierWordTokens = new TreeMap<String, IToken>(); + } + + public State createDeepCopy() { + State result = new State(); + result.compilerSourceParser = compilerSourceParser; + result.compilerSyntax = compilerSyntax; + result.instructionSet = instructionSet; + result.instructionsCaseSensitive = instructionsCaseSensitive; + result.instructionWordTokens = instructionWordTokens; + result.identifiersCaseSensitive = identifiersCaseSensitive; + result.identifierWordTokens = identifierWordTokens; + return result; + } + } + + // State of the AssemblerWordRule instance. + State state; + + /** Buffer used for pattern detection in evaluate() only. */ + private StringBuilder instructionBuffer = new StringBuilder(); + private StringBuilder identifierBuffer = new StringBuilder(); + private StringBuilder numberBuffer = new StringBuilder(); + + public AssemblerWordRule() { + + } + + public void setCompilerSourceParser(CompilerSourceParser compilerSourceParser) { + if (compilerSourceParser == null) { + throw new IllegalArgumentException("Parameter 'compilerSourceParser' must not be null."); + } + + State state = new State(); + state.compilerSourceParser = compilerSourceParser; + state.instructionSet = compilerSourceParser.getInstructionSet(); + state.compilerSyntax = compilerSourceParser.getCompilerSyntax(); + + state.instructionsCaseSensitive = state.compilerSyntax.areInstructionsCaseSensitive(); + state.identifiersCaseSensitive = state.compilerSyntax.areIdentifiersCaseSensitive(); + + synchronized (this) { + this.state = state; + } + } + + public void setInstructions() { + synchronized (this) { + state.instructionWordTokens.clear(); + + state.instructionsCaseSensitive = state.compilerSyntax.areInstructionsCaseSensitive(); + state.instructionSet = state.compilerSourceParser.getInstructionSet(); + + List<Instruction> instructions = state.instructionSet.getInstructions(); + + // Map with lower case name and corresponding token. + for (Instruction instruction : instructions) { + IToken token; + if (instruction instanceof Directive) { + token = directiveToken; + } else if (instruction instanceof Opcode) { + + Opcode opcode = (Opcode) instruction; + + switch (opcode.getType()) { + + case InstructionType.LEGAL_OPCODE: + token = legalOpcodeToken; + break; + case InstructionType.ILLEGAL_OPCODE: + + token = illegalOpcodeToken; + break; + case InstructionType.PSEUDO_OPCODE: + token = pseudoOpcodeToken; + break; + default: + throw new IllegalStateException("Unknown opcode type " + opcode.getType() + "."); + + } + } else { + throw new IllegalStateException("Unknown instruction type " + instruction.toString() + "."); + + } + // Case insensitive word rules expect upper case words. + if (state.instructionsCaseSensitive) { + state.instructionWordTokens.put(instruction.getName(), token); + } else { + state.instructionWordTokens.put(instruction.getUpperCaseName(), token); + } + } + } + + } + + /** + * Update the list of identifiers to be highlighted + * + * @param identifiers + * The list of identifiers, not <code>null</code>. + */ + public void setIdentifiers(List<CompilerSourceParserTreeObject> identifiers) { + if (identifiers == null) { + throw new IllegalArgumentException("Parameter 'identifiers' must not be null."); + } + synchronized (this) { + + for (CompilerSourceParserTreeObject element : identifiers) { + IToken token; + switch (element.getType()) { + case CompilerSourceParserTreeObjectType.EQUATE_DEFINITION: + token = equateIdentifierToken; + break; + case CompilerSourceParserTreeObjectType.LABEL_DEFINITION: + token = labelIdentifierToken; + break; + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + token = enumDefinitionSectionIdentifierToken; + break; + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + token = structureDefinitionSectionIdentifierToken; + break; + case CompilerSourceParserTreeObjectType.LOCAL_SECTION: + token = localSectionIdentifierToken; + break; + case CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION: + token = macroDefinitionSectionIdentifierToken; + break; + case CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION: + token = procedureDefinitionSectionIdentifierToken; + break; + + default: + throw new RuntimeException("Unexpected identifier element type " + element.getType() + " - " + + element.getTreePath() + "."); + } + if (element.getDescription().startsWith("@style=(")) { + String value = element.getDescription().substring(8); + int index = value.indexOf(")"); + if (index > 0) { + value = value.substring(0, index); + TextAttribute textAttribute = TextAttributeConverter.fromString(value); + token = new Token(textAttribute); + } + } + if (state.identifiersCaseSensitive) { + state.identifierWordTokens.put(element.getName(), token); + } else { + state.identifierWordTokens.put(element.getName().toUpperCase(), token); + + } + } + } + System.out.println(""+this+":"+state.identifierWordTokens.size()); + } + + /* + * @see IRule#evaluate(ICharacterScanner) + */ + @Override + public IToken evaluate(ICharacterScanner scanner) { + + // Create a local copy to prevent synchronization issues. + State localState; + synchronized (this) { + localState = state.createDeepCopy(); + } + + int c = scanner.read(); + boolean instructionStartCharacter = localState.instructionSet.isInstructionStartCharacter((char) c); + boolean identifierStartCharacter = localState.compilerSyntax.isIdentifierStartCharacter((char) c); + boolean numberStartCharacter = localState.compilerSyntax.isNumberStartCharacter((char) c); + if (c != ICharacterScanner.EOF + && (instructionStartCharacter || identifierStartCharacter || numberStartCharacter)) { + + instructionBuffer.setLength(0); + identifierBuffer.setLength(0); + numberBuffer.setLength(0); + int charactersRead = 0; + boolean instructionPartCharacter = instructionStartCharacter; + boolean identifierPartCharacter = identifierStartCharacter; + boolean numberPartCharacter = numberStartCharacter; + do { + charactersRead++; + if (instructionPartCharacter) { + instructionBuffer.append((char) c); + } + if (identifierPartCharacter) { + identifierBuffer.append((char) c); + } + if (numberPartCharacter) { + numberBuffer.append((char) c); + } + c = scanner.read(); + instructionPartCharacter = instructionPartCharacter + && localState.instructionSet.isInstructionPartCharacter((char) c); + identifierPartCharacter = identifierPartCharacter + && (localState.compilerSyntax.isIdentifierPartCharacter((char) c)); + numberPartCharacter = numberPartCharacter + && localState.compilerSyntax.isNumberPartCharacter((char) c); + + } while (c != ICharacterScanner.EOF + && (instructionPartCharacter || identifierPartCharacter || numberPartCharacter)); + scanner.unread(); + + String instructionString = instructionBuffer.toString(); + String identifierString = identifierBuffer.toString(); + String numberString = numberBuffer.toString(); + // System.out.println(instructionString + "/" + identifierString + // + "/" + numberString); + + // If case-insensitive, convert to upper case before + // accessing the map + if (!localState.instructionsCaseSensitive) { + instructionString = instructionString.toUpperCase(); + } + + IToken instructionToken = localState.instructionWordTokens.get(instructionString); + + // Anything found at all? + if (instructionToken == null && identifierString.length() == 0 && numberString.length() == 0) { + unreadBuffer(scanner, charactersRead); + return Token.UNDEFINED; + } + + // If the identifier string is longer, use it. + IToken token; + if (instructionToken == null || identifierString.length() > instructionString.length()) { + if (identifierString.length() >= numberString.length()) { + if (identifierString.length() == 0) { + return Token.UNDEFINED; + } + if (!localState.identifiersCaseSensitive) { + identifierString = identifierString.toUpperCase(); + } + token = localState.identifierWordTokens.get(identifierString); + + // Consume the next separator if there is one. + if (localState.compilerSyntax.isIdentifierSeparatorCharacter((char) c)) { + charactersRead--; + } + unreadBuffer(scanner, charactersRead - identifierString.length()); + if (token == null) { + token = Token.UNDEFINED; + } + return token; + } + unreadBuffer(scanner, charactersRead - numberString.length()); + return numberToken; + + } + if (instructionString.length() >= numberString.length()) { + unreadBuffer(scanner, charactersRead - instructionString.length()); + return instructionToken; + } else if (numberString.length() > 0) { + return numberToken; + } + + return Token.UNDEFINED; + } + + scanner.unread(); + return Token.UNDEFINED; + + } + + /** + * Returns the specified number of characters to the scanner. + * + * @param scanner + * The scanner to be used, not <code>null</code>. + * @param count + * The count. If the count is 0 or negative, no characters + * will be returned. + */ + private void unreadBuffer(ICharacterScanner scanner, int count) { + for (int i = 0; i < count; i++) { + scanner.unread(); + } + } + } + + private AssemblerEditor editor; + private Map<String, Token> tokens; + + // Numbers + IToken numberToken; + + // Instructions. + IToken directiveToken; + IToken legalOpcodeToken; + IToken illegalOpcodeToken; + IToken pseudoOpcodeToken; + + // Identifiers. + IToken equateIdentifierToken; + IToken labelIdentifierToken; + IToken enumDefinitionSectionIdentifierToken; + IToken structureDefinitionSectionIdentifierToken; + IToken localSectionIdentifierToken; + IToken macroDefinitionSectionIdentifierToken; + IToken procedureDefinitionSectionIdentifierToken; + + // Word rule + private AssemblerWordRule wordRule; + + /** + * Creates a new instance. Called by the + * {@link AssemblerSourceViewerConfiguration}. + * + * @param editor + * The underlying AssemblerEditor for the code scanner, not + * <code>null</code>. + */ + AssemblerSourceScanner(AssemblerEditor editor) { + if (editor == null) { + throw new IllegalArgumentException("Parameter 'editor' must not be null."); + } + this.editor = editor; + this.tokens = new TreeMap<String, Token>(); + + createTokens(); + createRules(); + } + + /** + * The token are stable over the life time of the editor, whereas the rules + * may change if the preferences are changed. + */ + private void createTokens() { + + // Numbers + numberToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_NUMBER); + + // Instructions + directiveToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_DIRECTVE); + legalOpcodeToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_LEGAL); + illegalOpcodeToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_ILLEGAL); + pseudoOpcodeToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_PSEUDO); + + // Identifiers + equateIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE); + labelIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LABEL); + enumDefinitionSectionIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION); + structureDefinitionSectionIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION); + localSectionIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION); + macroDefinitionSectionIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION); + procedureDefinitionSectionIdentifierToken = createToken(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION); + + } + + /** + * Creates a token bound to a text attribute name. + * + * @param textAttributeName + * The text attribute name, not empty and not <code>null</code>. + * @return The new token, not <code>null</code>. + */ + private IToken createToken(String textAttributeName) { + if (textAttributeName == null) { + throw new IllegalArgumentException("Parameter 'textAttributeName' must not be null."); + } + AssemblerPreferences preferences; + Token token; + preferences = editor.getPlugin().getPreferences(); + token = new Token(preferences.getEditorTextAttribute(textAttributeName)); + tokens.put(textAttributeName, token); + return token; + } + + /** + * Creates and sets the rules based on the the compiler syntax. + */ + private void createRules() { + + // Instructions, identifiers and numbers. + wordRule = new AssemblerWordRule(); + List<IRule> rules = new ArrayList<IRule>(4); + rules.add(wordRule); + setRules(rules.toArray(new IRule[rules.size()])); + + CompilerSourceParser compilerSourceParser = editor.createCompilerSourceParser(); + wordRule.setCompilerSourceParser(compilerSourceParser); + wordRule.setInstructions(); + } + + /** + * Update the list of identifiers to be highlighted + * + * @param identifiers + * The list of identifiers, not <code>null</code>. + */ + final void setIdentifiers(List<CompilerSourceParserTreeObject> identifiers) { + if (identifiers == null) { + throw new IllegalArgumentException("Parameter 'identifiers' must not be null."); + } + wordRule.setIdentifiers(identifiers); + } + + /** + * Dispose UI resources. + */ + final void dispose() { + for (Token token : tokens.values()) { + TextAttributeConverter.dispose((TextAttribute) token.getData()); + } + } + + /** + * Update the token based on the preferences. Called by + * {@link AssemblerSourceViewerConfiguration}. + * + * @param preferences + * The preferences, not <code>null</code>. + * @param changedPropertyNames + * The set of changed property names, not <code>null</code>. + * + * @return <code>true</code> If the editor has to be refreshed. + */ + final boolean preferencesChanged(AssemblerPreferences preferences, Set<String> changedPropertyNames) { + if (preferences == null) { + throw new IllegalArgumentException("Parameter 'preferences' must not be null."); + } + if (changedPropertyNames == null) { + throw new IllegalArgumentException("Parameter 'changedPropertyNames' must not be null."); + } + boolean refresh = false; + for (String propertyName : changedPropertyNames) { + Token token = tokens.get(propertyName); + if (token != null) { + TextAttributeConverter.dispose((TextAttribute) token.getData()); + token.setData(preferences.getEditorTextAttribute(propertyName)); + refresh = true; + + } else if (AssemblerPreferencesConstants.isCompilerCPUName(propertyName)) { + CompilerSourceParser compilerSourceParser = editor.createCompilerSourceParser(); + wordRule.setCompilerSourceParser(compilerSourceParser); + wordRule.setInstructions(); + refresh = true; + } + } + return refresh; + + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerSourceViewerConfiguration.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerSourceViewerConfiguration.java new file mode 100644 index 00000000..261c3bb4 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/AssemblerSourceViewerConfiguration.java @@ -0,0 +1,225 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.jface.text.contentassist.IContentAssistant; +import org.eclipse.jface.text.presentation.IPresentationReconciler; +import org.eclipse.jface.text.presentation.PresentationReconciler; +import org.eclipse.jface.text.reconciler.IReconciler; +import org.eclipse.jface.text.reconciler.IReconcilingStrategy; +import org.eclipse.jface.text.reconciler.MonoReconciler; +import org.eclipse.jface.text.rules.DefaultDamagerRepairer; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.editors.text.TextSourceViewerConfiguration; + +import com.wudsn.ide.asm.compiler.parser.CompilerSourcePartitionScanner; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesChangeListener; +import com.wudsn.ide.asm.preferences.AssemblerPreferencesConstants; + +/** + * Source configuration for the assembler editor. Provides syntax highlighting. + * + * Created and disposed by {@link AssemblerEditor}. + * + * @author Peter Dell + * @author Andy Reek + */ +final class AssemblerSourceViewerConfiguration extends TextSourceViewerConfiguration implements + AssemblerPreferencesChangeListener { + + /** + * The underlying assembler editor. + */ + private AssemblerEditor editor; + + /** + * Rule scanner for single line comments. + */ + private AssemblerRuleBasedScanner commentSingleScanner; + + /** + * Rule scanner for multiple lines comments. + */ + private AssemblerRuleBasedScanner commentMultipleScanner; + + /** + * Rule scanner for strings. + */ + private AssemblerRuleBasedScanner stringScanner; + + /** + * Rule scanner for assembler instructions. + */ + private AssemblerSourceScanner instructionScanner; + + /** + * Creates a new instance. Called by + * {@link AssemblerEditor#initializeEditor()}. + * + * @param editor + * The underlying assembler editor, not <code>null</code>. + * + * @param preferenceStore + * The preferences store, not <code>null</code>. + */ + AssemblerSourceViewerConfiguration(AssemblerEditor editor, IPreferenceStore preferenceStore) { + super(preferenceStore); + if (editor == null) { + throw new IllegalArgumentException("Parameter 'editor' must not be null."); + } + this.editor = editor; + editor.getPlugin().addPreferencesChangeListener(this); + + } + + /** + * Called by + * {@link AssemblerEditor#updateIdentifiers(com.wudsn.ide.asm.compiler.parser.CompilerSourceFile)} + * + * @return The instruction scanner, not <code>null</code>. + */ + final AssemblerSourceScanner getAssemblerInstructionScanner() { + if (instructionScanner == null) { + throw new IllegalStateException("Instruction scanner not yet created"); + } + return instructionScanner; + } + + /** + * Remove all rule scanners from property change listener. Used by + * {@link AssemblerEditor#dispose()}. + */ + final void dispose() { + if (commentSingleScanner != null) { + commentSingleScanner.dispose(); + commentSingleScanner = null; + } + if (commentMultipleScanner != null) { + commentMultipleScanner.dispose(); + commentMultipleScanner = null; + } + if (stringScanner != null) { + stringScanner.dispose(); + stringScanner = null; + } + if (instructionScanner != null) { + instructionScanner.dispose(); + instructionScanner = null; + } + editor.getPlugin().removePreferencesChangeListener(this); + } + + @Override + public void preferencesChanged(AssemblerPreferences preferences, Set<String> changedPropertyNames) { + if (preferences == null) { + throw new IllegalArgumentException("Parameter 'preferences' must not be null."); + } + if (changedPropertyNames == null) { + throw new IllegalArgumentException("Parameter 'changedPropertyNames' must not be null."); + } + boolean refresh = false; + refresh |= commentSingleScanner.preferencesChanged(preferences, changedPropertyNames); + refresh |= commentMultipleScanner.preferencesChanged(preferences, changedPropertyNames); + refresh |= stringScanner.preferencesChanged(preferences, changedPropertyNames); + refresh |= instructionScanner.preferencesChanged(preferences, changedPropertyNames); + if (refresh) { + editor.refreshSourceViewer(); + } + } + + @Override + public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) { + ContentAssistant assistant = new ContentAssistant(); + assistant + .setContentAssistProcessor(new AssemblerContentAssistProcessor(editor), IDocument.DEFAULT_CONTENT_TYPE); + assistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE); + assistant.enableAutoActivation(true); + assistant.enableAutoInsert(true); + assistant.setAutoActivationDelay(500); + assistant.enableColoredLabels(true); + + return assistant; + } + + @Override + public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { + if (sourceViewer == null) { + throw new IllegalArgumentException("Parameter 'sourceViewer' must not be null."); + } + PresentationReconciler reconciler = new PresentationReconciler(); + DefaultDamagerRepairer dr; + + commentSingleScanner = new AssemblerRuleBasedScanner( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_COMMENT); + dr = new DefaultDamagerRepairer(commentSingleScanner); + reconciler.setDamager(dr, CompilerSourcePartitionScanner.PARTITION_COMMENT_SINGLE); + reconciler.setRepairer(dr, CompilerSourcePartitionScanner.PARTITION_COMMENT_SINGLE); + + commentMultipleScanner = new AssemblerRuleBasedScanner( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_COMMENT); + dr = new DefaultDamagerRepairer(commentMultipleScanner); + reconciler.setDamager(dr, CompilerSourcePartitionScanner.PARTITION_COMMENT_MULTIPLE); + reconciler.setRepairer(dr, CompilerSourcePartitionScanner.PARTITION_COMMENT_MULTIPLE); + + stringScanner = new AssemblerRuleBasedScanner(AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_STRING); + dr = new DefaultDamagerRepairer(stringScanner); + reconciler.setDamager(dr, CompilerSourcePartitionScanner.PARTITION_STRING); + reconciler.setRepairer(dr, CompilerSourcePartitionScanner.PARTITION_STRING); + + instructionScanner = new AssemblerSourceScanner(editor); + dr = new DefaultDamagerRepairer(instructionScanner); + reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE); + reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE); + + return reconciler; + } + + @Override + public IReconciler getReconciler(ISourceViewer sourceViewer) { + if (sourceViewer == null) { + throw new IllegalArgumentException("Parameter 'sourceViewer' must not be null."); + } + IReconcilingStrategy reconcilingStrategy = new AssemblerReconcilingStategy(editor); + + MonoReconciler reconciler = new MonoReconciler(reconcilingStrategy, false); + reconciler.setProgressMonitor(new NullProgressMonitor()); + reconciler.setDelay(500); + + return reconciler; + } + + @SuppressWarnings("unchecked") + @Override + protected Map<String, IAdaptable> getHyperlinkDetectorTargets(ISourceViewer sourceViewer) { + Map<String, IAdaptable> targets = super.getHyperlinkDetectorTargets(sourceViewer); + targets.put(AssemblerHyperlinkDetector.TARGET, editor); + return targets; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSourceParserTreeObjectLabelProvider.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSourceParserTreeObjectLabelProvider.java new file mode 100644 index 00000000..71ebeac2 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSourceParserTreeObjectLabelProvider.java @@ -0,0 +1,201 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.graphics.Image; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObject; +import com.wudsn.ide.asm.compiler.parser.CompilerSourceParserTreeObjectType; + +/** + * LabelProvider for the {@link CompilerSourceParserTreeObject} instances in the + * outline page and the content assist popup. + * + * @author Peter Dell + * @author Daniel Mitte + */ +final class CompilerSourceParserTreeObjectLabelProvider extends DelegatingStyledCellLabelProvider { + + /** Default tree image */ + private final Image defaultImage; + + /** Outline definition section image */ + private final Image definitionSectionImage; + + /** Outline implementation section image */ + private final Image implementationSectionImage; + + /** Outline equate definition image */ + private final Image equateDefintionImage; + + /** Outline label definition image */ + private final Image labelDefinitionImage; + + /** Outline enum definition section image */ + private final Image enumDefinitionSectionImage; + + /** Outline structure definition section image */ + private final Image structureDefinitionSectionImage; + + /** Outline local section image */ + private final Image localSectionImage; + + /** Outline macro definition section image */ + private final Image macroDefinitionSectionImage; + + /** Outline pages section image */ + private final Image pagesSectionImage; + + /** Outline procedure definition section image */ + private final Image procedureDefinitionSectionImage; + + /** Outline repeat section image */ + private final Image repeatSectionImage; + + /** Outline source include image */ + private final Image sourceIncludeImage; + + /** Outline binary include image */ + private final Image binaryIncludeImage; + + /** Outline binary include image */ + private final Image binaryOutputImage; + + private static class StyledLabelProvider extends LabelProvider implements IStyledLabelProvider { + + /** + * Creation is local. + */ + StyledLabelProvider() { + + } + + @Override + public StyledString getStyledText(Object element) { + if (element == null) { + throw new IllegalArgumentException("Parameter 'element' must not be null."); + } + if (element instanceof CompilerSourceParserTreeObject) { + CompilerSourceParserTreeObject elem = (CompilerSourceParserTreeObject) element; + return elem.getStyledString(); + } + + return new StyledString(getText(element)); + } + } + + /** + * Creates a new instance. + * + * Called by + * {@link AssemblerContentOutlinePage#createControl(org.eclipse.swt.widgets.Composite)} + * . + */ + CompilerSourceParserTreeObjectLabelProvider() { + super(new StyledLabelProvider()); + AssemblerPlugin plugin; + plugin = AssemblerPlugin.getInstance(); + defaultImage = plugin.getImage("outline-default-16x16.gif"); + definitionSectionImage = plugin.getImage("outline-definition-section-16x16.gif"); + implementationSectionImage = plugin.getImage("outline-implementation-section-16x16.gif"); + + equateDefintionImage = plugin.getImage("outline-equate-definition-16x16.gif"); + labelDefinitionImage = plugin.getImage("outline-label-definition-16x16.gif"); + + enumDefinitionSectionImage = plugin.getImage("outline-enum-definition-section-16x16.gif"); + structureDefinitionSectionImage = plugin.getImage("outline-structure-definition-section-16x16.gif"); + localSectionImage = plugin.getImage("outline-local-section-16x16.gif"); + macroDefinitionSectionImage = plugin.getImage("outline-macro-definition-section-16x16.gif"); + pagesSectionImage = plugin.getImage("outline-pages-section-16x16.gif"); + procedureDefinitionSectionImage = plugin.getImage("outline-procedure-definition-section-16x16.gif"); + repeatSectionImage = plugin.getImage("outline-repeat-section-16x16.gif"); + + sourceIncludeImage = plugin.getImage("outline-source-include-16x16.gif"); + binaryIncludeImage = plugin.getImage("outline-binary-include-16x16.gif"); + binaryOutputImage = plugin.getImage("outline-binary-output-16x16.gif"); + } + + @Override + public Image getImage(Object element) { + Image result; + if (element instanceof CompilerSourceParserTreeObject) { + CompilerSourceParserTreeObject elem = (CompilerSourceParserTreeObject) element; + int type = elem.getType(); + + switch (type) { + case CompilerSourceParserTreeObjectType.DEFINITION_SECTION: + result = definitionSectionImage; + break; + case CompilerSourceParserTreeObjectType.IMPLEMENTATION_SECTION: + result = implementationSectionImage; + break; + + case CompilerSourceParserTreeObjectType.EQUATE_DEFINITION: + result = equateDefintionImage; + break; + case CompilerSourceParserTreeObjectType.LABEL_DEFINITION: + result = labelDefinitionImage; + break; + + case CompilerSourceParserTreeObjectType.ENUM_DEFINITION_SECTION: + result = enumDefinitionSectionImage; + break; + case CompilerSourceParserTreeObjectType.STRUCTURE_DEFINITION_SECTION: + result = structureDefinitionSectionImage; + break; + case CompilerSourceParserTreeObjectType.LOCAL_SECTION: + result = localSectionImage; + break; + case CompilerSourceParserTreeObjectType.MACRO_DEFINITION_SECTION: + result = macroDefinitionSectionImage; + break; + case CompilerSourceParserTreeObjectType.PAGES_SECTION: + result = pagesSectionImage; + break; + case CompilerSourceParserTreeObjectType.PROCEDURE_DEFINITION_SECTION: + result = procedureDefinitionSectionImage; + break; + case CompilerSourceParserTreeObjectType.REPEAT_SECTION: + result = repeatSectionImage; + break; + + case CompilerSourceParserTreeObjectType.SOURCE_INCLUDE: + result = sourceIncludeImage; + break; + case CompilerSourceParserTreeObjectType.BINARY_INCLUDE: + result = binaryIncludeImage; + break; + case CompilerSourceParserTreeObjectType.BINARY_OUTPUT: + result = binaryOutputImage; + break; + default: + result = defaultImage; + } + } else { + result = null; + } + + return result; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSymbolLabelProvider.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSymbolLabelProvider.java new file mode 100644 index 00000000..86652278 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSymbolLabelProvider.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import org.eclipse.swt.graphics.Image; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.compiler.CompilerSymbol; +import com.wudsn.ide.asm.compiler.CompilerSymbolType; + + +/** + * LabelProvider for the {@link CompilerSymbol} instances in the compiler + * symbols view. + * + * @author Peter Dell + */ +final class CompilerSymbolLabelProvider { + + /** Default image */ + private final Image defaultImage; + + /** Outline equate definition image */ + private final Image equateDefintionImage; + + /** Outline label definition image */ + private final Image labelDefinitionImage; + + /** Outline enum definition section image */ + private final Image enumDefinitionSectionImage; + + /** Outline structure definition section image */ + private final Image structureDefinitionSectionImage; + + /** Outline local section image */ + private final Image localSectionImage; + + /** Outline procedure definition section image */ + private final Image procedureDefinitionSectionImage; + + + CompilerSymbolLabelProvider() { + AssemblerPlugin plugin; + plugin = AssemblerPlugin.getInstance(); + defaultImage = plugin.getImage("outline-default-16x16.gif"); + + equateDefintionImage = plugin.getImage("outline-equate-definition-16x16.gif"); + labelDefinitionImage = plugin.getImage("outline-label-definition-16x16.gif"); + + enumDefinitionSectionImage = plugin.getImage("outline-enum-definition-section-16x16.gif"); + structureDefinitionSectionImage = plugin.getImage("outline-structure-definition-section-16x16.gif"); + localSectionImage = plugin.getImage("outline-local-section-16x16.gif"); + procedureDefinitionSectionImage = plugin.getImage("outline-procedure-definition-section-16x16.gif"); + + } + + public Image getImage(Object element) { + Image result; + if (element instanceof CompilerSymbol) { + CompilerSymbol elem = (CompilerSymbol) element; + int type = elem.getType(); + + switch (type) { + case CompilerSymbolType.EQUATE_DEFINITION: + result = equateDefintionImage; + break; + case com.wudsn.ide.asm.compiler.CompilerSymbolType.LABEL_DEFINITION: + result = labelDefinitionImage; + break; + + case CompilerSymbolType.ENUM_DEFINITION_SECTION: + result = enumDefinitionSectionImage; + break; + case CompilerSymbolType.STRUCTURE_DEFINITION_SECTION: + result = structureDefinitionSectionImage; + break; + case CompilerSymbolType.LOCAL_SECTION: + result = localSectionImage; + break; + + case CompilerSymbolType.PROCEDURE_DEFINITION_SECTION: + result = procedureDefinitionSectionImage; + break; + + default: + result = defaultImage; + } + } else { + result = null; + } + + return result; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSymbolsView.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSymbolsView.java new file mode 100644 index 00000000..95b4b27b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/editor/CompilerSymbolsView.java @@ -0,0 +1,296 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.editor; + +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.window.ToolTip; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.part.ViewPart; + +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.asm.compiler.CompilerSymbol; +import com.wudsn.ide.asm.compiler.CompilerSymbolType; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.TextUtility; + +// TODO Add column sorting +public final class CompilerSymbolsView extends ViewPart { + + public static final String ID = "com.wudsn.ide.asm.editor.CompilerSymbolsView"; + + // Model + private CompilerFiles compilerFiles; + private List<CompilerSymbol> compilerSymbols; + private Date updateTimestamp; + + // View + private Text filterTextField; + private Label sourceFileNameText; + private Label symbolsCountText; + private TableViewer viewer; + + public CompilerSymbolsView() { + compilerFiles = null; + compilerSymbols = Collections.emptyList(); + updateTimestamp = null; + } + + @Override + public void createPartControl(Composite parent) { + GridLayout layout = new GridLayout(4, false); + parent.setLayout(layout); + filterTextField = new Text(parent, SWT.BORDER | SWT.SEARCH); + filterTextField.setToolTipText(Texts.COMPILER_SYMBOLS_VIEW_FILTER_TOOLTIP); + filterTextField.setMessage(filterTextField.getToolTipText()); + filterTextField.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL)); + + // Filter as you type... + filterTextField.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent e) { + dataToUI(); + } + }); + Label sourceFileNameLabel = new Label(parent, SWT.NONE); + sourceFileNameLabel.setText(Texts.COMPILER_SYMBOLS_VIEW_SOURCE_LABEL); + sourceFileNameText = new Label(parent, SWT.NONE); + symbolsCountText = new Label(parent, SWT.NONE); + createViewer(parent); + } + + private void createViewer(Composite parent) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER); + createColumns(parent, viewer); + final Table table = viewer.getTable(); + table.setHeaderVisible(true); + table.setLinesVisible(true); + + viewer.setContentProvider(new ArrayContentProvider()); + dataToUI(); + + // Make the selection available to other views + getSite().setSelectionProvider(viewer); + + // Set the sorter for the table + + // Define layout for the viewer + GridData gridData = new GridData(); + gridData.verticalAlignment = GridData.FILL; + gridData.horizontalSpan = 4; + gridData.grabExcessHorizontalSpace = true; + gridData.grabExcessVerticalSpace = true; + gridData.horizontalAlignment = GridData.FILL; + viewer.getControl().setLayoutData(gridData); + + // Activate the tooltip support for the viewer + ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE); + } + + // create the columns for the table + private void createColumns(final Composite parent, final TableViewer viewer) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + if (viewer == null) { + throw new IllegalArgumentException("Parameter 'viewer' must not be null."); + } + int index = 0; + + // Column: Type + final CompilerSymbolLabelProvider labelProvider = new CompilerSymbolLabelProvider(); + TableViewerColumn col = createTableViewerColumn(Texts.COMPILER_SYMBOLS_VIEW_TYPE_COLUMN_LABEL, 40, index++); + col.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + return null; + } + + @Override + public String getToolTipText(Object element) { + CompilerSymbol compilerSymbol = (CompilerSymbol) element; + return CompilerSymbolType.getText(compilerSymbol.getType()); + } + + @Override + public Image getImage(Object element) { + return labelProvider.getImage(element); + } + }); + + // Column: Bank + col = createTableViewerColumn(Texts.COMPILER_SYMBOLS_VIEW_BANK_COLUMN_LABEL, 40, index++); + col.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + CompilerSymbol compilerSymbol = (CompilerSymbol) element; + return compilerSymbol.getBankString(); + } + }); + + // Column: Name + col = createTableViewerColumn(Texts.COMPILER_SYMBOLS_VIEW_NAME_COLUMN_LABEL, 200, index++); + col.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + CompilerSymbol compilerSymbol = (CompilerSymbol) element; + return compilerSymbol.getName(); + } + }); + + // Column: Hex Value + col = createTableViewerColumn(Texts.COMPILER_SYMBOLS_VIEW_HEX_VALUE_COLUMN_LABEL, 100, index++); + col.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + CompilerSymbol compilerSymbol = (CompilerSymbol) element; + return compilerSymbol.getValueAsHexString(); + } + }); + + // Column: Decimal Value + col = createTableViewerColumn(Texts.COMPILER_SYMBOLS_VIEW_DECIMAL_VALUE_COLUMN_LABEL, 100, index++); + col.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + CompilerSymbol compilerSymbol = (CompilerSymbol) element; + return compilerSymbol.getValueAsDecimalString(); + } + }); + + // Column: String Value + col = createTableViewerColumn(Texts.COMPILER_SYMBOLS_VIEW_STRING_VALUE_COLUMN_LABEL, 100, index++); + col.setLabelProvider(new ColumnLabelProvider() { + @Override + public String getText(Object element) { + CompilerSymbol compilerSymbol = (CompilerSymbol) element; + return compilerSymbol.getValueAsString(); + } + }); + } + + private TableViewerColumn createTableViewerColumn(String title, int bound, int colNumber) { + if (title == null) { + throw new IllegalArgumentException("Parameter 'title' must not be null."); + } + final TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE); + final TableColumn column = viewerColumn.getColumn(); + column.setText(title); + column.setWidth(bound); + column.setResizable(true); + column.setMoveable(true); + return viewerColumn; + } + + @Override + public void setFocus() { + filterTextField.setFocus(); + } + + public void setSymbols(CompilerFiles compilerFiles, List<CompilerSymbol> compilerSymbols) { + if (compilerFiles == null) { + throw new IllegalArgumentException("Parameter 'compilerFiles' must not be null."); + } + if (compilerSymbols == null) { + throw new IllegalArgumentException("Parameter 'compilerSymbols' must not be null."); + } + this.compilerFiles = compilerFiles; + this.compilerSymbols = new ArrayList<CompilerSymbol>(compilerSymbols); + this.updateTimestamp = new Date(); + if (viewer != null) { + dataToUI(); + } + } + + void dataToUI() { + String text = Texts.COMPILER_SYMBOLS_VIEW_SOURCE_NONE; + if (compilerFiles != null) { + text = compilerFiles.mainSourceFile.fileName; + text += " " + DateFormat.getTimeInstance().format(updateTimestamp); + } + sourceFileNameText.setText(text); + String filterTextSequence = filterTextField.getText().toUpperCase(); + + // A leading ! reverses the filter + boolean matchTarget= true; + if (filterTextSequence.startsWith("!")){ + filterTextSequence=filterTextSequence.substring(1); + matchTarget=false; + } + + String[] filterTexts = filterTextSequence.split("[ ]+"); + List<CompilerSymbol> filteredCompilerSymbols = compilerSymbols; + if (filterTexts.length > 0) { + filteredCompilerSymbols = new ArrayList<CompilerSymbol>(); + for (CompilerSymbol compilerSymbol : compilerSymbols) { + boolean match = true; + for (int i = 0; i < filterTexts.length && match; i++) { + String filterText = filterTexts[i]; + boolean symbolMatch = (compilerSymbol.getNameUpperCase().contains(filterText) + || compilerSymbol.getValueAsHexStringUpperCase().contains(filterText) || compilerSymbol + .getValueAsDecimalString().contains(filterText)) + || compilerSymbol.getValueAsStringUpperCase().contains(filterText); + match &= symbolMatch; + } + + if (match == matchTarget) { + filteredCompilerSymbols.add(compilerSymbol); + } + } + } + int totalCount = compilerSymbols.size(); + String totalCountText = NumberUtility.getLongValueDecimalString(totalCount); + + int filteredCount = filteredCompilerSymbols.size(); + if (totalCount > 0 && filteredCount < totalCount) { + String filteredCountText = NumberUtility.getLongValueDecimalString(filteredCount); + text = TextUtility.format(Texts.COMPILER_SYMBOLS_VIEW_SOURCE_FILTERED_COUNT, filteredCountText, + totalCountText); + } else { + text = TextUtility.format(Texts.COMPILER_SYMBOLS_VIEW_SOURCE_TOTAL_COUNT, totalCountText); + } + symbolsCountText.setText(text); + // Calling setInput will call getElements in the contentProvider + viewer.setInput(filteredCompilerSymbols); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerHelpContentProducer.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerHelpContentProducer.java new file mode 100644 index 00000000..aeed488f --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerHelpContentProducer.java @@ -0,0 +1,684 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.help; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.help.IHelpContentProducer; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.HardwareUtility; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.Compiler; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntax; +import com.wudsn.ide.asm.compiler.syntax.CompilerSyntaxUtility; +import com.wudsn.ide.asm.compiler.syntax.Directive; +import com.wudsn.ide.asm.compiler.syntax.Instruction; +import com.wudsn.ide.asm.compiler.syntax.InstructionSet; +import com.wudsn.ide.asm.compiler.syntax.InstructionType; +import com.wudsn.ide.asm.compiler.syntax.Opcode; +import com.wudsn.ide.asm.compiler.syntax.Opcode.OpcodeAddressingMode; +import com.wudsn.ide.asm.runner.RunnerDefinition; +import com.wudsn.ide.asm.runner.RunnerId; +import com.wudsn.ide.asm.runner.RunnerRegistry; +import com.wudsn.ide.base.common.EnumUtility; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.StringUtility; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Dynamic help content provider. Uses static pages and the meta data from the + * compiler definitions to build a comprehensive help. + * + * @author Peter Dell + * + * @since 1.6.1 + * + * TODO Complete opcode entries in Compiler.xml, also for extended and + * illegal opcodes + */ +public final class AssemblerHelpContentProducer implements IHelpContentProducer { + + // In order to get the navigation breadcrumbs automatically, + // the files have to have this suffix (see BreadcrumbsFilter). + public static final String EXTENSION = ".html"; + public static final String SECTION_EXTENSION = ".section.html"; + private static final String SECTION_PREFIX = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">" + + "<html><head>" + + "<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">" + + "<link rel=\"stylesheet\" href=\"../../../content/org.eclipse.platform/book.css\" type=\"text/css\"></link>" + + "<style type=\"text/css\">table,tbody,td,th {border-style:solid;border-width:1px;border-collapse:collapse; " + + "transition-property: background-color;transition-duration: 0.25s;transition-timing-function: linear;transition-delay: 0ms;} " + + "th {background-color:#0074cc;color:#fff } </style>" + "</head><body>"; + private static final String SECTION_SUFFIX = "</body></html>"; + + public static final String SCHEMA_COMPILER = "compiler/"; + public static final String SCHEMA_HARDWARE = "hardware/"; + + public static final String SECTION_GENERAL = "general"; + public static final String SECTION_INSTRUCTIONS = "instructions"; + public static final String SECTION_MANUAL = "manual"; + public static final String SECTION_MANUAL_FILE = "file"; + + public static final String SCHEMA_CPU = "cpu/"; + + private static final String ICONS_PATH = "/help/topic/com.wudsn.ide.asm/icons/"; + + @Override + public InputStream getInputStream(String pluginID, String href, Locale locale) { + if (AssemblerPlugin.ID.equals(pluginID)) { + int index = href.indexOf("?"); + if (index >= 0) { + href = href.substring(0, index); + } + if (href.startsWith(SCHEMA_COMPILER)) { + return getCompilerInputStream(href); + } else if (href.startsWith(SCHEMA_HARDWARE)) { + return getHardwareInputStream(href); + } else if (href.startsWith(SCHEMA_CPU)) { + return getCPUInputStream(href); + } else if (href.endsWith(".html")) { // Web site documents + return getHTMLInputStream(href); + } + } + + return null; + } + + /** + * Gets the HTML input stream for HTML files. + * + * @param href + * The href ending with ".html" + * @return The input stream, not <code>null</code>. + */ + private InputStream getHTMLInputStream(String href) { + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + AssemblerPlugin plugin = AssemblerPlugin.getInstance(); + IPath path = new Path(href); + InputStream result; + try { + result = FileLocator.openStream(plugin.getBundle(), path, false); + // HTML files that do not have an own <html> and <body> tag must be + // wrapped. + if (href.endsWith(SECTION_EXTENSION)) { + result = new HTMLWrapperInputStream(SECTION_PREFIX, SECTION_SUFFIX, result); + } + } catch (IOException ex) { + plugin.logError("Cannot open stream for {0}", new Object[] { href }, ex); + result = null; + } + + return result; + } + + /** + * Create a string build for formatted help content. + * + * @return The HTML write, not <code>null</code>. + */ + private HTMLWriter createHeader() { + HTMLWriter writer = new HTMLWriter(); + + writer.writeText(HTMLConstants.PREFIX); + return writer; + } + + /** + * Convert a string builder to an input stream. + * + * @param writer + * The HTML writer, not <code>null</code>. + * @return The input stream, not <code>null</code>. + */ + private InputStream getInputStream(HTMLWriter writer) { + if (writer == null) { + throw new IllegalArgumentException("Parameter 'writer' must not be null."); + } + writer.writeText(HTMLConstants.SUFFIX); + + try { + return new ByteArrayInputStream(writer.toHTML().getBytes(HTMLConstants.UTF8)); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + } + + private String getPath(String prefix, String href) { + if (prefix == null) { + throw new IllegalArgumentException("Parameter 'prefix' must not be null."); + } + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + String path = href.substring(prefix.length()); + int index = path.indexOf("?"); + if (index >= 0) { + path = path.substring(0, index); + } + + index = path.lastIndexOf('.'); + if (index > -1) { + path = path.substring(0, index); + return path; + } + return null; + } + + private Map<String, List<String>> getQueryParameters(String href) { + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + Map<String, List<String>> params = new HashMap<String, List<String>>(); + + try { + URI uri; + try { + uri = new URI(href); + } catch (URISyntaxException ex1) { + throw new RuntimeException("Cannot parse '" + href + "'", ex1); + } + String query = uri.getQuery(); + if (query != null) { + for (String param : query.split("&")) { + String pair[] = param.split("="); + String key; + + key = URLDecoder.decode(pair[0], "UTF-8"); + + String value = ""; + if (pair.length > 1) { + value = URLDecoder.decode(pair[1], "UTF-8"); + } + List<String> values = params.get(key); + if (values == null) { + values = new ArrayList<String>(); + params.put(key, values); + } + values.add(value); + } + } + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + return params; + } + + private InputStream getCompilerInputStream(String href) { + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + + String path = getPath(SCHEMA_COMPILER, href); + if (path == null) { + return null; + } + int index = path.indexOf("/"); + if (index <= 0) { + return null; + } + String compilerId = path.substring(0, index); + String section = path.substring(index + 1); + AssemblerPlugin assemblerPlugin = AssemblerPlugin.getInstance(); + CompilerRegistry compilerRegistry = assemblerPlugin.getCompilerRegistry(); + + // Find non-empty compiler executable path. + Compiler compiler = compilerRegistry.getCompiler(compilerId); + + if (section.startsWith(SECTION_GENERAL)) { + return getInputStream(getCompilerGeneralSection(compiler)); + } else if (section.startsWith(SECTION_MANUAL)) { + + String compilerExecutablePath = assemblerPlugin.getPreferences().getCompilerExecutablePath(compilerId); + if (StringUtility.isEmpty(compilerExecutablePath)) { + // ERROR: Help for the '{0}' compiler cannot be + // displayed because the path to the compiler executable + // is not set in the preferences. + String message = TextUtility.format(Texts.MESSAGE_E130, new String[] { compiler.getDefinition() + .getName() }); + HTMLWriter writer = createHeader(); + writer.writeText(message); + return getInputStream(writer); + } + + try { + File file = compiler.getDefinition().getHelpFile(compilerExecutablePath); + + // Handle file requests within the help directory. + List<String> fileNames = getQueryParameters(href).get(SECTION_MANUAL_FILE); + String fileName; + if (fileNames != null && fileNames.size() == 1) { + fileName = fileNames.get(0); + } else { + fileName = ""; + } + + if (StringUtility.isSpecified(fileName)) { + file = new File(file, fileName); + } + InputStream inputStream = new FileInputStream(file); + + index = file.getName().indexOf("."); + if (index == -1 || file.getName().substring(index).equalsIgnoreCase(".txt")) { + inputStream = new HTMLWrapperInputStream("<html><body><pre>", "</pre></body></html>", inputStream); + } + return inputStream; + + } catch (CoreException ex) { + HTMLWriter writer = createHeader(); + writer.writeText(ex.getMessage()); + return getInputStream(writer); + + } catch (FileNotFoundException ex) { + HTMLWriter writer = createHeader(); + writer.writeText(ex.getMessage()); + return getInputStream(writer); + + } + + } else if (section.equals(SECTION_INSTRUCTIONS)) { + return getInputStream(getCompilerInstructionsSection(compiler.getDefinition())); + + } + return null; + } + + private HTMLWriter getCompilerGeneralSection(Compiler compiler) { + CompilerDefinition compilerDefinition = compiler.getDefinition(); + + HTMLWriter writer = createHeader(); + + writer.beginTable(true); + + writer.writeTableRow(Texts.TOC_ASSEMBLER_NAME_LABEL, compilerDefinition.getName()); + + writer.writeTableRow(Texts.TOC_ASSEMBLER_HOME_PAGE_LABEL, + HTMLWriter.getLink(compilerDefinition.getHomePageURL(), compilerDefinition.getHomePageURL())); + + Hardware hardware = compilerDefinition.getDefaultHardware(); + writer.writeTableRow( + Texts.TOC_ASSEMBLER_DEFAULT_HARDWARE_LABEL, + HTMLWriter.getImage(ICONS_PATH + HardwareUtility.getImagePath(hardware), hardware.name(), + hardware.name())); + + List<CPU> cpus = compilerDefinition.getSupportedCPUs(); + writer.beginTableRow(); + writer.writeTableHeader(Texts.TOC_ASSEMBLER_SUPPORTED_CPUS_LABEL); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < cpus.size(); i++) { + builder.append(EnumUtility.getText(cpus.get(i))); + if (i < cpus.size() - 1) { + builder.append(", "); + } + } + writer.writeTableCell(builder.toString()); + writer.end(); + + writer.beginTableRow(); + writer.writeTableHeader(Texts.TOC_ASSEMBLER_DEFAULT_PARAMETERS_LABEL); + + writer.writeTableCell(compilerDefinition.getDefaultParameters()); + writer.end(); + + CompilerSyntax compilerSyntax = compilerDefinition.getSyntax(); + + writer.writeTableRow(Texts.TOC_ASSEMBLER_SYNTAX_IDENTIFIERS_CASE_SENSITIVE, compilerSyntax + .areIdentifiersCaseSensitive() ? Texts.TOC_ASSEMBLER_SYNTAX_YES : Texts.TOC_ASSEMBLER_SYNTAX_NO); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_IDENTIFIER_START_CHARACTERS, + getCompilerGeneralCharactersWrapped(compilerSyntax.getIdentifierStartCharacters())); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_IDENTIFIER_PART_CHARACTERS, + getCompilerGeneralCharactersWrapped(compilerSyntax.getIdentifierPartCharacters())); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_IDENTIFIER_SEPARATOR_CHARACTER, + compilerSyntax.getIdentifierSeparatorCharacter()); + + writer.writeTableRow(Texts.TOC_ASSEMBLER_SYNTAX_INSTRUCTIONS_CASE_SENSITIVE, compilerSyntax + .areInstructionsCaseSensitive() ? Texts.TOC_ASSEMBLER_SYNTAX_YES : Texts.TOC_ASSEMBLER_SYNTAX_NO); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_BLOCK_DEFINITION_CHARACTERS, + compilerSyntax.getBlockDefinitionCharacters()); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_COMPLETION_PROPOSAL_AUTO_ACTIVATION_CHARACTERS, new String( + compilerSyntax.getCompletionProposalAutoActivationCharacters())); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_LABEL_DEFINITION_SUFFIX_CHARACTER, + compilerSyntax.getLabelDefinitionSuffixCharacter()); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_MACRO_USAGE_PREFIX_CHARACTER, + compilerSyntax.getMacroUsagePrefixCharacter()); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_SOURCE_INCLUDE_DEFAULT_EXTENSION, + compilerSyntax.getSourceIncludeDefaultExtension()); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_MULTIPLE_LINES_COMMENT_DELIMITERS, + HTMLWriter.getString(compilerSyntax.getMultipleLinesCommentDelimiters())); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_SINGLE_LINE_COMMENT_DELIMITERS, + HTMLWriter.getString(compilerSyntax.getSingleLineCommentDelimiters())); + + writer.writeTableRowCode(Texts.TOC_ASSEMBLER_SYNTAX_STRING_DELIMITERS, + HTMLWriter.getString(compilerSyntax.getStringDelimiters())); + + writer.end(); + + return writer; + + } + + private String getCompilerGeneralCharactersWrapped(String text) { + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + int index = text.indexOf('Z'); + if (index > -1 && index < text.length() - 1) { + text = text.substring(0, index + 1) + " " + text.substring(index + 1); + } + return text; + } + + /** + * Creates an HTML string describing all instructions of the compiler. + * + * @param compilerDefinition + * The compiler definition, not <code>null</code>. + * @return The HTML string builder describing all instructions of the + * compiler, may be empty, not <code>null</code>. + */ + private HTMLWriter getCompilerInstructionsSection(CompilerDefinition compilerDefinition) { + if (compilerDefinition == null) { + throw new IllegalArgumentException("Parameter 'compilerDefinition' must not be null."); + } + HTMLWriter result = createHeader(); + + CompilerSyntax syntax = compilerDefinition.getSyntax(); + List<Instruction> directives = new ArrayList<Instruction>(); + List<Instruction> legalOpcodes = new ArrayList<Instruction>(); + List<Instruction> illegalOpcodes = new ArrayList<Instruction>(); + List<Instruction> pseudoOpcodes = new ArrayList<Instruction>(); + List<Instruction> w65816Opcodes = new ArrayList<Instruction>(); + + List<CPU> cpus = compilerDefinition.getSupportedCPUs(); + for (CPU cpu : cpus) { + for (Instruction instruction : syntax.getInstructionSet(cpu).getInstructions()) { + + if (instruction instanceof Directive) { + if (!directives.contains(instruction)) { + directives.add(instruction); + } + } + if (instruction instanceof Opcode) { + Opcode opcode = (Opcode) instruction; + switch (opcode.getType()) { + case InstructionType.LEGAL_OPCODE: + if (!opcode.isW65816()) { + if (!legalOpcodes.contains(instruction)) { + + legalOpcodes.add(opcode); + } + + } else { + if (!w65816Opcodes.contains(instruction)) { + w65816Opcodes.add(opcode); + } + } + break; + case InstructionType.ILLEGAL_OPCODE: + if (!illegalOpcodes.contains(instruction)) { + + illegalOpcodes.add(opcode); + } + break; + case InstructionType.PSEUDO_OPCODE: + if (!pseudoOpcodes.contains(instruction)) { + pseudoOpcodes.add(opcode); + } + break; + default: + + } + } + } + } + + getCompilerInstructions(result, Texts.TOC_ASSEMBLER_INSTRUCTION_TYPE_DIRECTIVES_LABEL, directives); + getCompilerInstructions(result, Texts.TOC_ASSEMBLER_INSTRUCTION_TYPE_LEGAL_OPCODES_LABEL, legalOpcodes); + getCompilerInstructions(result, Texts.TOC_ASSEMBLER_INSTRUCTION_TYPE_PSEUDO_OPCODES_LABEL, pseudoOpcodes); + getCompilerInstructions(result, Texts.TOC_ASSEMBLER_INSTRUCTION_TYPE_ILLEGAL_OPCODES_LABEL, illegalOpcodes); + getCompilerInstructions(result, Texts.TOC_ASSEMBLER_INSTRUCTION_TYPE_W65816_OPCODES_LABEL, w65816Opcodes); + + return result; + } + + private void getCompilerInstructions(HTMLWriter writer, String title, List<Instruction> instructions) { + if (writer == null) { + throw new IllegalArgumentException("Parameter 'writer' must not be null."); + } + if (title == null) { + throw new IllegalArgumentException("Parameter 'title' must not be null."); + } + if (instructions == null) { + throw new IllegalArgumentException("Parameter 'instructions' must not be null."); + } + if (instructions.isEmpty()) { + return; + } + + Collections.sort(instructions); + + writer.begin("h3", null); + writer.writeText(title); + writer.end(); + + writer.beginTable(); + writer.beginTableRow(); + writer.writeTableHeader(Texts.TOC_ASSEMBLER_INSTRUCTION_TYPE_LABEL); + writer.writeTableHeader(Texts.TOC_ASSEMBLER_INSTRUCTION_NAME_LABEL); + writer.writeTableHeader(Texts.TOC_ASSEMBLER_INSTRUCTION_DESCRIPTION_LABEL); + + writer.end(); + + for (Instruction instruction : instructions) { + String typeImagePath = ICONS_PATH + CompilerSyntaxUtility.getTypeImagePath(instruction); + String typeText = CompilerSyntaxUtility.getTypeText(instruction); + StringBuilder styledTitle = new StringBuilder(instruction.getStyledTitle()); + int[] offsets = instruction.getStyledTitleOffsets(); + + for (int j = offsets.length - 1; j >= 0; j--) { + styledTitle.insert(offsets[j] + 1, "</u>"); + styledTitle.insert(offsets[j], "<u>"); + } + + writer.beginTableRow(); + writer.writeTableCell(HTMLWriter.getImage(typeImagePath, typeText, "")); + writer.writeTableCell(instruction.getName()); + writer.writeTableCell(styledTitle.toString()); + writer.end(); + + } + writer.end(); + } + + private InputStream getHardwareInputStream(String href) { + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + String path = getPath(SCHEMA_HARDWARE, href); + if (path == null) { + return null; + } + Hardware hardware = Hardware.valueOf(path.toUpperCase()); + AssemblerPlugin assemblerPlugin = AssemblerPlugin.getInstance(); + RunnerRegistry runnerRegistry = assemblerPlugin.getRunnerRegistry(); + + HTMLWriter writer = createHeader(); + + writer.beginTable(); + writer.writeTableRow(Texts.TOC_HARDWARE_NAME_LABEL, EnumUtility.getText(hardware)); + writer.writeTableRow(Texts.TOC_HARDWARE_ID_LABEL, hardware.name()); + writer.writeTableRow(Texts.TOC_HARDWARE_ICON_LABEL, + HTMLWriter.getImage(ICONS_PATH + HardwareUtility.getImagePath(hardware), hardware.name(), "")); + + writer.writeTableRow(Texts.TOC_HARDWARE_DEFAULT_FILE_EXTENSION_LABEL, + HardwareUtility.getDefaultFileExtension(hardware)); + writer.end(); + + writer.begin("br", null); + writer.end(); + + writer.beginTable(); + writer.beginTableRow(); + writer.writeTableHeader(Texts.TOC_HARDWARE_EMULATOR_LABEL); + writer.writeTableHeader(Texts.TOC_HARDWARE_HOME_PAGE_LABEL); + writer.writeTableHeader(Texts.TOC_HARDWARE_DEFAULT_PARAMETERS_LABEL); + writer.end(); + + List<RunnerDefinition> runnerDefinitions = runnerRegistry.getDefinitions(hardware); + for (RunnerDefinition runnerDefinition : runnerDefinitions) { + + String runnerId = runnerDefinition.getId(); + if (runnerId.equals(RunnerId.DEFAULT_APPLICATION) || runnerId.equals(RunnerId.USER_DEFINED_APPLICATION)) { + continue; + } + writer.beginTableRow(); + writer.writeTableCell(runnerDefinition.getName()); + writer.writeTableCell(HTMLWriter.getLink(runnerDefinition.getHomePageURL(), + runnerDefinition.getHomePageURL())); + writer.writeTableCell(runnerDefinition.getDefaultCommandLine()); + + writer.end(); + + } + writer.end(); + + return getInputStream(writer); + } + + private InputStream getCPUInputStream(String href) { + + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + String path = getPath(SCHEMA_CPU, href); + if (path == null) { + return null; + } + + CPU cpu = CPU.valueOf(path.toUpperCase()); + AssemblerPlugin assemblerPlugin = AssemblerPlugin.getInstance(); + CompilerRegistry compilerRegistry = assemblerPlugin.getCompilerRegistry(); + + HTMLWriter writer = createHeader(); + + writer.beginTable(); + writer.writeTableRow(Texts.TOC_CPU_NAME_LABEL, EnumUtility.getText(cpu)); + writer.end(); + + writer.begin("br", null); + writer.end(); + + writer.beginTable(); + writer.beginTableRow(); + + writer.writeTableHeader(Texts.TOC_CPU_OPCODE_LABEL); + writer.writeTableHeader(Texts.TOC_ASSEMBLER_INSTRUCTION_DESCRIPTION_LABEL); + + List<CompilerDefinition> compilerDefinitions = compilerRegistry.getCompilerDefinitions(); + int compilerDefinitionCount = compilerDefinitions.size(); + InstructionSet[] instructionSets = new InstructionSet[compilerDefinitions.size()]; + for (int c = 0; c < compilerDefinitionCount; c++) { + CompilerDefinition compilerDefinition = compilerDefinitions.get(c); + instructionSets[c] = compilerDefinition.getSyntax().getInstructionSet(cpu); + writer.writeTableHeader(compilerDefinition.getName()); + + } + writer.end(); + + List<String> cellContents = new ArrayList<String>(compilerDefinitionCount); + + for (int opcode = 0; opcode < Opcode.MAX_OPCODES; opcode++) { + String opcodeText = null; + cellContents.clear(); + + for (int c = 0; c < compilerDefinitionCount; c++) { + InstructionSet instructionSet = instructionSets[c]; + List<OpcodeAddressingMode> opcodeAddressingModes = instructionSet.getOpcodeAddressingModes(opcode); + StringBuffer cellBuffer = new StringBuffer(); + for (int m = 0; m < opcodeAddressingModes.size(); m++) { + // There should only be one, but be robust here. + if (m > 0 && m < opcodeAddressingModes.size()) { + cellBuffer.append("<br/>"); + } + OpcodeAddressingMode opcodeAddressingMode = opcodeAddressingModes.get(m); + if (opcodeText == null) { + opcodeText = opcodeAddressingMode.getOpcode().getStyledTitle(); + } + cellBuffer.append(opcodeAddressingMode.getFormattedText()); + } + cellContents.add(cellBuffer.toString()); + + } + + if (opcodeText != null) { + writer.beginTableRow(); + writer.writeTableCell(HexUtility.getByteValueHexString(opcode)); + writer.writeTableCell(opcodeText); + + for (int c = 0; c < compilerDefinitionCount; c++) { + + writer.writeTableCell(cellContents.get(c)); + + } + writer.end(); + } + } + + writer.end(); + + return getInputStream(writer); + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider.java new file mode 100644 index 00000000..ad2f0aee --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider.java @@ -0,0 +1,414 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.help; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.help.AbstractTocProvider; +import org.eclipse.help.IToc; +import org.eclipse.help.ITocContribution; +import org.eclipse.help.ITopic; +import org.eclipse.help.ITopic2; +import org.eclipse.help.IUAElement; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.preferences.AssemblerPreferences; +import com.wudsn.ide.base.common.EnumUtility; + +/** + * Dynamic help content provider. Uses static pages and the meta data from the + * compiler definitions to build a comprehensive help. + * + * @author Peter Dell + * + * @since 1.6.1 + */ +public final class AssemblerTocProvider extends AbstractTocProvider { + + private static final class Toc implements IToc { + private ITopic[] topics; + + public Toc() { + + topics = createTopics(); + } + + @Override + public boolean isEnabled(IEvaluationContext context) { + return true; + } + + @Override + public IUAElement[] getChildren() { + return topics; + } + + @Override + public String getHref() { + return ""; + } + + @Override + public String getLabel() { + return Texts.TOC_WUDSN_IDE_LABEL; + } + + @Override + public ITopic[] getTopics() { + return topics; + } + + @Override + public ITopic getTopic(String href) { + return topics[0]; + } + } + + private static final class TocContribution implements ITocContribution { + + public TocContribution() { + } + + @Override + public String getCategoryId() { + return "CategoryID"; + } + + @Override + public String getContributorId() { + return AssemblerPlugin.ID; + } + + @Override + public String[] getExtraDocuments() { + return new String[0]; + } + + @Override + public String getId() { + return "ID"; + } + + @Override + public String getLocale() { + return ""; + } + + @Override + public String getLinkTo() { + return ""; + } + + @Override + public IToc getToc() { + return new Toc(); + } + + @Override + public boolean isPrimary() { + return true; + } + + } + + public AssemblerTocProvider() { + } + + @Override + public ITocContribution[] getTocContributions(String locale) { + return new ITocContribution[] { new TocContribution() }; + } + + private static ITopic createTopic(String href) { + if (href == null) { + throw new IllegalArgumentException( + "Parameter 'href' must not be null."); + } + + String label; + String key = href; + try { + ResourceBundle resourceBundle; + resourceBundle = ResourceBundle.getBundle( + "com/wudsn/ide/asm/help/AssemblerTocProvider", + Locale.getDefault(), + AssemblerTocProvider.class.getClassLoader()); + label = resourceBundle.getString(key); + } catch (MissingResourceException ex) { + label = href + " - Text missing"; + AssemblerPlugin.getInstance().logError( + "Resource for enum value {0} is missing.", + new Object[] { key }, ex); + } + return createTopic("", label, href, null); + } + + // See[Bug 382599] Help: Icons not taken from IToc2/ITopic2 Implementations + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=382599 + @SuppressWarnings("restriction") + private static ITopic createTopic(String icon, String label, String href, + ITopic[] subtopics) { + org.eclipse.help.internal.Topic result = new org.eclipse.help.internal.Topic(); + result.setAttribute(org.eclipse.help.internal.Topic.ATTRIBUTE_ICON, + icon); + result.setLabel(label); + result.setHref(href); + if (subtopics == null) { + subtopics = new ITopic2[0]; + } + result.appendChildren(subtopics); + return result; + } + + private static ITopic[] createTopicsArray(List<ITopic> topics) { + if (topics == null) { + throw new IllegalArgumentException( + "Parameter 'topics' must not be null."); + } + ITopic[] topicsArray; + topicsArray = new ITopic[topics.size()]; + topics.toArray(topicsArray); + return topicsArray; + } + + static ITopic[] createTopics() { + AssemblerPlugin assemblerPlugin = AssemblerPlugin.getInstance(); + CompilerRegistry compilerRegistry = assemblerPlugin + .getCompilerRegistry(); + List<CompilerDefinition> compilerDefinitions = compilerRegistry + .getCompilerDefinitions(); + + List<ITopic> ideTopics = createIDETopics(); + List<ITopic> assemblerTopics = createAssemblerTopics(compilerDefinitions); + List<ITopic> hardwareTopics = createHardwareTopics(); + List<ITopic> cpuTopics = createCPUTopics(); + + List<ITopic> topics = new ArrayList<ITopic>(); + + topics.add(createTopic("", Texts.TOC_IDE_TOPIC_LABEL, "", + createTopicsArray(ideTopics))); + topics.add(createTopic("", Texts.TOC_ASSEMBLERS_TOPIC_LABEL, "", + createTopicsArray(assemblerTopics))); + topics.add(createTopic("", Texts.TOC_HARDWARES_TOPIC_LABEL, "", + createTopicsArray(hardwareTopics))); + topics.add(createTopic("", Texts.TOC_CPUS_TOPIC_LABEL, "", + createTopicsArray(cpuTopics))); + + return createTopicsArray(topics); + } + + private static List<ITopic> createIDETopics() { + List<ITopic> topics = new ArrayList<ITopic>(); + + topics.add(createTopic("help/ide-tutorials.section.html")); + topics.add(createTopic("help/ide-features.section.html")); + topics.add(createTopic("help/ide-installation.section.html")); + topics.add(createTopic("help/ide-releases.section.html")); + topics.add(createTopic("help/ide-faq.section.html")); + topics.add(createTopic("help/ide-credits.section.html")); + return topics; + } + + private static List<ITopic> createAssemblerTopics( + List<CompilerDefinition> compilerDefinitions) { + if (compilerDefinitions == null) { + throw new IllegalArgumentException( + "Parameter 'compilerDefinitions' must not be null."); + } + int size = compilerDefinitions.size(); + List<ITopic> assemblerTopics = new ArrayList<ITopic>(size); + + for (int i = 0; i < size; i++) { + CompilerDefinition compilerDefinition = compilerDefinitions.get(i); + + String href = AssemblerHelpContentProducer.SCHEMA_COMPILER + + compilerDefinition.getId() + "/" + + AssemblerHelpContentProducer.SECTION_GENERAL + + AssemblerHelpContentProducer.EXTENSION; + + ITopic generalTopic = createTopic("", + Texts.TOC_ASSEMBLER_GENERAL_TOPIC_LABEL, href, null); + + href = AssemblerHelpContentProducer.SCHEMA_COMPILER + + compilerDefinition.getId() + "/" + + AssemblerHelpContentProducer.SECTION_INSTRUCTIONS + + AssemblerHelpContentProducer.EXTENSION; + ITopic opcodesTopic = createTopic("", + Texts.TOC_ASSEMBLER_INSTRUCTIONS_TOPIC_LABEL, href, null); + + AssemblerPreferences assemblerPreferences = AssemblerPlugin + .getInstance().getPreferences(); + String compilerExecutablePath = assemblerPreferences + .getCompilerExecutablePath(compilerDefinition.getId()); + + String icon = ""; + List<ITopic> manualTopics = new ArrayList<ITopic>(); + try { + File file = compilerDefinition + .getHelpFile(compilerExecutablePath); + + // Appending the help file path with the correct file + // extension allows in-place display for example for ".html" + // files. + String extension = file.getPath(); + int index = extension.lastIndexOf('.'); + if (index > 0) { + extension = extension.substring(index); + if (extension.equalsIgnoreCase(".pdf")) { + icon = "pdf"; + } + } else { + extension = ".html"; + } + + href = AssemblerHelpContentProducer.SCHEMA_COMPILER + + compilerDefinition.getId() + "/" + + AssemblerHelpContentProducer.SECTION_MANUAL + + extension; + + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File file2 : files) { + String encodedPath; + try { + encodedPath = URLEncoder.encode( + file2.getName(), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new IllegalArgumentException( + "Cannot encode file name '" + + file2.getName() + "'"); + } + + manualTopics + .add(createTopic( + "", + file2.getName(), + href + + "?" + + AssemblerHelpContentProducer.SECTION_MANUAL_FILE + + "=" + encodedPath, null)); + } + } + // if the file is folder, the manual does not have own + // content but only sub-topics + href = ""; + } + } catch (CoreException ex) { + href = AssemblerHelpContentProducer.SCHEMA_COMPILER + + compilerDefinition.getId() + "/" + + AssemblerHelpContentProducer.SECTION_MANUAL + ".html"; + } + + ITopic manualTopic = createTopic(icon, + Texts.TOC_ASSEMBLER_MANUAL_TOPIC_LABEL, href, + createTopicsArray(manualTopics)); + + assemblerTopics.add(createTopic("", compilerDefinition.getName(), + "", + new ITopic[] { generalTopic, opcodesTopic, manualTopic })); + } + return assemblerTopics; + } + + private static List<ITopic> createHardwareTopics() { + // Build hardware topics + List<ITopic> hardwareTopics = new ArrayList<ITopic>( + Hardware.values().length - 1); + for (Hardware hardware : Hardware.values()) { + if (hardware.equals(Hardware.GENERIC)) { + continue; + } + List<ITopic> chipTopics = new ArrayList<ITopic>(); + switch (hardware) { + + case ATARI2600: + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/sizes.txt")); + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/2600_mem_map.txt")); + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/stella.pdf")); + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt")); + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/tia_color.html")); + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/2600_tia_map.txt")); + chipTopics + .add(createTopic("help/www.qotile.net/minidig/docs/2600_riot_map.txt")); + break; + + case ATARI8BIT: + chipTopics + .add(createTopic("help/www.oxyron.de/html/registers_antic.html")); + chipTopics + .add(createTopic("help/www.oxyron.de/html/registers_gtia.html")); + chipTopics + .add(createTopic("help/www.oxyron.de/html/registers_pokey.html")); + break; + case C64: + chipTopics + .add(createTopic("help/www.oxyron.de/html/registers_rec.html")); + chipTopics + .add(createTopic("help/www.oxyron.de/html/registers_sid.html")); + chipTopics + .add(createTopic("help/www.oxyron.de/html/registers_vic2.html")); + break; + default: + // Nothing available. + + } + String href = AssemblerHelpContentProducer.SCHEMA_HARDWARE + + hardware.name().toLowerCase() + + AssemblerHelpContentProducer.EXTENSION; + hardwareTopics.add(createTopic("", EnumUtility.getText(hardware), + href, createTopicsArray(chipTopics))); + } + return hardwareTopics; + } + + private static List<ITopic> createCPUTopics() { + List<ITopic> cpuTopics = new ArrayList<ITopic>(CPU.values().length - 1); + for (CPU cpu : CPU.values()) { + String href = AssemblerHelpContentProducer.SCHEMA_CPU + + cpu.name().toLowerCase() + + AssemblerHelpContentProducer.EXTENSION; + cpuTopics + .add(createTopic("", EnumUtility.getText(cpu), href, null)); + } + return cpuTopics; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider.properties b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider.properties new file mode 100644 index 00000000..9045fd2a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider.properties @@ -0,0 +1,23 @@ +help/ide-credits.section.html=Credits +help/ide-faq.section.html=FAQ +help/ide-features.section.html=Features +help/ide-installation.section.html=Installation +help/ide-releases.section.html=Releases +help/ide-tutorials.section.html=Video Tutorials + +help/www.oxyron.de/html/registers_antic.html=ANTIC Reference +help/www.oxyron.de/html/registers_gtia.html=GTIA Reference +help/www.oxyron.de/html/registers_pokey.html=POKEY Reference +help/www.oxyron.de/html/registers_rec.html=REC Reference +help/www.oxyron.de/html/registers_sid.html=SID Reference +help/www.oxyron.de/html/registers_ted.html=TED Reference +help/www.oxyron.de/html/registers_vic1.html=VIC I Reference +help/www.oxyron.de/html/registers_vic2.html=VIC II Reference + +help/www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt=STELLA Programming Guide (Advanced) +help/www.qotile.net/minidig/docs/2600_mem_map.txt=Memory Map +help/www.qotile.net/minidig/docs/2600_riot_map.txt=RIOT Map +help/www.qotile.net/minidig/docs/2600_tia_map.txt=TIA Map +help/www.qotile.net/minidig/docs/sizes.txt=Cartridges +help/www.qotile.net/minidig/docs/stella.pdf=STELLA Programming Guide +help/www.qotile.net/minidig/docs/tia_color.html=TIA Colors \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider_de_DE.properties b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider_de_DE.properties new file mode 100644 index 00000000..e8b8d714 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/AssemblerTocProvider_de_DE.properties @@ -0,0 +1,23 @@ +help/ide-credits.section.html=Danke an... +help/ide-faq.section.html=Häufig gestellte Fragen +help/ide-features.section.html=Funktionen +help/ide-installation.section.html=Installation +help/ide-releases.section.html=Versionen +help/ide-tutorials.section.html=Video Lehrgänge + +help/www.oxyron.de/html/registers_antic.html=ANTIC Referenz +help/www.oxyron.de/html/registers_gtia.html=GTIA Referenz +help/www.oxyron.de/html/registers_pokey.html=POKEY Referenz +help/www.oxyron.de/html/registers_rec.html=REC Referenz +help/www.oxyron.de/html/registers_sid.html=SID Referenz +help/www.oxyron.de/html/registers_ted.html=TED Referenz +help/www.oxyron.de/html/registers_vic1.html=VIC I Referenz +help/www.oxyron.de/html/registers_vic2.html=VIC II Referenz + +help/www.qotile.net/minidig/docs/2600_advanced_prog_guide.txt=STELLA Programmierleitfaden (erweitert) +help/www.qotile.net/minidig/docs/2600_mem_map.txt=Speicher Addressübersicht +help/www.qotile.net/minidig/docs/2600_riot_map.txt=RIOT Addressübersicht +help/www.qotile.net/minidig/docs/2600_tia_map.txt=TIA Addressübersicht +help/www.qotile.net/minidig/docs/sizes.txt=Steckmodule +help/www.qotile.net/minidig/docs/stella.pdf=STELLA Programmierleitfaden +help/www.qotile.net/minidig/docs/tia_color.html=TIA Farben \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLConstants.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLConstants.java new file mode 100644 index 00000000..6f607208 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLConstants.java @@ -0,0 +1,45 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.help; + +/** + * HTML Constants. + * + * @author Peter Dell + * @since 1.6.3 + */ +final class HTMLConstants { + public static final String UTF8 = "UTF-8"; + + private static final String NL = "\n"; + private static final String DOC_TYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"; + private static final String CONTENT_TYPE = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + + UTF8 + "\" />"; + private static final String STYLE_SHEETS = "<link rel=\"STYLESHEET\" href=\"/help/content/org.eclipse.platform/book.css\" type=\"text/css\" />" + + NL + + "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"stylesheets/ide.css\"/>"; + + public static final String PREFIX = DOC_TYPE + NL + + "<html xmlns=\"http://www.w3.org/1999/xhtml\">" + NL + "<head>" + + NL + HTMLConstants.CONTENT_TYPE + NL + HTMLConstants.STYLE_SHEETS + + NL + "</head><body>"; + public static final String SUFFIX = "</body>" + NL + "</html>"; + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLWrapperInputStream.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLWrapperInputStream.java new file mode 100644 index 00000000..0c5d142b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLWrapperInputStream.java @@ -0,0 +1,104 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.help; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +/** + * Wraps plain text files into a HTML document envelope. + * + * @author Peter Dell + * @since 1.6.3 + * + */ +final class HTMLWrapperInputStream extends InputStream { + + private InputStream prefixInputStream; + private InputStream innerInputStream; + private InputStream suffixInputStream; + + public HTMLWrapperInputStream(String prefix, String suffix, + InputStream inputStream) { + if (prefix == null) { + throw new IllegalArgumentException( + "Parameter 'prefix' must not be null."); + } + if (suffix == null) { + throw new IllegalArgumentException( + "Parameter 'suffix' must not be null."); + } + if (inputStream == null) { + throw new IllegalArgumentException( + "Parameter 'inputStream' must not be null."); + } + + try { + prefixInputStream = new ByteArrayInputStream( + prefix.getBytes("UTF-8")); + + innerInputStream = inputStream; + suffixInputStream = new ByteArrayInputStream( + suffix.getBytes("UTF-8")); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + + } + + @Override + public int read() throws IOException { + int result; + + if (prefixInputStream != null) { + result = prefixInputStream.read(); + if (result != -1) { +// System.out.print((char)result); + + return result; + } + prefixInputStream = null; + } + + if (innerInputStream != null) { + result = innerInputStream.read(); + if (result != -1) { +// System.out.print((char)result); + return result; + } + innerInputStream = null; + } + + if (suffixInputStream != null) { + result = suffixInputStream.read(); + if (result != -1) { +// System.out.print((char)result); + + return result; + } + suffixInputStream = null; + } + return -1; + + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLWriter.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLWriter.java new file mode 100644 index 00000000..8b38b805 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/help/HTMLWriter.java @@ -0,0 +1,211 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.help; + +import java.util.ArrayList; +import java.util.List; + +/** + * Convenience HTML writer. + * + * @author Peter Dell + * @since 1.6.3 + */ +final class HTMLWriter { + private static final String BORDER_STYLE = "border-style:solid;border-width:1px;border-collapse:collapse;"; + + private StringBuilder builder; + private List<String> stack; + private boolean tableBorder; + + public HTMLWriter() { + builder = new StringBuilder(); + stack = new ArrayList<String>(); + } + + public void begin(String tag, String attributes) { + if (tag == null) { + throw new IllegalArgumentException("Parameter 'tag' must not be null."); + } + builder.append("<"); + builder.append(tag); + + if (attributes != null) { + builder.append(" "); + builder.append(attributes); + + } else { + if ((tag.equals("th") || tag.equals("td")) && tableBorder) { + builder.append(" style=\"border:1px solid\""); + } + } + builder.append(">"); + stack.add(tag); + } + + public void end() { + if (stack.isEmpty()) { + throw new RuntimeException("No open tag: " + builder); + } + String tag = stack.remove(stack.size() - 1); + builder.append("</"); + builder.append(tag); + builder.append(">\n"); + + if (tag.equals("table")) { + tableBorder = false; + } + + } + + public void writeText(String text) { + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + builder.append(text); + } + + public void beginTable() { + beginTable(true); + } + + public void beginTable(boolean border) { + tableBorder = border; + + begin("table", "style=\"text-align:left;" + (tableBorder ? BORDER_STYLE : "") + "\""); + } + + public void beginTableRow() { + begin("tr", null); + } + + public void writeTableRow(String header, String text) { + if (header == null) { + throw new IllegalArgumentException("Parameter 'header' must not be null."); + } + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + beginTableRow(); + writeTableHeader(header); + writeTableCell(text); + end(); + } + + public void writeTableRowCode(String header, String text) { + beginTableRow(); + writeTableHeader(header); + begin("td", "style=\"vertical-align:text-top;font-family:Courier New, Courier, monospace;border:1px solid;\""); + + // Cell content must not be empty, otherwise the border is not + // displayed. + if (text.trim().length() == 0) { + text = " "; + } + builder.append(text); + end(); + end(); + } + + public void writeTableRowCode(String header, char character) { + String text = ""; + if (character >= 32) { + text = Character.toString(character); + } + writeTableRowCode(header, text); + } + + public void writeTableHeader(String text) { + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + begin("th", "style=\"" + (tableBorder ? BORDER_STYLE : "") + ";vertical-align:text-top;white-space:nowrap;\""); + builder.append(text); + end(); + } + + public void writeTableCell(String text) { + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + begin("td", "style=\"" + (tableBorder ? BORDER_STYLE : "") + ";vertical-align:text-top;white-space:nowrap;\""); + builder.append(text); + end(); + } + + public void beginList() { + begin("ul", null); + } + + public void writeListItem(String text) { + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + begin("li", null); + builder.append(text); + end(); + } + + public String toHTML() { + if (!stack.isEmpty()) { + throw new IllegalStateException("There are still open tags: " + stack + "\n" + builder.toString()); + } + return builder.toString(); + } + + public static String getImage(String src, String alt, String text) { + if (src == null) { + throw new IllegalArgumentException("Parameter 'src' must not be null."); + } + if (alt == null) { + throw new IllegalArgumentException("Parameter 'alt' must not be null."); + } + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + return "<img style=\"vertical-align:middle\" src=\"" + src + "\" alt=\"" + alt + "\"/> " + text; + } + + public static String getLink(String href, String text) { + if (href == null) { + throw new IllegalArgumentException("Parameter 'href' must not be null."); + } + + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + return "<a href=\"" + href + "\" >" + text + "</a>"; + } + + public static String getString(List<String> list) { + if (list == null) { + throw new IllegalArgumentException("Parameter 'list' must not be null."); + } + StringBuilder builder = new StringBuilder(); + int size = list.size(); + for (int i = 0; i < size; i++) { + builder.append(list.get(i)); + if (i < size - 1) { + builder.append(" "); + } + } + return builder.toString(); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferences.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferences.java new file mode 100644 index 00000000..10796a07 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferences.java @@ -0,0 +1,192 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.TextAttribute; + +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.editor.AssemblerContentAssistProcessorDefaultCase; +import com.wudsn.ide.asm.editor.AssemblerEditorCompileCommandPositioningMode; +import com.wudsn.ide.base.common.AbstractIDEPlugin; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Facade class for typed access to the plugin preferences. + * + * @author Peter Dell + */ +public final class AssemblerPreferences { + + /** + * The preference store to which all calls are delegated. + */ + private IPreferenceStore preferenceStore; + + /** + * Created by {@link AbstractIDEPlugin} only. + * + * @param preferenceStore + * The preference store, not <code>null</code>. + */ + public AssemblerPreferences(IPreferenceStore preferenceStore) { + if (preferenceStore == null) { + throw new IllegalArgumentException( + "Parameter 'preferenceStore' must not be null."); + } + this.preferenceStore = preferenceStore; + } + + /** + * Gets the text attribute for a token type. + * + * @param name + * The name of the preferences for the token type, see + * {@link AssemblerPreferencesConstants}. + * + * @return The text attribute, not <code>null</code>. + */ + public TextAttribute getEditorTextAttribute(String name) { + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'name' must not be null."); + } + return TextAttributeConverter.fromString(preferenceStore + .getString(name)); + } + + /** + * Gets the default case content assist. + * + * @return The default case content assist, may be empty, not + * <code>null</code>. See + * {@link AssemblerContentAssistProcessorDefaultCase}. + */ + public String getEditorContentAssistProcessorDefaultCase() { + return getString(AssemblerPreferencesConstants.EDITOR_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE); + } + + /** + * Gets the compile command positioning mode. + * + * @return The positioning mode, may be empty, not <code>null</code>. See + * {@link AssemblerEditorCompileCommandPositioningMode}. + * @since 1.6.1 + */ + public String getEditorCompileCommandPositioningMode() { + return getString(AssemblerPreferencesConstants.EDITOR_COMPILE_COMMAND_POSITIONING_MODE); + } + + /** + * Gets the executable path for the compiler. + * + * @param compilerId + * The compiler id, not empty and not <code>null</code>. + * + * @return The executable path for the compiler, may be empty, not + * <code>null</code>. + */ + public String getCompilerExecutablePath(String compilerId) { + if (compilerId == null) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be null."); + } + if (StringUtility.isEmpty(compilerId)) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be empty."); + } + return getString(AssemblerPreferencesConstants + .getCompilerExecutablePathName(compilerId)); + } + + /** + * Gets the preferences for a compiler. + * + * @param compilerId + * The compiler id, not empty and not <code>null</code>. + * @param hardware + * The preferences or <code>null</code> if the compiler is not + * active for that hardware. + * + * @return The compiler preferences, not <code>null</code>. + */ + public CompilerPreferences getCompilerPreferences(String compilerId, + Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be null."); + } + if (StringUtility.isEmpty(compilerId)) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be empty."); + } + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + return new CompilerPreferences(this, compilerId, hardware); + + } + + /** + * Gets the current value of the string-valued preference with the given + * name. Returns the default-default value (the empty string <code>""</code> + * ) if there is no preference with the given name, or if the current value + * cannot be treated as a string. + * + * @param name + * The name of the preference, not <code>null</code>. + * @return The preference value, may be empty, not <code>null</code>. + */ + String getString(String name) { + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + String result; + result = preferenceStore.getString(name); + if (result == null) { + result = ""; + } else { + result = result.trim(); + } + + return result; + } + + /** + * Gets the current value of the boolean preference with the given name. + * Returns the default-default value <code>false</code> if there is no + * preference with the given name, or if the current value cannot be treated + * as a boolean. + * + * @param name + * The name of the preference, not <code>null</code>. + * @return The preference value. + */ + boolean getBoolean(String name) { + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + return preferenceStore.getBoolean(name); + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesChangeListener.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesChangeListener.java new file mode 100644 index 00000000..8a3f4f18 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesChangeListener.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import java.util.Set; + +/** + * Listener interface for global preferences changes. + * + * @author Peter Dell + * + * @sine 1.7.0 + */ +public interface AssemblerPreferencesChangeListener { + + /** + * Notify of changed properties. + * @param preferences + * The assembler preferences containing the new values, not + * <code>null</code>. + * @param changedPropertyNames + * The set of names of properties that have been changed, may be + * empty, not <code>null</code>. + */ + public void preferencesChanged(AssemblerPreferences preferences, Set<String> changedPropertyNames); +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesCompilersPage.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesCompilersPage.java new file mode 100644 index 00000000..12997cd4 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesCompilersPage.java @@ -0,0 +1,653 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.preference.BooleanFieldEditor; +import org.eclipse.jface.preference.ComboFieldEditor; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.RadioGroupFieldEditor; +import org.eclipse.jface.preference.StringFieldEditor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerOutputFolderMode; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.editor.AssemblerEditor; +import com.wudsn.ide.asm.runner.RunnerDefinition; +import com.wudsn.ide.asm.runner.RunnerId; +import com.wudsn.ide.asm.runner.RunnerRegistry; +import com.wudsn.ide.base.common.EnumUtility; +import com.wudsn.ide.base.common.ProcessWithLogs; +import com.wudsn.ide.base.common.StringUtility; +import com.wudsn.ide.base.common.TextUtility; +import com.wudsn.ide.base.gui.SWTFactory; + +/** + * Visual editor page for the assembler preferences regarding compilers. There + * is a separate page per {@link Hardware}. Subclasses only implement the + * constructor. + * + * @author Peter Dell + */ +public abstract class AssemblerPreferencesCompilersPage extends FieldEditorPreferencePage implements + IWorkbenchPreferencePage { + + private static final class Tab { + + public final String compilerId; + public final int tabIndex; + public final TabItem tabItem; + public final Control enabledControl; + public final Control disabledControl; + public final List<ControlDecoration> controlDecorations; + public boolean initialized; + public boolean enabled; + + public Tab(String compilerId, int tabIndex, TabItem tabItem, Control enabledControl, Control disabledControl, + List<ControlDecoration> controlDecorations) { + this.compilerId = compilerId; + this.tabIndex = tabIndex; + this.tabItem = tabItem; + this.enabledControl = enabledControl; + this.disabledControl = disabledControl; + this.controlDecorations = controlDecorations; + initialized = false; + enabled = false; + } + } + + /** + * Local workaround class to react on changes of a radio group field editor. + * By default the selection listener is set to the containing page, so we + * need a second listener mechanism. + */ + private final class RadioGroupFieldEditorWithAction extends RadioGroupFieldEditor { + + private IPropertyChangeListener propertyChangeListener; + + public RadioGroupFieldEditorWithAction(String name, String labelText, int numColumns, + String[][] labelAndValues, Composite parent) { + super(name, labelText, numColumns, labelAndValues, parent); + } + + @Override + protected void fireValueChanged(String property, Object oldValue, Object newValue) { + super.fireValueChanged(property, oldValue, newValue); + if (propertyChangeListener != null) { + propertyChangeListener.propertyChange(new PropertyChangeEvent(this, property, oldValue, newValue)); + } + + } + + public void setAdditionalPropertyChangeListener(IPropertyChangeListener propertyChangeListener) { + this.propertyChangeListener = propertyChangeListener; + + } + } + + /** + * Property change listener to set the enabled state of the output folder + * path field based on the output folder mode field. + */ + private final class OutputFolderModeChangeListener implements IPropertyChangeListener { + + private Composite outputFolderPathFieldEditorParent; + private StringFieldEditor outputFolderPathFieldEditor; + + public OutputFolderModeChangeListener(Composite outputFolderPathFieldEditorParent, + StringFieldEditor outputFolderPathFieldEditor) { + if (outputFolderPathFieldEditorParent == null) { + throw new IllegalArgumentException("Parameter 'outputFolderPathFieldEditorParent' must not be null."); + } + if (outputFolderPathFieldEditor == null) { + throw new IllegalArgumentException("Parameter 'outputFolderPathFieldEditor' must not be null."); + } + this.outputFolderPathFieldEditorParent = outputFolderPathFieldEditorParent; + this.outputFolderPathFieldEditor = outputFolderPathFieldEditor; + } + + @Override + public void propertyChange(PropertyChangeEvent event) { + if (event == null) { + throw new IllegalArgumentException("Parameter 'event' must not be null."); + } + setOutputFolderMode((String) event.getNewValue()); + } + + public void setOutputFolderMode(String newValue) { + boolean enabled; + enabled = CompilerOutputFolderMode.FIXED_FOLDER.equals(newValue); + outputFolderPathFieldEditor.setEnabled(enabled, outputFolderPathFieldEditorParent); + } + + } + + /** + * The type of hardware used to filter the compilers and emulators. + */ + final Hardware hardware; + + /** + * The owning plugin. + */ + private final AssemblerPlugin plugin; + + /** + * The tab folder and all visible tab items. + */ + private TabFolder tabFolder; + private final Map<String, Tab> tabs; + + /** + * The id of the compiler and runner to be used as default. + */ + private String activeCompilerId; + private String activeRunnerId; + + /** + * Creation is protected for sub-classes. + * + * @param hardware + * The type of hardware used to filter the compilers and + * emulators, not <code>null</code>. + */ + protected AssemblerPreferencesCompilersPage(Hardware hardware) { + super(GRID); + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + this.hardware = hardware; + plugin = AssemblerPlugin.getInstance(); + IPreferenceStore preferencesStore = plugin.getPreferenceStore(); + setPreferenceStore(preferencesStore); + + tabs = new TreeMap<String, Tab>(); + } + + @Override + public final void init(IWorkbench workbench) { + if (workbench == null) { + throw new IllegalArgumentException("Parameter 'workbench' must not be null."); + } + IEditorPart editor = workbench.getActiveWorkbenchWindow().getActivePage().getActiveEditor(); + if (editor instanceof AssemblerEditor) { + AssemblerEditor assemblerEditor; + assemblerEditor = (AssemblerEditor) editor; + activeCompilerId = assemblerEditor.getCompilerDefinition().getId(); + activeRunnerId = assemblerEditor.getCompilerPreferences().getRunnerId(); + } else { + activeCompilerId = ""; + activeRunnerId = ""; + } + } + + @Override + public final void createFieldEditors() { + + Composite parent = getFieldEditorParent(); + GridData gridData = new GridData(); + gridData.verticalIndent = 0; + gridData.horizontalIndent = 0; + parent.setLayoutData(gridData); + + createCompilerFieldEditors(parent); + setTabsStatus(); + } + + private void createCompilerFieldEditors(Composite parent) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + + // Create the editors for all compilers of the hardware. + CompilerRegistry compilerRegistry = plugin.getCompilerRegistry(); + List<CompilerDefinition> compilerDefinitions = compilerRegistry.getCompilerDefinitions(); + + tabFolder = new TabFolder(parent, SWT.FLAT); + for (CompilerDefinition compilerDefinition : compilerDefinitions) { + + createTabItem(tabFolder, compilerDefinition); + } + + // Default to tab item for active compiler or to first. + TabItem selectedTabItem = null; + if (activeCompilerId != null) { + Tab selectedTab = tabs.get(activeCompilerId); + if (selectedTab != null) { + selectedTabItem = selectedTab.tabItem; + } + } + if (selectedTabItem == null && tabFolder.getItemCount() > 0) { + selectedTabItem = tabFolder.getItem(0); + } + if (selectedTabItem != null) { + tabFolder.setSelection(selectedTabItem); + } + + // Make sure the control decorations are updated as required + tabFolder.addSelectionListener(new SelectionListener() { + + @Override + public void widgetSelected(SelectionEvent e) { + setTabsStatus(); + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + }); + } + + private void createTabItem(TabFolder tabFolder, CompilerDefinition compilerDefinition) { + if (tabFolder == null) { + throw new IllegalArgumentException("Parameter 'tabFolder' must not be null."); + } + if (compilerDefinition == null) { + throw new IllegalArgumentException("Parameter 'compilerDefinition' must not be null."); + } + + String[][] labelsAndValues; + labelsAndValues = new String[][] { + { Texts.PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_SOURCE_FOLDER_TEXT, + CompilerOutputFolderMode.SOURCE_FOLDER }, + { Texts.PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_TEMP_FOLDER_TEXT, CompilerOutputFolderMode.TEMP_FOLDER }, + { Texts.PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_FIXED_FOLDER_TEXT, + CompilerOutputFolderMode.FIXED_FOLDER } + + }; + + String compilerId = compilerDefinition.getId(); + TabItem tabItem = new TabItem(tabFolder, SWT.NONE); + tabItem.setText(compilerDefinition.getName()); + + Composite tabContent; + tabContent = SWTFactory.createComposite(tabFolder, 1, 1, GridData.FILL_BOTH); + + List<ControlDecoration> controlDecorations; + controlDecorations = new ArrayList<ControlDecoration>(); + + Composite composite; + + // Field: cpu + composite = SWTFactory.createComposite(tabContent, 2, 3, GridData.FILL_HORIZONTAL); + + // Filtering of CPU based on hardware is currently not implemented + // because expansion boards like a W65816 board might be there/added + // for a hardware. + List<CPU> cpus = compilerDefinition.getSupportedCPUs(); + String[][] entryNamesAndValues = new String[cpus.size()][]; + int i = 0; + for (CPU cpu : cpus) { + entryNamesAndValues[i] = new String[2]; + entryNamesAndValues[i][1] = cpu.name(); + entryNamesAndValues[i][0] = EnumUtility.getText(cpu); + i++; + } + + FieldEditor comboFieldEditor = new ComboFieldEditor(AssemblerPreferencesConstants.getCompilerCPUName( + compilerId, hardware), Texts.PREFERENCES_COMPILER_CPU_LABEL, entryNamesAndValues, composite); + comboFieldEditor.setEnabled(entryNamesAndValues.length > 1, composite); + addField(comboFieldEditor); + + String name; + + // Field: defaultParameters + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + + Label label = new Label(composite, SWT.LEFT); + label.setText(Texts.PREFERENCES_COMPILER_DEFAULT_PARAMETERS_LABEL); + Text textField = new Text(composite, SWT.SINGLE | SWT.BORDER); + textField.setEditable(false); + textField.setText(compilerDefinition.getDefaultParameters()); + GridData gd = new GridData(); + gd.horizontalSpan = 2; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + textField.setLayoutData(gd); + + // Field: parameters + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + StringFieldEditor parametersFieldEditor; + name = AssemblerPreferencesConstants.getCompilerParametersName(compilerId, hardware); + parametersFieldEditor = new StringFieldEditor(name, Texts.PREFERENCES_COMPILER_PARAMETERS_LABEL, tabContent); + + String compilerParametersHelp = Texts.PREFERENCES_COMPILER_PARAMETERS_HELP + "\n" + + Texts.PREFERENCES_COMPILER_PARAMETERS_VARIABLES; + controlDecorations.add(createHelpDecoration(parametersFieldEditor, tabContent, compilerParametersHelp)); + + gd = new GridData(); + gd.horizontalSpan = 1; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + gd.horizontalIndent = 14; + parametersFieldEditor.getTextControl(tabContent).setLayoutData(gd); + + addField(parametersFieldEditor); + + // Field: outputFolderMode + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + RadioGroupFieldEditorWithAction outputFolderModeChoiceEditor = new RadioGroupFieldEditorWithAction( + AssemblerPreferencesConstants.getCompilerOutputFolderModeName(compilerId, hardware), + Texts.PREFERENCES_COMPILER_OUTPUT_FOLDER_MODE_LABEL, 3, labelsAndValues, composite); + addField(outputFolderModeChoiceEditor); + + // Field: outputFolderPath + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + StringFieldEditor outputFolderPathFieldEditor; + outputFolderPathFieldEditor = new DirectoryFieldDownloadEditor( + AssemblerPreferencesConstants.getCompilerOutputFolderPathName(compilerId, hardware), + Texts.PREFERENCES_COMPILER_OUTPUT_FOLDER_PATH_LABEL, composite); + addField(outputFolderPathFieldEditor); + + // Create a connection between the output mode field and the output + // path field. + OutputFolderModeChangeListener outputFolderModeChangeListener; + outputFolderModeChangeListener = new OutputFolderModeChangeListener(composite, outputFolderPathFieldEditor); + // Set initial status based on current output folder mode. + outputFolderModeChangeListener.setOutputFolderMode(getPreferenceStore().getString( + outputFolderModeChoiceEditor.getPreferenceName())); + // Register for changes. + outputFolderModeChoiceEditor.setAdditionalPropertyChangeListener(outputFolderModeChangeListener); + + // Field: outputFileExtension + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + StringFieldEditor outputFileExtensionFieldEditor; + outputFileExtensionFieldEditor = new StringFieldEditor( + AssemblerPreferencesConstants.getCompilerOutputFileExtensionName(compilerId, hardware), + Texts.PREFERENCES_COMPILER_OUTPUT_FILE_EXTENSION_LABEL, composite); + + gd = new GridData(SWT.BEGINNING, SWT.FILL, true, false); + gd.widthHint = convertWidthInCharsToPixels(5); + outputFileExtensionFieldEditor.getTextControl(composite).setLayoutData(gd); + outputFileExtensionFieldEditor.getTextControl(composite); + addField(outputFileExtensionFieldEditor); + + composite = SWTFactory.createComposite(tabContent, 1, 2, GridData.FILL_HORIZONTAL); + + RunnerRegistry runnerRegistry = plugin.getRunnerRegistry(); + List<RunnerDefinition> runnerDefinitions; + runnerDefinitions = runnerRegistry.getDefinitions(hardware); + entryNamesAndValues = new String[runnerDefinitions.size()][]; + i = 0; + for (RunnerDefinition runnerDefinition : runnerDefinitions) { + entryNamesAndValues[i] = new String[2]; + entryNamesAndValues[i][1] = runnerDefinition.getId(); + entryNamesAndValues[i][0] = runnerDefinition.getName(); + i++; + } + comboFieldEditor = new ComboFieldEditor(AssemblerPreferencesConstants.getCompilerRunnerIdName(compilerId, + hardware), Texts.PREFERENCES_COMPILER_RUNNER_ID_LABEL, entryNamesAndValues, composite); + addField(comboFieldEditor); + createRunnerFieldEdiors(compilerId, composite, controlDecorations); + + Composite disabledControl = SWTFactory.createComposite(tabFolder, 1, 1, GridData.FILL_BOTH); + label = new Label(disabledControl, SWT.NONE); + label.setText(TextUtility.format(Texts.MESSAGE_E100, compilerDefinition.getName())); + Tab tab = new Tab(compilerId, tabs.size(), tabItem, tabContent, disabledControl, controlDecorations); + tabs.put(compilerId, tab); + + } + + void setTabsStatus() { + for (Tab tab : tabs.values()) { + setTabStatus(tab); + + } + // tabFolder.layout(); + // tabFolder.getParent().getParent().redraw(); + } + + private void setTabStatus(Tab tab) { + if (tab == null) { + throw new IllegalArgumentException("Parameter 'tab' must not be null."); + } + + AssemblerPreferences assemblerPreferences = plugin.getPreferences(); + + boolean enabled = StringUtility.isSpecified(assemblerPreferences.getCompilerExecutablePath(tab.compilerId)); + + if (!tab.initialized || enabled != tab.enabled) { + tab.initialized = true; + tab.enabled = enabled; + if (enabled) { + tab.tabItem.setControl(tab.enabledControl); + } else { + tab.tabItem.setControl(tab.disabledControl); + } + tab.disabledControl.setVisible(!enabled); + tab.enabledControl.setVisible(enabled); + } + boolean tabActive = tab.tabIndex == tabFolder.getSelectionIndex(); + for (ControlDecoration controlDecoration : tab.controlDecorations) { + if (enabled && tabActive) { + controlDecoration.show(); + } else { + controlDecoration.hide(); + } + } + + } + + private void createRunnerFieldEdiors(String compilerId, Composite parent, List<ControlDecoration> controlDecorations) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + if (controlDecorations == null) { + throw new IllegalArgumentException("Parameter 'controlDecorations' must not be null."); + } + + TabFolder tabFolder = new TabFolder(parent, SWT.NONE); + TabItem selectedTabItem = null; + GridData gd; + + gd = new GridData(); + gd.horizontalSpan = 1; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + tabFolder.setLayoutData(gd); + + RunnerRegistry runnerRegistry = plugin.getRunnerRegistry(); + List<RunnerDefinition> runnerDefinitions; + runnerDefinitions = runnerRegistry.getDefinitions(hardware); + + String runnerCommandLineHelp = Texts.PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_HELP + "\n" + + Texts.PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_VARIABLES; + + for (RunnerDefinition runnerDefinition : runnerDefinitions) { + + String runnerId = runnerDefinition.getId(); + + if (runnerId.equals(RunnerId.DEFAULT_APPLICATION)) { + continue; + } + + TabItem tabItem = new TabItem(tabFolder, SWT.NONE); + tabItem.setText(runnerDefinition.getName()); + + Composite tabContent; + tabContent = SWTFactory.createComposite(tabFolder, 2, 1, GridData.FILL_BOTH); + + String name = AssemblerPreferencesConstants.getCompilerRunnerExecutablePathName(compilerId, hardware, + runnerId); + + Composite composite; + composite = SWTFactory.createComposite(tabContent, 4, 2, GridData.FILL_HORIZONTAL); + FileFieldDownloadEditor fileFieldEditor = new FileFieldDownloadEditor(name, + Texts.PREFERENCES_COMPILER_RUNNER_EXECUTABLE_PATH_LABEL, composite); + fileFieldEditor.setFileExtensions(ProcessWithLogs.getExecutableExtensions()); + fileFieldEditor.setEnabled(runnerDefinition.isRunnerExecutablePathPossible(), composite); + addField(fileFieldEditor); + + // Field: defaultEmulatorParameters + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + + Label label = new Label(composite, SWT.LEFT); + label.setText(Texts.PREFERENCES_COMPILER_RUNNER_DEFAULT_COMMAND_LINE_LABEL); + Text textField = new Text(composite, SWT.SINGLE | SWT.BORDER); + textField.setEditable(false); + textField.setText(runnerDefinition.getDefaultCommandLine()); + gd = new GridData(); + gd.horizontalSpan = 2; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + textField.setLayoutData(gd); + + // Field: parameters + composite = SWTFactory.createComposite(tabContent, 2, 2, GridData.FILL_HORIZONTAL); + StringFieldEditor commandLineFieldEditor; + name = AssemblerPreferencesConstants.getCompilerRunnerCommandLineName(compilerId, hardware, runnerId); + commandLineFieldEditor = new StringFieldEditor(name, Texts.PREFERENCES_COMPILER_RUNNER_COMMAND_LINE_LABEL, + tabContent); + + gd = new GridData(); + gd.horizontalSpan = 1; + gd.horizontalAlignment = GridData.FILL; + gd.grabExcessHorizontalSpace = true; + gd.horizontalIndent = 14; + commandLineFieldEditor.getTextControl(tabContent).setLayoutData(gd); + addField(commandLineFieldEditor); + + controlDecorations.add(createHelpDecoration(commandLineFieldEditor, tabContent, runnerCommandLineHelp)); + String url = runnerDefinition.getHomePageURL(); + fileFieldEditor.setLinkURL(url); + + // Field: illegalOpcodesVisible + composite = SWTFactory.createComposite(tabContent, 2, 3, GridData.FILL_HORIZONTAL); + FieldEditor booleanFieldEditor = new BooleanFieldEditor( + AssemblerPreferencesConstants + .getCompilerRunnerWaitForCompletionName(compilerId, hardware, runnerId), + Texts.PREFERENCES_COMPILER_RUNNER_WAIT_FOR_COMPLETION_LABEL, composite); + + addField(booleanFieldEditor); + + tabItem.setControl(tabContent); + + if (runnerId.equals(activeRunnerId)) { + selectedTabItem = tabItem; + } + + } + + // Default to selected tab item. + if (selectedTabItem != null) { + tabFolder.setSelection(selectedTabItem); + } + } + + private ControlDecoration createHelpDecoration(StringFieldEditor parametersFieldEditor, Composite tabContent, + String text) { + if (parametersFieldEditor == null) { + throw new IllegalArgumentException("Parameter 'parametersFieldEditor' must not be null."); + } + if (tabContent == null) { + throw new IllegalArgumentException("Parameter 'tabContent' must not be null."); + } + if (text == null) { + throw new IllegalArgumentException("Parameter 'text' must not be null."); + } + Text textControl = parametersFieldEditor.getTextControl(tabContent); + ControlDecoration controlDecoration = new ControlDecoration(textControl, SWT.LEFT | SWT.CENTER); + controlDecoration.hide(); + controlDecoration.setShowHover(true); + controlDecoration.setDescriptionText(text); + + controlDecoration.setImage(AssemblerPlugin.getInstance().getImage("help-16x16.gif")); + + return controlDecoration; + } + + /** + * {@inheritDoc} + */ + @Override + public final boolean performOk() { + if (super.performOk()) { + saveChanges(); + return true; + } + return false; + } + + /** + * The field editor preference page implementation of a + * <code>PreferencePage</code> method loads all the field editors with their + * default values except for the executable paths. + */ + @Override + protected final void performDefaults() { + + super.performDefaults(); + + } + + @Override + public final void dispose() { + super.dispose(); + + } + + @Override + public void setVisible(boolean visible) { + super.setVisible(visible); + + if (visible) { + setTabsStatus(); + } + + } + + /** + * Saves all changes to the {@link IPreferenceStore}. + */ + private void saveChanges() { + + plugin.savePreferences(); + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesConstants.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesConstants.java new file mode 100644 index 00000000..054d5f6a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesConstants.java @@ -0,0 +1,419 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import java.util.HashSet; +import java.util.Set; + +import com.wudsn.ide.asm.Hardware; + +/** + * Constants for preferences. + * + * @author Peter Dell + */ +public final class AssemblerPreferencesConstants { + + /** + * Creation is private. + */ + private AssemblerPreferencesConstants() { + } + + /** + * Preference key for comment text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_COMMENT = "editor.text.attribute.comment"; //$NON-NLS-1$ + + /** + * Preferences key for string text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_STRING = "editor.text.attribute.string"; //$NON-NLS-1$ + + /** + * Preferences key for number text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_NUMBER = "editor.text.attribute.number"; //$NON-NLS-1$ + + /** + * Preference key for directive text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_DIRECTVE = "editor.text.attribute.directive"; //$NON-NLS-1$ + + /** + * Preference key for legal opcode text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_OPCODE_LEGAL = "editor.text.attribute.opcode.legal"; //$NON-NLS-1$ + + /** + * Preference key for illegal opcode text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_OPCODE_ILLEGAL = "editor.text.attribute.opcode.illegal"; //$NON-NLS-1$ + + /** + * Preference key for pseudo opcode text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_OPCODE_PSEUDO = "editor.text.attribute.opcode.pseudo"; //$NON-NLS-1$ + + /** + * Preference key for equate identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE = "editor.text.attribute.identifier.equate"; //$NON-NLS-1$ + + /** + * Preference key for label identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LABEL = "editor.text.attribute.identifier.label"; //$NON-NLS-1$ + + /** + * Preference key for enum identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION = "editor.text.attribute.identifier.enum"; //$NON-NLS-1$ + + /** + * Preference key for structure identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION = "editor.text.attribute.identifier.structure"; //$NON-NLS-1$ + + /** + * Preference key for local identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION = "editor.text.attribute.identifier.local"; //$NON-NLS-1$ + + /** + * Preference key for macro identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION = "editor.text.attribute.identifier.macro"; //$NON-NLS-1$ + + /** + * Preference key for procedure identifier text style. + */ + public static final String EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION = "editor.text.attribute.identifier.procedure"; //$NON-NLS-1$ + + /** + * Set of all preferences keys that depend on the global JFact text font + * setting. + */ + public static final Set<String> EDITOR_TEXT_ATTRIBUTES; + + /** + * Preference key for default case for content assist. + */ + static final String EDITOR_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE = "editor.content.assist.processor.default.case"; //$NON-NLS-1$ + + /** + * Preference key for positioning for for compiling. + * + * @since 1.6.1 + */ + static final String EDITOR_COMPILE_COMMAND_POSITIONING_MODE = "editor.compile.command.positioning.mode"; //$NON-NLS-1$ + + /** + * Static initialization. + */ + static { + EDITOR_TEXT_ATTRIBUTES = new HashSet<String>(); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_DIRECTVE); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LABEL); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_NUMBER); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_OPCODE_ILLEGAL); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_OPCODE_LEGAL); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_OPCODE_PSEUDO); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_STRING); + EDITOR_TEXT_ATTRIBUTES.add(EDITOR_TEXT_ATTRIBUTE_COMMENT); + } + + /** + * Determines if preference key name represents a setting for compiler + * opcodes visibility. + * + * @param name + * The name of the preferences key, not <code>null</code>. + * @return <code>true</code> if preference key name represents a setting for + * compiler opcodes visibility, <code>false</code> otherwise. + */ + public static boolean isCompilerCPUName(String name) { + if (name == null) { + throw new IllegalArgumentException("Parameter 'name' must not be null."); + } + boolean result = name.startsWith("compiler.") && name.endsWith(".cpu"); + return result; + } + + /** + * Gets preference key name for the compiler executable path. This is the + * only hardware independent compiler setting. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * + * @return The preference key name for the compiler executable path, not + * empty and not <code>null</code>. + */ + static String getCompilerExecutablePathName(String compilerId) { + + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + return "compiler." + compilerId + ".executable.path"; //$NON-NLS-1$ + } + + /** + * Gets preference key name for the compiler CPU visibility. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * + * @return The preference key name for the compiler CPU, not empty and not + * <code>null</code>. + */ + static String getCompilerCPUName(String compilerId, Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".cpu"; //$NON-NLS-1$ + } + + /** + * Gets preference key name for the compiler parameters. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * + * @return The preference key name for the compiler parameters, not empty + * and not <code>null</code>. + */ + static String getCompilerParametersName(String compilerId, Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".default.parameters"; //$NON-NLS-1$ + } + + /** + * Gets preference key name for the compiler output folder mode. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * + * @return The preference key name for the compiler output folder mode, not + * empty and not <code>null</code>. + */ + static String getCompilerOutputFolderModeName(String compilerId, Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".output.folder.mode"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name for the compiler output folder path. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * + * @return The preference key name for the compiler output folder path, not + * empty and not <code>null</code>. + */ + static String getCompilerOutputFolderPathName(String compilerId, Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".output.folder.path"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name for the compiler output file extension. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * + * @return The preference key name for the compiler output file extension, + * not empty and not <code>null</code>. + */ + static String getCompilerOutputFileExtensionName(String compilerId, Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".output.file.extension"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name for the runner to run the output file. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * + * @return The preference key name for the for the runner to run the output + * file, not empty and not <code>null</code>. + */ + static String getCompilerRunnerIdName(String compilerId, Hardware hardware) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".runner.id"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name for the runner executable path. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * @param runnerId + * The runner id, not <code>null</code>. + * + * @return The preference key name for the runner executable path, not empty + * and not <code>null</code>. + */ + static String getCompilerRunnerExecutablePathName(String compilerId, Hardware hardware, String runnerId) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".runner." + runnerId + ".executable.path"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name for the runner command line. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * @param runnerId + * The runner id, not <code>null</code>. + * + * @return The preference key name for the runner command line, not empty + * and not <code>null</code>. + */ + static String getCompilerRunnerCommandLineName(String compilerId, Hardware hardware, String runnerId) { + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".runner." + runnerId + ".parameters"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name for the runner wait for completion flag. + * + * @param compilerId + * The compiler id, not <code>null</code>. + * @param hardware + * The hardware, not <code>null</code>. + * @param runnerId + * The runner id, not <code>null</code>. + * + * @return The preference key name for the runner command line, not empty + * and not <code>null</code>. + * @since 1.6.1 + */ + static String getCompilerRunnerWaitForCompletionName(String compilerId, Hardware hardware, String runnerId) { + + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + return getCompilerHardwarePrefix(compilerId, hardware) + ".runner." + runnerId + ".waitForCompletion"; //$NON-NLS-1$ + + } + + /** + * Gets preference key name prefix for a given hardware and compiler. + * + * @param hardware + * The hardware, not <code>null</code>. + * @param compilerId + * The compiler id, not <code>null</code>. + * + * + * @return The preference key name prefix without trailing dot, not empty + * and not <code>null</code>. + */ + private static String getCompilerHardwarePrefix(String compilerId, Hardware hardware) { + + if (compilerId == null) { + throw new IllegalArgumentException("Parameter 'compilerId' must not be null."); + } + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + return "compiler." + compilerId + "." + hardware.name().toLowerCase(); + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesInitializer.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesInitializer.java new file mode 100644 index 00000000..01ac5c54 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesInitializer.java @@ -0,0 +1,171 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import java.util.List; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.widgets.Display; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.HardwareUtility; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerOutputFolderMode; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.editor.AssemblerContentAssistProcessorDefaultCase; +import com.wudsn.ide.asm.editor.AssemblerEditorCompileCommandPositioningMode; +import com.wudsn.ide.asm.runner.RunnerId; + +/** + * Initializer for setting defaults values in the preferences. + * + * @author Peter Dell + */ +public final class AssemblerPreferencesInitializer extends + AbstractPreferenceInitializer { + + /** + * Creation must be public default. + */ + public AssemblerPreferencesInitializer() { + } + + @Override + public void initializeDefaultPreferences() { + IPreferenceStore store = AssemblerPlugin.getInstance() + .getPreferenceStore(); + + initializeEditorPreferences(store); + + initializeCompilerPreferences(store); + + AssemblerPlugin.getInstance().savePreferences(); + } + + private void initializeEditorPreferences(IPreferenceStore store) { + if (store == null) { + throw new IllegalArgumentException( + "Parameter 'store' must not be null."); + } + // Editor. + Display display = Display.getCurrent(); + + TextAttribute textAttribute = new TextAttribute(new Color(display, 0, + 128, 0), null, SWT.ITALIC); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_COMMENT, + TextAttributeConverter.toString(textAttribute)); + + textAttribute = new TextAttribute(new Color(display, 0, 0, 255), null, + SWT.NORMAL); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_STRING, + TextAttributeConverter.toString(textAttribute)); + + textAttribute = new TextAttribute(new Color(display, 0, 0, 255), null, + SWT.BOLD); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_NUMBER, + TextAttributeConverter.toString(textAttribute)); + + textAttribute = new TextAttribute(new Color(display, 128, 64, 0), null, + SWT.BOLD); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_DIRECTVE, + TextAttributeConverter.toString(textAttribute)); + + textAttribute = new TextAttribute(new Color(display, 0, 0, 128), null, + SWT.BOLD); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_LEGAL, + TextAttributeConverter.toString(textAttribute)); + + textAttribute = new TextAttribute(new Color(display, 255, 32, 32), + null, SWT.BOLD); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_ILLEGAL, + TextAttributeConverter.toString(textAttribute)); + + textAttribute = new TextAttribute(new Color(display, 32, 128, 32), + null, SWT.BOLD); + store.setDefault( + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_PSEUDO, + TextAttributeConverter.toString(textAttribute)); + + // Content assist. + store.setDefault( + AssemblerPreferencesConstants.EDITOR_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE, + AssemblerContentAssistProcessorDefaultCase.LOWER_CASE); + + // Compiling. + store.setDefault( + AssemblerPreferencesConstants.EDITOR_COMPILE_COMMAND_POSITIONING_MODE, + AssemblerEditorCompileCommandPositioningMode.FIRST_ERROR_OR_WARNING); + } + + private void initializeCompilerPreferences(IPreferenceStore store) { + if (store == null) { + throw new IllegalArgumentException( + "Parameter 'store' must not be null."); + } + + CompilerRegistry compilerRegistry = AssemblerPlugin.getInstance() + .getCompilerRegistry(); + + List<CompilerDefinition> compilerDefinitions = compilerRegistry + .getCompilerDefinitions(); + for (CompilerDefinition compilerDefinition : compilerDefinitions) { + String compilerId; + String name; + compilerId = compilerDefinition.getId(); + + for (Hardware hardware : Hardware.values()) { + if (hardware.equals(Hardware.GENERIC)) { + continue; + } + store.setDefault(AssemblerPreferencesConstants + .getCompilerCPUName(compilerId, hardware), + compilerDefinition.getSupportedCPUs().get(0).toString()); + + name = AssemblerPreferencesConstants.getCompilerParametersName( + compilerId, hardware); + store.setDefault(name, + compilerDefinition.getDefaultParameters()); + name = AssemblerPreferencesConstants + .getCompilerOutputFolderModeName(compilerId, hardware); + store.setDefault(name, CompilerOutputFolderMode.TEMP_FOLDER); + name = AssemblerPreferencesConstants + .getCompilerOutputFileExtensionName(compilerId, + hardware); + store.setDefault(name, + HardwareUtility.getDefaultFileExtension(hardware)); + name = AssemblerPreferencesConstants.getCompilerRunnerIdName( + compilerId, hardware); + store.setDefault(name, RunnerId.DEFAULT_APPLICATION); + } + + } + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesPage.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesPage.java new file mode 100644 index 00000000..d333da6c --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/AssemblerPreferencesPage.java @@ -0,0 +1,597 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.eclipse.jface.preference.ColorSelector; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.FieldEditorPreferencePage; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.RadioGroupFieldEditor; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.TabFolder; +import org.eclipse.swt.widgets.TabItem; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.model.WorkbenchViewerComparator; + +import com.wudsn.ide.asm.AssemblerPlugin; +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.asm.compiler.CompilerDefinition; +import com.wudsn.ide.asm.compiler.CompilerRegistry; +import com.wudsn.ide.asm.editor.AssemblerContentAssistProcessorDefaultCase; +import com.wudsn.ide.asm.editor.AssemblerEditor; +import com.wudsn.ide.asm.editor.AssemblerEditorCompileCommandPositioningMode; +import com.wudsn.ide.base.common.ProcessWithLogs; +import com.wudsn.ide.base.gui.SWTFactory; + +/** + * Visual editor page for the assembler preferences. + * + * @author Peter Dell + */ +public final class AssemblerPreferencesPage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage { + + private abstract class TextAttributeSelectionListener implements SelectionListener { + + /** + * Creation is public. + */ + public TextAttributeSelectionListener() { + } + + @Override + public void widgetDefaultSelected(SelectionEvent e) { + } + + @Override + public void widgetSelected(SelectionEvent e) { + TextAttributeListItem item = getTextAttributeListItem(); + if (item == null) { + throw new IllegalStateException("No item selected."); + } + updateItem(item); + textAttributeListItemsViewer.refresh(); + addChangedProperty(item.getPreferencesKey()); + } + + abstract protected void updateItem(TextAttributeListItem item); + } + + /** + * The owning plugin. + */ + private AssemblerPlugin plugin; + + /** + * The set of property names for which the value was changed since the page + * was opened. + */ + private Set<String> changedPropertyNames; + + /** + * The id of the compiler to be used as default. + */ + private String activeCompilerId; + + /** + * The list of all text attributes and the corresponding preferences keys. + */ + private String[][] textAttributeListItemKeys; + + /** + * List for text attribute items. + */ + List<TextAttributeListItem> textAttributeListItems; + + /** + * Highlighting color list viewer + */ + TableViewer textAttributeListItemsViewer; + + /** + * Color selector for foreground color. + */ + ColorSelector textAttributeForegroundColorSelector; + + /** + * Check box for bold setting. + */ + Button textAttributeBoldCheckBox; + + /** + * Check box for italic setting. + */ + Button textAttributeItalicCheckBox; + + /** + * Creation must be public default. + */ + public AssemblerPreferencesPage() { + super(GRID); + plugin = AssemblerPlugin.getInstance(); + setPreferenceStore(plugin.getPreferenceStore()); + changedPropertyNames = new TreeSet<String>(); + textAttributeListItemKeys = new String[][] { + { Texts.PREFERENCES_TEXT_ATTRIBUTE_COMMENT_NAME, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_COMMENT }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_NUMBER_NAME, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_NUMBER }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_STRING_NAME, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_STRING }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_DIRECTIVE, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_DIRECTVE }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_OPCODE_LEGAL_NAME, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_LEGAL }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_OPCODE_ILLEGAL_NAME, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_ILLEGAL }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_OPCODE_PSEUDO_NAME, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_OPCODE_PSEUDO }, + + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_EQUATE }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LABEL, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LABEL }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_ENUM_DEFINITION_SECTION }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_STRUCTURE_DEFINITION_SECTION }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_LOCAL_SECTION }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_MACRO_DEFINITION_SECTION }, + { Texts.PREFERENCES_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION, + AssemblerPreferencesConstants.EDITOR_TEXT_ATTRIBUTE_IDENTIFIER_PROCEDURE_DEFINITION_SECTION } }; + } + + @Override + public void init(IWorkbench workbench) { + IEditorPart editor = workbench.getActiveWorkbenchWindow().getActivePage().getActiveEditor(); + if (editor instanceof AssemblerEditor) { + AssemblerEditor assemblerEditor; + assemblerEditor = (AssemblerEditor) editor; + activeCompilerId = assemblerEditor.getCompilerDefinition().getId(); + + } + changedPropertyNames.clear(); + } + + @Override + public void createFieldEditors() { + + Composite parent = getFieldEditorParent(); + + parent = SWTFactory.createComposite(parent, 1, 1, GridData.FILL_BOTH); + initializeTextAttributesList(); + createSyntaxHighlightingGroup(parent); + createEditorGroup(parent); + createCompilersGroup(parent); + } + + void addChangedProperty(String key) { + if (key == null) { + throw new IllegalArgumentException("Parameter 'key' must not be null."); + } + changedPropertyNames.add(key); + } + + @Override + public void dispose() { + + disposeTextAttributesList(); + super.dispose(); + } + + /** + * Creates all visual controls. + * + * @param parent + * The parent object, not <code>null</code>. + */ + private void createSyntaxHighlightingGroup(Composite parent) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + Group group = SWTFactory.createGroup(parent, Texts.PREFERENCES_SYNTAX_HIGHLIGHTING_GROUP_TITLE, 2, 1, + GridData.FILL_HORIZONTAL); + Label label; + GridLayout layout; + GridData gd; + + textAttributeListItemsViewer = new TableViewer(group, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER + | SWT.FULL_SELECTION); + textAttributeListItemsViewer.setLabelProvider(new TextAttributeListItemProvider()); + textAttributeListItemsViewer.setContentProvider(new TextAttributeListContentProvider()); + textAttributeListItemsViewer.setComparator(new WorkbenchViewerComparator()); + gd = new GridData(SWT.BEGINNING, SWT.FILL, false, true); + gd.heightHint = convertHeightInCharsToPixels(textAttributeListItems.size()); + textAttributeListItemsViewer.getControl().setLayoutData(gd); + + Composite stylesComposite = new Composite(group, SWT.NONE); + layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 2; + stylesComposite.setLayout(layout); + stylesComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false)); + + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalAlignment = GridData.BEGINNING; + gd.horizontalSpan = 2; + + label = new Label(stylesComposite, SWT.LEFT); + label.setText(Texts.PREFERENCES_FOREGROUND_COLOR_LABEL); + gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent = 20; + label.setLayoutData(gd); + + textAttributeForegroundColorSelector = new ColorSelector(stylesComposite); + Button foregroundColorButton = textAttributeForegroundColorSelector.getButton(); + gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + foregroundColorButton.setLayoutData(gd); + + textAttributeBoldCheckBox = new Button(stylesComposite, SWT.CHECK); + textAttributeBoldCheckBox.setText(Texts.PREFERENCES_BOLD_LABEL); + gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent = 20; + gd.horizontalSpan = 2; + textAttributeBoldCheckBox.setLayoutData(gd); + + textAttributeItalicCheckBox = new Button(stylesComposite, SWT.CHECK); + textAttributeItalicCheckBox.setText(Texts.PREFERENCES_ITALIC_LABEL); + gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent = 20; + gd.horizontalSpan = 2; + textAttributeItalicCheckBox.setLayoutData(gd); + + textAttributeListItemsViewer.addSelectionChangedListener(new ISelectionChangedListener() { + @Override + public void selectionChanged(SelectionChangedEvent event) { + handleSyntaxColorListSelection(); + } + }); + + foregroundColorButton.addSelectionListener(new TextAttributeSelectionListener() { + @Override + protected void updateItem(TextAttributeListItem item) { + if (item == null) { + throw new IllegalArgumentException("Parameter 'item' must not be null."); + } + TextAttribute textAttribute = item.getTextAttribute(); + Color foreground = textAttribute.getForeground(); + foreground.dispose(); + foreground = new Color(Display.getCurrent(), textAttributeForegroundColorSelector.getColorValue()); + item.setTextAttribute(new TextAttribute(foreground, textAttribute.getBackground(), textAttribute + .getStyle(), textAttribute.getFont())); + } + + }); + + textAttributeBoldCheckBox.addSelectionListener(new TextAttributeSelectionListener() { + @Override + protected void updateItem(TextAttributeListItem item) { + if (item == null) { + throw new IllegalArgumentException("Parameter 'item' must not be null."); + } + TextAttribute textAttribute = item.getTextAttribute(); + int style = (textAttribute.getStyle() & ~SWT.BOLD) + | (textAttributeBoldCheckBox.getSelection() ? SWT.BOLD : SWT.NONE); + Font font = textAttribute.getFont(); + FontData fontData = font.getFontData()[0]; + fontData = new FontData(fontData.getName(), fontData.getHeight(), style); + font.dispose(); + font = new Font(Display.getCurrent(), fontData); + item.setTextAttribute(new TextAttribute(textAttribute.getForeground(), textAttribute.getBackground(), + style, font)); + } + }); + + textAttributeItalicCheckBox.addSelectionListener(new TextAttributeSelectionListener() { + @Override + protected void updateItem(TextAttributeListItem item) { + if (item == null) { + throw new IllegalArgumentException("Parameter 'item' must not be null."); + } + TextAttribute textAttribute = item.getTextAttribute(); + int style = (textAttribute.getStyle() & ~SWT.ITALIC) + | (textAttributeItalicCheckBox.getSelection() ? SWT.ITALIC : SWT.NONE); + Font font = textAttribute.getFont(); + FontData fontData = font.getFontData()[0]; + fontData = new FontData(fontData.getName(), fontData.getHeight(), style); + font.dispose(); + font = new Font(Display.getCurrent(), fontData); + item.setTextAttribute(new TextAttribute(textAttribute.getForeground(), textAttribute.getBackground(), + style, font)); + } + }); + + inittialzeTextAttributesListViewer(); + + parent.layout(); + + } + + /** + * FIll the list items view an set the selection to the first item. + */ + private void inittialzeTextAttributesListViewer() { + textAttributeListItemsViewer.setInput(textAttributeListItems); + textAttributeListItemsViewer + .setSelection(new StructuredSelection(textAttributeListItemsViewer.getElementAt(0))); + } + + /** + * Dispose the text attribute list. + */ + private void disposeTextAttributesList() { + if (textAttributeListItems == null) { + throw new IllegalStateException("Attribute 'textAttributeListItems' must not be null."); + } + for (TextAttributeListItem item : textAttributeListItems) { + TextAttributeConverter.dispose(item.getTextAttribute()); + } + textAttributeListItems = null; + } + + /** + * Setup the text attribute list. + */ + private void initializeTextAttributesList() { + if (textAttributeListItems != null) { + throw new IllegalStateException("Attribute 'textAttributeListItems' must be null."); + } + textAttributeListItems = new ArrayList<TextAttributeListItem>(textAttributeListItemKeys.length); + + for (int i = 0, n = textAttributeListItemKeys.length; i < n; i++) { + String data; + TextAttribute textAttribute; + TextAttributeListItem item; + + data = getPreferenceStore().getString(textAttributeListItemKeys[i][1]); + textAttribute = TextAttributeConverter.fromString(data); + + item = new TextAttributeListItem(textAttributeListItemKeys[i][0], textAttributeListItemKeys[i][1]); + item.setTextAttribute(textAttribute); + textAttributeListItems.add(item); + } + } + + private void createEditorGroup(Composite parent) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + Group group = SWTFactory.createGroup(parent, Texts.PREFERENCES_EDITOR_GROUP_TITLE, 1, 1, + GridData.FILL_HORIZONTAL); + + Composite space = SWTFactory.createComposite(group, 2, 1, GridData.FILL_HORIZONTAL); + + String[][] labelsAndValues; + labelsAndValues = new String[][] { + { + + Texts.PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LOWER_CASE_TEXT, + AssemblerContentAssistProcessorDefaultCase.LOWER_CASE }, + { + + Texts.PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_UPPER_CASE_TEXT, + AssemblerContentAssistProcessorDefaultCase.UPPER_CASE } + + }; + + FieldEditor choiceFieldEditor = new RadioGroupFieldEditor( + AssemblerPreferencesConstants.EDITOR_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE, + Texts.PREFERENCES_CONTENT_ASSIST_PROCESSOR_DEFAULT_CASE_LABEL, 2, labelsAndValues, space); + addField(choiceFieldEditor); + + labelsAndValues = new String[][] { + { + + Texts.PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_OR_WARNING_TEXT, + AssemblerEditorCompileCommandPositioningMode.FIRST_ERROR_OR_WARNING }, + { + + Texts.PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_FIRST_ERROR_TEXT, + AssemblerEditorCompileCommandPositioningMode.FIRST_ERROR } + + }; + + choiceFieldEditor = new RadioGroupFieldEditor( + AssemblerPreferencesConstants.EDITOR_COMPILE_COMMAND_POSITIONING_MODE, + Texts.PREFERENCES_COMPILE_COMMAND_POSITIONING_MODE_LABEL, 2, labelsAndValues, space); + addField(choiceFieldEditor); + + } + + private void createCompilersGroup(Composite parent) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + TabFolder tabFolder = new TabFolder(parent, SWT.NONE); + TabItem selectedTabItem = null; + + // Create the editors for all compilers of the hardware. + CompilerRegistry compilerRegistry = plugin.getCompilerRegistry(); + List<CompilerDefinition> compilerDefinitions = compilerRegistry.getCompilerDefinitions(); + + for (CompilerDefinition compilerDefinition : compilerDefinitions) { + String compilerId = compilerDefinition.getId(); + TabItem tabItem = new TabItem(tabFolder, SWT.NONE); + tabItem.setText(compilerDefinition.getName()); + + Composite tabContent; + tabContent = SWTFactory.createComposite(tabFolder, 1, 1, GridData.FILL_HORIZONTAL); + tabItem.setControl(tabContent); + + Composite composite; + + String name; + name = AssemblerPreferencesConstants.getCompilerExecutablePathName(compilerId); + + // Field: executablePath + composite = SWTFactory.createComposite(tabContent, 4, 2, GridData.FILL_HORIZONTAL); + FileFieldDownloadEditor fileFieldEditor = new FileFieldDownloadEditor(name, + Texts.PREFERENCES_COMPILER_EXECUTABLE_PATH_LABEL, composite); + fileFieldEditor.setFileExtensions(ProcessWithLogs.getExecutableExtensions()); + + addField(fileFieldEditor); + + // Set URL only after editor was added. + String url = compilerDefinition.getHomePageURL(); + fileFieldEditor.setLinkURL(url); + + if (compilerId.equals(activeCompilerId)) { + selectedTabItem = tabItem; + } + } + + // Default to selected tab item. + if (selectedTabItem != null) { + tabFolder.setSelection(selectedTabItem); + } + } + + /** + * {@inheritDoc} + * + * This method is called when "Apply" or "OK" is pressed. + */ + @Override + public boolean performOk() { + if (super.performOk()) { + saveChanges(); + plugin.firePreferencesChangeEvent(changedPropertyNames); + return true; + } + return false; + } + + /** + * The field editor preference page implementation of a + * <code>PreferencePage</code> method loads all the field editors with their + * default values. + */ + @Override + protected void performDefaults() { + + super.performDefaults(); + + IPreferenceStore preferencesStore = getPreferenceStore(); + for (int i = 0, n = textAttributeListItemKeys.length; i < n; i++) { + String key = textAttributeListItemKeys[i][1]; + preferencesStore.setValue(key, preferencesStore.getDefaultString(key)); + addChangedProperty(key); + } + + disposeTextAttributesList(); + initializeTextAttributesList(); + inittialzeTextAttributesListViewer(); + } + + /** + * Saves all changes to the {@link IPreferenceStore}. + */ + private void saveChanges() { + String data; + IPreferenceStore store = getPreferenceStore(); + + for (TextAttributeListItem listItem : textAttributeListItems) { + data = TextAttributeConverter.toString(listItem.getTextAttribute()); + store.setValue(listItem.getPreferencesKey(), data); + + } + + plugin.savePreferences(); + + } + + @Override + public void propertyChange(PropertyChangeEvent event) { + super.propertyChange(event); + if (event.getSource() instanceof FieldEditor) { + FieldEditor fieldEditor = (FieldEditor) event.getSource(); + addChangedProperty(fieldEditor.getPreferenceName()); + } + } + + /** + * Update controls after item select. + */ + void handleSyntaxColorListSelection() { + TextAttributeListItem item = getTextAttributeListItem(); + + if (item == null) { + return; + } + + Color color; + boolean bold; + boolean italic; + + TextAttribute textAttribute = item.getTextAttribute(); + color = textAttribute.getForeground(); + bold = (textAttribute.getStyle() & SWT.BOLD) == SWT.BOLD; + italic = (textAttribute.getStyle() & SWT.ITALIC) == SWT.ITALIC; + + textAttributeForegroundColorSelector.setColorValue(color.getRGB()); + textAttributeBoldCheckBox.setSelection(bold); + textAttributeItalicCheckBox.setSelection(italic); + + textAttributeForegroundColorSelector.getButton().setEnabled(true); + textAttributeBoldCheckBox.setEnabled(true); + textAttributeItalicCheckBox.setEnabled(true); + } + + /** + * Returns the current highlighting color list item. + * + * @return The current highlighting color list item or <code>null</code>. + */ + TextAttributeListItem getTextAttributeListItem() { + TextAttributeListItem listItem; + IStructuredSelection selection = (IStructuredSelection) textAttributeListItemsViewer.getSelection(); + listItem = (TextAttributeListItem) selection.getFirstElement(); + return listItem; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/CompilerPreferences.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/CompilerPreferences.java new file mode 100644 index 00000000..6dc68904 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/CompilerPreferences.java @@ -0,0 +1,262 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.CPU; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.asm.compiler.CompilerOutputFolderMode; +import com.wudsn.ide.asm.runner.RunnerId; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Facade class for typed access to the global compiler preferences for a given + * hardware. + * + * @author Peter Dell + */ +public final class CompilerPreferences { + + private AssemblerPreferences assemblerPreferences; + private Hardware hardware; + private String compilerId; + + CompilerPreferences(AssemblerPreferences assemblerPreferences, + String compilerId, Hardware hardware) { + if (assemblerPreferences == null) { + throw new IllegalArgumentException( + "Parameter 'assemblerPreferences' must not be null."); + } + + if (compilerId == null) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be null."); + } + if (StringUtility.isEmpty(compilerId)) { + throw new IllegalArgumentException( + "Parameter 'compilerId' must not be empty."); + } + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + this.assemblerPreferences = assemblerPreferences; + this.compilerId = compilerId; + this.hardware = hardware; + } + + /** + * Gets the compiler id of the compiler. + * + * @return The compiler id of the compiler, not empty and not + * <code>null</code>. + */ + public String getCompilerId() { + return compilerId; + } + + /** + * Gets the hardware for which the compiler is invoked. + * + * @return The hardware, not <code>null</code>. + * + * @since 1.6.1 + */ + public Hardware getHardware() { + return hardware; + } + + /** + * Gets the CPU for which the instructions shall be active. + * + * @return The CPU, not <code>null</code>. + * + * @since 1.6.1 + */ + public CPU getCPU() { + CPU result; + String cpuString = assemblerPreferences + .getString(AssemblerPreferencesConstants.getCompilerCPUName( + compilerId, hardware)); + + if (StringUtility.isEmpty(cpuString)) { + result = CPU.MOS6502; + } else { + result = CPU.valueOf(cpuString); + } + return result; + } + + /** + * Determines if illegal opcodes shall be highlighted and proposed. + * + * @return <code>true</code> if yet, <code>false</code> otherwise. + */ + @Deprecated + public boolean isIllegalOpcodesVisible() { + return getCPU() == CPU.MOS6502_ILLEGAL; + } + + /** + * Determines if W65816 opcodes shall be highlighted and proposed. + * + * @return <code>true</code> if yet, <code>false</code> otherwise. + */ + @Deprecated + public boolean isW65816OpcodesVisible() { + return getCPU() == CPU.MOS65816; + } + + /** + * Gets the parameters for the compiler. + * + * @return The parameters path for the compiler, may be empty, not + * <code>null</code>. + */ + public String getParameters() { + return assemblerPreferences.getString(AssemblerPreferencesConstants + .getCompilerParametersName(compilerId, hardware)); + } + + /** + * Gets the output folder mode for the compiler. + * + * @return The output folder mode for the compiler, see + * {@link CompilerOutputFolderMode}, may be empty, not + * <code>null</code>. + */ + public String getOutputFolderMode() { + + return assemblerPreferences.getString(AssemblerPreferencesConstants + .getCompilerOutputFolderModeName(compilerId, hardware)); + } + + /** + * Gets the output folder for the compiler in case the output folder mode is + * {@link CompilerOutputFolderMode#FIXED_FOLDER}. + * + * @return The output folder mode for the compiler, see + * {@link CompilerOutputFolderMode#FIXED_FOLDER}, may be empty, not + * <code>null</code>. + */ + public String getOutputFolderPath() { + + return assemblerPreferences.getString(AssemblerPreferencesConstants + .getCompilerOutputFolderPathName(compilerId, hardware)); + } + + /** + * Gets the output file extension for the compiler. + * + * @return The output file extension may be empty, not <code>null</code>. + */ + public String getOutputFileExtension() { + + return assemblerPreferences.getString(AssemblerPreferencesConstants + .getCompilerOutputFileExtensionName(compilerId, hardware)); + } + + /** + * Gets the id of the default runner to run the output file. + * + * @return The id of the runner to run the output file, not empty and not + * <code>null</code>. + */ + public String getRunnerId() { + String result = assemblerPreferences + .getString(AssemblerPreferencesConstants + .getCompilerRunnerIdName(compilerId, hardware)); + if (StringUtility.isEmpty(result)) { + result = RunnerId.DEFAULT_APPLICATION; + } + return result; + } + + /** + * Gets the executable path for the runner. + * + * @param runnerId + * The runner id, not empty and not <code>null</code>. + * + * @return The executable path for the runner, may be empty, not + * <code>null</code>. + */ + public String getRunnerExecutablePath(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be null."); + } + if (StringUtility.isEmpty(runnerId)) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be empty."); + } + return assemblerPreferences.getString(AssemblerPreferencesConstants + .getCompilerRunnerExecutablePathName(compilerId, hardware, + runnerId)); + } + + /** + * Gets the parameters for the runner. + * + * @param runnerId + * The runner id, not empty and not <code>null</code>. + * + * @return The parameters for the runner, may be empty, not + * <code>null</code>. + */ + public String getRunnerCommandLine(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be null."); + } + if (StringUtility.isEmpty(runnerId)) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be empty."); + } + return assemblerPreferences.getString(AssemblerPreferencesConstants + .getCompilerRunnerCommandLineName(compilerId, hardware, + runnerId)); + } + + /** + * Gets the wait for completion indicator for the runner. + * + * @param runnerId + * The runner id, not empty and not <code>null</code>. + * + * @return <code>true</code>if waiting for completion is requested, + * <code>false</code> otherwise. + * + * @since 1.6.1 + */ + public boolean isRunnerWaitForCompletion(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be null."); + } + if (StringUtility.isEmpty(runnerId)) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be empty."); + } + return assemblerPreferences.getBoolean(AssemblerPreferencesConstants + .getCompilerRunnerWaitForCompletionName(compilerId, hardware, + runnerId)); + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/CompilerRunPreferences.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/CompilerRunPreferences.java new file mode 100644 index 00000000..2e8fbc29 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/CompilerRunPreferences.java @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import com.wudsn.ide.asm.AssemblerProperties; +import com.wudsn.ide.asm.Hardware; +import com.wudsn.ide.base.common.StringUtility; + +/** + * Facade class to mix compiler run specific preferences into the global + * preferences. + * + * @author Peter Dell + * + */ +public final class CompilerRunPreferences { + + private CompilerPreferences compilerPreferences; + private AssemblerProperties mainSourceFileProperties; + + public CompilerRunPreferences(CompilerPreferences compilerPreferences, AssemblerProperties mainSourceFileProperties) { + if (compilerPreferences == null) { + throw new IllegalArgumentException("Parameter 'compilerPreferences' must not be null."); + } + if (mainSourceFileProperties == null) { + throw new IllegalArgumentException("Parameter 'properties' must not be null."); + } + this.compilerPreferences = compilerPreferences; + this.mainSourceFileProperties=mainSourceFileProperties; + } + + /** + * Gets the hardware for which the compiler is invoked. + * + * @return The hardware, not <code>null</code>. + * + * @since 1.6.1 + */ + public Hardware getHardware() { + return compilerPreferences.getHardware(); + } + + /** + * Gets the parameters for the compiler. + * + * @return The parameters path for the compiler, may be empty, not + * <code>null</code>. + */ + public String getParameters() { + + String result; + + result = compilerPreferences.getParameters(); + return result; + } + + /** + * Gets the id of the runner to run the output file. + * + * @return The id of the runner to run the output file, not empty and not + * <code>null</code>. + */ + public String getRunnerId() { + String result; + + result = compilerPreferences.getRunnerId(); + return result; + } + + /** + * Gets the executable path for the runner. + * + * @param runnerId + * The runner id, not empty and not <code>null</code>. + * + * @return The executable path for the runner, may be empty, not + * <code>null</code>. + */ + public String getRunnerExecutablePath(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + if (StringUtility.isEmpty(runnerId)) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be empty."); + } + + String result; + + result = compilerPreferences.getRunnerExecutablePath(runnerId); + return result; + } + + /** + * Gets the parameters for the runner. + * + * @param runnerId + * The runner id, not empty and not <code>null</code>. + * + * @return The parameters for the runner, may be empty, not + * <code>null</code>. + */ + public String getRunnerCommandLine(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + if (StringUtility.isEmpty(runnerId)) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be empty."); + } + String result; + result = compilerPreferences.getRunnerCommandLine(runnerId); + return result; + } + + /** + * Gets the wait for completion indicator for the runner. + * + * @param runnerId + * The runner id, not empty and not <code>null</code>. + * + * @return <code>true</code>if waiting for completion is requested, + * <code>false</code> otherwise. + * + * @since 1.6.1 + */ + public boolean isRunnerWaitForCompletion(String runnerId) { + if (runnerId == null) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be null."); + } + if (StringUtility.isEmpty(runnerId)) { + throw new IllegalArgumentException("Parameter 'runnerId' must not be empty."); + } + boolean result; + result = compilerPreferences.isRunnerWaitForCompletion(runnerId); + return result; + } + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/DirectoryFieldDownloadEditor.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/DirectoryFieldDownloadEditor.java new file mode 100644 index 00000000..05f1e9d1 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/DirectoryFieldDownloadEditor.java @@ -0,0 +1,137 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.preference.DirectoryFieldEditor; +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.program.Program; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Listener; + +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Extended directory field editor with build-in download link. + * + * @author Peter Dell + */ +final class DirectoryFieldDownloadEditor extends DirectoryFieldEditor { + + Link link; + + public DirectoryFieldDownloadEditor(String name, String labelText, + Composite parent) { + super(name, labelText, parent); + } + + /** + * Override the method declared in {@link FieldEditor}. + */ + @Override + public int getNumberOfControls() { + return 4; + } + + @Override + protected void doFillIntoGrid(Composite parent, int numColumns) { + super.doFillIntoGrid(parent, numColumns - 1); + if (link == null) { + link = new Link(parent, SWT.NONE); + link.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent event) { + link = null; + } + }); + + link.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + String url = event.text; + if (url != null && url.length() > 0) { + Program.launch(event.text); + } + } + }); + + } + GridData gd = new GridData(); + gd.horizontalAlignment = GridData.FILL; + link.setLayoutData(gd); + } + + @Override + protected void adjustForNumColumns(int numColumns) { + ((GridData) getTextControl().getLayoutData()).horizontalSpan = numColumns - 3; + } + + /** + * Do not reset path to default. + */ + @Override + public void loadDefault() { + } + + /** + * Do not check input as file to allow selecting ".app" directories on MacOS X. + * + * @return <code>true</code> in all cases. + */ + @Override + protected boolean checkState() { + return true; + + } + + /** + * Sets the URL for the link label. + * + * @param url + * The URL, may be empty, not <code>null</code>. + */ + public void setLinkURL(String url) { + if (link == null) { + throw new IllegalArgumentException( + "Parameter 'link' must not be null."); + } + if (url == null) { + throw new IllegalArgumentException( + "Parameter 'url' must not be null."); + } + + if (url.length() > 0) { + link.setText("<a href=\"" + url + "\">" + + Texts.PREFERENCES_DOWNLOAD_LINK + "</a>"); + link.setToolTipText(TextUtility.format( + Texts.PREFERENCES_DOWNLOAD_LINK_TOOL_TIP, url)); + + } else { + link.setText(""); + link.setToolTipText(""); + } + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/FileFieldDownloadEditor.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/FileFieldDownloadEditor.java new file mode 100644 index 00000000..bfc8cc7a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/FileFieldDownloadEditor.java @@ -0,0 +1,137 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.preference.FieldEditor; +import org.eclipse.jface.preference.FileFieldEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.program.Program; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Listener; + +import com.wudsn.ide.asm.Texts; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Extended file field editor with build-in download link. + * + * @author Peter Dell + */ +final class FileFieldDownloadEditor extends FileFieldEditor { + + Link link; + + public FileFieldDownloadEditor(String name, String labelText, + Composite parent) { + super(name, labelText, parent); + } + + /** + * Override the method declared in {@link FieldEditor}. + */ + @Override + public int getNumberOfControls() { + return 4; + } + + @Override + protected void doFillIntoGrid(Composite parent, int numColumns) { + super.doFillIntoGrid(parent, numColumns - 1); + if (link == null) { + link = new Link(parent, SWT.NONE); + link.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent event) { + link = null; + } + }); + + link.addListener(SWT.Selection, new Listener() { + @Override + public void handleEvent(Event event) { + String url = event.text; + if (url != null && url.length() > 0) { + Program.launch(event.text); + } + } + }); + + } + GridData gd = new GridData(); + gd.horizontalAlignment = GridData.FILL; + link.setLayoutData(gd); + } + + @Override + protected void adjustForNumColumns(int numColumns) { + ((GridData) getTextControl().getLayoutData()).horizontalSpan = numColumns - 3; + } + + /** + * Do not reset path to default. + */ + @Override + public void loadDefault() { + } + + /** + * Do not check input as file to allow selecting ".app" directories on MacOS X. + * + * @return <code>true</code> in all cases. + */ + @Override + protected boolean checkState() { + return true; + + } + + /** + * Sets the URL for the link label. + * + * @param url + * The URL, may be empty, not <code>null</code>. + */ + public void setLinkURL(String url) { + if (link == null) { + throw new IllegalArgumentException( + "Parameter 'link' must not be null."); + } + if (url == null) { + throw new IllegalArgumentException( + "Parameter 'url' must not be null."); + } + + if (url.length() > 0) { + link.setText("<a href=\"" + url + "\">" + + Texts.PREFERENCES_DOWNLOAD_LINK + "</a>"); + link.setToolTipText(TextUtility.format( + Texts.PREFERENCES_DOWNLOAD_LINK_TOOL_TIP, url)); + + } else { + link.setText(""); + link.setToolTipText(""); + } + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeConverter.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeConverter.java new file mode 100644 index 00000000..187a9432 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeConverter.java @@ -0,0 +1,162 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.widgets.Display; + +import com.wudsn.ide.base.common.StringUtility; + +/** + * Convertdf go connvert the class TextAttributes into a PreferenceStore + * compatible form. + * + * @author Peter Dell + * @author Daniel Mitte + */ +public final class TextAttributeConverter { + + /** + * Converts preferences string to a color value. + * + * @param value + * The color as an RGB string of the for "r, g, b", may be empty + * or <code>null</code>. + * + * @return The color, not <code>null</code>. + */ + public static TextAttribute fromString(String value) { + + TextAttribute result; + Display display = Display.getCurrent(); + Color foregroundColor; + Color backgroundColor; + int style; + if (value != null) { + + String[] data = value.split(","); + + try { + int r, g, b; + + if (StringUtility.isEmpty(data[0] + data[1] + data[2])) { + foregroundColor = null; + } else { + r = new Integer(data[0]).intValue(); + g = new Integer(data[1]).intValue(); + b = new Integer(data[2]).intValue(); + foregroundColor = new Color(display, r, g, b); + } + if (StringUtility.isEmpty(data[3] + data[4] + data[5])) { + backgroundColor = null; + } else { + r = new Integer(data[3]).intValue(); + g = new Integer(data[4]).intValue(); + b = new Integer(data[5]).intValue(); + backgroundColor = new Color(display, r, g, b); + } + style = b = new Integer(data[6]).intValue(); + } catch (Exception ex) { + foregroundColor = new Color(display, 0, 0, 0); + backgroundColor = null; + style = SWT.NORMAL; + } + } else { + foregroundColor = new Color(display, 0, 0, 0); + backgroundColor = null; + style = SWT.NORMAL; + } + Font font = JFaceResources.getTextFont(); + FontData fontData = font.getFontData()[0]; + fontData = new FontData(fontData.getName(), fontData.getHeight(), style); + font = new Font(display, fontData); + result = new TextAttribute(foregroundColor, backgroundColor, style, + font); + return result; + } + + /** + * Converts a text attribute to a string, except for the font. + * + * @param textAttribute + * The text attribute, not <code>null</code>. + * @return The string, not <code>null</code>. + */ + public static String toString(TextAttribute textAttribute) { + if (textAttribute == null) { + throw new IllegalArgumentException( + "Parameter 'textAttribute' must not be null."); + } + + String result; + result = toString(textAttribute.getForeground()) + "," + + toString(textAttribute.getBackground()) + "," + + Integer.toString(textAttribute.getStyle()); + return result; + } + + /** + * Converts a color to a comma separated RGB string. + * + * @param color + * The color, may be <code>null</code>. + * @return The comma separated RGB string, not <code>null</code>. + */ + private static String toString(Color color) { + String result; + if (color == null) { + result = ",,"; + } else { + String red = Integer.toString(color.getRed()); + String green = Integer.toString(color.getGreen()); + String blue = Integer.toString(color.getBlue()); + result = red + "," + green + "," + blue; + } + return result; + } + + /** + * Dispose the colors and the font of the text attribute created by this class. + * + * @param textAttribute + * The text attribute or <code>null</code>. + * + * @since 1.6.0 + */ + public static void dispose(TextAttribute textAttribute) { + if (textAttribute != null) { + if (textAttribute.getForeground() != null) { + textAttribute.getForeground().dispose(); + } + if (textAttribute.getBackground() != null) { + textAttribute.getBackground().dispose(); + } + if (textAttribute.getFont() != null) { + textAttribute.getFont().dispose(); + } + } + + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListContentProvider.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListContentProvider.java new file mode 100644 index 00000000..556b28ce --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListContentProvider.java @@ -0,0 +1,61 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; +import java.util.List; + +/** + * List content provider. + * + * @author Peter Dell + */ +final class TextAttributeListContentProvider implements + IStructuredContentProvider { + + TextAttributeListContentProvider() { + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("unchecked") + public Object[] getElements(Object inputElement) { + return ((List<TextAttributeListItem>) inputElement).toArray(); + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() { + // Not used. + } + + /** + * {@inheritDoc} + */ + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // Not used. + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListItem.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListItem.java new file mode 100644 index 00000000..89773053 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListItem.java @@ -0,0 +1,98 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.text.TextAttribute; + +/** + * Item in the highlighting color list. + * + * @author Peter Dell + */ +final class TextAttributeListItem { + + /** Display name */ + private String displayName; + + /** Color preference key */ + private String preferencesKey; + + /** Text attribute */ + private TextAttribute textAttribute; + + TextAttributeListItem(String displayName, String preferencesKey) { + if (displayName == null) { + throw new IllegalArgumentException( + "Parameter 'displayName' must not be null."); + } + if (preferencesKey == null) { + throw new IllegalArgumentException( + "Parameter 'preferencesKey' must not be null."); + } + this.displayName = displayName; + this.preferencesKey = preferencesKey; + } + + /** + * Gets the preferences key. + * + * @return The preferences key, not empty and not <code>null</code>. + */ + public String getPreferencesKey() { + return preferencesKey; + } + + /** + * Gets the display name. + * + * @return The display name, not empty and not <code>null</code>. + */ + public String getDisplayName() { + return displayName; + } + + /** + * Sets the text attribute. + * + * @param textAttribute + * The text attribute, not <code>null</code>. + * + */ + public void setTextAttribute(TextAttribute textAttribute) { + if (textAttribute == null) { + throw new IllegalArgumentException( + "Parameter 'textAttribute' must not be null."); + } + this.textAttribute = textAttribute; + } + + /** + * Gets the text attribute. + * + * @return The text attribute, not <code>null</code>. + */ + public TextAttribute getTextAttribute() { + if (textAttribute == null) { + throw new IllegalStateException( + "Field 'textAttribute' must not be null."); + } + return textAttribute; + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListItemProvider.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListItemProvider.java new file mode 100644 index 00000000..6999038b --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/preferences/TextAttributeListItemProvider.java @@ -0,0 +1,70 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.preferences; + +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; + +/** + * Color list label provider. + * + * @author Peter Dell + */ +final class TextAttributeListItemProvider extends LabelProvider implements + IColorProvider, IFontProvider { + + TextAttributeListItemProvider() { + + } + + @Override + public String getText(Object element) { + return ((TextAttributeListItem) element).getDisplayName(); + } + + /** + * {@inheritDoc} + */ + @Override + public Color getForeground(Object element) { + return ((TextAttributeListItem) element).getTextAttribute() + .getForeground(); + } + + /** + * {@inheritDoc} + */ + @Override + public Color getBackground(Object element) { + return ((TextAttributeListItem) element).getTextAttribute() + .getBackground(); + } + + /** + * {@inheritDoc} + */ + @Override + public Font getFont(Object element) { + return ((TextAttributeListItem) element).getTextAttribute().getFont(); + } +} \ No newline at end of file diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/Runner.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/Runner.java new file mode 100644 index 00000000..d3f47280 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/Runner.java @@ -0,0 +1,111 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.runner; + +import java.io.File; + +import com.wudsn.ide.asm.compiler.CompilerFiles; +import com.wudsn.ide.asm.editor.AssemblerBreakpoint; + +/** + * Base class for runner implementations. + * + * @author Peter Dell + */ +public class Runner { + + private RunnerDefinition definition; + + /** + * Creation is protected. + */ + protected Runner() { + + } + + /** + * Sets the definition of the Runner. Called by {@link RunnerRegistry} only. + * + * @param definition + * The definition if the Runner, not <code>null</code>. + */ + final void setDefinition(RunnerDefinition definition) { + if (definition == null) { + throw new IllegalArgumentException("Parameter 'type' must not be null."); + } + this.definition = definition; + } + + /** + * Gets the definition of the Runner. + * + * @return The definition of the Runner, not <code>null</code>. + */ + public final RunnerDefinition getDefinition() { + if (definition == null) { + throw new IllegalStateException("Field 'definition' must not be null."); + } + return definition; + } + + + /** + * Creates the {@link File} object for the breakpoints file. + * + * @param files + * The assembler editor file containing the path to the output + * folder and file, not <code>null</code>. + * + * @return The file to created (if there are breakpoints) or deleted (if + * there are not), or <code>null</code> to indicate that the runner + * does no support breakpoints. + * + * @since 1.6.1 + */ + public File createBreakpointsFile(CompilerFiles files) { + if (files == null) { + throw new IllegalArgumentException("Parameter 'files' must not be null."); + } + return null; + } + + /** + * Creates the content for the breakpoints file. + * + * @param breakpoints + * The array of defined (possibly disabled) breakpoints, may be + * empty, not <code>null</code>. + * @param breakpointBuilder + * The sting builder for create the actual file content. @ + * + * @return The number of active breakpoints or <code>0</code> if no + * breakpoints are active. + * @since 1.6.1 + */ + public int createBreakpointsFileContent(AssemblerBreakpoint[] breakpoints, StringBuilder breakpointBuilder) { + if (breakpoints == null) { + throw new IllegalArgumentException("Parameter 'breakpoints' must not be null."); + } + if (breakpointBuilder == null) { + throw new IllegalArgumentException("Parameter 'breakpointBuilder' must not be null."); + } + return 0; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerDefinition.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerDefinition.java new file mode 100644 index 00000000..363ae72a --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerDefinition.java @@ -0,0 +1,245 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.runner; + +import com.wudsn.ide.asm.Hardware; + +/** + * Definition of a runner. The definition contains all static meta information + * about the runner. It is normally defined via an extension. + * + * + * For launching application under MacOS see + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=82155 and + * http://www.coderanch.com/t/111494/Mac-OS/launching-Safari-from-Java-App. + * + * @author Peter Dell + */ +public final class RunnerDefinition implements Comparable<RunnerDefinition> { + + public static final String RUNNER_EXECUTABLE_PATH = "${runnerExecutablePath}"; + public static final String OUTPUT_FILE_PATH = "${outputFilePath}"; + + // Id + private Hardware hardware; + private String id; + private String name; + + // Installation and use. + private String homePageURL; + + // Compiling. + private String defaultCommandLine; + + /** + * Creation is package local. Called by {@link RunnerRegistry} only. + */ + RunnerDefinition() { + + } + + /** + * Sets the hardware of the runner. Called by {@link RunnerRegistry} only. + * + * @param hardware + * The hardware, not <code>null</code>. + */ + final void setHardware(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + this.hardware = hardware; + } + + /** + * Gets the hardware of the runner. + * + * @return The hardware of the runner, not empty and not <code>null</code>. + */ + public final Hardware getHardware() { + if (hardware == null) { + throw new IllegalStateException( + "Field 'hardware' must not be null."); + } + return hardware; + } + + /** + * Sets the id of the runner. Called by {@link RunnerRegistry} only. The id + * is only unique together with the hardware returned by + * {@link #getHardware()}. + * + * @param id + * The id of the runner, not empty and not <code>null</code>. + */ + final void setId(String id) { + if (id == null) { + throw new IllegalArgumentException( + "Parameter 'id' must not be null."); + } + this.id = id; + } + + /** + * Gets the id of the runner. + * + * @return The id of the runner, not empty and not <code>null</code>. + */ + public final String getId() { + if (id == null) { + throw new IllegalStateException("Field 'id' must not be null."); + } + return id; + } + + /** + * Sets the name of the runner. Called by {@link RunnerRegistry} only. + * + * @param name + * The name of the runner, not empty and not <code>null</code>. + */ + final void setName(String name) { + if (name == null) { + throw new IllegalArgumentException( + "Parameter 'name' must not be null."); + } + this.name = name; + } + + /** + * Gets the name of the runner. + * + * @return The name of the runner, not empty and not <code>null</code>. + */ + public final String getName() { + if (name == null) { + throw new IllegalStateException("Field 'name' must not be null."); + } + return name; + } + + /** + * Determines if the runner allows a runner executable path to be + * configured. + * + * @return <code>true</code> if the runner allows a runner executable path + * to be configured, <code>false</code> otherwise. + */ + public final boolean isRunnerExecutablePathPossible() { + boolean result; + + result = id.equals(RunnerId.USER_DEFINED_APPLICATION) + || defaultCommandLine.contains(RUNNER_EXECUTABLE_PATH); + return result; + } + + /** + * Sets the absolute URL of the home page where the runner can be + * downloaded. Called by {@link RunnerRegistry} only. + * + * @param homePageURL + * The absolute URL of the home page where the runner can be + * downloaded. May be empty or <code>null</code>. + */ + final void setHomePageURL(String homePageURL) { + if (homePageURL == null) { + homePageURL = ""; + } + this.homePageURL = homePageURL; + } + + /** + * Gets the absolute URL of the home page where the runner can be + * downloaded. + * + * @return The absolute URL of the home page where the runner can be + * downloaded. The result may be empty, not <code>null</code>. + */ + public final String getHomePageURL() { + if (homePageURL == null) { + throw new IllegalStateException( + "Field 'homePageURL' must not be null."); + } + return homePageURL; + } + + /** + * Sets the runner default command line. Called by {@link RunnerRegistry} + * only. + * + * @param defaultCommandLine + * The runner default parameters, may be empty or + * <code>null</code>. + */ + final void setDefaultCommandLine(String defaultCommandLine) { + if (defaultCommandLine == null) { + defaultCommandLine = ""; + } + this.defaultCommandLine = defaultCommandLine; + } + + /** + * Gets the runner default command line. + * + * @return The runner default parameters, not <code>null</code>. + */ + public final String getDefaultCommandLine() { + if (defaultCommandLine == null) { + throw new IllegalStateException( + "Field 'defaultCommandLine' must not be null."); + } + return defaultCommandLine; + } + + /** + * Compare instances of this class based on their name. + * + * @param o + * The object to compare this one with, not <code>null</code>. + */ + @Override + public final int compareTo(RunnerDefinition o) { + if (o == null) { + throw new IllegalArgumentException( + "Parameter 'o' must not be null."); + } + if (name == null || o.name == null) { + throw new IllegalStateException( + "Field 'name' must not be null for this or for argument."); + + } + int result = id.compareTo(o.id); + if (result == 0) { + return 0; + } + if (id.equals(RunnerId.DEFAULT_APPLICATION)) { + return -1; + } else if (id.equals(RunnerId.USER_DEFINED_APPLICATION)) { + return +1; + } + return name.compareTo(o.name); + } + + @Override + public final String toString() { + return hardware.toString().toLowerCase() + "." + id; + } +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerId.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerId.java new file mode 100644 index 00000000..c6fedd38 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerId.java @@ -0,0 +1,40 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.runner; + +/** + * Predefined runner ids which are valid for all hardwares. + * + * @author Peter Dell + */ +public final class RunnerId { + + /** + * Creation is private. + */ + private RunnerId() { + } + + // The ID of the system default application for a file extension + public final static String DEFAULT_APPLICATION = "default_application"; + // The ID of the user defined application + public final static String USER_DEFINED_APPLICATION = "user_defined_application"; + +} diff --git a/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerRegistry.java b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerRegistry.java new file mode 100644 index 00000000..9e490345 --- /dev/null +++ b/com.wudsn.ide.asm/src/com/wudsn/ide/asm/runner/RunnerRegistry.java @@ -0,0 +1,233 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.asm.runner; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; + +import com.wudsn.ide.asm.Hardware; + +/** + * Registry for runners, based on the extension points + * {@value RunnerRegistry#RUNNERS}. + * + * @author Peter Dell + * + */ +public final class RunnerRegistry { + + /** + * The id of the extension point which provides the runners. + */ + private static final String RUNNERS = "com.wudsn.ide.asm.runners"; + + /** + * The registered runner definition. + */ + private List<RunnerDefinition> runnerDefinitionList; + + /** + * The cached map of runner instances. + */ + private Map<String, Runner> runnerMap; + + /** + * Creation is public. + */ + public RunnerRegistry() { + runnerDefinitionList = Collections.emptyList(); + runnerMap = Collections.emptyMap(); + + } + + /** + * Initializes the list of available runners. + */ + public void init() { + + runnerDefinitionList = new ArrayList<RunnerDefinition>(); + runnerMap = new TreeMap<String, Runner>(); + + IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry(); + IExtensionPoint extensionPoint = extensionRegistry + .getExtensionPoint(RUNNERS); + IExtension[] extensions = extensionPoint.getExtensions(); + + for (IExtension extension : extensions) { + IConfigurationElement[] configurationElements = extension + .getConfigurationElements(); + for (IConfigurationElement configurationElement : configurationElements) { + + RunnerDefinition runnerDefinition; + runnerDefinition = new RunnerDefinition(); + runnerDefinition.setId(configurationElement.getAttribute("id")); + runnerDefinition + .setHardware(Hardware.valueOf(configurationElement + .getAttribute("hardware"))); + runnerDefinition.setName(configurationElement + .getAttribute("name")); + runnerDefinition.setHomePageURL(configurationElement + .getAttribute("homePageURL")); + runnerDefinition.setDefaultCommandLine(configurationElement + .getAttribute("defaultCommandLine")); + + runnerDefinitionList.add(runnerDefinition); + + addRunner(configurationElement, runnerDefinition); + } + } + + runnerDefinitionList = new ArrayList<RunnerDefinition>( + runnerDefinitionList); + Collections.sort(runnerDefinitionList); + runnerDefinitionList = Collections + .unmodifiableList(runnerDefinitionList); + runnerMap = Collections.unmodifiableMap(runnerMap); + } + + /** + * Adds a new runner. + * + * @param configurationElement + * The configuration element used as class instance factory, not + * <code>null</code>. + * + * @param runnerDefinition + * The runner definition, not <code>null</code>. + */ + private void addRunner(IConfigurationElement configurationElement, + RunnerDefinition runnerDefinition) { + if (configurationElement == null) { + throw new IllegalArgumentException( + "Parameter 'configurationElement' must not be null."); + } + if (runnerDefinition == null) { + throw new IllegalArgumentException( + "Parameter 'runnerDefinition' must not be null."); + } + + String id = runnerDefinition.getHardware().toString().toLowerCase() + + "." + runnerDefinition.getId(); + + // Optionally use a specific runner implementation class. + Runner runner; + if (configurationElement.getAttribute("class") != null) { + try { + // The class loading must be delegated to the framework. + runner = (Runner) configurationElement + .createExecutableExtension("class"); + } catch (CoreException ex) { + throw new RuntimeException( + "Cannot create runner instance for id '" + id + "'.", + ex); + } + } else { + runner = new Runner(); + } + + runner.setDefinition(runnerDefinition); + runner = runnerMap.put(id, runner); + if (runner != null) { + throw new RuntimeException("Runner with id '" + + runnerDefinition.getId() + "' for hardware '" + + runnerDefinition.getHardware().toString() + + "' is already registered to class '" + + runner.getClass().getName() + "'."); + } + + } + + /** + * Gets the unmodifiable list of runner definitions, sorted by their name. + * + * @param hardware + * The hardware used for filtering, not <code>null</code>. + * + * @return The unmodifiable list of runner definitions which have the + * matching hardware or {@link Hardware#GENERIC}, sorted by their + * id, may be empty, not <code>null</code>. + */ + public List<RunnerDefinition> getDefinitions(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + + List<RunnerDefinition> result = new ArrayList<RunnerDefinition>( + runnerDefinitionList.size()); + for (RunnerDefinition runnerDefinition : runnerDefinitionList) { + if (runnerDefinition.getHardware().equals(hardware) + || runnerDefinition.getHardware().equals(Hardware.GENERIC)) { + result.add(runnerDefinition); + } + } + result = Collections.unmodifiableList(result); + return result; + } + + /** + * Gets the runner for a given id. Instances of runner are stateless + * singletons within the plugin. + * + * @param hardware + * The hardware, not <code>null</code>. + * @param runnerId + * The runner type, see {@link RunnerId}, not <code>null</code>. + * + * @return The runner, not <code>null</code>. + */ + public Runner getRunner(Hardware hardware, String runnerId) { + if (hardware == null) { + throw new IllegalArgumentException( + "Parameter 'hardware' must not be null."); + } + if (runnerId == null) { + throw new IllegalArgumentException( + "Parameter 'runnerId' must not be null."); + } + Runner result; + synchronized (runnerMap) { + + result = runnerMap.get(hardware.toString().toLowerCase() + "." + + runnerId); + if (result == null) { + result = runnerMap.get(Hardware.GENERIC.toString() + .toLowerCase() + "." + runnerId); + } + } + if (result == null) { + + throw new IllegalArgumentException("Unknown runner id '" + runnerId + + "' for hardware '" + hardware.toString() + "'."); + } + + return result; + } +} diff --git a/com.wudsn.ide.base.feature/.project b/com.wudsn.ide.base.feature/.project new file mode 100644 index 00000000..8e536f22 --- /dev/null +++ b/com.wudsn.ide.base.feature/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.wudsn.ide.base.feature</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.pde.FeatureBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.FeatureNature</nature> + </natures> +</projectDescription> diff --git a/com.wudsn.ide.base.feature/build.properties b/com.wudsn.ide.base.feature/build.properties new file mode 100644 index 00000000..5f6dacff --- /dev/null +++ b/com.wudsn.ide.base.feature/build.properties @@ -0,0 +1,3 @@ +bin.includes = feature.xml,\ + build.properties,\ + .project diff --git a/com.wudsn.ide.base.feature/feature.xml b/com.wudsn.ide.base.feature/feature.xml new file mode 100644 index 00000000..5c926ec5 --- /dev/null +++ b/com.wudsn.ide.base.feature/feature.xml @@ -0,0 +1,321 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feature + id="com.wudsn.ide.base.feature" + label="WUDSN Base Feature" + version="1.7.0.qualifier" + provider-name="WUDSN"> + + <description url="http:://www.wudsn.com"> + General IDE Enhancements for Eclipse like "Open Folder" context +menu in package explorer and "Sort" context menu in text editors +which are normally part of WUDSN IDE. This feature can be installed +in an Eclipse instance even if this instance is used to develop +WUDSN IDE or other Java code. Do not install this feature if +you plan to install WUDSN IDE itself. + </description> + + <copyright> + WUDSN IDE Copyright (C) 2009 - 2014 Peter Dell +66822 Lebach, Germany +This program is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, version 2 of the License. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. You should have +received a copy of the GNU General Public License along with +this program. If not, see http://www.gnu.org/licenses. + </copyright> + + <license url="http://www.gnu.org/licenses"> + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + </license> + + <plugin + id="com.wudsn.ide.base" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + +</feature> diff --git a/com.wudsn.ide.base/.classpath b/com.wudsn.ide.base/.classpath new file mode 100644 index 00000000..23e0cc4a --- /dev/null +++ b/com.wudsn.ide.base/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/com.wudsn.ide.base/.project b/com.wudsn.ide.base/.project new file mode 100644 index 00000000..695525d6 --- /dev/null +++ b/com.wudsn.ide.base/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.wudsn.ide.base</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.PluginNature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/com.wudsn.ide.base/.settings/org.eclipse.jdt.core.prefs b/com.wudsn.ide.base/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..54e493c0 --- /dev/null +++ b/com.wudsn.ide.base/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/com.wudsn.ide.base/META-INF/MANIFEST.MF b/com.wudsn.ide.base/META-INF/MANIFEST.MF new file mode 100644 index 00000000..2419ae4b --- /dev/null +++ b/com.wudsn.ide.base/META-INF/MANIFEST.MF @@ -0,0 +1,26 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: WUDSN IDE Base Plug-in +Bundle-SymbolicName: com.wudsn.ide.base;singleton:=true +Bundle-Version: 1.7.0.qualifier +Bundle-Activator: com.wudsn.ide.base.BasePlugin +Bundle-Localization: plugin +Bundle-Vendor: Peter Dell +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.core.resources, + org.eclipse.core.expressions, + org.eclipse.core.filesystem, + org.eclipse.jface.text, + org.eclipse.ui, + org.eclipse.ui.ide, + org.eclipse.ui.views, + org.eclipse.ui.workbench.texteditor, + org.eclipse.compare +Bundle-RequiredExecutionEnvironment: JavaSE-1.6 +Bundle-ActivationPolicy: lazy +Export-Package: com.wudsn.ide.base, + com.wudsn.ide.base.common, + com.wudsn.ide.base.editor, + com.wudsn.ide.base.editor.hex, + com.wudsn.ide.base.gui +Import-Package: org.eclipse.ui.internal.editors.text diff --git a/com.wudsn.ide.base/bin/.gitignore b/com.wudsn.ide.base/bin/.gitignore new file mode 100644 index 00000000..43e58b99 --- /dev/null +++ b/com.wudsn.ide.base/bin/.gitignore @@ -0,0 +1 @@ +/com diff --git a/com.wudsn.ide.base/build.properties b/com.wudsn.ide.base/build.properties new file mode 100644 index 00000000..0720079e --- /dev/null +++ b/com.wudsn.ide.base/build.properties @@ -0,0 +1,14 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + icons/,\ + plugin.properties,\ + plugin_de_DE.properties,\ + plugin.xml,\ + fonts/,\ + src/,\ + build.properties,\ + .project,\ + .classpath + diff --git a/com.wudsn.ide.base/fonts/atari8/AtariClassic-Regular.ttf b/com.wudsn.ide.base/fonts/atari8/AtariClassic-Regular.ttf new file mode 100644 index 00000000..073c065b Binary files /dev/null and b/com.wudsn.ide.base/fonts/atari8/AtariClassic-Regular.ttf differ diff --git a/com.wudsn.ide.base/fonts/atari8/AtariClassicInt-Regular.ttf b/com.wudsn.ide.base/fonts/atari8/AtariClassicInt-Regular.ttf new file mode 100644 index 00000000..3c5b788f Binary files /dev/null and b/com.wudsn.ide.base/fonts/atari8/AtariClassicInt-Regular.ttf differ diff --git a/com.wudsn.ide.base/fonts/atari8/ReadMe.rtf b/com.wudsn.ide.base/fonts/atari8/ReadMe.rtf new file mode 100644 index 00000000..a36354c3 --- /dev/null +++ b/com.wudsn.ide.base/fonts/atari8/ReadMe.rtf @@ -0,0 +1,317 @@ +{\rtf1\ansi\deff0\deftab720{\fonttbl{\f0\fswiss MS Sans Serif;}{\f1\froman\fcharset2 Symbol;}{\f2\fmodern\fprq1 Atari Classic Chunky;}{\f3\froman Times New Roman;}{\f4\fswiss\fprq2 Arial;}} +{\colortbl\red0\green0\blue0;\red0\green0\blue255;} +\deflang1033\pard\plain\f2\fs24\cf1 Atari Classic TrueType Fonts \plain\f2\fs24\cf0 +\par \plain\f4\fs16\cf0 (Windows Version 1.1) +\par Created by Mark Simonson (v.1.0-1998, v.1.1-2001) +\par marksim@bitstream.net +\par Website: Mac/Atari Fusion--Atari Home Computer Resources for Mac Users +\par http://www2.bitstream.net/~marksim/atarimac/ +\par Macintosh version also available. +\par +\par With these fonts installed, you can view and print Atari text files in any text editor that allows you to change fonts (WordPad, for example). Tip: In order to get the correct line breaks, you will need to change the ATASCII return character (155) to the DOS LF character. (In the Character Map accessory, the ATASCII return is the blank character that comes just before the inverse up-arrow.) +\par +\par There are three different fonts. \plain\f4\fs16\cf0\b Atari Classic Chunky \plain\f4\fs16\cf0 is a pixel-for-pixel copy of the original ATASCII character set. \plain\f4\fs16\cf0\b Atari Classic Smooth \plain\f4\fs16\cf0 interprets the pixel aliasing (stair steps) as diagonal lines. \plain\f4\fs16\cf0\b Atari Classic Extrasmooth \plain\f4\fs16\cf0 refines this idea further with the addition of curves. \plain\f4\fs16\cf0\b Smooth\plain\f4\fs16\cf0 and \plain\f4\fs16\cf0\b Extrasmooth\plain\f4\fs16\cf0 were designed for better appearance and legibility at larger sizes and on print-outs. Use the one that looks best to you. +\par +\par These fonts will tend to look uneven at font sizes that do not correspond to the 8-by-8 pixel grid that the characters are based on. Because Windows assumes 96ppi screen resolution, they will look best in a font size that is a multiple of 6 (i.e., 6pt, 12pt, 18pt, etc.). (In Windows, 6 points = 8 pixels.) +\par +\par The Atari Classic TrueType fonts duplicate the ATASCII character set on a low-level basis. Unlike a normal Windows font, ATASCII utilizes all character codes from $00 to $FF (0 to 255). The lower half are normal characters; the upper half are inverse versions of the lower half. The basic ASCII characters ($00 to $7F) correspond fairly closely except for the first 32, which don't normally contain characters in a Windows font. +\par +\par Due to differences between the way Windows and the Atari use character codes, not all characters will display properly in Windows. In fact, some characters will not display at all (though they do exist in the font). Unfortunately, this is due to certain character codes being reserved in Windows and there doesn't appear to be any way to work around it. The character codes affected are: $00-$1F (0-31), $7F-$81 (127-129), $8D-$90 (141-144), $9D (157), and $9F (158). +\par +\par Not all characters can be typed from the keyboard. You can however copy characters as needed from this document (see tables below). The Character Map desk accessory can help also. +\par +\par \plain\f4\fs16\cf0\b ATASCII CHARACTER SET TABLES +\par \plain\f4\fs16\cf0 +\par In order to see the ATASCII character set with these tables, the Atari Classic TrueType fonts must be installed. Characters that are not displayed properly are due to character code usage differences between ATASCII and Windows (see above). +\par +\par +\par \plain\f4\fs16\cf0\b TABLE 1: ATASCII Character Dump Block +\par \plain\f4\fs16\cf0 +\par All characters (ATASCII $00 thru $FF) 16 characters per +\par line. +\par +\par +\par \plain\f2\fs12\cf0 \'01\'02\'03\'04\'05\'06\'07\'08\tab +\par \'0b\'0c +\par \'0e\'0f +\par \'10\'11\'12\'13\'14\'15\'16\'17\'18\'19\'1a\'1b\'1c\'1d\'1e\'1f +\par !"#$%&'()*+,-./ +\par 0123456789:;<=>? +\par @ABCDEFGHIJKLMNO +\par PQRSTUVWXYZ[\\]^_ +\par `abcdefghijklmno +\par pqrstuvwxyz\{|\}~ +\par \'80\'81\'82\'83\'84\'85\'86\'87\'88\'89\'8a\'8b\'8c\'8d\'8e\'8f +\par \'90''""\bullet \endash \emdash \'98\'99\'9a \'9c\'9d\'9e\'9f +\par \~\'a1\'a2\'a3\'a4\'a5\'a6\'a7\'a8\'a9\'aa\'ab\'ac\'ad\'ae\'af +\par \'b0\'b1\'b2\'b3\'b4\'b5\'b6\'b7\'b8\'b9\'ba\'bb\'bc\'bd\'be\'bf +\par \'c0\'c1\'c2\'c3\'c4\'c5\'c6\'c7\'c8\'c9\'ca\'cb\'cc\'cd\'ce\'cf +\par \'d0\'d1\'d2\'d3\'d4\'d5\'d6\'d7\'d8\'d9\'da\'db\'dc\'dd\'de\'df +\par \'e0\'e1\'e2\'e3\'e4\'e5\'e6\'e7\'e8\'e9\'ea\'eb\'ec\'ed\'ee\'ef +\par \'f0\'f1\'f2\'f3\'f4\'f5\'f6\'f7\'f8\'f9\'fa\'fb\'fc\'fd\'fe\'ff +\par \plain\f4\fs16\cf0 +\par +\par \plain\f4\fs16\cf0\b TABLE 2: ATASCII Character Dump List +\par \plain\f4\fs16\cf0 +\par All characters (ATASCII $00 thru $FF) one character per +\par line with hexadecimal value indicated on the left. +\par +\par \plain\f2\fs12\cf0 00= +\par 01=\'01 +\par 02=\'02 +\par 03=\'03 +\par 04=\'04 +\par 05=\'05 +\par 06=\'06 +\par 07=\'07 +\par 08=\'08 +\par 09=\tab +\par 0A= +\par +\par 0B=\'0b +\par 0C=\'0c +\par 0D= +\par 0E=\'0e +\par 0F=\'0f +\par 10=\'10 +\par 11=\'11 +\par 12=\'12 +\par 13=\'13 +\par 14=\'14 +\par 15=\'15 +\par 16=\'16 +\par 17=\'17 +\par 18=\'18 +\par 19=\'19 +\par 1A=\'1a +\par 1B=\'1b +\par 1C=\'1c +\par 1D=\'1d +\par 1E=\'1e +\par 1F=\'1f +\par 20= +\par 21=! +\par 22=" +\par 23=# +\par 24=$ +\par 25=% +\par 26=& +\par 27=' +\par 28=( +\par 29=) +\par 2A=* +\par 2B=+ +\par 2C=, +\par 2D=- +\par 2E=. +\par 2F=/ +\par 30=0 +\par 31=1 +\par 32=2 +\par 33=3 +\par 34=4 +\par 35=5 +\par 36=6 +\par 37=7 +\par 38=8 +\par 39=9 +\par 3A=: +\par 3B=; +\par 3C=< +\par 3D== +\par 3E=> +\par 3F=? +\par 40=@ +\par 41=A +\par 42=B +\par 43=C +\par 44=D +\par 45=E +\par 46=F +\par 47=G +\par 48=H +\par 49=I +\par 4A=J +\par 4B=K +\par 4C=L +\par 4D=M +\par 4E=N +\par 4F=O +\par 50=P +\par 51=Q +\par 52=R +\par 53=S +\par 54=T +\par 55=U +\par 56=V +\par 57=W +\par 58=X +\par 59=Y +\par 5A=Z +\par 5B=[ +\par 5C=\\ +\par 5D=] +\par 5E=^ +\par 5F=_ +\par 60=` +\par 61=a +\par 62=b +\par 63=c +\par 64=d +\par 65=e +\par 66=f +\par 67=g +\par 68=h +\par 69=i +\par 6A=j +\par 6B=k +\par 6C=l +\par 6D=m +\par 6E=n +\par 6F=o +\par 70=p +\par 71=q +\par 72=r +\par 73=s +\par 74=t +\par 75=u +\par 76=v +\par 77=w +\par 78=x +\par 79=y +\par 7A=z +\par 7B=\{ +\par 7C=| +\par 7D=\} +\par 7E=~ +\par 7F= +\par 80=\'80 +\par 81=\'81 +\par 82=\'82 +\par 83=\'83 +\par 84=\'84 +\par 85=\'85 +\par 86=\'86 +\par 87=\'87 +\par 88=\'88 +\par 89=\'89 +\par 8A=\'8a +\par 8B=\'8b +\par 8C=\'8c +\par 8D=\'8d +\par 8E=\'8e +\par 8F=\'8f +\par 90=\'90 +\par 91=' +\par 92=' +\par 93=" +\par 94=" +\par 95=\bullet +\par 96=\endash +\par 97=\emdash +\par 98=\'98 +\par 99=\'99 +\par 9A=\'9a +\par 9B= +\par 9C=\'9c +\par 9D=\'9d +\par 9E=\'9e +\par 9F=\'9f +\par A0=\~ +\par A1=\'a1 +\par A2=\'a2 +\par A3=\'a3 +\par A4=\'a4 +\par A5=\'a5 +\par A6=\'a6 +\par A7=\'a7 +\par A8=\'a8 +\par A9=\'a9 +\par AA=\'aa +\par AB=\'ab +\par AC=\'ac +\par AD=\'ad +\par AE=\'ae +\par AF=\'af +\par B0=\'b0 +\par B1=\'b1 +\par B2=\'b2 +\par B3=\'b3 +\par B4=\'b4 +\par B5=\'b5 +\par B6=\'b6 +\par B7=\'b7 +\par B8=\'b8 +\par B9=\'b9 +\par BA=\'ba +\par BB=\'bb +\par BC=\'bc +\par BD=\'bd +\par BE=\'be +\par BF=\'bf +\par C0=\'c0 +\par C1=\'c1 +\par C2=\'c2 +\par C3=\'c3 +\par C4=\'c4 +\par C5=\'c5 +\par C6=\'c6 +\par C7=\'c7 +\par C8=\'c8 +\par C9=\'c9 +\par CA=\'ca +\par CB=\'cb +\par CC=\'cc +\par CD=\'cd +\par CE=\'ce +\par CF=\'cf +\par D0=\'d0 +\par D1=\'d1 +\par D2=\'d2 +\par D3=\'d3 +\par D4=\'d4 +\par D5=\'d5 +\par D6=\'d6 +\par D7=\'d7 +\par D8=\'d8 +\par D9=\'d9 +\par DA=\'da +\par DB=\'db +\par DC=\'dc +\par DD=\'dd +\par DE=\'de +\par DF=\'df +\par E0=\'e0 +\par E1=\'e1 +\par E2=\'e2 +\par E3=\'e3 +\par E4=\'e4 +\par E5=\'e5 +\par E6=\'e6 +\par E7=\'e7 +\par E8=\'e8 +\par E9=\'e9 +\par EA=\'ea +\par EB=\'eb +\par EC=\'ec +\par ED=\'ed +\par EE=\'ee +\par EF=\'ef +\par F0=\'f0 +\par F1=\'f1 +\par F2=\'f2 +\par F3=\'f3 +\par F4=\'f4 +\par F5=\'f5 +\par F6=\'f6 +\par F7=\'f7 +\par F8=\'f8 +\par F9=\'f9 +\par FA=\'fa +\par FB=\'fb +\par FC=\'fc +\par FD=\'fd +\par FE=\'fe +\par FF=\'ff +\par } +� \ No newline at end of file diff --git a/com.wudsn.ide.base/fonts/c64/C64Classic-Regular.ttf b/com.wudsn.ide.base/fonts/c64/C64Classic-Regular.ttf new file mode 100644 index 00000000..1583ded8 Binary files /dev/null and b/com.wudsn.ide.base/fonts/c64/C64Classic-Regular.ttf differ diff --git a/com.wudsn.ide.base/icons/copy-disabled.gif b/com.wudsn.ide.base/icons/copy-disabled.gif new file mode 100644 index 00000000..98626a81 Binary files /dev/null and b/com.wudsn.ide.base/icons/copy-disabled.gif differ diff --git a/com.wudsn.ide.base/icons/copy-enabled.gif b/com.wudsn.ide.base/icons/copy-enabled.gif new file mode 100644 index 00000000..71d7c95a Binary files /dev/null and b/com.wudsn.ide.base/icons/copy-enabled.gif differ diff --git a/com.wudsn.ide.base/icons/cut-disabled.gif b/com.wudsn.ide.base/icons/cut-disabled.gif new file mode 100644 index 00000000..bd431447 Binary files /dev/null and b/com.wudsn.ide.base/icons/cut-disabled.gif differ diff --git a/com.wudsn.ide.base/icons/cut-enabled.gif b/com.wudsn.ide.base/icons/cut-enabled.gif new file mode 100644 index 00000000..d044e597 Binary files /dev/null and b/com.wudsn.ide.base/icons/cut-enabled.gif differ diff --git a/com.wudsn.ide.base/icons/delete-disabled.gif b/com.wudsn.ide.base/icons/delete-disabled.gif new file mode 100644 index 00000000..9e142981 Binary files /dev/null and b/com.wudsn.ide.base/icons/delete-disabled.gif differ diff --git a/com.wudsn.ide.base/icons/hex-editor-16x16.gif b/com.wudsn.ide.base/icons/hex-editor-16x16.gif new file mode 100644 index 00000000..a8c58eba Binary files /dev/null and b/com.wudsn.ide.base/icons/hex-editor-16x16.gif differ diff --git a/com.wudsn.ide.base/icons/hex-editor-segment-16x16.gif b/com.wudsn.ide.base/icons/hex-editor-segment-16x16.gif new file mode 100644 index 00000000..29887165 Binary files /dev/null and b/com.wudsn.ide.base/icons/hex-editor-segment-16x16.gif differ diff --git a/com.wudsn.ide.base/icons/import-disabled.gif b/com.wudsn.ide.base/icons/import-disabled.gif new file mode 100644 index 00000000..cd447418 Binary files /dev/null and b/com.wudsn.ide.base/icons/import-disabled.gif differ diff --git a/com.wudsn.ide.base/icons/import-enabled.gif b/com.wudsn.ide.base/icons/import-enabled.gif new file mode 100644 index 00000000..d38085ad Binary files /dev/null and b/com.wudsn.ide.base/icons/import-enabled.gif differ diff --git a/com.wudsn.ide.base/icons/paste-disabled.gif b/com.wudsn.ide.base/icons/paste-disabled.gif new file mode 100644 index 00000000..1c7668db Binary files /dev/null and b/com.wudsn.ide.base/icons/paste-disabled.gif differ diff --git a/com.wudsn.ide.base/icons/paste-enabled.gif b/com.wudsn.ide.base/icons/paste-enabled.gif new file mode 100644 index 00000000..39dd4d9d Binary files /dev/null and b/com.wudsn.ide.base/icons/paste-enabled.gif differ diff --git a/com.wudsn.ide.base/icons/save-as-disabled.gif b/com.wudsn.ide.base/icons/save-as-disabled.gif new file mode 100644 index 00000000..df83a548 Binary files /dev/null and b/com.wudsn.ide.base/icons/save-as-disabled.gif differ diff --git a/com.wudsn.ide.base/icons/save-as-enabled.gif b/com.wudsn.ide.base/icons/save-as-enabled.gif new file mode 100644 index 00000000..466bfb11 Binary files /dev/null and b/com.wudsn.ide.base/icons/save-as-enabled.gif differ diff --git a/com.wudsn.ide.base/plugin.properties b/com.wudsn.ide.base/plugin.properties new file mode 100644 index 00000000..e4dc0b6f --- /dev/null +++ b/com.wudsn.ide.base/plugin.properties @@ -0,0 +1,58 @@ +com.wudsn.ide.base.editor.CommonOpenFolderCommand.name=Open Folder + +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.label=Convert +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.mnemonic=C +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand.name=To Decimal Values +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand.mnemonic=D +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand.name=To Hexadecimal Values +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand.mnemonic=H +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand.name=To Binary Values +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand.mnemonic=B + +com.wudsn.ide.base.editor.text.TextEditorSortMenu.label=Sort +com.wudsn.ide.base.editor.text.TextEditorSortMenu.mnemonic=S +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand.name=Case Sensitively +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand.mnemonic=C +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand.name=Case Sensitively Without Duplicates +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand.mnemonic=D +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand.name=Case Insensitively +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand.mnemonic=I +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand.name=Case Insensitively Without Duplicates +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand.mnemonic=W +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand.name=Numerically +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand.mnemonic=N +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand.name=Numerically Without Duplicates +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand.mnemonic=u +com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand.name=Reverse +com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand.mnemonic=R + +com.wudsn.ide.base.editor.BinaryFile.name=Binary File + +com.wudsn.ide.base.editor.hex.HexEditor.name=Hex Editor +com.wudsn.ide.base.editor.hex.HexEditorOpenCommand.name=Open With Hex Editor +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand.name=Copy +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsMenu.name=Copy as +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand.name=Copy as Decimal Values +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand.name=Copy as Decimal Values in Block Format +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand.name=Copy as Hexadecimal Values +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand.name=Copy as ASCII String +com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand.name=Paste +com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand.name=Save Selection as... + +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.BINARY=Binary +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_COM_FILE=Atari COM File +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_DISK_IMAGE=Atari Disk Image +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_DISK_IMAGE_K_FILE=Atari Disk Image (k-File) +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_MADS_FILE=Atari MADS File +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_SDX_FILE=Atari SpartaDOS X File +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_SAP_FILE=Atari SAP File +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.C64_PRG_FILE=C64 PRG File + +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ASCII=ASCII +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_ATASCII=Atari ATASCII +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_ATASCII_SCREEN_CODE=Atari ATASCII Screen Code +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_INTERNATIONAL=Atari International +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_INTERNATIONAL_SCREEN_CODE=Atari International Screen Code +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.C64_PETSCII_UPPER_CASE=C64 PETSCII Upper Case +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.C64_PETSCII_LOWER_CASE=C64 PETSCII Lower Case + diff --git a/com.wudsn.ide.base/plugin.xml b/com.wudsn.ide.base/plugin.xml new file mode 100644 index 00000000..2d6cb3e8 --- /dev/null +++ b/com.wudsn.ide.base/plugin.xml @@ -0,0 +1,632 @@ +<?xml version="1.0" encoding="UTF-8"?> +<?eclipse version="3.0"?> +<plugin> + <extension + name="CommonCommands" + point="org.eclipse.ui.commands"> + <command + id="com.wudsn.ide.base.editor.CommonOpenFolderCommand" + name="%com.wudsn.ide.base.editor.CommonOpenFolderCommand.name"> + </command> + </extension> + <extension + name="CommonHandlers" + point="org.eclipse.ui.handlers"> + <handler + class="com.wudsn.ide.base.editor.CommonOpenFolderCommandHandler" + commandId="com.wudsn.ide.base.editor.CommonOpenFolderCommand"> + </handler> + </extension> + <extension + point="org.eclipse.core.expressions.propertyTesters"> + <propertyTester + class="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand$EnabledPropertyTester" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand.EnabledPropertyTester" + namespace="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand" + properties="isEnabled" + type="org.eclipse.jface.viewers.ISelection"> + </propertyTester> + </extension> + <extension + point="org.eclipse.core.expressions.definitions"> + <definition + id="com.wudsn.ide.base.text.TextEditorActive"> + <with + variable="activeEditor"> + <instanceof + value="org.eclipse.ui.texteditor.ITextEditor"> + </instanceof> + </with> + </definition> + + </extension> + <extension + name="TextEditorCommands" + point="org.eclipse.ui.commands"> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand.name"> + </command> + <command + categoryId="org.eclipse.ui.category.edit" + id="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand" + name="%com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand.name"> + </command> + </extension> + <extension + name="TextEditorHandlers" + point="org.eclipse.ui.handlers"> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand$Handler" + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand"> + <activeWhen> + <and> + <with + variable="selection"> + <test + forcePluginActivation="false" + property="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand.isEnabled" + value="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand"> + </test> + </with> + </and> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand$Handler" + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand"> + <activeWhen> + <with + variable="selection"> + <test + forcePluginActivation="false" + property="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand.isEnabled" + value="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand"> + </test> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand$Handler" + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand"> + <activeWhen> + <with + variable="selection"> + <test + forcePluginActivation="false" + property="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersCommand.isEnabled" + value="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand"> + </test> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommandHandler" + commandId="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand"> + <activeWhen> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </activeWhen> + </handler> + </extension> + <extension + name="TextEditorMenus" + point="org.eclipse.ui.menus"> + <menuContribution + locationURI="popup:#TextEditorContext?after=additions"> + <menu + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu" + label="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.label"> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand" + style="push"> + </command> + <visibleWhen + checkEnabled="false"> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </visibleWhen> + </menu> + </menuContribution> + <menuContribution + locationURI="menu:edit?after=additions"> + <menu + label="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.label" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.mnemonic"> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand" + id="com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand.mnemonic" + style="push"> + </command> + <visibleWhen + checkEnabled="false"> + <reference + definitionId="com.wudsn.ide.base.text.TextEditorActive"> + </reference> + </visibleWhen> + </menu> + </menuContribution> + <menuContribution + locationURI="popup:#TextEditorContext?after=additions"> + <menu + id="com.wudsn.ide.base.editor.text.TextEditorSortMenu" + label="%com.wudsn.ide.base.editor.text.TextEditorSortMenu.label"> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommandHandler" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicates" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand" + style="push"> + </command> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <instanceof + value="org.eclipse.ui.texteditor.ITextEditor"> + </instanceof> + </with> + </visibleWhen> + </menu> + </menuContribution> + <menuContribution + locationURI="menu:edit?after=additions"> + <menu + label="%com.wudsn.ide.base.editor.text.TextEditorSortMenu.label" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortMenu.mnemonic"> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand.mnemonic" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand" + id="com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand" + mnemonic="%com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand.mnemonic" + style="push"> + </command> + <visibleWhen + checkEnabled="false"> + <with + variable="activeEditor"> + <instanceof + value="org.eclipse.ui.texteditor.ITextEditor"> + </instanceof> + </with> + </visibleWhen> + </menu> + </menuContribution> + </extension> + <extension + name="HexEditorCommands" + point="org.eclipse.ui.commands"> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorOpenCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorOpenCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand.name"> + </command> + <command + id="com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand" + name="%com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand.name"> + </command> + </extension> + <extension + name="HexEditorHandlers" + point="org.eclipse.ui.handlers"> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorOpenCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorOpenCommand"> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorClipboardCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand"> + <activeWhen> + <with + variable="activeMenuSelection"> + <instanceof + value="com.wudsn.ide.base.editor.hex.HexEditorSelection"> + </instanceof> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorClipboardCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand"> + <activeWhen> + <with + variable="activeMenuSelection"> + <instanceof + value="com.wudsn.ide.base.editor.hex.HexEditorSelection"> + </instanceof> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorClipboardCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand"> + <activeWhen> + <with + variable="activeMenuSelection"> + <instanceof + value="com.wudsn.ide.base.editor.hex.HexEditorSelection"> + </instanceof> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorClipboardCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand"> + <activeWhen> + <with + variable="activeMenuSelection"> + <instanceof + value="com.wudsn.ide.base.editor.hex.HexEditorSelection"> + </instanceof> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorClipboardCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand"> + <activeWhen> + <with + variable="activeMenuSelection"> + <instanceof + value="com.wudsn.ide.base.editor.hex.HexEditorSelection"> + </instanceof> + </with> + </activeWhen> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorClipboardCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand"> + </handler> + <handler + class="com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommandHandler" + commandId="com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand"> + <activeWhen> + <with + variable="activeMenuSelection"> + <instanceof + value="com.wudsn.ide.base.editor.hex.HexEditorSelection"> + </instanceof> + </with> + </activeWhen> + </handler> + </extension> + <extension + name="HexEditorMenus" + point="org.eclipse.ui.menus"> + <menuContribution + locationURI="popup:org.eclipse.ui.popup.any?after=group.open"> + <command + commandId="com.wudsn.ide.base.editor.CommonOpenFolderCommand" + id="com.wudsn.ide.base.editor.CommonOpenFolderCommand" + style="push"> + <visibleWhen + checkEnabled="false"> + <or> + <with + variable="activeMenuSelection"> + <and> + <iterate + ifEmpty="false" + operator="and"> + </iterate> + <instanceof + value="org.eclipse.jface.viewers.IStructuredSelection"> + </instanceof> + </and> + </with> + <with + variable="activeMenuEditorInput"> + <and> + <iterate + ifEmpty="false" + operator="and"> + </iterate> + <instanceof + value="org.eclipse.jface.viewers.IStructuredSelection"> + </instanceof> + </and> + </with> + </or> + </visibleWhen> + </command> + </menuContribution> + <menuContribution + locationURI="popup:org.eclipse.ui.popup.any?before=group.openWith"> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorOpenCommand" + id="com.wudsn.ide.base.editor.hex.HexEditorOpenCommand" + style="push"> + <visibleWhen + checkEnabled="false"> + <or> + <with + variable="activeMenuSelection"> + <iterate + ifEmpty="false" + operator="or"> + <instanceof + value="org.eclipse.core.resources.IFile"> + </instanceof> + </iterate> + </with> + <with + variable="activeMenuEditorInput"> + <iterate + ifEmpty="false" + operator="or"> + <instanceof + value="org.eclipse.ui.IFileEditorInput"> + </instanceof> + </iterate> + </with> + </or> + </visibleWhen> + </command> + </menuContribution> + <menuContribution + locationURI="popup:#HexEditorContext"> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand" + disabledIcon="icons/copy-disabled.gif" + icon="icons/copy-enabled.gif" + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand" + style="push"> + </command> + <menu + label="%com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsMenu.name"> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand" + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand" + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand" + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand" + id="com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand" + style="push"> + </command> + </menu> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand" + icon="icons/paste-enabled.gif" + id="%com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand.name" + label="%com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand.name" + style="push"> + </command> + <command + commandId="com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand" + disabledIcon="icons/save-as-disabled.gif" + icon="icons/save-as-enabled.gif" + label="%com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand.name" + style="push"> + </command> + </menuContribution> + </extension> + + <extension + name="HexEditor" + point="org.eclipse.ui.editors"> + <editor + class="com.wudsn.ide.base.editor.hex.HexEditor" + default="true" + icon="icons/hex-editor-16x16.gif" + id="com.wudsn.ide.base.editor.hex.HexEditor" + name="%com.wudsn.ide.base.editor.hex.HexEditor.name"> + </editor> + </extension> + </plugin> diff --git a/com.wudsn.ide.base/plugin_de_DE.properties b/com.wudsn.ide.base/plugin_de_DE.properties new file mode 100644 index 00000000..8e0503b7 --- /dev/null +++ b/com.wudsn.ide.base/plugin_de_DE.properties @@ -0,0 +1,58 @@ +com.wudsn.ide.base.editor.CommonOpenFolderCommand.name=Ordner öffnen + +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.label=Konvertieren +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersMenu.mnemonic=K +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand.name=In dezimale Werte +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand.mnemonic=d +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand.name=In hexadezimale Werte +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand.mnemonic=h +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand.name=In binäre Werte +com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand.mnemonic=b + +com.wudsn.ide.base.editor.text.TextEditorSortMenu.label=Sortieren +com.wudsn.ide.base.editor.text.TextEditorSortMenu.mnemonic=S +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand.name=Mit Groß-/Kleinschreibung +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveCommand.mnemonic=M +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand.name=Mit Groß-/Kleinschreibung ohne Duplikate +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseSensitiveWithoutDuplicatesCommand.mnemonic=D +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand.name=Ohne Groß-/Kleinschreibung +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveCommand.mnemonic=O +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand.name=Ohne Groß-/Kleinschreibung ohne Duplikate +com.wudsn.ide.base.editor.text.TextEditorSortLinesCaseInsensitiveWithoutDuplicatesCommand.mnemonic=u +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand.name=Numerisch +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericCommand.mnemonic=N +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand.name=Numerisch ohne Duplikate +com.wudsn.ide.base.editor.text.TextEditorSortLinesNumericWithoutDuplicatesCommand.mnemonic=m +com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand.name=Umkehren +com.wudsn.ide.base.editor.text.TextEditorReverseLinesCommand.mnemonic=U + +com.wudsn.ide.base.editor.BinaryFile.name=Binär-Datei + +com.wudsn.ide.base.editor.hex.HexEditor.name=Hex Editor +com.wudsn.ide.base.editor.hex.HexEditorOpenCommand.name=Öffnen mit Hex Editor +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand.name=Kopieren +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsMenu.name=Kopieren als +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand.name=Kopieren als dezimale Werte +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand.name=Kopieren als dezimale Werte im Blocksatz +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand.name=Kopieren als hexadezimale Werte +com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand.name=Kopieren als ASCII Text +com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand.name=Einfügen +com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand.name=Auswahl Speichern unter... + +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.BINARY=Binär +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_COM_FILE=Atari COM-Datei +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_DISK_IMAGE=Atari Disk Image +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_DISK_IMAGE_K_FILE=Atari Disk Image (k-Datei) +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_MADS_FILE=Atari MADS-Datei +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_SAP_FILE=Atari SAP-Datei +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.ATARI_SDX_FILE=Atari SpartaDOS X-Datei +com.wudsn.ide.base.editor.hex.HexEditorFileContentMode.C64_PRG_FILE=C64 PRG-Datei + +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ASCII=ASCII +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_ATASCII=Atari ATASCII +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_ATASCII_SCREEN_CODE=Atari ATASCII Bildschirmcode +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_INTERNATIONAL=Atari International +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.ATARI_INTERNATIONAL_SCREEN_CODE=Atari International Bildschirmcode +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.C64_PETSCII_UPPER_CASE=C64 PETSCII Großbuchstaben +com.wudsn.ide.base.editor.hex.HexEditorCharacterSet.C64_PETSCII_LOWER_CASE=C64 PETSCII Kleinbuchstaben + diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/BasePlugin.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/BasePlugin.java new file mode 100644 index 00000000..cbbecb92 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/BasePlugin.java @@ -0,0 +1,87 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base; + +import org.osgi.framework.BundleContext; + +import com.wudsn.ide.base.common.AbstractIDEPlugin; + +/** + * The activator class controls the plug-in life cycle + */ +public final class BasePlugin extends AbstractIDEPlugin { + + /** + * The plugin id. + */ + public static final String ID = "com.wudsn.ide.base"; + + /** + * The shared instance. + */ + private static BasePlugin plugin; + + /** + * Creates a new instance. Must be public for dynamic instantiation. + */ + public BasePlugin() { + } + + /** + * {@inheritDoc} + */ + @Override + protected String getPluginId() { + return ID; + } + + /** + * {@inheritDoc} + */ + @Override + public void start(BundleContext context) throws Exception { + if (context != null) { + super.start(context); + } + plugin = this; + } + + /** + * {@inheritDoc} + */ + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Gets the shared plugin instance + * + * @return The plug-in, not <code>null</code>. + */ + public static BasePlugin getInstance() { + if (plugin == null) { + throw new IllegalStateException("Plugin not initialized or already stopped"); + } + return plugin; + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts.java new file mode 100644 index 00000000..32df3a54 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts.java @@ -0,0 +1,116 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base; + +import org.eclipse.osgi.util.NLS; + +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.editor.hex.HexEditor; + +/** + * Class which holds the localized text constants. + * + * @author Peter Dell + */ +public final class Texts extends NLS { + + /** + * Common texts. + */ + public static String DIALOG_TITLE; + public static String FILE_PATH_FIELD_BROWSE_BUTTON_LABEL; + public static String FILE_PATH_FIELD_DIALOG_MESSAGE; + + /** + * Hex editor + */ + public static String HEX_EDITOR_FILE_SIZE; + + public static String HEX_EDITOR_ATARI_COM_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_COM_BLOCK_HEADER_PARAMETERS; + public static String HEX_EDITOR_ATARI_COM_BLOCK_ERROR; + + public static String HEX_EDITOR_ATARI_DISK_IMAGE_HEADER; + public static String HEX_EDITOR_ATARI_SECTOR_HEADER; + public static String HEX_EDITOR_ATARI_SECTOR_HEADER_PARAMETERS; + public static String HEX_EDITOR_ATARI_SECTOR_ERROR; + + public static String HEX_EDITOR_ATARI_MADS_RELOC_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_MADS_UPDATE_RELOC_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_MADS_UPDATE_SYMBOLS_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOLS_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOL_HEADER; + public static String HEX_EDITOR_ATARI_MADS_BLOCK_ERROR; + + public static String HEX_EDITOR_ATARI_SAP_FILE_HEADER; + + public static String HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER_PARAMETERS; + public static String HEX_EDITOR_ATARI_SDX_RELOC_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_SDX_UPDATE_RELOC_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_SDX_UPDATE_SYMBOLS_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_SDX_DEFINE_SYMBOLS_BLOCK_HEADER; + public static String HEX_EDITOR_ATARI_SDX_BLOCK_ERROR; + + public static String HEX_EDITOR_C64_PRG_HEADER; + public static String HEX_EDITOR_C64_PRG_HEADER_PARAMETERS; + public static String HEX_EDITOR_C64_PRG_ERROR; + + public static String HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_LABEL; + public static String HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_TEXT; + public static String HEX_EDITOR_FILE_CONTENT_MODE_FIELD_LABEL; + public static String HEX_EDITOR_CHARACTER_SET_TYPE_FIELD_LABEL; + public static String HEX_EDITOR_BYTES_PER_ROW_FIELD_LABEL; + + public static String HEX_EDITOR_SAVE_SELECTION_AS_DIALOG_TITLE; + + /** + * Messages for {@link FileUtility}. + */ + public static String MESSAGE_E200; + public static String MESSAGE_E201; + public static String MESSAGE_E202; + public static String MESSAGE_E203; + public static String MESSAGE_E204; + public static String MESSAGE_E205; + public static String MESSAGE_E206; + public static String MESSAGE_E207; + public static String MESSAGE_E208; + public static String MESSAGE_E209; + public static String MESSAGE_E210; + public static String MESSAGE_E211; + public static String MESSAGE_E212; + public static String MESSAGE_E213; + + /** + * Message for the {@link HexEditor} + */ + public static String MESSAGE_E300; + public static String MESSAGE_E301; + public static String MESSAGE_I302; + public static String MESSAGE_I303; + + /** + * Initializes the constants. + */ + static { + NLS.initializeMessages(Texts.class.getName(), Texts.class); + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts.properties b/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts.properties new file mode 100644 index 00000000..3e48edc4 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts.properties @@ -0,0 +1,63 @@ +DIALOG_TITLE=WUDSN IDE Dialog +FILE_PATH_FIELD_BROWSE_BUTTON_LABEL=Browse... +FILE_PATH_FIELD_DIALOG_MESSAGE=Select {0} + +HEX_EDITOR_FILE_SIZE=File contains ${0} ({1}) bytes. + +HEX_EDITOR_ATARI_COM_BLOCK_HEADER=COM block +HEX_EDITOR_ATARI_COM_BLOCK_HEADER_PARAMETERS={0}-{1} ({2}) +HEX_EDITOR_ATARI_COM_BLOCK_ERROR=COM file structure error + +HEX_EDITOR_ATARI_DISK_IMAGE_HEADER=ATR header +HEX_EDITOR_ATARI_SECTOR_HEADER=Sector +HEX_EDITOR_ATARI_SECTOR_HEADER_PARAMETERS=({2}) +HEX_EDITOR_ATARI_SECTOR_ERROR=Sector structure error + +HEX_EDITOR_ATARI_MADS_RELOC_BLOCK_HEADER=MADS RELOC block {0}-{1} {2} +HEX_EDITOR_ATARI_MADS_UPDATE_RELOC_BLOCK_HEADER=MADS UPDATE RELOC block {0} {1} +HEX_EDITOR_ATARI_MADS_UPDATE_SYMBOLS_BLOCK_HEADER=MADS UPDATE SYMBOLS block {0} {1} +HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOLS_BLOCK_HEADER=MADS DEFINE SYMBOLS block {0} +HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOL_HEADER=MADS DEFINE SYMBOL {0} {1} {2} {3} +HEX_EDITOR_ATARI_MADS_BLOCK_ERROR=MADS file structure error + +HEX_EDITOR_ATARI_SAP_FILE_HEADER=SAP Header + +HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER=SDX NON-RELOC block +HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER_PARAMETERS={0}-{1} ({2}) +HEX_EDITOR_ATARI_SDX_RELOC_BLOCK_HEADER=SDX RELOC block {0} {1} {2} {3} +HEX_EDITOR_ATARI_SDX_UPDATE_RELOC_BLOCK_HEADER=SDX UPDATE RELOC block {0} {1} +HEX_EDITOR_ATARI_SDX_UPDATE_SYMBOLS_BLOCK_HEADER=SDX UPDATE SYMBOLS block {0} {1} +HEX_EDITOR_ATARI_SDX_DEFINE_SYMBOLS_BLOCK_HEADER=SDX DEFINE SYMBOLS block {0} {1} {2} +HEX_EDITOR_ATARI_SDX_BLOCK_ERROR=SDX file structure error + +HEX_EDITOR_C64_PRG_HEADER=Program +HEX_EDITOR_C64_PRG_HEADER_PARAMETERS={0}-{1} ({2}) +HEX_EDITOR_C64_PRG_ERROR=PRG file structure error + +HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_LABEL=File Size +HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_TEXT=${0} ({1}) bytes +HEX_EDITOR_FILE_CONTENT_MODE_FIELD_LABEL=File Mode +HEX_EDITOR_CHARACTER_SET_TYPE_FIELD_LABEL=Character Set +HEX_EDITOR_BYTES_PER_ROW_FIELD_LABEL=Bytes per Row + +HEX_EDITOR_SAVE_SELECTION_AS_DIALOG_TITLE=Save ${0} ({1}) bytes as... + +MESSAGE_E200=Folder '{0}' does not exist. +MESSAGE_E201='{0}' is no folder but a file. +MESSAGE_E202=Cannot create folder '{0}'. +MESSAGE_E203=File '{0}' does not exist. +MESSAGE_E204='{0}' is no file but a folder. +MESSAGE_E205=Cannot open file '{0}' for reading. {1} +MESSAGE_E206=Cannot read content of file '{0}'. +MESSAGE_E207=Content of file '{0}' exceeds the specified maximum size of {1} bytes. +MESSAGE_E208=Content of file '{0}' exceeds the specified maximum size of {1} characters. +MESSAGE_E209=Cannot close input stream of file '{0}'. +MESSAGE_E210=Cannot create file '{0}'. +MESSAGE_E211=Cannot open file '{0}' for writing. +MESSAGE_E212=Cannot write content of file '{0}'. +MESSAGE_E213=Cannot close output stream of file '{0}'. + +MESSAGE_E300=File content cannot be interpreted as '{0}'. +MESSAGE_E301=File of type '{0}' is corrupted; check the last section of the file. +MESSAGE_I302=${0} ({1}) bytes copied to clipboard. +MESSAGE_I303=${0} ({1}) bytes saved as '{2}'. \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts_de_DE.properties b/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts_de_DE.properties new file mode 100644 index 00000000..9362106d --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/Texts_de_DE.properties @@ -0,0 +1,63 @@ +DIALOG_TITLE=WUDSN IDE Dialog +FILE_PATH_FIELD_BROWSE_BUTTON_LABEL=Durchsuchen... +FILE_PATH_FIELD_DIALOG_MESSAGE={0} auswählen + +HEX_EDITOR_FILE_SIZE=Datei enhält {0} ({1}) Bytes. + +HEX_EDITOR_ATARI_COM_BLOCK_HEADER=COM Block +HEX_EDITOR_ATARI_COM_BLOCK_HEADER_PARAMETERS={0}-{1} ({2}) +HEX_EDITOR_ATARI_COM_BLOCK_ERROR=COM Dateistruktur defekt + +HEX_EDITOR_ATARI_DISK_IMAGE_HEADER=ATR Header +HEX_EDITOR_ATARI_SECTOR_HEADER=Sektor +HEX_EDITOR_ATARI_SECTOR_HEADER_PARAMETERS=({2}) +HEX_EDITOR_ATARI_SECTOR_ERROR=Sektorfehler + +HEX_EDITOR_ATARI_MADS_RELOC_BLOCK_HEADER=MADS RELOC Block {0}-{1} {2} +HEX_EDITOR_ATARI_MADS_UPDATE_RELOC_BLOCK_HEADER=MADS UPDATE RELOC Block {0} {1} +HEX_EDITOR_ATARI_MADS_UPDATE_SYMBOLS_BLOCK_HEADER=MADS UPDATE SYMBOLS Block {0} {1} +HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOLS_BLOCK_HEADER=MADS DEFINE SYMBOLS Block {0} +HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOL_HEADER=MADS DEFINE SYMBOL {0} {1} {2} {3} +HEX_EDITOR_ATARI_MADS_BLOCK_ERROR=MADS Dateistruktur defekt + +HEX_EDITOR_ATARI_SAP_FILE_HEADER=SAP Header + +HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER=SDX NON-RELOC block +HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER_PARAMETERS={0}-{1} ({2}) +HEX_EDITOR_ATARI_SDX_RELOC_BLOCK_HEADER=SDX NON-RELOC Block {0} {1} {2} {3} +HEX_EDITOR_ATARI_SDX_UPDATE_RELOC_BLOCK_HEADER=SDX UPDATE RELOC Block {0} {1} +HEX_EDITOR_ATARI_SDX_UPDATE_SYMBOLS_BLOCK_HEADER=SDX UPDATE SYMBOLS Block {0} {1} +HEX_EDITOR_ATARI_SDX_DEFINE_SYMBOLS_BLOCK_HEADER=SDX DEFINE SYMBOLS Block {0} {1} {2} +HEX_EDITOR_ATARI_SDX_BLOCK_ERROR=SDX Dateistruktur defekt + +HEX_EDITOR_C64_PRG_HEADER=Programm +HEX_EDITOR_C64_PRG_HEADER_PARAMETERS={0}-{1} ({2}) +HEX_EDITOR_C64_PRG_ERROR=PRG Dateistruktur defekt + +HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_LABEL=Dateigröße +HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_TEXT=${0} ({1}) Bytes +HEX_EDITOR_FILE_CONTENT_MODE_FIELD_LABEL=Datei-Modus +HEX_EDITOR_CHARACTER_SET_TYPE_FIELD_LABEL=Zeichensatz +HEX_EDITOR_BYTES_PER_ROW_FIELD_LABEL=Bytes pro Zeile + +HEX_EDITOR_SAVE_SELECTION_AS_DIALOG_TITLE=Speichere ${0} ({1}) Bytes unter... + +MESSAGE_E200=Ordner '{0}' existiert nicht. +MESSAGE_E201='{0}' ist kein Ordner sondern eine Datei. +MESSAGE_E202=Ordner '{0}' kann nicht erstellt werden. +MESSAGE_E203=Datei '{0}' existiert nicht. +MESSAGE_E204='{0}' ist keine Datei sondern ein Ordner. +MESSAGE_E205=Datei '{0}' kann nicht zum Lesen geöffnet werden. +MESSAGE_E206=Inhalt der Datei '{0}' kann nicht gelesen werden. +MESSAGE_E207=Inhalt der Datei '{0}' überschreitet die angegebene Maximalgröße von {1} Bytes. +MESSAGE_E208=Inhalt der Datei '{0}' überschreitet die angegebene Maximalgröße von {1} Zeichen. +MESSAGE_E209=Eingabendatenstrom der Datei '{0}' kann geschlossen werden. +MESSAGE_E210=Datei '{0}' kann nicht erstellt werden. +MESSAGE_E211=Datei '{0}' kann nicht zum Schreiben geöffnet werden. +MESSAGE_E212=Inhalt der Datei '{0}' kann nicht geschrieben werden. +MESSAGE_E213=Ausgabedatenstrom der Datei '{0}' kann geschlossen werden. + +MESSAGE_E300=Inhalt der Datei kann nicht als '{0}' interpretiert werden. +MESSAGE_E301=Datei vom Typ '{0}' ist korrupt; überprüfen Sie den letzten Abschnitt der Datei +MESSAGE_I302=${0} ({1}) Bytes in die Zwischenablage kopiert. +MESSAGE_I303=${0} ({1}) Bytes gespeichert unter '{2}'. \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/AbstractIDEPlugin.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/AbstractIDEPlugin.java new file mode 100644 index 00000000..c8f7eea5 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/AbstractIDEPlugin.java @@ -0,0 +1,293 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.dialogs.ErrorDialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; +import org.osgi.service.prefs.BackingStoreException; + +import com.wudsn.ide.base.Texts; + +/** + * The plugin base class for IDE plugins. + * + * @author Peter Dell + */ +public abstract class AbstractIDEPlugin extends AbstractUIPlugin { + + private Map<String, Image> images; + + /** + * Creates a new instance. Must be public for dynamic instantiation. + */ + protected AbstractIDEPlugin() { + images = new HashMap<String, Image>(); + + } + + /** + * Gets the plugin id. + * + * @return The plugin id, not empty and not <code>null</code>. + */ + protected abstract String getPluginId(); + + /** + * {@inheritDoc} + */ + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + } + + /** + * {@inheritDoc} + */ + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + } + + /** + * Logs an info message to the plugin log and the standard output stream. + * + * @param message + * The message, not <code>null</code>. + * @param parameters + * The message parameters, may be empty or <code>null</code>. + */ + public final void log(String message, Object[] parameters) { + if (message == null) { + throw new IllegalArgumentException( + "Parameter 'message' must not be null."); + } + message = format(message, parameters); + getLog().log( + new Status(IStatus.INFO, getPluginId(), IStatus.OK, message, + null)); +// System.out.println(message); + } + + /** + * Logs an error message and an exception to the plugin log and the standard + * error stream. + * + * @param message + * The message, not <code>null</code>. + * @param parameters + * The message parameters, may be empty or <code>null</code>. + * @param th + * The throwable or <code>null</code>. + */ + public final void logError(String message, Object[] parameters, Throwable th) { + if (message == null) { + throw new IllegalArgumentException( + "Parameter 'message' must not be null."); + } + + message = format(message, parameters); + if (th != null) { + message = message + "\n" + th.getMessage(); + } + getLog().log( + new Status(IStatus.ERROR, getPluginId(), IStatus.ERROR, + message, th)); + // System.err.println(message); + // if (th != null) { + // th.printStackTrace(System.err); + // } + // System.err.flush(); + + } + + private String format(String message, Object... parameters) { + if (parameters == null) { + parameters = new String[0]; + } + String[] stringParameters = new String[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + Object parameter = parameters[i]; + String stringParameter; + if (parameter == null) { + stringParameter = "null"; + } else { + stringParameter = parameter.toString(); + } + stringParameters[i] = stringParameter; + } + message = TextUtility.format(message, stringParameters); + return message; + } + + /** + * Shows error message and an exception as error dialog and logs them to the + * plugin log and the standard error stream. + * + * @param shell + * The shell, not <code>null</code>. + * + * @param message + * The message, not <code>null</code>. + * @param th + * The throwable, not <code>null</code>. + */ + public final void showError(Shell shell, String message, Throwable th) { + if (shell == null) { + throw new IllegalArgumentException( + "Parameter 'shell' must not be null."); + } + if (message == null) { + throw new IllegalArgumentException( + "Parameter 'message' must not be null."); + } + if (th == null) { + throw new IllegalArgumentException( + "Parameter 'th' must not be null."); + } + + Status status = new Status(IStatus.ERROR, getPluginId(), message); + + ErrorDialog + .openError( + shell, + Texts.DIALOG_TITLE, + th.getClass().getName() + + ": " + + th.getMessage() + + "\nCheck the .log file in the .metadata folder of the workspace for details.", + status); + logError(message, null, th); + } + + public final void savePreferences() { + String pluginId; + pluginId = getPluginId(); + try { + InstanceScope.INSTANCE.getNode(pluginId).flush(); + } catch (BackingStoreException ex) { + + throw new RuntimeException("Cannot store preferences for plugin '" + + pluginId + "'", ex); + } + } + + /** + * Gets the image for the specified plug-in relative path. + * + * @param path + * The plug-in relative path, not <code>null</code>. + * + * @return The image, not <code>null</code>. + */ + public final Image getImage(String path) { + if (path == null) { + throw new IllegalArgumentException( + "Parameter 'path' must not be null."); + } + Image result; + synchronized (images) { + result = images.get(path); + if (result == null) { + + ImageDescriptor imageDescriptor; + imageDescriptor = getImageDescriptor(path); + if (imageDescriptor == null) { + throw new RuntimeException("Image '" + path + + "' not found."); + } + result = imageDescriptor.createImage(); + images.put(path, result); + } + } + + return result; + + } + + /** + * Gets the image for the specified plug-in relative path. + * + * @param path + * The plug-in relative path, not <code>null</code>. + * + * @return The image descriptor or <code>null</code> if no image resource + * was found. + */ + public final ImageDescriptor getImageDescriptor(String path) { + ImageDescriptor imageDescriptor; + imageDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin( + getPluginId(), "icons/" + path); + return imageDescriptor; + } + + /** + * Gets the absolute path of a entry from the plugin's directory. + * + * @param fileName + * The name of a file or directory, no compound name like + * "dir1\dir2" or "dir1\file1", not <code>null</code>. + * + * @return The absolute path of the file or directory in the plugin's + * directory, may be empty, not <code>null</code>. + */ + public final String getFilePathFromPlugin(String fileName) { + if (fileName == null) { + throw new IllegalArgumentException( + "Parameter 'entry' must not be null."); + } + URL url = null; + IPath path = null; + String result = ""; + + Enumeration<URL> enu = getBundle().findEntries("/", fileName, true); + if (enu != null && enu.hasMoreElements()) { + url = enu.nextElement(); + } + + if (url == null) { + return ""; + } + + try { + path = new Path(FileLocator.toFileURL(url).getPath()); + result = path.makeAbsolute().toOSString(); + } catch (Exception ex) { + result = ""; + } + + return result; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ByteArrayUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ByteArrayUtility.java new file mode 100644 index 00000000..50438e95 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ByteArrayUtility.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +/** + * Utility class to handle byte arrays. + */ +public final class ByteArrayUtility { + + public static final int KB = 1024; + public static final int MB = KB * KB; + + public static final int MASK_FF = 0xff; +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ClassPathUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ClassPathUtility.java new file mode 100644 index 00000000..261c7305 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ClassPathUtility.java @@ -0,0 +1,97 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; + +public final class ClassPathUtility { + + /** + * Find the URI of the ".jar" file containing the resource files. + * + * @return The URI of the ".jar" file containing the resource files or + * <code>null</code>. + */ + public static URI getJarURI() { + // Try to load simple menu from folder where the .jar file is located + ClassLoader classLoader = ClassPathUtility.class.getClassLoader(); + String jarPath = ClassPathUtility.class.getName().replace('.', '/') + ".class"; + URL url = classLoader.getResource(jarPath); + if (url == null) { + url = ClassLoader.getSystemResource(jarPath); + } + if (url == null) { + return null; + } + try { + URI uri = url.toURI(); + + // Convert "jar:file:/..." to file URI. + if (uri.getScheme().equals("jar")) { + String uriString = uri.getRawSchemeSpecificPart(); + int index = uriString.lastIndexOf("!"); + if (index > 0) { + uriString = uriString.substring(0, index); + uri = new URI(uriString); + return uri; + } + } + + } catch (URISyntaxException ex) { + throw new RuntimeException("Error when resolving URL '" + url + "'."); + } + return null; + + } + + /** + * The ".jar" file containing the resource files. + * + * @return The ".jar" file containing the resource files or + * <code>null</code>. + */ + public static File getJarFile() { + URI uri = getJarURI(); + + File result = null; + if (uri != null) { + result = new File(uri); + } + return result; + } + + /** + * The folder of the ".jar" file containing the resource files. + * + * @return The folder of the ".jar" file containing the resource files or + * <code>null</code>. + */ + public static File getJarFolder() { + + File result = getJarFile(); + if (result != null) { + result = result.getParentFile(); + } + return result; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/EnumUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/EnumUtility.java new file mode 100644 index 00000000..2e44efaa --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/EnumUtility.java @@ -0,0 +1,69 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Utility class for enums. + * + * @author Peter Dell + */ + +public final class EnumUtility { + + /** + * Gets the localized text for an enum value. + * + * @param enumValue + * The enum value, not <code>null</code>. + * @return The localized text, may be empty, not <code>null</code>. + */ + public static String getText(Enum<?> enumValue) { + if (enumValue == null) { + throw new IllegalArgumentException( + "Parameter 'enumValue' must not be null."); + } + + String result; + + Class<?> enumClass = enumValue.getClass(); + + String key = enumClass.getName() + "." + enumValue.name(); + try { + ResourceBundle resourceBundle; + + resourceBundle = ResourceBundle.getBundle("plugin", + Locale.getDefault(), enumClass.getClassLoader()); + result = resourceBundle.getString(key); + } catch (MissingResourceException ex) { + result = enumValue.name() + " - Text missing"; + BasePlugin.getInstance().logError( + "Resource for enum value {0} is missing.", + new Object[] { key }, ex); + } + return result; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/FileUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/FileUtility.java new file mode 100644 index 00000000..83e9ed9d --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/FileUtility.java @@ -0,0 +1,544 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +import com.wudsn.ide.base.BasePlugin; +import com.wudsn.ide.base.Texts; + +/** + * Utility class to access files and their content. + * + * @author Peter Dell + */ +public final class FileUtility { + + /** + * Intentionally read an unlimited amount of bytes. + */ + public static final int MAX_SIZE_UNLIMITED = -1; + + /** + * Intentionally read at most 1MB of bytes or chars. + */ + public static final int MAX_SIZE_1MB = 1024 * 1024; + + /** + * Buffer size for the bytes or chars. + */ + private static final int BUFFER_SIZE = 8192; + + /** + * Creation is private, + */ + private FileUtility() { + } + + /** + * Reads the content of a file as byte array. + * + * @param ioFile + * The file, not <code>null</code>. + * @param maxSize + * The maximum number of character to read or + * {@link #MAX_SIZE_UNLIMITED}. + * @param errorOnMaxSizeExceeded + * If <code>true</code>, an error will be thrown in case the + * specified maximum number of bytes is exceeded. If + * <code>false</code>, the content will be truncated to the + * specified maximum size (plus the internal buffer size). + * + * @return The content of the file, may be empty, not <code>null</code>. + * + * @throws CoreException + * If the file does not exist or cannot be read. + */ + public static byte[] readBytes(File ioFile, long maxSize, boolean errorOnMaxSizeExceeded) throws CoreException { + InputStream inputStream; + String filePath; + + if (ioFile == null) { + throw new IllegalArgumentException("Parameter 'ioFile' must not be null."); + } + + filePath = ioFile.getAbsolutePath(); + try { + inputStream = new FileInputStream(ioFile); + } catch (FileNotFoundException ex) { + // ERROR: Cannot open file '{0}' for reading. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E205, + filePath, ex.getMessage()), ex)); + } + return readBytes(filePath, inputStream, maxSize, errorOnMaxSizeExceeded); + + } + + /** + * Reads the content of a file as byte array. + * + * @param iFile + * The file, not <code>null</code>. + * @param maxSize + * The maximum number of character to read or + * {@link #MAX_SIZE_UNLIMITED}. + * @param errorOnMaxSizeExceeded + * If <code>true</code>, an error will be thrown in case the + * specified maximum number of bytes is exceeded. If + * <code>false</code>, the content will be truncated to the + * specified maximum size (plus the internal buffer size). + * + * @return The content of the file, may be empty, not <code>null</code>. + * + * @throws CoreException + * If the file does not exist or cannot be read. + */ + public static byte[] readBytes(IFile iFile, long maxSize, boolean errorOnMaxSizeExceeded) throws CoreException { + InputStream inputStream; + String filePath; + + if (iFile == null) { + throw new IllegalArgumentException("Parameter 'iFile' must not be null."); + } + + filePath = iFile.getFullPath().toString(); + try { + inputStream = iFile.getContents(); + } catch (CoreException ex) { + // ERROR: Cannot open file '{0}' for reading. {1} + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E205, + filePath, ex.getMessage()), ex)); + } + + return readBytes(filePath, inputStream, maxSize, errorOnMaxSizeExceeded); + + } + + private static byte[] readBytes(String filePath, InputStream inputStream, long maxSize, + boolean errrorOnMaxSizeExceeded) throws CoreException { + + if (filePath == null) { + throw new IllegalArgumentException("Parameter 'filePath' must not be null."); + } + if (inputStream == null) { + throw new IllegalArgumentException("Parameter 'inputStream' must not be null."); + } + if (maxSize < MAX_SIZE_UNLIMITED) { + throw new IllegalArgumentException("Parameter 'maxSize' must not be less than " + MAX_SIZE_UNLIMITED + "."); + } + + byte[] result; + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + int size = 0; + byte[] buffer = new byte[BUFFER_SIZE]; + int count = 0; + + do { + count = inputStream.read(buffer); + + if (count > 0) { + bos.write(buffer, 0, count); + + size += count; + } + } while ((count > -1) && (maxSize == MAX_SIZE_UNLIMITED || size <= maxSize)); + + // Specified maximum size exceeded? + if (maxSize != MAX_SIZE_UNLIMITED && size > maxSize && errrorOnMaxSizeExceeded) { + // ERROR: Content of file '{0}' exceeds the specified maximum + // size of {1} bytes. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E207, + filePath, String.valueOf(maxSize)))); + } + bos.close(); + + result = bos.toByteArray(); + + } catch (IOException ex) { + // ERROR: Cannot read content of file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E206, + filePath), ex)); + + } finally { + try { + inputStream.close(); + } catch (IOException ex) { + // ERROR: Cannot close input stream of file'{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E209, + filePath), ex)); + } + } + return result; + } + + /** + * Write a byte array to a file. + * + * @param ioFile + * The file, not <code>null</code>. + * @param content + * The content of the file, may be empty, not <code>null</code>. + * @throws CoreException + * If the file does not exist or cannot be read. + */ + public static void writeBytes(File ioFile, byte[] content) throws CoreException { + OutputStream outputStream; + String filePath; + + if (ioFile == null) { + throw new IllegalArgumentException("Parameter 'ioFile' must not be null."); + } + if (content == null) { + throw new IllegalArgumentException("Parameter 'content' must not be null."); + } + + filePath = ioFile.getAbsolutePath(); + + if (!ioFile.exists()) { + boolean result; + try { + result = ioFile.createNewFile(); + + } catch (IOException ex) { + // ERROR: Cannot create file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E210, + filePath), ex)); + } + if (!result) { + // ERROR: Cannot create file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E210, + filePath))); + } + } + + if (!ioFile.isFile()) { + // ERROR: '{0}' is no file but a folder. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E204, + filePath))); + } + try { + outputStream = new FileOutputStream(ioFile); + } catch (FileNotFoundException ex) { + // ERROR: Cannot open file '{0}' for writing. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E211, + filePath), ex)); + } + writeBytes(filePath, outputStream, content); + + } + + private static void writeBytes(String filePath, OutputStream outputStream, byte[] content) throws CoreException { + + if (filePath == null) { + throw new IllegalArgumentException("Parameter 'filePath' must not be null."); + } + if (outputStream == null) { + throw new IllegalArgumentException("Parameter 'outputStream' must not be null."); + } + if (content == null) { + throw new IllegalArgumentException("Parameter 'content' must not be null."); + } + + try { + outputStream.write(content); + } catch (IOException ex) { + // ERROR: Cannot write content of file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E212, + filePath), ex)); + } finally { + try { + outputStream.close(); + } catch (IOException ex) { + // ERROR: Cannot close output stream of file'{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E213, + filePath), ex)); + } + } + return; + } + + /** + * Reads the content of a file as string. + * + * @param ioFile + * The file, not <code>null</code>. + * @param maxSize + * The maximum number of character to read or + * {@link #MAX_SIZE_UNLIMITED}. + * @return The content of the file, may be empty, not <code>null</code>. + * @throws CoreException + * If the file does not exist or cannot be read. + */ + public static String readString(File ioFile, long maxSize) throws CoreException { + InputStream inputStream; + String filePath; + + if (ioFile == null) { + throw new IllegalArgumentException("Parameter 'ioFile' must not be null."); + } + + filePath = ioFile.getAbsolutePath(); + + if (!ioFile.exists()) { + // ERROR: File '{0}' does not exist. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E203, + filePath))); + } + if (!ioFile.isFile()) { + // ERROR: '{0}' is no file but a folder. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E204, + filePath))); + } + try { + inputStream = new FileInputStream(ioFile); + } catch (FileNotFoundException ex) { + // ERROR: Cannot open file '{0}' for reading. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E205, + filePath), ex)); + } + return readString(filePath, inputStream, maxSize); + + } + + /** + * Reads a string from an input stream. + * + * @param filePath + * The file path to be used in error messages. + * @param inputStream + * The input stream, not <code>null</code>. + * @param maxSize + * The maximum number of bytes to read, a non-negative integer or @link + * #MAX_SIZE_UNLIMITED}, see also {@link #MAX_SIZE_1MB}. + * @return The string, may be empty, not <code>null</code>. + * @throws CoreException + * in case the file cannot be read. the iNput stream has been + * closed in this case. + */ + public static String readString(String filePath, InputStream inputStream, long maxSize) throws CoreException { + + if (filePath == null) { + throw new IllegalArgumentException("Parameter 'filePath' must not be null."); + } + if (inputStream == null) { + throw new IllegalArgumentException("Parameter 'inputStream' must not be null."); + } + if (maxSize < MAX_SIZE_UNLIMITED) { + throw new IllegalArgumentException("Parameter 'maxSize' must not be less than " + MAX_SIZE_UNLIMITED + "."); + } + + StringBuilder result; + result = new StringBuilder(); + try { + InputStreamReader reader = new InputStreamReader(inputStream); + + int size = 0; + char[] buffer = new char[BUFFER_SIZE]; + int count = 0; + + do { + count = reader.read(buffer); + + if (count > 0) { + result.append(buffer, 0, count); + + size += count; + } + } while ((count > -1) && (maxSize == MAX_SIZE_UNLIMITED || size <= maxSize)); + + // Specified maximum size exceeded? + if (maxSize != MAX_SIZE_UNLIMITED && size > maxSize) { + // ERROR: Content of file '{0}' exceeds the specified maximum + // size of {1} characters. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E208, + filePath, String.valueOf(maxSize)))); + } + + reader.close(); + + } catch (IOException ex) { + // ERROR: Cannot read content of file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E206, + filePath), ex)); + } finally { + try { + inputStream.close(); + } catch (IOException ex) { + // ERROR: Cannot close input stream of file'{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E209, + filePath), ex)); + } + } + return result.toString(); + } + + /** + * Write a string to a file. + * + * @param ioFile + * The file, not <code>null</code>. + * @param content + * The content of the file, may be empty, not <code>null</code>. + * @throws CoreException + * If the file does not exist or cannot be read. + */ + public static void writeString(File ioFile, String content) throws CoreException { + OutputStream outputStream; + String filePath; + + if (ioFile == null) { + throw new IllegalArgumentException("Parameter 'ioFile' must not be null."); + } + if (content == null) { + throw new IllegalArgumentException("Parameter 'content' must not be null."); + } + + filePath = ioFile.getAbsolutePath(); + + if (!ioFile.exists()) { + boolean result; + try { + result = ioFile.createNewFile(); + + } catch (IOException ex) { + // ERROR: Cannot create file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E210, + filePath), ex)); + } + if (!result) { + // ERROR: Cannot create file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E210, + filePath))); + } + } + + if (!ioFile.isFile()) { + // ERROR: '{0}' is no file but a folder. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E204, + filePath))); + } + try { + outputStream = new FileOutputStream(ioFile); + } catch (FileNotFoundException ex) { + // ERROR: Cannot open file '{0}' for writing. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E211, + filePath), ex)); + } + writeString(filePath, outputStream, content); + + } + + private static void writeString(String filePath, OutputStream outputStream, String content) throws CoreException { + + if (filePath == null) { + throw new IllegalArgumentException("Parameter 'filePath' must not be null."); + } + if (outputStream == null) { + throw new IllegalArgumentException("Parameter 'outputStream' must not be null."); + } + if (content == null) { + throw new IllegalArgumentException("Parameter 'content' must not be null."); + } + + try { + OutputStreamWriter writer = new OutputStreamWriter(outputStream); + writer.write(content); + + writer.close(); + + } catch (IOException ex) { + // ERROR: Cannot write content of file '{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E212, + filePath), ex)); + } finally { + try { + outputStream.close(); + } catch (IOException ex) { + // ERROR: Cannot close output stream of file'{0}'. + throw new CoreException(new Status(IStatus.ERROR, BasePlugin.ID, TextUtility.format(Texts.MESSAGE_E213, + filePath), ex)); + } + } + + return; + } + + /** + * Gets the canonical file of a file. If the canonical path cannot be + * determined, the absolute path is returned. + * + * @param file + * The file, not <code>null</code>. + * @return The canonical file of the file,not <code>null</code>. + * + * @since 1.7.0 + */ + public static File getCanonicalFile(File file) { + if (file == null) { + throw new IllegalArgumentException("Parameter 'result' must not be null."); + } + File result; + try { + result = file.getCanonicalFile(); + } catch (IOException ex) { + result = file.getAbsoluteFile(); + } + return result; + + } + + /** + * Gets the file extension from a file path. + * + * @param path + * The file path, may be empty, not <code>null</code>. + * @return The file extension, may be empty, not <code>null</code>. + * + * @since 1.7.0 + */ + public static final String getFileExtension(String path) { + if (path == null) { + throw new IllegalArgumentException("Parameter 'path' must not be null."); + } + String extension = ""; + int index = path.lastIndexOf('.'); + if (index >= 0) { + extension = path.substring(index); + } + return extension; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/HexUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/HexUtility.java new file mode 100644 index 00000000..e555e086 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/HexUtility.java @@ -0,0 +1,159 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +/** + * Utility class to handle hex numbers. + */ +public final class HexUtility { + + /** + * Automatic length indicator for {@link #getLongValueHexString(long, int)}, + */ + public static final int AUTOMATIC_LENGTH = 0; + + /** + * Static array of all one-byte upper case hex numbers (00...FF) + */ + private static final String[] hexStrings; + + /** + * Static initialization. + */ + static { + + // Fill in the array of hex strings + hexStrings = new String[256]; + for (int i = 0; i < 256; i++) { + String s = Integer.toHexString(i).toUpperCase(); + hexStrings[i] = (s.length() < 2) ? ("0" + s) : s; + } + } + + /** + * Creation is private. + */ + private HexUtility() { + + } + + /** + * Gets the hex string for a single byte value. + * + * @param byteValue + * The byte value. + * @return The string, not empty and not <code>null</code>. + */ + public static String getByteValueHexString(int byteValue) { + if (byteValue < 0 || byteValue > 255) { + throw new IllegalArgumentException("Integer value " + byteValue + + " is no byte value."); + } + return hexStrings[byteValue]; + } + + /** + * Gets the minimum length of the hex string for a long value. + * + * @param longValue + * The non-negative long value. + * + * @return The minimum length of the hex string for the long value, an even + * positive integer. + */ + public static int getLongValueHexLength(long longValue) { + if (longValue < 0) { + throw new RuntimeException( + "Parameter 'longValue' must not be negative. Specified value is " + + longValue + "."); + } + int result = Long.toHexString(longValue).length(); + if ((result & 1) == 1) { + result++; + } + return result; + } + + /** + * Gets the hex string for a long value. + * + * @param longValue + * The non-negative long value. + * @return The string, not empty and not <code>null</code>. + */ + public static String getLongValueHexString(long longValue) { + return getLongValueHexString(longValue, + getLongValueHexLength(longValue)); + } + + /** + * Gets the hex string for a long value. + * + * @param longValue + * The non-negative long value. + * @param length + * The minimum number of characters to be used. If the result + * string is shorter, spaces will be prepended in case a length + * other then {@link #AUTOMATIC_LENGTH} is specified. + * @return The string, not empty and not <code>null</code>. + */ + public static String getLongValueHexString(long longValue, int length) { + if (longValue < 0) { + throw new RuntimeException( + "Parameter 'longValue' must not be negative. Specified value is " + + longValue + "."); + } + if (length < 0) { + throw new IllegalArgumentException( + "Parameter 'length' must not be negative. Specified value is " + + length + "."); + } + String result = Long.toHexString(longValue).toUpperCase(); + if (length > AUTOMATIC_LENGTH) { + int difference = length - result.length(); + if (difference > 0) { + StringBuilder builder = new StringBuilder(length); + for (int i = 0; i < difference; i++) { + builder.append('0'); + } + builder.insert(difference, result); + result = builder.toString(); + } + } + return result; + } + + /** + * Gets the ASCII char for a single byte value. + * + * @param byteValue + * The byte value. + * @return The string, not empty and not <code>null</code>. + */ + public static char getChar(int byteValue) { + char result; + if (byteValue >= 32 && byteValue <= 127) { + result = (char) (byteValue & 0xff); + } else { + result = ' '; + } + return result; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/IPathUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/IPathUtility.java new file mode 100644 index 00000000..1515c2cd --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/IPathUtility.java @@ -0,0 +1,77 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; + +public final class IPathUtility { + + /** + * Creation is private. + */ + private IPathUtility() { + } + + public static IPath createEmptyPath() { + return new Path(""); + } + + public static IPath makeRelative(IPath filePath, IPath filePathPrefix) { + if (filePath == null) { + throw new IllegalArgumentException( + "Parameter 'filePath' must not be null."); + } + if (filePathPrefix == null) { + throw new IllegalArgumentException( + "Parameter 'filePathPrefix' must not be null."); + } + if (filePath.isAbsolute() && !filePathPrefix.isEmpty()) { + if (filePathPrefix.isPrefixOf(filePath)) { + filePath = filePath.removeFirstSegments(filePathPrefix + .segmentCount()); + } + } + return filePath; + } + + public static IPath makeAbsolute(IPath filePath, IPath filePathPrefix, + boolean forcePrefix) { + if (filePath == null) { + throw new IllegalArgumentException( + "Parameter 'filePath' must not be null."); + } + if (filePathPrefix == null) { + throw new IllegalArgumentException( + "Parameter 'filePathPrefix' must not be null."); + } + + // If the file path is empty, the prefix is omitted by default. + // Only if forcePrefix is true, it is added. + if (!filePath.isEmpty() || forcePrefix) { + if (!filePath.isAbsolute() && !filePathPrefix.isEmpty()) { + if (!filePathPrefix.isPrefixOf(filePath)) { + filePath = filePathPrefix.append(filePath); + } + } + } + return filePath; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/InputStreamMonitor.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/InputStreamMonitor.java new file mode 100644 index 00000000..892b4019 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/InputStreamMonitor.java @@ -0,0 +1,176 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Writes to the input stream of a system process, queueing output if the stream + * is blocked. + * + * The input stream monitor writes to system in via an output stream. + * + * @author Peter Dell + */ +final class InputStreamMonitor { + + /** + * The stream which is being written to (connected to system in). + */ + private OutputStream outputStream; + + /** + * Whether the underlying output stream has been closed + */ + private boolean outputStreamClosed; + + /** + * The queue of output. + */ + private List<String> outputQueue; + /** + * A lock for ensuring that writes to the queue are contiguous + */ + private Object outputQueueLock; + + /** + * The thread which writes to the stream. + */ + private Thread thread; + + /** + * Creates an input stream monitor which writes to system in via the given + * output stream. + * + * @param outputStream + * output stream + */ + public InputStreamMonitor(OutputStream outputStream) { + if (outputStream == null) { + throw new IllegalArgumentException( + "Parameter 'outputStream' must not be null."); + } + this.outputStream = outputStream; + outputQueue = new ArrayList<String>(); + outputQueueLock = new Object(); + } + + /** + * Appends the given text to the stream, or queues the text to be written at + * a later time if the stream is blocked. + * + * @param text + * text to append + */ + public void write(String text) { + synchronized (outputQueueLock) { + outputQueue.add(text); + outputQueueLock.notifyAll(); + } + } + + /** + * Starts a thread which writes the stream. + */ + public void startMonitoring() { + if (thread == null) { + thread = new Thread(new RunnableWithLogging() { + @Override + public void runWithLogging() { + write(); + } + }, "InputStreamMonitor"); + thread.setDaemon(true); + thread.start(); + } + } + + /** + * Close all communications between this monitor and the underlying stream. + */ + public void close() { + if (thread != null) { + Thread thread = this.thread; + this.thread = null; + thread.interrupt(); + } + } + + /** + * Continuously writes to the stream. + */ + protected void write() { + while (thread != null) { + writeNext(); + } + if (!outputStreamClosed) { + try { + outputStream.close(); + } catch (IOException ex) { + BasePlugin.getInstance().logError("IOException during write()", + null, ex); + } + } + } + + /** + * Write the text in the queue to the stream. + */ + protected void writeNext() { + while (!outputQueue.isEmpty() && !outputStreamClosed) { + String text = outputQueue.get(0); + outputQueue.remove(0); + try { + outputStream.write(text.getBytes()); + outputStream.flush(); + } catch (IOException ex) { + BasePlugin.getInstance().logError( + "IOException during writeNext()", null, ex); + } + } + try { + synchronized (outputQueueLock) { + outputQueueLock.wait(); + } + } catch (InterruptedException e) { + } + } + + /** + * Closes the output stream attached to the standard input stream of this + * monitor's process. + * + * @exception IOException + * if an exception occurs closing the input stream + */ + public void closeInputStream() throws IOException { + if (!outputStreamClosed) { + outputStreamClosed = true; + outputStream.close(); + } else { + throw new IOException(); + } + + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/MarkerUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/MarkerUtility.java new file mode 100644 index 00000000..c0d9826e --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/MarkerUtility.java @@ -0,0 +1,125 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.ide.IDE; + +/** + * Utility class for processing markers. + * + * @author Peter Dell + * @since 1.6.1 + */ +public final class MarkerUtility { + + /** + * Creation is private. + */ + private MarkerUtility() { + + } + + /** + * Creates a marker associated with an {@link IFile} resource. + * + * @param file + * The {@link IFile} resource to which this message shall be + * attached, not <code>null</code>. + * @param lineNumber + * An positive integer value indicating the line number for a + * text marker. 0 to indicate that the line number is unknown. + * @param severity + * The message severity, see {@link IMarker#SEVERITY} + * @param message + * The message, may contain parameter "{0}" to "{9}". May be + * empty, not <code>null</code>. + * @param parameters + * The format parameters for the message, may be empty, not + * <code>null</code>. + * + * @return The marker representing the message, not <code>null</code>. + */ + public static IMarker createMarker(IFile file, int lineNumber, + int severity, String message, String... parameters) { + if (file == null) { + throw new IllegalArgumentException( + "Parameter 'file' must not be null."); + } + if (message == null) { + throw new IllegalArgumentException( + "Parameter 'message' must not be null."); + } + if (parameters == null) { + throw new IllegalArgumentException( + "Parameter 'parameters' must not be null."); + } + + message = TextUtility.format(message, parameters); + try { + + IMarker marker = file.createMarker(IMarker.PROBLEM); + if (lineNumber > 0) { + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + } + marker.setAttribute(IMarker.SEVERITY, severity); + marker.setAttribute(IMarker.MESSAGE, message); + marker.setAttribute(IMarker.TRANSIENT, true); + return marker; + } catch (CoreException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Navigates to the file and line number defined by the marker. + * + * @param editor + * The editor part to start from, not <code>null</code>. If + * required an additional editor will be opened in the same + * workbench page. + * + * @param marker + * The marker, not <code>null</code>. The resource of the marker + * must be an {@link IFile}. + */ + public static void gotoMarker(IEditorPart editor, IMarker marker) { + if (marker == null) { + throw new IllegalArgumentException( + "Parameter 'marker' must not be null."); + } + try { + IFile iFile = (IFile) marker.getResource(); + IWorkbenchPage page = editor.getSite().getPage(); + IEditorPart otherEditor = IDE.openEditor(page, iFile, true); + if (otherEditor != null) { + otherEditor.setFocus(); + IDE.gotoMarker(otherEditor, marker); + } + } catch (PartInitException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/NumberFactory.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/NumberFactory.java new file mode 100644 index 00000000..808d9b2f --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/NumberFactory.java @@ -0,0 +1,57 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +/** + * Factory class to obtain instances of elementary number type wrappers without + * creating new instances. + * + * @author Peter Dell + * + */ +public final class NumberFactory { + + private static final int MAX_INTEGERS = 2048; + private static final Integer[] INTEGERS; + + /** + * Static initialization. + */ + static { + INTEGERS = new Integer[MAX_INTEGERS]; + for (int i = 0; i < MAX_INTEGERS; i++) { + INTEGERS[i] = Integer.valueOf(i); + } + } + + /** + * Gets the @link Integer} instance for an int value. + * + * @param value + * The int value. + * @return The {@link Integer} instance, not <code>null</code>. + */ + public static final Integer getInteger(int value) { + if (0 <= value && value <= MAX_INTEGERS) { + + } + return Integer.valueOf(value); + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/NumberUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/NumberUtility.java new file mode 100644 index 00000000..ed384322 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/NumberUtility.java @@ -0,0 +1,89 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +/** + * Utility class to handle decimal numbers. + */ +public final class NumberUtility { + + /** + * Automatic length indicator for + * {@link #getLongValueDecimalString(long, int)}, + */ + public static final int AUTOMATIC_LENGTH = 0; + + /** + * Creation is private. + */ + private NumberUtility() { + } + + /** + * Gets the minimum length of the decimal string for a long value. + * + * @param longValue + * The non-negative long value. + * + * @return The minimum length of the decimal string for the long value, a + * positive integer. + */ + public static int getLongValueDecimalLength(int longValue) { + return Long.toString(longValue).length(); + } + + /** + * Gets the decimal string for a long value. + * + * @param longValue + * The non-negative long value. + * + * @return The string, not empty and not <code>null</code>. + */ + public static String getLongValueDecimalString(long longValue) { + return getLongValueDecimalString(longValue, AUTOMATIC_LENGTH); + } + + /** + * Gets the decimal string for a long value. + * + * @param longValue + * The non-negative long value. + * @param length + * The minimum number of characters to be used. If the result + * string is shorter, spaces will be prepended in case a length + * other then {@link #AUTOMATIC_LENGTH} is specified. + * @return The string, not empty and not <code>null</code>. + */ + public static String getLongValueDecimalString(long longValue, int length) { + + String result = Long.toString(longValue); + if (length > AUTOMATIC_LENGTH) { + int difference = length - result.length(); + if (difference > 0) { + StringBuilder builder = new StringBuilder(length); + builder.insert(difference, result); + result = builder.toString(); + } + } + return result; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/OutputStreamMonitor.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/OutputStreamMonitor.java new file mode 100644 index 00000000..7ff2fc9b --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/OutputStreamMonitor.java @@ -0,0 +1,206 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Monitors the output stream of a system process and notifies listeners of + * additions to the stream. + * + * The output stream monitor reads system out (or err) via and input stream. + * + * @author Peter Dell + */ +final class OutputStreamMonitor { + + /** + * The stream being monitored (connected system out or err). + */ + private InputStream inputStream; + + /** + * The stream to which the output is sent. + */ + private PrintStream outputStream; + + /** + * The encoding of the stream to which the output is sent. + */ + private String outputStreamEncoding; + /** + * The local copy of the stream contents + */ + private StringBuilder bufferedContent; + + /** + * The thread which reads from the stream + */ + private Thread thread; + + /** + * The size of the read buffer + */ + private static final int BUFFER_SIZE = 8192; + + /** + * Whether or not this monitor has been killed. When the monitor is killed, + * it stops reading from the stream immediately. + */ + private boolean killed; + + /** + * Creates an output stream monitor on the given stream (connected to system + * out or err). + * + * @param inputStream + * The input stream to read from, not <code>null</code>. + * @param outputStreamEncoding + * The stream encoding or <code>null</code> for system default. + * @param outputStream + * The output stream, not <code>null</code>. + */ + public OutputStreamMonitor(InputStream inputStream, String outputStreamEncoding, PrintStream outputStream) { + if (inputStream == null) { + throw new IllegalArgumentException("Parameter 'stream' must not be null."); + } + if (outputStream == null) { + throw new IllegalArgumentException("Parameter 'outputStream' must not be null."); + } + this.inputStream = new BufferedInputStream(inputStream, BUFFER_SIZE); + this.outputStreamEncoding = outputStreamEncoding; + this.outputStream = outputStream; + bufferedContent = new StringBuilder(); + } + + /** + * Causes the monitor to close all communications between it and the + * underlying stream by waiting for the thread to terminate. + */ + protected void close() { + if (this.thread != null) { + Thread thread = this.thread; + this.thread = null; + try { + thread.join(); + } catch (InterruptedException ie) { + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.core.model.IStreamMonitor#getContents() + */ + public String getContents() { + synchronized (bufferedContent) { + return bufferedContent.toString(); + } + } + + /** + * Continually reads from the stream. + * <p> + * This method, along with the <code>startReading</code> method is used to + * allow <code>OutputStreamMonitor</code> to implement <code>Runnable</code> + * without publicly exposing a <code>run</code> method. + */ + void read() { + long lastSleep = System.currentTimeMillis(); + byte[] bytes = new byte[8192]; + int read = 0; + while (read >= 0) { + try { + if (killed) { + break; + } + read = inputStream.read(bytes); + if (read > 0) { + String text; + if (outputStreamEncoding != null) { + text = new String(bytes, 0, read, outputStreamEncoding); + } else { + text = new String(bytes, 0, read); + } + synchronized (bufferedContent) { + bufferedContent.append(text); + outputStream.print(text); + outputStream.flush(); + } + } + } catch (IOException ioe) { + if (!killed) { + BasePlugin.getInstance().logError("IOException occured", null, ioe); + } + return; + } catch (NullPointerException ex) { + // killing the stream monitor while reading can cause an NPE + // when reading from the stream + if (!killed && this.thread != null) { + BasePlugin.getInstance().logError("Cannot read from stream", null, ex); + } + return; + } + + long currentTime = System.currentTimeMillis(); + if (currentTime - lastSleep > 1000) { + lastSleep = currentTime; + try { + Thread.sleep(100); // just give up CPU to maintain UI + // responsiveness. + } catch (InterruptedException e) { + } + } + } + try { + inputStream.close(); + } catch (IOException ex) { + BasePlugin.getInstance().logError("Cannot close input stream.", null, ex); + } + + } + + protected void kill() { + killed = true; + } + + /** + * Starts a thread which reads from the stream + */ + protected void startMonitoring() { + if (this.thread == null) { + this.thread = new Thread(new RunnableWithLogging() { + @Override + public void runWithLogging() { + read(); + } + }, "OutputStreamMonitor"); + } + this.thread.setDaemon(true); + this.thread.setPriority(Thread.MIN_PRIORITY); + this.thread.start(); + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ProcessWithLogs.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ProcessWithLogs.java new file mode 100644 index 00000000..a6875538 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ProcessWithLogs.java @@ -0,0 +1,227 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; + +import org.eclipse.core.runtime.Platform; + +import com.wudsn.ide.base.BasePlugin; + +/** + * The process with logs is the inter-process interface to the executables. The + * {@link System#out} and the {@link System#err} streams are captured into + * strings. + * + * @author Peter Dell + */ +public final class ProcessWithLogs { + + private final String[] commandArray; + private final File workingDirectory; + private int exitValue; + private String outputLog; + private String errorLog; + + public static final String[] getExecutableExtensions() { + + String[] extensions; + String os = Platform.getOS(); + if (os.equals(Platform.OS_WIN32)) { + // Default to ".exe" for Windows + extensions = new String[] { "*.exe", "*.jar", "*.*" }; + } else if (os.equals(Platform.OS_MACOSX)) { + // No restrictions for MacOS X since "*.app" is not always a good + // choice. + extensions = new String[0]; + } else { + // No restrictions for all other operating systems. + extensions = new String[0]; + } + return extensions; + } + + /** + * Creates a new process. + * + * @param commandArray + * The command array, not empty and not <code>null</code>. + * @param workingDirectory + * The working directory, not <code>null</code>. + */ + public ProcessWithLogs(String[] commandArray, File workingDirectory) { + if (commandArray == null) { + throw new IllegalArgumentException( + "Parameter 'fullCommandArray' must not be null."); + } + if (commandArray.length == 0) { + throw new IllegalArgumentException( + "Parameter 'fullCommandArray.length' must not be empty."); + } + for (int i = 0; i < commandArray.length; i++) { + if (commandArray[i] == null) { + throw new IllegalArgumentException( + "Parameter 'commandArray' must contain null at positition " + + i + "."); + } + } + + if (workingDirectory == null) { + throw new IllegalArgumentException( + "Parameter 'workingDirectory' must not be null."); + } + + this.commandArray = commandArray; + this.workingDirectory = FileUtility.getCanonicalFile(workingDirectory); + exitValue = 0; + outputLog = ""; + errorLog = ""; + } + + /** + * Executes the compiler. + * + * @param out + * The print stream for the output output, see {@link System#out} + * . + * @param err + * The print stream for the error output, see {@link System#err}. + * @param wait + * <code>true</code> to wait for the process to terminate and + * collect the output. + * + * @throws IOException + * The the creation of the process fails. + */ + public void exec(PrintStream out, PrintStream err, boolean wait) + throws IOException { + + if (out == null) { + throw new IllegalArgumentException( + "Parameter 'out' must not be null."); + } + if (err == null) { + throw new IllegalArgumentException( + "Parameter 'err' must not be null."); + } + Process process = null; + exitValue = 0; + outputLog = ""; + errorLog = ""; + Profiler profiler = new Profiler(this); + profiler.begin("exec"); + try { + BasePlugin.getInstance().log( + "Executing process '{0}' in working directory '{1}'.", + new Object[] { getCommandArrayString(), + workingDirectory.getPath() }); + + process = Runtime.getRuntime().exec(commandArray, null, + workingDirectory); + } catch (IOException ex) { + BasePlugin.getInstance().logError( + "Cannot execute process '{0}' in working directory '{1}'.", + new Object[] { getCommandArrayString(), + workingDirectory.getPath() }, ex); + throw ex; + } finally { + profiler.end("exec"); + } + if (wait) { + String encoding = null; + StreamsProxy streamsProxy = new StreamsProxy(process, encoding, + out, err); + try { + profiler.begin("waitFor"); + process.waitFor(); + } catch (InterruptedException ex) { + BasePlugin.getInstance().logError("Process interrupted", null, + ex); + throw new IOException(ex.getMessage()); + } finally { + profiler.end("waitFor"); + process.destroy(); + } + + streamsProxy.close(); + exitValue = process.exitValue(); + outputLog = streamsProxy.getOutputStreamMonitor().getContents(); + errorLog = streamsProxy.getErrorStreamMonitor().getContents(); + } + + } + + /** + * Gets the command array to be as string. + * + * @return The command array as string, may be empty, not <code>null</code> + * .. + */ + public String getCommandArrayString() { + StringBuilder result; + result = new StringBuilder(); + for (int i = 0; i < commandArray.length; i++) { + result.append(commandArray[i]); + if (i < commandArray.length - 1) { + result.append(" "); + } + } + return result.toString(); + } + + /** + * Gets the working directory. + * + * @return The working directory, not <code>null</code>. + */ + public File getWorkingDirectory() { + return workingDirectory; + } + + /** + * Gets the exit value of the process. + * + * @return The exit value of the process. + */ + public int getExitValue() { + return exitValue; + } + + /** + * Gets the output log captured from {@link System#out}. + * + * @return The output log, maybe empty, not <code>null</code>. + */ + public String getOutputLog() { + return outputLog; + } + + /** + * Gets the error log captured from {@link System#out}. + * + * @return The output log, maybe empty, not <code>null</code>. + */ + public String getErrorLog() { + return errorLog; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/Profiler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/Profiler.java new file mode 100644 index 00000000..c0ee2047 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/Profiler.java @@ -0,0 +1,88 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.util.HashMap; +import java.util.Map; + +import com.wudsn.ide.base.BasePlugin; + +public final class Profiler { + + public static final String PROPERTY_NAME = "com.wudsn.ide.common.base.Profiler"; + + private static final class Entry { + + public final long startTimeMillis; + public final String description; + + public Entry(String description) { + if (description == null) { + throw new IllegalArgumentException( + "Parameter 'description' must not be null."); + } + startTimeMillis = System.currentTimeMillis(); + this.description = description; + } + } + + private Object owner; + private Map<String, Entry> statistics; + + public Profiler(Object owner) { + if (owner == null) { + throw new IllegalArgumentException( + "Parameter 'owner' must not be null."); + } + this.owner = owner; + statistics = new HashMap<String, Entry>(); + } + + public void end(String key) { + Entry entry = statistics.get(key); + if (entry == null) { + throw new IllegalStateException("No begin for key '" + key + "'."); + } + + if (Boolean.getBoolean(PROPERTY_NAME)) { + Long duration = new Long( + (System.currentTimeMillis() - entry.startTimeMillis)); + if (entry.description.isEmpty()) { + BasePlugin.getInstance().log("Time for '{0}:{1}' is {2}ms", + new Object[] { owner, key, duration }); + } else { + BasePlugin.getInstance() + .log("Time for '{0}:{1}' of {2} is {3}ms", + new Object[] { owner, key, entry.description, + duration }); + } + } + + } + + public void begin(String key) { + begin(key, ""); + } + + public void begin(String key, String description) { + statistics.put(key, new Entry(description)); + + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/PropertiesSerializer.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/PropertiesSerializer.java new file mode 100644 index 00000000..292830f4 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/PropertiesSerializer.java @@ -0,0 +1,233 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import java.util.Enumeration; +import java.util.Properties; + +/** + * Serializer which reads and writes simple and complex data types as + * properties. + * + * @author Peter Dell + */ +public class PropertiesSerializer { + + protected final SequencedProperties properties; + + /** + * Creation is public. + */ + public PropertiesSerializer() { + + properties = new SequencedProperties(); + } + + public final SequencedProperties getProperties() { + return properties; + } + + protected final void setProperty(String key, String value) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + + properties.put(key, value); + } + + public final String readString(String key, String defaultValue) { + + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (defaultValue == null) { + throw new IllegalArgumentException( + "Parameter 'defaultValue' must not be null."); + } + String result; + result = properties.getProperty(key); + if (result == null) { + result = defaultValue; + } + + return result; + } + + public final void writeString(String key, String value) { + + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + setProperty(key, value); + } + + public boolean readBoolean(String key, boolean defaultValue) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + + } + boolean result; + result = defaultValue; + String text = properties.getProperty(key); + if (text != null) { + result = Boolean.parseBoolean(text); + } + return result; + } + + public final void writeBoolean(String key, boolean value) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + properties.setProperty(key, Boolean.toString(value)); + } + + public final <T extends Enum<?>> T readEnum(String key, T defaultValue, + Class<T> enumClass) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (defaultValue == null) { + throw new IllegalArgumentException( + "Parameter 'defaultValue' must not be null."); + } + if (enumClass == null) { + throw new IllegalArgumentException( + "Parameter 'enumClass' must not be null."); + } + T result; + result = defaultValue; + String text = properties.getProperty(key); + if (text != null) { + T[] enumConstants = enumClass.getEnumConstants(); + + for (int i = 0; i < enumConstants.length; i++) { + T enumConstant = enumConstants[i]; + if (enumConstant.name().equals(text)) { + result = enumConstant; + break; + } + } + } + + return result; + } + + public final <T extends Enum<?>> void writeEnum(String key, T value) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + setProperty(key, value.name()); + } + + public final int readInteger(String key, int defaultValue) { + + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + + int result; + String text = properties.getProperty(key); + if (text == null) { + result = defaultValue; + } else { + try { + result = Integer.parseInt(text); + } catch (NumberFormatException ex) { + result = 0; + } + } + + return result; + } + + public final void writeInteger(String key, int value) { + + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + + setProperty(key, String.valueOf(value)); + } + + public final void readProperties(String key, PropertiesSerializer value) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + String prefix = key + "."; + Properties valueProperties = value.getProperties(); + valueProperties.clear(); + Enumeration<Object> i = properties.keys(); + while (i.hasMoreElements()) { + String valueKey = (String) i.nextElement(); + if (valueKey.startsWith(prefix)) { + valueProperties.setProperty( + valueKey.substring(prefix.length()), + properties.getProperty(valueKey)); + } + } + } + + public final void writeProperties(String key, PropertiesSerializer value) { + if (key == null) { + throw new IllegalArgumentException( + "Parameter 'key' must not be null."); + } + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + String prefix = key + "."; + Properties valueProperties = value.getProperties(); + Enumeration<Object> i = valueProperties.keys(); + while (i.hasMoreElements()) { + String valueKey = (String) i.nextElement(); + setProperty(prefix + valueKey, + valueProperties.getProperty(valueKey)); + } + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ResourceUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ResourceUtility.java new file mode 100644 index 00000000..641f4698 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/ResourceUtility.java @@ -0,0 +1,317 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.common; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLStreamHandler; +import java.util.jar.JarFile; +import java.util.zip.ZipEntry; + +import org.eclipse.core.runtime.CoreException; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Utility class to access resources in the class path. + * + * @author Peter Dell + */ +public final class ResourceUtility { + + public interface ResourceModifier { + public byte[] modifyResource(URL url, byte[] data); + } + + private final static class ResourceURLStreamHandler extends URLStreamHandler { + + ResourceModifier resourceModifier; + + /** + * Creates a new handler. + * + * @param resourceModifier + * The resource modifier or <code>null</code>. + */ + public ResourceURLStreamHandler(ResourceModifier resourceModifier) { + this.resourceModifier = resourceModifier; + } + + @Override + protected URLConnection openConnection(URL url) throws IOException { + + return new URLConnection(url) { + + @Override + public void connect() throws IOException { + } + + @Override + public String getContentType() { + return "text/html"; + } + + @Override + public InputStream getInputStream() throws IOException { + byte[] data; + data = null; + + // Handle "jar" protocol. + if (url.getProtocol().equals("jar")) { + + // Extract the "file:/... " part from the + // "jar:file:/..." + // URL. + String path = url.getPath(); + + // Check if there's a ".jar" available. + URI jarURI = ClassPathUtility.getJarURI(); + if (jarURI != null) { + + // If yes, strip the path prefix to determine the + // relative path. + String jarURIString = jarURI.toString(); + if (path.startsWith(jarURIString)) { + path = path.substring(jarURIString.length() + 2); + } + } + data = ResourceUtility.loadResourceAsByteArray(path); + if (data == null) { + throw new IOException("No resource found with path '" + path + "' found."); + } + + } // Handle "file:" protocol. + else if (url.getProtocol().equals("file")) { + File file; + try { + file = new File(url.toURI()); + data = FileUtility.readBytes(file, FileUtility.MAX_SIZE_UNLIMITED, false); + + } catch (URISyntaxException ex) { + // ignore, not found + } catch (CoreException ex) { + throw new IOException(ex.getMessage()); + } + } + + if (data == null) { + data = ("Invalid URL: " + url).getBytes(); + } else { + if (resourceModifier != null) { + data = resourceModifier.modifyResource(url, data); + if (data == null) { + data = ("Resource modified returned null for URL: " + url).getBytes(); + } + + } + } + return new ByteArrayInputStream(data); + + } + }; + + } + } + + private final static class JarEntryInputStream extends InputStream { + private JarFile jar; + private InputStream zipEntryInputStream; + + public JarEntryInputStream(File jarFile, String path) throws IOException { + if (jarFile == null) { + throw new IllegalArgumentException("Parameter 'jarFile' must not be null."); + } + if (path == null) { + throw new IllegalArgumentException("Parameter 'path' must not be null."); + } + + jar = new JarFile(jarFile); + ZipEntry zipEntry = jar.getEntry(path); + zipEntryInputStream = jar.getInputStream(zipEntry); + } + + @Override + public int read() throws IOException { + return zipEntryInputStream.read(); + } + + @Override + public void close() throws IOException { + try { + zipEntryInputStream.close(); + } catch (IOException ex) { + throw (ex); + } finally { + try { + jar.close(); + } catch (IOException ex) { + throw (ex); + } + } + } + } + + /** + * Creates a new handler. + * + * @param resourceModifier + * The resource modifier or <code>null</code>. + * @return The handler, not <code>null</code>. + */ + public static URLStreamHandler createStreamHandler(ResourceModifier resourceModifier) { + return new ResourceURLStreamHandler(resourceModifier); + + } + + /** + * Creation is private, + */ + private ResourceUtility() { + + } + + /** + * Self implemented logic to bypass the bug described in <a + * href="http://bugs.sun.com/view_bug.do?bug_id=4523159">JDK-4523159 : + * getResourceAsStream on jars in path with "!"</a>. + * + * @param path + * The path of the resource to load, not <code>null</code>. + * @return The input stream or <code>null</code> if the source was not + * found. + */ + private static InputStream getInputStream(String path) { + if (path == null) { + throw new IllegalArgumentException("Parameter 'path' must not be null."); + } + // If there is no loader, the program was launched using the Java + // boot class path and the system class loader must be used. + ClassLoader loader = ResourceUtility.class.getClassLoader(); + URL url = (loader == null) ? ClassLoader.getSystemResource(path) : loader.getResource(path); + InputStream result = null; + try { + if (url != null) { + try { + result = url.openStream(); + } catch (IOException ignore) { + } + if (result == null) { + File jarFile = ClassPathUtility.getJarFile(); + + if (jarFile != null) { + result = new JarEntryInputStream(jarFile, path); + + } + } + } + } catch (IOException ex) { + BasePlugin.getInstance().logError("Cannot get input stream for path '{0}'", new Object[] { path }, ex); + } + return result; + } + + /** + * Loads a resource as byte array. + * + * @param path + * The resource path, not empty, not <code>null</code>. + * @return The binary resource content or <code>null</code> if the resource + * was not found. + */ + public static byte[] loadResourceAsByteArray(String path) { + if (path == null) { + throw new IllegalArgumentException("Parameter 'path' must not be null."); + } + if (StringUtility.isEmpty(path)) { + throw new IllegalArgumentException("Parameter 'path' must not be empty."); + } + InputStream inputStream = getInputStream(path); + if (inputStream == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + try { + byte[] buffer = new byte[8192]; + int actualLength; + while ((actualLength = inputStream.read(buffer, 0, buffer.length)) != -1) { + outputStream.write(buffer, 0, actualLength); + } + + } catch (IOException ex) { + BasePlugin.getInstance().logError("Cannot load resource '{0}'.", new Object[] { path }, ex); + } finally { + + try { + inputStream.close(); + } catch (IOException ignore) { + } + } + return outputStream.toByteArray(); + } + + /** + * Loads a resource as string. + * + * @param path + * The resource path, not empty, not <code>null</code>. + * @return The resource content or <code>null</code> if the resource was not + * found. + */ + public static String loadResourceAsString(String path) { + if (path == null) { + throw new IllegalArgumentException("Parameter 'path' must not be null."); + } + if (StringUtility.isEmpty(path)) { + throw new IllegalArgumentException("Parameter 'path' must not be empty."); + } + final InputStream inputStream = getInputStream(path); + if (inputStream == null) { + return null; + } + StringBuilder builder = new StringBuilder(); + try { + InputStreamReader reader = new InputStreamReader(inputStream); + char[] buffer = new char[8192]; + int actualLength; + while ((actualLength = reader.read(buffer, 0, buffer.length)) != -1) { + builder.append(buffer, 0, actualLength); + } + reader.close(); + + } catch (IOException ex) { + BasePlugin.getInstance().logError("Cannot load resource '{0}'.", new Object[] { path }, ex); + } finally { + + try { + inputStream.close(); + } catch (IOException ignore) { + } + } + return builder.toString(); + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/RunnableWithLogging.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/RunnableWithLogging.java new file mode 100644 index 00000000..b333b100 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/RunnableWithLogging.java @@ -0,0 +1,45 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Wrapper class which ensures that all exceptions in runnables are logged. + * + * @author Peter Dell + */ +public abstract class RunnableWithLogging implements Runnable { + + @Override + public final void run() { + try { + runWithLogging(); + + } catch (Throwable th) { + BasePlugin.getInstance().logError( + "Runnable '{0}' ended with exception.", + new Object[] { getClass().getName() }, th); + } + } + + protected abstract void runWithLogging(); + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/SequencedProperties.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/SequencedProperties.java new file mode 100644 index 00000000..c97ed890 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/SequencedProperties.java @@ -0,0 +1,80 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.common; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Properties which keep their sequence of keys. + * + * @author Peter Dell + */ +public final class SequencedProperties extends Properties { + /** + * Not used. + */ + private static final long serialVersionUID = 1L; + + private List<Object> propertyNames; + + public SequencedProperties() { + propertyNames = new ArrayList<Object>(); + } + + @Override + public synchronized Object put(Object key, Object value) { + if (propertyNames.contains(key)) { + throw new IllegalArgumentException("Value for key '" + key + + "' already added."); + } + propertyNames.add(key); + return super.put(key, value); + } + + /** + * Returns an enumeration of the keys in this hashtable. + * + * @return an enumeration of the keys in this hashtable. + * @see Enumeration + * @see #elements() + * @see #keySet() + * @see Map + */ + @Override + public synchronized Enumeration<Object> keys() { + return Collections.enumeration(propertyNames); + } + + @Override + public synchronized String toString() { + StringBuilder builder = new StringBuilder(); + for (Object key : propertyNames) { + + builder.append(key).append("=").append(get(key)).append("\n"); + } + return builder.toString(); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/StreamsProxy.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/StreamsProxy.java new file mode 100644 index 00000000..e6dd8881 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/StreamsProxy.java @@ -0,0 +1,100 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.common; + +import java.io.IOException; +import java.io.PrintStream; + +/** + * Wrapper around the input and output stream of a process. The stream a + * redirected to the corresponding {@link InputStreamMonitor} and + * {@link OutputStreamMonitor} + * + * @author Peter Dell + */ +final class StreamsProxy { + + private OutputStreamMonitor outputMonitor; + private OutputStreamMonitor errorMonitor; + private InputStreamMonitor inputMonitor; + private boolean closed; + + public StreamsProxy(Process process, String encoding, PrintStream out, PrintStream err) { + if (process == null) { + throw new IllegalArgumentException( + "Parameter 'process' must not be null"); + } + if (out == null) { + throw new IllegalArgumentException("Parameter 'out' must not be null."); + } + if (err == null) { + throw new IllegalArgumentException("Parameter 'err' must not be null."); + } + outputMonitor = new OutputStreamMonitor(process.getInputStream(), + encoding, out); + errorMonitor = new OutputStreamMonitor(process.getErrorStream(), + encoding, err); + inputMonitor = new InputStreamMonitor(process.getOutputStream()); + outputMonitor.startMonitoring(); + errorMonitor.startMonitoring(); + inputMonitor.startMonitoring(); + + } + + public void close() { + if (!closed) { + closed = true; + outputMonitor.close(); + errorMonitor.close(); + inputMonitor.close(); + } + } + + public void kill() { + closed = true; + outputMonitor.kill(); + errorMonitor.kill(); + inputMonitor.close(); + } + + public OutputStreamMonitor getErrorStreamMonitor() { + return errorMonitor; + } + + public OutputStreamMonitor getOutputStreamMonitor() { + return outputMonitor; + } + + public void write(String input) throws IOException { + if (!closed) { + inputMonitor.write(input); + } else { + throw new IOException("StreamsProxy alsready closed"); + } + } + + public void closeInputStream() throws IOException { + if (!closed) { + inputMonitor.closeInputStream(); + } else { + throw new IOException("StreamsProxy alsready closed"); + } + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/StringUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/StringUtility.java new file mode 100644 index 00000000..bbaed395 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/StringUtility.java @@ -0,0 +1,70 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + +/** + * Utility class to handle strings. + * + * @author Peter Dell + * + */ +public final class StringUtility { + + /** + * Creation is private. + */ + private StringUtility() { + + } + + /** + * Determines if a string value is empty, i.e. has zero length or is only + * containing white spaces. + * + * @param value + * The string value, not <code>null</code>. + * @return <code>true</code> if the value is empty or only containing of + * white spaces, <code>false</code> otherwise. + */ + public static boolean isEmpty(String value) { + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + return value.trim().length() == 0; + } + + /** + * Determines if a string value is specified, i.e. not empty and not only + * containing white spaces. + * + * @param value + * The string value, not <code>null</code>. + * @return <code>true</code> if the value is not empty and not only + * containing of white spaces, <code>false</code> otherwise. + */ + public static boolean isSpecified(String value) { + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + return value.trim().length() > 0; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/common/TextUtility.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/TextUtility.java new file mode 100644 index 00000000..c8249e18 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/common/TextUtility.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.common; + + +/** + * Utility class for processing text. + * + * @author Peter Dell + */ +public final class TextUtility { + + /** + * Parameter variable tokens. + */ + private static final String[] PARAMETERS = { "{0}", "{1}", "{2}", "{3}", + "{4}", "{5}", "{6}", "{7}", "{8}", "{9}" }; + + /** + * Creation is private. + */ + private TextUtility() { + + } + + /** + * Formats a text with parameters "{0}" to "{9}". + * + * @param text + * The text with the parameters "{0}" to "{9}", may be empty, not + * <code>null</code>. + * @param parameters + * The parameters, may be empty or <code>null</code>. + * + * @return The formatted text, may be empty, not <code>null</code>. + */ + public static String format(String text, String... parameters) { + if (text == null) { + throw new IllegalArgumentException( + "Parameter 'text' must not be null."); + } + if (parameters == null) { + parameters = new String[0]; + } + for (int i = 0; i < parameters.length; i++) { + String parameter = parameters[i]; + if (parameter == null) { + parameter = ""; + } + text = text.replace(PARAMETERS[i], parameter); + } + return text; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/CommonOpenEditorCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/CommonOpenEditorCommandHandler.java new file mode 100644 index 00000000..16547f8c --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/CommonOpenEditorCommandHandler.java @@ -0,0 +1,151 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.part.FileEditorInput; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.StringUtility; + +/** + * The action to open an editor from the context menu. The editor will become + * the new default editor for the resources on which the action is executed + * successfully. + * + * @author Peter Dell + */ +public abstract class CommonOpenEditorCommandHandler extends AbstractHandler { + + private final String editorId; + + /** + * Creation is protected, is only call by sub-classes. + * + * @param editorId + * The editor id, not empty and not <code>null</code>. + */ + protected CommonOpenEditorCommandHandler(String editorId) { + if (editorId == null) { + throw new IllegalArgumentException( + "Parameter 'editorId' must not be null."); + } + if (StringUtility.isEmpty(editorId)) { + throw new IllegalArgumentException( + "Parameter 'editorId' must not be empty."); + } + this.editorId = editorId; + } + + @Override + public final Object execute(ExecutionEvent event) throws ExecutionException { + List<IFile> files = new ArrayList<IFile>(3); + ISelection menuSelection; + menuSelection = HandlerUtil.getActiveMenuSelection(event); + ISelection menuEditorInputSelection; + menuEditorInputSelection = HandlerUtil.getActiveMenuEditorInput(event); + + if (menuSelection instanceof IStructuredSelection) { + Iterator<?> i = ((IStructuredSelection) menuSelection).iterator(); + while (i.hasNext()) { + Object object = i.next(); + openFile(files, object); + + } + } else if (menuEditorInputSelection instanceof IStructuredSelection) { + Iterator<?> i = ((IStructuredSelection) menuEditorInputSelection) + .iterator(); + while (i.hasNext()) { + Object object = i.next(); + if (object instanceof IFileEditorInput) { + IFileEditorInput fileEditorInput = (IFileEditorInput) object; + openFile(files, fileEditorInput.getFile()); + } + } + } + + return null; + } + + /** + * Opens the specified file on the editor. + * + * @param files + * The modifiable list of files already opened, not + * <code>null</code>. + * @param object + * The IFile object or <code>null</code>. + */ + private void openFile(List<IFile> files, Object object) { + + if (files == null) { + throw new IllegalArgumentException( + "Parameter 'files' must not be null."); + } + if (object instanceof IFile) { + + IFile file = (IFile) object; + if (!files.contains(file)) { + files.add(file); + + IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench() + .getActiveWorkbenchWindow(); + IWorkbenchPage page = workbenchWindow.getActivePage(); + + // Open an editor on the new file. + try { + FileEditorInput editorInput = new FileEditorInput(file); + IEditorPart editor = page.findEditor(editorInput); + if (editor != null) { + String id = editor.getEditorSite().getId(); + if (!id.equals(editorId)) { + if (!page.closeEditor(editor, true)) { + return; + } + } + } + IDE.setDefaultEditor(file, editorId); + IDE.openEditor(page, editorInput, editorId); + } catch (PartInitException exception) { + MessageDialog.openError(workbenchWindow.getShell(), + Texts.DIALOG_TITLE, exception.getMessage()); + } + } + } + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/CommonOpenFolderCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/CommonOpenFolderCommandHandler.java new file mode 100644 index 00000000..d4da9417 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/CommonOpenFolderCommandHandler.java @@ -0,0 +1,154 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.program.Program; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.handlers.HandlerUtil; + +import com.wudsn.ide.base.common.FileUtility; + +/** + * Event handler for the "Open Folder" context menu command to start a given + * editor. + * + * @author Peter Dell + */ +public final class CommonOpenFolderCommandHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + List<String> folderPaths = new ArrayList<String>(3); + + ISelection menuSelection; + menuSelection = HandlerUtil.getActiveMenuSelection(event); + ISelection menuEditorInputSelection; + menuEditorInputSelection = HandlerUtil.getActiveMenuEditorInput(event); + + if (menuSelection instanceof IStructuredSelection) { + Iterator<?> i = ((IStructuredSelection) menuSelection).iterator(); + while (i.hasNext()) { + Object object = i.next(); + openFolder(folderPaths, object); + + } + } else if (menuEditorInputSelection instanceof IStructuredSelection) { + Iterator<?> i = ((IStructuredSelection) menuEditorInputSelection).iterator(); + while (i.hasNext()) { + Object object = i.next(); + if (object instanceof IFileEditorInput) { + IFileEditorInput fileEditorInput = (IFileEditorInput) object; + openFolder(folderPaths, fileEditorInput.getFile()); + } + } + } + + return null; + } + + /** + * Open the folder for an object (resource). + * + * @param folderPaths + * The modifiable list of folders already opened, not + * <code>null</code>. + * @param object + * The object of which the folder shall be opened or + * <code>null</code>. + */ + private void openFolder(List<String> folderPaths, Object object) { + if (folderPaths == null) { + throw new IllegalArgumentException("Parameter 'folderPaths' must not be null."); + } + String folderPath; + + if (object instanceof IAdaptable && !(object instanceof IResource || object instanceof File)) { + IAdaptable adapter = (IAdaptable) object; + object = adapter.getAdapter(IResource.class); + if (object == null) { + object = adapter.getAdapter(File.class); + } + } + + if (object instanceof IResource) { + IResource resource = (IResource) object; + + IPath path = resource.getRawLocation(); + if (path == null) { + path = resource.getLocation(); + } + folderPath = getFolderPath(path); + + } else if (object instanceof File) { + File file = (File) object; + + if (file.isDirectory()) { + folderPath = FileUtility.getCanonicalFile(file).getPath(); + } else { + folderPath = FileUtility.getCanonicalFile(file.getParentFile()).getPath(); + } + + } else { + folderPath = null; + } + + if (folderPath != null && !folderPaths.contains(folderPath)) { + folderPaths.add(folderPath); + Program.launch(folderPath); + } + } + + /** + * Converts an IPath to a real OS path. + * + * @param path + * The path or <code>null</code>. + * @return The folder path, may be empty or <code>null</code>. + */ + private String getFolderPath(IPath path) { + String folderPath; + if (path != null) { + String resourcePath = path.toOSString(); + File file = FileUtility.getCanonicalFile(new File(resourcePath)); + if (file.isDirectory()) { + folderPath = file.getPath(); + } else { + folderPath = file.getParentFile().getPath(); + } + } else { + folderPath = null; + } + return folderPath; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/Hardware.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/Hardware.java new file mode 100644 index 00000000..be17c367 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/Hardware.java @@ -0,0 +1,31 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.editor.hex; + +/** + * Enum for the supported hardware platforms. Used for file content modes and + * character sets. + * + * @author Peter Dell + * + */ +public enum Hardware { + + GENERIC, ATARI8BIT, C64 +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditor.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditor.java new file mode 100644 index 00000000..78f47a71 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditor.java @@ -0,0 +1,648 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import java.io.File; +import java.util.Iterator; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.action.MenuManager; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorSite; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.IPathEditorInput; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.part.EditorPart; +import org.eclipse.ui.views.contentoutline.IContentOutlinePage; + +import com.wudsn.ide.base.BasePlugin; +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.ByteArrayUtility; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.Profiler; +import com.wudsn.ide.base.common.TextUtility; +import com.wudsn.ide.base.gui.Action; +import com.wudsn.ide.base.gui.Application; +import com.wudsn.ide.base.gui.EnumField; +import com.wudsn.ide.base.gui.IntegerField; +import com.wudsn.ide.base.gui.MessageManager; +import com.wudsn.ide.base.gui.SWTFactory; +import com.wudsn.ide.base.gui.TextField; + +/** + * The Hex Editor. This editor offset and outline view for the block of the file + * and a context menu for copying the current selection clip board in different + * formats. + * + * TODO Complete copy & paste, complete documentation + * + * @author Peter Dell + */ +public final class HexEditor extends EditorPart implements ISelectionProvider, + Application { + + private static final String LABEL_SUFFIX = ": "; + + public final class MessageIds { + + /** + * Creation is private. + */ + private MessageIds() { + } + + public static final int FILE_CONTENT_MODE = 1; + public static final int CHARACTER_SET = 2; + public static final int BYTES_PER_LINE = 3; + } + + public final class Actions { + + /** + * Creation is private. + */ + private Actions() { + } + + public static final int FILE_CONTENT_MODE_CHANGED = 1000; + public static final int CHARACTER_SET_TYPE_CHANGED = 1001; + public static final int BYTES_PER_ROW_CHANGED = 1002; + } + + public static final String ID = "com.wudsn.ide.base.editor.hex.HexEditor"; + + private static final String CONTEXT_MENU_ID = "#HexEditorContext"; + private static final long MAX_FILE_SIZE = 8 * ByteArrayUtility.MB; + + private MessageManager messageManager; + private HexEditorParserComponent parserComponent; + + // Editor content outline page. + private HexEditorContentOutlinePage contentOutlinePage; + + // Editor header area. + private TextField fileContentSizeField; + private EnumField<HexEditorFileContentMode> fileContentModeField; + private EnumField<HexEditorCharacterSet> characterSetField; + private IntegerField bytesPerRowField; + + private StyledText textField; + + // File source. + private IFile iFile; + private File ioFile; + + /** + * This main method is for testing the speed of the source file parser + * component only. + * + * @param args + * Not used, not <code>null</code>. + * @throws Exception + * If anything goes terribly wrong. + */ + public static void main(String[] args) throws Exception { + + // Initialize for stand alone usage. + new BasePlugin().start(null); + + HexEditorParserComponent parser = new HexEditorParserComponent( + new MessageManager(new HexEditor())); + parser.setFileContent(new byte[100000]); + parser.determinePossibleFileContentModes(); + + long startTimeMillis = System.currentTimeMillis(); + parser.setFileContentMode(HexEditorFileContentMode.BINARY); + parser.parseFileContent(); + long duration = System.currentTimeMillis() - startTimeMillis; + System.out.println(duration); + System.exit(0); + + } + + /** + * Creation is public. Called by extension point + * "org.eclipse.ui.popupMenus". + */ + public HexEditor() { + super(); + + messageManager = new MessageManager(this); + + parserComponent = new HexEditorParserComponent(messageManager); + + } + + /** + * @see org.eclipse.ui.IEditorPart#init(IEditorSite, IEditorInput) + */ + @Override + public void init(IEditorSite site, IEditorInput input) + throws PartInitException { + setSite(site); + setInput(input); + + try { + load(); + } catch (CoreException ex) { + BasePlugin.getInstance().showError(site.getShell(), + ex.getMessage(), ex); + } + } + + /** + * @see org.eclipse.ui.IWorkbenchPart#createPartControl(Composite) + */ + @Override + public void createPartControl(Composite parent) { + + getSite().setSelectionProvider(this); + + GridLayout gridLayout = new GridLayout(1, true); + gridLayout.marginWidth = 0; + parent.setLayout(gridLayout); + GridData gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING); + gd.horizontalSpan = 1; + parent.setLayoutData(gd); + + Composite header = SWTFactory.createComposite(parent, 8, 1, + GridData.FILL_HORIZONTAL); + FillLayout fillLayout = new FillLayout(SWT.HORIZONTAL); + fillLayout.marginWidth = 10; + header.setLayout(fillLayout); + + fileContentSizeField = new TextField(header, + Texts.HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_LABEL + LABEL_SUFFIX, + SWT.READ_ONLY); + fileContentSizeField.getLabel().setAlignment(SWT.RIGHT); + + fileContentModeField = new EnumField<HexEditorFileContentMode>(header, + Texts.HEX_EDITOR_FILE_CONTENT_MODE_FIELD_LABEL + LABEL_SUFFIX, + HexEditorFileContentMode.class, null); + fileContentModeField.getLabel().setAlignment(SWT.RIGHT); + + messageManager.registerField(fileContentModeField, + MessageIds.FILE_CONTENT_MODE); + fileContentModeField.addSelectionAction(new Action( + Actions.FILE_CONTENT_MODE_CHANGED, this)); + + characterSetField = new EnumField<HexEditorCharacterSet>(header, + Texts.HEX_EDITOR_CHARACTER_SET_TYPE_FIELD_LABEL + LABEL_SUFFIX, + HexEditorCharacterSet.class, null); + characterSetField.getLabel().setAlignment(SWT.RIGHT); + messageManager.registerField(characterSetField, + MessageIds.CHARACTER_SET); + characterSetField.addSelectionAction(new Action( + Actions.CHARACTER_SET_TYPE_CHANGED, this)); + + bytesPerRowField = new IntegerField(header, + Texts.HEX_EDITOR_BYTES_PER_ROW_FIELD_LABEL + LABEL_SUFFIX, + null, false, 1, SWT.NONE); + bytesPerRowField.getLabel().setAlignment(SWT.RIGHT); + messageManager.registerField(characterSetField, + MessageIds.BYTES_PER_LINE); + bytesPerRowField.getControl().addKeyListener(new KeyListener() { + + @Override + public void keyReleased(KeyEvent e) { + if (e.keyCode == '\r') { + performAction(new Action(Actions.BYTES_PER_ROW_CHANGED, + HexEditor.this)); + } + + } + + @Override + public void keyPressed(KeyEvent e) { + + } + }); + bytesPerRowField.getControl().addFocusListener(new FocusListener() { + + @Override + public void focusLost(FocusEvent e) { + performAction(new Action(Actions.BYTES_PER_ROW_CHANGED, + HexEditor.this)); + + } + + @Override + public void focusGained(FocusEvent e) { + } + }); + + // SWT.WRAP is very slow, so it's not used. + textField = new StyledText(parent, SWT.SCROLL_LINE | SWT.V_SCROLL + | SWT.H_SCROLL | SWT.READ_ONLY); + gd = new GridData(GridData.FILL_VERTICAL | GridData.FILL_HORIZONTAL); + gd.horizontalIndent = 0; + textField.setLayoutData(gd); + textField.setIndent(10); + textField.setLineSpacing(0); + + // Create a menu manager for the context menu. + MenuManager manager = new MenuManager(CONTEXT_MENU_ID, CONTEXT_MENU_ID); + manager.setRemoveAllWhenShown(true); + + // Create menu and link to the field. + Menu textContextMenu = manager.createContextMenu(textField); + textField.setMenu(textContextMenu); + + getEditorSite().registerContextMenu(CONTEXT_MENU_ID, manager, this, + false); + messageManager.clearMessages(); + dataToUi(); + } + + @Override + public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) { + if (adapter != null && IContentOutlinePage.class.equals(adapter)) { + if (contentOutlinePage == null) { + contentOutlinePage = new HexEditorContentOutlinePage(this); + + contentOutlinePage.setInput(parserComponent.getOutlineBlocks()); + } + + return contentOutlinePage; + } + return super.getAdapter(adapter); + } + + /** + * @see org.eclipse.ui.IWorkbenchPart#setFocus() + */ + @Override + public void setFocus() { + textField.setFocus(); + } + + private void load() throws CoreException { + + // Clear fields. + ioFile = null; + iFile = null; + + String fileName = ""; + IEditorInput input = getEditorInput(); + if (input instanceof IFileEditorInput) { + // Input file found in Eclipse Workspace. + iFile = ((IFileEditorInput) input).getFile(); + ioFile = iFile.getRawLocation().toFile(); + fileName = iFile.getName(); + } else if (input instanceof IPathEditorInput) { + // Input file is outside the Eclipse Workspace + IPathEditorInput pathEditorInput = (IPathEditorInput) input; + IPath path = pathEditorInput.getPath(); + ioFile = path.toFile(); + fileName = ioFile.getName(); + + } else { + // Not supported. + } + + byte[] fileContent; + Profiler profiler = new Profiler(this); + profiler.begin("readBytes", fileName); + if (ioFile != null) { + fileContent = FileUtility.readBytes(ioFile, MAX_FILE_SIZE, false); + } else if (iFile != null) { + fileContent = FileUtility.readBytes(iFile, MAX_FILE_SIZE, false); + } else { + fileContent = new byte[0]; + } + profiler.end("readBytes"); + + // Set the content, determine the default file content mode and + // character set. + parserComponent.setFileContent(fileContent); + HexEditorFileContentMode defaultFileContentMode = parserComponent + .determinePossibleFileContentModes(); + HexEditorCharacterSet defaultCharacterSet = HexEditorCharacterSet + .getDefaultCharacterSet(defaultFileContentMode); + parserComponent.setFileContentMode(defaultFileContentMode); + parserComponent.setCharacterSet(defaultCharacterSet); + + setPartName(fileName); + + } + + public int getBytesPerRow() { + return parserComponent.getBytesPerRow(); + } + + @Override + public void addSelectionChangedListener(ISelectionChangedListener listener) { + // Nothing. + } + + @Override + public void removeSelectionChangedListener( + ISelectionChangedListener listener) { + // Nothing. + } + + @Override + public HexEditorSelection getSelection() { + + Point selection = textField.getSelection(); + + // if (selection.x == selection.y) { + // return null; + // } + + // BasePlugin.getInstance().log( + // "HexEditor selection.x={0} selection.y={1}", + // new Object[] { String.valueOf(selection.x), + // String.valueOf(selection.y) }); + + return parserComponent.getSelection(selection.x, selection.y); + } + + @Override + public void setSelection(ISelection selection) { + // Single range selection? + if (selection instanceof HexEditorSelection) { + HexEditorSelection hexEditorSelection = (HexEditorSelection) selection; + int textStartOffset = parserComponent + .getByteTextOffset(hexEditorSelection.getStartOffset()); + int textEndOffset = parserComponent + .getByteTextOffset(hexEditorSelection.getEndOffset()); + setSelectionOffsets(textStartOffset, textEndOffset); + // Range of outline tree objects? + } else if (selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + + if (structuredSelection.getFirstElement() instanceof HexEditorContentOutlineTreeObject) { + Iterator<?> i = ((IStructuredSelection) selection).iterator(); + + int textStartOffset = Integer.MAX_VALUE; + int textEndOffset = Integer.MIN_VALUE; + while (i.hasNext()) { + HexEditorContentOutlineTreeObject treeObject = (HexEditorContentOutlineTreeObject) i + .next(); + textStartOffset = Math.min(treeObject.getTextStartOffset(), + textStartOffset); + textEndOffset = Math.max(treeObject.getTextEndOffset(), + textEndOffset); + } + setSelectionOffsets(textStartOffset, textEndOffset); + } + + } + setFocus(); + } + + private void setSelectionOffsets(int textStartOffset, int textEndOffset) { + try { + // Mark complete selection area. This also scrolls to + // the end of the area. + textField.setSelection(new Point(textStartOffset, textEndOffset)); + // + // // But we want to see start of the selection are, so + // // position explicitly. + textField.setTopIndex(textField.getContent().getLineAtOffset( + textStartOffset)); + } catch (IllegalArgumentException x) { + // Ignore + } + } + + /** + * Gets the file path for saving the current selection. Called by + * {@link HexEditorSaveSelectionAsCommandHandler }. + * + * @return The file path, not <code>null</code>. + */ + public String getSelectionSaveFilePath() { + String result = "Selection"; + String extension = ".bin"; + if (parserComponent.getFileContentMode().equals( + HexEditorFileContentMode.ATARI_DISK_IMAGE_K_FILE)) { + extension = ".xex"; + } + + if (ioFile != null) { + result = ioFile.getAbsolutePath(); + int index = result.lastIndexOf('.'); + if (index >= 0) { + result = result.substring(0, index); + } + } + + result += extension; + + return result; + } + + @Override + public boolean isDirty() { + return false; + } + + @Override + public void doSave(IProgressMonitor monitor) { + // Nothing. + + } + + @Override + public boolean isSaveAsAllowed() { + return false; + } + + @Override + public void doSaveAs() { + // Nothing. + } + + /** + * {@inheritDoc} + */ + @Override + public MessageManager getMessageManager() { + return messageManager; + } + + private void dataFromUi() { + messageManager.clearMessages(); + parserComponent.setFileContentMode(fileContentModeField.getValue()); + parserComponent.setCharacterSet(characterSetField.getValue()); + int bytesPerRow = bytesPerRowField.getValue(); + if (bytesPerRow < 1) { + bytesPerRow = 16; + } else if (bytesPerRow > 256) { + bytesPerRow = 256; + } + parserComponent.setBytesPerRow(bytesPerRow); + } + + private void dataToUi() { + + // File content size. + String text = TextUtility.format( + Texts.HEX_EDITOR_FILE_CONTENT_SIZE_FIELD_TEXT, + HexUtility.getLongValueHexString(parserComponent + .getFileContent().length), NumberUtility + .getLongValueDecimalString(parserComponent + .getFileContent().length)); + fileContentSizeField.setValue(text); + + // File content mode. + fileContentModeField.setValue(parserComponent.getFileContentMode()); + + // Character set. + HexEditorCharacterSet characterSet = parserComponent.getCharacterSet(); + characterSetField.setValue(characterSet); + if (!textField.getFont().equals(characterSet.getFont())) { + textField.setFont(characterSet.getFont()); + } + + // Bytes per Row + bytesPerRowField.setValue(parserComponent.getBytesPerRow()); + + if (parserComponent.isParsingFileContentRequired()) { + StyledString styledString = parserComponent.parseFileContent(); + textField.setText(styledString.getString()); + textField.setStyleRanges(styledString.getStyleRanges()); + + if (contentOutlinePage != null) { + contentOutlinePage.setInput(parserComponent.getOutlineBlocks()); + } + } + + messageManager.displayMessages(); + } + + /** + * {@inheritDoc} + */ + @Override + public void performAction(Action action) { + try { + + ISelection oldSelection = null; + dataFromUi(); + + switch (action.getId()) { + case Actions.FILE_CONTENT_MODE_CHANGED: + case Actions.CHARACTER_SET_TYPE_CHANGED: + case Actions.BYTES_PER_ROW_CHANGED: + oldSelection = getSelection(); + break; + } + + dataToUi(); + if (oldSelection != null) { + setSelection(oldSelection); + } + } catch (Exception ex) { + BasePlugin.getInstance().showError(getSite().getShell(), + "Error in update()", ex); + } + + } + + /** + * Called by {@link HexEditorClipboardCommandHandler}. + * + * @param bytes + * The byte array to be pasted, may be empty, not + * <code>null</code>. + * + * TODO Hex paste is not working yet + */ + final void pasteFromClipboard(byte[] bytes) { + if (bytes == null) { + throw new IllegalArgumentException( + "Parameter 'bytes' must not be null."); + } + HexEditorSelection selection = getSelection(); + byte[] newFileContent; + + // If there is no end offset, we insert the new bytes. + if (selection.getEndOffset() != HexEditorParserComponent.UNDEFINED_OFFSET) { + int selectionLength = selection.getEndOffset() + - selection.getStartOffset() + 1; + newFileContent = new byte[parserComponent.getFileContent().length + - selectionLength + bytes.length]; + System.arraycopy(parserComponent.getFileContent(), 0, + newFileContent, 0, selection.getStartOffset()); + System.arraycopy(bytes, 0, newFileContent, + selection.getStartOffset(), bytes.length); + int length = parserComponent.getFileContent().length + - selection.getEndOffset() - 1; + if (length > 0) { + // TODO Hex paste is not working yet + System.arraycopy(parserComponent.getFileContent(), + selection.getEndOffset(), + + newFileContent, selection.getStartOffset() + + bytes.length, length); + } + messageManager + .sendMessage( + 0, + IStatus.OK, + "${0} ({1}) bytes pasted from clipboard to replace ${2} ({3}) bytes ", + HexUtility.getLongValueHexString(bytes.length), + NumberUtility + .getLongValueDecimalString(bytes.length), + HexUtility.getLongValueHexString(selectionLength), + NumberUtility + .getLongValueDecimalString(selectionLength)); + } else { + // If there is and end offset, we replace the selection with the new + // bytes. + newFileContent = parserComponent.getFileContent(); + messageManager.sendMessage(0, IStatus.OK, + "${0} ({1}) bytes inserted from clipboard", + HexUtility.getLongValueHexString(bytes.length), + NumberUtility.getLongValueDecimalString(bytes.length)); + } + + parserComponent.setFileContent(newFileContent); + dataToUi(); + + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorCharacterSet.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorCharacterSet.java new file mode 100644 index 00000000..3b166a6c --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorCharacterSet.java @@ -0,0 +1,270 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import java.io.File; +import java.util.Map; +import java.util.TreeMap; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Display; + +import com.wudsn.ide.base.BasePlugin; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.ResourceUtility; + +/** + * Logical character set with physical font and character mapping. + * + * @since 1.7.0 + */ +enum HexEditorCharacterSet { + ASCII, ATARI_ATASCII, ATARI_ATASCII_SCREEN_CODE, ATARI_INTERNATIONAL, ATARI_INTERNATIONAL_SCREEN_CODE, C64_PETSCII_UPPER_CASE, C64_PETSCII_LOWER_CASE; + + /** + * Data class to encapsulate lazy loading and reuse of SWT fonts. + */ + private final static class Data { + + // Atari and C64 font by Mark Simonson, marksim@bitstream.net, + // http://www2.bitstream.net/~marksim/atarimac + private final static String ATARI_FONT_PATH = "fonts/atari8/AtariClassic-Regular.ttf"; + private final static String ATARI_FONT_NAME = "Atari Classic"; + private final static String ATARI_INT_FONT_PATH = "fonts/atari8/AtariClassicInt-Regular.ttf"; + private final static String ATARI_INT_FONT_NAME = "Atari Classic Int"; + private final static int ATARI_FONT_SIZE = 6; + + private final static String C64_FONT_PATH = "fonts/c64/C64Classic-Regular.ttf"; + private final static String C64_FONT_NAME = "C64 Classic"; + private final static int C64_FONT_SIZE = 6; + + private static Map<HexEditorCharacterSet, Data> instanceMap; + private static Map<String, Font> fontMap; + + Font font; + char[] characterMapping; + + static { + instanceMap = new TreeMap<HexEditorCharacterSet, Data>(); + fontMap = new TreeMap<String, Font>(); + } + + /** + * Gets a data instance base on the font type. + * + * @param type + * The font type, not <code>null</code>. + * + * @return The instance, not <code>null</code>. + */ + public static Data getInstance(HexEditorCharacterSet type) { + if (type == null) { + throw new IllegalArgumentException("Parameter 'type' must not be null."); + } + Data result; + synchronized (instanceMap) { + result = instanceMap.get(type); + // Add "|| true" below to disable caching for debugging + // purposes. + if (result == null) { + result = new Data(); + result.font = null; + result.characterMapping = new char[256]; + String fontPath = null; + String fontName = ""; + int fontSize = -1; + switch (type) { + case ASCII: + for (int i = 0; i < 256; i++) { + // 7-bit ASCII + int charValue = i & 0x7f; + // Convert control characters to "." + if (charValue < 0x20) { + charValue = '.'; + } + result.characterMapping[i] = (char) charValue; + } + break; + case ATARI_ATASCII: + fontPath = ATARI_FONT_PATH; + fontName = ATARI_FONT_NAME; + fontSize = ATARI_FONT_SIZE; + result.setIdentityMapping(); + break; + case ATARI_ATASCII_SCREEN_CODE: + fontPath = ATARI_FONT_PATH; + fontName = ATARI_FONT_NAME; + fontSize = ATARI_FONT_SIZE; + result.setAtariScreenCodeMapping(); + break; + case ATARI_INTERNATIONAL: + fontPath = ATARI_INT_FONT_PATH; + fontName = ATARI_INT_FONT_NAME; + fontSize = ATARI_FONT_SIZE; + result.setIdentityMapping(); + break; + case ATARI_INTERNATIONAL_SCREEN_CODE: + fontPath = ATARI_INT_FONT_PATH; + fontName = ATARI_INT_FONT_NAME; + fontSize = ATARI_FONT_SIZE; + result.setAtariScreenCodeMapping(); + break; + case C64_PETSCII_UPPER_CASE: + fontPath = C64_FONT_PATH; + fontName = C64_FONT_NAME; + fontSize = C64_FONT_SIZE; + result.setIdentityMapping(); + break; + case C64_PETSCII_LOWER_CASE: + fontPath = C64_FONT_PATH; + fontName = C64_FONT_NAME; + fontSize = C64_FONT_SIZE; + for (int i = 0; i < 256; i++) { + result.characterMapping[i] = (char) (0x200 + i); + } + break; + default: + throw new IllegalArgumentException("Unsupported font type " + type + "."); + } + + if (fontPath != null) { + // Check if temp file is already cached? + result.font = fontMap.get(fontPath); + if (result.font == null) { + File file = null; + try { + file = File.createTempFile("Data", null); + byte[] content = ResourceUtility.loadResourceAsByteArray(fontPath); + FileUtility.writeBytes(file, content); + } catch (Exception ex) { + BasePlugin.getInstance().logError( + "Error while copying font data of font '{0}' to temporary file.", + new Object[] { fontPath }, ex); + if (file != null) { + file.delete(); + } + file = null; + } + + // If temp file is present, + if (file != null) { + Device device = Display.getDefault(); + String absolutePath = file.getAbsolutePath(); + if (device.loadFont(absolutePath)) { + result.font = new Font(device, fontName, fontSize, SWT.NORMAL); + // Make sure the file is kept until the + // process + // ends. + file.deleteOnExit(); + fontMap.put(fontPath, result.font); + + } else { + // Loading failed, so no need to keep the + // file. + file.delete(); + } + } + } + } + + if (result.font == null) { + result.font = JFaceResources.getTextFont(); + } + instanceMap.put(type, result); + } + } + return result; + } + + private void setIdentityMapping() { + for (int i = 0; i < 256; i++) { + characterMapping[i] = (char) (0x100 + i); + } + } + + private void setAtariScreenCodeMapping() { + for (int i = 0; i < 256; i++) { + int charValue = i & 0x7f; + if (charValue < 0x40) { + charValue = charValue + 0x20; + } else if (charValue < 0x60) { + charValue = charValue - 0x40; + } + if (i > 0x80) { + charValue |= 0x80; + } + characterMapping[i] = (char) (0x100 + charValue); + } + } + + /** + * Creation is private. + */ + private Data() { + } + + } + + /** + * Determines the default character set for a given file content mode. + * + * @param fileContentMode + * The file content mode, not <code>null</code>. + * @return The default character set, not <code>null</code>. + */ + public static HexEditorCharacterSet getDefaultCharacterSet(HexEditorFileContentMode fileContentMode) { + if (fileContentMode == null) { + throw new IllegalArgumentException("Parameter 'fileContentMode' must not be null."); + } + switch (fileContentMode.getHardware()) { + case GENERIC: + return ASCII; + case ATARI8BIT: + return ATARI_ATASCII; + case C64: + return C64_PETSCII_UPPER_CASE; + } + throw new IllegalArgumentException("File content mode " + fileContentMode + " has an unknown hardware " + + fileContentMode.getHardware()); + } + + /** + * Gets the SWT font. + * + * @return The SWT font, not <code>null</code>. + */ + public Font getFont() { + return Data.getInstance(this).font; + } + + /** + * Gets the character mapping. + * + * @return The character mapping as an array of 256 char values, not + * <code>null</code>. + */ + public char[] getCharacterMapping() { + return Data.getInstance(this).characterMapping; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorClipboardCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorClipboardCommandHandler.java new file mode 100644 index 00000000..a9dd3e44 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorClipboardCommandHandler.java @@ -0,0 +1,184 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.dnd.Transfer; +import org.eclipse.ui.IWorkbenchSite; +import org.eclipse.ui.handlers.HandlerUtil; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; + +/** + * The handler for the commands to copy the selection to the clipboard. + * + * @author Peter Dell + */ +public final class HexEditorClipboardCommandHandler extends HexEditorSelectionCommandHandler { + + public static final class CommandIds { + + private CommandIds() { + } + + public static final String COPY = "com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardCommand"; + public static final String COPY_AS_HEX_VALUES = "com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsHexValuesCommand"; + public static final String COPY_AS_DECIMAL_VALUES = "com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesCommand"; + public static final String COPY_AS_DECIMAL_VALUES_BLOCK = "com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsDecimalValuesBlockCommand"; + public static final String COPY_AS_ASCII_STRING = "com.wudsn.ide.base.editor.hex.HexEditorCopyToClipboardAsASCIIStringCommand"; + public static final String PASTE = "com.wudsn.ide.base.editor.hex.HexEditorPasteFromClipboardCommand"; + } + + /** + * Creation is public. Called by extension point "org.eclipse.ui.handlers". + */ + public HexEditorClipboardCommandHandler() { + super(); + } + + @Override + protected void performAction() throws ExecutionException { + + byte[] bytes; + bytes = hexEditorSelection.getBytes(); + StringBuilder builder = new StringBuilder(5 * bytes.length); + String lineSeparator = System.getProperty("line.separator"); + + Object[] data; + Transfer[] transfers; + + int bytesPerRow = hexEditor.getBytesPerRow(); + if (commandId.equals(CommandIds.COPY) && !hexEditorSelection.isEmpty()) { + data = new Object[] { hexEditorSelection }; + transfers = new Transfer[] { HexEditorSelectionTransfer.getInstance() }; + copyToClipboard(bytes, data, transfers); + + } else if ((commandId.equals(CommandIds.COPY_AS_HEX_VALUES) + || commandId.equals(CommandIds.COPY_AS_DECIMAL_VALUES) + || commandId.equals(CommandIds.COPY_AS_DECIMAL_VALUES_BLOCK) || commandId + .equals(CommandIds.COPY_AS_ASCII_STRING)) && !hexEditorSelection.isEmpty()) { + if (commandId.equals(CommandIds.COPY_AS_HEX_VALUES)) { + builder.append(".byte "); + for (int i = 0; i < bytes.length; i++) { + builder.append("$"); + builder.append(HexUtility.getByteValueHexString(bytes[i] & 0xff)); + if ((i + 1) % bytesPerRow == 0) { + builder.append(lineSeparator); + if (i < bytes.length - 1) { + builder.append(".byte "); + } + } else { + if (i < bytes.length - 1) { + builder.append(','); + } + } + } + } else if (commandId.equals(CommandIds.COPY_AS_DECIMAL_VALUES) + || commandId.equals(CommandIds.COPY_AS_DECIMAL_VALUES_BLOCK)) { + // In block mode, decimals are aligned to 3 digits. + boolean block = commandId.equals(CommandIds.COPY_AS_DECIMAL_VALUES_BLOCK); + builder.append(".byte "); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i] & 0xff; + if (block) { + if (b < 10) { + builder.append(" "); + } else if (b < 100) { + builder.append(' '); + } + } + builder.append(Integer.toString(b)); + if ((i + 1) % bytesPerRow == 0) { + builder.append(lineSeparator); + if (i < bytes.length - 1) { + builder.append(".byte "); + } + } else { + if (i < bytes.length - 1) { + builder.append(','); + } + } + } + } else if (commandId.equals(CommandIds.COPY_AS_ASCII_STRING)) { + for (int i = 0; i < bytes.length; i++) { + char c = (char) (bytes[i] & 0xff); + builder.append(c); + } + } else { + throw new IllegalArgumentException("Unknown command '" + commandId + "'."); + } + data = new Object[] { builder.toString(), hexEditorSelection }; + transfers = new Transfer[] { TextTransfer.getInstance(), HexEditorSelectionTransfer.getInstance() }; + copyToClipboard(bytes, data, transfers); + + } else if (commandId.equals(CommandIds.PASTE)) { + pasteFromClipboard(); + } + + } + + private void copyToClipboard(byte[] bytes, Object[] data, Transfer[] transfers) throws ExecutionException { + if (bytes == null) { + throw new IllegalArgumentException("Parameter 'bytes' must not be null."); + } + if (data == null) { + throw new IllegalArgumentException("Parameter 'data' must not be null."); + } + if (transfers == null) { + throw new IllegalArgumentException("Parameter 'transfers' must not be null."); + } + IWorkbenchSite site = HandlerUtil.getActiveSiteChecked(event); + Clipboard clipboard = new Clipboard(site.getShell().getDisplay()); + try { + + clipboard.setContents(data, transfers); + + } finally { + clipboard.dispose(); + } + + // INFO: ${0} ({1}) bytes copied to clipboard. + messageManager.sendMessage(0, IStatus.OK, Texts.MESSAGE_I302, HexUtility.getLongValueHexString(bytes.length), + NumberUtility.getLongValueDecimalString(bytes.length)); + } + + private void pasteFromClipboard() throws ExecutionException { + + IWorkbenchSite site = HandlerUtil.getActiveSiteChecked(event); + Clipboard clipboard = new Clipboard(site.getShell().getDisplay()); + try { + + Object data = clipboard.getContents(HexEditorSelectionTransfer.getInstance()); + if (data != null) { + byte[] bytes = ((HexEditorSelection) data).getBytes(); + hexEditor.pasteFromClipboard(bytes); + + } + } finally { + clipboard.dispose(); + } + + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineLabelProvider.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineLabelProvider.java new file mode 100644 index 00000000..6b0feec7 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineLabelProvider.java @@ -0,0 +1,86 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.swt.graphics.Image; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Label provider for the tree objects in the outline page. + * + * @author Peter Dell + */ +final class HexEditorContentOutlineLabelProvider extends + DelegatingStyledCellLabelProvider { + + /** Outline segment image */ + private final Image segmentImage; + + private static class HexEditorStyledLabelProvider extends LabelProvider + implements IStyledLabelProvider { + + /** + * Creation is local. + */ + HexEditorStyledLabelProvider() { + + } + + @Override + public StyledString getStyledText(Object element) { + if (element == null) { + throw new IllegalArgumentException( + "Parameter 'element' must not be null."); + } + HexEditorContentOutlineTreeObject treeObject; + treeObject = (HexEditorContentOutlineTreeObject) element; + return treeObject.getStyledString(); + } + } + + /** + * Creates a new instance. + * + * Called by + * {@link HexEditorContentOutlinePage#createControl(org.eclipse.swt.widgets.Composite)} + * . + */ + HexEditorContentOutlineLabelProvider() { + super(new HexEditorStyledLabelProvider()); + BasePlugin plugin; + plugin = BasePlugin.getInstance(); + segmentImage = plugin + .getImage("hex-editor-segment-16x16.gif"); + } + + @Override + public Image getImage(Object element) { + Image result; + + result = segmentImage; + + return result; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlinePage.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlinePage.java new file mode 100644 index 00000000..0fd57cb8 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlinePage.java @@ -0,0 +1,88 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.views.contentoutline.ContentOutlinePage; + +/** + * Content outline page for the hex editor. + * + * @author Peter Dell + */ +final class HexEditorContentOutlinePage extends ContentOutlinePage { + + private HexEditor editor; + private Object input; + + HexEditorContentOutlinePage(HexEditor editor) { + if (editor == null) { + throw new IllegalArgumentException( + "Parameter 'editor' must not be null."); + } + this.editor = editor; + + } + + @Override + public void createControl(Composite parent) { + super.createControl(parent); + + TreeViewer viewer = getTreeViewer(); + viewer.getControl().setFont(JFaceResources.getTextFont()); + viewer + .setContentProvider(new HexEditorContentOutlineTreeContentProvider()); + viewer.setLabelProvider(new HexEditorContentOutlineLabelProvider()); + viewer.addSelectionChangedListener(this); + viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS); + + updateTreeView(); + } + + @Override + public void selectionChanged(SelectionChangedEvent event) { + super.selectionChanged(event); + + ISelection selection = event.getSelection(); + editor.setSelection(selection); + } + + void setInput(Object input) { + if (input == null) { + throw new IllegalArgumentException( + "Parameter 'input' must not be null."); + } + this.input = input; + updateTreeView(); + } + + private void updateTreeView() { + TreeViewer viewer = getTreeViewer(); + if (viewer != null) { + viewer.setInput(input); + } + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineTreeContentProvider.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineTreeContentProvider.java new file mode 100644 index 00000000..b2182a6c --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineTreeContentProvider.java @@ -0,0 +1,94 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import java.util.List; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * Tree content provider to {@link HexEditorContentOutlinePage}. + * + * @author Peter Dell + */ +final class HexEditorContentOutlineTreeContentProvider implements + ITreeContentProvider { + + HexEditorContentOutlineTreeContentProvider() { + } + + /** + * {@inheritDoc} + */ + @Override + public Object[] getChildren(Object parentElement) { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getParent(Object element) { + + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean hasChildren(Object element) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public Object[] getElements(Object inputElement) { + Object[] result; + if (inputElement instanceof List<?>) { + result = ((List<?>) inputElement).toArray(); + } else { + result = null; + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() { + + } + + /** + * {@inheritDoc} + */ + @Override + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineTreeObject.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineTreeObject.java new file mode 100644 index 00000000..65842ab2 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorContentOutlineTreeObject.java @@ -0,0 +1,148 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.jface.viewers.StyledString; + +/** + * The object representing a node in the hex editor content outline tree. + * + * @author Peter Dell + */ +public final class HexEditorContentOutlineTreeObject { + + private final StyledString styledString; + private int fileStartOffset; + private int textStartOffset; + private int fileEndOffset; + private int textEndOffset; + + /** + * Create a new instance. + * + * @param styledString + * The styled string of the instance, may be empty not + * <code>null</code>. + */ + public HexEditorContentOutlineTreeObject(StyledString styledString) { + if (styledString == null) { + throw new IllegalArgumentException( + "Parameter 'styledString' must not be null."); + } + this.styledString = new StyledString().append(styledString); + } + + /** + * Gets the styled string of the object. + * + * @return The styled string, not <code>null</code>. + */ + public StyledString getStyledString() { + return styledString; + } + + /** + * Gets the start offset of the tree object in the file. + * + * @return The start offset, a non-negative integer. + */ + public int getFileStartOffset() { + return fileStartOffset; + } + + /** + * Sets the start offset of the tree object in the file. + * + * @param fileOffset + * The start offset, a non-negative integer or <code>-1</code> if the offset is not defined. + */ + public void setFileStartOffset(int fileOffset) { + + this.fileStartOffset = fileOffset; + } + + /** + * Gets the end offset of the tree object in the file. + * + * @return The end offset, a non-negative integer or <code>-1</code> if the offset is not defined. + */ + public int getFileEndOffset() { + return fileEndOffset; + } + + /** + * Sets the end offset of the tree object in the file or <code>-1</code> if the offset is not defined. + * + * @param fileOffset + * The end offset, a non-negative integer or <code>-1</code> if the offset is not defined. + */ + public void setFileEndOffset(int fileOffset) { + + this.fileEndOffset = fileOffset; + } + + /** + * Gets the start offset of the tree object in the text. + * + * @return The offset, a non-negative integer. + */ + public int getTextStartOffset() { + return textStartOffset; + } + + /** + * Sets text start offset of the tree object in the text + * + * @param textOffset + * The offset, a non-negative integer. + */ + public void setTextStartOffset(int textOffset) { + if (textOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'textOffset' must not be negative. Specified value is " + + textOffset + "."); + } + this.textStartOffset = textOffset; + } + + /** + * Gets the end offset of the tree object in the text. + * + * @return The offset, a non-negative integer. + */ + public int getTextEndOffset() { + return textEndOffset; + } + + /** + * Sets text end offset of the tree object in the text + * + * @param textOffset + * The offset, a non-negative integer. + */ + public void setTextEndOffset(int textOffset) { + if (textOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'textOffset' must not be negative. Specified value is " + + textOffset + "."); + } + this.textEndOffset = textOffset; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorFileContentMode.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorFileContentMode.java new file mode 100644 index 00000000..6cdba4ca --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorFileContentMode.java @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +enum HexEditorFileContentMode { + + BINARY(Hardware.GENERIC), ATARI_COM_FILE(Hardware.ATARI8BIT), ATARI_DISK_IMAGE(Hardware.ATARI8BIT), ATARI_DISK_IMAGE_K_FILE( + Hardware.ATARI8BIT), ATARI_MADS_FILE(Hardware.ATARI8BIT), ATARI_SDX_FILE(Hardware.ATARI8BIT), ATARI_SAP_FILE( + Hardware.ATARI8BIT), C64_PRG_FILE(Hardware.C64); + + private Hardware hardware; + + private HexEditorFileContentMode(Hardware hardware) { + if (hardware == null) { + throw new IllegalArgumentException("Parameter 'hardware' must not be null."); + } + this.hardware = hardware; + } + + public Hardware getHardware() { + return hardware; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorOpenCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorOpenCommandHandler.java new file mode 100644 index 00000000..c7e834bb --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorOpenCommandHandler.java @@ -0,0 +1,39 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import com.wudsn.ide.base.editor.CommonOpenEditorCommandHandler; + + +/** + * The action to open the hex editor from the context menu. + * + * @author Peter Dell + */ +public final class HexEditorOpenCommandHandler extends + CommonOpenEditorCommandHandler { + + /** + * Creation is public. Called by extension "org.eclipse.ui.popupMenus". + */ + public HexEditorOpenCommandHandler() { + super(HexEditor.ID); + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorParser.java new file mode 100644 index 00000000..1d2314ca --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorParser.java @@ -0,0 +1,226 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.StyledString.Styler; + +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.StringUtility; +import com.wudsn.ide.base.common.TextUtility; + +public abstract class HexEditorParser { + + private HexEditorParserComponent owner; + protected Styler offsetStyler; + protected Styler addressStyler; + + /** + * Creation is protected. + */ + protected HexEditorParser() { + + } + + /** + * Initialized by owner. + * + * @param owner + * The owner, not <code>null</code>. + * @param offsetStyler + * The offset styler, not <code>null</code>. + * @param addressStyler + * The address styler, not <code>null</code>. + */ + void init(HexEditorParserComponent owner, Styler offsetStyler, Styler addressStyler) { + if (owner == null) { + throw new IllegalArgumentException("Parameter 'owner' must not be null."); + } + if (offsetStyler == null) { + throw new IllegalArgumentException("Parameter 'offsetStyler' must not be null."); + } + if (addressStyler == null) { + throw new IllegalArgumentException("Parameter 'offsetStyler' must not be null."); + } + this.owner = owner; + this.offsetStyler = offsetStyler; + this.addressStyler = addressStyler; + } + + /** + * Public API for parsing. + * + * @param contentBuilder + * The content builder, not <code>null</code>. + * @return <code>true</code> if parsing was OK, <code>false</code>otherwise. + */ + public abstract boolean parse(StyledString contentBuilder); + + /** + * Gets the length of the file content. + * + * @return The length of the file content, a non-negative integer. + */ + protected final int getFileContentLength() { + return owner.getFileContent().length; + } + + /** + * Gets a byte from the file content. + * + * @param offset + * The offset, a non-negative integer. + * @return The byte from the file content. + */ + protected final int getFileContentByte(int offset) { + return owner.getFileContentByte(offset); + } + + /** + * Gets a word from the file content. + * + * @param offset + * The offset, a non-negative integer. + * @return The word from the file content. + */ + protected final int getFileContentWord(int offset) { + return owner.getFileContentWord(offset); + } + + /** + * Prints a block header in the context area and adds a block to the + * outline. + * + * @param contentBuilder + * The content builder, not <code>null</code>. + * @param blockHeaderText + * The header text for the block, may be empty, not + * <code>null</code>. + * @param blockHeaderNumber + * The block count or <code>-1</code> if count shall not be + * displayed. + * @param blockHeaderParameterText + * The pattern text of the form "{0}-{1} ({2})" + * @param offset + * The start offset, a non-negative integer. + * @param startAddress + * The start address, a non-negative integer. + * @param endAddress + * The end address, a non-negative integer. + * + * @return The tree object representing the block. + */ + protected final HexEditorContentOutlineTreeObject printBlockHeader(StyledString contentBuilder, + String blockHeaderText, int blockHeaderNumber, String blockHeaderParameterText, int offset, + int startAddress, int endAddress) { + + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + int blockLength = endAddress - startAddress + 1; + String blockHeaderNumberText; + if (blockHeaderNumber >= 0) { + blockHeaderNumberText = NumberUtility.getLongValueDecimalString(blockHeaderNumber); + } else { + blockHeaderNumberText = ""; + } + int length = Math.max(4, HexUtility.getLongValueHexLength(endAddress)); + String hexText = TextUtility.format(blockHeaderParameterText, + HexUtility.getLongValueHexString(startAddress, length), + HexUtility.getLongValueHexString(endAddress, length), + HexUtility.getLongValueHexString(blockLength, length)); + + String decimalText = TextUtility.format(blockHeaderParameterText, + NumberUtility.getLongValueDecimalString(startAddress), + NumberUtility.getLongValueDecimalString(endAddress), + NumberUtility.getLongValueDecimalString(blockLength)); + + StyledString styledString; + styledString = new StyledString(); + styledString.append(blockHeaderText, offsetStyler); + if (blockHeaderNumber >= 0) { + styledString.append(" "); + styledString.append(blockHeaderNumberText, offsetStyler); + + } + if (StringUtility.isSpecified(blockHeaderParameterText)) { + styledString.append(" : "); + styledString.append(hexText, addressStyler); + styledString.append(" : "); + styledString.append(decimalText); + } + + contentBuilder.append(blockHeaderText, offsetStyler); + if (blockHeaderNumber >= 0) { + contentBuilder.append(" "); + contentBuilder.append(blockHeaderNumberText, offsetStyler); + } + contentBuilder.append("\n"); + return owner.printBlockHeader( contentBuilder, styledString,offset); + } + + /** + * Prints a block header in the context area and adds a block to the + * outline. + * + * @param contentBuilder + * The content builder, not <code>null</code>. + * @param headerStyledString + * The style string for the block header in the outline, not + * <code>null</code>. + * @param offset + * The start offset, a non-negative integer. + * + * @return The tree object representing the block. + */ + protected final HexEditorContentOutlineTreeObject printBlockHeader(StyledString contentBuilder, + StyledString headerStyledString, int offset) { + return owner.printBlockHeader(contentBuilder, headerStyledString, offset); + } + + /** + * Prints the last block in case if contains an error like the wrong number + * of bytes. + * + * @param contentBuilder + * The content builder, not <code>null</code>. + * @param errorText + * The error text, not empty and not <code>null</code>. + * @param length + * The length of the last block, a non-negative integer. + * @param offset + * The offset of the last block, a non-negative integer. + */ + protected final void printBlockWithError(StyledString contentBuilder, String errorText, int length, int offset) { + owner.printBlockWithError(contentBuilder, errorText, length, offset); + } + + protected final void skipByteTextIndex(int offset) { + owner.skipByteTextIndex(offset); + + } + + protected final int printBytes(HexEditorContentOutlineTreeObject treeObject, StyledString contentBuilder, + int offset, int maxOffset, boolean withStartAddress, int startAddress) { + return owner.printBytes(treeObject, contentBuilder, offset, maxOffset, withStartAddress, startAddress); + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorParserComponent.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorParserComponent.java new file mode 100644 index 00000000..4b089099 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorParserComponent.java @@ -0,0 +1,727 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.editor.hex; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.preference.JFacePreferences; +import org.eclipse.jface.viewers.StyledString; +import org.eclipse.jface.viewers.StyledString.Styler; +import org.eclipse.swt.graphics.TextStyle; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.EnumUtility; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.Profiler; +import com.wudsn.ide.base.common.TextUtility; +import com.wudsn.ide.base.editor.hex.HexEditor.MessageIds; +import com.wudsn.ide.base.editor.hex.parser.AtariCOMParser; +import com.wudsn.ide.base.editor.hex.parser.AtariDiskImageKFileParser; +import com.wudsn.ide.base.editor.hex.parser.AtariDiskImageParser; +import com.wudsn.ide.base.editor.hex.parser.AtariMADSParser; +import com.wudsn.ide.base.editor.hex.parser.AtariParser; +import com.wudsn.ide.base.editor.hex.parser.AtariSAPParser; +import com.wudsn.ide.base.editor.hex.parser.AtariSDXParser; +import com.wudsn.ide.base.editor.hex.parser.BinaryParser; +import com.wudsn.ide.base.editor.hex.parser.C64PRGParser; +import com.wudsn.ide.base.gui.MessageManager; + +final class HexEditorParserComponent { + + public static final int UNDEFINED_OFFSET = -1; + private final static int BYTES_PER_ROW = 16; + private static final int INT_FF = 0xff; + + // Callback API. + private MessageManager messageManager; + + // Style components. + private Styler offsetStyler; + private Styler addressStyler; + private Styler charStyler; + private Styler errorStyler; + + // File content and state. + private boolean fileContentParsed; + private HexEditorFileContentMode fileContentMode; + private byte[] fileContent; + private int bytesPerRow; + private HexEditorCharacterSet characterSet; + + // Previous state with regards to parsing. + private HexEditorFileContentMode oldFileContentMode; + private byte[] oldFileContent; + private int oldBytesPerRow; + private HexEditorCharacterSet oldCharacterSet; + + // Parsing state. + private List<HexEditorFileContentMode> possibleFileContentModes; + private List<HexEditorContentOutlineTreeObject> outlineBlocks; + private int[] byteTextOffsets; + private int byteTextIndex; + + // Line buffers for binary to hex and char conversion. + private char[] hexChars; + private char[] hexBuffer; + private char[] charBuffer; + + public HexEditorParserComponent(MessageManager messageManager) { + if (messageManager == null) { + throw new IllegalArgumentException( + "Parameter 'messageManager' must not be null."); + } + this.messageManager = messageManager; + + // Get static stylers for the styled string. + offsetStyler = StyledString.createColorRegistryStyler( + JFacePreferences.COUNTER_COLOR, null); + addressStyler = StyledString.createColorRegistryStyler( + JFacePreferences.QUALIFIER_COLOR, null); + charStyler = StyledString.createColorRegistryStyler( + JFacePreferences.HYPERLINK_COLOR, null); + + charStyler = new Styler() { + + @Override + public void applyStyles(TextStyle textStyle) { + textStyle.font = null; + + } + + }; + + errorStyler = StyledString.createColorRegistryStyler( + JFacePreferences.ERROR_COLOR, null); + + // Initialize hex chars and normal character set type. + hexChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + characterSet = HexEditorCharacterSet.ASCII; + + clear(); + + } + + private void clear() { + // Initialize with empty file. + fileContentParsed = false; + fileContentMode = HexEditorFileContentMode.BINARY; + setFileContent(new byte[0]); + characterSet = HexEditorCharacterSet.ASCII; + bytesPerRow = BYTES_PER_ROW; + + oldFileContentMode = null; + oldFileContent = null; + oldCharacterSet = null; + oldBytesPerRow = 0; + + possibleFileContentModes = new ArrayList<HexEditorFileContentMode>(); + outlineBlocks = new ArrayList<HexEditorContentOutlineTreeObject>(); + } + + public void setFileContent(byte[] fileContent) { + if (fileContent == null) { + throw new IllegalArgumentException( + "Parameter 'fileContent' must not be null."); + } + this.fileContent = fileContent; + initByteTextOffsets(); + } + + /** + * Reserve enough space for the lookup table that maps text offsets to file + * offsets. + */ + private void initByteTextOffsets() { + // Twice the space, because some format display the content twice, for + // example ATARI_DISK_IMAGE_K_FILE. + byteTextOffsets = new int[fileContent.length * 2]; + byteTextIndex = 0; + } + + /** + * Gets the current file content. + * + * @return The file content, may be empty or <code>null</code>. + */ + public byte[] getFileContent() { + return fileContent; + } + + /** + * Determines the possible file content modes based on the file content. + * + * @return The suggested default file content mode, not <code>null</code>. + */ + public HexEditorFileContentMode determinePossibleFileContentModes() { + + HexEditorFileContentMode result = HexEditorFileContentMode.BINARY; + possibleFileContentModes.clear(); + possibleFileContentModes.add(fileContentMode); + + HexEditorFileContentMode defaultMode = result; + + // COM header present? + if (fileContent.length > 6) { + // AtariDOS COM file? + if (getFileContentWord(0) == AtariParser.COM_HEADER) { + int startAddress = getFileContentWord(2); + int endAddress = getFileContentWord(4); + if (startAddress >= 0 && endAddress >= startAddress) { + defaultMode = HexEditorFileContentMode.ATARI_COM_FILE; + possibleFileContentModes.add(defaultMode); + + if (fileContent.length > 16) { + if (getFileContentWord(6) == AtariMADSParser.RELOC_HEADER) { + defaultMode = HexEditorFileContentMode.ATARI_MADS_FILE; + possibleFileContentModes.add(defaultMode); + } + } + // New default? + if (result.equals(HexEditorFileContentMode.BINARY)) { + result = defaultMode; + } + } + } // SpartaDOS X non relocatable file? + else if (getFileContentWord(0) == AtariSDXParser.NON_RELOC_HEADER) { + int startAddress = getFileContentWord(2); + int endAddress = getFileContentWord(4); + if (startAddress > 0 && endAddress >= startAddress) { + defaultMode = HexEditorFileContentMode.ATARI_SDX_FILE; + possibleFileContentModes.add(defaultMode); + // New default? + if (result.equals(HexEditorFileContentMode.BINARY)) { + result = defaultMode; + } + } + } // SpartaDOS X relocatable file? + else if (getFileContentWord(0) == AtariSDXParser.RELOC_HEADER + && fileContent.length > 8) { + int blockNumber = getFileContentByte(2); + if (blockNumber > 0) { + defaultMode = HexEditorFileContentMode.ATARI_SDX_FILE; + possibleFileContentModes.add(defaultMode); + // New default? + if (result.equals(HexEditorFileContentMode.BINARY)) { + result = defaultMode; + } + } + } + } + + // ATR header present? + if ((fileContent.length > 16 && getFileContentByte(0) == 0x96 && getFileContentByte(1) == 0x02)) { + defaultMode = HexEditorFileContentMode.ATARI_DISK_IMAGE; + possibleFileContentModes.add(defaultMode); + + // Special case of k-file (converted COM file) + int offset = AtariDiskImageKFileParser.ATARI_DISK_IMAGE_K_FILE_COM_FILE_OFFSET; + if (fileContent.length > offset + 2 + && getFileContentWord(offset) == 0xffff) { + final int[] kFileBootHeader = new int[] { 0x00, 0x03, 0x00, + 0x07, 0x14, 0x07, 0x4C, 0x14, 0x07 }; + boolean kFileBootHeaderFound = true; + for (int i = 0; i < kFileBootHeader.length; i++) { + if (getFileContentByte(16 + i) != kFileBootHeader[i]) { + kFileBootHeaderFound = false; + } + } + if (kFileBootHeaderFound) { + defaultMode = HexEditorFileContentMode.ATARI_DISK_IMAGE_K_FILE; + possibleFileContentModes.add(defaultMode); + } + } + + // New default? + if (result.equals(HexEditorFileContentMode.BINARY)) { + result = defaultMode; + } + } + + // SAP header present? + if ((fileContent.length > 11 && getFileContentByte(0) == 0x53 && getFileContentByte(1) == 0x41) + && getFileContentByte(2) == 0x50) { + possibleFileContentModes + .add(HexEditorFileContentMode.ATARI_SAP_FILE); + // New default? + if (result.equals(HexEditorFileContentMode.BINARY)) { + result = HexEditorFileContentMode.ATARI_SAP_FILE; + } + } + + // PRG header present? + if ((fileContent.length > 2 && getFileContentWord(0) + + getFileContent().length - 2 < 0x10000)) { + possibleFileContentModes.add(HexEditorFileContentMode.C64_PRG_FILE); + int loadAddress = getFileContentWord(0); + if (result.equals(HexEditorFileContentMode.BINARY) + && loadAddress >= 0x800 && loadAddress < 0x2000) { + result = HexEditorFileContentMode.C64_PRG_FILE; + } + } + return result; + } + + /** + * Sets the file content for {@link #parseFileContent()}. + * + * @param fileContentMode + * The file content mode, not <code>null</code>. + */ + public void setFileContentMode(HexEditorFileContentMode fileContentMode) { + if (fileContentMode == null) { + throw new IllegalArgumentException( + "Parameter 'fileContentMode' must not be null."); + } + this.fileContentMode = fileContentMode; + } + + /** + * Gets the file content for {@link #parseFileContent()}. + * + * @return fileContentMode The file content mode, not <code>null</code>. + */ + public HexEditorFileContentMode getFileContentMode() { + return fileContentMode; + } + + /** + * Sets the character set type. + * + * @param characterSet + * The character set type, not <code>null</code>. + */ + public void setCharacterSet(HexEditorCharacterSet characterSet) { + if (characterSet == null) { + throw new IllegalArgumentException( + "Parameter 'characterSet' must not be null."); + } + + this.characterSet = characterSet; + } + + /** + * Gets the character set type. + * + * @return characterSet The character set type, not <code>null</code>. + */ + public HexEditorCharacterSet getCharacterSet() { + return characterSet; + } + + /** + * Sets the number of bytes per row for {@link #parseFileContent()}. + * + * @param bytesPerRow + * The number of bytes per row, a positive integer. + */ + public void setBytesPerRow(int bytesPerRow) { + if (bytesPerRow < 1) { + throw new IllegalArgumentException( + "Parameter 'bytesPerRow' must not be positive. Specified valie was " + + bytesPerRow + "."); + } + this.bytesPerRow = bytesPerRow; + } + + /** + * Gets the number of bytes per row for {@link #parseFileContent()}. + * + * @return The number of bytes per row, a positive integer. + */ + public int getBytesPerRow() { + return bytesPerRow; + } + + /** + * Determines if parsing is required. + * + * @return <code>true</code> if parsing is required, <code>false</code> + * otherwise. + */ + public boolean isParsingFileContentRequired() { + return !fileContentParsed + || !Arrays.equals(fileContent, oldFileContent) + || !fileContentMode.equals(oldFileContentMode) + || !characterSet.equals(oldCharacterSet) + || bytesPerRow != oldBytesPerRow; + } + + /** + * Parse the file content set with {@link #setFileContent(byte[])} according + * to the parameters set with + * {@link #setFileContentMode(HexEditorFileContentMode)}, + * {@link #setBytesPerRow(int)} and + * {@link #setCharacterSet(HexEditorCharacterSet)}. + * + * @return The styles string representing the content. + */ + public StyledString parseFileContent() { + + Profiler profiler = new Profiler(this); + profiler.begin("parseFileContent", fileContent.length + " bytes"); + + outlineBlocks.clear(); + initByteTextOffsets(); + + StyledString contentBuilder = new StyledString(); + HexEditorContentOutlineTreeObject treeObject; + String text = TextUtility.format(Texts.HEX_EDITOR_FILE_SIZE, + HexUtility.getLongValueHexString(fileContent.length), + NumberUtility.getLongValueDecimalString(fileContent.length)); + contentBuilder.append(text); + treeObject = new HexEditorContentOutlineTreeObject(contentBuilder); + treeObject.setFileStartOffset(0); + treeObject.setTextStartOffset(contentBuilder.length()); + outlineBlocks.add(treeObject); + + contentBuilder = new StyledString(); + if (!possibleFileContentModes.contains(fileContentMode)) { + messageManager.sendMessage(MessageIds.FILE_CONTENT_MODE, + IStatus.ERROR, Texts.MESSAGE_E300, + EnumUtility.getText(fileContentMode)); + return contentBuilder; + } + + if (fileContent.length > 0) { + boolean error; + HexEditorParser parser; + if (fileContentMode.equals(HexEditorFileContentMode.ATARI_COM_FILE)) { + parser = new AtariCOMParser(); + } else if (fileContentMode + .equals(HexEditorFileContentMode.ATARI_DISK_IMAGE)) { + parser = new AtariDiskImageParser(); + } else if (fileContentMode + .equals(HexEditorFileContentMode.ATARI_DISK_IMAGE_K_FILE)) { + parser = new AtariDiskImageKFileParser(); + } else if (fileContentMode + .equals(HexEditorFileContentMode.ATARI_MADS_FILE)) { + parser = new AtariMADSParser(); + } else if (fileContentMode + .equals(HexEditorFileContentMode.ATARI_SDX_FILE)) { + parser = new AtariSDXParser(); + } else if (fileContentMode + .equals(HexEditorFileContentMode.ATARI_SAP_FILE)) { + parser = new AtariSAPParser(); + } else if (fileContentMode + .equals(HexEditorFileContentMode.C64_PRG_FILE)) { + parser = new C64PRGParser(); + } else { + parser = new BinaryParser(); + } + + // Initialize the buffers for the hex and char conversion. + hexBuffer = new char[3 + bytesPerRow * 3 + 2]; + for (int i = 0; i < hexBuffer.length; i++) { + hexBuffer[i] = ' '; + } + hexBuffer[1] = ':'; + hexBuffer[hexBuffer.length - 2] = '|'; + charBuffer = new char[bytesPerRow + 1]; + charBuffer[charBuffer.length - 1] = '\n'; + + parser.init(this, offsetStyler, addressStyler); + error = parser.parse(contentBuilder); + if (error) { + messageManager.sendMessage(MessageIds.FILE_CONTENT_MODE, + IStatus.ERROR, Texts.MESSAGE_E301, + EnumUtility.getText(fileContentMode)); + } + } + + profiler.end("parseFileContent"); + + // Copy current state to state backup for change detection in {@link + // #isParsingFileContentRequired}, + fileContentParsed = true; + oldFileContentMode = fileContentMode; + oldFileContent = fileContent; + oldCharacterSet = characterSet; + oldBytesPerRow = bytesPerRow; + + return contentBuilder; + } + + /** + * Gets a byte from the file content. + * + * @param offset + * The offset, a non-negative integer. + * @return The byte from the file content. + */ + final int getFileContentByte(int offset) { + return fileContent[offset] & INT_FF; + } + + /** + * Gets a word from the file content. + * + * @param offset + * The offset, a non-negative integer. + * @return The word from the file content. + */ + final int getFileContentWord(int offset) { + return getFileContentByte(offset) + 256 + * getFileContentByte(offset + 1); + } + + /** + * Prints a block header in the context area and adds a block to the + * outline. + * + * @param contentBuilder + * The content builder, not <code>null</code>. + * @param headerStyledString + * The style string for the block header in the outline, not + * <code>null</code>. + * @param offset + * The start offset, a non-negative integer. + * + * @return The tree object representing the block. + */ + final HexEditorContentOutlineTreeObject printBlockHeader( + StyledString contentBuilder, StyledString headerStyledString, + int offset) { + + if (contentBuilder == null) { + throw new IllegalArgumentException( + "Parameter 'contentBuilder' must not be null."); + } + if (headerStyledString == null) { + throw new IllegalArgumentException( + "Parameter 'styledString' must not be null."); + } + HexEditorContentOutlineTreeObject treeObject; + + treeObject = new HexEditorContentOutlineTreeObject(headerStyledString); + treeObject.setFileStartOffset(offset); + treeObject.setTextStartOffset(contentBuilder.length()); + outlineBlocks.add(treeObject); + return treeObject; + } + + /** + * Prints the last block in case if contains an error like the wrong number + * of bytes. + * + * @param contentBuilder + * The content builder, not <code>null</code>. + * @param errorText + * The error text, not empty and not <code>null</code>. + * @param length + * The length of the last block, a non-negative integer. + * @param offset + * The offset of the last block, a non-negative integer. + */ + final void printBlockWithError(StyledString contentBuilder, + String errorText, int length, int offset) { + if (contentBuilder == null) { + throw new IllegalArgumentException( + "Parameter 'contentBuilder' must not be null."); + } + + if (errorText == null) { + throw new IllegalArgumentException( + "Parameter 'errorText' must not be null."); + } + HexEditorContentOutlineTreeObject treeObject; + StyledString styledString = new StyledString(errorText, errorStyler); + treeObject = new HexEditorContentOutlineTreeObject(styledString); + treeObject.setFileStartOffset(UNDEFINED_OFFSET); + treeObject.setFileEndOffset(UNDEFINED_OFFSET); + treeObject.setTextStartOffset(contentBuilder.length()); + treeObject.setTextEndOffset(contentBuilder.length()); + + outlineBlocks.add(treeObject); + contentBuilder.append(styledString); + contentBuilder.append("\n"); + offset = printBytes(treeObject, contentBuilder, offset, length - 1, + true, 0); + } + + final void skipByteTextIndex(int offset) { + byteTextIndex += offset; + } + + final int printBytes(HexEditorContentOutlineTreeObject treeObject, + StyledString contentBuilder, int offset, int maxOffset, + boolean withStartAddress, int startAddress) { + + if (offset < 0) { + throw new IllegalArgumentException( + "Parameter 'offset' must not be negative, specified value is " + + offset + "."); + } + if (maxOffset < 0) { + throw new IllegalArgumentException( + "Parameter 'offset' must not be negative, specified value is " + + maxOffset + "."); + } + int length = Math.max(4, HexUtility.getLongValueHexLength(maxOffset)); + char[] characterMapping = characterSet.getCharacterMapping(); + while (offset <= maxOffset) { + int contentBuilderLineStartOffset = contentBuilder.length(); + + contentBuilder.append( + HexUtility.getLongValueHexString(offset, length), + offsetStyler); + + if (withStartAddress) { + contentBuilder.append(" : "); + contentBuilder.append( + HexUtility.getLongValueHexString(startAddress, length), + addressStyler); + + } + + // Remember byte offset where the new line starts. + int contentBuilderStartOffset = contentBuilder.length(); + int h = 3; + for (int b = 0; b < bytesPerRow; b++) { + char highChar; + char lowChar; + char charValue; + if (offset > maxOffset) { + highChar = ' '; + lowChar = ' '; + charValue = ' '; + } else { + int byteValue = getFileContentByte(offset); + highChar = hexChars[byteValue >> 4]; + lowChar = hexChars[byteValue & 0xf]; + charValue = characterMapping[byteValue]; + byteTextOffsets[byteTextIndex++] = (b == 0 ? contentBuilderLineStartOffset + : contentBuilderStartOffset + h); + offset++; + startAddress++; + } + hexBuffer[h++] = highChar; + hexBuffer[h++] = lowChar; + h++; + charBuffer[b] = charValue; + } + contentBuilder.append(hexBuffer); + contentBuilder.append(charBuffer, charStyler); + } + treeObject.setFileEndOffset(offset); + treeObject.setTextEndOffset(contentBuilder.length()); + return offset; + } + + /** + * Gets the list of outline blocks determined by {@link #parseFileContent()} + * . + * + * @return The list of outline blocks, may be empty, not <code>null</code>. + */ + public List<HexEditorContentOutlineTreeObject> getOutlineBlocks() { + return outlineBlocks; + } + + /** + * Gets the selection represented by the start and end offset in the text + * field. + * + * @param x + * is the offset of the first selected character + * @param y + * is the offset after the last selected character. + * @return The selection or <code>null</code>. + */ + public HexEditorSelection getSelection(int x, int y) { + + int startOffset = UNDEFINED_OFFSET; + int endOffset = UNDEFINED_OFFSET; + int textOffset = 0; + for (int i = 0; i < byteTextIndex + && (startOffset == UNDEFINED_OFFSET || endOffset == UNDEFINED_OFFSET); i++) { + int nextTextOffset; + if (i < byteTextIndex - 1) { + nextTextOffset = byteTextOffsets[i + 1]; + } else { + nextTextOffset = Integer.MAX_VALUE; + } + if (startOffset == UNDEFINED_OFFSET && textOffset - 1 <= x + && x < nextTextOffset - 1) { + startOffset = i; + } + if (startOffset != UNDEFINED_OFFSET + && endOffset == UNDEFINED_OFFSET && textOffset < y + && y <= nextTextOffset) { + endOffset = i; + } + textOffset = nextTextOffset; + + } + + if (startOffset == UNDEFINED_OFFSET) { + return null; + } + int length; + byte[] bytes; + + length = endOffset - startOffset + 1; + // BasePlugin.getInstance().log("HexEditor.getSelection(): startOffset={0} endoffset={1} length={2}", + // new Object[] { String.valueOf(startOffset), + // String.valueOf(endOffset), String.valueOf(length) }); + // Length not empty and selection does not cross file end boundary. + if (length > 0 && endOffset < fileContent.length) { + // Reposition into first occurrence of in the file. + // This is relevant for the format that display the content more + // than once. + bytes = new byte[length]; + // startOffset = startOffset % fileContent.length; + // endOffset = endOffset % fileContent.length; + // length = endOffset - startOffset + 1; + System.arraycopy(fileContent, startOffset, bytes, 0, length); + + // BasePlugin.getInstance().log( + // "HexEditor startOffset={0} endoffset={1} length={2}", + // new Object[] { String.valueOf(startOffset), + // String.valueOf(endOffset), String.valueOf(length) }); + + } else { + endOffset = startOffset; + bytes = new byte[0]; + } + HexEditorSelection hexEditorSelection = new HexEditorSelection( + startOffset, endOffset, bytes); + return hexEditorSelection; + } + + /** + * Gets the text offset for a byte offset. + * + * @param byteOffset + * The byte offset in the original byte array. + * @return The text offset where the byte is represented or <code>-1</code> + * if there is no such text offset. + */ + public int getByteTextOffset(int byteOffset) { + if (byteOffset < byteTextOffsets.length) { + return byteTextOffsets[byteOffset]; + } + return -1; + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSaveSelectionAsCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSaveSelectionAsCommandHandler.java new file mode 100644 index 00000000..94d68e08 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSaveSelectionAsCommandHandler.java @@ -0,0 +1,79 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import java.io.File; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.handlers.HandlerUtil; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.FileUtility; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.TextUtility; + +public final class HexEditorSaveSelectionAsCommandHandler extends HexEditorSelectionCommandHandler { + + public static final class CommandIds { + + private CommandIds() { + } + + public static final String SAVE_SELECTION_AS = "com.wudsn.ide.base.editor.hex.HexEditorSaveSelectionAsCommand"; + } + + @Override + protected void performAction() throws ExecutionException { + if (commandId.equals(CommandIds.SAVE_SELECTION_AS) && !hexEditorSelection.isEmpty()) { + Shell shell = HandlerUtil.getActiveShell(event); + if (shell == null) { + return; + } + byte[] content = hexEditorSelection.getBytes(); + + FileDialog dialog = new FileDialog(shell, SWT.SAVE); + int length = content.length; + String hexLength = HexUtility.getLongValueHexString(length); + String decimalLength = NumberUtility.getLongValueDecimalString(length); + dialog.setText(TextUtility + .format(Texts.HEX_EDITOR_SAVE_SELECTION_AS_DIALOG_TITLE, hexLength, decimalLength)); + dialog.setFileName(hexEditor.getSelectionSaveFilePath()); + String filePath = dialog.open(); + if (filePath != null) { + try { + FileUtility.writeBytes(new File(filePath), content); + // INFO: ${0} ({1}) bytes saved as '{2}'. + hexEditor.getMessageManager().sendMessage(0, IStatus.OK, Texts.MESSAGE_I303, hexLength, + decimalLength, filePath); + } catch (CoreException ex) { + throw new ExecutionException(ex.getMessage()); + } + } + } + + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelection.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelection.java new file mode 100644 index 00000000..94ff11c2 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelection.java @@ -0,0 +1,106 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.jface.viewers.ISelection; + +/** + * Container class for selections in the hex editor. + * + * @author Peter Dell + * + */ +final class HexEditorSelection implements ISelection { + + private int startOffset; + private int endOffset; + private byte[] bytes; + + /** + * Creates a new selection. + * + * @param startOffset + * The start offset in the original array, a non-negative number. + * @param endOffset + * The end offset in the original array, a non-negative number + * greater or equal to the start offset. + * @param bytes + * The content of the selection, may be empty, not + * <code>null</code>. + */ + public HexEditorSelection(int startOffset, int endOffset, byte[] bytes) { + + if (startOffset < 0) { + throw new IllegalArgumentException("Parameter 'startOffset' must not be negative, specified value is " + + startOffset + "."); + } + if (endOffset < startOffset) { + throw new IllegalArgumentException("Parameter 'endOffset' must not be smaller than startOffset " + + startOffset + ", specified value is " + endOffset + "."); + } + if (bytes == null) { + throw new IllegalArgumentException("Parameter 'bytes' must not be null."); + } + this.startOffset = startOffset; + this.endOffset = endOffset; + this.bytes = bytes; + } + + @Override + public boolean isEmpty() { + return bytes.length == 0; + } + + /** + * Gets the start offset of the selection in the original array. + * + * @return The start offset in the original array, a non-negative number. + * + */ + public int getStartOffset() { + return startOffset; + } + + /** + * Gets the end offset in the original array. + * + * @return The end offset in the original array, a non-negative number + * greater or equal to the start offset. + */ + public int getEndOffset() { + return endOffset; + } + + /** + * Gets the content of the selection. + * + * @return The content of the selection, may be empty, not <code>null</code> + * . + */ + public byte[] getBytes() { + return bytes; + } + + @Override + public String toString() { + return "HexEditorSelection from " + startOffset + " to " + endOffset + ": " + bytes.length + " bytes"; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelectionCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelectionCommandHandler.java new file mode 100644 index 00000000..f5a34bb7 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelectionCommandHandler.java @@ -0,0 +1,77 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; + +import com.wudsn.ide.base.gui.MessageManager; + +/** + * The handler based class for commands based on the {@link HexEditorSelection}. + * + * @author Peter Dell + */ +public abstract class HexEditorSelectionCommandHandler extends AbstractHandler { + + protected ExecutionEvent event; + protected String commandId; + protected HexEditorSelection hexEditorSelection; + protected HexEditor hexEditor; + protected MessageManager messageManager; + + /** + * Creation is protected. + */ + protected HexEditorSelectionCommandHandler() { + super(); + } + + @Override + public final Object execute(ExecutionEvent event) throws ExecutionException { + IEditorPart editorPart; + ISelection menuEditorInputSelection; + editorPart = HandlerUtil.getActiveEditor(event); + menuEditorInputSelection = HandlerUtil.getActiveMenuSelection(event); + + if (editorPart instanceof HexEditor && menuEditorInputSelection instanceof HexEditorSelection) { + + this.event = event; + this.commandId = event.getCommand().getId(); + this.hexEditorSelection = (HexEditorSelection) menuEditorInputSelection; + this.hexEditor = ((HexEditor) editorPart); + this.messageManager = hexEditor.getMessageManager(); + messageManager.clearMessages(); + + performAction(); + + messageManager.displayMessages(); + + } + + return null; + } + + protected abstract void performAction() throws ExecutionException; +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelectionTransfer.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelectionTransfer.java new file mode 100644 index 00000000..e01cc6a4 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/HexEditorSelectionTransfer.java @@ -0,0 +1,96 @@ +package com.wudsn.ide.base.editor.hex; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.eclipse.swt.dnd.ByteArrayTransfer; +import org.eclipse.swt.dnd.TransferData; + +public final class HexEditorSelectionTransfer extends ByteArrayTransfer { + + private static final String HEX_EDITOR_SELECTION_NAME = "HexEditorSelection"; + private static final int HEX_EDITOR_SELECTION_ID = registerType(HEX_EDITOR_SELECTION_NAME); + private static HexEditorSelectionTransfer instance = new HexEditorSelectionTransfer(); + + private HexEditorSelectionTransfer() { + } + + public static HexEditorSelectionTransfer getInstance() { + return instance; + } + + @Override + public void javaToNative(Object object, TransferData transferData) { + if (object == null || !(object instanceof HexEditorSelection)) + return; + + if (isSupportedType(transferData)) { + HexEditorSelection hexEditorSelection = (HexEditorSelection) object; + try { + // write data to a byte array and then ask super to convert to + // pMedium + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream writeOut = new DataOutputStream(out); + byte[] bytes = hexEditorSelection.getBytes(); + writeOut.writeInt(hexEditorSelection.getStartOffset()); + writeOut.writeInt(hexEditorSelection.getEndOffset()); + writeOut.writeInt(bytes.length); + writeOut.write(bytes); + + byte[] buffer = out.toByteArray(); + writeOut.close(); + + super.javaToNative(buffer, transferData); + + } catch (IOException e) { + } + } + } + + @Override + public Object nativeToJava(TransferData transferData) { + + if (isSupportedType(transferData)) { + + byte[] buffer = (byte[]) super.nativeToJava(transferData); + if (buffer == null) { + return null; + } + + HexEditorSelection hexEditorSelection; + hexEditorSelection = null; + try { + ByteArrayInputStream in = new ByteArrayInputStream(buffer); + DataInputStream readIn = new DataInputStream(in); + while (readIn.available() > 0) { + int startOffset=readIn.readInt(); + int endOffset=readIn.readInt(); + int size = readIn.readInt(); + byte[] bytes = new byte[size]; + readIn.read(bytes); + hexEditorSelection = new HexEditorSelection(startOffset, endOffset, bytes); + + } + readIn.close(); + } catch (IOException ex) { + hexEditorSelection = null; + } + return hexEditorSelection; + } + + return null; + } + + @Override + protected String[] getTypeNames() { + return new String[] { HEX_EDITOR_SELECTION_NAME }; + } + + @Override + protected int[] getTypeIds() { + return new int[] { HEX_EDITOR_SELECTION_ID }; + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariCOMParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariCOMParser.java new file mode 100644 index 00000000..c437b998 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariCOMParser.java @@ -0,0 +1,36 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +public final class AtariCOMParser extends AtariParser { + + @Override + public boolean parse(StyledString contentBuilder) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + int offset = 0; + int fileContentLenght = getFileContentLength(); + return parseAtariCOMFile(contentBuilder, offset, fileContentLenght); + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariDiskImageKFileParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariDiskImageKFileParser.java new file mode 100644 index 00000000..b9056a0a --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariDiskImageKFileParser.java @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +public final class AtariDiskImageKFileParser extends AtariDiskImageParser { + // The offset where the COM file starts in an Atari Disk Image (k-file). + public static final int ATARI_DISK_IMAGE_K_FILE_COM_FILE_OFFSET = 16 + 3 * 128; + + @Override + public boolean parse(StyledString contentBuilder) { + + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + + boolean error = super.parse(contentBuilder); + + // If the disk image is a k-file image, the contained COM file is parsed + // as well. + if (!error) { + // The length of the k-file is stored in $709/$70a. + int length = ATARI_DISK_IMAGE_K_FILE_COM_FILE_OFFSET + getFileContentByte(0x19) + 256 * getFileContentByte(0x1a); + error = parseAtariCOMFile(contentBuilder, ATARI_DISK_IMAGE_K_FILE_COM_FILE_OFFSET, length); + } + return error; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariDiskImageParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariDiskImageParser.java new file mode 100644 index 00000000..7781ad2d --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariDiskImageParser.java @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; + +public class AtariDiskImageParser extends AtariParser { + + @Override + public boolean parse(StyledString contentBuilder) { + + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + + boolean error = false; + int length = getFileContentLength(); + int offset = 0; + + HexEditorContentOutlineTreeObject treeObject; + treeObject = printBlockHeader(contentBuilder, Texts.HEX_EDITOR_ATARI_DISK_IMAGE_HEADER, -1, "", offset, offset, + offset + 15); + offset = printBytes(treeObject, contentBuilder, offset, offset + 15, true, 0); + contentBuilder.append("\n"); + + boolean blockMode; + + blockMode = true; + + int mainSectorSize = getFileContentByte(4) + 256 * getFileContentByte(5); + int bootSectorSize = mainSectorSize; + + if (bootSectorSize == 256 && (length % 256) == 128 + 16) { + bootSectorSize = 128; + } + int startAddress = 0; + int sectorCount; + int sectorSize; + + sectorCount = 1; + sectorSize = bootSectorSize; + try { + while (blockMode && !error) { + treeObject = printBlockHeader(contentBuilder, Texts.HEX_EDITOR_ATARI_SECTOR_HEADER, sectorCount, + + Texts.HEX_EDITOR_ATARI_SECTOR_HEADER_PARAMETERS, offset, startAddress, startAddress + sectorSize - 1); + offset = printBytes(treeObject, contentBuilder, offset, offset + sectorSize - 1, true, startAddress); + contentBuilder.append("\n"); + + if (offset >= length) { + blockMode = false; + } else if (length - offset < sectorSize) { + error = true; + } + sectorCount++; + if (sectorCount > 3) { + sectorSize = mainSectorSize; + } + } + } catch (RuntimeException ex) { + contentBuilder.append(ex.toString()); + } + if (error) { + printBlockWithError(contentBuilder, Texts.HEX_EDITOR_ATARI_SECTOR_ERROR, length, offset); + } + return error; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariMADSParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariMADSParser.java new file mode 100644 index 00000000..9f9dfd83 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariMADSParser.java @@ -0,0 +1,221 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.TextUtility; +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; +import com.wudsn.ide.base.editor.hex.HexEditorParser; + +/** + * All parsing in here is based on the format description in the MADS online documentation. + * + * @author Peter Dell + */ +public class AtariMADSParser extends HexEditorParser { + + public static final int COM_HEADER = 0xffff; + public static final int RELOC_HEADER = 0x524d; + public static final int UPDATE_RELOC_HEADER = 0xffef; + public static final int UPDATE_SYMBOLS_HEADER = 0xffee; + public static final int DEFINE_SYMBOLS_HEADER = 0xffed; + + @Override + public boolean parse(StyledString contentBuilder) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + + boolean error; + int offset = 0; + + // Skip offset bytes in lookup array. + skipByteTextIndex(offset); + + HexEditorContentOutlineTreeObject treeObject; + int fileContentLength = getFileContentLength(); + + error = (fileContentLength - offset) < 17; + boolean first = true; + boolean more = true; + try { + while (more && !error) { + if (!first) { + contentBuilder.append("\n"); + } + first = false; + if (offset == fileContentLength) { + more = false; + } else { + int header = getFileContentWord(offset); + if (header == COM_HEADER && getFileContentWord(offset + 6) == RELOC_HEADER) { + int startAddress = getFileContentWord(offset + 2); + int endAddress = getFileContentWord(offset + 4); + int config = getFileContentByte(offset + 9); + + String headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_MADS_RELOC_BLOCK_HEADER, + HexUtility.getLongValueHexString(startAddress, 4), + HexUtility.getLongValueHexString(endAddress, 4), + HexUtility.getByteValueHexString(config)); + + treeObject = printHeader(contentBuilder, offset, headerText); + offset = printBytes(treeObject, contentBuilder, offset, offset + 15, true, 0); + + int blockEnd = offset + endAddress - startAddress; + + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, true, startAddress); + + } else if (header == UPDATE_RELOC_HEADER) { + + int type = getFileContentByte(offset + 2); + int dataLength = getFileContentWord(offset + 3); + + treeObject = printTypedHeader(contentBuilder, offset, + Texts.HEX_EDITOR_ATARI_MADS_UPDATE_RELOC_BLOCK_HEADER, type, dataLength); + offset = printBytes(treeObject, contentBuilder, offset, offset + 4, false, 0); + + // The exception is an update block for address high + // bytes ">", where for such a block an extra BYTE is + // stored for each address (low byte of address being + // modified). + int dataSize = (type == '>' ? 3 : 2); + int blockEnd = offset + (dataLength * dataSize) - 1; + + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, false, 0); + + } else if (header == UPDATE_SYMBOLS_HEADER) { + /** + * <pre> + * HEADER WORD ($FFEE) + * TYPE CHAR (B-YTE, W-ORD, L-ONG, D-WORD, <, >) + * DATA_LENGTH WORD + * LABEL_LENGTH WORD + * LABEL_NAME ATASCII + * DATA WORD .. .. .. (DATA_LENGTH words) + * </pre> + */ + int type = getFileContentByte(offset + 2); + int dataLength = getFileContentWord(offset + 3); + int labelLength = getFileContentWord(offset + 5); + + treeObject = printTypedHeader(contentBuilder, offset, + Texts.HEX_EDITOR_ATARI_MADS_UPDATE_SYMBOLS_BLOCK_HEADER, type, dataLength); + offset = printBytes(treeObject, contentBuilder, offset, offset + 6, false, 0); + offset = printBytes(treeObject, contentBuilder, offset, offset + labelLength - 1, false, 0); + offset = printBytes(treeObject, contentBuilder, offset, offset + dataLength * 2 - 1, false, 0); + + } else if (header == DEFINE_SYMBOLS_HEADER) { + + int length = getFileContentWord(offset + 2); + String headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOLS_BLOCK_HEADER, + HexUtility.getLongValueHexString(length, 4)); + StyledString headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + + offset += 4; + for (int i = 0; i < length; i++) { + int type = getFileContentByte(offset); + int labelType = getFileContentByte(offset + 1); + int labelLength = getFileContentWord(offset + 2); + String labelName = getLabelName(offset + 4, labelLength); + int address = getFileContentWord(offset + 4 + labelLength); + int headerEnd = offset + 6 + labelLength - 1; + switch (labelType) { + case 'P': + int procType = getFileContentByte(headerEnd + 1); + int paramCount = getFileContentWord(headerEnd + 2); + headerEnd += 4; + for (int j = 0; j < paramCount; j++) { + switch (procType) { + case 'D': + break; + case 'R': + headerEnd += 1; + break; + case 'V': + headerEnd += 1; + int paramLenght = getFileContentWord(headerEnd); + headerEnd += 2 + paramLenght; + break; + } + } + break; + case 'A': + break; + case 'S': + break; + } + + headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_MADS_DEFINE_SYMBOL_HEADER, + String.valueOf((char) type), String.valueOf((char) labelType), labelName, + HexUtility.getLongValueHexString(address, 4)); + headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + offset = printBytes(treeObject, contentBuilder, offset, headerEnd, false, 0); + + } + + } else { + error = true; + } + } + } + } catch (RuntimeException ex) { + contentBuilder.append(ex.toString()); + error = true; + } + + if (error) { + printBlockWithError(contentBuilder, Texts.HEX_EDITOR_ATARI_MADS_BLOCK_ERROR, fileContentLength, offset); + } + return error; + } + + private HexEditorContentOutlineTreeObject printHeader(StyledString contentBuilder, int offset, String headerText) { + HexEditorContentOutlineTreeObject treeObject; + StyledString headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + return treeObject; + } + + private HexEditorContentOutlineTreeObject printTypedHeader(StyledString contentBuilder, int offset, String text, + int type, int dataLength) { + HexEditorContentOutlineTreeObject treeObject; + String headerText = TextUtility.format(text, String.valueOf((char) type), + HexUtility.getLongValueHexString(dataLength, 4)); + treeObject = printHeader(contentBuilder, offset, headerText); + return treeObject; + } + + private String getLabelName(int offset, int length) { + StringBuffer buffer = new StringBuffer(8); + for (int i = 0; i < length; i++) { + buffer.append((char) getFileContentByte(offset + i)); + } + return buffer.toString(); + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariParser.java new file mode 100644 index 00000000..01da078f --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariParser.java @@ -0,0 +1,127 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; +import com.wudsn.ide.base.editor.hex.HexEditorParser; + +public abstract class AtariParser extends HexEditorParser { + + public final static int COM_HEADER = 0xffff; + + protected final boolean parseAtariCOMFile(StyledString contentBuilder, int offset, int fileContentLength) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + boolean error; + int startAddress; + int endAddress; + + int blockCount; + int blockEnd; + + // Skip offset bytes in lookup array. + skipByteTextIndex(offset); + + HexEditorContentOutlineTreeObject treeObject; + + error = (fileContentLength - offset) < 7; + if (!error) { + startAddress = getFileContentWord(offset + 2); + endAddress = getFileContentWord(offset + 4); + + blockCount = 1; + blockEnd = offset + endAddress - startAddress + 6; + treeObject = printBlockHeader(contentBuilder, Texts.HEX_EDITOR_ATARI_COM_BLOCK_HEADER, blockCount, + Texts.HEX_EDITOR_ATARI_COM_BLOCK_HEADER_PARAMETERS, offset, startAddress, endAddress); + offset = printBytes(treeObject, contentBuilder, offset, offset + 5, true, 0); + + boolean blockMode; + blockMode = true; + error = blockEnd >= fileContentLength; + try { + while (blockMode && !error) { + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, true, startAddress); + + int headerLength = -1; + // No more bytes left? + if (offset == fileContentLength ) { + blockMode = false; + } else + // At least 5 bytes available? (4 header bytes and 1 data + // byte) + if (fileContentLength - offset < 5) { + error = true; + } else { + boolean comHeader; + comHeader = getFileContentWord(offset) == COM_HEADER; + if (comHeader) { + // At least 7 bytes available? (6 header bytes and 1 + // data byte) + if (fileContentLength - offset < 7) { + error = true; + } else { + // Inner COM header found + headerLength = 6; + startAddress = getFileContentByte(offset + 2) + 256 * getFileContentByte(offset + 3); + endAddress = getFileContentByte(offset + 4) + 256 * getFileContentByte(offset + 5); + } + } else { + // No inner COM header found + headerLength = 4; + startAddress = getFileContentByte(offset + 0) + 256 * getFileContentByte(offset + 1); + endAddress = getFileContentByte(offset + 2) + 256 * getFileContentByte(offset + 3); + } + error = endAddress < startAddress; + } + + if (blockMode) { + contentBuilder.append("\n"); + } + + if (blockMode && !error) { + blockCount++; + blockEnd = offset + endAddress - startAddress + headerLength; + if (blockEnd < fileContentLength) { + + treeObject = printBlockHeader(contentBuilder, Texts.HEX_EDITOR_ATARI_COM_BLOCK_HEADER, + blockCount, Texts.HEX_EDITOR_ATARI_COM_BLOCK_HEADER_PARAMETERS, offset, + startAddress, endAddress); + offset = printBytes(treeObject, contentBuilder, offset, offset + headerLength - 1, true, 0); + } else { + error = true; + } + } + } + } catch (RuntimeException ex) { + contentBuilder.append(ex.toString()); + error = true; + } + } + if (error) { + printBlockWithError(contentBuilder, Texts.HEX_EDITOR_ATARI_COM_BLOCK_ERROR, fileContentLength, offset); + } + return error; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariSAPParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariSAPParser.java new file mode 100644 index 00000000..d62131a6 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariSAPParser.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; + +public final class AtariSAPParser extends AtariParser { + + @Override + public boolean parse(StyledString contentBuilder) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + int offset = 0; + int fileContentLenght = getFileContentLength(); + int maxOffset = fileContentLenght - 2; + while (offset < maxOffset && getFileContentByte(offset) != 0xff && getFileContentByte(offset) != 0xff) { + offset++; + } + if (offset == maxOffset) { + return false; + } + HexEditorContentOutlineTreeObject treeObject = printBlockHeader(contentBuilder, + Texts.HEX_EDITOR_ATARI_SAP_FILE_HEADER, -1, "", 0, 0, 0); + printBytes(treeObject, contentBuilder, 0, offset - 1, false, 0); + contentBuilder.append("\n"); + + return parseAtariCOMFile(contentBuilder, offset, fileContentLenght); + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariSDXParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariSDXParser.java new file mode 100644 index 00000000..d6697737 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/AtariSDXParser.java @@ -0,0 +1,195 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; +import com.wudsn.ide.base.common.TextUtility; +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; +import com.wudsn.ide.base.editor.hex.HexEditorParser; + +public class AtariSDXParser extends HexEditorParser { + + public static final int NON_RELOC_HEADER = 0xfffa; + public static final int RELOC_HEADER = 0xfffe; + public static final int UPDATE_RELOC_HEADER = 0xfffd; + public static final int UPDATE_SYMBOLS_HEADER = 0xfffb; + public static final int DEFINE_SYMBOLS_HEADER = 0xfffc; + + @Override + public boolean parse(StyledString contentBuilder) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + + boolean error; + int offset = 0; + + // Skip offset bytes in lookup array. + skipByteTextIndex(offset); + + HexEditorContentOutlineTreeObject treeObject; + int fileContentLength = getFileContentLength(); + + error = (fileContentLength - offset) < 7; + boolean first = true; + boolean more = true; + try { + while (more && !error) { + if (!first) { + contentBuilder.append("\n"); + } + first = false; + if (offset == fileContentLength) { + more = false; + } else { + int header = getFileContentWord(offset); + if (header == NON_RELOC_HEADER) { + int startAddress = getFileContentWord(offset + 2); + int endAddress = getFileContentWord(offset + 4); + + treeObject = printBlockHeader(contentBuilder, + Texts.HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER, -1, + Texts.HEX_EDITOR_ATARI_SDX_NON_RELOC_BLOCK_HEADER_PARAMETERS, offset, startAddress, + endAddress); + offset = printBytes(treeObject, contentBuilder, offset, offset + 5, true, 0); + + int blockEnd = offset + endAddress - startAddress; + + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, true, startAddress); + + } else if (header == RELOC_HEADER) { + int blockNumber = getFileContentByte(offset + 2); + int blockId = getFileContentByte(offset + 3); + int blockOffset = getFileContentWord(offset + 4); + int blockLength = getFileContentWord(offset + 6); + + String headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_SDX_RELOC_BLOCK_HEADER, + NumberUtility.getLongValueDecimalString(blockNumber), + HexUtility.getByteValueHexString(blockId), + HexUtility.getLongValueHexString(blockOffset, 4), + HexUtility.getLongValueHexString(blockLength, 4)); + StyledString headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + offset = printBytes(treeObject, contentBuilder, offset, offset + 7, true, 0); + + int blockEnd = offset + blockLength - 1; + + // Print bytes only of the block is not marked as EMPTY + if ((blockId & 0x80) != 0x80) { + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, true, blockOffset); + } + } else if (header == UPDATE_RELOC_HEADER) { + int blockNumber = getFileContentByte(offset + 2); + int blockLength = getFileContentWord(offset + 3); + + String headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_SDX_UPDATE_RELOC_BLOCK_HEADER, + NumberUtility.getLongValueDecimalString(blockNumber), + HexUtility.getLongValueHexString(blockLength, 4)); + StyledString headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + offset = printBytes(treeObject, contentBuilder, offset, offset + 4, true, 0); + int blockEnd = getBlockEnd(offset); + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, true, 0); + } else if (header == UPDATE_SYMBOLS_HEADER) { + String symbolName=getSymbolName(offset+2); + int blockLength = getFileContentWord(offset + 10); + + String headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_SDX_UPDATE_SYMBOLS_BLOCK_HEADER, + symbolName, HexUtility.getLongValueHexString(blockLength, 4)); + StyledString headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + offset = printBytes(treeObject, contentBuilder, offset, offset + 11, true, 0); + int blockEnd = getBlockEnd(offset); + offset = printBytes(treeObject, contentBuilder, offset, blockEnd, true, 0); + } else if (header == DEFINE_SYMBOLS_HEADER) { + int blockNumber = getFileContentByte(offset + 2); + int blockOffset = getFileContentWord(offset + 3); + String symbolName=getSymbolName(offset+5); + + String headerText = TextUtility.format(Texts.HEX_EDITOR_ATARI_SDX_DEFINE_SYMBOLS_BLOCK_HEADER, + NumberUtility.getLongValueDecimalString(blockNumber), + HexUtility.getLongValueHexString(blockOffset), symbolName); + StyledString headerStyledString = new StyledString(headerText, offsetStyler); + contentBuilder.append(headerStyledString).append("\n"); + treeObject = printBlockHeader(contentBuilder, headerStyledString, offset); + offset = printBytes(treeObject, contentBuilder, offset, offset + 12, true, 0); + } else { + error = true; + } + } + } + } catch (RuntimeException ex) { + contentBuilder.append(ex.toString()); + error = true; + } + + if (error) { + printBlockWithError(contentBuilder, Texts.HEX_EDITOR_ATARI_SDX_BLOCK_ERROR, fileContentLength, offset); + } + return error; + } + + /** + * Gets end offset for UPDATE_RELOC_HEADER and UPDATE_SYMBOLS_HEADER. + * + * @param offset + * The start offset, a non-negative integer. + * @return The end offset, a non-negative integer. + */ + private int getBlockEnd(int offset) { + int fileContentLength = getFileContentLength(); + int i = offset; + int blockEnd = -1; + while (blockEnd < 0 && i < fileContentLength) { + int location = getFileContentByte(i); + switch (location) { + case 0xfc: + blockEnd = i; + break; + case 0xfd: + i += 3; + break; + case 0xfe: + i += 3; + break; + default: + i++; + break; + } + } + return blockEnd; + } + + private String getSymbolName(int offset) { + StringBuffer buffer = new StringBuffer(8); + for (int i = 0; i < 8; i++) { + buffer.append((char) getFileContentByte(offset + i)); + } + return buffer.toString(); + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/BinaryParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/BinaryParser.java new file mode 100644 index 00000000..0f4fe8dd --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/BinaryParser.java @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; +import com.wudsn.ide.base.editor.hex.HexEditorParser; + +public class BinaryParser extends HexEditorParser { + + @Override + public boolean parse(StyledString contentBuilder) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + HexEditorContentOutlineTreeObject treeObject = new HexEditorContentOutlineTreeObject(contentBuilder); + printBytes(treeObject, contentBuilder, 0, getFileContentLength() - 1, false, 0); + return false; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/C64PRGParser.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/C64PRGParser.java new file mode 100644 index 00000000..cd0cb187 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/hex/parser/C64PRGParser.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.hex.parser; + +import org.eclipse.jface.viewers.StyledString; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.editor.hex.HexEditorContentOutlineTreeObject; +import com.wudsn.ide.base.editor.hex.HexEditorParser; + +public class C64PRGParser extends HexEditorParser { + + @Override + public boolean parse(StyledString contentBuilder) { + if (contentBuilder == null) { + throw new IllegalArgumentException("Parameter 'contentBuilder' must not be null."); + } + boolean error; + int startAddress; + int endAddress; + + int length = getFileContentLength(); + int offset = 0; + + error = (length < 2); + if (!error) { + startAddress = getFileContentByte(offset + 0) + 256 * getFileContentByte(offset + 1); + endAddress = startAddress + length - 3; + + HexEditorContentOutlineTreeObject treeObject; + treeObject = printBlockHeader(contentBuilder, Texts.HEX_EDITOR_C64_PRG_HEADER, -1, + Texts.HEX_EDITOR_C64_PRG_HEADER_PARAMETERS, offset, startAddress, endAddress); + offset = printBytes(treeObject, contentBuilder, offset, offset + 1, true, 0); + + error = endAddress > 0xffff; + if (!error) { + printBytes(treeObject, contentBuilder, offset, length - 1, true, startAddress); + } + } + if (error) { + printBlockWithError(contentBuilder, Texts.HEX_EDITOR_C64_PRG_ERROR, length, offset); + } + return error; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorConvertNumbersCommand.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorConvertNumbersCommand.java new file mode 100644 index 00000000..7319298c --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorConvertNumbersCommand.java @@ -0,0 +1,296 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.expressions.PropertyTester; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.ITextEditorExtension2; + +/** + * Command handler for converting number within a text selection.<br/> + * TODO Check block selection mode and read-only mode for multi line conversion, + * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=382707<br/> + * + * @author Peter Dell + * + * @since 1.6.3 + */ +public final class TextEditorConvertNumbersCommand { + + public static final class Id { + public static final String TO_DECIMAL = "com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToDecimalCommand"; + public static final String TO_HEXA_DECIMAL = "com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToHexaDecimalCommand"; + public static final String TO_BINARY = "com.wudsn.ide.base.editor.text.TextEditorConvertNumbersToBinaryCommand"; + } + + private static final class Mode { + public final static int DECIMAL = 1; + public final static int HEXA_DECIMAL = 2; + public final static int BINARY = 3; + } + + /** + * In the productive code, this field must be <code>false</code>, to enable + * the property checks when the menu is created. To debug the conversion, + * this field must be set to <code>true</code>, to disable the property + * checks when the menu is created. + */ + + private static final boolean DEBUG = false; + + public static final class EnabledPropertyTester extends PropertyTester { + + public EnabledPropertyTester() { + } + + @Override + public boolean test(final Object receiver, final String property, final Object[] args, + final Object expectedValue) { + + if (property.equals("isEnabled") && receiver instanceof ITextSelection && expectedValue instanceof String) { + ITextSelection selection = (ITextSelection) receiver; + String commandId = (String) expectedValue; + int length = selection.getLength(); + boolean enabled; + // For performance reasons, the test is skipped of the selected + // block is large and we simply assume there are numbers in. + if (length <= 0) { + enabled = false; + } else if (length > 1024 || DEBUG) { + enabled = true; + } else { + StringBuilder result = new StringBuilder(selection.getLength()); + enabled = convertNumberValues(selection, commandId, result); + } + return enabled; + + } + return false; + } + } + + public static final class Handler extends AbstractHandler { + + public Handler() { + + } + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IEditorPart editor; + String commandId; + + commandId = event.getCommand().getId(); + editor = HandlerUtil.getActiveEditorChecked(event); + + if (!(editor instanceof ITextEditor)) { + return null; + } + + // Find editor, document, selection, start and end line. + ITextEditor textEditor; + IDocument document; + ITextSelection selection; + + textEditor = (ITextEditor) editor; + + document = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()); + selection = (ITextSelection) textEditor.getSelectionProvider().getSelection(); + StringBuilder result = new StringBuilder(selection.getLength()); + if (convertNumberValues(selection, commandId, result)) { + try { + + if (editor instanceof ITextEditorExtension2) { + ITextEditorExtension2 extension = (ITextEditorExtension2) editor; + if (!extension.validateEditorInputState()) { + return null; + } + } + // TODO: Multiple replace edits in case of block selection. + ReplaceEdit replaceEdit = new ReplaceEdit(selection.getOffset(), selection.getLength(), + result.toString()); + replaceEdit.apply(document); + + } catch (BadLocationException ex) { + throw new RuntimeException(ex); + } + } + + return null; + } + + } + + final static boolean convertNumberValues(ITextSelection selection, String commandId, StringBuilder result) { + + if (selection == null) { + throw new IllegalArgumentException("Parameter 'selection' must not be null."); + } + + int mode; + if (commandId.equals(Id.TO_DECIMAL)) { + mode = Mode.DECIMAL; + } else if (commandId.equals(Id.TO_HEXA_DECIMAL)) { + mode = Mode.HEXA_DECIMAL; + } else if (commandId.equals(Id.TO_BINARY)) { + mode = Mode.BINARY; + } else { + throw new IllegalArgumentException("Unsupported command '" + commandId + "'."); + } + if (result == null) { + throw new IllegalArgumentException("Parameter 'result' must not be null."); + } + + // if (selection instanceof IBlockTextSelection) { + // return false; + // } + + int length = selection.getLength(); + if (length == 0) { + return false; + } + + String text = selection.getText(); + length = text.length(); // Because it might be a block selection + boolean numbersFound = false; + + StringBuilder number = new StringBuilder(); + int offset = 0; + int endOffset = length; + + try { + while (offset < endOffset) { + char c1; + char c2; + long value; + c1 = text.charAt(offset); + + if (offset < endOffset - 1) { + c2 = text.charAt(offset + 1); + } else { + c2 = ' '; + } + + if (c1 >= '0' && c1 <= '9') { + number.setLength(0); + while (offset < endOffset && c1 >= '0' && c1 <= '9') { + number.append(c1); + offset++; + if (offset < endOffset) { + c1 = text.charAt(offset); + } + } + value = Long.parseLong(number.toString()); + appendNumberValue(result, mode, value); + numbersFound = true; + + } else if (c1 == '$' + && ((c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'F') || (c2 >= 'a' && c2 <= 'f'))) { + number.setLength(0); + offset++; + c1 = c2; + while (offset < endOffset + && ((c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'F') || (c1 >= 'a' && c1 <= 'f'))) { + number.append(c1); + offset++; + if (offset < endOffset) { + c1 = text.charAt(offset); + } + } + + value = Long.parseLong(number.toString(), 16); + appendNumberValue(result, mode, value); + numbersFound = true; + + } else if (c1 == '%' && (c2 >= '0' && c2 <= '1')) { + number.setLength(0); + offset++; + c1 = c2; + while (offset < endOffset && ((c1 >= '0' && c1 <= '1'))) { + number.append(c1); + offset++; + if (offset < endOffset) { + c1 = text.charAt(offset); + } + } + value = Long.parseLong(number.toString(), 2); + appendNumberValue(result, mode, value); + numbersFound = true; + } else { + result.append(c1); + offset++; + } + } + } catch (NumberFormatException ex) { + return false; // For example if the number becomes too large + } + + // Do nothing if the text is already the same. + if (result.toString().equals(selection.getText())) { + numbersFound = false; + } + return numbersFound; + } + + private static void appendNumberValue(StringBuilder result, int mode, long value) { + if (result == null) { + throw new IllegalArgumentException("Parameter 'result' must not be null."); + } + + switch (mode) { + case Mode.DECIMAL: + result.append(Long.toString(value)); + break; + case Mode.HEXA_DECIMAL: + result.append("$"); + String hexValue = Long.toHexString(value); + int hexLength = hexValue.length(); + while ((hexLength & 1) != 0) { + result.append('0'); + hexLength++; + } + result.append(hexValue); + break; + case Mode.BINARY: + result.append("%"); + String binaryValue = Long.toBinaryString(value); + int binarLength = binaryValue.length(); + while ((binarLength & 7) != 0) { + result.append('0'); + binarLength++; + } + result.append(binaryValue); + break; + + default: + throw new IllegalArgumentException("Unsupported mode " + mode + "."); + } + + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorLinesCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorLinesCommandHandler.java new file mode 100644 index 00000000..4ab04468 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorLinesCommandHandler.java @@ -0,0 +1,139 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.handlers.HandlerUtil; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.ITextEditorExtension2; + +/** + * Base class for text editor actions operating on the selected lines. Inspired + * by http://www.stateofflow.com/projects/2/sortit. + * + * @author Peter Dell + */ +abstract class TextEditorLinesCommandHandler extends AbstractHandler { + + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + IEditorPart editor; + editor = HandlerUtil.getActiveEditorChecked(event); + + if (!(editor instanceof ITextEditor)) { + return null; + } + + // Find editor, document, selection, start and end line. + ITextEditor textEditor; + IDocument document; + ITextSelection selection; + int endLineIndex; + int startLineIndex; + + textEditor = (ITextEditor) editor; + + document = textEditor.getDocumentProvider().getDocument( + textEditor.getEditorInput()); + selection = (ITextSelection) textEditor.getSelectionProvider() + .getSelection(); + endLineIndex = selection.getEndLine(); + startLineIndex = selection.getStartLine(); + if (startLineIndex == endLineIndex) { + startLineIndex = 0; + endLineIndex = document.getNumberOfLines() - 1; + } + + try { + // Collect lines. + int startOffset; + int length; + + startOffset = document.getLineOffset(startLineIndex); + length = 0; + + List<String> lines; + lines = new ArrayList<String>(); + for (int line = startLineIndex; line <= endLineIndex; line++) { + int delimiterLength = document.getLineDelimiter(line) == null ? 0 + : document.getLineDelimiter(line).length(); + String lineText = document.get(document.getLineOffset(line), + document.getLineLength(line) - delimiterLength); + lines.add(lineText); + length = length + lineText.length() + delimiterLength; + } + + process(event.getCommand(), lines); + + int lineCount = lines.size(); + String lineDelimiter = document.getLineDelimiter(startLineIndex); + StringBuilder replacementText = new StringBuilder(); + for (int i = 0; i < lineCount; i++) { + replacementText.append(lines.get(i)); + if ((i < lineCount - 1) || (i == lineCount - 1) + && (document.getLineDelimiter(endLineIndex) != null)) { + replacementText.append(lineDelimiter); + } + } + + if (editor instanceof ITextEditorExtension2) { + ITextEditorExtension2 extension = (ITextEditorExtension2) editor; + if (!extension.validateEditorInputState()) { + return null; + } + } + + ReplaceEdit replaceEdit = new ReplaceEdit(startOffset, length, + replacementText.toString()); + replaceEdit.apply(document); + // re-select the lines that have been processed + textEditor.getSelectionProvider().setSelection(selection); + } catch (BadLocationException ex) { + throw new ExecutionException("Error during handler execution.", ex); + } + return null; + } + + /** + * Subclasses must process the lines given in the list. The processed list + * will be used to rebuild the lines in the editor. + * + * @param command + * The command which is currently executed, not <code>null</code> + * . + * + * @param lines + * The modifiable list of lines, may be empty, not + * <code>null</code>. + */ + protected abstract void process(Command command, List<String> lines); + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorReverseLinesCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorReverseLinesCommandHandler.java new file mode 100644 index 00000000..bd2cbf9a --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorReverseLinesCommandHandler.java @@ -0,0 +1,51 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.commands.Command; + +/** + * Action to reverse the sequence of lines. + * + * @author Peter Dell + */ +public final class TextEditorReverseLinesCommandHandler extends + TextEditorLinesCommandHandler { + + @Override + protected void process(Command command, List<String> lines) { + if (command == null) { + throw new IllegalArgumentException( + "Parameter 'command' must not be null."); + } + if (lines == null) { + throw new IllegalArgumentException( + "Parameter 'lines' must not be null."); + } + for (int i = 0; i < lines.size() / 2; i++) { + int j = lines.size() - i - 1; + Collections.swap(lines, i, j); + + } + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCaseInsensitiveCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCaseInsensitiveCommandHandler.java new file mode 100644 index 00000000..b2dff158 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCaseInsensitiveCommandHandler.java @@ -0,0 +1,37 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import java.util.Comparator; + +/** + * Action to sort lines case insensitively. + * + * @author Peter Dell + */ +public final class TextEditorSortLinesCaseInsensitiveCommandHandler extends + TextEditorSortLinesCommandHandler { + + @Override + protected Comparator<String> getComparator() { + return String.CASE_INSENSITIVE_ORDER; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCaseSensitiveCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCaseSensitiveCommandHandler.java new file mode 100644 index 00000000..ee4cd981 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCaseSensitiveCommandHandler.java @@ -0,0 +1,50 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import java.util.Comparator; + +/** + * Action to sort lines case sensitively. + * + * @author Peter Dell + */ +public final class TextEditorSortLinesCaseSensitiveCommandHandler extends + TextEditorSortLinesCommandHandler { + + private static final Comparator<String> CASE_SENSITIVE_COMPARATOR = new CaseSensitiveComparator(); + + private static class CaseSensitiveComparator implements Comparator<String> { + + public CaseSensitiveComparator() { + } + + @Override + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + } + + @Override + protected Comparator<String> getComparator() { + return CASE_SENSITIVE_COMPARATOR; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCommandHandler.java new file mode 100644 index 00000000..2c3bf79b --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesCommandHandler.java @@ -0,0 +1,71 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.core.commands.Command; + +/** + * Base class for text editor actions sorting on the selected lines. + * + * @author Peter Dell + */ +abstract class TextEditorSortLinesCommandHandler extends + TextEditorLinesCommandHandler { + + @Override + protected void process(Command command, List<String> lines) { + if (command == null) { + throw new IllegalArgumentException( + "Parameter 'command' must not be null."); + } + if (lines == null) { + throw new IllegalArgumentException( + "Parameter 'lines' must not be null."); + } + Comparator<String> comparator = getComparator(); + Collections.sort(lines, comparator); + + if (command.getId().endsWith("WithoutDuplicatesCommand")) { + int i = 0; + while (lines.size() > 1 && i < lines.size() - 1) { + String line1 = lines.get(i); + String line2 = lines.get(i + 1); + int result = comparator.compare(line1, line2); + if (result == 0) { + lines.remove(i + 1); + } else { + i++; + } + } + } + } + + /** + * Sub classes must provide a comparator for strings. + * + * @return The comparator, not <code>null</code>. + */ + protected abstract Comparator<String> getComparator(); + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesNumericCommandHandler.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesNumericCommandHandler.java new file mode 100644 index 00000000..26bc7a0d --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/editor/text/TextEditorSortLinesNumericCommandHandler.java @@ -0,0 +1,81 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.editor.text; + +import java.util.Comparator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.wudsn.ide.base.common.StringUtility; + +/** + * Action to sort lines by the first decimal number found starting at first + * position. + * + * @author Peter Dell + */ +public final class TextEditorSortLinesNumericCommandHandler extends + TextEditorSortLinesCommandHandler { + + private static final class NumericComparator implements Comparator<String> { + + private static final Double MAXIMUM = new Double( + Double.POSITIVE_INFINITY); + private static final Pattern numericPattern = Pattern + .compile("[-+]?([0-9]*\\.)?[0-9]+([eE][-+]?[0-9]+)?"); //$NON-NLS-1$ + + public NumericComparator() { + + } + + @Override + public int compare(String o1, String o2) { + return getNumber(o1).compareTo(getNumber(o2)); + } + + private Double getNumber(String text) { + Double result; + + result = MAXIMUM; + + if (text != null && StringUtility.isSpecified(text)) { + try { + Matcher m = numericPattern.matcher(text); + if (m.find()) { + text = text.substring(m.start(), m.end()); + result = Double.valueOf(text); + } + + } catch (NumberFormatException ignore) { + + } + } + return result; + } + } + + private static final NumericComparator COMPARATOR = new NumericComparator(); + + @Override + protected Comparator<String> getComparator() { + return COMPARATOR; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Action.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Action.java new file mode 100644 index 00000000..e600ce86 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Action.java @@ -0,0 +1,109 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; + +/** + * Action class which abstracts from SWT. + * + * @author Peter Dell + * + */ +public final class Action implements SelectionListener { + + private int id; + private ActionListener actionListener; + private boolean enabled; + + /** + * Creates a new action. + * + * @param id + * The action is, a non-negative integer. + * @param actionListener + * The action listener, not <code>null</code>. + */ + public Action(int id, ActionListener actionListener) { + if (id < 0) { + throw new IllegalArgumentException( + "Parameter 'id' must not be negative. Specified value is " + + id + "."); + } + if (actionListener == null) { + throw new IllegalArgumentException( + "Parameter 'actionListener' must not be null."); + } + this.id = id; + this.actionListener = actionListener; + this.enabled = true; + } + + /** + * Gets the id of the action. + * + * @return The id of the action, a non-negative integer. + */ + public int getId() { + return id; + } + + /** + * Sets the enabled state of the action. Disabled actions do not fire. + * + * @param enabled + * <code>true</code> to enable the action, <code>false</code> to + * disable the action. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + + } + + /** + * Callback from SWT, do not use. + */ + @Override + public void widgetDefaultSelected(SelectionEvent e) { + performAction(); + } + + /** + * Callback from SWT, do not use. + */ + @Override + public void widgetSelected(SelectionEvent e) { + performAction(); + + } + + private void performAction() { + if (enabled) { + actionListener.performAction(this); + } + } + + @Override + public String toString() { + return "actionId=" + id; + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/ActionListener.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/ActionListener.java new file mode 100644 index 00000000..da405cf4 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/ActionListener.java @@ -0,0 +1,35 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +/** + * Consumers of {@link Action} have to implement this interface. + * @author Peter Dell + * + */ +public interface ActionListener { + + /** + * Perform the operation associated with an action. + * + * @param action The action, not <code>null</code>. + */ + public void performAction(Action action); +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Application.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Application.java new file mode 100644 index 00000000..f2a5a5da --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Application.java @@ -0,0 +1,33 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import com.wudsn.ide.base.gui.MessageManager; + +/** + * Interface to be implemented by applications. + * + * @author Peter Dell + * + */ +public interface Application extends ActionListener{ + + public MessageManager getMessageManager(); +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/CheckBoxField.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/CheckBoxField.java new file mode 100644 index 00000000..9f323dab --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/CheckBoxField.java @@ -0,0 +1,133 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** + * Check box field for boolean value. + * + * @author Peter Dell + * + * @since 1.6.0 + */ +public final class CheckBoxField extends Field { + + private Button button; + private List<Action> selectionActions; + + /** + * Creates a check box field. + * + * @param parent + * The parent composite, not <code>null</code>. + * @param labelText + * The label text, not <code>null</code>. + * @param style + * The SWT style. + */ + public CheckBoxField(Composite parent, String labelText, int style) { + if (parent == null) { + throw new IllegalArgumentException( + "Parameter 'parent' must not be null."); + } + if (labelText == null) { + throw new IllegalArgumentException( + "Parameter 'labelText' must not be null."); + } + + label = new Label(parent, SWT.NONE); + label.setText(labelText); + button = new Button(parent, SWT.CHECK | style); + selectionActions = new ArrayList<Action>(1); + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return button; + } + + public void setVisible(boolean visible) { + label.setVisible(visible); + button.setVisible(visible); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean enabled) { + label.setEnabled(enabled); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEditable(boolean editable) { + button.setEnabled(editable); + } + + /** + * Sets the value. + * + * @param value + * The value. + */ + public void setValue(boolean value) { + button.setSelection(value); + } + + /** + * Gets the value. + * + * @return The value. + */ + public boolean getValue() { + + return button.getSelection(); + } + + /** + * Adds a selection action which is fire when the field content changes. + * + * @param action + * The selection action, not <code>null</code>. + */ + public void addSelectionAction(Action action) { + if (action == null) { + throw new IllegalArgumentException( + "Parameter 'action' must not be null."); + } + selectionActions.add(action); + button.addSelectionListener(action); + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/EnumField.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/EnumField.java new file mode 100644 index 00000000..f5944731 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/EnumField.java @@ -0,0 +1,226 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ +package com.wudsn.ide.base.gui; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +import com.wudsn.ide.base.BasePlugin; + +/** + * Combo field offering the elements of an enum class in generic and type safe + * manner. + * + * @author Peter Dell + * + * @param <T> + * The enum class. + */ +public final class EnumField<T extends Enum<?>> extends Field { + + private static final class Entry<T> { + public T constant; + public String name; + public String text; + + public Entry() { + } + + } + + private Combo combo; + private List<Entry<T>> entries; + + /** + * Creates a new enum field. + * + * @param parent + * The parent composite, not <code>null</code>. + * @param labelText + * The label text, not <code>null</code>. + * @param enumClass + * The enum class, not <code>null</code>. + * @param visibleValues + * The non-empty array containing the subset of enum values to be + * displayed or <code>null</code> to display all values. + */ + public EnumField(Composite parent, String labelText, + Class<? extends T> enumClass, T visibleValues[]) { + if (parent == null) { + throw new IllegalArgumentException( + "Parameter 'parent' must not be null."); + } + if (labelText == null) { + throw new IllegalArgumentException( + "Parameter 'labelText' must not be null."); + } + if (enumClass == null) { + throw new IllegalArgumentException( + "Parameter 'enumClass' must not be null."); + } + if (visibleValues != null && visibleValues.length == 0) { + throw new IllegalArgumentException( + "Parameter 'visibleValues' must not be empty"); + } + label = new Label(parent, SWT.NONE); + label.setText(labelText); + combo = new Combo(parent, SWT.DROP_DOWN); + + ResourceBundle resourceBundle; + resourceBundle = ResourceBundle.getBundle("plugin", + Locale.getDefault(), enumClass.getClassLoader()); + + T[] constants = enumClass.getEnumConstants(); + + if (constants.length == 0) { + throw new IllegalArgumentException("Enum class '" + enumClass + + "' has no constants."); + } + + // Ensure all visible values are contained in the set of constants. + if (visibleValues != null) { + for (int i = 0; i < visibleValues.length; i++) { + T visibleValue = visibleValues[i]; + boolean found = false; + for (int j = 0; j < constants.length; j++) { + if (constants[j] == visibleValue) { + found = true; + continue; + } + } + if (!found) { + throw new IllegalArgumentException( + "Parameter 'visibleValues' contain the undefined value '" + + visibleValue + "'."); + } + } + constants = visibleValues; + } + + // Read the localized texts and create the entries. + entries = new ArrayList<Entry<T>>(constants.length); + for (int i = 0; i < constants.length; i++) { + Entry<T> entry = new Entry<T>(); + entry.constant = constants[i]; + entry.name = constants[i].name(); + String key = enumClass.getName() + "." + entry.name; + try { + entry.text = resourceBundle.getString(key); + } catch (MissingResourceException ex) { + entry.text = entry.name + " - Text missing"; + BasePlugin.getInstance().logError( + "Resource for enum value {0} is missing.", + new Object[] { key }, ex); + } + + entries.add(entry); + } + + for (Entry<T> entry : entries) { + combo.add(entry.text); + } + combo.select(0); + + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return combo; + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean enabled) { + label.setEnabled(enabled); + combo.setEnabled(enabled); + + } + + /** + * {@inheritDoc} + */ + @Override + public void setEditable(boolean editable) { + // There is only a style SWT#READ_ONLY, but no changeable property + } + + /** + * Sets the value. + * + * @param value + * The value, not <code>null</code>. + */ + public void setValue(T value) { + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + for (int i = 0; i < entries.size(); i++) { + if (value.name().equals(entries.get(i).name)) { + combo.select(i); + break; + } + } + } + + /** + * Gets the value. + * + * @return The value, not <code>null</code>. + */ + public T getValue() { + int index; + index = combo.getSelectionIndex(); + if (index == -1) { + throw new IllegalStateException("No item selected"); + } + T result = entries.get(index).constant; + return result; + } + + /** + * Adds a selection action. + * + * @param action + * The selection action, not <code>null</code>. + */ + public void addSelectionAction(Action action) { + if (action == null) { + throw new IllegalArgumentException( + "Parameter 'action' must not be null."); + } + combo.addSelectionListener(action); + + } + +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Field.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Field.java new file mode 100644 index 00000000..babffef2 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/Field.java @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** + * Base class for all fields. + * + * @author Peter Dell + * + */ +public abstract class Field { + + protected Label label; + private ChangeListener changeListener; + + /** + * Creation is protected. + */ + protected Field() { + + } + + /** + * Gets the label for the field. + * + * @return The label, not <code>null</code>. + */ + public final Label getLabel() { + if (label == null) { + throw new IllegalStateException("Label not yet created."); + } + return label; + } + + /** + * Gets the control relevant for messages decorations. + * + * @return The control relevant for messages decorations, not + * <code>null</code>. + */ + public abstract Control getControl(); + + /** + * Sets the enabled state of the field. + * + * @param enabled + * The enabled state of the field. + */ + public abstract void setEnabled(boolean enabled); + + /** + * Sets the editable state of the field. + * + * @param editable + * The editable state of the field. + */ + public abstract void setEditable(boolean editable); + + /** + * Adds a change listener to this field. + * + * @param changeListener + * The change listener, not <code>null</code>. + */ + public final void addChangeListener(ChangeListener changeListener) { + if (changeListener == null) { + throw new IllegalArgumentException("Parameter 'changeListener' must not be null."); + } + this.changeListener = changeListener; + } + + protected final void notifyChangeListenner() { + if (changeListener != null) { + changeListener.stateChanged(new ChangeEvent(this)); + } + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/FilePathField.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/FilePathField.java new file mode 100644 index 00000000..e16e58e5 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/FilePathField.java @@ -0,0 +1,277 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.ElementTreeSelectionDialog; +import org.eclipse.ui.model.BaseWorkbenchContentProvider; +import org.eclipse.ui.model.WorkbenchLabelProvider; + +import com.wudsn.ide.base.Texts; +import com.wudsn.ide.base.common.IPathUtility; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Text field with a build in file browser button. + * + * @author Peter Dell + * + */ +public final class FilePathField extends Field { + + /** + * Inner class to handle the file selection dialog. + */ + private final class BrowseButtonSelectionAdapter extends SelectionAdapter { + + public BrowseButtonSelectionAdapter() { + } + + @Override + public void widgetSelected(SelectionEvent evt) { + + IPath newValue = getResourcePath(); + if (newValue != null) { + newValue = IPathUtility.makeRelative(newValue, filePathPrefix); + // Set field content and notify change listeners. + filePathField.setText(newValue.toPortableString()); + } + } + + /** + * Helper to open the resource selection dialog. + * + * @return The file name the user selected or <code>null</code> if not. + */ + private IPath getResourcePath() { + + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + + ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(browseButton.getShell(), + new WorkbenchLabelProvider(), new BaseWorkbenchContentProvider()); + + dialog.setTitle(TextUtility.format(Texts.FILE_PATH_FIELD_DIALOG_MESSAGE, label.getText())); + dialog.setInput(workspaceRoot); + IPath filePath = new Path(filePathField.getText()); + filePath = IPathUtility.makeAbsolute(filePath, filePathPrefix, true); + IFile ifile = workspaceRoot.getFile(filePath); + // If file is not there, default to its parent (folder). + Object selection; + if (ifile != null && ifile.exists()) { + selection = ifile; + } else { + int size = filePath.segmentCount(); + List<IResource> resources = new ArrayList<IResource>(size); + IResource resource = ifile; + while (resource != null) { + resource = resource.getParent(); + if (resource != null) { + resources.add(resource); + } + } + selection = new TreePath(resources.toArray(new IResource[resources.size()])); + } + dialog.setInitialSelection(selection); + + if (dialog.open() == Window.OK) { + Object[] result = dialog.getResult(); + if (result != null && result.length > 0) { + ifile = (IFile) result[0]; + return ifile.getFullPath(); + } + + } + return null; + } + } + + /** + * Constructor fields. + */ + final int dialogMode; + final Text filePathField; + final ModifyListener filePathFieldModifyListener; + + /** + * State fields. + */ + private boolean enabled; + private boolean editable; + + /** + * Runtime fields. + */ + Button browseButton; + IPath filePathPrefix; + + /** + * Creates a new file path field. + * + * @param parent + * The parent composite, not <code>null</code>. + * @param labelText + * The label text, not <code>null</code>. + * @param dialogMode + * {@link SWT#OPEN} or e {@link SWT#SAVE}. + */ + public FilePathField(Composite parent, String labelText, int dialogMode) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + if (dialogMode != SWT.OPEN && dialogMode != SWT.SAVE) { + throw new IllegalArgumentException( + "Parameter 'dialogMode' must be 'SWT.OPEN' or 'SWT.SAVE'. Specified value is " + dialogMode + "."); + } + this.dialogMode = dialogMode; + + label = new Label(parent, SWT.NONE); + label.setText(labelText); + + filePathField = new Text(parent, SWT.SINGLE | SWT.BORDER); + filePathField.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + filePathFieldModifyListener = new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + FilePathField.this.notifyChangeListenner(); + + } + }; + enabled = true; + editable = true; + + browseButton = new Button(parent, SWT.PUSH); + browseButton.setText(Texts.FILE_PATH_FIELD_BROWSE_BUTTON_LABEL); + + browseButton.addSelectionListener(new BrowseButtonSelectionAdapter()); + browseButton.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent event) { + browseButton = null; + } + }); + + filePathPrefix = new Path(""); + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return filePathField; + } + + public void setLabelText(String labelText) { + label.setText(labelText); + + } + + public void setVisible(boolean visible) { + label.setVisible(visible); + filePathField.setVisible(visible); + browseButton.setVisible(visible); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + label.setEnabled(enabled); + filePathField.setEnabled(enabled); + filePathField.setEditable(enabled & editable); + browseButton.setEnabled(enabled); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEditable(boolean editable) { + this.editable = editable; + filePathField.setEditable(enabled & editable); + } + + /** + * Sets the path prefix which will be stripped automatically if it is the + * prefix of the user input. + * + * @param filePathPrefix + * The path prefix, may be empty, not <code>null</code>. + */ + public void setFilePathPrefix(IPath filePathPrefix) { + if (filePathPrefix == null) { + throw new IllegalArgumentException("Parameter 'filePathPrefix' must not be null."); + } + this.filePathPrefix = filePathPrefix; + } + + /** + * Sets the value. + * + * @param value + * The value, not <code>null</code>. + */ + public void setValue(String value) { + if (value == null) { + throw new IllegalArgumentException("Parameter 'value' must not be null."); + } + filePathField.removeModifyListener(filePathFieldModifyListener); + filePathField.setText(value); + filePathField.addModifyListener(filePathFieldModifyListener); + } + + /** + * Gets the value. + * + * @return The value, not <code>null</code>. + */ + public String getValue() { + String result = filePathField.getText(); + result = result.trim(); + return result; + + } +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/IntegerField.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/IntegerField.java new file mode 100644 index 00000000..46b48de0 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/IntegerField.java @@ -0,0 +1,291 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +import com.wudsn.ide.base.common.HexUtility; +import com.wudsn.ide.base.common.NumberUtility; + +/** + * Numeric field for integers. + * + * TODO Have explicit Hex Mode/sub class with minimum digi number for 16 bit + * addresses + * + * @author Peter Dell + * + */ +public final class IntegerField extends Field { + + private int[] defaultValues; + private boolean hexMode; + private int digitLength; + + private Combo combo; + private Text text; + private List<Action> selectionActions; + + /** + * Creates a integer field. + * + * @param parent + * The parent composite, not <code>null</code>. + * @param labelText + * The label text, not <code>null</code>. + * @param defaultValues + * The array of default values or <code>null</code>. + * @param digitLength + * The minimum digit length, see {@link NumberUtility} and + * {@link HexUtility}. + * @param hexMode + * <code>true</code> if display and value help shall be in hex + * mode + * @param style + * The SWT style. + */ + public IntegerField(Composite parent, String labelText, + int[] defaultValues, boolean hexMode, int digitLength, int style) { + if (parent == null) { + throw new IllegalArgumentException( + "Parameter 'parent' must not be null."); + } + if (labelText == null) { + throw new IllegalArgumentException( + "Parameter 'labelText' must not be null."); + } + this.hexMode = hexMode; + this.digitLength = digitLength; + + label = new Label(parent, SWT.NONE); + label.setText(labelText); + + ModifyListener modifyListener= new ModifyListener() { + + @Override + public void modifyText(ModifyEvent e) { + IntegerField.this.notifyChangeListenner(); + + } + }; + + if (defaultValues != null) { + if (defaultValues.length == 0) { + throw new IllegalArgumentException( + "Parameter 'defaultValues0' must not be empty."); + } + combo = new Combo(parent, SWT.DROP_DOWN | style); + + setDefaultValues(defaultValues); + combo.select(0); + combo.addModifyListener(modifyListener); + + } else { + text = new Text(parent, style); + text.addModifyListener(modifyListener); + } + + + selectionActions = new ArrayList<Action>(1); + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + if (combo != null) { + return combo; + } + return text; + } + + public void setVisible(boolean visible) { + label.setVisible(visible); + if (combo != null) { + combo.setVisible(visible); + } else { + text.setVisible(visible); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean enabled) { + label.setEnabled(enabled); + if (combo != null) { + combo.setEnabled(enabled); + } else { + text.setEnabled(enabled); + } + + } + + /** + * {@inheritDoc} + */ + @Override + public void setEditable(boolean editable) { + if (combo != null) { + // There is only an SWT.READ_ONLY style but not property + } else { + text.setEditable(editable); + } + + } + + public void setDefaultValues(int[] defaultValues) { + if (defaultValues == null) { + throw new IllegalArgumentException( + "Parameter 'defaultValues' must not be null."); + } + + if (defaultValues.length == 0) { + throw new IllegalArgumentException( + "Parameter 'defaultValues' must not be empty."); + } + + if (this.defaultValues != null) { + if (Arrays.equals(defaultValues, this.defaultValues)) { + return; + } + } + combo.removeAll(); + + // Compute maxmimum. + int max = 0; + for (int defaultValue : defaultValues) { + if (defaultValue > max) { + max = defaultValue; + } + } + + for (int defaultValue : defaultValues) { + String textValue; + if (hexMode) { + textValue = HexUtility.getLongValueHexString(defaultValue, + digitLength); + } else { + textValue = NumberUtility.getLongValueDecimalString( + defaultValue, digitLength); + } + combo.add(textValue); + } + this.defaultValues = defaultValues; + } + + /** + * Sets the value. + * + * @param value + * The value. + */ + public void setValue(int value) { + String textValue; + if (hexMode) { + textValue = HexUtility.getLongValueHexString(value, digitLength); + } else { + textValue = NumberUtility.getLongValueDecimalString(value, + digitLength); + } + for (Action action : selectionActions) { + action.setEnabled(false); + } + setText(textValue); + for (Action action : selectionActions) { + action.setEnabled(true); + } + } + + private void setText(String value) { + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + + if (combo != null) { + combo.setText(value); + } else { + text.setText(value); + } + } + + /** + * Gets the value. + * + * @return The value. + */ + public int getValue() { + int result; + String textValue = getText().toLowerCase(); + + try { + if (hexMode) { + result = Integer.parseInt(textValue, 16); + } else { + result = Integer.parseInt(textValue); + + } + } catch (NumberFormatException ex) { + result = 0; + } + + return result; + } + + private String getText() { + String result; + if (combo != null) { + result = combo.getText(); + } else { + result = text.getText(); + } + return result; + } + + /** + * Adds a selection action which is fired when the field content changes. + * + * @param action + * The selection action, not <code>null</code>. + */ + public void addSelectionAction(Action action) { + if (action == null) { + throw new IllegalArgumentException( + "Parameter 'action' must not be null."); + } + selectionActions.add(action); + combo.addSelectionListener(action); + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/MessageManager.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/MessageManager.java new file mode 100644 index 00000000..491323a8 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/MessageManager.java @@ -0,0 +1,370 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.action.IStatusLineManager; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPart; + +import com.wudsn.ide.base.BasePlugin; +import com.wudsn.ide.base.common.TextUtility; + +/** + * Message manager for an work benach part of type {@link IEditorPart} or + * {@link IViewPart}. + * + * @author Peter Dell + * + */ +public final class MessageManager { + + private final static class FieldRegistryEntry { + + private Field field; + private int messageId; + + private ControlDecoration controlDecoration; + + public FieldRegistryEntry(Field field, int messageId) { + if (field == null) { + throw new IllegalArgumentException( + "Parameter 'field' must not be null."); + } + if (messageId < 0) { + throw new IllegalArgumentException( + "Parameter 'messageId' must not be negative."); + } + this.field = field; + this.messageId = messageId; + + Control control = field.getControl(); + + controlDecoration = new ControlDecoration(control, SWT.RIGHT + | SWT.TOP); + controlDecoration.setShowHover(true); + } + + public Field getField() { + return field; + } + + public int getMessageId() { + return messageId; + } + + public ControlDecoration getControlDecoration() { + return controlDecoration; + + } + } + + private static final class MessageQueueEntry { + + private int messageId; + private int severity; + private String message; + private String[] parameters; + private Throwable throwable; + + public MessageQueueEntry(int messageId, int severity, String message, + String[] parameters, Throwable throwable) { + this.messageId = messageId; + this.severity = severity; + this.message = message; + this.parameters = parameters; + this.throwable = throwable; + } + + public int getMessageId() { + return messageId; + } + + public int getSeverity() { + return severity; + } + + public String getMessage() { + return message; + } + + public String[] getParameters() { + return parameters; + } + + public Throwable getThrowable() { + return throwable; + } + + } + + private IWorkbenchPart workbenchPart; + + private IStatusLineManager statusLineManager; + + private List<FieldRegistryEntry> fieldRegistryEntries; + + private List<MessageQueueEntry> messageQueueEntries; + private boolean messageQueueError; + + private Color yellow; + private Color red; + + public MessageManager(IWorkbenchPart workbenchPart) { + if (workbenchPart == null) { + throw new IllegalArgumentException( + "Parameter 'workbenchPart' must not be null."); + } + this.workbenchPart = workbenchPart; + + fieldRegistryEntries = new ArrayList<FieldRegistryEntry>(); + messageQueueEntries = new ArrayList<MessageQueueEntry>(); + messageQueueError = false; + + yellow = new Color(Display.getDefault(), 0, 255, 255); + red = new Color(Display.getDefault(), 255, 0, 0); + + } + + public void dispose() { + for (FieldRegistryEntry fieldRegistryEntry : fieldRegistryEntries) { + ControlDecoration controlDecoration; + controlDecoration = fieldRegistryEntry.getControlDecoration(); + controlDecoration.hide(); + controlDecoration.dispose(); + } + fieldRegistryEntries.clear(); + + yellow.dispose(); + red.dispose(); + } + + private void initStatusLineManager() { + if (statusLineManager == null) { + if (workbenchPart instanceof IEditorPart) { + statusLineManager = ((IEditorPart) workbenchPart) + .getEditorSite().getActionBars().getStatusLineManager(); + } else if (workbenchPart instanceof IViewPart) { + statusLineManager = ((IViewPart) workbenchPart).getViewSite() + .getActionBars().getStatusLineManager(); + } else { + throw new IllegalStateException("Workbench part " + + workbenchPart + " has an unsupported type."); + } + } + } + + public void registerField(Field field, int messageId) { + if (field == null) { + throw new IllegalArgumentException( + "Parameter 'field' must not be null."); + } + if (messageId < 0) { + throw new IllegalArgumentException( + "Parameter 'messageId' must not be negative."); + } + FieldRegistryEntry fieldRegistryEntry = new FieldRegistryEntry(field, + messageId); + fieldRegistryEntries.add(fieldRegistryEntry); + } + + public void clearMessages() { + messageQueueEntries.clear(); + messageQueueError = false; + + for (FieldRegistryEntry fieldRegistryEntry : fieldRegistryEntries) { + + Control control = fieldRegistryEntry.getField().getControl(); + control.setForeground(control.getParent().getForeground()); + ControlDecoration controlDecoration; + controlDecoration = fieldRegistryEntry.getControlDecoration(); + controlDecoration.hide(); + } + + initStatusLineManager(); + statusLineManager.setMessage(null); + statusLineManager.setErrorMessage(null); + + } + + /** + * Sends a message to the message queue. + * + * @param messageId + * The message id identifying the target UI element of the + * message. + * @param severity + * The severity, see {@link IStatus#INFO},{@link IStatus#WARNING} + * , {@link IStatus#ERROR}. + * @param message + * The message text, not <code>null</code>. + * @param parameters + * The message parameters, may be empty or null. + */ + public void sendMessage(int messageId, int severity, String message, + String... parameters) { + if (message == null) { + throw new IllegalArgumentException( + "Parameter 'message' must not be null."); + } + MessageQueueEntry messageQueueEntry; + messageQueueEntry = new MessageQueueEntry(messageId, severity, message, + parameters, null); + addMessageQueueEntry(messageQueueEntry); + } + + /** + * Sends a message to the message queue based on a core exception. + * + * @param messageId + * The message id identifying the target UI element of the + * message. + * @param coreException + * The core exception with the status and text information, not + * <code>null</code>. + */ + public void sendMessage(int messageId, CoreException coreException) { + if (coreException == null) { + throw new IllegalArgumentException( + "Parameter 'coreException' must not be null."); + } + MessageQueueEntry messageQueueEntry; + messageQueueEntry = new MessageQueueEntry(messageId, coreException + .getStatus().getSeverity(), coreException.getStatus() + .getMessage(), null, coreException); + addMessageQueueEntry(messageQueueEntry); + + } + + private void addMessageQueueEntry(MessageQueueEntry messageQueueEntry) { + if (messageQueueEntry == null) { + throw new IllegalArgumentException( + "Parameter 'messageQueueEntry' must not be null."); + } + messageQueueEntries.add(messageQueueEntry); + if (messageQueueEntry.getSeverity() == IStatus.ERROR) { + messageQueueError = true; + } + } + + public boolean containsError() { + return messageQueueError; + } + + public void displayMessages() { + + initStatusLineManager(); + + FieldDecorationRegistry fieldDecorationRegistry = FieldDecorationRegistry + .getDefault(); + Image informationImage = fieldDecorationRegistry.getFieldDecoration( + FieldDecorationRegistry.DEC_INFORMATION).getImage(); + Image warningImage = fieldDecorationRegistry.getFieldDecoration( + FieldDecorationRegistry.DEC_INFORMATION).getImage(); + Image errorImage = fieldDecorationRegistry.getFieldDecoration( + FieldDecorationRegistry.DEC_ERROR).getImage(); + + for (MessageQueueEntry messageQueueEntry : messageQueueEntries) { + + String messageText = TextUtility.format( + messageQueueEntry.getMessage(), + messageQueueEntry.getParameters()); + + BasePlugin plugin = BasePlugin.getInstance(); + + switch (messageQueueEntry.getSeverity()) { + case IStatus.OK: + statusLineManager.setMessage(messageText); + + plugin.log(messageQueueEntry.getMessage(), + messageQueueEntry.getParameters()); + break; + + case IStatus.INFO: + case IStatus.WARNING: + case IStatus.ERROR: + Image image; + switch (messageQueueEntry.getSeverity()) { + case IStatus.INFO: + image = informationImage; + break; + case IStatus.WARNING: + image = warningImage; + break; + case IStatus.ERROR: + image = errorImage; + break; + default: + image = null; + } + for (FieldRegistryEntry fieldRegistryEntry : fieldRegistryEntries) { + if (fieldRegistryEntry.getMessageId() == messageQueueEntry + .getMessageId()) { + + ControlDecoration controlDecoration; + Control control; + control = fieldRegistryEntry.getField().getControl(); + + if (messageQueueEntry.getSeverity() == IStatus.ERROR) { + control.setForeground(red); + } else { + control.setForeground(control.getParent() + .getForeground()); + } + + controlDecoration = fieldRegistryEntry + .getControlDecoration(); + + controlDecoration.setImage(image); + controlDecoration.setDescriptionText(messageText); + controlDecoration.show(); + } + } + if (messageQueueEntry.getSeverity() == IStatus.ERROR) { + statusLineManager.setErrorMessage(messageText); + } else { + statusLineManager.setMessage(messageText); + + } + plugin.logError(messageQueueEntry.getMessage(), + messageQueueEntry.getParameters(), + messageQueueEntry.getThrowable()); + break; + default: + throw new IllegalStateException("Severity " + + messageQueueEntry.getSeverity() + + " is not supported."); + } + + } + } +} diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/MultiLineTextField.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/MultiLineTextField.java new file mode 100644 index 00000000..9ffcc6b8 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/MultiLineTextField.java @@ -0,0 +1,210 @@ +/** + * Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.Bullet; +import org.eclipse.swt.custom.CaretEvent; +import org.eclipse.swt.custom.CaretListener; +import org.eclipse.swt.custom.LineBackgroundEvent; +import org.eclipse.swt.custom.LineBackgroundListener; +import org.eclipse.swt.custom.LineStyleEvent; +import org.eclipse.swt.custom.LineStyleListener; +import org.eclipse.swt.custom.ST; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GlyphMetrics; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; + +/** + * Multi line text field without label. + * + * @author Peter Dell + * + * @since 1.6.0 + */ +public final class MultiLineTextField extends Field { + + static final Color LINE_NUMBER_COLOR = new Color(Display.getCurrent(), new RGB(0, 0, 255)); + static final Color LINE_HIGHLIGHT_COLOR = new Color(Display.getCurrent(), new RGB(220, 220, 255)); + + Color parentBackground; + Color ownBackground; + private StyledText text; + private ModifyListener textModifyListener; + + /** + * Creates a text field. + * + * @param parent + * The parent composite, not <code>null</code>. + * @param style + * The SWT style. + */ + public MultiLineTextField(final Composite parent, int style) { + if (parent == null) { + throw new IllegalArgumentException("Parameter 'parent' must not be null."); + } + + parentBackground = parent.getBackground(); + text = new StyledText(parent, style | SWT.MULTI); + ownBackground = text.getBackground(); + + text.addCaretListener(new CaretListener() { + + @Override + public void caretMoved(CaretEvent event) { + StyledText text = (StyledText) event.widget; + text.redraw(); + } + }); + text.addLineStyleListener(new LineStyleListener() { + @Override + public void lineGetStyle(LineStyleEvent event) { + // Set the line number + StyledText text = (StyledText) event.widget; + event.bulletIndex = text.getLineAtOffset(event.lineOffset); + + // Set the style, 12 pixels wide for each digit + StyleRange style = new StyleRange(); + style.metrics = new GlyphMetrics(0, 0, Integer.toString(text.getLineCount() + 1).length() * 12); + style.foreground = LINE_NUMBER_COLOR; + + // Create and set the bullet + event.bullet = new Bullet(ST.BULLET_NUMBER, style); + } + }); + + text.addLineBackgroundListener(new LineBackgroundListener() { + + @Override + public void lineGetBackground(LineBackgroundEvent event) { + StyledText text = (StyledText) event.widget; + if (text.getEditable()) { + if (text.getLineAtOffset(event.lineOffset) == text.getLineAtOffset(text.getCaretOffset())) { + event.lineBackground = LINE_HIGHLIGHT_COLOR; + } else { + event.lineBackground = ownBackground; + } + } else { + event.lineBackground = parentBackground; + } + } + }); + + textModifyListener = new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + MultiLineTextField.this.notifyChangeListenner(); + } + }; + } + + /** + * Gets the text control representing this text field. + * + * @return The text control, not <code>null</code>. + */ + public StyledText getText() { + return text; + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return text; + } + + public void setVisible(boolean visible) { + text.setVisible(visible); + + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean enabled) { + text.setEnabled(enabled); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEditable(boolean editable) { + text.setEditable(editable); + text.setBackground(editable ? ownBackground : parentBackground); + } + + /** + * Sets the value. + * + * @param value + * The value, not <code>null</code>. + */ + public void setValue(String value) { + if (value == null) { + throw new IllegalArgumentException("Parameter 'value' must not be null."); + } + if (!value.equals(text.getText())) { + text.removeModifyListener(textModifyListener); + text.setText(value); + text.addModifyListener(textModifyListener); + } + } + + /** + * Gets the value. + * + * @return The value, not <code>null</code>. + */ + public String getValue() { + String result; + result = text.getText(); + result = result.trim(); + return result; + + } + + /** + * Sets the selection. + * <p> + * Indexing is zero based. The range of a selection is from 0..N where N is + * the number of characters in the widget. + * + * @param start + * new caret position. + */ + + public void setSelection(int start) { + text.setSelection(start); + text.setFocus(); + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/SWTFactory.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/SWTFactory.java new file mode 100644 index 00000000..dbb5507e --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/SWTFactory.java @@ -0,0 +1,133 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; + +/** + * Utility class for creating SWT components. Based on the SWT factory of the + * JDT. + * + * @author Peter Dell + */ +public final class SWTFactory { + private SWTFactory() { + } + + /** + * Creates a composite that uses the parent's font and has a grid layout + * + * @param parent + * The parent to add the composite to, not <code>null</code>. + * @param columns + * The number of columns the composite should have. + * @param hspan + * The horizontal span the new composite should take up in the + * parent. + * @param style + * The fill style of the composite {@link GridData#GridData(int)} + * . + * @return The new composite with a grid layout, not <code>null</code>. + */ + public static Composite createComposite(Composite parent, int columns, + int hspan, int style) { + if (parent == null) { + throw new IllegalArgumentException( + "Parameter 'parent' must not be null."); + } + Composite composite = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(columns, false); + gridLayout.marginWidth = 0; + composite.setLayout(gridLayout); + composite.setFont(parent.getFont()); + GridData gd = new GridData(style); + gd.horizontalSpan = hspan; + composite.setLayoutData(gd); + return composite; + } + + /** + * Creates a group that uses the parent's font and has a grid layout + * + * @param parent + * The parent to add the composite to, not <code>null</code>. + * @param text + * The group title, not <code>null</code>. + * @param columns + * The number of columns the composite should have. + * @param hspan + * The horizontal span the new composite should take up in the + * parent. + * @param fill + * The fill style of the composite {@link GridData}. + * @return The new composite with a grid layout, not <code>null</code>. + */ + public static Group createGroup(Composite parent, String text, int columns, + int hspan, int fill) { + if (parent == null) { + throw new IllegalArgumentException( + "Parameter 'parent' must not be null."); + } + if (text == null) { + throw new IllegalArgumentException( + "Parameter 'text' must not be null."); + } + Group group = new Group(parent, SWT.NONE); + GridLayout layout = new GridLayout(columns, true); + group.setLayout(layout); + group.setText(text); + group.setFont(parent.getFont()); + GridData gd = new GridData(fill); + gd.horizontalSpan = hspan; + group.setLayoutData(gd); + return group; + } + + /** + * Create a number of labels to fill up the layout. + * + * @param composite + * The composite which is used as parent for the label, not + * <code>null</code>. + * @param number + * The number of labels to be created, a positive integer. + */ + @SuppressWarnings("unused") + public static void createLabels(Composite composite, int number) { + if (composite == null) { + throw new IllegalArgumentException( + "Parameter 'composite' must not be null."); + } + if (number < 1) { + throw new IllegalArgumentException( + "Parameter 'number' must not be positive. Specified value is " + + number + "."); + } + for (int i = 0; i < number; i++) { + new Label(composite, SWT.NONE); + } + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/TextField.java b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/TextField.java new file mode 100644 index 00000000..e018cb07 --- /dev/null +++ b/com.wudsn.ide.base/src/com/wudsn/ide/base/gui/TextField.java @@ -0,0 +1,133 @@ +/** +* Copyright (C) 2009 - 2014 <a href="http://www.wudsn.com" target="_top">Peter Dell</a> + * + * This file is part of WUDSN IDE. + * + * WUDSN IDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * WUDSN IDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WUDSN IDE. If not, see <http://www.gnu.org/licenses/>. + */ + +package com.wudsn.ide.base.gui; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + +/** + * Simple text field. + * + * @author Peter Dell + * + */ +public final class TextField extends Field { + + private Text text; + + /** + * Creates a text field. + * + * @param parent + * The parent composite, not <code>null</code>. + * @param labelText + * The label text, not <code>null</code>. + * @param style + * The SWT style. + */ + public TextField(Composite parent, String labelText, + int style) { + if (parent == null) { + throw new IllegalArgumentException( + "Parameter 'parent' must not be null."); + } + if (labelText == null) { + throw new IllegalArgumentException( + "Parameter 'labelText' must not be null."); + } + + label = new Label(parent, SWT.RIGHT); + label.setText(labelText); + + text = new Text(parent, style); + + } + + /** + * Gets the text control representing this text field. + * + * @return The text control, not <code>null</code>. + */ + public Text getText() { + return text; + } + + /** + * {@inheritDoc} + */ + @Override + public Control getControl() { + return text; + } + + public void setVisible(boolean visible) { + label.setVisible(visible); + text.setVisible(visible); + + } + + /** + * {@inheritDoc} + */ + @Override + public void setEnabled(boolean enabled) { + label.setEnabled(enabled); + text.setEnabled(enabled); + } + + /** + * {@inheritDoc} + */ + @Override + public void setEditable(boolean editable) { + text.setEditable(editable); + } + + /** + * Sets the value. + * + * @param value + * The value, not <code>null</code>. + */ + public void setValue(String value) { + if (value == null) { + throw new IllegalArgumentException( + "Parameter 'value' must not be null."); + } + text.setText(value); + } + + /** + * Gets the value. + * + * @return The value, not <code>null</code>. + */ + public String getValue() { + String result; + result = text.getText(); + result = result.trim(); + return result; + + } + +} \ No newline at end of file diff --git a/com.wudsn.ide.feature/.project b/com.wudsn.ide.feature/.project new file mode 100644 index 00000000..b9470f0d --- /dev/null +++ b/com.wudsn.ide.feature/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>com.wudsn.ide.feature</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.pde.FeatureBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.pde.FeatureNature</nature> + </natures> +</projectDescription> diff --git a/com.wudsn.ide.feature/build.properties b/com.wudsn.ide.feature/build.properties new file mode 100644 index 00000000..2b07cd1a --- /dev/null +++ b/com.wudsn.ide.feature/build.properties @@ -0,0 +1,4 @@ +bin.includes = feature.xml,\ + gpl-2_0.txt,\ + build.properties,\ + .project diff --git a/com.wudsn.ide.feature/feature.xml b/com.wudsn.ide.feature/feature.xml new file mode 100644 index 00000000..62a96009 --- /dev/null +++ b/com.wudsn.ide.feature/feature.xml @@ -0,0 +1,378 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feature + id="com.wudsn.ide.feature" + label="WUDSN IDE" + version="1.7.0.qualifier" + provider-name="WUDSN"> + + <description url="http://www.wudsn.com"> + The WUDSN IDE is an Eclipse feature which provides integrated +an assembler development environment for Apple II, Atari 2600, +Atari 7800, Atari 8-bit, Commodore 8-bit and Nintendo 8-bit +computers. + </description> + + <copyright> + WUDSN IDE Copyright (C) 2009 - 2018 Peter Dell +66822 Lebach, Germany +This program is free software: you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation, version 2 of the License. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. You should have +received a copy of the GNU General Public License along with +this program. If not, see http://www.gnu.org/licenses. +This software includes the runtime of the Rhino JavaScript engine +published under the Mozialla Public Licence (MPL) as part of +the graphics editor. +See http://www.mozilla.org/rhino for details. +This software includes algorithms and code ported from the First +Atari Image Library (FAIL) as part of the graphics editor. +See http://http://fail.sourceforge.net/ for details. +This software uses the libraries from Another Slight Atari Player +(ASAP) for replaying Atari 8-bit sound modules. +See http://asap.sourceforge.net/ for details. +This software uses the libraries from JSIDPlay2 for replaying +C64 sound modules. +See http://jsidplay2.sourceforge.net/ for details. + </copyright> + + <license url="http://www.gnu.org/licenses"> + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + </license> + + <url> + <update label="WUDSN Eclipse Update Site" url="http://www.wudsn.com/update"/> + </url> + + <plugin + id="com.wudsn.ide.asm" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.wudsn.ide.base" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.wudsn.ide.gfx" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.wudsn.ide.asm.compilers" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.wudsn.ide.asm.compilers.test" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.wudsn.ide.dsk" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + + <plugin + id="com.wudsn.ide.snd" + download-size="0" + install-size="0" + version="0.0.0" + unpack="false"/> + +</feature> diff --git a/com.wudsn.ide.feature/gpl-2_0.txt b/com.wudsn.ide.feature/gpl-2_0.txt new file mode 100644 index 00000000..6e812ec1 --- /dev/null +++ b/com.wudsn.ide.feature/gpl-2_0.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS