Compare commits

...

342 Commits
1.47 ... master

Author SHA1 Message Date
Denis Molony
2bc330e377 tidying 2024-05-07 08:34:02 +10:00
Denis Molony
6c81bcc93b allow dos 4.5 2024-05-07 08:28:33 +10:00
Denis Molony
171fd66ba1 tidying 2024-05-02 12:38:45 +10:00
Denis Molony
aa77ec1cd4 fixed catalog date display problem 2024-04-29 18:26:31 +10:00
Denis Molony
8f95988596 link to DiskBrowserApp 2024-04-06 16:33:53 +10:00
Denis Molony
8cb682f068 added C1/4100 2024-03-12 17:37:13 +10:00
Denis Molony
dfc7a68580 renamed jarFolder 2023-09-30 23:55:41 +10:00
Denis Molony
4b476457d3 added ant build.xml 2023-09-28 15:27:27 +10:00
Denis Molony
3bf972a761 tidying 2023-09-16 06:51:31 +10:00
Denis Molony
fd5bee6e5c tidying 2023-04-07 19:50:02 +10:00
Denis Molony
0af6714119 tidying 2023-03-16 09:00:31 +10:00
Denis Molony
22dfaf65c6 spacing 2023-03-11 16:35:48 +10:00
Denis Molony
f4a6465b04 shorten absolute path 2023-02-24 12:33:21 +10:00
Denis Molony
64d5ce9ee2 allow 2img length = 0 2023-02-20 13:02:53 +10:00
Denis Molony
4e48437adc tidying 2023-02-15 20:30:34 +10:00
Denis Molony
e2abdcabc9 added comment 2023-01-16 13:08:46 +10:00
Denis Molony
bc0993e9d3 better pascal TEXT handling 2022-12-15 20:35:26 +10:00
Denis Molony
9fba3893ba tidying 2022-12-11 01:59:44 +10:00
Denis Molony
09c4542118 Merge branch 'master' of https://github.com/dmolony/DiskBrowser.git 2022-12-11 01:56:35 +10:00
Denis Molony
af3ead0441 tidying 2022-12-11 01:54:00 +10:00
Denis Molony
c12392200b
Merge pull request #24 from frankmilliron/master
Add some new equates
2022-11-27 04:53:01 +10:00
frankmilliron
794fef8610
Add some new equates 2022-11-26 10:31:06 -08:00
frankmilliron
2ec0e10c5e
Add some new equates 2022-11-26 10:27:06 -08:00
Denis Molony
668ed719fa tidying 2022-09-16 06:26:52 +10:00
Denis Molony
23b95675cf tidying 2022-08-22 16:55:32 +10:00
Denis Molony
b98ecbed8d allow truncated bin2 files 2022-08-10 21:01:21 +10:00
Denis Molony
82381a6a47 fixed DateTime bug 2022-08-10 18:06:43 +10:00
Denis Molony
3f2f1bfbca stupid eclipse 2022-08-10 15:10:16 +10:00
Denis Molony
a7119528a6 reorder block allocations 2022-08-10 15:08:10 +10:00
Denis Molony
6f943ae8c2 tidying 2022-07-31 10:08:15 +10:00
Denis Molony
5add6c0729 tidying 2022-07-31 09:58:00 +10:00
Denis Molony
921c946ab5 Added Squeeze format (BQY) 2022-07-31 09:21:52 +10:00
Denis Molony
898587b23b Added a formatted checkbox to the save dialog 2022-07-31 07:13:59 +10:00
Denis Molony
37c489f252 Fixed Pascal bug with invalid dates 2022-07-29 23:00:30 +10:00
Denis Molony
036e47b9b1 tidying 2022-07-09 08:13:05 +10:00
Denis Molony
250c1a9bd9 more spell code 2022-06-21 13:59:42 +10:00
Denis Molony
b19e89d7cf fixed bug in known spells 2022-06-21 12:51:43 +10:00
Denis Molony
d71cabb754 tidying 2022-06-17 18:24:43 +10:00
Denis Molony
848b2469ae refactoring Character 2022-06-11 15:52:58 +10:00
Denis Molony
989b1d5ab9 fixed link to WizardryApp 2022-06-10 15:59:47 +10:00
Denis Molony
e0a2c50d5b fixed attributes bug 2022-06-10 15:56:48 +10:00
Denis Molony
8765299cf4 tidying 2022-06-09 16:14:52 +10:00
Denis Molony
97fe58d94d refactoring getWizLong and getSignedShort 2022-06-06 10:15:14 +10:00
Denis Molony
7d4d8c75e6 adjusted huffman decode routine 2022-06-04 13:06:20 +10:00
Denis Molony
f5664a9ce9 Updated Item 2022-05-31 17:44:41 +10:00
Denis Molony
9d8cdcd67b explained wiz4 naming convention 2022-05-29 19:58:12 +10:00
Denis Molony
12b74f9d21 Wizardry IV screen 2022-05-29 19:51:19 +10:00
Denis Molony
1b405c6f38 tidying 2022-05-29 18:53:08 +10:00
Denis Molony
66d6910f91 started combining CharacterV1 and CharacterV4 2022-05-29 16:21:20 +10:00
Denis Molony
572ee8c3a1 added possessions to characters 2022-05-29 15:16:11 +10:00
Denis Molony
2a14b7baad display attributes and spells 2022-05-29 12:52:42 +10:00
Denis Molony
a633e28ed3 added character parties 2022-05-29 10:43:10 +10:00
Denis Molony
ca11e8ee68 improved group routine 2022-05-28 19:15:32 +10:00
Denis Molony
ce52253f5d found how to group characters 2022-05-28 18:54:06 +10:00
Denis Molony
e408aa3d62 tidying 2022-05-28 16:19:37 +10:00
Denis Molony
c4b4d17f52 Items and Monsters for Wizardry IV 2022-05-28 12:59:25 +10:00
Denis Molony
0b72f1a37d Added characters to Wiz4 2022-05-27 18:30:30 +10:00
Denis Molony
1b86b31233 removed GregorianCalendar 2022-05-11 10:09:27 +10:00
Denis Molony
1b9e9d47ac checking Wiz4 disks 2022-05-03 15:36:37 +10:00
Denis Molony
decc781572 tidying 2022-04-13 09:51:17 +10:00
Denis Molony
16ccc2632a allow woz disks to be saved as converted disk images 2022-04-09 18:25:02 +10:00
Denis Molony
40c2afbd48 tidying 2022-04-06 18:05:37 +10:00
Denis Molony
f9038810d2 wrong picture 2022-03-31 12:06:39 +10:00
Denis Molony
3435e825df use stored experience points 2022-03-31 12:04:23 +10:00
Denis Molony
ff7c6fd126 found bug in Wizardry exp points calculation 2022-03-29 18:00:32 +10:00
Denis Molony
91846d8563 later screen 2022-03-28 18:36:17 +10:00
Denis Molony
19b6339ac5 calculate experience points correctly 2022-03-28 17:02:01 +10:00
Denis Molony
db4953618c allow woz format pascal disks 2022-03-28 13:55:49 +10:00
Denis Molony
c1deee56a0 tidying 2022-03-26 21:37:41 +10:00
Denis Molony
b1ed1a74ba tidying 2022-03-26 18:07:17 +10:00
Denis Molony
cf0cfd278b added Message 2022-03-18 19:22:07 +10:00
Denis Molony
48aca6e1eb tidying 2022-03-14 08:46:56 +10:00
Denis Molony
d83ac3afda changed Maze hex output to debug screen 2022-03-10 21:06:52 +10:00
Denis Molony
2270b1f6db tidying 2022-03-10 19:04:52 +10:00
Denis Molony
da67dbe0d7 more details on maze levels 2022-03-10 16:21:18 +10:00
Denis Molony
133352ba31 various 2022-02-07 15:28:22 +10:00
Denis Molony
761c43dc3d changed award function 2022-02-07 15:26:42 +10:00
Denis Molony
d03de97247 fixed stack overflow bug 2022-02-07 15:26:17 +10:00
Denis Molony
e67bbeaf8b made some functions visible for Wizardry Disk Browser 2022-01-27 13:14:07 +10:00
Denis Molony
6dfc72b2d2 adding new instanceof pattern 2021-09-28 19:17:10 +10:00
Denis Molony
fb748df4ae tidying 2021-09-19 17:09:34 +10:00
Denis Molony
4a03c411fc renamed DataPanel to OutputPanel 2021-09-18 23:44:48 +10:00
Denis Molony
54db7d95d6 replaced pascal screen 2021-09-18 23:28:48 +10:00
Denis Molony
59b22be2bd tidying 2021-09-16 18:24:13 +10:00
Denis Molony
47a07f5cfe added second pascal screen 2021-09-15 14:30:28 +10:00
Denis Molony
bca12f0738 tidying 2021-09-15 10:51:45 +10:00
Denis Molony
27100ad38e DreamGraphix 3200 2021-08-30 16:56:43 +10:00
Denis Molony
2a6fd74013 tidying 2021-08-20 20:04:53 +10:00
Denis Molony
4767c317d0 fixed bad prodos date 2021-07-30 19:33:03 +10:00
Denis Molony
068f382c87 tidying 2021-07-30 06:03:40 +10:00
Denis Molony
e7d8c4ebc2 tidying 2021-07-29 13:18:13 +10:00
Denis Molony
9ef0f82dea tidying 2021-07-29 13:17:36 +10:00
Denis Molony
fba8c07142 tidying 2021-07-29 09:35:07 +10:00
Denis Molony
4aefc8b695 abandoned load address indexing 2021-07-29 09:33:41 +10:00
Denis Molony
a8574d24f8 moved code to Utility 2021-07-29 06:32:22 +10:00
Denis Molony
449e6c0e9a tidying 2021-07-28 17:19:32 +10:00
Denis Molony
09b6f66855 MS floating point 2021-07-28 14:42:28 +10:00
Denis Molony
f30fe1a78a debugged bug in debug 2021-07-27 17:58:39 +10:00
Denis Molony
0e40e25710 debug for CP/M basic 2021-07-27 17:55:19 +10:00
Denis Molony
f02c932d91 tidying 2021-07-27 15:57:46 +10:00
Denis Molony
c18cfe9ed7 more CP/M basic tokens 2021-07-27 11:58:43 +10:00
Denis Molony
dcb3e32845 more CP/M basic 2021-07-26 21:39:05 +10:00
Denis Molony
08d6d1b136 better CP/M basic 2021-07-26 15:42:40 +10:00
Denis Molony
954b0c0bd8 more CP/M basic 2021-07-26 12:02:52 +10:00
Denis Molony
f22b4dcd46 tidying 2021-07-25 21:11:50 +10:00
Denis Molony
c42481637e oops 2021-07-25 18:31:02 +10:00
Denis Molony
7ca8160d13 initial CP/M basic 2021-07-25 18:30:22 +10:00
Denis Molony
26316a82a9 better CP/M format checker 2021-07-24 20:59:00 +10:00
Denis Molony
5f29cfcd09 removed protected from AbstractFile.name 2021-07-24 18:08:40 +10:00
Denis Molony
7f2e963689 tidying 2021-07-22 19:40:52 +10:00
Denis Molony
8ea18c8cc1 resource fork in DefaultAppleFile 2021-07-21 21:17:43 +10:00
Denis Molony
f92fe5b57e edit 2021-07-02 20:36:59 +10:00
Denis Molony
bb4dcbdd7c edit 2021-07-02 20:33:10 +10:00
Denis Molony
8aec65449e edit 2021-07-02 20:31:31 +10:00
Denis Molony
ff273e7df9 edit 2021-07-02 20:26:59 +10:00
Denis Molony
1a1f9df93f edit 2021-07-02 16:33:12 +10:00
Denis Molony
80a07c0496 edit 2021-07-02 16:11:53 +10:00
Denis Molony
8369e4a788 edit 2021-07-02 16:09:37 +10:00
Denis Molony
bfeab1477c edit 2021-07-02 16:04:42 +10:00
Denis Molony
ad8464c6c2 edit 2021-07-02 16:03:31 +10:00
Denis Molony
b1ff2f7740 edit 2021-07-02 16:01:45 +10:00
Denis Molony
4040238bbf edit 2021-07-02 15:59:50 +10:00
Denis Molony
70f21070d5 edit 2021-07-02 15:58:19 +10:00
Denis Molony
91693bbc37 edit 2021-07-02 15:56:54 +10:00
Denis Molony
8a9b824302 edit 2021-07-02 15:53:09 +10:00
Denis Molony
06f4713baa edit 2021-07-02 15:49:55 +10:00
Denis Molony
e7ded68b03 extra screen 2021-07-02 15:48:30 +10:00
Denis Molony
58cec66535 edited 2021-07-02 15:45:07 +10:00
Denis Molony
c105d07e60 doco for applesoft formatting 2021-07-02 15:43:17 +10:00
Denis Molony
3e32b3caf0 fixed bolloxed pascal date format 2021-06-25 19:04:20 +10:00
Denis Molony
7aa80b8b5e date format fix 2021-06-19 19:46:59 +10:00
Denis Molony
5e053872a9 CPM fixes 2021-06-18 10:48:38 +10:00
Denis Molony
7d162a4baf more listeners, recognise deleted CPM files 2021-06-14 15:55:35 +10:00
Denis Molony
83d5be3f9f more PropertyChangeListeners 2021-06-06 16:27:30 +10:00
Denis Molony
ea0a827331 fixed the hide panel actions 2021-06-04 16:53:04 +10:00
Denis Molony
dc551285bb tidying 2021-06-03 18:11:44 +10:00
Denis Molony
95538f5282 Extracted AnimationWorker from DataPanel 2021-06-03 14:59:13 +10:00
Denis Molony
9d706d62ef extracted ImagePanel from DataPanel 2021-06-03 14:52:10 +10:00
Denis Molony
63fc59accc using PropertyChangeListeners 2021-06-02 11:48:57 +10:00
Denis Molony
beb0830c25 created more listeners 2021-06-01 20:29:34 +10:00
Denis Molony
ae1188ae27 tidying 2021-06-01 20:21:21 +10:00
Denis Molony
1b27ea02e7 tidying 2021-05-22 20:15:20 +10:00
Denis Molony
c2f2277717 use both 2021-05-22 11:10:59 +10:00
Denis Molony
79ccd6793c https://jdk.java.net/16/ 2021-05-22 11:02:49 +10:00
Denis Molony
5be0236845 changed to https://jdk.java.net 2021-05-22 10:55:19 +10:00
Denis Molony
59bf768d14 more descriptions 2021-05-22 10:41:04 +10:00
Denis Molony
566479450a added duplicates screenshot 2021-05-22 10:31:33 +10:00
Denis Molony
b45e7d7e54 new disk list image 2021-05-22 09:59:06 +10:00
Denis Molony
9dddd2be3e combined file and buffer saves 2021-05-21 14:36:44 +10:00
Denis Molony
d3f21e0d49 better save error dialogs 2021-05-21 13:34:33 +10:00
Denis Molony
af706e03c7 tidying 2021-05-21 12:59:54 +10:00
Denis Molony
b6496e9c87 created nufx package 2021-05-21 12:57:03 +10:00
Denis Molony
a33aedd750 tidying 2021-05-21 12:50:23 +10:00
Denis Molony
13647213c6 refactoring 2021-05-20 20:13:06 +10:00
Denis Molony
5ea96a260d Save disk/file/sectors 2021-05-20 12:44:40 +10:00
Denis Molony
438dd9dd4c tidying save actions 2021-05-19 19:28:04 +10:00
Denis Molony
45a97d2959 tidying 2021-05-19 19:07:45 +10:00
Denis Molony
a8de9099b1 tidying 2021-05-19 18:19:55 +10:00
Denis Molony
adf77ba603 tidying 2021-05-19 18:19:25 +10:00
Denis Molony
62c09af14f consolidated some of the getWord() type routines in Utility, also allow
user to save the current file buffer to the local file system.
2021-05-19 18:13:17 +10:00
Denis Molony
ddadfd3198 display resource forks 2021-05-17 21:07:09 +10:00
Denis Molony
4f95871d83 tidying 2021-05-14 12:38:45 +10:00
Denis Molony
fe2df5247c removed getWordBigEndian 2021-05-14 12:23:01 +10:00
Denis Molony
3bd836a828 removing calls to intValue() 2021-05-14 12:19:32 +10:00
Denis Molony
a72bdff81d Allow partial unpacking of corrupt NuFX disks 2021-05-13 14:56:56 +10:00
Denis Molony
90f8657722 binary2 summary 2021-05-12 19:15:32 +10:00
Denis Molony
b7106787d1 keep suffixes private 2021-05-12 18:14:49 +10:00
Denis Molony
3c2816baf0 Delay the toString call on AppleDisk 2021-05-12 17:08:24 +10:00
Denis Molony
c6443ff89e display nufx disks correctly 2021-05-08 19:56:57 +10:00
Denis Molony
269a1bbd83 show NuFX contents on disk file display 2021-05-06 21:30:23 +10:00
Denis Molony
768111571e no summary 2021-05-06 18:21:57 +10:00
Denis Molony
d6d0b1672e wrap print lines at 80 columns 2021-05-06 18:14:40 +10:00
Denis Molony
76b2d4a291 moved toString() back to SubLine 2021-05-06 08:26:47 +10:00
Denis Molony
c3ee00673d applesoft alignment bug 2021-05-06 08:12:46 +10:00
Denis Molony
7ec277e0a3 tidying 2021-05-05 14:32:38 +10:00
Denis Molony
33b58e8d79 indicate resource forks 2021-05-05 13:10:33 +10:00
Denis Molony
73e964fe76 tidying 2021-05-04 16:31:11 +10:00
Denis Molony
7d79f8f365 use eof 2021-05-04 10:02:20 +10:00
Denis Molony
2dff5a966c write resource fork 2021-05-04 08:50:30 +10:00
Denis Molony
ccbfc47a66 removed buffer parameter 2021-05-04 07:24:08 +10:00
Denis Molony
032da321c3 Created FileWriter 2021-05-04 06:12:44 +10:00
Denis Molony
39269d3b27 tidying 2021-05-02 14:46:52 +10:00
Denis Molony
a006053297 NuFX summary 2021-05-02 14:45:26 +10:00
Denis Molony
4353d0e0ca tidying 2021-05-02 09:38:24 +10:00
Denis Molony
f7b455a7f1 re-enabled bxy 2021-04-28 14:34:41 +10:00
Denis Molony
bb1a7af6c8 debug output for prodos catalogs 2021-04-27 21:26:09 +10:00
Denis Molony
0032b0c2bb testing 2021-04-26 10:03:43 +10:00
Denis Molony
113917e278 tidying 2021-04-25 18:14:14 +10:00
Denis Molony
8db6489649 tidying 2021-04-25 12:08:09 +10:00
Denis Molony
e135b65948 bug when adding a catalog block to the top level 2021-04-24 15:37:57 +10:00
Denis Molony
d0bfde2e80 parentPointer bug 2021-04-24 13:01:58 +10:00
Denis Molony
8cdda92146 check return value for addFile() 2021-04-19 14:10:01 +10:00
Denis Molony
20121431af fixed some constants 2021-04-19 14:01:27 +10:00
Denis Molony
59290f7d25 tidy volume name 2021-04-19 12:58:19 +10:00
Denis Molony
4ab834300d use file name as volume name 2021-04-19 12:46:45 +10:00
Denis Molony
43ff3c203e bug fixes 2021-04-19 12:33:14 +10:00
Denis Molony
373e8d29a3 rename tmp files 2021-04-19 09:53:37 +10:00
Denis Molony
e6100d247a tidying 2021-04-18 20:11:26 +10:00
Denis Molony
838791ad05 removed ThreadHeader 2021-04-18 17:34:35 +10:00
Denis Molony
5681aa4270 crc bug with sdk disk images 2021-04-18 13:30:50 +10:00
Denis Molony
a51a1251c6 deleted unused file 2021-04-18 11:25:24 +10:00
Denis Molony
e9b0bcf218 tidying 2021-04-18 11:25:07 +10:00
Denis Molony
25d2ccf950 tidying 2021-04-18 11:18:59 +10:00
Denis Molony
a9ce1d8d73 choose best volume name 2021-04-18 08:54:21 +10:00
Denis Molony
e3ac682fc0 tidying 2021-04-18 07:32:03 +10:00
Denis Molony
49fa2903fc removed GregorianCalendar from prodos 2021-04-17 17:33:27 +10:00
Denis Molony
1c9cbc4942 lzw2 crc bug fixed 2021-04-17 14:41:36 +10:00
Denis Molony
2cb40b84e4 initial shk support 2021-04-17 12:14:06 +10:00
Denis Molony
6aa882ecbd partial writing 2021-04-16 21:08:59 +10:00
Denis Molony
b0b00b4029 tidying 2021-04-16 13:50:28 +10:00
Denis Molony
645c6a4a36 new package for writing prodos disks 2021-04-15 17:27:20 +10:00
Denis Molony
2168025e80 Split NuFX 2021-03-29 11:04:01 +10:00
Denis Molony
ce16479742 tidying, new V16 switch 2021-03-28 16:44:36 +10:00
Denis Molony
0637f526ca Initial MagicWindow support 2021-03-26 11:11:34 +10:00
Denis Molony
1b7be1189d better handle prodos text files with record length = 1 2021-03-18 15:15:08 +10:00
Denis Molony
9fece93d31 tidying 2021-03-17 14:31:37 +10:00
Denis Molony
62bf355ca3 tidying 2021-03-09 16:23:05 +10:00
Denis Molony
5e133715cd tidying 2021-03-06 10:29:54 +10:00
Denis Molony
df955b4885 tidying 2021-03-04 13:57:49 +10:00
Denis Molony
a290327a3e created HeaderFormatter 2021-03-04 09:23:20 +10:00
Denis Molony
34c8c16aba created XrefFormatter 2021-03-04 01:34:09 +10:00
Denis Molony
a4aa18330c created program formatters 2021-03-03 23:19:13 +10:00
Denis Molony
d4d568ac78 new Alignment class 2021-02-26 12:52:32 +10:00
Denis Molony
d71075a16c preparing to change alignment 2021-02-26 08:16:14 +10:00
Denis Molony
a20f026cf2 tidying 2021-02-25 15:59:41 +10:00
Denis Molony
e3eb32d493 tidying 2021-02-23 20:06:42 +10:00
Denis Molony
903ec192e2 wrapping finished 2021-02-23 07:56:51 +10:00
Denis Molony
5901ea87ce tidying 2021-02-22 20:11:52 +10:00
Denis Molony
30c44770d3 wrapping bug 2021-02-22 15:32:07 +10:00
Denis Molony
d1afecbfa0 tidying 2021-02-22 13:41:34 +10:00
Denis Molony
228c70e1ea Applesoft ugly line-wrapping option 2021-02-22 13:09:45 +10:00
Denis Molony
5507271a74 fixed REM alignment issue 2021-02-22 09:24:34 +10:00
Denis Molony
a8030c469c tidying 2021-02-21 18:03:26 +10:00
Denis Molony
20ad92fbd5 tidying 2021-02-21 07:42:40 +10:00
Denis Molony
1aaee043a3 removed CALL target from integer list 2021-02-21 06:50:18 +10:00
Denis Molony
ba6c067fb3 tidying 2021-02-20 19:49:48 +10:00
Denis Molony
506742bbac keep parameters out of call target 2021-02-20 19:33:53 +10:00
Denis Molony
6a1f7043cb removing options 2021-01-21 21:02:45 +10:00
Denis Molony
78350ac7ac tidying 2021-01-19 09:54:52 +10:00
Denis Molony
a8c83642d9 tidying 2021-01-19 06:01:16 +10:00
Denis Molony
135a91c985 tidying 2021-01-19 05:47:01 +10:00
Denis Molony
4791366b1e better applesoft debug 2021-01-18 10:06:17 +10:00
Denis Molony
2ef764ea5c Beagle Bros hacks 2021-01-17 08:59:01 +10:00
Denis Molony
1062a18004 tidying 2021-01-16 10:15:27 +10:00
Denis Molony
10041dd35a add applesoft screen to README 2021-01-16 08:16:40 +10:00
Denis Molony
dacfb130cb allow unterminated strings 2021-01-16 08:10:19 +10:00
Denis Molony
112fbc086b trim DATA 2021-01-15 08:37:17 +10:00
Denis Molony
ca64401dde allow empty DATA 2021-01-15 08:19:34 +10:00
Denis Molony
301b6c27c4 moved string handling to SubLine 2021-01-14 21:55:57 +10:00
Denis Molony
7d8c1641e9 tidying 2021-01-14 20:07:41 +10:00
Denis Molony
42524619af shortuts 2021-01-14 17:28:42 +10:00
Denis Molony
85f0f17e61 combine literals and strings under constants 2021-01-14 08:57:57 +10:00
Denis Molony
4a435e64e7 floats 2021-01-14 08:47:10 +10:00
Denis Molony
488b1cfeb2 handle negative literals 2021-01-14 06:29:36 +10:00
Denis Molony
5a6c3841f9 better line wrapping 2021-01-12 10:34:29 +10:00
Denis Molony
486180e423 tidying 2021-01-11 12:59:01 +10:00
Denis Molony
8633d082b3 rename menu items 2021-01-11 12:18:05 +10:00
Denis Molony
4cf1c43fee split applesoft options into two groups 2021-01-11 12:01:01 +10:00
Denis Molony
18289f382c removed spaces from tokens 2021-01-11 08:04:08 +10:00
Denis Molony
fdc15f7eb3 tidying 2021-01-10 20:18:45 +10:00
Denis Molony
ba7c178a5c tidying 2021-01-10 17:23:19 +10:00
Denis Molony
f5ca23da26 added option to not format applesoft 2021-01-10 16:18:48 +10:00
Denis Molony
e7554f7856 use ApplesoftConstants as interface 2021-01-08 17:52:47 +10:00
Denis Molony
dbeedff1a2 import static 2021-01-08 12:03:12 +10:00
Denis Molony
ce0cabc8e2 tidying 2021-01-07 12:56:20 +10:00
Denis Molony
21f7da6fc5 tidying 2021-01-06 19:15:58 +10:00
Denis Molony
d9a43577f7 tidying 2021-01-06 17:37:54 +10:00
Denis Molony
99fb63d30d align line numbers 2021-01-06 11:36:41 +10:00
Denis Molony
94a5753fea xref line numbers in columns 2021-01-05 18:59:36 +10:00
Denis Molony
4071baff77 tidying 2021-01-05 13:53:28 +10:00
Denis Molony
6e70142f59 don't include goto, gosub, on 2021-01-05 13:42:59 +10:00
Denis Molony
d801e85736 applesoft constants 2021-01-05 13:24:23 +10:00
Denis Molony
0ffff61265 separate array xref 2021-01-04 20:13:31 +10:00
Denis Molony
dd547ac58b renamed constant 2021-01-03 16:16:24 +10:00
Denis Molony
f32be5d55a exclude & from variable list 2021-01-03 16:05:16 +10:00
Denis Molony
bc2c0b0abd list CALLs option 2021-01-03 15:06:51 +10:00
Denis Molony
a0d702be73 unique variable names 2021-01-03 13:04:24 +10:00
Denis Molony
bda2e3358a changed headings 2021-01-01 10:49:42 +10:00
Denis Molony
16c677b83d wrap variable xref lines 2020-12-31 19:50:53 +10:00
Denis Molony
dcecfb1399 Extracted SourceLine and SubLine 2020-12-30 13:06:50 +10:00
Denis Molony
5815bf5048 added menu option 2020-12-29 14:52:32 +10:00
Denis Molony
0dddd3c32e Check for unique variable names 2020-12-29 14:39:29 +10:00
Denis Molony
c8e57055fb removed DATA 2020-12-29 10:45:43 +10:00
Denis Molony
dd89855d64 remove duplicate lines 2020-12-26 19:51:51 +10:00
Denis Molony
51a98398ca Applesoft symbol table 2020-12-26 18:32:40 +10:00
Denis Molony
f0244793a7 indent split REM correctly 2020-12-25 14:30:26 +10:00
Denis Molony
f84e0b0ce7 Split DIM option 2020-12-24 18:41:35 +10:00
Denis Molony
331cff059f ON GOSUB, ON GOTO 2020-12-24 16:25:40 +10:00
Denis Molony
1e27aafde0 List applesoft strings 2020-12-24 12:03:00 +10:00
Denis Molony
14888c22e8 tidying 2020-12-23 20:50:26 +10:00
Denis Molony
8f019f7e8c show Xref 2020-12-23 20:34:18 +10:00
Denis Molony
c07b8795bf more preferences 2020-12-21 16:10:07 +10:00
Denis Molony
3deda1c06a more Basic preferences 2020-12-21 15:33:03 +10:00
Denis Molony
77276ab156 adjusted hybrid disks, added basic preference 2020-12-21 07:50:51 +10:00
Denis Molony
0b13adbee9 fixed some infocom bugs 2020-12-20 11:34:42 +10:00
Denis Molony
e7c4423ebf moved debug to AbstractFile 2020-12-19 18:40:59 +10:00
Denis Molony
651674d1ed tidying 2020-12-19 18:23:55 +10:00
Denis Molony
fe648c7e2c Added debug output to Applesoft 2020-12-19 15:42:25 +10:00
Denis Molony
0fb9abf6c7 tidying 2020-12-19 09:05:35 +10:00
Denis Molony
697c164aee check DOS format bug 2020-12-18 19:10:18 +10:00
Denis Molony
26d218621b text file changes 2020-12-18 14:28:53 +10:00
Denis Molony
7c51072a10 test PAT 2020-12-18 11:19:20 +10:00
Denis Molony
3a33c4c3e0 fixed basic bug, text bug 2020-12-18 11:00:37 +10:00
Denis Molony
de59d9ae29 indexing error in basic 2020-12-15 19:18:56 +10:00
Denis Molony
75a43ee2e2 tidying 2020-12-13 16:14:38 +10:00
Denis Molony
a9c51331b2 fixed DOS catalog names 2020-12-12 20:41:57 +10:00
Denis Molony
e8d9af5fa4 minor dos 4.1 changes 2020-12-12 19:53:45 +10:00
Denis Molony
13a45cd9bf improved text preferences 2020-12-01 21:02:01 +10:00
Denis Molony
145bcb38de formatted REM statements 2020-11-22 20:23:35 +10:00
Denis Molony
e2e4b554e7 wrap DATA lines 2020-11-22 11:04:27 +10:00
Denis Molony
54d04ea575 fixed DOS catalog entry display name 2020-11-21 21:15:08 +10:00
Denis Molony
9fdbdf740c tidying 2020-11-20 19:19:51 +10:00
Denis Molony
678c49f540 improved option for onlyShowTargetLineNumbers 2020-11-11 19:13:07 +10:00
Denis Molony
9b1d09defe Added THEN option for IF statements 2020-11-11 14:26:30 +10:00
Denis Molony
c0dc5e8875 tidying 2020-11-10 17:32:58 +10:00
Denis Molony
080cafcd22 better empty file handling 2020-10-28 18:26:29 +10:00
Denis Molony
1b57001315 allow empty pascal files 2020-10-28 15:42:48 +10:00
Denis Molony
d8e2914258 tidying 2020-09-18 14:57:57 +10:00
Denis Molony
8c07de944d common parent for text files 2020-09-14 19:51:14 +10:00
Denis Molony
5c66c39478 spacing 2020-09-14 18:10:36 +10:00
Denis Molony
7280ead732 header underlines 2020-09-14 17:59:15 +10:00
Denis Molony
2401be0094 be nice 2020-09-13 11:39:54 +10:00
Denis Molony
dd10dadb7e added header underlines 2020-09-13 10:40:57 +10:00
Denis Molony
600c3a5ce2 added header underlines 2020-09-13 10:38:45 +10:00
Denis Molony
147f7e03f2 put 'show header' option first 2020-09-13 10:30:39 +10:00
Denis Molony
92824e5db6 Added text preferences 2020-09-13 10:22:49 +10:00
Denis Molony
67349bf999 changed DosMasterDisk to DosMasterFile 2020-08-01 14:45:26 +10:00
Denis Molony
d41620daf2 tidying 2020-07-25 21:24:00 +10:00
Denis Molony
751f3cef9c tidying 2020-07-25 21:20:26 +10:00
Denis Molony
6152256d46 tidying 2020-07-25 21:08:56 +10:00
Denis Molony
00b2e35c25 tidying 2020-07-24 08:47:16 +10:00
Denis Molony
28fd65008e restore 20-06 2020-07-23 19:54:48 +10:00
Denis Molony
3dfde675c3 testing 20-06 2020-07-23 19:52:10 +10:00
Denis Molony
7847e10990 back to where I was 2020-07-23 19:50:22 +10:00
Denis Molony
1cac234d36 I still hate eclipse 2020-07-23 18:33:37 +10:00
Denis Molony
6b04f6f687 still testing 2020-07-23 18:30:56 +10:00
Denis Molony
453648a975 testing restore 2020-07-23 18:28:32 +10:00
Denis Molony
57d9cc675f I hate eclipse 2020-07-23 18:23:27 +10:00
Denis Molony
929602c384 stupid eclipse 2020-07-23 18:21:42 +10:00
Denis Molony
2ecdf83292 remove test 2020-07-23 18:15:58 +10:00
Denis Molony
713fb38972 test installation 2020-07-23 18:12:16 +10:00
Denis Molony
419ce93f65 Dos Master exploring 2020-07-23 17:39:41 +10:00
236 changed files with 13706 additions and 5720 deletions

4
.gitignore vendored
View File

@ -1,4 +0,0 @@
/bin/
.project
.classpath
build.xml

View File

@ -1,5 +1,6 @@
# Apple II Disk Browser
### Alternative
There is a new release of [DiskBrowser2](https://github.com/dmolony/diskbrowser2) available.
### Features
- Cross-platform (Windows, MacOS, Linux)
- Disk formats
@ -30,7 +31,7 @@
* [Usage](resources/usage.md)
### Installation
* Install the **latest** version of the [JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html).
* Install the **latest** version of the [JDK](https://www.oracle.com/java/technologies/downloads/).
* Download DiskBrowser.jar from the [releases](https://github.com/dmolony/diskbrowser/releases) page.
* Double-click the jar file, or enter 'java -jar DiskBrowser.jar' in the terminal.
* Set your root folder (the top-level folder where you keep your disk images).
@ -47,16 +48,29 @@ Click on any sector in the Disk Layout panel and the Output panel will display t
![Sector details](resources/sector.png?raw=true "Sector details")
#### Cross-platform
Java runs on Windows, MacOS and Linux.
![Windows](resources/windows.png?raw=true "Windows")
#### Graphics
![Graphics](resources/graphics.png?raw=true "Graphics")
#### Applesoft Formatting and Analysis
Applesoft programs are displayed in a modern, easily-readable format. A comprehensive cross-listing of variables, strings, calls and jumps is available. Easily find duplicate variable names.
For the truly retro look, programs can be displayed in the [original 40-column line wrap](resources/basic.md) mode.
![Applesoft](resources/basic.png?raw=true "Applesoft")
#### Pascal code
![Pascal](resources/pascal.png?raw=true "Pascal")
![Pascal](resources/pascal1.png?raw=true "Pascal text")
![Pascal](resources/pascal2.png?raw=true "Pascal internals")
#### Infocom
![Infocom](resources/zork.png?raw=true "Infocom")
#### Wizardry
Wizardry scenarios 1 to 3 are reasonably complete, Wizardry IV and V are partially done. For a dedicated Wizardry application see [WizardryApp](https://github.com/dmolony/WizardryApp).
![Wizardry](resources/wizardry.png?raw=true "Wizardry")
Scenarios 4 and 5 come on multiple disks, and they need to be named so that the only difference between disk names is the identifier before the '.dsk' suffix.
![Wizardry](resources/wizardry4.png?raw=true "Wizardry IV")
#### Visicalc
DiskBrowser has an inbuilt Visicalc processor which will evaluate the sheet and display the results.
![Visicalc](resources/visicalc.png?raw=true "Visicalc")
#### Complete Disk List
Generates a list of all the disks in your collection. The list can be sorted by any of the table headings. Checksums can be generated as needed, or for the whole collection.
![Disk List](resources/disklist.png?raw=true "Disk List")
Select a disk and click the duplicates button to see all of the disks that share the same checksum.
![Duplicates](resources/duplicates.png?raw=true "Duplicates")

40
build.xml Normal file
View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="DiskBrowser" default="jar" basedir=".">
<property name="srcDir" location="src" />
<property name="binDir" location="bin" />
<property name="jarDir" location="${user.home}/Dropbox/Java" />
<property name="jarFile" location="${jarDir}/DiskBrowser.jar" />
<target name="version">
<echo>DiskBrowser.jar</echo>
<echo>${ant.version}</echo>
<echo>Java/JVM version: ${ant.java.version}</echo>
<echo>Java/JVM detail version: ${java.version}</echo>
</target>
<target name="init" depends="version">
<delete file="${binDir}/*.class" />
<delete file="${jarFile}" />
</target>
<target name="compile" depends="init">
<javac debug="on" srcdir="${srcDir}" destdir="${binDir}" includeantruntime="false">
<classpath>
<pathelement location="." />
</classpath>
</javac>
</target>
<target name="jar" depends="compile">
<jar destfile="${jarFile}">
<fileset dir="${binDir}" />
<zipfileset src="${jarDir}/InputPanel.jar" />
<manifest>
<attribute name="Main-Class" value="com.bytezone.diskbrowser.gui.DiskBrowser" />
</manifest>
</jar>
</target>
</project>

13
resources/basic.md Normal file
View File

@ -0,0 +1,13 @@
### Applesoft program listing
#### State of the art circa 1980
<img src="basic0.png" alt="terrible" width="700"/>
#### Original (using Print Char 21)
<img src="basic5.png" alt="truly awful" width="400"/>
#### No formatting
<img src="basic2.png" alt="better" width="850"/>
#### Formatted
<img src="basic3.png" alt="best" width="550"/>

BIN
resources/basic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 KiB

BIN
resources/basic0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

BIN
resources/basic1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
resources/basic2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
resources/basic3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
resources/basic4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
resources/basic5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 724 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 784 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
resources/duplicates.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 KiB

BIN
resources/pascal1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
resources/pascal2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
resources/wizardry4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -6,17 +6,21 @@ import javax.swing.JComponent;
import javax.swing.JPanel;
import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.prodos.ResourceFork;
import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
public abstract class AbstractFile implements DataSource
// -----------------------------------------------------------------------------------//
{
static boolean showDebugText;
protected String name;
public byte[] buffer;
protected AssemblerProgram assembler;
AssemblerProgram assembler;
protected BufferedImage image;
protected int loadAddress;
int loadAddress;
ResourceFork resourceFork;
// ---------------------------------------------------------------------------------//
public AbstractFile (String name, byte[] buffer)
@ -26,12 +30,58 @@ public abstract class AbstractFile implements DataSource
this.buffer = buffer;
}
// ---------------------------------------------------------------------------------//
public void setName (String name)
// ---------------------------------------------------------------------------------//
{
this.name = name; // Infocom ZObject uses this - but it sucks
}
// ---------------------------------------------------------------------------------//
@Override
public String getText () // Override this to get a tailored text representation
// ---------------------------------------------------------------------------------//
{
return "Name : " + name + "\n\nNo text description";
StringBuilder text = new StringBuilder ();
text.append ("Name : " + name + "\n\nNo text description");
if (resourceFork != null)
{
text.append ("\n\nResource Fork:\n\n");
text.append (resourceFork);
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
@Override
public byte[] getBuffer ()
// ---------------------------------------------------------------------------------//
{
return buffer;
}
// ---------------------------------------------------------------------------------//
public static void setDefaultDebug (boolean value)
// ---------------------------------------------------------------------------------//
{
showDebugText = value;
}
// ---------------------------------------------------------------------------------//
public void setResourceFork (ResourceFork resourceFork)
// ---------------------------------------------------------------------------------//
{
this.resourceFork = resourceFork;
}
// ---------------------------------------------------------------------------------//
public static void setDebug (boolean value)
// ---------------------------------------------------------------------------------//
{
showDebugText = value;
}
// ---------------------------------------------------------------------------------//
@ -90,7 +140,6 @@ public abstract class AbstractFile implements DataSource
public JComponent getComponent ()
// ---------------------------------------------------------------------------------//
{
JPanel panel = new JPanel ();
return panel;
return new JPanel ();
}
}

View File

@ -0,0 +1,82 @@
package com.bytezone.diskbrowser.applefile;
// ---------------------------------------------------------------------------------//
class Alignment implements ApplesoftConstants
// ---------------------------------------------------------------------------------//
{
int equalsPosition;
int targetLength;
SubLine firstSubLine;
SubLine lastSubLine;
// ---------------------------------------------------------------------------------//
void reset ()
// ---------------------------------------------------------------------------------//
{
equalsPosition = 0;
targetLength = 0;
firstSubLine = null;
lastSubLine = null;
}
// ---------------------------------------------------------------------------------//
void setFirst (SubLine subline)
// ---------------------------------------------------------------------------------//
{
reset ();
firstSubLine = subline;
check (subline);
}
// ---------------------------------------------------------------------------------//
void check (SubLine subline)
// ---------------------------------------------------------------------------------//
{
if (equalsPosition < subline.equalsPosition)
equalsPosition = subline.equalsPosition;
int temp = subline.endPosition - subline.equalsPosition;
if (targetLength < temp)
targetLength = temp;
lastSubLine = subline;
}
// ---------------------------------------------------------------------------------//
public String getAlignedText (SubLine subline)
// ---------------------------------------------------------------------------------//
{
StringBuilder line = subline.toStringBuilder (); // get line
if (equalsPosition == 0 || subline.is (TOKEN_REM))
return line.toString ();
int alignEqualsPos = equalsPosition;
int targetLength = subline.endPosition - equalsPosition;
// insert spaces before '=' until it lines up with the other assignment lines
while (alignEqualsPos-- > subline.equalsPosition)
line.insert (subline.equalsPosition, ' ');
if (line.charAt (line.length () - 1) == ':')
while (targetLength++ <= this.targetLength)
line.append (" ");
return line.toString ();
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Equals position ..... %d%n", equalsPosition));
text.append (String.format ("Target length ....... %d%n", targetLength));
text.append (String.format ("First subline ....... %s%n", firstSubLine));
text.append (String.format ("Last subline ........ %s", lastSubLine));
return text.toString ();
}
}

View File

@ -0,0 +1,177 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_BACKSPACE;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_CR;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LF;
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
import com.bytezone.diskbrowser.gui.BasicPreferences;
// -----------------------------------------------------------------------------------//
public class AppleBasicFormatter extends BasicFormatter
// -----------------------------------------------------------------------------------//
{
private final LineFormatter flatFormatter = new FlatLine ();
private final LineFormatter wrapFormatter = new WrapLine ();
// ---------------------------------------------------------------------------------//
public AppleBasicFormatter (ApplesoftBasicProgram program,
BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
super (program, basicPreferences);
}
// ---------------------------------------------------------------------------------//
@Override
public void append (StringBuilder fullText)
// ---------------------------------------------------------------------------------//
{
int loadAddress = getLoadAddress ();
int ptr = 0;
int linkField;
StringBuilder currentLine = new StringBuilder ();
LineFormatter formatter =
basicPreferences.appleLineWrap ? wrapFormatter : flatFormatter;
while ((linkField = getShort (buffer, ptr)) != 0)
{
int lineNumber = getShort (buffer, ptr + 2);
currentLine.append (String.format (" %d ", lineNumber));
ptr += 4;
ptr = formatter.formatLine (currentLine, ptr);
if (ptr != (linkField - loadAddress))
System.out.printf ("%s: ptr: %04X, nextLine: %04X%n", program.name,
ptr + loadAddress, linkField);
currentLine.append (NEWLINE);
fullText.append (currentLine);
currentLine.setLength (0);
}
}
// ---------------------------------------------------------------------------------//
interface LineFormatter
// ---------------------------------------------------------------------------------//
{
abstract int formatLine (StringBuilder currentLine, int ptr);
}
// ---------------------------------------------------------------------------------//
class FlatLine implements LineFormatter
// ---------------------------------------------------------------------------------//
{
// -------------------------------------------------------------------------------//
@Override
public int formatLine (StringBuilder currentLine, int ptr)
// -------------------------------------------------------------------------------//
{
byte b;
while ((b = buffer[ptr++]) != 0)
if (isHighBitSet (b))
{
String token = String.format (" %s ", ApplesoftConstants.tokens[b & 0x7F]);
currentLine.append (token);
}
else
switch (b)
{
case ASCII_CR:
currentLine.append (NEWLINE);
break;
case ASCII_BACKSPACE:
if (currentLine.length () > 0)
currentLine.deleteCharAt (currentLine.length () - 1);
break;
case ASCII_LF:
int indent = getIndent (currentLine);
currentLine.append ("\n");
for (int i = 0; i < indent; i++)
currentLine.append (" ");
break;
default:
currentLine.append ((char) b);
}
return ptr;
}
}
// ---------------------------------------------------------------------------------//
class WrapLine implements LineFormatter
// ---------------------------------------------------------------------------------//
{
private static final int LEFT_MARGIN = 5;
private static final int RIGHT_MARGIN = 33;
// -------------------------------------------------------------------------------//
@Override
public int formatLine (StringBuilder currentLine, int ptr)
// -------------------------------------------------------------------------------//
{
byte b;
int cursor = currentLine.length ();
while ((b = buffer[ptr++]) != 0)
if (isHighBitSet (b))
{
String token = String.format (" %s ", ApplesoftConstants.tokens[b & 0x7F]);
currentLine.append (token);
cursor = incrementCursor (currentLine, cursor, token.length ());
}
else
switch (b)
{
case ASCII_CR:
currentLine.append (NEWLINE);
cursor = 0;
break;
case ASCII_BACKSPACE:
if (cursor > 0)
{
currentLine.deleteCharAt (currentLine.length () - 1);
--cursor;
}
break;
case ASCII_LF:
currentLine.append ("\n");
for (int i = 0; i < cursor; i++)
currentLine.append (" ");
break;
default:
currentLine.append ((char) b);
cursor = incrementCursor (currentLine, cursor, 1);
}
return ptr;
}
// -------------------------------------------------------------------------------//
private int incrementCursor (StringBuilder currentLine, int cursor, int size)
// -------------------------------------------------------------------------------//
{
assert size <= 9; // longest token possible (7 plus 2 spaces)
cursor += size;
if ((cursor) >= RIGHT_MARGIN)
{
cursor = cursor >= 40 ? cursor - 40 : LEFT_MARGIN;
currentLine.append ("\n ".substring (0, cursor + 1));
}
return cursor;
}
}
}

View File

@ -1,755 +1,87 @@
package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
public class ApplesoftBasicProgram extends BasicProgram
// -----------------------------------------------------------------------------------//
public class ApplesoftBasicProgram extends BasicProgram implements ApplesoftConstants
// -----------------------------------------------------------------------------------//
{
private static final byte TOKEN_FOR = (byte) 0x81;
private static final byte TOKEN_NEXT = (byte) 0x82;
private static final byte TOKEN_INPUT = (byte) 0x84;
private static final byte TOKEN_LET = (byte) 0xAA;
private static final byte TOKEN_GOTO = (byte) 0xAB;
private static final byte TOKEN_IF = (byte) 0xAD;
private static final byte TOKEN_GOSUB = (byte) 0xB0;
private static final byte TOKEN_REM = (byte) 0xB2;
private static final byte TOKEN_PRINT = (byte) 0xBA;
private static final byte TOKEN_THEN = (byte) 0xC4;
private static final byte TOKEN_EQUALS = (byte) 0xD0;
private final List<SourceLine> sourceLines = new ArrayList<> ();
private final int endPtr;
private final Set<Integer> gotoLines = new HashSet<> ();
private final Set<Integer> gosubLines = new HashSet<> ();
private int ptr = 0; // end-of-program marker
private final UserBasicFormatter userBasicFormatter;
private final AppleBasicFormatter appleBasicFormatter;
private final DebugBasicFormatter debugBasicFormatter;
private final XrefFormatter xrefFormatter;
private final HeaderFormatter headerFormatter;
// ---------------------------------------------------------------------------------//
public ApplesoftBasicProgram (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
int ptr = 0;
int prevOffset = 0;
int max = buffer.length - 4; // need at least 4 bytes to make a SourceLine
while (ptr < max)
while (buffer[ptr + 1] != 0) // msb of link field
{
int offset = Utility.unsignedShort (buffer, ptr);
if (offset <= prevOffset)
break;
SourceLine line = new SourceLine (ptr);
SourceLine line = new SourceLine (this, buffer, ptr);
sourceLines.add (line);
ptr += line.length;
prevOffset = offset;
ptr += line.length; // assumes lines are contiguous
}
endPtr = ptr;
userBasicFormatter = new UserBasicFormatter (this, basicPreferences);
appleBasicFormatter = new AppleBasicFormatter (this, basicPreferences);
debugBasicFormatter = new DebugBasicFormatter (this, basicPreferences);
xrefFormatter = new XrefFormatter (this, basicPreferences);
headerFormatter = new HeaderFormatter (this, basicPreferences);
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder fullText = new StringBuilder ();
Stack<String> loopVariables = new Stack<String> ();
StringBuilder text = new StringBuilder ();
if (basicPreferences.showHeader)
addHeader (fullText);
int alignPos = 0;
StringBuilder text;
int baseOffset = basicPreferences.showTargets ? 12 : 8;
headerFormatter.append (text);
for (SourceLine line : sourceLines)
if (showDebugText)
{
text = new StringBuilder (getBase (line) + " ");
int indent = loopVariables.size (); // each full line starts at the loop indent
int ifIndent = 0; // IF statement(s) limit back indentation by NEXT
for (SubLine subline : line.sublines)
{
// Allow empty statements (caused by a single colon)
if (subline.isEmpty ())
continue;
// A REM statement might conceal an assembler routine
// - see P.CREATE on Diags2E.DSK
if (subline.is (TOKEN_REM) && subline.containsToken ())
{
int address = subline.getAddress () + 1; // skip the REM token
fullText.append (text + String.format ("REM - Inline assembler @ $%02X (%d)%n",
address, address));
String padding = " ".substring (0, text.length () + 2);
for (String asm : subline.getAssembler ())
fullText.append (padding + asm + "\n");
continue;
}
// Reduce the indent by each NEXT, but only as far as the IF indent allows
if (subline.is (TOKEN_NEXT))
{
popLoopVariables (loopVariables, subline);
indent = Math.max (ifIndent, loopVariables.size ());
}
// Are we joining REM lines with the previous subline?
if (!basicPreferences.splitRem && subline.isJoinableRem ())
{
// Join this REM statement to the previous line, so no indenting
fullText.deleteCharAt (fullText.length () - 1); // remove newline
fullText.append (" ");
}
else // ... otherwise do all the indenting and showing of targets etc.
{
// Prepare target indicators for subsequent sublines (ie no line number)
if (basicPreferences.showTargets && !subline.isFirst ())
if (subline.is (TOKEN_GOSUB))
text.append ("<<--");
else if (subline.is (TOKEN_GOTO) || subline.isImpliedGoto ())
text.append (" <--");
// Align assign statements if required
if (basicPreferences.alignAssign)
alignPos = alignEqualsPosition (subline, alignPos);
int column = indent * 2 + baseOffset;
while (text.length () < column)
text.append (" ");
}
// Add the current text, then reset it
int pos = subline.is (TOKEN_REM) ? 0 : alignPos;
String lineText = subline.getAlignedText (pos);
// Check for a wrappable REM statement
// (see SEA BATTLE on DISK283.DSK)
if (subline.is (TOKEN_REM) && lineText.length () > basicPreferences.wrapRemAt + 4)
{
// System.out.println (lineText.length ());
String copy = lineText.substring (4);
text.append ("REM ");
int inset = text.length () + 1;
List<String> remarks = splitRemark (copy, basicPreferences.wrapRemAt);
boolean first = true;
for (String remark : remarks)
{
if (first)
{
first = false;
text.append (remark);
}
else
text.append ("\n ".substring (0, inset) + remark);
}
}
else
text.append (lineText);
// Check for a wrappable PRINT statement
// (see FROM MACHINE LANGUAGE TO BASIC on DOSToolkit2eB.dsk)
if (basicPreferences.wrapPrintAt > 0 //
&& (subline.is (TOKEN_PRINT) || subline.is (TOKEN_INPUT))
&& countChars (text, ASCII_QUOTE) == 2 // just start and end quotes
&& countChars (text, ASCII_CARET) == 0) // no control characters
// && countChars (text, ASCII_SEMI_COLON) == 0)
{
if (true) // new method
{
List<String> lines = splitPrint (lineText);
if (lines != null)
{
int offset = text.indexOf ("PRINT");
if (offset < 0)
offset = text.indexOf ("INPUT");
String fmt = "%-" + offset + "." + offset + "s%s%n";
String padding = text.substring (0, offset);
for (String s : lines)
{
fullText.append (String.format (fmt, padding, s));
padding = "";
}
}
else
fullText.append (text + "\n");
}
else // old method
{
int first = text.indexOf ("\"") + 1;
int last = text.indexOf ("\"", first + 1) - 1;
if ((last - first) > basicPreferences.wrapPrintAt)
{
int ptr = first + basicPreferences.wrapPrintAt;
do
{
fullText.append (text.substring (0, ptr)
+ "\n ".substring (0, first + 1));
text.delete (0, ptr);
ptr = basicPreferences.wrapPrintAt;
} while (text.length () > basicPreferences.wrapPrintAt);
}
fullText.append (text + "\n");
}
}
else
fullText.append (text + "\n");
text.setLength (0);
// Calculate indent changes that take effect after the current subline
if (subline.is (TOKEN_IF))
ifIndent = ++indent;
else if (subline.is (TOKEN_FOR))
{
loopVariables.push (subline.forVariable);
++indent;
}
}
// Reset alignment value if we just left an IF - the indentation will be different now.
if (ifIndent > 0)
alignPos = 0;
debugBasicFormatter.append (text);
return Utility.rtrim (text);
}
int ptr = endPtr + 2;
if (ptr < buffer.length - 1) // sometimes there's an extra byte on the end
if (sourceLines.size () == 0)
{
int offset = Utility.unsignedShort (buffer, 0);
int programLoadAddress = offset - getLineLength (0);
fullText.append ("\nExtra data:\n\n");
fullText.append (HexFormatter.formatNoHeader (buffer, ptr, buffer.length - ptr,
programLoadAddress + ptr));
text.append ("\n\nThis page intentionally left blank");
return text.toString ();
}
if (fullText.length () > 0)
fullText.deleteCharAt (fullText.length () - 1); // remove last newline
return fullText.toString ();
}
private List<String> splitPrint (String line)
{
int first = line.indexOf ("\"") + 1;
int last = line.indexOf ("\"", first + 1) - 1;
if (first != 7 || (last - first) <= basicPreferences.wrapPrintAt)
return null;
int charsLeft = last - first + 1;
List<String> lines = new ArrayList<> ();
String padding = line.substring (0, 7);
line = line.substring (7);
String sub;
while (true)
{
if (line.length () >= basicPreferences.wrapPrintAt)
{
sub = line.substring (0, basicPreferences.wrapPrintAt);
line = line.substring (basicPreferences.wrapPrintAt);
}
else
{
sub = line;
line = "";
}
String subline = padding + sub;
charsLeft -= basicPreferences.wrapPrintAt;
if (charsLeft > 0)
lines.add (subline);
else
{
lines.add (subline + line);
break;
}
padding = " ";
}
return lines;
}
private List<String> splitRemark (String remark, int wrapLength)
{
List<String> remarks = new ArrayList<> ();
while (remark.length () > wrapLength)
{
int max = Math.min (wrapLength, remark.length () - 1);
while (max > 0 && remark.charAt (max) != ' ')
--max;
if (max == 0)
break;
remarks.add (remark.substring (0, max));
remark = remark.substring (max);
}
remarks.add (remark);
return remarks;
}
private int countChars (StringBuilder text, byte ch)
{
int total = 0;
for (int i = 0; i < text.length (); i++)
if (text.charAt (i) == ch)
total++;
return total;
}
private String getBase (SourceLine line)
{
if (!basicPreferences.showTargets)
return String.format (" %5d", line.lineNumber);
String lineNumberText = String.format ("%5d", line.lineNumber);
SubLine subline = line.sublines.get (0);
String c1 = " ", c2 = " ";
if (subline.is (TOKEN_GOSUB))
c1 = "<<";
if (subline.is (TOKEN_GOTO))
c1 = " <";
if (gotoLines.contains (line.lineNumber))
c2 = "> ";
if (gosubLines.contains (line.lineNumber))
c2 = ">>";
if (c1.equals (" ") && !c2.equals (" "))
c1 = "--";
if (!c1.equals (" ") && c2.equals (" "))
c2 = "--";
if (basicPreferences.onlyShowTargetLineNumbers && !c2.startsWith (">"))
lineNumberText = "";
return String.format ("%s%s %s", c1, c2, lineNumberText);
}
// Decide whether the current subline needs to be aligned on its equals sign. If so,
// and the column hasn't been calculated, read ahead to find the highest position.
private int alignEqualsPosition (SubLine subline, int currentAlignPosition)
{
if (subline.assignEqualPos > 0) // does the line have an equals sign?
{
if (currentAlignPosition == 0)
currentAlignPosition = findHighest (subline); // examine following sublines for alignment
return currentAlignPosition;
}
return 0; // reset it
}
// The IF processing is so that any assignment that is being aligned doesn't continue
// to the next full line (because the indentation has changed).
private int findHighest (SubLine startSubline)
{
boolean started = false;
int highestAssign = startSubline.assignEqualPos;
fast: for (SourceLine line : sourceLines)
{
boolean inIf = false;
for (SubLine subline : line.sublines)
{
if (started)
{
// Stop when we come to a line without an equals sign (except for non-split REMs).
// Lines that start with a REM always break.
if (subline.assignEqualPos == 0
// && (splitRem || !subline.is (TOKEN_REM) || subline.isFirst ()))
&& (basicPreferences.splitRem || !subline.isJoinableRem ()))
break fast; // of champions
if (subline.assignEqualPos > highestAssign)
highestAssign = subline.assignEqualPos;
}
else if (subline == startSubline)
started = true;
else if (subline.is (TOKEN_IF))
inIf = true;
}
if (started && inIf)
break;
}
return highestAssign;
}
@Override
public String getHexDump ()
{
if (buffer.length < 2)
return super.getHexDump ();
StringBuilder pgm = new StringBuilder ();
if (basicPreferences.showHeader)
addHeader (pgm);
int ptr = 0;
int offset = Utility.unsignedShort (buffer, 0);
int programLoadAddress = offset - getLineLength (0);
while (ptr <= endPtr) // stop at the same place as the source listing
{
int length = getLineLength (ptr);
if (length == 0)
{
pgm.append (
HexFormatter.formatNoHeader (buffer, ptr, 2, programLoadAddress + ptr));
ptr += 2;
break;
}
if (ptr + length < buffer.length)
pgm.append (
HexFormatter.formatNoHeader (buffer, ptr, length, programLoadAddress + ptr)
+ "\n\n");
ptr += length;
}
if (ptr < buffer.length)
{
int length = buffer.length - ptr;
pgm.append ("\n\n");
pgm.append (
HexFormatter.formatNoHeader (buffer, ptr, length, programLoadAddress + ptr));
}
return pgm.toString ();
}
private void addHeader (StringBuilder pgm)
{
pgm.append ("Name : " + name + "\n");
pgm.append (String.format ("Length : $%04X (%<,d)%n", buffer.length));
pgm.append (String.format ("Load at : $%04X (%<,d)%n%n", getLoadAddress ()));
}
private int getLoadAddress ()
{
int programLoadAddress = 0;
if (buffer.length > 1)
{
int offset = Utility.intValue (buffer[0], buffer[1]);
programLoadAddress = offset - getLineLength (0);
}
return programLoadAddress;
}
private int getLineLength (int ptr)
{
int offset = Utility.unsignedShort (buffer, ptr);
if (offset == 0)
return 0;
ptr += 4; // skip offset and line number
int length = 5;
while (ptr < buffer.length && buffer[ptr++] != 0)
length++;
return length;
}
private void popLoopVariables (Stack<String> loopVariables, SubLine subline)
{
if (subline.nextVariables.length == 0) // naked NEXT
{
if (loopVariables.size () > 0)
loopVariables.pop ();
}
if (basicPreferences.userFormat)
userBasicFormatter.append (text);
else
for (String variable : subline.nextVariables)
// e.g. NEXT X,Y,Z
while (loopVariables.size () > 0)
if (sameVariable (variable, loopVariables.pop ()))
break;
appleBasicFormatter.append (text);
if (basicPreferences.showAllXref)
xrefFormatter.append (text);
return Utility.rtrim (text);
}
private boolean sameVariable (String v1, String v2)
// ---------------------------------------------------------------------------------//
List<SourceLine> getSourceLines ()
// ---------------------------------------------------------------------------------//
{
if (v1.equals (v2))
return true;
if (v1.length () >= 2 && v2.length () >= 2 && v1.charAt (0) == v2.charAt (0)
&& v1.charAt (1) == v2.charAt (1))
return true;
return false;
return sourceLines;
}
private class SourceLine
// ---------------------------------------------------------------------------------//
int getEndPtr ()
// ---------------------------------------------------------------------------------//
{
List<SubLine> sublines = new ArrayList<> ();
int lineNumber;
int linePtr;
int length;
public SourceLine (int ptr)
{
linePtr = ptr;
lineNumber = Utility.intValue (buffer[ptr + 2], buffer[ptr + 3]);
int startPtr = ptr += 4;
boolean inString = false; // can toggle
boolean inRemark = false; // can only go false -> true
byte b;
while ((b = buffer[ptr++]) != 0)
{
if (inRemark) // cannot terminate a REM
continue;
if (inString)
{
if (b == ASCII_QUOTE) // terminate string
inString = false;
continue;
}
switch (b)
{
// break IF statements into two sublines (allows for easier line indenting)
case TOKEN_IF:
// skip to THEN or GOTO - if not found then it's an error
while (buffer[ptr] != TOKEN_THEN && buffer[ptr] != TOKEN_GOTO
&& buffer[ptr] != 0)
ptr++;
// keep THEN with the IF
if (buffer[ptr] == TOKEN_THEN)
++ptr;
// create subline from the condition (and THEN if it exists)
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
startPtr = ptr;
break;
// end of subline, so add it, advance startPtr and continue
case ASCII_COLON:
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
startPtr = ptr;
break;
case TOKEN_REM:
if (ptr != startPtr + 1) // REM appears mid-line (should follow a colon)
{
System.out.println ("mid-line REM token");
// System.out.println (HexFormatter.format (buffer, startPtr, 10));
sublines.add (new SubLine (this, startPtr, (ptr - startPtr) - 1));
startPtr = ptr - 1;
}
else
inRemark = true;
break;
case ASCII_QUOTE:
inString = true;
break;
}
}
// add whatever is left
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
this.length = ptr - linePtr;
}
}
private class SubLine
{
SourceLine parent;
int startPtr;
int length;
String[] nextVariables;
String forVariable = "";
int targetLine = -1;
// used for aligning the equals sign
int assignEqualPos;
public SubLine (SourceLine parent, int startPtr, int length)
{
this.parent = parent;
this.startPtr = startPtr;
this.length = length;
byte b = buffer[startPtr];
if (isHighBitSet (b))
{
switch (b)
{
case TOKEN_FOR:
int p = startPtr + 1;
while (buffer[p] != TOKEN_EQUALS)
forVariable += (char) buffer[p++];
break;
case TOKEN_NEXT:
if (length == 2) // no variables
nextVariables = new String[0];
else
{
String varList = new String (buffer, startPtr + 1, length - 2);
nextVariables = varList.split (",");
}
break;
case TOKEN_LET:
recordEqualsPosition ();
break;
case TOKEN_GOTO:
String target = new String (buffer, startPtr + 1, length - 2);
try
{
gotoLines.add (Integer.parseInt (target));
}
catch (NumberFormatException e)
{
System.out.println (
"Error parsing : GOTO " + target + " in " + parent.lineNumber);
}
break;
case TOKEN_GOSUB:
String target2 = new String (buffer, startPtr + 1, length - 2);
try
{
gosubLines.add (Integer.parseInt (target2));
}
catch (NumberFormatException e)
{
System.out.println (HexFormatter.format (buffer, startPtr + 1, length - 2));
System.out.println (
"Error parsing : GOSUB " + target2 + " in " + parent.lineNumber);
}
break;
}
}
else
{
if (isDigit (b)) // numeric, so must be a line number
{
String target = new String (buffer, startPtr, length - 1);
try
{
targetLine = Integer.parseInt (target);
gotoLines.add (targetLine);
}
catch (NumberFormatException e)
{
System.out.printf ("b: %d, start: %d, length: %d%n", b, startPtr,
(length - 1));
System.out.println (target);
System.out.println (HexFormatter.format (buffer, startPtr, length - 1));
System.out.println (e);
}
}
// else if (basicPreferences.alignAssign)
else
recordEqualsPosition ();
}
}
private boolean isImpliedGoto ()
{
byte b = buffer[startPtr];
if (isHighBitSet (b))
return false;
return (isDigit (b));
}
// Record the position of the equals sign so it can be aligned with adjacent lines.
private void recordEqualsPosition ()
{
int p = startPtr + 1;
int max = startPtr + length;
while (buffer[p] != TOKEN_EQUALS && p < max)
p++;
if (buffer[p] == TOKEN_EQUALS)
assignEqualPos = toString ().indexOf ('='); // use expanded line
}
private boolean isJoinableRem ()
{
return is (TOKEN_REM) && !isFirst ();
}
public boolean isFirst ()
{
return (parent.linePtr + 4) == startPtr;
}
public boolean is (byte token)
{
return buffer[startPtr] == token;
}
public boolean isEmpty ()
{
return length == 1 && buffer[startPtr] == 0;
}
public boolean containsToken ()
{
// ignore first byte, check the rest for tokens
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
if (isHighBitSet (buffer[p]))
return true;
return false;
}
public int getAddress ()
{
return getLoadAddress () + startPtr;
}
public String getAlignedText (int alignPosition)
{
StringBuilder line = toStringBuilder ();
while (alignPosition-- > assignEqualPos)
line.insert (assignEqualPos, ' ');
return line.toString ();
}
// A REM statement might conceal an assembler routine
public String[] getAssembler ()
{
byte[] buffer2 = new byte[length - 1];
System.arraycopy (buffer, startPtr + 1, buffer2, 0, buffer2.length);
AssemblerProgram program =
new AssemblerProgram ("REM assembler", buffer2, getAddress () + 1);
return program.getAssembler ().split ("\n");
}
@Override
public String toString ()
{
return toStringBuilder ().toString ();
}
public StringBuilder toStringBuilder ()
{
StringBuilder line = new StringBuilder ();
// All sublines end with 0 or : except IF lines that are split into two
int max = startPtr + length - 1;
if (buffer[max] == 0)
--max;
for (int p = startPtr; p <= max; p++)
{
byte b = buffer[p];
if (isHighBitSet (b))
{
if (line.length () > 0 && line.charAt (line.length () - 1) != ' ')
line.append (' ');
int val = b & 0x7F;
if (val < ApplesoftConstants.tokens.length)
line.append (ApplesoftConstants.tokens[val]);
}
else if (isControlCharacter (b))
line.append (basicPreferences.showCaret ? "^" + (char) (b + 64) : "");
else
line.append ((char) b);
}
return line;
}
return ptr;
}
}

View File

@ -1,42 +1,66 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public interface ApplesoftConstants
// -----------------------------------------------------------------------------------//
{
String[] tokens = { //
"END ", "FOR ", "NEXT ", "DATA ", // 0x80 - 0x83
"INPUT ", "DEL ", "DIM ", "READ ", // 0x84 - 0x87
"GR ", "TEXT ", "PR# ", "IN# ", // 0x88 - 0x8B
"CALL ", "PLOT ", "HLIN ", "VLIN ", // 0x8C - 0x8F
"HGR2", "HGR", "HCOLOR=", "HPLOT ", // 0x90
"DRAW ", "XDRAW ", "HTAB ", "HOME ", // 0x94
"ROT=", "SCALE=", "SHLOAD ", "TRACE ", // 0x98
"NOTRACE ", "NORMAL ", "INVERSE ", "FLASH ", // 0x9C
"COLOR=", "POP ", "VTAB ", "HIMEM:", // 0xA0
"LOMEM:", "ONERR ", "RESUME", "RECALL ", // 0xA4 - 0xA7
"STORE ", "SPEED=", "LET ", "GOTO ", // 0xA8
"RUN ", "IF ", "RESTORE ", "&", // 0xAC
"GOSUB ", "RETURN ", "REM ", "STOP ", // 0xB0
"ON ", "WAIT ", "LOAD ", "SAVE ", // 0xB4
"DEF", "POKE ", "PRINT ", "CONT", // 0xB8
"LIST ", "CLEAR ", "GET ", "NEW ", // 0xBC
"TAB(", "TO ", "FN ", "SPC(", // 0xC0
"THEN ", "AT ", "NOT ", "STEP ", // 0xC4
"+ ", "- ", "* ", "/ ", // 0xC8
"^ ", "AND ", "OR ", "> ", // 0xCC
"= ", "< ", "SGN ", "INT ", // 0xD0
"ABS ", "USR", "FRE ", "SCRN(", // 0xD4
"PDL ", "POS ", "SQR ", "RND ", // 0xD8
"LOG ", "EXP ", "COS ", "SIN ", // 0xDC
"TAN ", "ATN ", "PEEK ", "LEN ", // 0xE0 - 0xE3
"STR$ ", "VAL ", "ASC ", "CHR$ ", // 0xE4 - 0xE7
"LEFT$ ", "RIGHT$ ", "MID$ ", "", // 0xE8 - 0xEB
"", "", "", "", // 0xEC - 0xEF
"ELSE", "MOD", "INC", "DEC", // 0xF0 - 0xF3
"DEEK", "DOKE", "REPEAT", "UNTIL", // 0xF4 - 0xF7
"", "", "", "", // 0xF8 - 0xFB
"", "", "", "", // 0xFC - 0xFF
"END", "FOR", "NEXT", "DATA", // 0x80 - 0x83
"INPUT", "DEL", "DIM", "READ", // 0x84 - 0x87
"GR", "TEXT", "PR#", "IN#", // 0x88 - 0x8B
"CALL", "PLOT", "HLIN", "VLIN", // 0x8C - 0x8F
"HGR2", "HGR", "HCOLOR=", "HPLOT", // 0x90
"DRAW", "XDRAW", "HTAB", "HOME", // 0x94
"ROT=", "SCALE=", "SHLOAD", "TRACE", // 0x98
"NOTRACE", "NORMAL", "INVERSE", "FLASH", // 0x9C
"COLOR=", "POP", "VTAB", "HIMEM:", // 0xA0
"LOMEM:", "ONERR", "RESUME", "RECALL", // 0xA4 - 0xA7
"STORE", "SPEED=", "LET", "GOTO", // 0xA8
"RUN", "IF", "RESTORE", "&", // 0xAC
"GOSUB", "RETURN", "REM", "STOP", // 0xB0
"ON", "WAIT", "LOAD", "SAVE", // 0xB4
"DEF", "POKE", "PRINT", "CONT", // 0xB8
"LIST", "CLEAR", "GET", "NEW", // 0xBC
"TAB(", "TO", "FN", "SPC(", // 0xC0
"THEN", "AT", "NOT", "STEP", // 0xC4
"+", "-", "*", "/", // 0xC8
"^", "AND", "OR", ">", // 0xCC
"=", "<", "SGN", "INT", // 0xD0
"ABS", "USR", "FRE", "SCRN(", // 0xD4
"PDL", "POS", "SQR", "RND", // 0xD8
"LOG", "EXP", "COS", "SIN", // 0xDC
"TAN", "ATN", "PEEK", "LEN", // 0xE0 - 0xE3
"STR$", "VAL", "ASC", "CHR$", // 0xE4 - 0xE7
"LEFT$", "RIGHT$", "MID$", "", // 0xE8 - 0xEB
"", "", "", "", // 0xEC - 0xEF
"", "", "", "", // 0xF0 - 0xF3
"", "", "", "", // 0xF4 - 0xF7
"", "", "", "", // 0xF8 - 0xFB
"", "", "", "", // 0xFC - 0xFF
};
static final byte TOKEN_FOR = (byte) 0x81;
static final byte TOKEN_NEXT = (byte) 0x82;
static final byte TOKEN_DATA = (byte) 0x83;
static final byte TOKEN_INPUT = (byte) 0x84;
static final byte TOKEN_DIM = (byte) 0x86;
static final byte TOKEN_CALL = (byte) 0x8C;
static final byte TOKEN_ONERR = (byte) 0xA5;
static final byte TOKEN_LET = (byte) 0xAA;
static final byte TOKEN_GOTO = (byte) 0xAB;
static final byte TOKEN_IF = (byte) 0xAD;
static final byte TOKEN_AMPERSAND = (byte) 0xAF;
static final byte TOKEN_GOSUB = (byte) 0xB0;
static final byte TOKEN_RETURN = (byte) 0xB1;
static final byte TOKEN_REM = (byte) 0xB2;
static final byte TOKEN_ON = (byte) 0xB4;
static final byte TOKEN_DEF = (byte) 0xB8;
static final byte TOKEN_PRINT = (byte) 0xBA;
static final byte TOKEN_FN = (byte) 0xC2;
static final byte TOKEN_THEN = (byte) 0xC4;
static final byte TOKEN_MINUS = (byte) 0xC9;
static final byte TOKEN_EQUALS = (byte) 0xD0;
int[] tokenAddresses =
{ 0xD870, 0xD766, 0xDCF9, 0xD995, 0xDBB2, 0xF331, 0xDFD9, 0xDBE2, 0xF390, 0xF399,
0xF1E5, 0xF1DE, 0xF1D5, 0xF225, 0xF232, 0xF241, 0xF3D8, 0xF3E2, 0xF6E9, 0xF6FE,

View File

@ -3,9 +3,13 @@ package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList;
import java.util.List;
// -----------------------------------------------------------------------------------//
public class AssemblerBlocks
// -----------------------------------------------------------------------------------//
{
// ---------------------------------------------------------------------------------//
public AssemblerBlocks (byte[] buffer, int loadAddress)
// ---------------------------------------------------------------------------------//
{
int ptr = 0;
boolean inCode = true;

View File

@ -1,67 +1,69 @@
package com.bytezone.diskbrowser.applefile;
public interface AssemblerConstants
{
// 1A = INC A, 3A = DEC A
String[] mnemonics = { "BRK", "ORA", "???", "???", "TSB", "ORA", "ASL", "???", // 00
"PHP", "ORA", "ASL", "???", "TSB", "ORA", "ASL", "???", // 08
"BPL", "ORA", "ORA", "???", "TRB", "ORA", "ASL", "???", // 10
"CLC", "ORA", "INC", "???", "TRB", "ORA", "ASL", "???", // 18
"JSR", "AND", "???", "???", "BIT", "AND", "ROL", "???", // 20
"PLP", "AND", "ROL", "???", "BIT", "AND", "ROL", "???", // 28
"BMI", "AND", "AND", "???", "BIT", "AND", "ROL", "???", // 30
"SEC", "AND", "DEC", "???", "BIT", "AND", "ROL", "???", // 38
"RTI", "EOR", "???", "???", "???", "EOR", "LSR", "???", // 40
"PHA", "EOR", "LSR", "???", "JMP", "EOR", "LSR", "???", // 48
"BVC", "EOR", "EOR", "???", "???", "EOR", "LSR", "???", // 50
"CLI", "EOR", "PHY", "???", "???", "EOR", "LSR", "???", // 58
"RTS", "ADC", "???", "???", "STZ", "ADC", "ROR", "???", // 60
"PLA", "ADC", "ROR", "???", "JMP", "ADC", "ROR", "???", // 68
"BVS", "ADC", "ADC", "???", "STZ", "ADC", "ROR", "???", // 70
"SEI", "ADC", "PLY", "???", "JMP", "ADC", "ROR", "???", // 78
"BRA", "STA", "???", "???", "STY", "STA", "STX", "???", // 80
"DEY", "BIT", "TXA", "???", "STY", "STA", "STX", "???", // 88
"BCC", "STA", "STA", "???", "STY", "STA", "STX", "???", // 90
"TYA", "STA", "TXS", "???", "STZ", "STA", "STZ", "???", // 98
"LDY", "LDA", "LDX", "???", "LDY", "LDA", "LDX", "???", // A0
"TAY", "LDA", "TAX", "???", "LDY", "LDA", "LDX", "???", // A8
"BCS", "LDA", "LDA", "???", "LDY", "LDA", "LDX", "???", // B0
"CLV", "LDA", "TSX", "???", "LDY", "LDA", "LDX", "???", // B8
"CPY", "CMP", "???", "???", "CPY", "CMP", "DEC", "???", // C0
"INY", "CMP", "DEX", "???", "CPY", "CMP", "DEC", "???", // C8
"BNE", "CMP", "CMP", "???", "???", "CMP", "DEC", "???", // D0
"CLD", "CMP", "PHX", "???", "???", "CMP", "DEC", "???", // D8
"CPX", "SBC", "???", "???", "CPX", "SBC", "INC", "???", // E0
"INX", "SBC", "NOP", "???", "CPX", "SBC", "INC", "???", // E8
"BEQ", "SBC", "SBC", "???", "???", "SBC", "INC", "???", // F0
"SED", "SBC", "PLX", "???", "???", "SBC", "INC", "???" }; // F8
byte[] sizes2 = { 1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 00 - 0F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 10 - 1F
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 20 - 2F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 30 - 3F
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 40 - 4F
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 1, 0, 0, 3, 3, 0, // 50 - 5F
1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 60 - 6F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 70 - 7F
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 80 - 8F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 90 - 9F
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // A0 - AF
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // B0 - BF
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // C0 - CF
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 1, 0, 0, 3, 3, 0, // D0 - DF
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // E0 - EF
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 1, 0, 0, 3, 3, 0 }; // F0 - FF
byte[] sizes = { 1, 1, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2 };
String[] mode =
{ "Implied", "Accumulator", "Immediate", "Absolute", "Absolute, X", "Absolute, Y",
"(Absolute, X)", "(Absolute)", "Zero page", "Zero page, X", "Zero page, Y",
"(Zero page, X)", "(Zero page), Y", "(Zero page)", "Relative" };
byte[] chip65c02 =
{ 0x04, 0x0C, 0x12, 0x14, 0x1A, 0x1C, 0x32, 0x34, 0x3A, 0x3C, 0x52, 0x5A, 0x64,
0x72, 0x74, 0x7A, 0x7C, (byte) 0x80, (byte) 0x89, (byte) 0x92, (byte) 0x9C,
(byte) 0x9E, (byte) 0xB2, (byte) 0xD2, (byte) 0xDA, (byte) 0xF2, (byte) 0xFA, };
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public interface AssemblerConstants
// -----------------------------------------------------------------------------------//
{
// 1A = INC A, 3A = DEC A
String[] mnemonics = { "BRK", "ORA", "???", "???", "TSB", "ORA", "ASL", "???", // 00
"PHP", "ORA", "ASL", "???", "TSB", "ORA", "ASL", "???", // 08
"BPL", "ORA", "ORA", "???", "TRB", "ORA", "ASL", "???", // 10
"CLC", "ORA", "INC", "???", "TRB", "ORA", "ASL", "???", // 18
"JSR", "AND", "???", "???", "BIT", "AND", "ROL", "???", // 20
"PLP", "AND", "ROL", "???", "BIT", "AND", "ROL", "???", // 28
"BMI", "AND", "AND", "???", "BIT", "AND", "ROL", "???", // 30
"SEC", "AND", "DEC", "???", "BIT", "AND", "ROL", "???", // 38
"RTI", "EOR", "???", "???", "???", "EOR", "LSR", "???", // 40
"PHA", "EOR", "LSR", "???", "JMP", "EOR", "LSR", "???", // 48
"BVC", "EOR", "EOR", "???", "???", "EOR", "LSR", "???", // 50
"CLI", "EOR", "PHY", "???", "???", "EOR", "LSR", "???", // 58
"RTS", "ADC", "???", "???", "STZ", "ADC", "ROR", "???", // 60
"PLA", "ADC", "ROR", "???", "JMP", "ADC", "ROR", "???", // 68
"BVS", "ADC", "ADC", "???", "STZ", "ADC", "ROR", "???", // 70
"SEI", "ADC", "PLY", "???", "JMP", "ADC", "ROR", "???", // 78
"BRA", "STA", "???", "???", "STY", "STA", "STX", "???", // 80
"DEY", "BIT", "TXA", "???", "STY", "STA", "STX", "???", // 88
"BCC", "STA", "STA", "???", "STY", "STA", "STX", "???", // 90
"TYA", "STA", "TXS", "???", "STZ", "STA", "STZ", "???", // 98
"LDY", "LDA", "LDX", "???", "LDY", "LDA", "LDX", "???", // A0
"TAY", "LDA", "TAX", "???", "LDY", "LDA", "LDX", "???", // A8
"BCS", "LDA", "LDA", "???", "LDY", "LDA", "LDX", "???", // B0
"CLV", "LDA", "TSX", "???", "LDY", "LDA", "LDX", "???", // B8
"CPY", "CMP", "???", "???", "CPY", "CMP", "DEC", "???", // C0
"INY", "CMP", "DEX", "???", "CPY", "CMP", "DEC", "???", // C8
"BNE", "CMP", "CMP", "???", "???", "CMP", "DEC", "???", // D0
"CLD", "CMP", "PHX", "???", "???", "CMP", "DEC", "???", // D8
"CPX", "SBC", "???", "???", "CPX", "SBC", "INC", "???", // E0
"INX", "SBC", "NOP", "???", "CPX", "SBC", "INC", "???", // E8
"BEQ", "SBC", "SBC", "???", "???", "SBC", "INC", "???", // F0
"SED", "SBC", "PLX", "???", "???", "SBC", "INC", "???" }; // F8
byte[] sizes2 = { 1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 00 - 0F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 10 - 1F
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 20 - 2F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 30 - 3F
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 40 - 4F
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 1, 0, 0, 3, 3, 0, // 50 - 5F
1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 60 - 6F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 70 - 7F
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 80 - 8F
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 90 - 9F
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // A0 - AF
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // B0 - BF
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // C0 - CF
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 1, 0, 0, 3, 3, 0, // D0 - DF
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // E0 - EF
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 1, 0, 0, 3, 3, 0 }; // F0 - FF
byte[] sizes = { 1, 1, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2 };
String[] mode =
{ "Implied", "Accumulator", "Immediate", "Absolute", "Absolute, X", "Absolute, Y",
"(Absolute, X)", "(Absolute)", "Zero page", "Zero page, X", "Zero page, Y",
"(Zero page, X)", "(Zero page), Y", "(Zero page)", "Relative" };
byte[] chip65c02 =
{ 0x04, 0x0C, 0x12, 0x14, 0x1A, 0x1C, 0x32, 0x34, 0x3A, 0x3C, 0x52, 0x5A, 0x64,
0x72, 0x74, 0x7A, 0x7C, (byte) 0x80, (byte) 0x89, (byte) 0x92, (byte) 0x9C,
(byte) 0x9E, (byte) 0xB2, (byte) 0xD2, (byte) 0xDA, (byte) 0xF2, (byte) 0xFA, };
}

View File

@ -1,454 +1,488 @@
package com.bytezone.diskbrowser.applefile;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.bytezone.diskbrowser.gui.AssemblerPreferences;
import com.bytezone.diskbrowser.gui.DiskBrowser;
import com.bytezone.diskbrowser.utilities.HexFormatter;
public class AssemblerProgram extends AbstractFile
{
static AssemblerPreferences assemblerPreferences; // set by MenuHandler
private static Map<Integer, String> equates;
private final int loadAddress;
private int executeOffset;
private byte[] extraBuffer = new byte[0];
private List<Integer> entryPoints;
private List<StringLocation> stringLocations;
public static void setAssemblerPreferences (AssemblerPreferences assemblerPreferences)
{
AssemblerProgram.assemblerPreferences = assemblerPreferences;
}
public AssemblerProgram (String name, byte[] buffer, int address)
{
super (name, buffer);
this.loadAddress = address;
if (equates == null)
getEquates ();
// AssemblerBlocks assemblerBlocks = new AssemblerBlocks (buffer, address);
}
public AssemblerProgram (String name, byte[] buffer, int address, int executeOffset)
{
this (name, buffer, address);
this.executeOffset = executeOffset;
}
public void setExtraBuffer (byte[] fullBuffer, int offset, int length)
{
if (length >= 0)
{
this.extraBuffer = new byte[length];
System.arraycopy (fullBuffer, offset, extraBuffer, 0, length);
}
else
System.out.println ("Invalid length in setExtraBuffer() : " + length);
}
@Override
public String getHexDump ()
{
// It might be useful to add opt-O to change the offset. Sometimes it's useful
// to see the hex dump offset from zero, other times it's better to use the
// load address.
String text = HexFormatter.format (buffer, 0, buffer.length, loadAddress);
if (extraBuffer.length == 0)
return text;
return text + "\n\nData outside actual buffer:\n\n" + HexFormatter
.format (extraBuffer, 0, extraBuffer.length, loadAddress + buffer.length);
}
@Override
public String getAssembler ()
{
if (buffer == null)
return "No buffer";
if (assembler == null)
this.assembler = new AssemblerProgram (name, buffer, loadAddress);
if (extraBuffer.length == 0)
return assembler.getText ();
String extraName = String.format ("%s (extra)", name);
AssemblerProgram assemblerProgram =
new AssemblerProgram (extraName, extraBuffer, loadAddress + buffer.length);
return assembler.getText () + "\n\n" + assemblerProgram.getText ();
}
private void addHeader (StringBuilder pgm)
{
pgm.append (String.format ("Name : %s%n", name));
pgm.append (String.format ("Length : $%04X (%,d)%n", buffer.length, buffer.length));
pgm.append (String.format ("Load at : $%04X (%,d)%n", loadAddress, loadAddress));
if (executeOffset > 0)
pgm.append (String.format ("Entry : $%04X%n", (loadAddress + executeOffset)));
pgm.append ("\n");
}
@Override
public String getText ()
{
StringBuilder pgm = new StringBuilder ();
if (assemblerPreferences.showHeader)
addHeader (pgm);
pgm.append (getListing ());
if (assemblerPreferences.showStrings)
pgm.append (getStringsText ());
return pgm.toString ();
}
private String getListing ()
{
StringBuilder pgm = new StringBuilder ();
List<AssemblerStatement> lines = getLines ();
if (stringLocations == null)
getStrings ();
// if the assembly doesn't start at the beginning, just dump the bytes that
// are skipped
for (int i = 0; i < executeOffset; i++)
pgm.append (String.format (" %04X: %02X%n", (loadAddress + i), buffer[i]));
for (AssemblerStatement cmd : lines)
{
StringBuilder line = new StringBuilder ();
String arrowText = assemblerPreferences.showTargets ? getArrow (cmd) : "";
line.append (
String.format ("%3.3s %04X: %02X ", arrowText, cmd.address, cmd.value));
if (cmd.size > 1)
line.append (String.format ("%02X ", cmd.operand1));
if (cmd.size > 2)
line.append (String.format ("%02X ", cmd.operand2));
while (line.length () < 23)
line.append (" ");
line.append (cmd.mnemonic + " " + cmd.operand);
if (cmd.offset != 0)
{
int branch = cmd.address + cmd.offset + 2;
line.append (String.format ("$%04X", branch < 0 ? branch += 0xFFFF : branch));
}
else if (cmd.target > 0
&& (cmd.target < loadAddress - 1 || cmd.target > (loadAddress + buffer.length)))
{
while (line.length () < 40)
line.append (" ");
String text = equates.get (cmd.target);
if (text != null)
line.append ("; " + text);
else
for (int i = 0, max = ApplesoftConstants.tokenAddresses.length; i < max; i++)
if (cmd.target == ApplesoftConstants.tokenAddresses[i])
{
line.append ("; Applesoft - " + ApplesoftConstants.tokens[i]);
break;
}
}
pgm.append (line.toString () + "\n");
}
if (pgm.length () > 0)
pgm.deleteCharAt (pgm.length () - 1);
return pgm.toString ();
}
// private int showString (AssemblerStatement cmd, StringBuilder line)
// {
// int key = cmd.address - loadAddress;
// if (strings.containsKey (key))
// {
// while (line.length () < 40)
// line.append (" ");
// String s = strings.get (key);
// line.append ("# " + s);
// return s.length () - cmd.size;
// }
// return 0;
// }
private List<AssemblerStatement> getLines ()
{
List<AssemblerStatement> lines = new ArrayList<> ();
Map<Integer, AssemblerStatement> linesMap = new HashMap<> ();
List<Integer> targets = new ArrayList<> ();
int ptr = executeOffset;
int address = loadAddress + executeOffset;
while (ptr < buffer.length)
{
AssemblerStatement cmd = new AssemblerStatement (buffer[ptr]);
lines.add (cmd);
linesMap.put (address, cmd);
cmd.address = address;
if (cmd.size == 2 && ptr < buffer.length - 1)
cmd.addData (buffer[ptr + 1]);
else if (cmd.size == 3 && ptr < buffer.length - 2)
cmd.addData (buffer[ptr + 1], buffer[ptr + 2]);
else
cmd.size = 1;
// JMP, JMP, JSR
if (cmd.target >= loadAddress && cmd.target < (loadAddress + buffer.length)
&& (cmd.value == 0x4C || cmd.value == 0x6C || cmd.value == 0x20))
targets.add (cmd.target);
// branch relative
if (cmd.offset != 0)
targets.add (cmd.address + cmd.offset + 2);
address += cmd.size;
ptr += cmd.size;
}
for (Integer target : targets)
{
AssemblerStatement cmd = linesMap.get (target);
if (cmd != null)
cmd.isTarget = true;
}
return lines;
}
private String getStringsText ()
{
if (stringLocations.size () == 0)
return "";
StringBuilder text = new StringBuilder ("\n\nPossible strings:\n\n");
for (StringLocation stringLocation : stringLocations)
{
int address = stringLocation.offset + loadAddress;
text.append (String.format ("%s %04X - %04X %s %n",
entryPoints.contains (stringLocation.offset) ? "*" : " ", address,
address + stringLocation.length, stringLocation));
}
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
private void getStrings ()
{
entryPoints = new ArrayList<> ();
stringLocations = new ArrayList<> ();
int start = 0;
for (int ptr = 0; ptr < buffer.length; ptr++)
{
if ((buffer[ptr] & 0x80) != 0) // hi bit set
continue;
if (buffer[ptr] == 0x0D) // CR
continue;
if (ptr - start > 3)
stringLocations.add (new StringLocation (start, ptr - 1));
start = ptr + 1;
}
if (buffer.length - start > 3)
stringLocations.add (new StringLocation (start, buffer.length - 1));
int max = buffer.length - 2;
for (StringLocation stringLocation : stringLocations)
for (int ptr = 0; ptr < max; ptr++)
if (stringLocation.matches (buffer, ptr))
{
entryPoints.add (stringLocation.offset);
break;
}
}
private String getArrow (AssemblerStatement cmd)
{
String arrow = "";
if (cmd.value == 0x4C || cmd.value == 0x6C || cmd.value == 0x60 || cmd.offset != 0)
arrow = "<--";
if (cmd.value == 0x20 && isLocal (cmd.target)) // JSR
arrow = "<--";
if (cmd.isTarget)
if (arrow.isEmpty ())
arrow = "-->";
else
arrow = "<->";
return arrow;
}
private boolean isLocal (int target)
{
return target >= loadAddress
&& target < loadAddress + buffer.length + extraBuffer.length;
}
private void getEquates ()
{
equates = new HashMap<Integer, String> ();
DataInputStream inputEquates =
new DataInputStream (DiskBrowser.class.getClassLoader ()
.getResourceAsStream ("com/bytezone/diskbrowser/applefile/equates.txt"));
BufferedReader in = new BufferedReader (new InputStreamReader (inputEquates));
String line;
try
{
while ((line = in.readLine ()) != null)
{
if (!line.isEmpty () && !line.startsWith ("*"))
{
int address = Integer.parseInt (line.substring (0, 4), 16);
if (equates.containsKey (address))
System.out.printf ("Duplicate equate entry : %04X%n" + address);
else
equates.put (address, line.substring (6));
}
}
in.close ();
}
catch (IOException e)
{
e.printStackTrace ();
}
}
class StringLocation
{
int offset;
byte hi, lo;
int length;
boolean zeroTerminated;
boolean lowTerminated;
boolean hasLengthByte;
int digits;
int letters;
int punctuation;
int controlChars;
int spaces;
public StringLocation (int first, int last)
{
offset = first;
length = last - offset + 1;
int end = last + 1;
zeroTerminated = end < buffer.length && buffer[end] == 0;
lowTerminated = end < buffer.length && buffer[end] >= 32 && buffer[end] < 127;
if (first > 0 && (buffer[first] & 0xFF) == length + 1)
{
hasLengthByte = true;
--offset;
++length;
}
hi = (byte) ((offset + loadAddress) >>> 8);
lo = (byte) ((offset + loadAddress) & 0x00FF);
for (int i = offset; i < offset + length; i++)
{
int val = buffer[i] & 0x7F;
if (val < 32 || val == 127)
++controlChars;
else if (val == 32)
++spaces;
else if (val >= 48 && val <= 57)
++digits;
else if (val >= 65 && val <= 90)
++letters;
else if (val >= 97 && val <= 122)
++letters;
else
++punctuation;
}
}
boolean matches (byte[] buffer, int ptr)
{
return lo == buffer[ptr] && hi == buffer[ptr + 1];
}
boolean likelyString ()
{
return spaces > 0 || letters > punctuation;
}
public String address ()
{
return String.format ("%04X %02X %02X", offset, hi, lo);
}
public String toStatisticsString ()
{
return String.format ("%2d, %2d, %2d, %2d, %2d", digits, letters, punctuation,
controlChars, spaces);
}
@Override
public String toString ()
{
StringBuilder text = new StringBuilder ();
if (hasLengthByte)
text.append ("<length>");
for (int i = offset; i < offset + length; i++)
{
int val = buffer[i] & 0x7F;
if (val == 4)
text.append ("<ctrl-D>");
else if (val == 10)
text.append ("<LF>");
else if (val == 13)
text.append ("<CR>");
else
text.append ((char) val);
}
if (lowTerminated)
text.append ((char) buffer[offset + length]);
return text.toString ();
}
}
package com.bytezone.diskbrowser.applefile;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.bytezone.diskbrowser.gui.AssemblerPreferences;
import com.bytezone.diskbrowser.gui.DiskBrowser;
import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
public class AssemblerProgram extends AbstractFile
// -----------------------------------------------------------------------------------//
{
static AssemblerPreferences assemblerPreferences; // set by MenuHandler
private static Map<Integer, String> equates;
private final int loadAddress;
private int executeOffset;
private byte[] extraBuffer = new byte[0];
private List<Integer> entryPoints;
private List<StringLocation> stringLocations;
// ---------------------------------------------------------------------------------//
public static void setAssemblerPreferences (AssemblerPreferences assemblerPreferences)
// ---------------------------------------------------------------------------------//
{
AssemblerProgram.assemblerPreferences = assemblerPreferences;
}
// ---------------------------------------------------------------------------------//
public AssemblerProgram (String name, byte[] buffer, int address)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
this.loadAddress = address;
if (equates == null)
getEquates ();
// AssemblerBlocks assemblerBlocks = new AssemblerBlocks (buffer, address);
}
// ---------------------------------------------------------------------------------//
public AssemblerProgram (String name, byte[] buffer, int address, int executeOffset)
// ---------------------------------------------------------------------------------//
{
this (name, buffer, address);
this.executeOffset = executeOffset;
}
// ---------------------------------------------------------------------------------//
public void setExtraBuffer (byte[] fullBuffer, int offset, int length)
// ---------------------------------------------------------------------------------//
{
if (length >= 0)
{
this.extraBuffer = new byte[length];
System.arraycopy (fullBuffer, offset, extraBuffer, 0, length);
}
else
System.out.println ("Invalid length in setExtraBuffer() : " + length);
}
// ---------------------------------------------------------------------------------//
@Override
public String getHexDump ()
// ---------------------------------------------------------------------------------//
{
// It might be useful to add opt-O to change the offset. Sometimes it's useful
// to see the hex dump offset from zero, other times it's better to use the
// load address.
String text = HexFormatter.format (buffer, 0, buffer.length, loadAddress);
if (extraBuffer.length == 0)
return text;
return text + "\n\nData outside actual buffer:\n\n" + HexFormatter
.format (extraBuffer, 0, extraBuffer.length, loadAddress + buffer.length);
}
// ---------------------------------------------------------------------------------//
@Override
public String getAssembler ()
// ---------------------------------------------------------------------------------//
{
if (buffer == null)
return "No buffer";
if (assembler == null)
this.assembler = new AssemblerProgram (name, buffer, loadAddress);
if (extraBuffer.length == 0)
return assembler.getText ();
String extraName = String.format ("%s (extra)", name);
AssemblerProgram assemblerProgram =
new AssemblerProgram (extraName, extraBuffer, loadAddress + buffer.length);
return assembler.getText () + "\n\n" + assemblerProgram.getText ();
}
// ---------------------------------------------------------------------------------//
private void addHeader (StringBuilder pgm)
// ---------------------------------------------------------------------------------//
{
pgm.append (String.format ("Name : %s%n", name));
pgm.append (String.format ("Length : $%04X (%,d)%n", buffer.length, buffer.length));
pgm.append (String.format ("Load at : $%04X (%,d)%n", loadAddress, loadAddress));
if (executeOffset > 0)
pgm.append (String.format ("Entry : $%04X%n", (loadAddress + executeOffset)));
pgm.append ("\n");
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder pgm = new StringBuilder ();
if (assemblerPreferences.showHeader)
addHeader (pgm);
pgm.append (getListing ());
if (assemblerPreferences.showStrings)
pgm.append (getStringsText ());
return pgm.toString ();
}
// ---------------------------------------------------------------------------------//
private String getListing ()
// ---------------------------------------------------------------------------------//
{
StringBuilder pgm = new StringBuilder ();
List<AssemblerStatement> lines = getLines ();
if (stringLocations == null)
getStrings ();
// if the assembly doesn't start at the beginning, just dump the bytes that
// are skipped
for (int i = 0; i < executeOffset; i++)
pgm.append (String.format (" %04X: %02X%n", (loadAddress + i), buffer[i]));
for (AssemblerStatement cmd : lines)
{
StringBuilder line = new StringBuilder ();
String arrowText = assemblerPreferences.showTargets ? getArrow (cmd) : "";
line.append (
String.format ("%3.3s %04X: %02X ", arrowText, cmd.address, cmd.value));
if (cmd.size > 1)
line.append (String.format ("%02X ", cmd.operand1));
if (cmd.size > 2)
line.append (String.format ("%02X ", cmd.operand2));
while (line.length () < 23)
line.append (" ");
line.append (cmd.mnemonic + " " + cmd.operand);
if (cmd.offset != 0)
{
int branch = cmd.address + cmd.offset + 2;
line.append (String.format ("$%04X", branch < 0 ? branch += 0xFFFF : branch));
}
else if (cmd.target > 0
&& (cmd.target < loadAddress - 1 || cmd.target > (loadAddress + buffer.length)))
{
while (line.length () < 40)
line.append (" ");
String text = equates.get (cmd.target);
if (text != null)
line.append ("; " + text);
else
for (int i = 0, max = ApplesoftConstants.tokenAddresses.length; i < max; i++)
if (cmd.target == ApplesoftConstants.tokenAddresses[i])
{
line.append ("; Applesoft - " + ApplesoftConstants.tokens[i]);
break;
}
}
pgm.append (line.toString () + "\n");
}
if (pgm.length () > 0)
pgm.deleteCharAt (pgm.length () - 1);
return pgm.toString ();
}
// private int showString (AssemblerStatement cmd, StringBuilder line)
// {
// int key = cmd.address - loadAddress;
// if (strings.containsKey (key))
// {
// while (line.length () < 40)
// line.append (" ");
// String s = strings.get (key);
// line.append ("# " + s);
// return s.length () - cmd.size;
// }
// return 0;
// }
// ---------------------------------------------------------------------------------//
private List<AssemblerStatement> getLines ()
// ---------------------------------------------------------------------------------//
{
List<AssemblerStatement> lines = new ArrayList<> ();
Map<Integer, AssemblerStatement> linesMap = new HashMap<> ();
List<Integer> targets = new ArrayList<> ();
int ptr = executeOffset;
int address = loadAddress + executeOffset;
while (ptr < buffer.length)
{
AssemblerStatement cmd = new AssemblerStatement (buffer[ptr]);
lines.add (cmd);
linesMap.put (address, cmd);
cmd.address = address;
if (cmd.size == 2 && ptr < buffer.length - 1)
cmd.addData (buffer[ptr + 1]);
else if (cmd.size == 3 && ptr < buffer.length - 2)
cmd.addData (buffer[ptr + 1], buffer[ptr + 2]);
else
cmd.size = 1;
// JMP, JMP, JSR
if (cmd.target >= loadAddress && cmd.target < (loadAddress + buffer.length)
&& (cmd.value == 0x4C || cmd.value == 0x6C || cmd.value == 0x20))
targets.add (cmd.target);
// branch relative
if (cmd.offset != 0)
targets.add (cmd.address + cmd.offset + 2);
address += cmd.size;
ptr += cmd.size;
}
for (Integer target : targets)
{
AssemblerStatement cmd = linesMap.get (target);
if (cmd != null)
cmd.isTarget = true;
}
return lines;
}
// ---------------------------------------------------------------------------------//
private String getStringsText ()
// ---------------------------------------------------------------------------------//
{
if (stringLocations.size () == 0)
return "";
StringBuilder text = new StringBuilder ("\n\nPossible strings:\n\n");
for (StringLocation stringLocation : stringLocations)
{
int address = stringLocation.offset + loadAddress;
text.append (String.format ("%s %04X - %04X %s %n",
entryPoints.contains (stringLocation.offset) ? "*" : " ", address,
address + stringLocation.length, stringLocation));
}
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private void getStrings ()
// ---------------------------------------------------------------------------------//
{
entryPoints = new ArrayList<> ();
stringLocations = new ArrayList<> ();
int start = 0;
for (int ptr = 0; ptr < buffer.length; ptr++)
{
if ((buffer[ptr] & 0x80) != 0) // hi bit set
continue;
if (buffer[ptr] == 0x0D) // CR
continue;
if (ptr - start > 3)
stringLocations.add (new StringLocation (start, ptr - 1));
start = ptr + 1;
}
if (buffer.length - start > 3)
stringLocations.add (new StringLocation (start, buffer.length - 1));
int max = buffer.length - 2;
for (StringLocation stringLocation : stringLocations)
for (int ptr = 0; ptr < max; ptr++)
if (stringLocation.matches (buffer, ptr))
{
entryPoints.add (stringLocation.offset);
break;
}
}
// ---------------------------------------------------------------------------------//
private String getArrow (AssemblerStatement cmd)
// ---------------------------------------------------------------------------------//
{
String arrow = "";
if (cmd.value == 0x4C || cmd.value == 0x6C || cmd.value == 0x60 || cmd.offset != 0)
arrow = "<--";
if (cmd.value == 0x20 && isLocal (cmd.target)) // JSR
arrow = "<--";
if (cmd.isTarget)
if (arrow.isEmpty ())
arrow = "-->";
else
arrow = "<->";
return arrow;
}
// ---------------------------------------------------------------------------------//
private boolean isLocal (int target)
// ---------------------------------------------------------------------------------//
{
return target >= loadAddress
&& target < loadAddress + buffer.length + extraBuffer.length;
}
// ---------------------------------------------------------------------------------//
private void getEquates ()
// ---------------------------------------------------------------------------------//
{
equates = new HashMap<Integer, String> ();
DataInputStream inputEquates =
new DataInputStream (DiskBrowser.class.getClassLoader ()
.getResourceAsStream ("com/bytezone/diskbrowser/applefile/equates.txt"));
BufferedReader in = new BufferedReader (new InputStreamReader (inputEquates));
String line;
try
{
while ((line = in.readLine ()) != null)
{
if (!line.isEmpty () && !line.startsWith ("*"))
{
int address = Integer.parseInt (line.substring (0, 4), 16);
if (equates.containsKey (address))
System.out.printf ("Duplicate equate entry : %04X%n" + address);
else
equates.put (address, line.substring (6));
}
}
in.close ();
}
catch (IOException e)
{
e.printStackTrace ();
}
}
// ---------------------------------------------------------------------------------//
class StringLocation
// ---------------------------------------------------------------------------------//
{
int offset;
byte hi, lo;
int length;
boolean zeroTerminated;
boolean lowTerminated;
boolean hasLengthByte;
int digits;
int letters;
int punctuation;
int controlChars;
int spaces;
public StringLocation (int first, int last)
{
offset = first;
length = last - offset + 1;
int end = last + 1;
zeroTerminated = end < buffer.length && buffer[end] == 0;
lowTerminated = end < buffer.length && buffer[end] >= 32 && buffer[end] < 127;
if (first > 0 && (buffer[first] & 0xFF) == length + 1)
{
hasLengthByte = true;
--offset;
++length;
}
hi = (byte) ((offset + loadAddress) >>> 8);
lo = (byte) ((offset + loadAddress) & 0x00FF);
for (int i = offset; i < offset + length; i++)
{
int val = buffer[i] & 0x7F;
if (val < 32 || val == 127)
++controlChars;
else if (val == 32)
++spaces;
else if (val >= 48 && val <= 57)
++digits;
else if (val >= 65 && val <= 90)
++letters;
else if (val >= 97 && val <= 122)
++letters;
else
++punctuation;
}
}
boolean matches (byte[] buffer, int ptr)
{
return lo == buffer[ptr] && hi == buffer[ptr + 1];
}
boolean likelyString ()
{
return spaces > 0 || letters > punctuation;
}
public String address ()
{
return String.format ("%04X %02X %02X", offset, hi, lo);
}
public String toStatisticsString ()
{
return String.format ("%2d, %2d, %2d, %2d, %2d", digits, letters, punctuation,
controlChars, spaces);
}
@Override
public String toString ()
{
StringBuilder text = new StringBuilder ();
if (hasLengthByte)
text.append ("<length>");
for (int i = offset; i < offset + length; i++)
{
int val = buffer[i] & 0x7F;
if (val == 4)
text.append ("<ctrl-D>");
else if (val == 10)
text.append ("<LF>");
else if (val == 13)
text.append ("<CR>");
else
text.append ((char) val);
}
if (lowTerminated)
text.append ((char) buffer[offset + length]);
return text.toString ();
}
}
}

View File

@ -6,7 +6,9 @@ import java.util.Comparator;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class AssemblerStatement
// -----------------------------------------------------------------------------------//
{
public byte value;
public String mnemonic;
@ -21,7 +23,9 @@ public class AssemblerStatement
public boolean isTarget;
public byte operand1, operand2;
// ---------------------------------------------------------------------------------//
public static void print ()
// ---------------------------------------------------------------------------------//
{
AssemblerStatement[] statements = new AssemblerStatement[256];
System.out.println ();
@ -67,7 +71,9 @@ public class AssemblerStatement
}
}
// ---------------------------------------------------------------------------------//
public AssemblerStatement (byte opcode)
// ---------------------------------------------------------------------------------//
{
this.value = opcode;
this.opcode = opcode & 0xFF;
@ -76,7 +82,9 @@ public class AssemblerStatement
this.operand = "";
}
// ---------------------------------------------------------------------------------//
String getChar (byte val)
// ---------------------------------------------------------------------------------//
{
int c = val & 0xFF;
if (c > 127)
@ -92,7 +100,9 @@ public class AssemblerStatement
return (char) c + "";
}
// ---------------------------------------------------------------------------------//
public void addData ()
// ---------------------------------------------------------------------------------//
{
switch (opcode)
{
@ -142,7 +152,9 @@ public class AssemblerStatement
}
}
// ---------------------------------------------------------------------------------//
public void addData (byte b)
// ---------------------------------------------------------------------------------//
{
operand1 = b;
String address = "$" + HexFormatter.format2 (b);
@ -277,7 +289,9 @@ public class AssemblerStatement
}
}
// ---------------------------------------------------------------------------------//
public void addData (byte b1, byte b2)
// ---------------------------------------------------------------------------------//
{
operand1 = b1;
operand2 = b2;
@ -365,8 +379,10 @@ public class AssemblerStatement
}
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
if (offset == 0)
return String.format ("%06X %d %3s %-10s %02X", address, size, mnemonic, operand,

View File

@ -0,0 +1,56 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
import java.util.List;
import com.bytezone.diskbrowser.gui.BasicPreferences;
// -----------------------------------------------------------------------------------//
public abstract class BasicFormatter implements ApplesoftConstants
// -----------------------------------------------------------------------------------//
{
static final String NEWLINE = "\n";
ApplesoftBasicProgram program;
BasicPreferences basicPreferences;
byte[] buffer;
List<SourceLine> sourceLines;
// ---------------------------------------------------------------------------------//
public BasicFormatter (ApplesoftBasicProgram program, BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
this.program = program;
this.basicPreferences = basicPreferences;
this.buffer = program.getBuffer ();
this.sourceLines = program.getSourceLines ();
}
// ---------------------------------------------------------------------------------//
public abstract void append (StringBuilder fullText);
// ---------------------------------------------------------------------------------//
// ---------------------------------------------------------------------------------//
int getLoadAddress ()
// ---------------------------------------------------------------------------------//
{
return (buffer.length > 3) ? getShort (buffer, 0) - getFirstLineLength () : 0;
}
// ---------------------------------------------------------------------------------//
private int getFirstLineLength ()
// ---------------------------------------------------------------------------------//
{
int linkField = getShort (buffer, 0);
if (linkField == 0)
return 2;
int ptr = 4; // skip link field and line number
while (ptr < buffer.length && buffer[ptr++] != 0)
;
return ptr;
}
}

View File

@ -2,38 +2,23 @@ package com.bytezone.diskbrowser.applefile;
import com.bytezone.diskbrowser.gui.BasicPreferences;
// -----------------------------------------------------------------------------------//
public abstract class BasicProgram extends AbstractFile
// -----------------------------------------------------------------------------------//
{
static final byte ASCII_QUOTE = 0x22;
static final byte ASCII_COLON = 0x3A;
static final byte ASCII_SEMI_COLON = 0x3B;
static final byte ASCII_CARET = 0x5E;
static BasicPreferences basicPreferences; // set by MenuHandler
// ---------------------------------------------------------------------------------//
public static void setBasicPreferences (BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
BasicProgram.basicPreferences = basicPreferences;
}
// ---------------------------------------------------------------------------------//
public BasicProgram (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
boolean isHighBitSet (byte value)
{
return (value & 0x80) != 0;
}
boolean isControlCharacter (byte value)
{
int val = value & 0xFF;
return val > 0 && val < 32;
}
boolean isDigit (byte value)
{
return value >= 48 && value <= 57;
}
}

View File

@ -1,9 +1,11 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.utilities.Utility;
import com.bytezone.diskbrowser.utilities.Utility;;
// -----------------------------------------------------------------------------------//
public class BasicProgramGS extends BasicProgram
@ -134,7 +136,7 @@ public class BasicProgramGS extends BasicProgram
ptr += labelLength;
int lineLength = buffer[ptr] & 0xFF;
lineNumber = Utility.intValue (buffer[ptr + 1], buffer[ptr + 2]);
lineNumber = Utility.getShort (buffer, ptr + 1);
length = labelLength + lineLength;
if (lineNumber == 0)

View File

@ -0,0 +1,251 @@
package com.bytezone.diskbrowser.applefile;
import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
public class BasicTextFile extends TextFile
// -----------------------------------------------------------------------------------//
{
private static String underline =
"------------------------------------------" + "------------------------------------\n";
private static String fullUnderline = "---------- ------- " + underline;
private int recordLength; // prodos aux
private List<TextBuffer> buffers; // only used if it is a Prodos text file
private int eof;
private boolean prodosFile;
// ---------------------------------------------------------------------------------//
public BasicTextFile (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
// ---------------------------------------------------------------------------------//
public BasicTextFile (String name, byte[] buffer, int auxType, int eof)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
this.eof = eof;
recordLength = auxType;
prodosFile = true;
}
// ---------------------------------------------------------------------------------//
public BasicTextFile (String name, List<TextBuffer> buffers, int auxType, int eof)
// ---------------------------------------------------------------------------------//
{
super (name, null);
this.buffers = buffers;
this.eof = eof;
recordLength = auxType;
prodosFile = true;
}
// ---------------------------------------------------------------------------------//
@Override
public String getHexDump ()
// ---------------------------------------------------------------------------------//
{
if (buffers == null || recordLength <= 1)
return (super.getHexDump ());
StringBuilder text = new StringBuilder ();
for (TextBuffer tb : buffers)
for (int i = 0, rec = 0; i < tb.buffer.length; i += tb.reclen, rec++)
{
text.append ("\nRecord #" + (tb.firstRecNo + rec) + "\n");
text.append (HexFormatter.format (tb.buffer, i, tb.reclen) + "\n");
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
if (textPreferences.showHeader)
{
text.append ("Name : " + name + "\n");
if (prodosFile)
{
text.append (String.format ("Record length : %,8d%n", recordLength));
text.append (String.format ("End of file : %,8d%n", eof));
}
else
text.append (String.format ("End of file : %,8d%n", buffer.length));
text.append ("\n");
}
// check whether file is spread over multiple buffers
if (buffers != null)
return treeFileText (text); // usually calls knownLength()
// check whether the record length is known
if (recordLength <= 0)
return unknownLength (text).toString ();
if (textPreferences.showTextOffsets && recordLength > 1)
{
text.append (" Offset Record# Text values\n");
text.append (fullUnderline);
}
else
{
text.append ("Text values\n");
text.append (underline);
}
return knownLength (text, 0).toString ();
}
// ---------------------------------------------------------------------------------//
private String treeFileText (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
if (recordLength == 1) // stupid length
{
for (TextBuffer textBuffer : buffers)
{
buffer = textBuffer.buffer;
text = unknownLength (text);
}
return text.toString ();
}
if (textPreferences.showTextOffsets)
{
text.append (" Offset Record# Text values\n");
text.append (fullUnderline);
}
else
{
text.append ("Text values\n");
text.append (underline);
}
for (TextBuffer textBuffer : buffers)
{
buffer = textBuffer.buffer;
text = knownLength (text, textBuffer.firstRecNo);
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private StringBuilder knownLength (StringBuilder text, int recNo)
// ---------------------------------------------------------------------------------//
{
for (int ptr = 0; ptr < buffer.length; ptr += recordLength)
{
if (buffer[ptr] == 0)
{
recNo++;
continue;
}
int len = buffer.length - ptr;
int bytes = Math.min (len, recordLength);
while (buffer[ptr + bytes - 1] == 0)
bytes--;
if ((buffer[ptr + bytes - 1] & 0x7F) == 0x0D) // ignore CR
bytes--;
String line = HexFormatter.getString (buffer, ptr, bytes);
if (textPreferences.showTextOffsets)
{
line = line.replaceAll ("\\n", "\n ");
text.append (String.format ("%,10d %,8d %s%n", recNo * recordLength, recNo, line));
}
else
text.append (String.format ("%s%n", line));
recNo++;
}
return text;
}
// ---------------------------------------------------------------------------------//
private StringBuilder unknownLength (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
int nulls = 0;
int ptr = 0;
int size = buffer.length;
int lastVal = 0;
if (textPreferences.showTextOffsets)
{
text.append (" Offset Text values\n");
text.append ("---------- " + underline);
}
else
{
text.append (" Text values\n");
text.append (underline);
}
if (buffer.length == 0)
return text;
if (buffer[0] != 0 && textPreferences.showTextOffsets)
text.append (String.format ("%,10d ", ptr));
int gcd = 0;
while (ptr < size)
{
int val = buffer[ptr++] & 0x7F; // strip hi-order bit
if (val == 0)
++nulls;
else if (val == 0x0D) // carriage return
text.append ("\n");
else
{
if (nulls > 0)
{
if (textPreferences.showTextOffsets)
text.append (String.format ("%,10d ", ptr - 1));
nulls = 0;
gcd = gcd == 0 ? ptr - 1 : gcd (gcd, ptr - 1);
}
else if (lastVal == 0x0D && textPreferences.showTextOffsets)
text.append (String.format ("%,10d ", ptr - 1));
text.append ((char) val);
}
lastVal = val;
}
if (gcd > 0)
text.append (String.format ("%nGCD: %,d", gcd));
else if (text.length () > 0 && text.charAt (text.length () - 1) == '\n')
text.deleteCharAt (text.length () - 1);
return text;
}
// ---------------------------------------------------------------------------------//
private int gcd (int a, int b)
// ---------------------------------------------------------------------------------//
{
return a == 0 ? b : gcd (b % a, a);
}
}

View File

@ -5,7 +5,9 @@ import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
public class BootSector extends AbstractSector
// -----------------------------------------------------------------------------------//
{
private static final byte[] skew = { 0x00, 0x0D, 0x0B, 0x09, 0x07, 0x05, 0x03, 0x01,
0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, 0x0F };
@ -16,20 +18,26 @@ public class BootSector extends AbstractSector
AssemblerProgram assembler2;
String name; // DOS or Prodos
// ---------------------------------------------------------------------------------//
public BootSector (Disk disk, byte[] buffer, String name, DiskAddress diskAddress)
// ---------------------------------------------------------------------------------//
{
super (disk, buffer, diskAddress);
this.name = name;
}
// ---------------------------------------------------------------------------------//
public BootSector (Disk disk, byte[] buffer, String name)
// ---------------------------------------------------------------------------------//
{
super (disk, buffer);
this.name = name;
}
// ---------------------------------------------------------------------------------//
@Override
public String createText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
@ -80,7 +88,9 @@ public class BootSector extends AbstractSector
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private boolean matches (byte[] buffer, int offset, byte[] test)
// ---------------------------------------------------------------------------------//
{
if (test.length == 0 || test.length > buffer.length - offset)
return false;

View File

@ -0,0 +1,229 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class CPMBasicFile extends BasicProgram
// -----------------------------------------------------------------------------------//
{
String[] tokens = { //
"", "END", "FOR", "NEXT", "DATA", "INPUT", "DIM", "READ", // 0x80
"LET", "GOTO", "RUN", "IF", "RESTORE", "GOSUB", "RETURN", "REM", // 0x88
"STOP", "PRINT", "CLEAR", "LIST", "NEW", "ON", "DEF", "POKE", // 0x90
"", "", "", "LPRINT", "LLIST", "WIDTH", "ELSE", "", // 0x98
"", "SWAP", "ERASE", "", "ERROR", "RESUME", "DELETE", "", // 0xA0
"RENUM", "DEFSTR", "DEFINT", "", "DEFDBL", "LINE", "", "WHILE", // 0xA8
"WEND", "CALL", "WRITE", "COMMON", "CHAIN", // 0xB0
"OPTION", "RANDOMIZE", "SYSTEM", // 0xB5
"OPEN", "FIELD", "GET", "PUT", "CLOSE", "LOAD", "MERGE", "", // 0xB8
"NAME", "KILL", "LSET", "RSET", "SAVE", "RESET", "TEXT", "HOME", // 0xC0
"VTAB", "HTAB", "INVERSE", "NORMAL", "", "", "", "", // 0xC8
"", "", "", "", "", "WAIT", "", "", // 0xD0
"", "", "", "", "", "TO", "THEN", "TAB(", // 0xD8
"STEP", "USR", "FN", "SPC(", "", "ERL", "ERR", "STRING$", // 0xE0
"USING", "INSTR", "'", "VARPTR", "", "", "INKEY$", ">", // 0xE8
"=", "<", "+", "-", "*", "/", "", "AND", // 0xF0
"OR", "", "", "", "MOD", "/", "", "", // 0xF8
};
String[] functions = { //
"", "LEFT$", "RIGHT$", "MID$", "SGN", "INT", "ABS", "SQR", // 0x80
"RND", "SIN", "LOG", "EXP", "COS", "TAN", "ATN", "FRE", // 0x88
"POS", "LEN", "STR$", "VAL", "ASC", "CHR$", "PEEK", "SPACE$", // 0x90
"OCT$", "HEX$", "LPOS", "CINT", "CSNG", "CDBL", "FIX", "", // 0x98
"", "", "", "", "", "", "", "", // 0xA0
"", "", "CVI", "CVS", "CVD", "", "EOF", "LOC", // 0xA8
"", "MKI$", "MKS$", "MKD$", // 0xB0
};
// ---------------------------------------------------------------------------------//
public CPMBasicFile (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
if (basicPreferences.showHeader)
text.append ("Name : " + name + "\n\n");
int ptr = 5;
while (buffer[ptr] != 0)
ptr++;
if (showDebugText)
return debugText ();
ptr = 1;
while (ptr < buffer.length)
{
int nextAddress = getShort (buffer, ptr);
if (nextAddress == 0)
break;
int lineNumber = getShort (buffer, ptr + 2);
text.append (String.format (" %d ", lineNumber));
ptr += 4;
while (true)
{
int val = buffer[ptr++] & 0xFF;
if (val == 0)
break;
if ((val & 0x80) != 0)
{
if (val == 0xFF)
{
val = buffer[ptr++] & 0xFF;
String token = functions[val & 0x7F];
if (token.length () == 0)
token = String.format ("<FF %02X>", val);
text.append (token);
}
else
{
String token = tokens[val & 0x7F];
if (token.length () == 0)
token = String.format ("<%02X>", val);
text.append (token);
}
continue;
}
if (val >= 0x20 && val <= 0x7E) // printable
{
// check for stupid apostrophe comment
if (val == 0x3A && ptr + 1 < buffer.length && buffer[ptr] == (byte) 0x8F
&& buffer[ptr + 1] == (byte) 0xEA)
{
text.append ("'");
ptr += 2;
}
else if (val == 0x3A && ptr < buffer.length && buffer[ptr] == (byte) 0x9E)
{
// ignore colon before ELSE
}
else
text.append (String.format ("%s", (char) val));
continue;
}
if (val >= 0x11 && val <= 0x1A) // inline numbers
{
text.append (val - 0x11);
continue;
}
switch (val)
{
case 0x07:
text.append ("<BELL>");
break;
case 0x09:
text.append (" ");
break;
case 0x0A:
text.append ("\n ");
break;
case 0x0C:
text.append ("&H" + String.format ("%X", Utility.getShort (buffer, ptr)));
ptr += 2;
break;
case 0x0E: // same as 0x1C ??
text.append (Utility.getShort (buffer, ptr));
ptr += 2;
break;
case 0x0F:
text.append (buffer[ptr++] & 0xFF);
break;
case 0x1C: // same as 0x0E ??
text.append (Utility.getShort (buffer, ptr));
ptr += 2;
break;
case 0x1D:
String d4 = Utility.floatValueMS4 (buffer, ptr) + "";
if (d4.endsWith (".0"))
d4 = d4.substring (0, d4.length () - 2);
text.append (d4 + "!");
ptr += 4;
break;
case 0x1F:
String d8 = Utility.floatValueMS8 (buffer, ptr) + "";
if (d8.endsWith (".0"))
d8 = d8.substring (0, d8.length () - 2);
text.append (d8 + "#");
ptr += 8;
break;
default:
text.append (String.format ("<%02X>", val));
}
}
text.append ("\n");
}
return Utility.rtrim (text);
}
// ---------------------------------------------------------------------------------//
private String debugText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
int ptr = 1;
int lastPtr;
while (ptr < buffer.length)
{
int nextAddress = getShort (buffer, ptr);
if (nextAddress == 0)
break;
int lineNumber = getShort (buffer, ptr + 2);
lastPtr = ptr;
ptr += 4;
int val;
while ((val = buffer[ptr++]) != 0)
{
ptr += switch (val)
{
case 0x0C, 0x0E, 0x1C -> 2; // 2 byte numeric
case 0x1D -> 4; // 4 byte single precision
case 0x1F -> 8; // 8 byte double precision
case 0x0F, 0xFF -> 1; // 1 byte numeric, function table entry
default -> 0;
};
}
text.append (String.format (" %d %s%n", lineNumber,
HexFormatter.getHexString (buffer, lastPtr + 4, ptr - lastPtr - 4)));
}
return Utility.rtrim (text);
}
}

View File

@ -1,26 +1,35 @@
package com.bytezone.diskbrowser.applefile;
public class CPMTextFile extends AbstractFile
// -----------------------------------------------------------------------------------//
public class CPMTextFile extends TextFile
// -----------------------------------------------------------------------------------//
{
// ---------------------------------------------------------------------------------//
public CPMTextFile (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append ("Name : " + name + "\n\n");
if (textPreferences.showHeader)
text.append ("Name : " + name + "\n\n");
int ptr = 0;
while (ptr < buffer.length && buffer[ptr] != (byte) 0x1A)
{
String line = getLine (ptr);
text.append (line + "\n");
ptr += line.length () + 2;
ptr += line.length () + 1;
if (ptr < buffer.length && buffer[ptr - 1] == 0x0D && buffer[ptr] == 0x0A)
++ptr;
while (ptr < buffer.length && buffer[ptr] == 0)
++ptr;
}
@ -31,12 +40,14 @@ public class CPMTextFile extends AbstractFile
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private String getLine (int ptr)
// ---------------------------------------------------------------------------------//
{
StringBuilder line = new StringBuilder ();
int max = buffer.length - 1;
while (ptr < max && buffer[ptr] != 0x0D && buffer[ptr + 1] != 0x0A)
// int max = buffer.length - 1;
while (ptr < buffer.length && buffer[ptr] != 0x0D && buffer[ptr] != 0x0A)
line.append ((char) (buffer[ptr++] & 0x7F));
return line.toString ();

View File

@ -0,0 +1,103 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
import static com.bytezone.diskbrowser.utilities.Utility.isDigit;
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
import static com.bytezone.diskbrowser.utilities.Utility.isLetter;
import com.bytezone.diskbrowser.gui.BasicPreferences;
import com.bytezone.diskbrowser.utilities.HexFormatter;
// -----------------------------------------------------------------------------------//
public class DebugBasicFormatter extends BasicFormatter
// -----------------------------------------------------------------------------------//
{
int endPtr;
// ---------------------------------------------------------------------------------//
public DebugBasicFormatter (ApplesoftBasicProgram program,
BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
super (program, basicPreferences);
endPtr = program.getEndPtr ();
}
// ---------------------------------------------------------------------------------//
@Override
public void append (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
int loadAddress = getLoadAddress ();
for (SourceLine sourceLine : sourceLines)
{
text.append (String.format ("%5d %s%n", sourceLine.lineNumber,
HexFormatter.formatNoHeader (buffer, sourceLine.linePtr, 4,
loadAddress + sourceLine.linePtr)));
for (SubLine subline : sourceLine.sublines)
{
String token = getDisplayToken (buffer[subline.startPtr]);
String formattedHex = HexFormatter.formatNoHeader (buffer, subline.startPtr,
subline.length, loadAddress + subline.startPtr);
for (String bytes : formattedHex.split (NEWLINE))
{
text.append (String.format (" %-8s %s%n", token, bytes));
token = "";
}
}
text.append (NEWLINE);
}
// check for assembler routines after the basic code
if (endPtr < buffer.length)
{
int length = buffer.length - endPtr;
int ptr = endPtr;
if (length >= 2)
{
text.append (" ");
text.append (HexFormatter.formatNoHeader (buffer, endPtr, 2, loadAddress + ptr));
text.append ("\n\n");
ptr += 2;
length -= 2;
}
if (length > 0)
{
// show the extra bytes as a hex dump
String formattedHex = HexFormatter.formatNoHeader (buffer, ptr,
buffer.length - ptr, loadAddress + ptr);
for (String bytes : formattedHex.split (NEWLINE))
text.append (String.format (" %s%n", bytes));
}
if (length > 1)
{
// show the extra bytes as a disassembly
byte[] extraBuffer = new byte[length];
System.arraycopy (buffer, ptr, extraBuffer, 0, extraBuffer.length);
AssemblerProgram assemblerProgram =
new AssemblerProgram ("extra", extraBuffer, loadAddress + ptr);
text.append ("\n");
text.append (assemblerProgram.getText ());
}
}
}
// ---------------------------------------------------------------------------------//
private String getDisplayToken (byte b)
// ---------------------------------------------------------------------------------//
{
if (isHighBitSet (b))
return ApplesoftConstants.tokens[b & 0x7F];
if (isDigit (b) || isLetter (b) || b == ASCII_COLON || b == 0)
return "";
return "*******";
}
}

View File

@ -18,6 +18,7 @@ public class DefaultAppleFile extends AbstractFile
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
this.text = "Name : " + name + "\n\n" + text;
}
@ -35,8 +36,10 @@ public class DefaultAppleFile extends AbstractFile
{
if (text != null)
return text;
if (buffer == null)
return "Invalid file : " + name;
return super.getText ();
}
}

View File

@ -1,4 +1,4 @@
package com.bytezone.diskbrowser.dosmaster;
package com.bytezone.diskbrowser.applefile;
import java.io.File;
import java.io.IOException;
@ -11,20 +11,57 @@ import java.util.List;
import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.prodos.ProdosDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class DosMasterDisk
public class DosMasterFile extends AbstractFile
// -----------------------------------------------------------------------------------//
{
private static final String base = "/Users/denismolony/Dropbox/Examples/Testing/";
// ---------------------------------------------------------------------------------//
public DosMasterFile (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
// ---------------------------------------------------------------------------------//
public static boolean isDos33 (ProdosDisk parentDisk, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
// System.out.println (HexFormatter.format (buffer, 0x38, 0x30));
System.out.printf ("%nHighest Block: %04X (%<,d)%n",
parentDisk.getDisk ().getTotalBlocks () - 1);
System.out.print ("\nSlots/Drives: ");
for (int i = 0; i < 8; i++)
{
System.out.printf ("%02X ", buffer[0x38 + i]);
if (i % 2 == 1)
System.out.print (": ");
}
System.out.print ("\nFirst Block : ");
for (int i = 0; i < 16; i += 2)
{
System.out.printf ("%04X ", Utility.getShort (buffer, 0x40 + i));
if (i % 4 == 2)
System.out.print (": ");
}
System.out.print ("\nLast Block : ");
for (int i = 0; i < 8; i += 2)
System.out.printf ("%04X : ", Utility.getShort (buffer, 0x50 + i));
System.out.print ("\nImage Size : ");
for (int i = 0; i < 8; i += 2)
System.out.printf ("%04X : ", Utility.getShort (buffer, 0x58 + i));
System.out.print ("\nAddress : ");
for (int i = 0; i < 8; i += 2)
System.out.printf ("%04X : ", Utility.getShort (buffer, 0x60 + i));
System.out.println ();
System.out.println ();
System.out.println ("# S D B Lo B Hi Size Vols Secs");
@ -38,25 +75,27 @@ public class DosMasterDisk
int slot = (slotDrive & 0x70) >>> 4;
int drive = ((slotDrive & 0x80) >>> 7) + 1;
int firstBlock = Utility.unsignedShort (buffer, 0x40 + i * 2); // of first volume
int firstBlock = Utility.getShort (buffer, 0x40 + i * 2); // of first volume
int skip = i / 2 * 2; // 0, 0, 2, 2, 4, 4, 6, 6 - same for both drives
int lastBlock = Utility.unsignedShort (buffer, 0x50 + skip); // of last volume
int volSize = Utility.unsignedShort (buffer, 0x58 + skip);
int lastBlock = Utility.getShort (buffer, 0x50 + skip); // of last volume
int volSize = Utility.getShort (buffer, 0x58 + skip);
int originalFirstBlock = firstBlock;
if (firstBlock > lastBlock) // WTF?
firstBlock -= 16 * 4096;
firstBlock -= 0x10000;
int vols = (lastBlock - firstBlock) / volSize - 1;
int sectors = volSize * 2;
System.out.printf ("%d %02X %d %d %04X %04X %04X %3d %3d%n", i,
buffer[0x38 + i], slot, drive, firstBlock, lastBlock, volSize, vols, sectors);
System.out.printf ("%d %02X %d %d %04X %04X %04X %3d %4d%n", i,
buffer[0x38 + i], slot, drive, originalFirstBlock, lastBlock, volSize, vols,
sectors);
if (vols > 0 && true)
{
int volNo = 10;
int volNo = 1;
int firstDiskBlock = firstBlock + volNo * volSize;
int lastDiskBlock = firstDiskBlock + volSize;
@ -66,15 +105,14 @@ public class DosMasterDisk
daList.add (disk.getDiskAddress (block));
byte[] diskBuffer = disk.readBlocks (daList);
System.out.println (HexFormatter.format (diskBuffer));
System.out.printf ("Buffer: %,d%n", diskBuffer.length);
System.out.printf ("Blocks: %,d x 2 = %,d%n", daList.size (), daList.size () * 2);
if (false)
createDisk (String.format ("%sVol%03d.dsk", base, i), diskBuffer);
createDisk (String.format ("%sVol%03d.dsk", base, volNo), diskBuffer);
}
}
// oldCode (parentDisk, buffer);
return false;
}
@ -101,7 +139,7 @@ public class DosMasterDisk
}
// ---------------------------------------------------------------------------------//
private void oldCode (ProdosDisk parentDisk, byte[] buffer)
private static void oldCode (ProdosDisk parentDisk, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
@ -138,9 +176,9 @@ public class DosMasterDisk
text.append (String.format ("Slot %d, Drive %d has", s / 16, dr + 1));
int ptr = v0 + 2 * d0 + 2 * dr;
int st = Utility.unsignedShort (buffer, ptr); // start block of first volume
int sz = Utility.unsignedShort (buffer, vsiz + d0); // blocks per volume
int v = Utility.unsignedShort (buffer, size + d0); // end block of last volume
int st = Utility.getShort (buffer, ptr); // start block of first volume
int v = Utility.getShort (buffer, size + d0); // end block of last volume
int sz = Utility.getShort (buffer, vsiz + d0); // blocks per volume
if (st > v)
st -= 16 * 4096;
@ -156,8 +194,8 @@ public class DosMasterDisk
if (num > 0 && false)
{
// for (int i = 1; i <= num; i++)
int i = 15;
for (int i = 1; i <= num; i++)
// int i = 15;
{
int firstBlock = st + i * sz;
int lastBlock = firstBlock + sz;

View File

@ -1,24 +1,33 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class ErrorMessageFile extends AbstractFile
// -----------------------------------------------------------------------------------//
{
String text;
// ---------------------------------------------------------------------------------//
public ErrorMessageFile (String name, byte[] buffer, Exception e)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
StringBuilder text = new StringBuilder ();
text.append ("Oops! : " + e.toString () + "\n\n");
for (StackTraceElement ste : e.getStackTrace ())
text.append (ste + "\n");
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
this.text = text.toString ();
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
return text;
}

View File

@ -31,6 +31,8 @@ public class ExoBuffer
private int tableLo[] = new int[100];
private int tableHi[] = new int[100];
private static boolean debug = false;
// ---------------------------------------------------------------------------------//
public ExoBuffer (byte[] inBuffer)
// ---------------------------------------------------------------------------------//
@ -43,12 +45,21 @@ public class ExoBuffer
case 0x6000:
outBuffer = new byte[0x2000]; // HGR
break;
case 0x8000:
outBuffer = new byte[0x4000]; // DHGR
break;
case 0xA000:
outBuffer = new byte[0x8000]; // SHR
break;
case 0x5FF8: // this is not working correctly - see CLODE019
return;
default:
System.out.printf ("Invalid buffer size: %04X%n",
Utility.getShortBigEndian (inBuffer, 0));
}
decrunch ();
@ -67,14 +78,22 @@ public class ExoBuffer
// ---------------------------------------------------------------------------------//
{
if (auxType != 0x1FF8 && auxType != 0x3FF8)
{
if (debug)
System.out.println ("wrong auxType");
return false;
}
int address = Utility.unsignedShort (buffer, buffer.length - 2);
int address = Utility.getShort (buffer, buffer.length - 2);
if (address != 0x6000 && address != 0x8000 && address != 0xA000)
{
if (debug)
System.out.printf ("wrong address: %04X%n", address);
return false;
}
return false; // not dealing with fuckwits
return true;
}
// ---------------------------------------------------------------------------------//

View File

@ -5,6 +5,7 @@ package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class ExoBufferC
// -----------------------------------------------------------------------------------//
{
private static int PBIT_BITS_ORDER_BE = 0;
private static int PBIT_BITS_COPY_GT_7 = 1;

View File

@ -1,11 +1,15 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class FaddenHiResImage extends OriginalHiResImage
// -----------------------------------------------------------------------------------//
{
// https://github.com/fadden/fhpack/blob/master/fhpack.cpp
// ---------------------------------------------------------------------------------//
public FaddenHiResImage (String name, byte[] buffer, int fileType, int auxType,
int endOfFile)
// ---------------------------------------------------------------------------------//
{
super (name, buffer, fileType, auxType, endOfFile);

View File

@ -6,7 +6,9 @@ import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class FileTypeDescriptorTable extends AbstractFile
// -----------------------------------------------------------------------------------//
{
int versionMajor;
int versionMinor;
@ -18,17 +20,19 @@ public class FileTypeDescriptorTable extends AbstractFile
private final List<IndexRecord> indexRecords = new ArrayList<> ();
// ---------------------------------------------------------------------------------//
public FileTypeDescriptorTable (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
versionMajor = buffer[0] & 0xFF;
versionMinor = buffer[1] & 0xFF;
flags = Utility.unsignedShort (buffer, 2);
numEntries = Utility.unsignedShort (buffer, 4);
spareWord = Utility.unsignedShort (buffer, 6);
indexRecordSize = Utility.unsignedShort (buffer, 8);
offsetToIdx = Utility.unsignedShort (buffer, 10);
flags = Utility.getShort (buffer, 2);
numEntries = Utility.getShort (buffer, 4);
spareWord = Utility.getShort (buffer, 6);
indexRecordSize = Utility.getShort (buffer, 8);
offsetToIdx = Utility.getShort (buffer, 10);
int ptr = offsetToIdx;
for (int i = 0; i < numEntries; i++)
@ -38,8 +42,10 @@ public class FileTypeDescriptorTable extends AbstractFile
}
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ("Name : " + name + "\n\n");
text.append ("File Type Descriptor Table\n\n");
@ -62,7 +68,9 @@ public class FileTypeDescriptorTable extends AbstractFile
return text.toString ();
}
// ---------------------------------------------------------------------------------//
class IndexRecord
// ---------------------------------------------------------------------------------//
{
int fileType;
int auxType;
@ -72,10 +80,10 @@ public class FileTypeDescriptorTable extends AbstractFile
public IndexRecord (byte[] buffer, int offset)
{
fileType = Utility.unsignedShort (buffer, offset);
auxType = Utility.unsignedLong (buffer, offset + 2);
flags = Utility.unsignedShort (buffer, offset + 6);
this.offset = Utility.unsignedShort (buffer, offset + 8);
fileType = Utility.getShort (buffer, offset);
auxType = Utility.getLong (buffer, offset + 2);
flags = Utility.getShort (buffer, offset + 6);
this.offset = Utility.getShort (buffer, offset + 8);
string = HexFormatter.getPascalString (buffer, this.offset);
}

View File

@ -62,6 +62,8 @@ public class FinderData extends AbstractFile
text.append (String.format ("%02X%n", buffer[ptr++]));
}
}
else
text.append (String.format ("Unknown finder data version: %d%n", version));
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);

View File

@ -32,9 +32,11 @@ public class FontFile extends CharacterList
{
if (buffer.length % 8 != 0)
return false;
for (int i = 0; i < 8; i++)
if (buffer[i] != 0 && buffer[i] != 0x7F)
return false;
return true;
}

View File

@ -0,0 +1,26 @@
package com.bytezone.diskbrowser.applefile;
import com.bytezone.diskbrowser.gui.BasicPreferences;
// -----------------------------------------------------------------------------------//
public class HeaderFormatter extends BasicFormatter
// -----------------------------------------------------------------------------------//
{
// ---------------------------------------------------------------------------------//
public HeaderFormatter (ApplesoftBasicProgram program,
BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
super (program, basicPreferences);
}
// ---------------------------------------------------------------------------------//
@Override
public void append (StringBuilder fullText)
// ---------------------------------------------------------------------------------//
{
fullText.append ("Name : " + program.name + "\n");
fullText.append (String.format ("Length : $%04X (%<,d)%n", buffer.length));
fullText.append (String.format ("Load at : $%04X (%<,d)%n%n", getLoadAddress ()));
}
}

View File

@ -18,98 +18,101 @@ public abstract class HiResImage extends AbstractFile
{
static final String[] auxTypes =
{ "Paintworks Packed SHR Image", "Packed Super Hi-Res Image",
"Super Hi-Res Image (Apple Preferred Format)", "Packed QuickDraw II PICT File",
"Packed Super Hi-Res 3200 color image" };
"Super Hi-Res Image (Apple Preferred Format)", "Packed QuickDraw II PICT File",
"Packed Super Hi-Res 3200 color image", "DreamGraphix" };
static final int COLOR_TABLE_SIZE = 32;
static final int COLOR_TABLE_OFFSET_AUX_0 = 32_256;
static final int COLOR_TABLE_OFFSET_AUX_2 = 32_000;
public static final int FADDEN_AUX = 0x8066;
private byte[] fourBuf = new byte[4];
private ColorTable defaultColorTable320 = new ColorTable (0, 0x00);
private ColorTable defaultColorTable640 = new ColorTable (0, 0x80);
// ---- ---- ------ -------------------------------------- ------------------------
// File Type Aux Name Description
// ---- ---- ------ -------------------------------------- ------------------------
// $06 BIN isGif() OriginalHiResImage
// $06 BIN isPng() OriginalHiResImage
// $06 BIN .BMP isBmp() OriginalHiResImage
// $06 BIN .AUX DoubleHiResImage
// $06 BIN .PAC DoubleHiResImage
// $06 BIN .A2FC DoubleHiResImage
// $06 BIN $2000 eof $4000 DoubleHiResImage
// $06 BIN $1FFF eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
// $06 BIN $2000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
// $06 BIN $4000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage (?)
// $06 BIN $4000 eof $4000 DoubleHiResImage (?)
// $06 BIN .3200 SHRPictureFile2
// $06 BIN .3201 SHRPictureFile2 packed
// ---- ---- ------ -------------------------------------- ------------------------
// $08 FOT <$4000 Apple II Graphics File OriginalHiResImage
// $08 FOT $4000 Packed Hi-Res file ???
// $08 FOT $4001 Packed Double Hi-Res file ???
// $08 FOT $8066 Fadden Hi-res FaddenHiResImage
// ---- ---- ------ -------------------------------------- ------------------------
// * $C0 PNT $0000 Paintworks Packed Super Hi-Res SHRPictureFile2
// * $C0 PNT $0001 Packed IIGS Super Hi-Res Image SHRPictureFile2
// * $C0 PNT $0002 IIGS Super Hi-Res Picture File (APF) SHRPictureFile1
// $C0 PNT $0003 Packed IIGS QuickDraw II PICT File SHRPictureFile2 *
// * $C0 PNT $0004 Packed Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3201
// $C0 PNT $1000
// $C0 PNT $8000 Drawplus? Paintworks Gold?
// $C0 PNT $8001 GTv background picture
// $C0 PNT $8005 DreamGraphix document
// $C0 PNT $8006 GIF
// ---- ---- ------ -------------------------------------- ------------------------
// * $C1 PIC $0000 IIGS Super Hi-Res Image SHRPictureFile2
// $C1 PIC $0001 IIGS QuickDraw II PICT File SHRPictureFile2 *
// * $C1 PIC $0002 Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3200
// $C1 PIC $2000 = $C1/0000
// $C1 PIC $4100 = $C1/0000
// $C1 PIC $4950 = $C1/0000
// $C1 PIC $8001 Allison raw image
// $C1 PIC $8002 Thunderscan
// $C1 PIC $8003 DreamGraphix
// ---- ---- ------ -------------------------------------- ------------------------
// $C2 ANI Paintworks animation
// $C3 PAL Paintworks palette
// ---- ---- ------ -------------------------------------- ------------------------
// ---- ---- ------ -------------------------------------- ------------------------
// File Type Aux Name Description
// ---- ---- ------ -------------------------------------- ------------------------
// $06 BIN isGif() OriginalHiResImage
// $06 BIN isPng() OriginalHiResImage
// $06 BIN .BMP isBmp() OriginalHiResImage
// $06 BIN .AUX DoubleHiResImage
// $06 BIN .PAC DoubleHiResImage
// $06 BIN .A2FC DoubleHiResImage
// $06 BIN $2000 eof $4000 DoubleHiResImage
// $06 BIN $1FFF eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
// $06 BIN $2000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
// $06 BIN $4000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage (?)
// $06 BIN $4000 eof $4000 DoubleHiResImage (?)
// $06 BIN .3200 SHRPictureFile2
// $06 BIN .3201 SHRPictureFile2 packed
// ---- ---- ------ -------------------------------------- ------------------------
// $08 FOT <$4000 Apple II Graphics File OriginalHiResImage
// $08 FOT $4000 Packed Hi-Res file ???
// $08 FOT $4001 Packed Double Hi-Res file ???
// $08 FOT $8066 Fadden Hi-res FaddenHiResImage
// ---- ---- ------ -------------------------------------- ------------------------
// * $C0 PNT $0000 Paintworks Packed Super Hi-Res SHRPictureFile2
// * $C0 PNT $0001 Packed IIGS Super Hi-Res Image SHRPictureFile2
// * $C0 PNT $0002 IIGS Super Hi-Res Picture File (APF) SHRPictureFile1
// $C0 PNT $0003 Packed IIGS QuickDraw II PICT File SHRPictureFile2 *
// * $C0 PNT $0004 Packed Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3201
// $C0 PNT $1000
// $C0 PNT $8000 Drawplus? Paintworks Gold?
// $C0 PNT $8001 GTv background picture
// $C0 PNT $8005 DreamGraphix document SHRPictureFile2
// $C0 PNT $8006 GIF
// ---- ---- ------ -------------------------------------- ------------------------
// * $C1 PIC $0000 IIGS Super Hi-Res Image SHRPictureFile2
// $C1 PIC $0001 IIGS QuickDraw II PICT File SHRPictureFile2 *
// * $C1 PIC $0002 Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3200
// $C1 PIC $2000 = $C1/0000
// $C1 PIC $4100 = $C1/0000
// $C1 PIC $4950 = $C1/0000
// $C1 PIC $8001 Allison raw image
// $C1 PIC $8002 Thunderscan
// $C1 PIC $8003 DreamGraphix
// ---- ---- ------ -------------------------------------- ------------------------
// $C2 ANI Paintworks animation
// $C3 PAL Paintworks palette
// ---- ---- ------ -------------------------------------- ------------------------
// packed unpacked
// $06.3200 1
// $06.3201 .
// $08 0000 .
// $08 4000 .
// $08 4001 .
// $08 8066 3
// $C0 0000 1
// $C0 0001 $C1 0000 2 1
// $C0 0002 1,5
// $C0 0003 $C1 0001 . .
// $C0 0004 $C1 0002 . 1
// $C0 1000 .
// $C0 8000 .
// $C0 8001 .
// $C0 8005 .
// $C0 8006 .
// $C1 0042 4
// $C1 0043 4
// $C1 2000 .
// $C1 4100 1
// $C1 4950 .
// $C1 8001 .
// $C1 8002 .
// $C1 8003 .
// packed unpacked
// $06.3200 1
// $06.3201 .
// $08 0000 .
// $08 4000 .
// $08 4001 .
// $08 8066 3
// $C0 0000 1
// $C0 0001 $C1 0000 2 1
// $C0 0002 1,5
// $C0 0003 $C1 0001 . .
// $C0 0004 $C1 0002 . 1
// $C0 1000 .
// $C0 8000 .
// $C0 8001 .
// $C0 8005 6
// $C0 8006 .
// $C1 0042 4
// $C1 0043 4
// $C1 2000 .
// $C1 4100 1
// $C1 4950 .
// $C1 8001 .
// $C1 8002 .
// $C1 8003 .
// 1 Graphics & Animation.2mg
// 2 0603 Katie's Farm - Disk 2.po
// 3 CompressedSlides.do
// 4 System Addons.hdv
// 5 gfx.po
//
// 6 Dream Grafix v1.02.po
// see also - https://docs.google.com/spreadsheets/d
// /1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/edit#gid=0
// . /1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/edit#gid=0
// also - http://lukazi.blogspot.com/2017/03/double-high-resolution-graphics-dhgr.html
static PaletteFactory paletteFactory = new PaletteFactory ();
@ -297,27 +300,20 @@ public abstract class HiResImage extends AbstractFile
break;
case ProdosConstants.FILE_TYPE_PNT: // 0xC0
auxText = auxType > 4 ? "Unknown aux: " + auxType : auxTypes[auxType];
if (auxType == 0x8005)
auxText = auxTypes[5];
else
auxText = auxType > 4 ? "Unknown aux: " + auxType : auxTypes[auxType];
break;
case ProdosConstants.FILE_TYPE_PIC: // 0xC1
switch (auxType)
auxText = switch (auxType)
{
case 0:
case 0x2000:
case 0x0042:
case 0x0043:
auxText = "Super Hi-res Screen Image";
break;
case 1:
auxText = "QuickDraw PICT File";
break;
case 2:
auxText = "Super Hi-Res 3200 color image";
break;
default:
auxText = "Unknown aux: " + auxType;
}
case 0, 0x2000, 0x0042, 0x0043 -> "Super Hi-res Screen Image";
case 1 -> "QuickDraw PICT File";
case 2 -> "Super Hi-Res 3200 color image";
default -> "Unknown aux: " + auxType;
};
}
if (!auxText.isEmpty ())
@ -505,7 +501,7 @@ public abstract class HiResImage extends AbstractFile
int calculateBufferSize (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
// int ptr = 0;
// int ptr = 0;
int size = 0;
while (ptr < buffer.length)
{
@ -630,15 +626,15 @@ public abstract class HiResImage extends AbstractFile
return false;
String text = new String (buffer, 0, 2);
int size = Utility.unsignedLong (buffer, 2);
int size = Utility.getLong (buffer, 2);
if (false)
{
int empty = Utility.unsignedLong (buffer, 6);
int offset = Utility.unsignedLong (buffer, 10);
int header = Utility.unsignedLong (buffer, 14);
int width = Utility.unsignedLong (buffer, 18);
int height = Utility.unsignedLong (buffer, 22);
int empty = Utility.getLong (buffer, 6);
int offset = Utility.getLong (buffer, 10);
int header = Utility.getLong (buffer, 14);
int width = Utility.getLong (buffer, 18);
int height = Utility.getLong (buffer, 22);
System.out.println (buffer.length);
System.out.println (size);
@ -810,7 +806,7 @@ public abstract class HiResImage extends AbstractFile
public ColorEntry (byte[] data, int offset)
// -------------------------------------------------------------------------------//
{
value = Utility.unsignedShort (data, offset);
value = Utility.getShort (data, offset);
int red = ((value >> 8) & 0x0f) * 17;
int green = ((value >> 4) & 0x0f) * 17;
@ -839,8 +835,8 @@ public abstract class HiResImage extends AbstractFile
public DirEntry (byte[] data, int offset)
// -------------------------------------------------------------------------------//
{
numBytes = Utility.unsignedShort (data, offset);
mode = Utility.unsignedShort (data, offset + 2);
numBytes = Utility.getShort (data, offset);
mode = Utility.getShort (data, offset + 2);
}
// -------------------------------------------------------------------------------//

View File

@ -29,16 +29,16 @@ public class IconFile extends AbstractFile implements ProdosConstants
{
super (name, buffer);
iBlkNext = Utility.unsignedLong (buffer, 0);
iBlkID = Utility.unsignedShort (buffer, 4);
iBlkPath = Utility.unsignedLong (buffer, 6);
iBlkNext = Utility.getLong (buffer, 0);
iBlkID = Utility.getShort (buffer, 4);
iBlkPath = Utility.getLong (buffer, 6);
iBlkName = HexFormatter.getHexString (buffer, 10, 16);
int ptr = 26;
while (true)
{
int dataLen = Utility.unsignedShort (buffer, ptr);
int dataLen = Utility.getShort (buffer, ptr);
if (dataLen == 0 || (dataLen + ptr) > buffer.length)
break;
@ -131,7 +131,7 @@ public class IconFile extends AbstractFile implements ProdosConstants
public Icon (byte[] fullBuffer, int ptr)
// -------------------------------------------------------------------------------//
{
iDataLen = Utility.unsignedShort (fullBuffer, ptr);
iDataLen = Utility.getShort (fullBuffer, ptr);
buffer = new byte[iDataLen];
System.arraycopy (fullBuffer, ptr, buffer, 0, buffer.length);
@ -142,8 +142,8 @@ public class IconFile extends AbstractFile implements ProdosConstants
len = buffer[66] & 0xFF;
dataName = new String (buffer, 67, len);
iDataType = Utility.unsignedShort (buffer, 82);
iDataAux = Utility.unsignedShort (buffer, 84);
iDataType = Utility.getShort (buffer, 82);
iDataAux = Utility.getShort (buffer, 84);
if (debug)
{
@ -200,10 +200,10 @@ public class IconFile extends AbstractFile implements ProdosConstants
public Image (byte[] buffer, int ptr) throws InvalidImageException
// -------------------------------------------------------------------------------//
{
iconType = Utility.unsignedShort (buffer, ptr);
iconSize = Utility.unsignedShort (buffer, ptr + 2);
iconHeight = Utility.unsignedShort (buffer, ptr + 4);
iconWidth = Utility.unsignedShort (buffer, ptr + 6);
iconType = Utility.getShort (buffer, ptr);
iconSize = Utility.getShort (buffer, ptr + 2);
iconHeight = Utility.getShort (buffer, ptr + 4);
iconWidth = Utility.getShort (buffer, ptr + 6);
if (debug)
{

View File

@ -3,7 +3,9 @@ package com.bytezone.diskbrowser.applefile;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class IntegerBasicProgram extends BasicProgram
// -----------------------------------------------------------------------------------//
{
private static String[] tokens =
{ "?", "?", "?", " : ", "?", "?", "?", "?", "?", "?", "?", "?", "CLR", "?", "?",
@ -19,13 +21,17 @@ public class IntegerBasicProgram extends BasicProgram
"LIST ", ",", "LIST ", "POP ", "NODSP ", "NODSP ", "NOTRACE ", "DSP ", "DSP ",
"TRACE ", "PR#", "IN#", };
// ---------------------------------------------------------------------------------//
public IntegerBasicProgram (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder pgm = new StringBuilder ();
pgm.append ("Name : " + name + "\n");
@ -77,7 +83,9 @@ public class IntegerBasicProgram extends BasicProgram
return pgm.toString ();
}
// ---------------------------------------------------------------------------------//
private void appendAssembler (StringBuilder pgm, int ptr, int lineLength)
// ---------------------------------------------------------------------------------//
{
for (int i = ptr + 3; i < ptr + lineLength - 1; i++)
{
@ -93,7 +101,9 @@ public class IntegerBasicProgram extends BasicProgram
}
}
// ---------------------------------------------------------------------------------//
private boolean checkForAssembler ()
// ---------------------------------------------------------------------------------//
{
int ptr = 0;
@ -117,7 +127,9 @@ public class IntegerBasicProgram extends BasicProgram
return false;
}
// ---------------------------------------------------------------------------------//
private boolean checkForSCAssembler ()
// ---------------------------------------------------------------------------------//
{
if (buffer.length == 0)
{
@ -130,7 +142,9 @@ public class IntegerBasicProgram extends BasicProgram
return buffer[lineLength - 1] == 0;
}
// ---------------------------------------------------------------------------------//
private void appendSCAssembler (StringBuilder text, int ptr)
// ---------------------------------------------------------------------------------//
{
int lineNumber = (buffer[ptr + 2] & 0xFF) * 256 + (buffer[ptr + 1] & 0xFF);
text.append (String.format ("%4d: ", lineNumber));
@ -156,7 +170,9 @@ public class IntegerBasicProgram extends BasicProgram
}
}
// ---------------------------------------------------------------------------------//
private void appendInteger (StringBuilder text, int ptr, int lineLength)
// ---------------------------------------------------------------------------------//
{
int lineNumber = Utility.intValue (buffer[ptr + 1], buffer[ptr + 2]);
@ -208,8 +224,10 @@ public class IntegerBasicProgram extends BasicProgram
}
}
// ---------------------------------------------------------------------------------//
@Override
public String getHexDump ()
// ---------------------------------------------------------------------------------//
{
if (false)
return super.getHexDump ();
@ -247,6 +265,7 @@ public class IntegerBasicProgram extends BasicProgram
return pgm.toString ();
}
/*
* To find integer basic in memory:
* $CA $CB contain the starting address ($9464)

View File

@ -0,0 +1,53 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class MagicWindowText extends AbstractFile
// -----------------------------------------------------------------------------------//
{
// ---------------------------------------------------------------------------------//
public MagicWindowText (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
}
// this was copied from SimpleText, should probably combine them
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append ("Name : " + name + "\n");
text.append (String.format ("End of file : %,8d%n%n", buffer.length));
int ptr = 0x100;
while (ptr < buffer.length && buffer[ptr] != 0x00)
{
String line = getLine (ptr);
text.append (line + "\n");
ptr += line.length () + 1;
if (ptr < buffer.length && buffer[ptr] == 0x0A)
ptr++;
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private String getLine (int ptr)
// ---------------------------------------------------------------------------------//
{
StringBuilder line = new StringBuilder ();
// added check for 0x00 eol 17/01/17
while (ptr < buffer.length && buffer[ptr] != (byte) 0x8D && buffer[ptr] != 0x00)
{
line.append ((char) (buffer[ptr++] & 0x7F));
}
return line.toString ();
}
}

View File

@ -1,6 +1,8 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class MerlinSource extends AbstractFile
// -----------------------------------------------------------------------------------//
{
int ptr;
private static int[] tabs = { 12, 19, 35 };
@ -10,7 +12,9 @@ public class MerlinSource extends AbstractFile
private boolean prodosFile;
// Source : Prodos text file
// ---------------------------------------------------------------------------------//
public MerlinSource (String name, byte[] buffer, int recordLength, int eof)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
this.eof = eof;
@ -19,7 +23,9 @@ public class MerlinSource extends AbstractFile
}
// Source : Dos binary file
// ---------------------------------------------------------------------------------//
public MerlinSource (String name, byte[] buffer, int loadAddress)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
this.eof = 0;
@ -27,8 +33,10 @@ public class MerlinSource extends AbstractFile
this.loadAddress = loadAddress;
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
@ -53,7 +61,9 @@ public class MerlinSource extends AbstractFile
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private String getLine ()
// ---------------------------------------------------------------------------------//
{
StringBuilder line = new StringBuilder ();
boolean comment = false;
@ -85,7 +95,9 @@ public class MerlinSource extends AbstractFile
return line.toString ();
}
// ---------------------------------------------------------------------------------//
private StringBuilder tab (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
int nextTab = 0;
for (int tab : tabs)

View File

@ -70,6 +70,8 @@ public class OriginalHiResImage extends HiResImage
for (int ptr = base; ptr < max; ptr++)
{
int value = buffer[ptr] & 0x7F;
if ((buffer[ptr] & 0x80) != 0)
System.out.printf ("bit shift pixel found in %s%n", name);
for (int px = 0; px < 7; px++)
{
int val = (value >> px) & 0x01;

View File

@ -1,6 +1,8 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class Palette
// -----------------------------------------------------------------------------------//
{
/*-
* Michael Pohoreski - The Apple II Forever Anthology
@ -32,24 +34,32 @@ public class Palette
private final String name;
private final int[] colours;
// ---------------------------------------------------------------------------------//
public Palette (String name, int[] colours)
// ---------------------------------------------------------------------------------//
{
this.name = name;
this.colours = colours;
}
// ---------------------------------------------------------------------------------//
public String getName ()
// ---------------------------------------------------------------------------------//
{
return name;
}
// ---------------------------------------------------------------------------------//
public int[] getColours ()
// ---------------------------------------------------------------------------------//
{
return colours;
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
return String.format ("Palette: %s", name);
}

View File

@ -3,7 +3,9 @@ package com.bytezone.diskbrowser.applefile;
import java.util.ArrayList;
import java.util.List;
// -----------------------------------------------------------------------------------//
public class PaletteFactory
// -----------------------------------------------------------------------------------//
{
private final List<Palette> palettes = new ArrayList<> ();
private int currentPalette;
@ -13,7 +15,9 @@ public class PaletteFactory
FORWARDS, BACKWARDS
}
// ---------------------------------------------------------------------------------//
public PaletteFactory ()
// ---------------------------------------------------------------------------------//
{
palettes.add (//
new Palette ("Virtual II", new int[] { 0x000000, // 0 black
@ -168,7 +172,9 @@ public class PaletteFactory
}));
}
// ---------------------------------------------------------------------------------//
public Palette cyclePalette (CycleDirection direction)
// ---------------------------------------------------------------------------------//
{
switch (direction)
{
@ -187,28 +193,38 @@ public class PaletteFactory
return getCurrentPalette ();
}
// ---------------------------------------------------------------------------------//
public List<Palette> getPalettes ()
// ---------------------------------------------------------------------------------//
{
return palettes;
}
// ---------------------------------------------------------------------------------//
public Palette getCurrentPalette ()
// ---------------------------------------------------------------------------------//
{
return palettes.get (currentPalette);
}
// ---------------------------------------------------------------------------------//
public int getCurrentPaletteIndex ()
// ---------------------------------------------------------------------------------//
{
return currentPalette;
}
// ---------------------------------------------------------------------------------//
public void setCurrentPalette (int index)
// ---------------------------------------------------------------------------------//
{
assert index >= 0 && index < palettes.size ();
currentPalette = index;
}
// ---------------------------------------------------------------------------------//
public void setCurrentPalette (Palette palette)
// ---------------------------------------------------------------------------------//
{
int count = 0;
for (Palette p : palettes)
@ -222,12 +238,16 @@ public class PaletteFactory
}
}
// ---------------------------------------------------------------------------------//
public Palette get (int index)
// ---------------------------------------------------------------------------------//
{
return palettes.get (index);
}
// ---------------------------------------------------------------------------------//
private int rgb (int red, int green, int blue)
// ---------------------------------------------------------------------------------//
{
return red << 16 | green << 8 | blue;
}

View File

@ -22,13 +22,13 @@ public class PascalArea extends AbstractFile
{
super (name, buffer);
size = Utility.unsignedShort (buffer, 0);
volumes = Utility.unsignedShort (buffer, 2);
size = Utility.getShort (buffer, 0);
volumes = Utility.getShort (buffer, 2);
ppmName = HexFormatter.getPascalString (buffer, 4);
start = Utility.unsignedShort (buffer, 8);
length = Utility.unsignedShort (buffer, 11);
start = Utility.getShort (buffer, 8);
length = Utility.getShort (buffer, 11);
defaultUnit = buffer[13] & 0xFF;
oldDriver = Utility.unsignedShort (buffer, 14);
oldDriver = Utility.getShort (buffer, 14);
// writeProtected = buffer[12] != 0;
}

View File

@ -47,7 +47,7 @@ public class PascalCode extends AbstractFile
for (int i = 0; i < 16; i++)
{
String codeName = HexFormatter.getString (buffer, 0x40 + i * 8, 8).trim ();
int size = Utility.intValue (buffer[i * 4 + 2], buffer[i * 4 + 3]);
int size = Utility.getShort (buffer, i * 4 + 2);
if (codeName.length () == 0 && size > 0)
codeName = "<NULL" + ++nonameCounter + ">";
if (size > 0)

View File

@ -79,10 +79,8 @@ public class PascalCodeStatement implements PascalConstants
int min = ptr + padding + 7;
int max = min + (p2 - p1) * 2;
for (int i = min; i <= max; i += 2)
{
jumps.add (
new Jump (i, i - Utility.intValue (buffer[i], buffer[i + 1]), v++));
}
jumps.add (new Jump (i, i - Utility.getShort (buffer, i), v++));
break;
// UB, <block> - word aligned

View File

@ -4,6 +4,8 @@ package com.bytezone.diskbrowser.applefile;
public class PascalInfo extends AbstractFile
// -----------------------------------------------------------------------------------//
{
private static final byte CR = 0x0D;
// ---------------------------------------------------------------------------------//
public PascalInfo (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
@ -19,10 +21,7 @@ public class PascalInfo extends AbstractFile
StringBuilder text = new StringBuilder (getHeader ());
for (int i = 0; i < buffer.length; i++)
if (buffer[i] == 0x0D)
text.append ("\n");
else
text.append ((char) buffer[i]);
text.append (buffer[i] == CR ? "\n" : (char) buffer[i]);
return text.toString ();
}

View File

@ -36,7 +36,7 @@ public class PascalProcedure
this.buffer = buffer;
this.slot = slot;
int p = buffer.length - 2 - slot * 2;
offset = Utility.intValue (buffer[p], buffer[p + 1]);
offset = Utility.getShort (buffer, p);
procOffset = p - offset;
valid = procOffset > 0;
@ -44,10 +44,10 @@ public class PascalProcedure
{
procedureNo = buffer[procOffset] & 0xFF;
procLevel = buffer[procOffset + 1] & 0xFF;
codeStart = Utility.intValue (buffer[procOffset - 2], buffer[procOffset - 1]);
codeEnd = Utility.intValue (buffer[procOffset - 4], buffer[procOffset - 3]);
parmSize = Utility.intValue (buffer[procOffset - 6], buffer[procOffset - 5]);
dataSize = Utility.intValue (buffer[procOffset - 8], buffer[procOffset - 7]);
codeStart = Utility.getShort (buffer, procOffset - 2);
codeEnd = Utility.getShort (buffer, procOffset - 4);
parmSize = Utility.getShort (buffer, procOffset - 6);
dataSize = Utility.getShort (buffer, procOffset - 8);
}
}

View File

@ -43,14 +43,12 @@ public class PascalSegment extends AbstractFile implements PascalConstants
// this.blockOffset = blockOffset;
// this.relocator = relocator;
this.blockNo = Utility.intValue (fullBuffer[seq * 4], fullBuffer[seq * 4 + 1]);
this.size = Utility.intValue (fullBuffer[seq * 4 + 2], fullBuffer[seq * 4 + 3]);
this.blockNo = Utility.getShort (fullBuffer, seq * 4);
this.size = Utility.getShort (fullBuffer, seq * 4 + 2);
segKind =
Utility.intValue (fullBuffer[0xC0 + seq * 2], fullBuffer[0xC0 + seq * 2 + 1]);
segKind = Utility.getShort (fullBuffer, 0xC0 + seq * 2);
textAddress =
Utility.intValue (fullBuffer[0xE0 + seq * 2], fullBuffer[0xE0 + seq * 2 + 1]);
textAddress = Utility.getShort (fullBuffer, 0xE0 + seq * 2);
// segment 1 is the main segment, 2-6 are used by the system, and 7
// onwards is for the program
@ -65,10 +63,8 @@ public class PascalSegment extends AbstractFile implements PascalConstants
version = (flags & 0xD0) >> 5;
intrinsSegs1 =
Utility.intValue (fullBuffer[0x120 + seq * 4], fullBuffer[0x120 + seq * 4 + 1]);
intrinsSegs2 = Utility.intValue (fullBuffer[0x120 + seq * 4 + 2],
fullBuffer[0x120 + seq * 4 + 3]);
intrinsSegs1 = Utility.getShort (fullBuffer, 0x120 + seq * 4);
intrinsSegs2 = Utility.getShort (fullBuffer, 0x120 + seq * 4 + 2);
int offset = blockNo * 512;

View File

@ -1,9 +1,11 @@
package com.bytezone.diskbrowser.applefile;
// -----------------------------------------------------------------------------------//
public class PascalText extends AbstractFile
public class PascalText extends TextFile
// -----------------------------------------------------------------------------------//
{
private final static int PAGE_SIZE = 1024;
// ---------------------------------------------------------------------------------//
public PascalText (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
@ -16,26 +18,34 @@ public class PascalText extends AbstractFile
public String getText ()
// ---------------------------------------------------------------------------------//
{
// Text files are broken up into 1024-byte pages.
// [DLE] [indent] [text] [CR] ... [nulls]
StringBuilder text = new StringBuilder (getHeader ());
int ptr = 0x400;
int ptr = PAGE_SIZE; // skip text editor header
while (ptr < buffer.length)
{
if (buffer[ptr] == 0x00)
if (buffer[ptr] == 0x00) // padding to page boundary
{
++ptr;
ptr = (ptr / PAGE_SIZE + 1) * PAGE_SIZE; // skip to next page
continue;
}
if (buffer[ptr] == 0x10)
if (buffer[ptr] == 0x10) // Data Link Escape code
{
int tab = buffer[ptr + 1] - 0x20;
int tab = (buffer[ptr + 1] & 0xFF) - 32; // indent amaount
while (tab-- > 0)
text.append (" ");
ptr += 2;
}
String line = getLine (ptr);
text.append (line + "\n");
ptr += line.length () + 1;
while (buffer[ptr] != 0x0D)
text.append ((char) buffer[ptr++]);
text.append ("\n");
ptr++;
}
if (text.length () > 0)
@ -43,14 +53,4 @@ public class PascalText extends AbstractFile
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private String getLine (int ptr)
// ---------------------------------------------------------------------------------//
{
StringBuilder line = new StringBuilder ();
while (buffer[ptr] != 0x0D)
line.append ((char) buffer[ptr++]);
return line.toString ();
}
}

View File

@ -0,0 +1,187 @@
package com.bytezone.diskbrowser.applefile;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import com.bytezone.diskbrowser.disk.FormattedDisk;
import com.bytezone.diskbrowser.prodos.DirectoryHeader;
import com.bytezone.diskbrowser.prodos.ProdosConstants;
import com.bytezone.diskbrowser.prodos.ProdosDisk;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class ProdosDirectory extends AbstractFile implements ProdosConstants
// -----------------------------------------------------------------------------------//
{
private static Locale US = Locale.US; // to force 3 character months
static final DateTimeFormatter df = DateTimeFormatter.ofPattern ("d-LLL-yy", US);
static final DateTimeFormatter tf = DateTimeFormatter.ofPattern ("H:mm");
static final String UNDERLINE =
"----------------------------------------------------\n";
private static final String NO_DATE = "<NO DATE>";
private final ProdosDisk parentFD;
private final int totalBlocks;
private final int freeBlocks;
private final int usedBlocks;
// ---------------------------------------------------------------------------------//
public ProdosDirectory (FormattedDisk parent, String name, byte[] buffer,
int totalBlocks, int freeBlocks, int usedBlocks)
// ---------------------------------------------------------------------------------//
{
super (name, buffer);
this.parentFD = (ProdosDisk) parent;
this.totalBlocks = totalBlocks;
this.freeBlocks = freeBlocks;
this.usedBlocks = usedBlocks;
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
if (showDebugText)
return getDebugText ();
else
return getDirectoryText ();
}
// ---------------------------------------------------------------------------------//
private String getDebugText ()
// ---------------------------------------------------------------------------------//
{
List<DirectoryHeader> directoryHeaders = parentFD.getDirectoryHeaders ();
StringBuilder text = new StringBuilder ();
for (DirectoryHeader directoryHeader : directoryHeaders)
{
text.append (UNDERLINE);
text.append (directoryHeader.getText ());
text.append ("\n");
text.append (UNDERLINE);
directoryHeader.listFileEntries (text);
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private String getDirectoryText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append ("File : " + parentFD.getDisplayPath () + "\n\n");
for (int i = 0; i < buffer.length; i += ENTRY_SIZE)
{
int storageType = (buffer[i] & 0xF0) >> 4;
if (storageType == 0)
continue; // break??
int nameLength = buffer[i] & 0x0F;
String filename = HexFormatter.getString (buffer, i + 1, nameLength);
String subType = "";
switch (storageType)
{
case VOLUME_HEADER:
case SUBDIRECTORY_HEADER:
String root = storageType == VOLUME_HEADER ? "/" : "";
text.append (root + filename + "\n\n");
text.append (" NAME TYPE BLOCKS "
+ "MODIFIED CREATED ENDFILE SUBTYPE" + "\n\n");
break;
case FREE:
case SEEDLING:
case SAPLING:
case TREE:
case PASCAL_ON_PROFILE:
case GSOS_EXTENDED_FILE:
case SUBDIRECTORY:
int type = buffer[i + 16] & 0xFF;
int blocks = Utility.getShort (buffer, i + 19);
LocalDateTime createdDate = Utility.getAppleDate (buffer, i + 24);
LocalDateTime modifiedDate = Utility.getAppleDate (buffer, i + 33);
String dateC =
createdDate == null ? NO_DATE : createdDate.format (df).toUpperCase ();
String dateM =
modifiedDate == null ? NO_DATE : modifiedDate.format (df).toUpperCase ();
String timeC = createdDate == null ? "" : createdDate.format (tf);
String timeM = modifiedDate == null ? "" : modifiedDate.format (tf);
int eof = Utility.intValue (buffer[i + 21], buffer[i + 22], buffer[i + 23]);
int fileType = buffer[i + 16] & 0xFF;
String locked = (buffer[i + 30] & 0xE0) == 0xE0 ? " " : "*";
int aux = Utility.getShort (buffer, i + 31);
switch (fileType)
{
case FILE_TYPE_TEXT:
subType = String.format ("R=%5d", aux);
break;
case FILE_TYPE_BINARY:
case FILE_TYPE_PNT:
case FILE_TYPE_PIC:
case FILE_TYPE_FOT:
subType = String.format ("A=$%4X", aux);
break;
case FILE_TYPE_AWP:
int flags = Utility.intValue (buffer[i + 32], buffer[i + 31]); // aux backwards!
if (flags != 0)
filename = convert (filename, flags);
break;
default:
subType = "";
}
String forkFlag = storageType == 5 ? "+" : " ";
text.append (
String.format ("%s%-15s %3s%s %5d %9s %5s %9s %5s %8d %7s %04X%n",
locked, filename, ProdosConstants.fileTypes[type], forkFlag, blocks,
dateM, timeM, dateC, timeC, eof, subType, aux));
break;
default:
text.append (" <Unknown strage type : " + storageType + "\n");
}
}
text.append (
String.format ("%nBLOCKS FREE:%5d BLOCKS USED:%5d TOTAL BLOCKS:%5d%n",
freeBlocks, usedBlocks, totalBlocks));
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private String convert (String name, int flags)
// ---------------------------------------------------------------------------------//
{
char[] newName = name.toCharArray ();
for (int i = 0, weight = 0x8000; i < newName.length; i++, weight >>>= 1)
{
if ((flags & weight) != 0)
{
if (newName[i] == '.')
newName[i] = ' ';
else if (newName[i] >= 'A' && newName[i] <= 'Z')
newName[i] += 32;
}
}
return new String (newName);
}
}

View File

@ -72,37 +72,37 @@ public class QuickDrawFont extends CharacterList
int ptr = nameLength + 1; // start of header record
headerSize = Utility.unsignedShort (buffer, ptr);
headerSize = Utility.getShort (buffer, ptr);
fontDefinitionOffset = nameLength + 1 + headerSize * 2;
fontFamily = Utility.unsignedShort (buffer, ptr + 2);
fontStyle = Utility.unsignedShort (buffer, ptr + 4);
fontSize = Utility.unsignedShort (buffer, ptr + 6);
fontFamily = Utility.getShort (buffer, ptr + 2);
fontStyle = Utility.getShort (buffer, ptr + 4);
fontSize = Utility.getShort (buffer, ptr + 6);
versionMajor = buffer[ptr + 8] & 0xFF;
versionMinor = buffer[ptr + 9] & 0xFF;
extent = Utility.unsignedShort (buffer, ptr + 10);
extent = Utility.getShort (buffer, ptr + 10);
ptr = fontDefinitionOffset;
fontType = Utility.unsignedShort (buffer, ptr);
firstChar = Utility.unsignedShort (buffer, ptr + 2);
lastChar = Utility.unsignedShort (buffer, ptr + 4);
widMax = Utility.unsignedShort (buffer, ptr + 6);
kernMax = Utility.signedShort (buffer, ptr + 8);
nDescent = Utility.signedShort (buffer, ptr + 10);
fRectWidth = Utility.unsignedShort (buffer, ptr + 12);
fRectHeight = Utility.unsignedShort (buffer, ptr + 14);
fontType = Utility.getShort (buffer, ptr);
firstChar = Utility.getShort (buffer, ptr + 2);
lastChar = Utility.getShort (buffer, ptr + 4);
widMax = Utility.getShort (buffer, ptr + 6);
kernMax = Utility.getSignedShort (buffer, ptr + 8);
nDescent = Utility.getSignedShort (buffer, ptr + 10);
fRectWidth = Utility.getShort (buffer, ptr + 12);
fRectHeight = Utility.getShort (buffer, ptr + 14);
owTLoc = Utility.unsignedShort (buffer, ptr + 16);
owTLoc = Utility.getShort (buffer, ptr + 16);
offsetWidthTableOffset = (ptr + 16) + owTLoc * 2;
locationTableOffset = offsetWidthTableOffset - (lastChar - firstChar + 3) * 2;
bitImageOffset = ptr + 26;
ascent = Utility.unsignedShort (buffer, ptr + 18);
descent = Utility.unsignedShort (buffer, ptr + 20);
leading = Utility.unsignedShort (buffer, ptr + 22);
rowWords = Utility.unsignedShort (buffer, ptr + 24);
ascent = Utility.getShort (buffer, ptr + 18);
descent = Utility.getShort (buffer, ptr + 20);
leading = Utility.getShort (buffer, ptr + 22);
rowWords = Utility.getShort (buffer, ptr + 24);
totalCharacters = lastChar - firstChar + 2; // includes 'missing' character
@ -151,12 +151,12 @@ public class QuickDrawFont extends CharacterList
for (int i = 0, max = totalCharacters + 1; i < max; i++)
{
// index into the strike
int location = Utility.unsignedShort (buffer, locationTableOffset + i * 2);
int location = Utility.getShort (buffer, locationTableOffset + i * 2);
int j = i + 1; // next character
if (j < max)
{
int nextLocation = Utility.unsignedShort (buffer, locationTableOffset + j * 2);
int nextLocation = Utility.getShort (buffer, locationTableOffset + j * 2);
int pixelWidth = nextLocation - location;
if (pixelWidth > 0)
@ -259,9 +259,8 @@ public class QuickDrawFont extends CharacterList
if (offset == 255 && width == 255)
continue;
int location = Utility.unsignedShort (buffer, locationTableOffset + i * 2);
int nextLocation =
Utility.unsignedShort (buffer, locationTableOffset + (i + 1) * 2);
int location = Utility.getShort (buffer, locationTableOffset + i * 2);
int nextLocation = Utility.getShort (buffer, locationTableOffset + (i + 1) * 2);
int pixelWidth = nextLocation - location;
text.append (String.format (

View File

@ -27,7 +27,7 @@ public class SHRPictureFile1 extends HiResImage
int ptr = 0;
while (ptr < buffer.length)
{
int len = Utility.unsignedLong (buffer, ptr);
int len = Utility.getLong (buffer, ptr);
if (len == 0 || len > buffer.length)
{
System.out.printf ("Block length: %d%n", len);
@ -141,7 +141,7 @@ public class SHRPictureFile1 extends HiResImage
int lo = dirEntry.mode & 0x00FF; // mode bit if hi == 0
boolean fillMode = (dirEntry.mode & 0x20) != 0;
// assert fillMode == false;
// assert fillMode == false;
if (hi != 0)
System.out.println ("hi not zero");
@ -232,7 +232,7 @@ public class SHRPictureFile1 extends HiResImage
super (kind, data);
int ptr = 5 + kind.length ();
numColorTables = Utility.unsignedShort (data, ptr);
numColorTables = Utility.getShort (data, ptr);
ptr += 2;
colorTables = new ColorTable[numColorTables];
@ -242,7 +242,8 @@ public class SHRPictureFile1 extends HiResImage
if (ptr < data.length - 32)
colorTables[i] = new ColorTable (i, data, ptr);
else
colorTables[i] = new ColorTable (i, 0x00); // default empty table !! not finished
colorTables[i] = new ColorTable (i, 0x00); // default empty table !! not
// finished
ptr += 32;
}
}
@ -286,9 +287,9 @@ public class SHRPictureFile1 extends HiResImage
super (kind, data);
int ptr = 5 + kind.length ();
masterMode = Utility.unsignedShort (data, ptr);
pixelsPerScanLine = Utility.unsignedShort (data, ptr + 2);
numColorTables = Utility.unsignedShort (data, ptr + 4);
masterMode = Utility.getShort (data, ptr);
pixelsPerScanLine = Utility.getShort (data, ptr + 2);
numColorTables = Utility.getShort (data, ptr + 4);
mode640 = (masterMode & 0x80) != 0;
ptr += 6;
@ -299,7 +300,7 @@ public class SHRPictureFile1 extends HiResImage
ptr += 32;
}
numScanLines = Utility.unsignedShort (data, ptr);
numScanLines = Utility.getShort (data, ptr);
ptr += 2;
scanLineDirectory = new DirEntry[numScanLines];
@ -332,11 +333,11 @@ public class SHRPictureFile1 extends HiResImage
ptr = 0;
for (int line = 0; line < numScanLines; line++)
{
// if (isOddAndEmpty (packedScanLines[line]))
// {
// System.out.println ("Odd number of bytes in empty buffer in " + name);
// break;
// }
// if (isOddAndEmpty (packedScanLines[line]))
// {
// System.out.println ("Odd number of bytes in empty buffer in " + name);
// break;
// }
int bytesUnpacked = unpack (packedScanLines[line], 0,
packedScanLines[line].length, unpackedBuffer, ptr);
@ -352,16 +353,17 @@ public class SHRPictureFile1 extends HiResImage
}
// -------------------------------------------------------------------------------//
// private boolean isOddAndEmpty (byte[] buffer)
// // -------------------------------------------------------------------------------//
// {
// if (buffer.length % 2 == 0)
// return false;
// for (byte b : buffer)
// if (b != 0)
// return false;
// return true;
// }
// private boolean isOddAndEmpty (byte[] buffer)
// //
// -------------------------------------------------------------------------------//
// {
// if (buffer.length % 2 == 0)
// return false;
// for (byte b : buffer)
// if (b != 0)
// return false;
// return true;
// }
// -------------------------------------------------------------------------------//
@Override

View File

@ -5,6 +5,7 @@ import java.awt.image.DataBuffer;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.nufx.LZW3;
import com.bytezone.diskbrowser.prodos.ProdosConstants;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
@ -99,10 +100,10 @@ public class SHRPictureFile2 extends HiResImage
unpack (buffer, 0, buffer.length, newBuffer, 0);
buffer = newBuffer;
int mode = Utility.unsignedShort (this.buffer, 0);
int rect1 = Utility.unsignedLong (this.buffer, 2);
int rect2 = Utility.unsignedLong (this.buffer, 6);
int version = Utility.unsignedShort (this.buffer, 10); // $8211
int mode = Utility.getShort (this.buffer, 0);
int rect1 = Utility.getLong (this.buffer, 2);
int rect2 = Utility.getLong (this.buffer, 6);
int version = Utility.getShort (this.buffer, 10); // $8211
break;
@ -144,6 +145,36 @@ public class SHRPictureFile2 extends HiResImage
break;
case 0x8005:
int ptr = buffer.length - 17;
int imageType = Utility.getShort (buffer, ptr);
int imageHeight = Utility.getShort (buffer, ptr + 2);
int imageWidth = Utility.getShort (buffer, ptr + 4);
String id = HexFormatter.getPascalString (buffer, ptr + 6);
assert "DreamWorld".equals (id);
int expectedLen = 32000 + 512;
if (imageType == 0) // 256 colours
expectedLen += (256 + 512);
else // 3200 colours
expectedLen += 6400;
byte[] dstBuffer = new byte[expectedLen + 1024];
LZW3 lzw3 = new LZW3 ();
int bytes = lzw3.unpack (buffer, dstBuffer, expectedLen);
buffer = dstBuffer;
colorTables = new ColorTable[imageHeight];
for (int i = 0; i < colorTables.length; i++)
{
colorTables[i] = new ColorTable (i, this.buffer, 32000 + i * COLOR_TABLE_SIZE);
colorTables[i].reverse ();
}
break;
default:
System.out.printf ("%s: PNT unknown aux: %04X%n", name, auxType);
failureReason = "unknown PNT aux";
@ -154,13 +185,13 @@ public class SHRPictureFile2 extends HiResImage
private void doAnimation ()
// ---------------------------------------------------------------------------------//
{
// int len = HexFormatter.unsignedLong (buffer, 0x8000);
delay = Utility.unsignedLong (buffer, 0x8004);
// int len = HexFormatter.getLong (buffer, 0x8000);
delay = Utility.getLong (buffer, 0x8004);
if (delay > 60)
delay = 10;
delay = delay * 1000 / 60;
// int offset = HexFormatter.unsignedLong (buffer, 0x8008);
// int offset = HexFormatter.getLong (buffer, 0x8008);
// int blockLen = eof - 0x8008;
// System.out.printf ("Delay: %,d%n", delay);
@ -173,7 +204,7 @@ public class SHRPictureFile2 extends HiResImage
int start = ptr;
while (ptr < buffer.length)
{
int off = Utility.unsignedShort (buffer, ptr);
int off = Utility.getShort (buffer, ptr);
ptr += 4;
if (off == 0)
@ -299,7 +330,7 @@ public class SHRPictureFile2 extends HiResImage
while (true)
{
int offset = Utility.unsignedShort (buffer, ptr);
int offset = Utility.getShort (buffer, ptr);
if (offset == 0)
break;

View File

@ -1,16 +1,40 @@
package com.bytezone.diskbrowser.applefile;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class SegmentDictionary
// -----------------------------------------------------------------------------------//
{
private final boolean isValid;
private int[] codeAddress = new int[16];
private int[] codeLength = new int[16];
private String[] segName = new String[16];
// ---------------------------------------------------------------------------------//
public SegmentDictionary (String name, byte[] buffer)
// ---------------------------------------------------------------------------------//
{
isValid = !name.equals ("SYSTEM.INTERP"); // temporary
int ptr = 0;
for (int seg = 0; seg < 16; seg++)
{
codeAddress[seg] = Utility.getShort (buffer, ptr);
ptr += 2;
codeLength[seg] = Utility.getShort (buffer, ptr);
ptr += 2;
}
ptr = 64;
for (int seg = 0; seg < 16; seg++)
{
segName[seg] = new String (buffer, ptr, 8);
ptr += 8;
}
// for (int seg = 0; seg < 16; seg++)
// System.out.printf ("%04X %04X %s%n", codeAddress[seg], codeLength[seg], segName[seg]);
}
// ---------------------------------------------------------------------------------//

View File

@ -37,6 +37,8 @@ public class SegmentHeader
String loadname;
String segname;
boolean debug = false;
// ---------------------------------------------------------------------------------//
public SegmentHeader (byte[] buffer, int offset)
// ---------------------------------------------------------------------------------//
@ -52,20 +54,20 @@ public class SegmentHeader
version = buffer[offset + 15] & 0xFF;
banksize = Utility.getLong (buffer, offset + 16);
kind2 = Utility.getWord (buffer, offset + 20);
unused = Utility.getWord (buffer, offset + 22);
kind2 = Utility.getShort (buffer, offset + 20);
unused = Utility.getShort (buffer, offset + 22);
org = Utility.getLong (buffer, offset + 24);
align = Utility.getLong (buffer, offset + 28);
numsex = buffer[offset + 32] & 0xFF;
lcbank = buffer[offset + 33] & 0xFF;
segnum = Utility.getWord (buffer, offset + 34);
segnum = Utility.getShort (buffer, offset + 34);
entry = Utility.getLong (buffer, offset + 36);
dispname = Utility.getWord (buffer, offset + 40);
dispdata = Utility.getWord (buffer, offset + 42);
dispname = Utility.getShort (buffer, offset + 40);
dispdata = Utility.getShort (buffer, offset + 42);
decodeKind ();
@ -79,7 +81,8 @@ public class SegmentHeader
else
segname = "not finished";
System.out.println (this);
if (debug)
System.out.println (this);
int ptr = offset + dispdata;
while (true)
@ -89,25 +92,33 @@ public class SegmentHeader
if (recType > 0 && recType <= 0xDF)
{
System.out.printf ("Const: %02X%n", recType);
System.out.println (HexFormatter.format (buffer, ptr, recType + 1, ptr));
if (debug)
{
System.out.printf ("Const: %02X%n", recType);
System.out.println (HexFormatter.format (buffer, ptr, recType + 1, ptr));
}
ptr += recType + 1;
continue;
}
System.out.printf ("%02X ", recType);
if (debug)
System.out.printf ("%02X ", recType);
switch (recType)
{
case 0x00: // END
System.out.println ("END");
if (debug)
System.out.println ("END");
break;
case 0xE0: // ALIGN
System.out.printf ("ALIGN:%n");
if (debug)
System.out.printf ("ALIGN:%n");
break;
case 0xE1: // ORG
System.out.printf ("ORG:%n");
if (debug)
System.out.printf ("ORG:%n");
break;
case 0xE2: // RELOC
@ -115,8 +126,9 @@ public class SegmentHeader
int bitShift = buffer[ptr + 2] & 0xFF;
int segmentOffset = Utility.getLong (buffer, ptr + 3);
int value = Utility.getLong (buffer, ptr + 7);
System.out.printf ("RELOC: %02X %02X %08X %08X%n", bytesRelocated, bitShift,
segmentOffset, value);
if (debug)
System.out.printf ("RELOC: %02X %02X %08X %08X%n", bytesRelocated, bitShift,
segmentOffset, value);
ptr += 11;
continue;
@ -124,102 +136,121 @@ public class SegmentHeader
int count1 = buffer[ptr + 1] & 0xFF;
int count2 = buffer[ptr + 2] & 0xFF;
int operandOffset = Utility.getLong (buffer, ptr + 3);
int fileNo = Utility.getWord (buffer, ptr + 7);
int segNo = Utility.getWord (buffer, ptr + 9);
int fileNo = Utility.getShort (buffer, ptr + 7);
int segNo = Utility.getShort (buffer, ptr + 9);
int subroutineOffset = Utility.getLong (buffer, ptr + 11);
System.out.printf ("INTERSEG: %02X %02X %08X %04X %04X %08X%n", count1, count2,
operandOffset, fileNo, segNo, subroutineOffset);
if (debug)
System.out.printf ("INTERSEG: %02X %02X %08X %04X %04X %08X%n", count1,
count2, operandOffset, fileNo, segNo, subroutineOffset);
ptr += 15;
break;
case 0xE4: // USING
System.out.printf ("USING:%n");
if (debug)
System.out.printf ("USING:%n");
break;
case 0xE5: // STRONG
System.out.printf ("STRONG:%n");
if (debug)
System.out.printf ("STRONG:%n");
break;
case 0xE6: // GLOBAL
System.out.printf ("GLOBAL:%n");
if (debug)
System.out.printf ("GLOBAL:%n");
break;
case 0xE7: // GEQU
System.out.printf ("GEQU:%n");
if (debug)
System.out.printf ("GEQU:%n");
break;
case 0xE8: // MEM
System.out.printf ("MEM:%n");
if (debug)
System.out.printf ("MEM:%n");
break;
case 0xEB: // EXPR
System.out.printf ("EXPR:%n");
if (debug)
System.out.printf ("EXPR:%n");
break;
case 0xEC: // ZEXPR
System.out.printf ("ZEXPR:%n");
if (debug)
System.out.printf ("ZEXPR:%n");
break;
case 0xED: // BEXPR
System.out.printf ("BEXPR:%n");
if (debug)
System.out.printf ("BEXPR:%n");
break;
case 0xEE: // RELEXPR
System.out.printf ("RELEXPR:%n");
if (debug)
System.out.printf ("RELEXPR:%n");
break;
case 0xEF: // LOCAL
System.out.printf ("LOCAL:%n");
if (debug)
System.out.printf ("LOCAL:%n");
break;
case 0xF0: // EQU
String label = HexFormatter.getPascalString (buffer, ptr + 1);
System.out.printf ("EQU: %s%n", label);
if (debug)
System.out.printf ("EQU: %s%n", label);
break;
case 0xF1: // DS
System.out.printf ("DS:%n");
if (debug)
System.out.printf ("DS:%n");
break;
case 0xF2: // LCONST
int constLength = Utility.getLong (buffer, ptr + 1);
System.out.printf ("Const: %04X%n", constLength);
if (debug)
System.out.printf ("Const: %04X%n", constLength);
ptr += constLength + 5;
continue;
case 0xF3: // LEXPR
System.out.printf ("LEXPR:%n");
if (debug)
System.out.printf ("LEXPR:%n");
break;
case 0xF4: // ENTRY
System.out.printf ("ENTRY:%n");
if (debug)
System.out.printf ("ENTRY:%n");
break;
case 0xF5: // cRELOC
int cBytesRelocated = buffer[ptr + 1] & 0xFF;
int cBitShift = buffer[ptr + 2] & 0xFF;
int cSegmentOffset = Utility.getWord (buffer, ptr + 3);
int cValue = Utility.getWord (buffer, ptr + 5);
System.out.printf ("cRELOC: %02X %02X %08X %08X%n", cBytesRelocated, cBitShift,
cSegmentOffset, cValue);
int cSegmentOffset = Utility.getShort (buffer, ptr + 3);
int cValue = Utility.getShort (buffer, ptr + 5);
if (debug)
System.out.printf ("cRELOC: %02X %02X %08X %08X%n", cBytesRelocated,
cBitShift, cSegmentOffset, cValue);
ptr += 7;
continue;
case 0xF6: // cINTERSEG
int cCount1 = buffer[ptr + 1] & 0xFF;
int cCount2 = buffer[ptr + 2] & 0xFF;
int cOperandOffset = Utility.getWord (buffer, ptr + 3);
int cOperandOffset = Utility.getShort (buffer, ptr + 3);
int cSegNo = buffer[ptr + 5] & 0xFF;
int cSubroutineOffset = Utility.getWord (buffer, ptr + 6);
System.out.printf ("cINTERSEG: %02X %02X %04X %02X %04X%n", cCount1, cCount2,
cOperandOffset, cSegNo, cSubroutineOffset);
int cSubroutineOffset = Utility.getShort (buffer, ptr + 6);
if (debug)
System.out.printf ("cINTERSEG: %02X %02X %04X %02X %04X%n", cCount1, cCount2,
cOperandOffset, cSegNo, cSubroutineOffset);
ptr += 8;
continue;
case 0xF7: // SUPER
int superLength = Utility.getLong (buffer, ptr + 1);
int recordType = buffer[ptr + 5] & 0xFF;
System.out.printf ("Super type %02X%n", recordType);
if (debug)
System.out.printf ("Super type %02X%n", recordType);
ptr += superLength + 5;
continue;
@ -228,7 +259,8 @@ public class SegmentHeader
break;
}
System.out.println ();
if (debug)
System.out.println ();
break;
}
}
@ -258,33 +290,18 @@ public class SegmentHeader
kindPrivate = (segAttr & 0x40) != 0;
kindStatic = (segAttr & 0x80) == 0;
switch (segType)
kindWhereText = switch (segType)
{
case 0:
kindWhereText = "Code Segment";
break;
case 1:
kindWhereText = "Data Segment";
break;
case 2:
kindWhereText = "Jump Table Segment";
break;
case 4:
kindWhereText = "Pathname Segment";
break;
case 8:
kindWhereText = "Library Dictionary Segment";
break;
case 0x10:
kindWhereText = "Initialization Segment";
break;
case 0x11:
kindWhereText = "Absolute Bank Segment";
break;
case 0x12:
kindWhereText = "Direct Page / Stack Segment";
break;
}
case 0x00 -> "Code Segment";
case 0x01 -> "Data Segment";
case 0x02 -> "Jump Table Segment";
case 0x04 -> "Pathname Segment";
case 0x08 -> "Library Dictionary Segment";
case 0x10 -> "Initialization Segment";
case 0x11 -> "Absolute Bank Segment";
case 0x12 -> "Direct Page / Stack Segment";
default -> "Unknown";
};
}
// ---------------------------------------------------------------------------------//

View File

@ -77,21 +77,14 @@ public class Selector extends AbstractFile
labelLength = buffer[ptr] & 0xFF;
label = new String (buffer, ptr + 1, labelLength);
copyFlags = buffer[ptr + 15];
switch (copyFlags & 0xFF)
copyText = switch (copyFlags & 0xFF)
{
case 0:
copyText = "First boot";
break;
case 0x80:
copyText = "First use";
break;
case 0xC0:
copyText = "Never";
break;
default:
copyText = "Unknown";
break;
}
case 0x00 -> "First boot";
case 0x80 -> "First use";
case 0xC0 -> "Never";
default -> "Unknown";
};
}
}

View File

@ -0,0 +1,235 @@
package com.bytezone.diskbrowser.applefile;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
class Shape
// -----------------------------------------------------------------------------------//
{
private static final int SIZE = 400;
private final byte[] buffer;
private final int index;
int offset;
int actualLength;
int minRow, maxRow;
int minCol, maxCol;
int startRow = SIZE / 2;
int startCol = SIZE / 2;
int[][] grid = new int[SIZE][SIZE];
int[][] displayGrid;
boolean valid;
BufferedImage image;
// ---------------------------------------------------------------------------------//
public Shape (byte[] buffer, int index)
// ---------------------------------------------------------------------------------//
{
this.index = index;
this.buffer = buffer;
int row = startRow;
int col = startCol;
offset = Utility.getShort (buffer, index * 2 + 2);
int ptr = offset;
while (ptr < buffer.length)
{
int value = buffer[ptr++] & 0xFF;
if (value == 0)
break;
// P = plot
// DD = direction to move
int v1 = value >>> 6; // DD......
int v2 = (value & 0x38) >>> 3; // ..PDD...
int v3 = value & 0x07; // .....PDD
// rightmost 3 bits
if (v3 >= 4)
if (!plot (grid, row, col))
return;
if (v3 == 0 || v3 == 4)
row--;
else if (v3 == 1 || v3 == 5)
col++;
else if (v3 == 2 || v3 == 6)
row++;
else
col--;
// middle 3 bits
if (v2 >= 4)
if (!plot (grid, row, col))
return;
// cannot move up without plotting if v1 is zero
if ((v2 == 0 && v1 != 0) || v2 == 4)
row--;
else if (v2 == 1 || v2 == 5)
col++;
else if (v2 == 2 || v2 == 6)
row++;
else if (v2 == 3 || v2 == 7)
col--;
// leftmost 2 bits (cannot plot or move up)
if (v1 == 1)
col++;
else if (v1 == 2)
row++;
else if (v1 == 3)
col--;
}
actualLength = ptr - offset;
// endRow = row;
// endCol = col;
// find min and max rows with pixels
minRow = startRow;
maxRow = startRow;
// minRow = Math.min (minRow, endRow);
// maxRow = Math.max (maxRow, endRow);
for (row = 1; row < grid.length; row++)
{
if (grid[row][0] > 0)
{
minRow = Math.min (minRow, row);
maxRow = Math.max (maxRow, row);
}
}
// find min and max columns with pixels
minCol = startCol;
maxCol = startCol;
// minCol = Math.min (minCol, endCol);
// maxCol = Math.max (maxCol, endCol);
for (col = 1; col < grid[0].length; col++)
{
if (grid[0][col] > 0)
{
minCol = Math.min (minCol, col);
maxCol = Math.max (maxCol, col);
}
}
valid = true;
}
// ---------------------------------------------------------------------------------//
void convertGrid (int offsetRows, int offsetColumns, int rows, int columns)
// ---------------------------------------------------------------------------------//
{
// System.out.printf ("Converting shape # %d%n", index);
// System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows,
// offsetColumns);
// System.out.printf ("rows %d cols %d%n", rows, columns);
displayGrid = new int[rows][columns];
for (int row = 0; row < rows; row++)
for (int col = 0; col < columns; col++)
displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col];
grid = null;
// draw the image
image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY);
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
int element = 0;
for (int row = 0; row < rows; row++)
for (int col = 0; col < columns; col++)
dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255);
startRow -= offsetRows;
startCol -= offsetColumns;
// endRow -= offsetRows;
// endCol -= offsetColumns;
}
// ---------------------------------------------------------------------------------//
private boolean plot (int[][] grid, int row, int col)
// ---------------------------------------------------------------------------------//
{
if (row < 0 || row >= SIZE || col < 0 || col >= SIZE)
{
System.out.printf ("Shape table out of range: %d, %d%n", row, col);
return false;
}
grid[row][col] = 1; // plot
grid[0][col]++; // increment total column dots
grid[row][0]++; // increment total row dots
return true;
}
// ---------------------------------------------------------------------------------//
public void drawText (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
text.append (String.format ("Shape : %d%n", index));
text.append (String.format ("Size : %d%n", actualLength));
// text.append (String.format ("Width : %d%n", width));
// text.append (String.format ("Height : %d%n", height));
// append the shape's data
String bytes = HexFormatter.getHexString (buffer, offset, actualLength);
int ptr = offset;
for (String s : split (bytes))
{
text.append (String.format (" %04X : %s%n", ptr, s));
ptr += 16;
}
text.append ("\n");
for (int row = 0; row < displayGrid.length; row++)
{
for (int col = 0; col < displayGrid[0].length; col++)
if (col == startCol && row == startRow)
text.append (displayGrid[row][col] > 0 ? " @" : " .");
// else if (col == endCol && row == endRow)
// text.append (displayGrid[row][col] > 0 ? " #" : " .");
else if (displayGrid[row][col] == 0)
text.append (" ");
else
text.append (" X");
text.append ("\n");
}
text.append ("\n");
}
// ---------------------------------------------------------------------------------//
private List<String> split (String line)
// ---------------------------------------------------------------------------------//
{
List<String> list = new ArrayList<> ();
while (line.length () > 48)
{
list.add (line.substring (0, 47));
line = line.substring (48);
}
list.add (line);
return list;
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol,
maxCol);
}
}

View File

@ -3,11 +3,9 @@ package com.bytezone.diskbrowser.applefile;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
/*-
@ -27,7 +25,6 @@ public class ShapeTable extends AbstractFile
// -----------------------------------------------------------------------------------//
{
private final List<Shape> shapes = new ArrayList<> ();
private static final int SIZE = 400;
int maxWidth = 0;
int maxHeight = 0;
@ -131,7 +128,7 @@ public class ShapeTable extends AbstractFile
return false;
// check index points inside the file
int offset = Utility.unsignedShort (buffer, ptr);
int offset = Utility.getShort (buffer, ptr);
if (offset == 0 || offset >= buffer.length)
return false;
@ -142,216 +139,4 @@ public class ShapeTable extends AbstractFile
return true;
}
// ---------------------------------------------------------------------------------//
class Shape
// ---------------------------------------------------------------------------------//
{
private final byte[] buffer;
private final int index;
int offset;
int actualLength;
int minRow, maxRow;
int minCol, maxCol;
int startRow = SIZE / 2;
int startCol = SIZE / 2;
int[][] grid = new int[SIZE][SIZE];
int[][] displayGrid;
boolean valid;
private BufferedImage image;
public Shape (byte[] buffer, int index)
{
this.index = index;
this.buffer = buffer;
int row = startRow;
int col = startCol;
offset = Utility.unsignedShort (buffer, index * 2 + 2);
int ptr = offset;
while (ptr < buffer.length)
{
int value = buffer[ptr++] & 0xFF;
if (value == 0)
break;
// P = plot
// DD = direction to move
int v1 = value >>> 6; // DD......
int v2 = (value & 0x38) >>> 3; // ..PDD...
int v3 = value & 0x07; // .....PDD
// rightmost 3 bits
if (v3 >= 4)
if (!plot (grid, row, col))
return;
if (v3 == 0 || v3 == 4)
row--;
else if (v3 == 1 || v3 == 5)
col++;
else if (v3 == 2 || v3 == 6)
row++;
else
col--;
// middle 3 bits
if (v2 >= 4)
if (!plot (grid, row, col))
return;
// cannot move up without plotting if v1 is zero
if ((v2 == 0 && v1 != 0) || v2 == 4)
row--;
else if (v2 == 1 || v2 == 5)
col++;
else if (v2 == 2 || v2 == 6)
row++;
else if (v2 == 3 || v2 == 7)
col--;
// leftmost 2 bits (cannot plot or move up)
if (v1 == 1)
col++;
else if (v1 == 2)
row++;
else if (v1 == 3)
col--;
}
actualLength = ptr - offset;
// endRow = row;
// endCol = col;
// find min and max rows with pixels
minRow = startRow;
maxRow = startRow;
// minRow = Math.min (minRow, endRow);
// maxRow = Math.max (maxRow, endRow);
for (row = 1; row < grid.length; row++)
{
if (grid[row][0] > 0)
{
minRow = Math.min (minRow, row);
maxRow = Math.max (maxRow, row);
}
}
// find min and max columns with pixels
minCol = startCol;
maxCol = startCol;
// minCol = Math.min (minCol, endCol);
// maxCol = Math.max (maxCol, endCol);
for (col = 1; col < grid[0].length; col++)
{
if (grid[0][col] > 0)
{
minCol = Math.min (minCol, col);
maxCol = Math.max (maxCol, col);
}
}
valid = true;
}
void convertGrid (int offsetRows, int offsetColumns, int rows, int columns)
{
// System.out.printf ("Converting shape # %d%n", index);
// System.out.printf ("offsetRows %d offsetCols %d%n", offsetRows,
// offsetColumns);
// System.out.printf ("rows %d cols %d%n", rows, columns);
displayGrid = new int[rows][columns];
for (int row = 0; row < rows; row++)
for (int col = 0; col < columns; col++)
displayGrid[row][col] = grid[offsetRows + row][offsetColumns + col];
grid = null;
// draw the image
image = new BufferedImage (columns, rows, BufferedImage.TYPE_BYTE_GRAY);
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
int element = 0;
for (int row = 0; row < rows; row++)
for (int col = 0; col < columns; col++)
dataBuffer.setElem (element++, displayGrid[row][col] == 0 ? 0 : 255);
startRow -= offsetRows;
startCol -= offsetColumns;
// endRow -= offsetRows;
// endCol -= offsetColumns;
}
private boolean plot (int[][] grid, int row, int col)
{
if (row < 0 || row >= SIZE || col < 0 || col >= SIZE)
{
System.out.printf ("Shape table out of range: %d, %d%n", row, col);
return false;
}
grid[row][col] = 1; // plot
grid[0][col]++; // increment total column dots
grid[row][0]++; // increment total row dots
return true;
}
public void drawText (StringBuilder text)
{
text.append (String.format ("Shape : %d%n", index));
text.append (String.format ("Size : %d%n", actualLength));
// text.append (String.format ("Width : %d%n", width));
// text.append (String.format ("Height : %d%n", height));
// append the shape's data
String bytes = HexFormatter.getHexString (buffer, offset, actualLength);
int ptr = offset;
for (String s : split (bytes))
{
text.append (String.format (" %04X : %s%n", ptr, s));
ptr += 16;
}
text.append ("\n");
for (int row = 0; row < displayGrid.length; row++)
{
for (int col = 0; col < displayGrid[0].length; col++)
if (col == startCol && row == startRow)
text.append (displayGrid[row][col] > 0 ? " @" : " .");
// else if (col == endCol && row == endRow)
// text.append (displayGrid[row][col] > 0 ? " #" : " .");
else if (displayGrid[row][col] == 0)
text.append (" ");
else
text.append (" X");
text.append ("\n");
}
text.append ("\n");
}
private List<String> split (String line)
{
List<String> list = new ArrayList<> ();
while (line.length () > 48)
{
list.add (line.substring (0, 47));
line = line.substring (48);
}
list.add (line);
return list;
}
@Override
public String toString ()
{
return String.format ("%3d %3d %3d %3d %3d", index, minRow, maxRow, minCol,
maxCol);
}
}
}

View File

@ -32,6 +32,13 @@ public class SimpleText extends AbstractFile
if (ptr < buffer.length && buffer[ptr] == 0x0A)
ptr++;
}
if (resourceFork != null)
{
text.append ("\n\nResource Fork\n=============\n");
text.append (resourceFork);
}
return text.toString ();
}

View File

@ -0,0 +1,99 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_QUOTE;
import static com.bytezone.diskbrowser.utilities.Utility.getShort;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class SourceLine implements ApplesoftConstants
// -----------------------------------------------------------------------------------//
{
ApplesoftBasicProgram program;
int linkField;
int lineNumber;
int linePtr;
int length;
byte[] buffer;
List<SubLine> sublines = new ArrayList<> ();
// ---------------------------------------------------------------------------------//
SourceLine (ApplesoftBasicProgram program, byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
this.program = program;
this.buffer = buffer;
linePtr = ptr;
linkField = getShort (buffer, ptr);
lineNumber = getShort (buffer, ptr + 2);
int startPtr = ptr += 4; // skip link field and lineNumber
boolean inString = false; // can toggle
boolean inRemark = false; // can only go false -> true
byte b;
while (ptr < buffer.length && (b = buffer[ptr++]) != 0)
{
if (inRemark) // cannot terminate a REM
continue;
if (inString)
{
if (b == ASCII_QUOTE) // terminate string
inString = false;
continue;
}
switch (b)
{
// break IF statements into two sublines (allows for easier line indenting)
case TOKEN_IF:
while (buffer[ptr] != TOKEN_THEN && buffer[ptr] != TOKEN_GOTO
&& buffer[ptr] != 0)
ptr++;
if (buffer[ptr] == TOKEN_THEN) // keep THEN with the IF
++ptr;
startPtr = addSubLine (startPtr, ptr); // create subline from the condition
break;
case ASCII_COLON: // end of subline
startPtr = addSubLine (startPtr, ptr);
break;
case TOKEN_REM:
if (ptr == startPtr + 1) // at start of line
inRemark = true;
else // mid-line - should be illegal
{
System.out.printf ("%s : %5d mid-line REM token%n", program.name, lineNumber);
startPtr = addSubLine (startPtr, --ptr); // point back to the REM
}
break;
case Utility.ASCII_QUOTE:
inString = true;
break;
}
}
length = ptr - linePtr;
addSubLine (startPtr, ptr);
}
// ---------------------------------------------------------------------------------//
private int addSubLine (int startPtr, int ptr)
// ---------------------------------------------------------------------------------//
{
sublines.add (new SubLine (this, startPtr, ptr - startPtr));
return ptr;
}
}

View File

@ -26,10 +26,10 @@ public class StoredVariables extends AbstractFile
int strPtr = buffer.length;
text.append ("File length : " + HexFormatter.format4 (buffer.length));
int totalLength = Utility.intValue (buffer[0], buffer[1]);
int totalLength = Utility.getShort (buffer, 0);
text.append ("\nTotal length : " + HexFormatter.format4 (totalLength));
int varLength = Utility.intValue (buffer[2], buffer[3]);
int varLength = Utility.getShort (buffer, 2);
text.append ("\nVar length : " + HexFormatter.format4 (varLength));
text.append ("\n\n");
@ -51,8 +51,8 @@ public class StoredVariables extends AbstractFile
}
else if (suffix == '%')
{
intValue = Utility.intValue (buffer[ptr + 3], buffer[ptr + 2]);
if ((buffer[ptr + 2] & 0x80) > 0)
intValue = Utility.intValue (buffer[ptr + 3], buffer[ptr + 2]); // backwards!
if ((buffer[ptr + 2] & 0x80) != 0)
intValue -= 65536;
text.append (" = " + intValue);
}
@ -60,7 +60,7 @@ public class StoredVariables extends AbstractFile
{
if (hasValue (ptr + 2))
{
String value = HexFormatter.floatValue (buffer, ptr + 2) + "";
String value = Utility.floatValue (buffer, ptr + 2) + "";
if (value.endsWith (".0"))
text.append (" = " + value.substring (0, value.length () - 2));
else
@ -101,7 +101,7 @@ public class StoredVariables extends AbstractFile
suffix = ' ';
}
StringBuffer variableName = new StringBuffer ();
StringBuilder variableName = new StringBuilder ();
variableName.append (c1);
if (c2 > 32)
variableName.append (c2);
@ -116,12 +116,14 @@ public class StoredVariables extends AbstractFile
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ("(");
for (int i = 0; i < values.length; i++)
{
text.append (values[i]);
if (i < values.length - 1)
text.append (',');
}
return text.append (')').toString ();
}
@ -133,14 +135,14 @@ public class StoredVariables extends AbstractFile
{
String variableName = getVariableName (buffer[ptr], buffer[ptr + 1]);
text.append ("\n");
int offset = Utility.intValue (buffer[ptr + 2], buffer[ptr + 3]);
int offset = Utility.getShort (buffer, ptr + 2);
int dimensions = buffer[ptr + 4] & 0xFF;
int[] dimensionSizes = new int[dimensions];
int totalElements = 0;
for (int i = 0; i < dimensions; i++)
{
int p = i * 2 + 5 + ptr;
int elements = Utility.intValue (buffer[p + 1], buffer[p]);
int elements = Utility.intValue (buffer[p + 1], buffer[p]); // backwards!
dimensionSizes[dimensions - i - 1] = elements - 1;
if (totalElements == 0)
totalElements = elements;
@ -160,7 +162,7 @@ public class StoredVariables extends AbstractFile
text.append (variableName + " " + getDimensionText (values) + " = ");
if (elementSize == 2)
{
int intValue = Utility.intValue (buffer[p + 1], buffer[p]);
int intValue = Utility.intValue (buffer[p + 1], buffer[p]); // backwards
if ((buffer[p] & 0x80) > 0)
intValue -= 65536;
text.append (intValue + "\n");
@ -178,7 +180,7 @@ public class StoredVariables extends AbstractFile
else if (elementSize == 5)
{
if (hasValue (p))
text.append (HexFormatter.floatValue (buffer, p));
text.append (Utility.floatValue (buffer, p));
text.append ("\n");
}
p += elementSize;
@ -201,6 +203,7 @@ public class StoredVariables extends AbstractFile
for (int i = 0; i < 5; i++)
if (buffer[p + i] != 0)
return true;
return false;
}
@ -212,10 +215,10 @@ public class StoredVariables extends AbstractFile
StringBuffer text = new StringBuffer ();
text.append ("File length : " + HexFormatter.format4 (buffer.length));
int totalLength = Utility.intValue (buffer[0], buffer[1]);
int totalLength = Utility.getShort (buffer, 0);
text.append ("\nTotal length : " + HexFormatter.format4 (totalLength));
int varLength = Utility.intValue (buffer[2], buffer[3]);
int varLength = Utility.getShort (buffer, 2);
text.append ("\nVar length : " + HexFormatter.format4 (varLength));
int unknown = buffer[4] & 0xFF;
@ -232,14 +235,14 @@ public class StoredVariables extends AbstractFile
text.append ("\nArrays : \n\n");
while (ptr < totalLength + 5)
{
int offset = Utility.intValue (buffer[ptr + 2], buffer[ptr + 3]);
int offset = Utility.getShort (buffer, ptr + 2);
int dimensions = buffer[ptr + 4] & 0xFF;
int[] dimensionSizes = new int[dimensions];
int totalElements = 0;
for (int i = 0; i < dimensions; i++)
{
int p = i * 2 + 5 + ptr;
int elements = Utility.intValue (buffer[p + 1], buffer[p]);
int elements = Utility.intValue (buffer[p + 1], buffer[p]); // backwards!
dimensionSizes[dimensions - i - 1] = elements;
if (totalElements == 0)
totalElements = elements;

View File

@ -0,0 +1,734 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_BACKSPACE;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COLON;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_COMMA;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_CR;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_DOLLAR;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LEFT_BRACKET;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_LF;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_MINUS;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_PERCENT;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_QUOTE;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_RIGHT_BRACKET;
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
import static com.bytezone.diskbrowser.utilities.Utility.isControlCharacter;
import static com.bytezone.diskbrowser.utilities.Utility.isDigit;
import static com.bytezone.diskbrowser.utilities.Utility.isHighBitSet;
import static com.bytezone.diskbrowser.utilities.Utility.isLetter;
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleVariable;
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;;
// -----------------------------------------------------------------------------------//
public class SubLine implements ApplesoftConstants
// -----------------------------------------------------------------------------------//
{
SourceLine sourceLine;
byte[] buffer;
int startPtr;
int length;
String[] nextVariables;
String forVariable = "";
int equalsPosition; // used for aligning the equals sign
int endPosition; // not sure yet - possibly for aligning REMs
String functionArgument;
String functionName;
String callTarget;
private final List<Integer> gotoLines = new ArrayList<> ();
private final List<Integer> gosubLines = new ArrayList<> ();
private final List<String> variables = new ArrayList<> ();
private final List<String> functions = new ArrayList<> ();
private final List<String> arrays = new ArrayList<> ();
private final List<Integer> constantsInt = new ArrayList<> ();
private final List<Float> constantsFloat = new ArrayList<> ();
private final List<String> stringsText = new ArrayList<> ();
// ---------------------------------------------------------------------------------//
SubLine (SourceLine sourceLine, int offset, int length)
// ---------------------------------------------------------------------------------//
{
this.sourceLine = sourceLine;
this.startPtr = offset;
this.length = length;
this.buffer = sourceLine.buffer;
int ptr = startPtr;
byte firstByte = buffer[startPtr];
if (isToken (firstByte))
{
doToken (firstByte);
if (is (TOKEN_REM) || is (TOKEN_DATA)) // no further processing
return;
if (is (TOKEN_CALL))
ptr = startPtr + callTarget.length ();
else
ptr = startPtr + 1;
}
else
{
if (isDigit (firstByte)) // split IF xx THEN nnn
{
addXref (getLineNumber (buffer, startPtr), gotoLines);
return;
}
else if (isLetter (firstByte)) // variable name
setEqualsPosition ();
else if (isEndOfLine (firstByte)) // empty subline
return;
else // probably Beagle Bros 0D or 0A
System.out.printf ("%s unexpected bytes at line %5d:%n%s%n",
sourceLine.program.name, sourceLine.lineNumber,
HexFormatter.formatNoHeader (buffer, startPtr, length));
}
String var = "";
boolean inQuote = false;
boolean inFunction = false;
boolean inDefine = false;
int stringPtr = 0;
int endOfLine = startPtr + length - 1;
while (isEndOfLine (buffer[endOfLine])) // zero or colon
--endOfLine;
while (ptr <= endOfLine)
{
byte b = buffer[ptr++];
if (inDefine) // ignore the name and argument
{
if (b == TOKEN_EQUALS)
inDefine = false;
continue;
}
if (b == TOKEN_DEF)
{
inDefine = true;
continue;
}
if (b == TOKEN_FN)
{
assert !inDefine;
inFunction = true;
continue;
}
if (inQuote)
{
if (b == ASCII_QUOTE) // ignore strings
{
inQuote = false;
addString (stringPtr, ptr);
}
continue;
}
if (b == ASCII_QUOTE)
{
inQuote = true;
stringPtr = ptr;
continue;
}
if (isPossibleVariable (b) || isPossibleNumber (b))
{
if (var.isEmpty () && isPossibleNumber (b) && buffer[ptr - 2] == TOKEN_MINUS)
var = "-";
var += (char) b;
// allow for PRINT A$B$
if ((b == ASCII_DOLLAR || b == ASCII_PERCENT) // var name end
&& buffer[ptr] != ASCII_LEFT_BRACKET) // not an array
{
checkVar (var, b);
var = "";
}
}
else
{
if (inFunction)
{
checkFunction (var, b);
inFunction = false;
}
else
checkVar (var, b);
var = "";
}
}
if (inQuote)
addString (stringPtr, ptr); // unterminated string
else
checkVar (var, (byte) 0); // unprocessed variable or number
}
// ---------------------------------------------------------------------------------//
private boolean isEndOfLine (byte b)
// ---------------------------------------------------------------------------------//
{
return b == 0 || b == ASCII_COLON;
}
// ---------------------------------------------------------------------------------//
private void addString (int stringPtr, int ptr)
// ---------------------------------------------------------------------------------//
{
stringsText.add (new String (buffer, stringPtr - 1, ptr - stringPtr + 1));
}
// ---------------------------------------------------------------------------------//
private void checkFunction (String var, byte terminator)
// ---------------------------------------------------------------------------------//
{
// assert terminator == ASCII_LEFT_BRACKET;
if (!functions.contains (var))
functions.add (var);
}
// ---------------------------------------------------------------------------------//
private void checkVar (String var, byte terminator)
// ---------------------------------------------------------------------------------//
{
if (var.length () == 0)
return;
if (!isLetter ((byte) var.charAt (0)))
{
if (is (TOKEN_GOTO) || is (TOKEN_GOSUB) || is (TOKEN_ON) || is (TOKEN_ONERR))
return; // ignore line numbers
addNumber (var);
return;
}
if (is (TOKEN_DEF) && (var.equals (functionName) || var.equals (functionArgument)))
return;
if (terminator == ASCII_LEFT_BRACKET)
{
if (!arrays.contains (var))
arrays.add (var);
}
else if (!variables.contains (var))
variables.add (var);
}
// ---------------------------------------------------------------------------------//
private void doToken (byte b)
// ---------------------------------------------------------------------------------//
{
switch (b)
{
case TOKEN_FOR:
int p = startPtr + 1;
while (buffer[p] != TOKEN_EQUALS)
forVariable += (char) buffer[p++];
break;
case TOKEN_NEXT:
if (length == 2) // no variables
nextVariables = new String[0];
else
{
String varList = new String (buffer, startPtr + 1, length - 2);
nextVariables = varList.split (",");
}
break;
case TOKEN_LET:
setEqualsPosition ();
break;
case TOKEN_GOTO:
int targetLine = getLineNumber (buffer, startPtr + 1);
addXref (targetLine, gotoLines);
break;
case TOKEN_GOSUB:
targetLine = getLineNumber (buffer, startPtr + 1);
addXref (targetLine, gosubLines);
break;
case TOKEN_ON:
p = startPtr + 1;
int max = startPtr + length - 1;
while (p < max && buffer[p] != TOKEN_GOTO && buffer[p] != TOKEN_GOSUB)
p++;
switch (buffer[p++])
{
case TOKEN_GOSUB:
for (int destLine : getLineNumbers (buffer, p))
addXref (destLine, gosubLines);
break;
case TOKEN_GOTO:
for (int destLine : getLineNumbers (buffer, p))
addXref (destLine, gotoLines);
break;
default:
System.out.println ("GOTO / GOSUB not found");
}
break;
case TOKEN_ONERR:
if (buffer[startPtr + 1] == TOKEN_GOTO)
{
targetLine = getLineNumber (buffer, startPtr + 2);
addXref (targetLine, gotoLines);
}
break;
case TOKEN_CALL:
callTarget = getCallTarget ();
break;
case TOKEN_DEF:
byte[] lineBuffer = getBuffer ();
assert lineBuffer[0] == TOKEN_FN;
int leftBracket = getPosition (lineBuffer, 1, ASCII_LEFT_BRACKET);
int rightBracket = getPosition (lineBuffer, leftBracket + 1, ASCII_RIGHT_BRACKET);
functionName = new String (lineBuffer, 1, leftBracket - 1);
functionArgument =
new String (lineBuffer, leftBracket + 1, rightBracket - leftBracket - 1);
functions.add (functionName);
break;
case TOKEN_DATA:
for (String chunk : new String (getBuffer ()).split (","))
{
chunk = chunk.trim ();
if (chunk.isEmpty ())
continue;
b = (byte) chunk.charAt (0);
if (isPossibleNumber (b) || b == ASCII_MINUS)
{
if (!addNumber (chunk))
stringsText.add (chunk);
}
else
stringsText.add (chunk);
}
break;
}
}
// ---------------------------------------------------------------------------------//
private boolean addNumber (String var)
// ---------------------------------------------------------------------------------//
{
try
{
if (var.indexOf ('.') < 0) // no decimal point
{
int varInt = Integer.parseInt (var);
if (!constantsInt.contains (varInt))
constantsInt.add (varInt);
}
else
{
float varFloat = Float.parseFloat (var);
if (!constantsFloat.contains (varFloat))
constantsFloat.add (varFloat);
}
}
catch (NumberFormatException nfe)
{
return false;
}
return true;
}
// ---------------------------------------------------------------------------------//
private String getCallTarget ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
int ptr = startPtr + 1;
int max = startPtr + length - 1;
while (ptr < max)
{
byte b = buffer[ptr++];
if (isToken (b))
text.append (tokens[b & 0x7F]);
else if (b == ASCII_COMMA) // end of call target
break;
else
text.append ((char) b);
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private int getPosition (byte[] buffer, int start, byte value)
// ---------------------------------------------------------------------------------//
{
for (int i = start; i < buffer.length; i++)
if (buffer[i] == value)
return i;
return -1;
}
// ---------------------------------------------------------------------------------//
private void addXref (int targetLine, List<Integer> list)
// ---------------------------------------------------------------------------------//
{
if (!list.contains (targetLine))
list.add (targetLine);
}
// ---------------------------------------------------------------------------------//
private List<Integer> getLineNumbers (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
List<Integer> lineNumbers = new ArrayList<> ();
int start = ptr;
while (ptr < buffer.length && buffer[ptr] != 0 && buffer[ptr] != ASCII_COLON)
ptr++;
String s = new String (buffer, start, ptr - start);
String[] chunks = s.split (",");
try
{
for (String chunk : chunks)
lineNumbers.add (Integer.parseInt (chunk));
}
catch (NumberFormatException e)
{
System.out.printf ("NFE2: %s%n", s);
}
return lineNumbers;
}
// ---------------------------------------------------------------------------------//
private int getLineNumber (byte[] buffer, int ptr)
// ---------------------------------------------------------------------------------//
{
int lineNumber = 0;
while (ptr < buffer.length && isDigit (buffer[ptr]))
lineNumber = lineNumber * 10 + (buffer[ptr++] & 0xFF) - 0x30;
return lineNumber;
}
// ---------------------------------------------------------------------------------//
boolean isImpliedGoto ()
// ---------------------------------------------------------------------------------//
{
return (isDigit (buffer[startPtr]));
}
// Record the position of the equals sign so it can be aligned with adjacent lines.
// Illegal lines could have a variable name with no equals sign.
// ---------------------------------------------------------------------------------//
private void setEqualsPosition ()
// ---------------------------------------------------------------------------------//
{
int p = startPtr;
int max = startPtr + length;
while (++p < max)
if (buffer[p] == TOKEN_EQUALS)
{
String expandedLine = toString ();
equalsPosition = expandedLine.indexOf ('=');
endPosition = expandedLine.length ();
if (expandedLine.endsWith (":"))
endPosition--;
break;
}
}
// ---------------------------------------------------------------------------------//
boolean isJoinableRem ()
// ---------------------------------------------------------------------------------//
{
return is (TOKEN_REM) && isNotFirst ();
}
// ---------------------------------------------------------------------------------//
boolean isFirst ()
// ---------------------------------------------------------------------------------//
{
return (sourceLine.linePtr + 4) == startPtr;
}
// ---------------------------------------------------------------------------------//
boolean isNotFirst ()
// ---------------------------------------------------------------------------------//
{
return !isFirst ();
}
// ---------------------------------------------------------------------------------//
boolean is (byte token)
// ---------------------------------------------------------------------------------//
{
return buffer[startPtr] == token;
}
// ---------------------------------------------------------------------------------//
boolean has (byte token)
// ---------------------------------------------------------------------------------//
{
int ptr = startPtr;
int max = startPtr + length;
while (++ptr < max)
if (buffer[ptr] == token)
return true;
return false;
}
// ---------------------------------------------------------------------------------//
boolean isEmpty ()
// ---------------------------------------------------------------------------------//
{
return length == 1 && buffer[startPtr] == 0;
}
// ---------------------------------------------------------------------------------//
boolean containsToken ()
// ---------------------------------------------------------------------------------//
{
// ignore first byte, check the rest for tokens
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
if (isToken (buffer[p]))
return true;
return false;
}
// ---------------------------------------------------------------------------------//
boolean containsControlChars ()
// ---------------------------------------------------------------------------------//
{
for (int p = startPtr + 1, max = startPtr + length; p < max; p++)
{
int c = buffer[p] & 0xFF;
if (c == 0)
break;
if (c < 32)
return true;
}
return false;
}
// ---------------------------------------------------------------------------------//
void addFormattedRem (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
int ptr = startPtr + 1;
int max = startPtr + length - 1;
if (isFirst ())
{
if (containsBackspaces (ptr, max)) // probably going to erase the line number
{
// apple format uses left-justified line numbers so the length varies
text.setLength (0);
text.append (String.format (" %d REM ", sourceLine.lineNumber)); // mimic apple
}
else
text.append (" REM ");
}
else
text.append ("REM ");
while (ptr < max)
{
switch (buffer[ptr])
{
case ASCII_BACKSPACE:
if (text.length () > 0)
text.deleteCharAt (text.length () - 1);
break;
case ASCII_CR:
text.append ("\n");
break;
case ASCII_LF:
int indent = getIndent (text);
text.append ("\n");
for (int i = 0; i < indent; i++)
text.append (" ");
break;
default:
text.append ((char) buffer[ptr]); // do not mask with 0xFF
}
ptr++;
}
}
// ---------------------------------------------------------------------------------//
private boolean containsBackspaces (int ptr, int max)
// ---------------------------------------------------------------------------------//
{
while (ptr < max)
if (buffer[ptr++] == ASCII_BACKSPACE)
return true;
return false;
}
// ---------------------------------------------------------------------------------//
public byte[] getBuffer ()
// ---------------------------------------------------------------------------------//
{
int len = length - 1;
if (buffer[startPtr + len] == ASCII_COLON || buffer[startPtr + len] == 0)
len--;
byte[] buffer2 = new byte[len];
System.arraycopy (buffer, startPtr + 1, buffer2, 0, buffer2.length);
return buffer2;
}
// ---------------------------------------------------------------------------------//
boolean isToken (byte b)
// ---------------------------------------------------------------------------------//
{
return isHighBitSet (b);
}
// ---------------------------------------------------------------------------------//
List<String> getVariables ()
// ---------------------------------------------------------------------------------//
{
return variables;
}
// ---------------------------------------------------------------------------------//
List<String> getFunctions ()
// ---------------------------------------------------------------------------------//
{
return functions;
}
// ---------------------------------------------------------------------------------//
List<String> getArrays ()
// ---------------------------------------------------------------------------------//
{
return arrays;
}
// ---------------------------------------------------------------------------------//
List<Integer> getGotoLines ()
// ---------------------------------------------------------------------------------//
{
return gotoLines;
}
// ---------------------------------------------------------------------------------//
List<Integer> getGosubLines ()
// ---------------------------------------------------------------------------------//
{
return gosubLines;
}
// ---------------------------------------------------------------------------------//
List<Integer> getConstantsInt ()
// ---------------------------------------------------------------------------------//
{
return constantsInt;
}
// ---------------------------------------------------------------------------------//
List<Float> getConstantsFloat ()
// ---------------------------------------------------------------------------------//
{
return constantsFloat;
}
// ---------------------------------------------------------------------------------//
List<String> getStringsText ()
// ---------------------------------------------------------------------------------//
{
return stringsText;
}
// ---------------------------------------------------------------------------------//
StringBuilder toStringBuilder ()
// ---------------------------------------------------------------------------------//
{
StringBuilder line = new StringBuilder ();
// All sublines end with 0 or : except IF lines that are split into two
int max = startPtr + length - 1;
if (buffer[max] == 0)
--max;
if (isImpliedGoto () && !ApplesoftBasicProgram.basicPreferences.showThen)
line.append ("GOTO ");
for (int p = startPtr; p <= max; p++)
{
byte b = buffer[p];
if (isToken (b))
{
if (line.length () > 0 && line.charAt (line.length () - 1) != ' ')
line.append (' ');
int val = b & 0x7F;
if (b != TOKEN_THEN || ApplesoftBasicProgram.basicPreferences.showThen)
line.append (ApplesoftConstants.tokens[val] + " ");
}
// else if (Utility.isControlCharacter (b))
// line.append (ApplesoftBasicProgram.basicPreferences.showCaret
// ? "^" + (char) (b + 64) : "?");
else if (!isControlCharacter (b))
line.append ((char) b);
}
return line;
}
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
return toStringBuilder ().toString ();
}
}

View File

@ -46,6 +46,7 @@ public class TextBuffer
StringBuilder text = new StringBuilder ();
text.append ("Record length : " + reclen + "\n");
text.append ("Buffer length : " + buffer.length + "\n");
text.append ("First record : " + firstRecNo + "\n\n");
text.append (HexFormatter.format (buffer, 0, buffer.length) + "\n");

197
src/com/bytezone/diskbrowser/applefile/TextFile.java Executable file → Normal file
View File

@ -1,17 +1,12 @@
package com.bytezone.diskbrowser.applefile;
import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.gui.TextPreferences;
// -----------------------------------------------------------------------------------//
public class TextFile extends AbstractFile
public abstract class TextFile extends AbstractFile
// -----------------------------------------------------------------------------------//
{
private int recordLength; // prodos aux
private List<TextBuffer> buffers; // only used if it is a Prodos text file
private int eof;
private boolean prodosFile;
static TextPreferences textPreferences; // set by MenuHandler
// ---------------------------------------------------------------------------------//
public TextFile (String name, byte[] buffer)
@ -21,189 +16,9 @@ public class TextFile extends AbstractFile
}
// ---------------------------------------------------------------------------------//
public TextFile (String name, byte[] buffer, int auxType, int eof)
public static void setTextPreferences (TextPreferences textPreferences)
// ---------------------------------------------------------------------------------//
{
this (name, buffer);
this.eof = eof;
recordLength = auxType;
prodosFile = true;
TextFile.textPreferences = textPreferences;
}
// ---------------------------------------------------------------------------------//
public TextFile (String name, List<TextBuffer> buffers, int auxType, int eof)
// ---------------------------------------------------------------------------------//
{
super (name, null);
this.buffers = buffers;
this.eof = eof;
recordLength = auxType;
prodosFile = true;
}
// ---------------------------------------------------------------------------------//
@Override
public String getHexDump ()
// ---------------------------------------------------------------------------------//
{
if (buffers == null)
return (super.getHexDump ());
StringBuilder text = new StringBuilder ();
for (TextBuffer tb : buffers)
{
for (int i = 0, rec = 0; i < tb.buffer.length; i += tb.reclen, rec++)
{
text.append ("\nRecord #" + (tb.firstRecNo + rec) + "\n");
text.append (HexFormatter.format (tb.buffer, i, tb.reclen) + "\n");
}
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
@Override
public String getText ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append ("Name : " + name + "\n");
if (prodosFile)
{
text.append (String.format ("Record length : %,8d%n", recordLength));
text.append (String.format ("End of file : %,8d%n", eof));
}
else
text.append (String.format ("End of file : %,8d%n", buffer.length));
text.append ("\n");
// check whether file is spread over multiple buffers
if (buffers != null)
return treeFileText (text); // calls knownLength()
// check whether the record length is known
if (recordLength == 0)
return unknownLength (text);
text.append ("Offset Record# Text values\n");
text.append (
"------ ------- -------------------------------------------------------\n");
return knownLength (text, 0).toString ();
}
// ---------------------------------------------------------------------------------//
private String treeFileText (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
text.append (" Offset Record# Text values\n");
text.append (
"---------- ------- -------------------------------------------------------\n");
for (TextBuffer tb : buffers)
{
buffer = tb.buffer;
text = knownLength (text, tb.firstRecNo);
}
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private StringBuilder knownLength (StringBuilder text, int recNo)
// ---------------------------------------------------------------------------------//
{
for (int ptr = 0; ptr < buffer.length; ptr += recordLength)
{
if (buffer[ptr] == 0)
{
recNo++;
continue;
}
int len = buffer.length - ptr;
int bytes = len < recordLength ? len : recordLength;
while (buffer[ptr + bytes - 1] == 0)
bytes--;
if ((buffer[ptr + bytes - 1] & 0x7F) == 0x0D) // ignore CR
bytes--;
String line = HexFormatter.getString (buffer, ptr, bytes);
line = line.replaceAll ("\\n", "\n ");
text.append (
String.format ("%,10d %,8d %s%n", recNo * recordLength, recNo++, line));
}
return text;
}
// ---------------------------------------------------------------------------------//
private String unknownLength (StringBuilder text)
// ---------------------------------------------------------------------------------//
{
int nulls = 0;
int ptr = 0;
int size = buffer.length;
int lastVal = 0;
boolean newFormat = true;
boolean showAllOffsets = true;
if (newFormat)
{
text.append (" Offset Text values\n");
text.append ("---------- -------------------------------------------------------"
+ "-------------------\n");
if (buffer.length == 0)
return text.toString ();
if (buffer[0] != 0)
text.append (String.format ("%,10d ", ptr));
}
int gcd = 0;
while (ptr < size)
{
int val = buffer[ptr++] & 0x7F; // strip hi-order bit
if (val == 0)
++nulls;
else if (val == 0x0D) // carriage return
text.append ("\n");
else
{
if (nulls > 0)
{
if (newFormat)
text.append (String.format ("%,10d ", ptr - 1));
else
text.append ("\nNew record at : " + (ptr - 1) + "\n");
nulls = 0;
gcd = gcd == 0 ? ptr - 1 : gcd (gcd, ptr - 1);
}
else if (lastVal == 0x0D && newFormat)
if (showAllOffsets)
text.append (String.format ("%,10d ", ptr - 1));
else
text.append (" ");
text.append ((char) val);
}
lastVal = val;
}
if (gcd > 0)
text.append (String.format ("%nGCD: %,d", gcd));
else if (text.length () > 0 && text.charAt (text.length () - 1) == '\n')
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
// ---------------------------------------------------------------------------------//
private int gcd (int a, int b)
// ---------------------------------------------------------------------------------//
{
return a == 0 ? b : gcd (b % a, a);
}
}
}

View File

@ -0,0 +1,351 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_DOLLAR;
import static com.bytezone.diskbrowser.utilities.Utility.ASCII_PERCENT;
import static com.bytezone.diskbrowser.utilities.Utility.getIndent;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.bytezone.diskbrowser.gui.BasicPreferences;
// -----------------------------------------------------------------------------------//
public class UserBasicFormatter extends BasicFormatter
// -----------------------------------------------------------------------------------//
{
private static final Pattern dimPattern =
Pattern.compile ("[A-Z][A-Z0-9]*[$%]?\\([0-9]+(,[0-9]+)*\\)[,:]?");
private static final int INDENT_SIZE = 2;
private static final String EIGHT_SPACES = " ";
private static final String FOUR_SPACES = " ";
private static boolean FORCE = true;
private static boolean NO_FORCE = false;
// ---------------------------------------------------------------------------------//
public UserBasicFormatter (ApplesoftBasicProgram program,
BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
super (program, basicPreferences);
}
// ---------------------------------------------------------------------------------//
@Override
public void append (StringBuilder fullText)
// ---------------------------------------------------------------------------------//
{
boolean insertBlankLine = false;
int baseOffset = 7; // 5 digit line number + 2 spaces
Stack<String> loopVariables = new Stack<> ();
Alignment alignment = new Alignment ();
for (SourceLine line : sourceLines)
{
StringBuilder text = new StringBuilder (String.format ("%5d", (line.lineNumber)));
int indentLevel = loopVariables.size (); // each full line starts at the loop indent
int ifIndent = 0; // IF statement(s) limit back indentation by NEXT
for (SubLine subline : line.sublines)
{
// Allow empty statements (caused by a single colon)
if (subline.isEmpty ())
continue;
// A REM statement might conceal an assembler routine
// - see P.CREATE on Diags2E.DSK
if (subline.is (TOKEN_REM) && subline.containsToken ())
{
int address = getLoadAddress () + subline.startPtr + 1; // skip the REM token
fullText.append (text + String.format ("REM - Inline assembler @ $%02X (%d)%n",
address, address));
String padding = " ".substring (0, text.length () + 2);
for (String asm : getRemAssembler (subline))
fullText.append (padding + asm + NEWLINE);
continue;
}
// Beagle Bros often have multiline REM statements
if (subline.is (TOKEN_REM) && basicPreferences.formatRem
&& subline.containsControlChars ())
{
subline.addFormattedRem (text);
fullText.append (text + NEWLINE);
continue;
}
// Reduce the indent by each NEXT, but only as far as the IF indent allows
if (subline.is (TOKEN_NEXT))
{
popLoopVariables (loopVariables, subline);
indentLevel = Math.max (ifIndent, loopVariables.size ());
}
// Are we joining REM lines with the previous subline?
if (joinableRem (subline))
{
// Join this REM statement to the previous line, so no indenting
fullText.deleteCharAt (fullText.length () - 1); // remove newline
fullText.append (" ");
}
else // ... otherwise do all the indenting
{
// Align assign statements if required
if (basicPreferences.alignAssign)
alignEqualsPosition (subline, alignment);
int column = indentLevel * INDENT_SIZE + baseOffset;
while (text.length () < column)
text.append (" ");
}
// Add the current text, then reset it
String lineText = alignment.getAlignedText (subline);
if (subline.is (TOKEN_DATA) && basicPreferences.deleteExtraDataSpace)
lineText = lineText.replaceFirst ("DATA +", "DATA "); // regex
// Check for a wrappable REM/DATA/DIM statement
// (see SEA BATTLE on DISK283.DSK)
int inset = Math.max (text.length (), getIndent (fullText)) + 1;
if (subline.is (TOKEN_REM) && lineText.length () > basicPreferences.wrapRemAt)
{
List<String> lines =
splitLine (lineText, basicPreferences.wrapRemAt, ' ', FORCE);
addSplitLines (lines, text, inset);
}
else if (subline.is (TOKEN_DATA)
&& lineText.length () > basicPreferences.wrapDataAt)
{
List<String> lines =
splitLine (lineText, basicPreferences.wrapDataAt, ',', FORCE);
addSplitLines (lines, text, inset);
}
else if (subline.is (TOKEN_PRINT)
&& lineText.length () > basicPreferences.wrapPrintAt)
{
List<String> lines =
splitLine (lineText, basicPreferences.wrapDataAt, ';', NO_FORCE);
addSplitLines (lines, text, inset);
}
else if (subline.is (TOKEN_DIM) && basicPreferences.splitDim)
{
List<String> lines = splitDim (lineText);
addSplitLines (lines, text, inset);
}
else
text.append (lineText);
if (subline == alignment.lastSubLine)
alignment.reset ();
fullText.append (text);
fullText.append (NEWLINE);
text.setLength (0);
// Calculate indent changes that take effect after the current subline
if (subline.is (TOKEN_IF))
ifIndent = ++indentLevel;
else if (subline.is (TOKEN_FOR))
{
String latestLoopVar = loopVariables.size () > 0 ? loopVariables.peek () : "";
if (!subline.forVariable.equals (latestLoopVar)) // don't add repeated loop
{
loopVariables.push (subline.forVariable);
++indentLevel;
}
}
else if (basicPreferences.blankAfterReturn && subline.is (TOKEN_RETURN)
&& subline.isFirst ())
insertBlankLine = true;
}
if (insertBlankLine)
{
fullText.append (NEWLINE);
insertBlankLine = false;
}
}
}
// ---------------------------------------------------------------------------------//
private List<String> splitLine (String line, int wrapLength, char breakChar,
boolean force)
// ---------------------------------------------------------------------------------//
{
int spaceAt = 0;
while (spaceAt < line.length () && line.charAt (spaceAt) != ' ')
++spaceAt;
String indent = spaceAt < 8 ? EIGHT_SPACES.substring (0, spaceAt + 1) : EIGHT_SPACES;
List<String> lines = new ArrayList<> ();
while (line.length () > wrapLength)
{
int breakAt = wrapLength - 1;
while (breakAt > spaceAt && line.charAt (breakAt) != breakChar)
--breakAt;
if (breakAt <= spaceAt)
break;
lines.add (line.substring (0, breakAt + 1)); // keep breakChar at end
line = indent + line.substring (breakAt + 1).trim ();
}
if (force)
while (line.length () > wrapLength) // no breakChars found
{
lines.add (line.substring (0, wrapLength));
line = indent + line.substring (wrapLength);
}
lines.add (line);
return lines;
}
// ---------------------------------------------------------------------------------//
private List<String> splitDim (String line)
// ---------------------------------------------------------------------------------//
{
List<String> lines = new ArrayList<> ();
Matcher m = dimPattern.matcher (line);
while (m.find ())
lines.add (FOUR_SPACES + m.group ());
if (lines.size () > 0)
lines.set (0, "DIM " + lines.get (0).trim ());
return lines;
}
// ---------------------------------------------------------------------------------//
private void addSplitLines (List<String> lines, StringBuilder text, int indent)
// ---------------------------------------------------------------------------------//
{
boolean first = true;
for (String line : lines)
if (first)
{
first = false;
text.append (line);
}
else
text.append (
"\n ".substring (0, indent) + line);
}
// Decide whether the current subline needs to be aligned on its equals sign. If so,
// and the column hasn't been calculated, read ahead to find the highest position.
// ---------------------------------------------------------------------------------//
private void alignEqualsPosition (SubLine subline, Alignment alignment)
// ---------------------------------------------------------------------------------//
{
if (subline.equalsPosition == 0)
{
alignment.reset ();
return;
}
if (alignment.equalsPosition == 0)
findHighest (subline, alignment);
}
// The IF processing is so that any assignment that is being aligned doesn't continue
// to the next full line (because the indentation has changed).
// ---------------------------------------------------------------------------------//
private void findHighest (SubLine startSubline, Alignment alignment)
// ---------------------------------------------------------------------------------//
{
boolean started = false;
alignment.setFirst (startSubline);
outerLoop: for (int i = sourceLines.indexOf (startSubline.sourceLine); i < sourceLines
.size (); i++)
{
boolean precededByIf = false;
for (SubLine subline : sourceLines.get (i).sublines)
{
if (started)
{
// Stop when we come to a subline without an equals sign (joinable REMs
// can be ignored)
if (subline.equalsPosition == 0 && !joinableRem (subline))
break outerLoop;
if (subline.equalsPosition > 0)
alignment.check (subline);
}
else if (subline == startSubline)
started = true;
else if (subline.is (TOKEN_IF))
precededByIf = true;
}
if (started && precededByIf) // sublines of IF have now finished
break; // don't continue with following SourceLine
}
}
// ---------------------------------------------------------------------------------//
private boolean joinableRem (SubLine subline)
// ---------------------------------------------------------------------------------//
{
return subline.isJoinableRem () && !basicPreferences.splitRem;
}
// ---------------------------------------------------------------------------------//
private void popLoopVariables (Stack<String> loopVariables, SubLine subline)
// ---------------------------------------------------------------------------------//
{
if (subline.nextVariables.length == 0) // naked NEXT
{
if (loopVariables.size () > 0)
loopVariables.pop ();
}
else
for (String variable : subline.nextVariables) // e.g. NEXT X,Y,Z
while (loopVariables.size () > 0)
if (sameVariable (variable, loopVariables.pop ()))
break;
}
// ---------------------------------------------------------------------------------//
private boolean sameVariable (String v1, String v2)
// ---------------------------------------------------------------------------------//
{
return getUniqueName (v1).equals (getUniqueName (v2));
}
// ---------------------------------------------------------------------------------//
private String getUniqueName (String symbol)
// ---------------------------------------------------------------------------------//
{
int ptr = symbol.length () - 1;
if (symbol.charAt (ptr) == ASCII_DOLLAR // string
|| symbol.charAt (ptr) == ASCII_PERCENT) // integer
ptr--;
return (ptr <= 1) ? symbol : symbol.substring (0, 2) + symbol.substring (ptr + 1);
}
// A REM statement might conceal an assembler routine
// ---------------------------------------------------------------------------------//
private String[] getRemAssembler (SubLine subline)
// ---------------------------------------------------------------------------------//
{
AssemblerProgram program = new AssemblerProgram ("REM assembler",
subline.getBuffer (), getLoadAddress () + subline.startPtr + 1);
return program.getAssembler ().split (NEWLINE);
}
}

View File

@ -6,7 +6,6 @@ import com.bytezone.diskbrowser.visicalc.Sheet;
public class VisicalcFile extends AbstractFile
// -----------------------------------------------------------------------------------//
{
private static boolean debug;
private Sheet sheet;
// ---------------------------------------------------------------------------------//
@ -27,25 +26,11 @@ public class VisicalcFile extends AbstractFile
StringBuilder text = new StringBuilder ();
text.append ("Visicalc : " + name + "\n\n");
text.append (sheet.getTextDisplay (debug));
text.append (sheet.getTextDisplay (showDebugText));
return text.toString ();
}
// ---------------------------------------------------------------------------------//
public static void setDefaultDebug (boolean value)
// ---------------------------------------------------------------------------------//
{
debug = value;
}
// ---------------------------------------------------------------------------------//
public static void setDebug (boolean value)
// ---------------------------------------------------------------------------------//
{
debug = value;
}
// ---------------------------------------------------------------------------------//
public static boolean isVisicalcFile (byte[] buffer)
// ---------------------------------------------------------------------------------//

View File

@ -0,0 +1,460 @@
package com.bytezone.diskbrowser.applefile;
import static com.bytezone.diskbrowser.utilities.Utility.isPossibleNumber;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.bytezone.diskbrowser.gui.BasicPreferences;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class XrefFormatter extends BasicFormatter
// -----------------------------------------------------------------------------------//
{
private static final String underline =
"----------------------------------------------------"
+ "----------------------------------------------";
private final Map<Integer, List<Integer>> gotoLines = new TreeMap<> ();
private final Map<Integer, List<Integer>> gosubLines = new TreeMap<> ();
private final Map<Integer, List<Integer>> constantsInt = new TreeMap<> ();
private final Map<Float, List<Integer>> constantsFloat = new TreeMap<> ();
private final Map<String, List<Integer>> callLines = new TreeMap<> ();
private final Map<String, List<Integer>> symbolLines = new TreeMap<> ();
private final Map<String, List<Integer>> functionLines = new TreeMap<> ();
private final Map<String, List<Integer>> arrayLines = new TreeMap<> ();
private final Map<String, List<String>> uniqueSymbols = new TreeMap<> ();
private final Map<String, List<String>> uniqueArrays = new TreeMap<> ();
private final List<Integer> stringsLine = new ArrayList<> ();
private final List<String> stringsText = new ArrayList<> ();
private final String formatLeft;
private final String formatLineNumber;
private final String formatRight;
private final int longestVarName;
private final int maxDigits;
// ---------------------------------------------------------------------------------//
public XrefFormatter (ApplesoftBasicProgram program, BasicPreferences basicPreferences)
// ---------------------------------------------------------------------------------//
{
super (program, basicPreferences);
for (SourceLine sourceLine : program.getSourceLines ())
checkXref (sourceLine);
longestVarName = getLongestName ();
maxDigits = getMaxDigits ();
// build format strings based on existing line numbers and variable names
formatLeft = longestVarName > 7 ? "%-" + longestVarName + "." + longestVarName + "s "
: "%-7.7s ";
formatRight = formatLeft.replace ("-", "");
formatLineNumber = "%" + maxDigits + "d ";
}
// ---------------------------------------------------------------------------------//
void checkXref (SourceLine line)
// ---------------------------------------------------------------------------------//
{
for (SubLine subline : line.sublines)
{
for (String symbol : subline.getVariables ())
checkVar (symbol, line.lineNumber, symbolLines, uniqueSymbols);
for (String symbol : subline.getArrays ())
checkVar (symbol, line.lineNumber, arrayLines, uniqueArrays);
for (String symbol : subline.getFunctions ())
checkFunction (line.lineNumber, symbol);
for (int targetLine : subline.getGosubLines ())
addNumberInt (line.lineNumber, targetLine, gosubLines);
for (int targetLine : subline.getGotoLines ())
addNumberInt (line.lineNumber, targetLine, gotoLines);
for (int num : subline.getConstantsInt ())
addNumberInt (line.lineNumber, num, constantsInt);
for (float num : subline.getConstantsFloat ())
addNumberFloat (line.lineNumber, num, constantsFloat);
if (subline.callTarget != null)
addString (line.lineNumber, subline.callTarget, callLines);
for (String s : subline.getStringsText ())
{
stringsText.add (s);
stringsLine.add (line.lineNumber);
}
}
}
// ---------------------------------------------------------------------------------//
@Override
public void append (StringBuilder fullText)
// ---------------------------------------------------------------------------------//
{
if (basicPreferences.showSymbols)
{
if (!symbolLines.isEmpty ())
showSymbolsLeft (fullText, symbolLines, "Var");
if (!arrayLines.isEmpty ())
showSymbolsLeft (fullText, arrayLines, "Array");
}
if (basicPreferences.showDuplicateSymbols)
{
if (!uniqueSymbols.isEmpty ())
showDuplicates (fullText, uniqueSymbols, "Var");
if (!uniqueArrays.isEmpty ())
showDuplicates (fullText, uniqueArrays, "Array");
}
if (basicPreferences.showFunctions && !functionLines.isEmpty ())
showSymbolsLeft (fullText, functionLines, "Fnction");
if (basicPreferences.showConstants)
{
if (!constantsInt.isEmpty ())
showSymbolsRightInt (fullText, constantsInt, "Integer");
if (!constantsFloat.isEmpty ())
showSymbolsRightFloat (fullText, constantsFloat, "Float");
if (stringsLine.size () > 0)
{
heading (fullText, formatRight, "Line", "String");
for (int i = 0; i < stringsLine.size (); i++)
fullText.append (String.format (formatRight + "%s%n", stringsLine.get (i),
stringsText.get (i)));
}
}
if (basicPreferences.showGosubGoto)
{
if (!gosubLines.isEmpty ())
showSymbolsRight (fullText, gosubLines, "GOSUB");
if (!gotoLines.isEmpty ())
showSymbolsRight (fullText, gotoLines, "GOTO");
}
if (basicPreferences.showCalls && !callLines.isEmpty ())
showSymbolsLeftRight (fullText, callLines, " CALL");
}
// ---------------------------------------------------------------------------------//
private int getMaxDigits ()
// ---------------------------------------------------------------------------------//
{
if (sourceLines.size () == 0)
return 4; // anything non-zero
SourceLine lastLine = sourceLines.get (sourceLines.size () - 1);
return (lastLine.lineNumber + "").length ();
}
// ---------------------------------------------------------------------------------//
private int getLongestName ()
// ---------------------------------------------------------------------------------//
{
int longestName = getLongestName (symbolLines, 0);
longestName = getLongestName (arrayLines, longestName);
longestName = getLongestName (functionLines, longestName);
return longestName;
}
// ---------------------------------------------------------------------------------//
private int getLongestName (Map<String, List<Integer>> map, int longestName)
// ---------------------------------------------------------------------------------//
{
for (String symbol : map.keySet ())
if (symbol.length () > longestName)
longestName = symbol.length ();
return longestName;
}
// ---------------------------------------------------------------------------------//
private void heading (StringBuilder fullText, String format, String... heading)
// ---------------------------------------------------------------------------------//
{
if (fullText.charAt (fullText.length () - 2) != '\n')
fullText.append (NEWLINE);
fullText.append (String.format (format, underline));
fullText.append (underline);
fullText.append (NEWLINE);
fullText.append (String.format (format, heading[0]));
if (heading.length == 1)
fullText.append ("Line numbers");
else
fullText.append (heading[1]);
fullText.append (NEWLINE);
fullText.append (String.format (format, underline));
fullText.append (underline);
fullText.append (NEWLINE);
}
// ---------------------------------------------------------------------------------//
private void showDuplicates (StringBuilder fullText, Map<String, List<String>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
boolean headingShown = false;
for (String key : map.keySet ())
{
List<String> usage = map.get (key);
if (usage.size () > 1)
{
if (!headingShown)
{
headingShown = true;
heading (fullText, formatLeft, heading, "Duplicate Names");
}
String line = usage.toString ();
line = line.substring (1, line.length () - 1);
fullText.append (String.format ("%-6s %s%n", key, line));
}
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsLeft (StringBuilder fullText, Map<String, List<Integer>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatLeft, heading);
for (String symbol : map.keySet ()) // left-justify strings
{
if (symbol.length () <= 7)
appendLineNumbers (fullText, String.format (formatLeft, symbol),
map.get (symbol));
else
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsLeftRight (StringBuilder fullText,
Map<String, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatLeft, heading);
for (String symbol : map.keySet ()) // left-justify strings
{
if (isNumeric (symbol))
appendLineNumbers (fullText, String.format (formatRight, symbol),
map.get (symbol));
else if (symbol.length () <= 7)
appendLineNumbers (fullText, String.format (formatLeft, symbol),
map.get (symbol));
else
appendLineNumbers (fullText, symbol + " ", map.get (symbol));
}
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRight (StringBuilder fullText, Map<Integer, List<Integer>> map,
String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (Integer symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRightInt (StringBuilder fullText,
Map<Integer, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (int symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private void showSymbolsRightFloat (StringBuilder fullText,
Map<Float, List<Integer>> map, String heading)
// ---------------------------------------------------------------------------------//
{
heading (fullText, formatRight, heading);
for (float symbol : map.keySet ()) // right-justify integers
appendLineNumbers (fullText, String.format (formatRight, symbol), map.get (symbol));
}
// ---------------------------------------------------------------------------------//
private boolean isNumeric (String value)
// ---------------------------------------------------------------------------------//
{
byte[] bytes = value.getBytes ();
int start = value.charAt (0) == Utility.ASCII_MINUS ? 1 : 0;
for (int i = start; i < bytes.length; i++)
if (!isPossibleNumber (bytes[i]))
return false;
return true;
}
// ---------------------------------------------------------------------------------//
private void appendLineNumbers (StringBuilder fullText, String symbol,
List<Integer> lineNumbers)
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (symbol);
for (int lineNo : lineNumbers)
{
if (text.length () > underline.length () - maxDigits + longestVarName)
{
fullText.append (text);
fullText.append (NEWLINE);
text.setLength (0);
text.append (String.format (formatRight, ""));
}
text.append (String.format (formatLineNumber, lineNo));
}
if (text.length () > longestVarName + 3)
fullText.append (text + "\n");
}
// ---------------------------------------------------------------------------------//
private void checkVar (String var, int lineNumber, Map<String, List<Integer>> map,
Map<String, List<String>> unique)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (var);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (var, lines);
}
if (lines.size () == 0)
lines.add (lineNumber);
else
{
int lastLine = lines.get (lines.size () - 1);
if (lastLine != lineNumber)
lines.add (lineNumber);
}
checkUniqueName (var, unique);
}
// ---------------------------------------------------------------------------------//
private void checkFunction (int sourceLine, String var)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = functionLines.get (var);
if (lines == null)
{
lines = new ArrayList<> ();
functionLines.put (var, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addNumberInt (int sourceLine, Integer key, Map<Integer, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addNumberFloat (int sourceLine, Float key, Map<Float, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addString (int sourceLine, String key, Map<String, List<Integer>> map)
// ---------------------------------------------------------------------------------//
{
List<Integer> lines = map.get (key);
if (lines == null)
{
lines = new ArrayList<> ();
map.put (key, lines);
}
addLine (lines, sourceLine);
}
// ---------------------------------------------------------------------------------//
private void addLine (List<Integer> lines, int lineNumber)
// ---------------------------------------------------------------------------------//
{
if (lines.size () == 0)
lines.add (lineNumber);
else
{
int lastLine = lines.get (lines.size () - 1);
if (lastLine != lineNumber)
lines.add (lineNumber);
}
}
// ---------------------------------------------------------------------------------//
private void checkUniqueName (String symbol, Map<String, List<String>> map)
// ---------------------------------------------------------------------------------//
{
String uniqueName = getUniqueName (symbol);
List<String> usage = map.get (uniqueName);
if (usage == null)
{
usage = new ArrayList<> ();
map.put (uniqueName, usage);
}
if (!usage.contains (symbol))
usage.add (symbol);
}
// ---------------------------------------------------------------------------------//
private String getUniqueName (String symbolName)
// ---------------------------------------------------------------------------------//
{
int ptr = symbolName.length () - 1;
if (symbolName.charAt (ptr) == Utility.ASCII_DOLLAR // string
|| symbolName.charAt (ptr) == Utility.ASCII_PERCENT) // integer
ptr--;
return (ptr <= 1) ? symbolName
: symbolName.substring (0, 2) + symbolName.substring (ptr + 1);
}
}

View File

@ -22,10 +22,19 @@
0035 YSAV1
0036 CSWL
0037 CSHW
0044 A5L - volume number?
003D A1H
003C A1L
003F A2H
003E A2L
0041 A3H
0040 A3L
0043 A4H
0042 A4L
0045 A5H
0044 A5L
004E RND-LO
004F RND-HI
0050 LINNUM
0050 LINNUM line number, unsigned word
0067 Basic program address LO
0068 Basic program address HI
0069 Basic variables address LO
@ -52,8 +61,48 @@
0200 Input buffer
03D0 Applesoft warm start
03EA VECT
A56E catalog routine
03F2 RST: Control-reset vector
03F3 RST: Control-reset vector
03F4 RST: Control-reset checksum (EOR #$A5)
03FB NMI: Non-Maskable Interrupt vector
03FC NMI: Non-Maskable Interrupt vector
03F8 USR: user vector (Control-Y)
03F9 USR: user vector (Control-Y)
03FE IRQ: Interrupt Request/BRK vector
03FF IRQ: Interrupt Request/BRK vector
A54F DOS 3.3 INIT
A413 DOS 3.3 LOAD
A397 DOS 3.3 SAVE
A4D1 DOS 3.3 RUN
A4F0 DOS 3.3 CHAIN
A263 DOS 3.3 DELETE
A271 DOS 3.3 LOCK
A275 DOS 3.3 UNLOCK
A2EA DOS 3.3 CLOSE
A51B DOS 3.3 READ
A5C6 DOS 3.3 EXEC
A510 DOS 3.3 WRITE
A5DD DOS 3.3 POSITION
A2A3 DOS 3.3 OPEN
A298 DOS 3.3 APPEND
A281 DOS 3.3 RENAME
A56E DOS 3.3 CATALOG
A233 DOS 3.3 MON
A23D DOS 3.3 NOMON
A229 DOS 3.3 PR#
A22E DOS 3.3 IN#
A251 DOS 3.3 MAXFILES
A57A DOS 3.3 FP
A59E DOS 3.3 INT
A331 DOS 3.3 BSAVE
A35D DOS 3.3 BLOAD
A38E DOS 3.3 BRUN
A27D DOS 3.3 VERIFY
BF00 ProDOS MLI entry point
BF98 ProDOS Machine ID Byte
* C000 80STOREOFF Allow page2 to switch video page1 page2
C001 80STOREON Allow page2 to switch main & aux video memory
@ -68,8 +117,12 @@ C009 ALTZPON Enable aux memory from $0000-$01FF & avl BSR
C00A SLOTC3ROMOFF Enable main ROM from $C300-$C3FF
C00B SLOTC3ROMON Enable slot ROM from $C300-$C3FF
C000 KYBD - last key pressed
C010 STROBE - Clear KYBD
C000 KBD - Last key pressed
C010 KBDSTRB - Clear KYBD
C019 VBL - Vertical Blank
C020 TAPEOUT - Toggle cassette output
C030 SPKR - Toggle speaker
C040 STROBE - Output strobe pulse to game I/O connector
C050 TXTCLR - Display Graphics
C051 TXTSET - Display Text
C052 MIXCLR - Display Full Screen
@ -78,6 +131,15 @@ C054 TXTPAGE1 - Display Page 1
C055 TXTPAGE2 - If 80STORE Off: Display Page 2, If 80STORE On: Read/Write Aux Display Mem
C056 LORES - Display LoRes Graphics
C057 HIRES - Display HiRes Graphics
C060 TAPEIN - Read audio from cassette input
C061 PB0 - Read joystick button 0/Open-Apple key
C062 PB1 - Read joystick button 1/Closed-Apple key
C063 PB2 - Read joystick button 2
C064 PADDL0 - Read paddle/joystick 0
C065 PADDL1 - Read paddle/joystick 1
C066 PADDL2 - Read paddle/joystick 2
C067 PADDL3 - Read paddle/joystick 3
C070 PTRIG - Clear paddle/joystick timer
C080 Read RAM bank 2; no write
C081 ROMIN - Read ROM; write RAM bank 2
@ -96,6 +158,9 @@ C08D Read ROM; write RAM bank 1
C08E Read ROM; no write
C08F Read/write RAM bank 1
C600 BOOT0 - Disk II controller ROM
0801 BOOT1 - Disk II bootstrap RAM
D52C INLIN numeric input
DB3A STROUT - output a string
DB5C output a character
@ -106,6 +171,7 @@ DEC0 SYNCHR
DEC9 syntax error
DFE3 PTRGET
E000 Applesoft BASIC entry
E053 find a variable
E10C convert FP to INT
E2F2 GIVAYF - convert (A,Y) to FP
@ -177,15 +243,16 @@ F941 PRINTAX - print AX registers in hex
F948 PRBLNK - print 3 spaces
F94A PRBL2 - print X blank spaces
FAA6 reboot DOS
FAA6 PWRUP - reboot
FAFF 0 = Autostart ROM, 1 = Old Monitor
FB1E PREAD - read game paddle
FB2F initialise text screen
FB2F INIT - initialise text screen
FB39 text mode - SETTXT
FB40 SETGR
FB5B TABV - monitor tab routine
FB6F set powerup checksum
FB6F SETPWRC - set powerup checksum
FBB3 VERSION - monitor ROM ID byte
FBC1 BASCALC - calculate video address
FBDD BELL1 - beep speaker
FBF4 CURSRIT - move cursor right
@ -193,6 +260,7 @@ FBF4 CURSRIT - move cursor right
FC10 CURSLFT - move cursor left
FC1A CURSUP - move cursor up
FC22 VTAB
FC24 VTABZ
FC42 CLREOP - clear to end of page
FC58 HOME - clear screen
FC62 CR
@ -215,12 +283,13 @@ FDE3 PRHEX - print a hex digit
FDED COUT - print a character (in Acc)
FDF0 COUT1 - print character to screen
FE2C move a block of memory
FE1F IDROUTINE - detect //gs
FE2C MOVE - move a block of memory
FE80 SETINV - set inverse mode
FE84 SETNORM - set normal mode
FE89 disconnect DOS from I/O links
FE89 SETKBD - disconnect DOS from I/O links
FE8B INPORT
FE93 disconnect DOS from I/O links
FE93 SETVID - disconnect DOS from I/O links
FE95 OUTPORT
FECD WRITE
FEFD READ
@ -231,6 +300,14 @@ FF3A BELL
FF3F IOREST - restore all registers
FF4A IOSAVE - save all registers
FF58 RTS - jump to <address on stack> + 1
FF59 Monitor cold entry point
FF59 MON - Monitor cold entry point (w/BELL)
FF69 MONZ - Monitor entry point from BASIC (CALL -151)
FFA7 GETNUM - move num to A2L.A2H
FFC7 ZMODE - monitor get ASCII return
FFFA NMI_VECTOR
FFFB NMI_VECTOR
FFFC RESET_VECTOR
FFFD RESET_VECTOR
FFFE IRQ_VECTOR
FFFF IRQ_VECTOR

View File

@ -52,7 +52,7 @@ public class AppleworksADBFile extends AbstractFile
dbMinVersion = buffer[218] & 0xFF;
headerSize = Utility.unsignedShort (buffer, 0);
headerSize = Utility.getShort (buffer, 0);
cursorDirectionSRL = buffer[30];
cursorDirectionMRL = (char) buffer[31];
currentDisplay = (char) buffer[34];
@ -60,7 +60,7 @@ public class AppleworksADBFile extends AbstractFile
categoryNames = new String[categories];
totalReports = buffer[38] & 0xFF;
int recs = Utility.unsignedShort (buffer, 36);
int recs = Utility.getShort (buffer, 36);
totalRecords = dbMinVersion == 0 ? recs : recs & 0x7FFF;
for (int i = 0; i < 30; i++)
@ -79,9 +79,9 @@ public class AppleworksADBFile extends AbstractFile
for (int i = 0; i < 3; i++)
{
selectionRules[i] = Utility.unsignedShort (buffer, 223 + i * 2);
testTypes[i] = Utility.unsignedShort (buffer, 229 + i * 2);
continuation[i] = Utility.unsignedShort (buffer, 235 + i * 2);
selectionRules[i] = Utility.getShort (buffer, 223 + i * 2);
testTypes[i] = Utility.getShort (buffer, 229 + i * 2);
continuation[i] = Utility.getShort (buffer, 235 + i * 2);
comparison[i] = new String (buffer, 241 + i * 20, 20);
}
@ -106,7 +106,7 @@ public class AppleworksADBFile extends AbstractFile
ptr += 600;
}
int length = Utility.unsignedShort (buffer, ptr);
int length = Utility.getShort (buffer, ptr);
ptr += 2;
if (length == 0)
@ -118,7 +118,7 @@ public class AppleworksADBFile extends AbstractFile
for (int recordNo = 0; recordNo < totalRecords; recordNo++)
{
length = Utility.unsignedShort (buffer, ptr);
length = Utility.getShort (buffer, ptr);
ptr += 2;
if (length == 0)
break;

View File

@ -25,7 +25,7 @@ public class AppleworksSSFile extends AbstractFile
int ptr = header.ssMinVers == 0 ? 300 : 302;
while (ptr < buffer.length)
{
int length = Utility.unsignedShort (buffer, ptr);
int length = Utility.getShort (buffer, ptr);
if (length == 0xFFFF)
break;
@ -103,7 +103,7 @@ public class AppleworksSSFile extends AbstractFile
calcOrder = (char) buffer[131];
calcFrequency = (char) buffer[132];
lastRow = Utility.unsignedShort (buffer, 133);
lastRow = Utility.getShort (buffer, 133);
lastColumn = buffer[135] & 0xFF;
windowLayout = (char) buffer[136];
windowSynch = buffer[137] != 0;
@ -204,15 +204,15 @@ public class AppleworksSSFile extends AbstractFile
r1 = buffer[offset + 3] & 0xFF;
c1 = buffer[offset + 4] & 0xFF;
r2 = Utility.unsignedShort (buffer, offset + 5);
r2 = Utility.getShort (buffer, offset + 5);
c2 = buffer[offset + 7] & 0xFF;
r3 = Utility.unsignedShort (buffer, offset + 8);
r3 = Utility.getShort (buffer, offset + 8);
c3 = buffer[offset + 10] & 0xFF;
r4 = Utility.unsignedShort (buffer, offset + 11);
r4 = Utility.getShort (buffer, offset + 11);
c4 = buffer[offset + 13] & 0xFF;
r5 = buffer[offset + 14] & 0xFF;
c5 = buffer[offset + 15] & 0xFF;
r6 = Utility.unsignedShort (buffer, offset + 16);
r6 = Utility.getShort (buffer, offset + 16);
c6 = buffer[offset + 18] & 0xFF;
r7 = buffer[offset + 19] & 0xFF;
c7 = buffer[offset + 20] & 0xFF;
@ -265,7 +265,7 @@ public class AppleworksSSFile extends AbstractFile
public Row (int ptr)
{
rowNumber = Utility.unsignedShort (buffer, ptr);
rowNumber = Utility.getShort (buffer, ptr);
ptr += 2; // first control byte
int column = 0;

View File

@ -139,7 +139,7 @@ public class AppleworksWPFile extends AbstractFile
break;
default:
System.out.printf ("Unknown value in %s: %02X %02X%n", name, b1, b2);
System.out.printf ("Unknown value in %s: %02X %02X%n", getName (), b1, b2);
}
ptr += 2;
}

View File

@ -14,7 +14,7 @@ class CellAddress
// ---------------------------------------------------------------------------------//
{
colRef = buffer[offset];
rowRef = Utility.intValue (buffer[offset + 1], buffer[offset + 2]);
rowRef = Utility.getShort (buffer, offset + 1);
}
// ---------------------------------------------------------------------------------//

View File

@ -131,9 +131,9 @@ abstract class Report
if (buffer[offset + 480 + fudge] == 0) // test high byte
for (int i = 0; i < 3; i++)
{
selectionRules[i] = Utility.unsignedShort (buffer, offset + 479 + i * 2 + fudge);
testTypes[i] = Utility.unsignedShort (buffer, offset + 485 + i * 2 + fudge);
continuation[i] = Utility.unsignedShort (buffer, offset + 491 + i * 2 + fudge);
selectionRules[i] = Utility.getShort (buffer, offset + 479 + i * 2 + fudge);
testTypes[i] = Utility.getShort (buffer, offset + 485 + i * 2 + fudge);
continuation[i] = Utility.getShort (buffer, offset + 491 + i * 2 + fudge);
comparison[i] = pascalString (buffer, offset + 497 + i * 32 + fudge);
}
else

View File

@ -26,23 +26,28 @@ class CPMCatalogSector extends AbstractSector
for (int i = 0; i <= 255; i += CATALOG_ENTRY_SIZE)
{
if (buffer[i] == (byte) 0xE5)
if (buffer[i] == (byte) 0xE5 && buffer[i + 1] == (byte) 0xE5)
break;
int userNumber = buffer[i] & 0xFF;
if (userNumber > 31 && userNumber != (byte) 0xE5)
break;
boolean readOnly = (buffer[i + 9] & 0x80) != 0;
boolean systemFile = (buffer[i + 10] & 0x80) != 0;
boolean unknown = (buffer[i + 11] & 0x80) != 0;
String type;
String extra;
if (readOnly || systemFile)
if (readOnly || systemFile || unknown)
{
byte[] typeBuffer = new byte[3];
typeBuffer[0] = (byte) (buffer[i + 9] & 0x7F);
typeBuffer[1] = (byte) (buffer[i + 10] & 0x7F);
typeBuffer[2] = buffer[i + 11];
typeBuffer[2] = (byte) (buffer[i + 11] & 0x7F);
type = new String (typeBuffer).trim ();
extra = String.format (" (%s%s)", readOnly ? "read only" : "",
systemFile ? "system file" : "");
extra = String.format (" (%s%s%s)", readOnly ? "read only" : "",
systemFile ? "system file" : "", unknown ? "unknown" : "");
}
else
{
@ -50,8 +55,14 @@ class CPMCatalogSector extends AbstractSector
extra = "";
}
addText (text, buffer, i, 1, "User number");
addText (text, buffer, i + 1, 4, "File name : " + new String (buffer, i + 1, 8));
if (buffer[i] == (byte) 0xE5)
addText (text, buffer, i, 1, "Deleted file?");
else
addText (text, buffer, i, 1, "User number");
if (buffer[i + 1] == 0)
addText (text, buffer, i + 1, 4, "File name : ");
else
addText (text, buffer, i + 1, 4, "File name : " + new String (buffer, i + 1, 8));
addText (text, buffer, i + 5, 4, "");
addText (text, buffer, i + 9, 3, "File type : " + type + extra);
addText (text, buffer, i + 12, 1, "Extent counter LO");

View File

@ -1,6 +1,7 @@
package com.bytezone.diskbrowser.cpm;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import javax.swing.tree.DefaultMutableTreeNode;
@ -13,11 +14,15 @@ import com.bytezone.diskbrowser.disk.Disk;
import com.bytezone.diskbrowser.disk.DiskAddress;
import com.bytezone.diskbrowser.disk.SectorType;
import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.HexFormatter;
// https://www.retrotechnology.com/dri/howto_cpm.html
// -----------------------------------------------------------------------------------//
public class CPMDisk extends AbstractFormattedDisk
// -----------------------------------------------------------------------------------//
{
private static final int EMPTY_BYTE_VALUE = 0xE5;
private final Color green = new Color (0, 200, 0);
public final SectorType catalogSector = new SectorType ("Catalog", green);
@ -50,7 +55,7 @@ public class CPMDisk extends AbstractFormattedDisk
sectorTypesList.add (macSector);
sectorTypesList.add (otherSector);
setEmptyByte ((byte) 0xE5);
setEmptyByte ((byte) EMPTY_BYTE_VALUE);
// search for the version string
for (int i = 8; i >= 4; i -= 2)
@ -74,22 +79,28 @@ public class CPMDisk extends AbstractFormattedDisk
sectorTypes[da.getBlockNo ()] = catalogSector;
byte[] buffer = disk.readBlock (da);
int b1 = buffer[0] & 0xFF;
int b2 = buffer[1] & 0xFF;
if (b1 == 0xE5)
if (b1 == EMPTY_BYTE_VALUE && (b2 == EMPTY_BYTE_VALUE || b2 == 0))
continue;
if (b1 > 31)
if (b1 > 31 && b1 != EMPTY_BYTE_VALUE)
break;
if (b2 < 32 || (b2 > 126 && b2 != 0xE5))
if (b2 <= 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
break;
for (int i = 0; i < buffer.length; i += 32)
{
b1 = buffer[i] & 0xFF;
b2 = buffer[i + 1] & 0xFF;
if (b1 == 0xE5)
break;
if (b2 < 32 || (b2 > 126 && b2 != 0xE5))
if (b1 == EMPTY_BYTE_VALUE) // deleted file??
continue;
if (b2 <= 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
break;
DirectoryEntry entry = new DirectoryEntry (this, buffer, i);
@ -111,9 +122,6 @@ public class CPMDisk extends AbstractFormattedDisk
}
}
// root.setUserObject (getCatalog ()); // override the disk's default display
// makeNodeVisible (rootNode.getFirstLeaf ());
volumeNode.setUserObject (getCatalog ());
makeNodeVisible (volumeNode.getFirstLeaf ());
}
@ -147,6 +155,7 @@ public class CPMDisk extends AbstractFormattedDisk
{
if (fileEntries.size () > 0 && fileEntries.size () > fileNo)
return fileEntries.get (fileNo).getSectors ();
return null;
}
@ -180,19 +189,17 @@ public class CPMDisk extends AbstractFormattedDisk
public AppleFileSource getCatalog ()
// ---------------------------------------------------------------------------------//
{
String newLine = String.format ("%n");
String line =
"---- --------- --- - - -- -- -- -- ----------------------------"
+ "-------------------" + newLine;
String line = "---- --------- --- - - -- -- -- -- ----------------------------"
+ "-------------------\n";
StringBuilder text = new StringBuilder ();
text.append (String.format ("Disk : %s%n%n", getDisplayPath ()));
text.append ("User Name Typ R S Ex S2 S1 RC Blocks" + newLine);
text.append (String.format ("File : %s%n%n", getDisplayPath ()));
text.append ("User Name Typ R S Ex S2 S1 RC Blocks\n");
text.append (line);
for (AppleFileSource entry : fileEntries)
{
text.append (((DirectoryEntry) entry).line ());
text.append (newLine);
text.append ("\n");
}
text.append (line);
if (version != 0)
@ -204,6 +211,64 @@ public class CPMDisk extends AbstractFormattedDisk
// ---------------------------------------------------------------------------------//
public static boolean isCorrectFormat (AppleDisk disk)
// ---------------------------------------------------------------------------------//
{
boolean debug = false;
disk.setInterleave (3);
// collect catalog sectors
List<DiskAddress> catalog = new ArrayList<> ();
for (int i = 0; i < 8; i++)
catalog.add (disk.getDiskAddress (3, i));
byte[] buffer = disk.readBlocks (catalog);
if (debug)
System.out.println (HexFormatter.format (buffer));
for (int i = 0; i < 2; i++)
{
int start = i * 1024;
int end = start + 1024;
for (int ptr = start; ptr < end; ptr += 32)
{
if (buffer[ptr] == (byte) EMPTY_BYTE_VALUE)
{
if (buffer[ptr + 1] == (byte) EMPTY_BYTE_VALUE //
|| buffer[ptr + 1] == 0) // finished this block
break;
continue; // deleted file?
}
int userNo = buffer[ptr] & 0xFF;
if (userNo > 31)
return false;
for (int j = 1; j < 12; j++)
{
int ch = buffer[ptr + j] & 0x7F; // remove flag
if (ch < 32 || ch > 126) // invalid ascii
return false;
}
if (debug)
{
String fileName = new String (buffer, ptr + 1, 8);
String fileType = new String (buffer, ptr + 9, 3);
System.out.printf ("%2d %s %s%n", userNo, fileName, fileType);
}
}
}
if (debug)
System.out.println ("CP/M disk");
return true;
}
// ---------------------------------------------------------------------------------//
private static boolean isCorrectFormat2 (AppleDisk disk)
// ---------------------------------------------------------------------------------//
{
disk.setInterleave (3);
@ -222,26 +287,60 @@ public class CPMDisk extends AbstractFormattedDisk
for (int sector = 0; sector < 8; sector++)
{
byte[] buffer = disk.readBlock (3, sector);
System.out.println (HexFormatter.format (buffer));
// check if entire sector is empty (everything == 0xE5)
if (bufferContainsAll (buffer, (byte) 0xE5))
if (bufferContainsAll (buffer, (byte) EMPTY_BYTE_VALUE))
break;
int b1 = buffer[0] & 0xFF;
int b2 = buffer[1] & 0xFF;
if (b1 == EMPTY_BYTE_VALUE && (b2 == EMPTY_BYTE_VALUE || b2 == 0))
continue;
if (b1 > 31 && b1 != EMPTY_BYTE_VALUE)
break;
if (b2 < 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
break;
for (int i = 0; i < buffer.length; i += 32)
{
int val = buffer[i] & 0xFF;
if (val == 0xE5)
break;
b1 = buffer[i] & 0xFF;
b2 = buffer[i + 1] & 0xFF;
if (val > 31)
if (b1 == EMPTY_BYTE_VALUE) // deleted file??
continue;
if (b2 < 32 || (b2 > 126 && b2 != EMPTY_BYTE_VALUE))
return false;
for (int j = 1; j <= 8; j++)
{
val = buffer[i + j] & 0xFF;
if (val < 32 || (val > 126 && val != 0xE5))
return false;
}
// int val = buffer[i] & 0xFF;
// if (val == EMPTY_BYTE_VALUE)
// {
// if (debug)
// System.out.println ("empty value found - deleted file?");
// break;
// }
// if (val > 31)
// {
// if (debug)
// System.out.println ("val > 31");
// return false;
// }
// for (int j = 1; j <= 8; j++)
// {
// val = buffer[i + j] & 0xFF;
// if (val < 32 || (val > 126 && val != EMPTY_BYTE_VALUE))
// {
// if (debug)
// System.out.println ("val < 32 || val > 126");
// return false;
// }
// }
}
}

View File

@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.applefile.AppleFileSource;
import com.bytezone.diskbrowser.applefile.CPMBasicFile;
import com.bytezone.diskbrowser.applefile.CPMTextFile;
import com.bytezone.diskbrowser.applefile.DefaultAppleFile;
import com.bytezone.diskbrowser.disk.AppleDiskAddress;
@ -54,7 +55,11 @@ class DirectoryEntry implements AppleFileSource
type = new String (typeBuffer).trim ();
userNumber = buffer[offset] & 0xFF;
name = new String (buffer, offset + 1, 8).trim ();
if (userNumber == 0xE5 && buffer[offset + 1] == 0)
name = "";
else
name = new String (buffer, offset + 1, 8).trim ();
extent = buffer[offset + 12] & 0xFF;
s2 = buffer[offset + 13] & 0xFF;
s1 = buffer[offset + 14] & 0xFF;
@ -120,6 +125,7 @@ class DirectoryEntry implements AppleFileSource
for (DiskAddress sector : blocks)
if (sector.matches (da))
return true;
return false;
}
@ -133,9 +139,8 @@ class DirectoryEntry implements AppleFileSource
char ro = readOnly ? '*' : ' ';
char sf = systemFile ? '*' : ' ';
String text =
String.format ("%3d %-8s %-3s %s %s %02X %02X %02X %02X %s",
userNumber, name, type, ro, sf, extent, s2, s1, recordsUsed, bytes);
String text = String.format ("%3d %-8s %-3s %s %s %02X %02X %02X %02X %s",
userNumber, name, type, ro, sf, extent, s2, s1, recordsUsed, bytes);
for (DirectoryEntry entry : entries)
text = text + "\n" + entry.line ();
@ -207,23 +212,18 @@ class DirectoryEntry implements AppleFileSource
byte[] exactBuffer = new byte[len];
System.arraycopy (buffer, 0, exactBuffer, 0, len);
int max = Math.min (256, exactBuffer.length);
int count = 0;
for (int i = 1; i < max; i++)
{
if (exactBuffer[i - 1] == 0x0D && exactBuffer[i] == 0x0A)
++count;
}
if ("COM".equals (type))
appleFile = new DefaultAppleFile (name, exactBuffer, "COM File");
else if ("DVR".equals (type))
appleFile = new DefaultAppleFile (name, exactBuffer, "DVR File");
else if ("ASM".equals (type) || "DOC".equals (type) || "TXT".equals (type)
|| count > 2)
else if ("ASM".equals (type) || "DOC".equals (type) || "COB".equals (type)
|| "HLP".equals (type) || "TXT".equals (type) || "LET".equals (type) || "ALX".equals (type)
|| "SRC".equals (type) || "H".equals (type) || exactBuffer[len - 1] == 0x1A)
appleFile = new CPMTextFile (name, exactBuffer);
else if ("BAS".equals (type))
appleFile = new CPMBasicFile (name, exactBuffer);
else
appleFile = new DefaultAppleFile (name, exactBuffer, "CPM File : " + type);
appleFile = new DefaultAppleFile (name, exactBuffer, "CPM File : " + name + "." + type);
return appleFile;
}

View File

@ -67,6 +67,7 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
*/
sectorTypesList.add (emptySector);
sectorTypesList.add (usedSector);
/*
* Hopefully every used sector will be changed by the subclass to something
* sensible, but deleted files will always leave the sector as used/unknown
@ -74,16 +75,23 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
*/
setSectorTypes ();
setGridLayout ();
/*
* Create the disk name as the root for the catalog tree. Subclasses will
* have to append their catalog entries to this node.
*/
String name = getName ();
if (name.endsWith (".tmp"))
name = "tmp.dsk";
DefaultAppleFileSource afs =
new DefaultAppleFileSource (getName (), disk.toString (), this);
// new DefaultAppleFileSource (name, disk.toString (), this);
new DefaultAppleFileSource (name, new DefaultDataSource (disk), this);
DefaultMutableTreeNode root = new DefaultMutableTreeNode (afs);
DefaultTreeModel treeModel = new DefaultTreeModel (root);
catalogTree = new JTree (treeModel);
treeModel.setAsksAllowsChildren (true); // allows empty nodes to appear as folders
/*
* Add an ActionListener to the disk in case the interleave or blocksize
* changes
@ -124,50 +132,37 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
{
int totalBlocks = disk.getTotalBlocks ();
switch (totalBlocks)
Dimension newGridLayout = switch (totalBlocks)
{
case 280:
gridLayout = new Dimension (8, 35);
break;
case 455:
gridLayout = new Dimension (13, 35);
break;
case 560:
gridLayout = new Dimension (16, 35);
break;
case 704:
gridLayout = new Dimension (16, 44);
break;
case 768:
gridLayout = new Dimension (16, 48);
break;
case 1600:
case 280 -> new Dimension (8, 35);
case 455 -> new Dimension (13, 35);
case 560 -> new Dimension (16, 35);
case 704 -> new Dimension (16, 44);
case 768 -> new Dimension (16, 48);
case 800 -> new Dimension (8, 100);
case 1600 ->
{
if (disk.getBlocksPerTrack () == 32)
gridLayout = new Dimension (disk.getBlocksPerTrack (), disk.getTotalTracks ());
yield new Dimension (32, 50);
else
gridLayout = new Dimension (16, 100);
break;
case 2048:
gridLayout = new Dimension (8, 256);
break;
default:
yield new Dimension (16, 100);
}
case 2048 -> new Dimension (8, 256);
case 3200 -> new Dimension (16, 200);
default ->
{
int[] sizes = { 32, 20, 16, 8 };
for (int size : sizes)
if ((totalBlocks % size) == 0)
{
gridLayout = new Dimension (size, totalBlocks / size);
break;
}
if (gridLayout == null)
System.out.println ("Unusable total blocks : " + totalBlocks);
}
yield new Dimension (size, totalBlocks / size);
yield null;
}
};
if (newGridLayout == null)
System.out.println ("Unusable total blocks : " + totalBlocks);
else
gridLayout = newGridLayout;
}
// ---------------------------------------------------------------------------------//
@ -211,8 +206,7 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
DefaultMutableTreeNode root = getCatalogTreeRoot ();
if (root.getUserObject () == null)
root.setUserObject (
new DefaultAppleFileSource (getName (), disk.toString (), this));
root.setUserObject (new DefaultAppleFileSource (getName (), disk.toString (), this));
}
// ---------------------------------------------------------------------------------//
@ -239,13 +233,15 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
public String getDisplayPath ()
// ---------------------------------------------------------------------------------//
{
if (originalPath != null)
return originalPath.toString ();
String home = System.getProperty ("user.home");
String path = disk.getFile ().getAbsolutePath ();
if (path.startsWith (home))
return "~" + path.substring (home.length ());
String path =
originalPath != null ? originalPath.toString () : disk.getFile ().getAbsolutePath ();
int pos = path.indexOf (home);
if (pos == 0 || (path.startsWith ("/Volumes/") && pos > 0))
return "~" + path.substring (home.length () + pos);
return disk.getFile ().getAbsolutePath ();
}
@ -328,24 +324,21 @@ public abstract class AbstractFormattedDisk implements FormattedDisk
{
Enumeration<TreeNode> children = node.breadthFirstEnumeration ();
if (children != null)
{
while (children.hasMoreElements ())
{
DefaultMutableTreeNode childNode =
(DefaultMutableTreeNode) children.nextElement ();
DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) children.nextElement ();
if (childNode.getUserObject ().toString ().indexOf (name) > 0)
return childNode;
}
}
return null;
}
// ---------------------------------------------------------------------------------//
/*
* These routines just hand back the information that was created above, and
* added to by the subclass.
*/
// ---------------------------------------------------------------------------------//
@Override
public SectorType getSectorType (int block)
// ---------------------------------------------------------------------------------//

View File

@ -8,19 +8,16 @@ import javax.swing.JPanel;
import com.bytezone.diskbrowser.applefile.AssemblerProgram;
import com.bytezone.diskbrowser.gui.DataSource;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public abstract class AbstractSector implements DataSource
// -----------------------------------------------------------------------------------//
{
private static String newLine = String.format ("%n");
private static String newLine2 = newLine + newLine;
final public byte[] buffer;
protected Disk disk;
protected DiskAddress diskAddress;
AssemblerProgram assembler;
String description;
// ---------------------------------------------------------------------------------//
public AbstractSector (Disk disk, byte[] buffer, DiskAddress diskAddress)
@ -57,6 +54,14 @@ public abstract class AbstractSector implements DataSource
return HexFormatter.format (buffer, 0, buffer.length);
}
// ---------------------------------------------------------------------------------//
@Override
public byte[] getBuffer ()
// ---------------------------------------------------------------------------------//
{
return buffer;
}
// ---------------------------------------------------------------------------------//
@Override
public BufferedImage getImage ()
@ -70,9 +75,7 @@ public abstract class AbstractSector implements DataSource
public String getText ()
// ---------------------------------------------------------------------------------//
{
if (description == null)
description = createText ();
return description;
return createText ();
}
// ---------------------------------------------------------------------------------//
@ -85,10 +88,11 @@ public abstract class AbstractSector implements DataSource
{
StringBuilder text = new StringBuilder ();
text.append (title + newLine2);
text.append ("Offset Value Description" + newLine);
text.append (title + "\n\n");
text.append ("Offset Value Description\n");
text.append ("======= =========== "
+ "===============================================================" + newLine);
+ "===============================================================\n");
return text;
}
@ -138,14 +142,13 @@ public abstract class AbstractSector implements DataSource
String desc)
// ---------------------------------------------------------------------------------//
{
if (size == 1)
desc += " (" + (b[offset] & 0xFF) + ")";
else if (size == 2)
desc +=
String.format (" (%,d)", ((b[offset + 1] & 0xFF) * 256 + (b[offset] & 0xFF)));
else if (size == 3)
desc += String.format (" (%,d)", ((b[offset + 2] & 0xFF) * 65536)
+ ((b[offset + 1] & 0xFF) * 256) + (b[offset] & 0xFF));
desc += switch (size)
{
case 1 -> " (" + (b[offset] & 0xFF) + ")";
case 2 -> String.format (" (%,d)", Utility.getShort (b, offset));
case 3 -> String.format (" (%,d)", Utility.readTriple (b, offset));
default -> "";
};
addText (text, b, offset, size, desc);
}

View File

@ -13,11 +13,13 @@ import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import com.bytezone.diskbrowser.applefile.AppleFileSource;
import com.bytezone.diskbrowser.nib.NibFile;
import com.bytezone.diskbrowser.nib.V2dFile;
import com.bytezone.diskbrowser.nib.WozFile;
import com.bytezone.diskbrowser.nufx.Binary2;
import com.bytezone.diskbrowser.nufx.NuFX;
import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.Utility;
// -----------------------------------------------------------------------------------//
public class AppleDisk implements Disk
@ -33,17 +35,24 @@ public class AppleDisk implements Disk
private final int tracks; // usually 35 for floppy disks
private int sectors; // 8 or 16 (or 32 for unidos)
private int blocks; // 280 or 560 for floppy disks, higher for HD
private int dosVersion;
private final int trackSize; // 4096
public int sectorSize; // 256 or 512
private NuFX nuFX;
private Binary2 bin2;
private WozFile wozFile;
private PrefixDiskCopy prefixDiskCopy;
private Prefix2mg prefix2mg;
private int interleave = 0;
private static int[][] interleaveSector = //
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, // None
{ 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal
{ 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom
{ 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM
{ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, //
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }, // None
{ 0, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 15 }, // Prodos/Pascal
{ 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 }, // Infocom
{ 0, 6, 12, 3, 9, 15, 14, 5, 11, 2, 8, 7, 13, 4, 10, 1 } }; // CPM
// Physical disk interleave:
// Info from http://www.applelogic.org/TheAppleIIEGettingStarted.html
@ -82,8 +91,6 @@ public class AppleDisk implements Disk
private ActionListener actionListenerList;
private List<DiskAddress> blockList;
private WozFile wozFile;
private final boolean debug = false;
// ---------------------------------------------------------------------------------//
@ -94,8 +101,7 @@ public class AppleDisk implements Disk
}
// ---------------------------------------------------------------------------------//
public AppleDisk (File file, int tracks, int sectors, int skip)
throws FileFormatException
public AppleDisk (File file, int tracks, int sectors, int skip) throws FileFormatException
// ---------------------------------------------------------------------------------//
{
assert (file.exists ()) : "No such path :" + file.getAbsolutePath ();
@ -113,15 +119,14 @@ public class AppleDisk implements Disk
if ("2mg".equalsIgnoreCase (suffix) || "2IMG".equals (prefix))
{
// System.out.println ("checking 2mg");
if ("2IMG".equals (prefix))
{
Prefix2mg prefix2mg = new Prefix2mg (buffer);
prefix2mg = new Prefix2mg (buffer);
if (debug)
System.out.println (prefix2mg);
if (prefix2mg.diskData > 0)
this.blocks = prefix2mg.diskData / 4096 * 8; // reduce blocks to a multiple of 8
if (prefix2mg.length > 0)
this.blocks = prefix2mg.length / 4096 * 8; // reduce blocks to a multiple of 8
this.sectorSize = 512;
this.trackSize = 8 * sectorSize;
@ -142,7 +147,7 @@ public class AppleDisk implements Disk
}
else if ("img".equals (suffix) || "dimg".equals (suffix))
{
PrefixDiskCopy prefixDiskCopy = new PrefixDiskCopy (buffer);
prefixDiskCopy = new PrefixDiskCopy (buffer);
blocks = prefixDiskCopy.getBlocks ();
this.sectorSize = 512;
@ -152,7 +157,7 @@ public class AppleDisk implements Disk
tracks = blocks / 8; // change parameter!
sectors = 8; // change parameter!
}
else if (suffix.equalsIgnoreCase ("HDV")
else if (suffix.equalsIgnoreCase ("HDV") //
|| (suffix.equalsIgnoreCase ("po") && tracks > 50)) // ULTIMATE APPLE1 CFFA 3.5.po
{
//this.blocks = (int) file.length () / 4096 * 8; // reduce blocks to a multiple of 8
@ -160,7 +165,7 @@ public class AppleDisk implements Disk
this.sectorSize = 512;
this.trackSize = sectors * sectorSize;
}
else if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4
else if (file.length () == 143360 && tracks == 256 && sectors == 8) // wiz4 or wiz5
{
this.blocks = tracks * sectors;
this.sectorSize = 512;
@ -232,12 +237,27 @@ public class AppleDisk implements Disk
checkSectorsForData ();
}
// ---------------------------------------------------------------------------------//
void setNuFX (NuFX nufx)
// ---------------------------------------------------------------------------------//
{
this.nuFX = nufx;
}
// ---------------------------------------------------------------------------------//
void setBinary2 (Binary2 bin2)
// ---------------------------------------------------------------------------------//
{
this.bin2 = bin2;
}
// ---------------------------------------------------------------------------------//
public AppleDisk (V2dFile disk, int tracks, int sectors)
// ---------------------------------------------------------------------------------//
{
this.tracks = tracks;
this.sectors = sectors;
file = disk.file;
diskBuffer = disk.getDiskBuffer ();
@ -337,6 +357,13 @@ public class AppleDisk implements Disk
return false;
}
// ---------------------------------------------------------------------------------//
public void setDosVersion (int version)
// ---------------------------------------------------------------------------------//
{
this.dosVersion = version;
}
/*
* Routines that implement the Disk interface
*/
@ -440,6 +467,7 @@ public class AppleDisk implements Disk
readBuffer (da, buffer, ptr);
ptr += sectorSize;
}
return buffer;
}
@ -528,8 +556,7 @@ public class AppleDisk implements Disk
{
if (!isValidAddress (block))
{
System.out.printf ("getDiskAddress: Invalid block : %d of %d%n", block,
this.blocks);
System.out.printf ("getDiskAddress: Invalid block : %d of %d%n", block, this.blocks);
return null;
// return new AppleDiskAddress (this, 0); // this was looping 26/07/2016
}
@ -564,8 +591,13 @@ public class AppleDisk implements Disk
public boolean isValidAddress (int track, int sector)
// ---------------------------------------------------------------------------------//
{
track &= 0x3F;
sector &= 0x1F;
if (dosVersion >= 0x41)
{
track &= 0x3F;
sector &= 0x1F;
}
// else
// System.out.println ("normal dos");
if (track < 0 || track >= this.tracks)
return false;
@ -590,10 +622,9 @@ public class AppleDisk implements Disk
// ---------------------------------------------------------------------------------//
{
assert da.getDisk () == this : "Disk address not applicable to this disk";
assert sectorSize == SECTOR_SIZE
|| sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize;
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : "
+ interleave;
assert sectorSize == SECTOR_SIZE || sectorSize == BLOCK_SIZE : "Invalid sector size : "
+ sectorSize;
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " + interleave;
if (sectorSize == SECTOR_SIZE)
{
@ -606,8 +637,7 @@ public class AppleDisk implements Disk
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset, SECTOR_SIZE);
diskOffset = getBufferOffset (da, 1);
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE,
SECTOR_SIZE);
System.arraycopy (diskBuffer, diskOffset, buffer, bufferOffset + SECTOR_SIZE, SECTOR_SIZE);
}
}
@ -616,10 +646,9 @@ public class AppleDisk implements Disk
// ---------------------------------------------------------------------------------//
{
assert da.getDisk () == this : "Disk address not applicable to this disk";
assert sectorSize == SECTOR_SIZE
|| sectorSize == BLOCK_SIZE : "Invalid sector size : " + sectorSize;
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : "
+ interleave;
assert sectorSize == SECTOR_SIZE || sectorSize == BLOCK_SIZE : "Invalid sector size : "
+ sectorSize;
assert interleave >= 0 && interleave <= MAX_INTERLEAVE : "Invalid interleave : " + interleave;
if (sectorSize == SECTOR_SIZE)
{
@ -636,6 +665,13 @@ public class AppleDisk implements Disk
}
}
// ---------------------------------------------------------------------------------//
public byte[] getBuffer ()
// ---------------------------------------------------------------------------------//
{
return diskBuffer;
}
// ---------------------------------------------------------------------------------//
private int getBufferOffset (DiskAddress da)
// ---------------------------------------------------------------------------------//
@ -684,11 +720,11 @@ public class AppleDisk implements Disk
}
// ---------------------------------------------------------------------------------//
public AppleFileSource getDetails ()
// ---------------------------------------------------------------------------------//
{
return new DefaultAppleFileSource (toString (), file.getName (), null);
}
// private AppleFileSource getDetails ()
// // ---------------------------------------------------------------------------------//
// {
// return new DefaultAppleFileSource (toString (), file.getName (), null);
// }
// ---------------------------------------------------------------------------------//
@Override
@ -697,28 +733,29 @@ public class AppleDisk implements Disk
{
StringBuilder text = new StringBuilder ();
String path = file.getAbsolutePath ();
String home = System.getProperty ("user.home");
if (path.startsWith (home))
path = "~" + path.substring (home.length ());
text.append (String.format ("Path................. %s%n", path));
text.append (String.format ("File name............ %s%n", file.getName ()));
text.append (String.format ("File size............ %,d%n", file.length ()));
text.append (String.format ("Tracks............... %d%n", tracks));
text.append (String.format ("Sectors.............. %d%n", sectors));
text.append (String.format ("Blocks............... %,d%n", blocks));
text.append (String.format ("Track size........... %,d%n", trackSize));
text.append (String.format ("Sector size.......... %d%n", sectorSize));
text.append (String.format ("Interleave........... %d", interleave));
text.append (
String.format ("Path ......... %s%n", Utility.getShortPath (file.getAbsolutePath ())));
text.append (String.format ("File name .... %s%n", file.getName ()));
text.append (String.format ("File size .... %,d%n", file.length ()));
text.append (String.format ("Tracks ....... %d%n", tracks));
text.append (String.format ("Sectors ...... %d%n", sectors));
text.append (String.format ("Blocks ....... %,d%n", blocks));
text.append (String.format ("Track size ... %,d%n", trackSize));
text.append (String.format ("Sector size .. %d%n", sectorSize));
text.append (String.format ("Interleave ... %d%n%n", interleave));
if (wozFile != null)
{
text.append ("\n\n");
text.append (wozFile);
}
else if (nuFX != null)
text.append (nuFX);
else if (bin2 != null)
text.append (bin2);
else if (prefixDiskCopy != null)
text.append (prefixDiskCopy);
else if (prefix2mg != null)
text.append (prefix2mg);
return text.toString ();
return Utility.rtrim (text).toString ();
}
// ---------------------------------------------------------------------------------//

View File

@ -35,7 +35,7 @@ public class AppleDiskAddress implements DiskAddress
// ---------------------------------------------------------------------------------//
{
this.owner = owner;
zeroFlag = (track & 0x40) != 0;
zeroFlag = (track & 0x40) == 0x40;
this.track = track & 0x3F;
this.sector = sector & 0x1F;
this.block = this.track * owner.getBlocksPerTrack () + this.sector;

View File

@ -49,8 +49,8 @@ public class DefaultAppleFileSource implements AppleFileSource
{
this (title, file, owner);
this.blocks = blocks;
if (file instanceof DefaultDataSource)
((DefaultDataSource) file).buffer = owner.getDisk ().readBlocks (blocks);
if (file instanceof DefaultDataSource dds)
dds.buffer = owner.getDisk ().readBlocks (blocks);
}
// ---------------------------------------------------------------------------------//
@ -58,8 +58,8 @@ public class DefaultAppleFileSource implements AppleFileSource
// ---------------------------------------------------------------------------------//
{
this.blocks = blocks;
if (file instanceof DefaultDataSource)
((DefaultDataSource) file).buffer = owner.getDisk ().readBlocks (blocks);
if (file instanceof DefaultDataSource dds)
dds.buffer = owner.getDisk ().readBlocks (blocks);
}
// ---------------------------------------------------------------------------------//

View File

@ -14,6 +14,7 @@ public class DefaultDataSource implements DataSource
{
public String text;
byte[] buffer;
Object textSource;
// ---------------------------------------------------------------------------------//
public DefaultDataSource (String text)
@ -22,6 +23,13 @@ public class DefaultDataSource implements DataSource
this.text = text;
}
// ---------------------------------------------------------------------------------//
public DefaultDataSource (Object textSource)
// ---------------------------------------------------------------------------------//
{
this.textSource = textSource;
}
// ---------------------------------------------------------------------------------//
@Override
public String getAssembler ()
@ -40,6 +48,14 @@ public class DefaultDataSource implements DataSource
return null;
}
// ---------------------------------------------------------------------------------//
@Override
public byte[] getBuffer ()
// ---------------------------------------------------------------------------------//
{
return buffer;
}
// ---------------------------------------------------------------------------------//
@Override
public BufferedImage getImage ()
@ -53,7 +69,7 @@ public class DefaultDataSource implements DataSource
public String getText ()
// ---------------------------------------------------------------------------------//
{
return text;
return textSource == null ? text : textSource.toString ();
}
// ---------------------------------------------------------------------------------//
@ -61,7 +77,6 @@ public class DefaultDataSource implements DataSource
public JComponent getComponent ()
// ---------------------------------------------------------------------------------//
{
System.out.println ("In DefaultDataSource.getComponent()");
JPanel panel = new JPanel ();
return panel;
}

View File

@ -1,5 +1,6 @@
package com.bytezone.diskbrowser.disk;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -18,10 +19,12 @@ import com.bytezone.diskbrowser.infocom.InfocomDisk;
import com.bytezone.diskbrowser.nib.NibFile;
import com.bytezone.diskbrowser.nib.V2dFile;
import com.bytezone.diskbrowser.nib.WozFile;
import com.bytezone.diskbrowser.nufx.Binary2;
import com.bytezone.diskbrowser.nufx.NuFX;
import com.bytezone.diskbrowser.pascal.PascalDisk;
import com.bytezone.diskbrowser.prodos.ProdosDisk;
import com.bytezone.diskbrowser.utilities.FileFormatException;
import com.bytezone.diskbrowser.utilities.NuFX;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
import com.bytezone.diskbrowser.wizardry.Wizardry4BootDisk;
import com.bytezone.diskbrowser.wizardry.WizardryScenarioDisk;
@ -32,6 +35,13 @@ public class DiskFactory
{
private static boolean debug = false;
private static final int DISK_800K = 819200;
private static final int DISK_143K = 143360;
private static final int DISK_116K = 116480;
private static NuFX nuFX;
private static Binary2 binary2;
// ---------------------------------------------------------------------------------//
private DiskFactory ()
// ---------------------------------------------------------------------------------//
@ -46,32 +56,22 @@ public class DiskFactory
}
// ---------------------------------------------------------------------------------//
public static FormattedDisk createDisk (String path)
// ---------------------------------------------------------------------------------//
{
FormattedDisk disk = create (path);
// if (disk.getDisk ().getInterleave () > 0)
// {
// System.out.println (disk);
// System.out.println ();
// }
return disk;
}
// ---------------------------------------------------------------------------------//
private static FormattedDisk create (String path)
public static FormattedDisk createDisk (String pathName)
// ---------------------------------------------------------------------------------//
{
if (debug)
System.out.println ("\nFactory : " + path);
System.out.println ("\nFactory : " + pathName);
File file = new File (path);
nuFX = null;
binary2 = null;
File file = new File (pathName);
if (!file.exists ())
return null;
String suffix = path.substring (path.lastIndexOf (".") + 1).toLowerCase ();
String suffix = pathName.substring (pathName.lastIndexOf (".") + 1).toLowerCase ();
Boolean compressed = false;
Path originalPath = Paths.get (path);
Path originalPath = Paths.get (pathName);
if ("gz".equals (suffix))
{
@ -79,7 +79,7 @@ public class DiskFactory
System.out.println (" ** gzip **");
try
{
InputStream in = new GZIPInputStream (new FileInputStream (path));
InputStream in = new GZIPInputStream (new FileInputStream (pathName));
File tmp = File.createTempFile ("gzip", null);
FileOutputStream fos = new FileOutputStream (tmp);
@ -98,7 +98,7 @@ public class DiskFactory
}
catch (IOException e) // can get EOFException: Unexpected end of ZLIB input stream
{
e.printStackTrace ();
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
return null;
}
}
@ -108,7 +108,7 @@ public class DiskFactory
System.out.println (" ** zip **");
try
{
ZipFile zipFile = new ZipFile (path);
ZipFile zipFile = new ZipFile (pathName);
Enumeration<? extends ZipEntry> entries = zipFile.entries ();
while (entries.hasMoreElements ()) // loop until first valid name
@ -141,37 +141,88 @@ public class DiskFactory
}
catch (IOException e)
{
e.printStackTrace ();
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
return null;
}
}
if (suffix.equals ("sdk"))
if ("sdk".equals (suffix) // NuFX disk
|| "shk".equals (suffix) // NuFX files or disk
|| "bxy".equals (suffix)) // NuFX in Bin2
{
if (debug)
System.out.println (" ** sdk **");
System.out.println (" ** sdk/shk/bxy **");
try
{
NuFX nuFX = new NuFX (file);
File tmp = File.createTempFile ("sdk", null);
nuFX = new NuFX (file.toPath ());
if (nuFX.getTotalDisks () == 0 && nuFX.getTotalFiles () == 0)
{
if (debug)
System.out.println ("Empty NuFX file");
return null;
}
byte[] diskBuffer = nuFX.getDiskBuffer ();
if (diskBuffer == null)
return null;
File tmp = File.createTempFile (suffix, null);
FileOutputStream fos = new FileOutputStream (tmp);
fos.write (nuFX.getBuffer ());
fos.write (diskBuffer);
fos.close ();
tmp.deleteOnExit ();
file = tmp;
suffix = "dsk";
compressed = true;
}
catch (IOException e)
catch (Exception e)
{
e.printStackTrace ();
// e.printStackTrace ();
if (e.getMessage () == null)
System.out.println (e);
else
System.out.println (e.getMessage ());
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
// System.out.println (nuFX);
return null;
}
catch (FileFormatException e)
}
else if ("bny".equals (suffix) || "bqy".equals (suffix)) // Binary2 uncompressed files
{
if (debug)
System.out.println (" ** bny/bqy **");
try
{
binary2 = new Binary2 (file.toPath ());
byte[] diskBuffer = binary2.getDiskBuffer ();
File tmp = File.createTempFile (suffix, null);
FileOutputStream fos = new FileOutputStream (tmp);
fos.write (diskBuffer);
fos.close ();
tmp.deleteOnExit ();
file = tmp;
suffix = "dsk";
compressed = true;
}
catch (Exception e)
{
// e.printStackTrace ();
System.out.println (e.getMessage ());
System.out.printf ("Error unpacking: %s%n", file.getAbsolutePath ());
// System.out.println (binary2);
return null;
}
}
else if ("bsq".equals (suffix))
{
if (debug)
System.out.println (" ** bsq **");
byte[] prefix = getPrefix (file);
System.out.println (HexFormatter.format (prefix));
String key = "FiLeStArTfIlEsTaRt";
}
FormattedDisk disk = null;
FormattedDisk disk2 = null;
@ -221,38 +272,38 @@ public class DiskFactory
// Toolkit.do = 143488
if (((suffix.equals ("po") || suffix.equals ("dsk") || suffix.equals ("do"))
&& file.length () > 143360))
&& file.length () > DISK_143K))
{
if (file.length () < 143500) // slightly bigger than a floppy
{
System.out.println ("File length is wrong: " + file.length ());
disk = checkDos (new AppleDisk (file, 35, 16));
if (disk != null)
return disk;
return check (disk);
}
if (debug)
System.out.printf (" Checking po or dsk hard drive: %,d%n", file.length ());
System.out.printf ("Checking po or dsk hard drive: %,d%n", file.length ());
disk = checkHardDisk (file);
if (disk != null)
{
if (compressed)
disk.setOriginalPath (originalPath);
return disk;
return check (disk);
}
if (file.length () == 819200) // 800K 3.5"
if (file.length () == DISK_800K) // 800K 3.5"
{
if (debug)
System.out.println ("UniDos ?");
// 2 x 400k disk images
AppleDisk appleDisk1 = new AppleDisk (file, 50, 32);
AppleDisk appleDisk2 = new AppleDisk (file, 50, 32, (int) (file.length () / 2));
AppleDisk appleDisk2 = new AppleDisk (file, 50, 32, 0x64000);
disk = checkUnidos (appleDisk1, 1);
disk2 = checkUnidos (appleDisk2, 2);
if (disk != null && disk2 != null)
return new DualDosDisk (disk, disk2);
return new HybridDisk (disk, disk2);
}
if (debug)
@ -299,9 +350,10 @@ public class DiskFactory
{
if (debug)
System.out.println (" --> PRODOS hard disk");
return new ProdosDisk (disk800);
disk = new ProdosDisk (disk800);
}
disk = new DataDisk (disk800);
else
disk = new DataDisk (disk800);
}
else
{
@ -309,11 +361,15 @@ public class DiskFactory
disk = checkDos (appleDisk256);
if (disk == null)
disk = checkProdos (new AppleDisk (wozFile, 35, 8));
if (disk == null)
disk = checkPascalDisk (new AppleDisk (wozFile, 35, 8));
if (disk == null)
disk = new DataDisk (appleDisk256);
}
}
disk.setOriginalPath (originalPath); // allow Save converted disk...
return disk;
}
catch (Exception e)
@ -348,7 +404,7 @@ public class DiskFactory
long length = file.length ();
if (length == 116480) // 13 sector disk
if (length == DISK_116K) // 13 sector floppy disk
{
if (debug)
System.out.println (" ** 13 sector **");
@ -360,7 +416,7 @@ public class DiskFactory
return disk == null ? new DataDisk (appleDisk) : disk;
}
if (length != 143360)
if (length != DISK_143K) // 16 sector floppy disk
{
System.out.printf ("%s: invalid file length : %,d%n", file.getName (),
file.length ());
@ -372,7 +428,7 @@ public class DiskFactory
if (true)
{
// long checksum = appleDisk256.getBootChecksum ();
// long checksum = appleDisk256.getBootChecksum ();
long checksum = 0;
if (checksum == 227968344L) // empty boot sector
@ -397,13 +453,13 @@ public class DiskFactory
if (debug)
System.out.println (" known DOS checksum : " + checksum);
disk = checkDos (appleDisk256);
// disk2 = checkProdos (appleDisk512); // no need for this
// disk2 = checkProdos (appleDisk512); // no need for this
if (disk2 != null && disk != null) // should be impossible
{
if (debug)
System.out.println (" --> Dual dos/prodos 1");
System.out.println ("** impossible **");
disk = new DualDosDisk (disk, disk2);
disk = new HybridDisk (disk, disk2);
}
}
else if (checksum == 1737448647L //
@ -418,7 +474,7 @@ public class DiskFactory
{
if (debug)
System.out.println (" --> Dual prodos/dos 2");
disk = new DualDosDisk (disk, disk2);
disk = new HybridDisk (disk, disk2);
}
}
else if (checksum == 2803644711L // Apple Pascal disk 0
@ -431,7 +487,7 @@ public class DiskFactory
disk = checkPascalDisk (appleDisk512);
disk2 = checkDos (appleDisk256);
if (disk2 != null)
disk = new DualDosDisk (disk, disk2);
disk = new HybridDisk (disk, disk2);
}
else if (checksum == 3028642627L //
|| checksum == 2070151659L) // Enchanter
@ -443,22 +499,22 @@ public class DiskFactory
else if (debug)
System.out.println (" unknown checksum : " + checksum);
// else if (checksum == 1212926910L || checksum == 1365043894L
// || checksum == 2128073918L)
// disk = checkCPMDisk (file);
// else if (checksum == 1212926910L || checksum == 1365043894L
// || checksum == 2128073918L)
// disk = checkCPMDisk (file);
// System.out.println (checksum);
// System.out.println (checksum);
if (disk != null)
{
if (compressed)
disk.setOriginalPath (originalPath);
return disk;
return check (disk);
}
// empty boot sector
if (checksum != 227968344L && false)
System.out.println ("Unknown checksum : " + checksum + " : " + path);
System.out.println ("Unknown checksum : " + checksum + " : " + pathName);
}
if (debug)
@ -478,12 +534,25 @@ public class DiskFactory
disk2 = checkProdos (appleDisk512);
if (disk2 != null)
disk = new DualDosDisk (disk, disk2);
{
disk = new HybridDisk (disk, disk2);
return disk;
}
AppleDisk appleDisk = new AppleDisk (file, 35, 16);
disk2 = checkCPMDisk (appleDisk);
if (disk2 != null)
disk = new DualDosDisk (disk, disk2);
{
disk = new HybridDisk (disk, disk2);
return disk;
}
disk2 = checkPascalDisk (appleDisk512);
if (disk2 != null)
{
disk = new HybridDisk (disk, disk2);
return disk;
}
}
}
else if (suffix.equals ("po"))
@ -516,6 +585,21 @@ public class DiskFactory
if (disk != null && compressed)
disk.setOriginalPath (originalPath);
return check (disk);
}
// ---------------------------------------------------------------------------------//
private static FormattedDisk check (FormattedDisk disk)
// ---------------------------------------------------------------------------------//
{
if (disk.getDisk () instanceof AppleDisk appleDisk)
{
if (nuFX != null)
appleDisk.setNuFX (nuFX);
else if (binary2 != null)
appleDisk.setBinary2 (binary2);
}
return disk;
}
@ -532,7 +616,10 @@ public class DiskFactory
{
if (debug)
System.out.println (" --> DOS");
return new DosDisk (disk);
DosDisk dosDisk = new DosDisk (disk);
// disk.setDosVersion (dosDisk.getVersion ());
return dosDisk;
}
}
catch (Exception e)
@ -599,7 +686,7 @@ public class DiskFactory
{
if (debug)
{
System.out.println ("\nChecking Prodos hard disk");
System.out.println ("\nChecking Prodos/Pascal hard disk");
System.out.printf ("Total blocks : %f%n", (float) file.length () / 512);
System.out.printf ("Total tracks : %f%n", (float) file.length () / 4096);
System.out.printf ("File length : %d%n", file.length ());
@ -622,13 +709,19 @@ public class DiskFactory
{
System.out.println ("*** extended ***"); // System Addons.hdv
}
AppleDisk disk = new AppleDisk (file, tracks, 8);
AppleDisk disk;
// if (nuFX == null)
disk = new AppleDisk (file, tracks, 8);
// else
// disk = new AppleDisk (file, tracks, 8, nuFX);
if (ProdosDisk.isCorrectFormat (disk))
{
if (debug)
System.out.println (" --> PRODOS hard disk");
return new ProdosDisk (disk);
}
if (PascalDisk.isCorrectFormat (disk, debug))
{
if (debug)
@ -639,11 +732,12 @@ public class DiskFactory
catch (Exception e)
{
System.out.println (e);
e.printStackTrace ();
System.out.println ("Prodos hard disk had error");
}
if (debug)
System.out.println (" not a Prodos hard disk\n");
System.out.println (" not a Prodos/Pascal hard disk\n");
return null;
}
@ -768,11 +862,16 @@ public class DiskFactory
if (Wizardry4BootDisk.isWizardryIVorV (disk, debug))
{
if (debug)
System.out.println ("checking Wizardry IV or V");
String fileName = file.getAbsolutePath ().toLowerCase ();
int pos = file.getAbsolutePath ().indexOf ('.');
char c = fileName.charAt (pos - 1);
// String suffix = fileName.substring (pos + 1);
int requiredDisks = c == '1' ? 6 : c == 'a' ? 10 : 0;
int pos = fileName.lastIndexOf ('.');
char c = fileName.charAt (pos - 1); // '1' (wiz4) or 'a' (wiz5)
int requiredDisks = c == '1' ? 7 : c == 'a' ? 10 : 0;
if (debug)
System.out.printf ("Required disks: %d%n", requiredDisks);
if (requiredDisks > 0)
{
@ -793,7 +892,7 @@ public class DiskFactory
}
}
if (debug)
System.out.println ("Not a Wizardry IV disk");
System.out.println ("Not a Wizardry IV or V disk");
PascalDisk pascalDisk = new PascalDisk (disk);
return pascalDisk;
@ -803,6 +902,9 @@ public class DiskFactory
private static boolean collectDataDisks (String fileName, int dotPos, AppleDisk[] disks)
// ---------------------------------------------------------------------------------//
{
if (debug)
System.out.println ("Collecting Wizardry disks");
char c = fileName.charAt (dotPos - 1);
String suffix = fileName.substring (dotPos + 1);
@ -810,14 +912,16 @@ public class DiskFactory
{
String old = new String (c + "." + suffix);
String rep = new String ((char) (c + i - 1) + "." + suffix);
File f = new File (fileName.replace (old, rep));
if (debug)
System.out.println (f);
if (!f.exists () || !f.isFile ())
return false;
AppleDisk dataDisk = new AppleDisk (f, 35, 8);
dataDisk.setInterleave (1);
disks[i] = dataDisk;
disks[i] = new AppleDisk (f, 35, 8);
disks[i].setInterleave (1);
}
return true;
@ -858,4 +962,22 @@ public class DiskFactory
return null;
}
// ---------------------------------------------------------------------------------//
private static byte[] getPrefix (File file)
// ---------------------------------------------------------------------------------//
{
byte[] buffer = new byte[1024];
try (BufferedInputStream bis = new BufferedInputStream (new FileInputStream (file)))
{
bis.read (buffer);
}
catch (IOException e)
{
e.printStackTrace ();
System.exit (1);
}
return buffer;
}
}

View File

@ -81,7 +81,4 @@ public interface FormattedDisk
public int falseNegativeBlocks ();
public String getName ();
}
// getFileTypeList ()
// getFiles (FileType type)
}

View File

@ -2,6 +2,7 @@ package com.bytezone.diskbrowser.disk;
import java.awt.Dimension;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JTree;
@ -16,21 +17,21 @@ import com.bytezone.diskbrowser.gui.DataSource;
// Should be renamed MultiVolumeDisk (and allow >2 volumes)
// -----------------------------------------------------------------------------------//
public class DualDosDisk implements FormattedDisk
public class HybridDisk implements FormattedDisk
// -----------------------------------------------------------------------------------//
{
private final FormattedDisk[] disks = new FormattedDisk[2];
private final List<FormattedDisk> disks = new ArrayList<> (2);
private int currentDisk;
private final JTree tree;
// ---------------------------------------------------------------------------------//
public DualDosDisk (FormattedDisk disk0, FormattedDisk disk1)
public HybridDisk (FormattedDisk disk0, FormattedDisk disk1)
// ---------------------------------------------------------------------------------//
{
assert disk0 != disk1;
String diskName = disk0.getDisk ().getFile ().getName ();
String text = "This disk contains files from DOS and another OS\n\n"
+ disk0.getDisk () + "\n\n" + disk1.getDisk ();
String text = "This disk is a hybrid of two or more OS\n\n" + disk0.getDisk ()
+ "\n\n" + disk1.getDisk ();
DefaultAppleFileSource dafs = new DefaultAppleFileSource (diskName, text, this);
DefaultMutableTreeNode root = new DefaultMutableTreeNode (dafs);
@ -41,8 +42,8 @@ public class DualDosDisk implements FormattedDisk
// allow empty nodes to appear as folders
treeModel.setAsksAllowsChildren (true);
disks[0] = disk0;
disks[1] = disk1;
disks.add (disk0);
disks.add (disk1);
disk0.setParent (this);
disk1.setParent (this);
@ -69,7 +70,7 @@ public class DualDosDisk implements FormattedDisk
public List<DiskAddress> getFileSectors (int fileNo)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getFileSectors (fileNo);
return disks.get (currentDisk).getFileSectors (fileNo);
}
// ---------------------------------------------------------------------------------//
@ -77,7 +78,7 @@ public class DualDosDisk implements FormattedDisk
public List<AppleFileSource> getCatalogList ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getCatalogList ();
return disks.get (currentDisk).getCatalogList ();
}
// ---------------------------------------------------------------------------------//
@ -85,7 +86,7 @@ public class DualDosDisk implements FormattedDisk
public DataSource getFormattedSector (DiskAddress da)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getFormattedSector (da);
return disks.get (currentDisk).getFormattedSector (da);
}
// ---------------------------------------------------------------------------------//
@ -93,7 +94,7 @@ public class DualDosDisk implements FormattedDisk
public SectorType getSectorType (DiskAddress da)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getSectorType (da);
return disks.get (currentDisk).getSectorType (da);
}
// ---------------------------------------------------------------------------------//
@ -101,7 +102,7 @@ public class DualDosDisk implements FormattedDisk
public SectorType getSectorType (int track, int sector)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getSectorType (track, sector);
return disks.get (currentDisk).getSectorType (track, sector);
}
// ---------------------------------------------------------------------------------//
@ -109,7 +110,7 @@ public class DualDosDisk implements FormattedDisk
public SectorType getSectorType (int block)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getSectorType (block);
return disks.get (currentDisk).getSectorType (block);
}
// ---------------------------------------------------------------------------------//
@ -117,7 +118,7 @@ public class DualDosDisk implements FormattedDisk
public List<SectorType> getSectorTypeList ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getSectorTypeList ();
return disks.get (currentDisk).getSectorTypeList ();
}
// ---------------------------------------------------------------------------------//
@ -125,23 +126,30 @@ public class DualDosDisk implements FormattedDisk
public Disk getDisk ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getDisk ();
return disks.get (currentDisk).getDisk ();
}
// ---------------------------------------------------------------------------------//
public void setCurrentDisk (FormattedDisk fd)
// ---------------------------------------------------------------------------------//
{
if (disks[0] == fd)
currentDisk = 0;
else if (disks[1] == fd)
currentDisk = 1;
else
{
// this happens when the top-level folder is selected (i.e. neither disk)
System.out.println ("Disk not found: " + fd);
// Utility.printStackTrace ();
}
for (int i = 0; i < disks.size (); i++)
if (disks.get (i) == fd)
{
currentDisk = i;
break;
}
// if (disks[0] == fd)
// currentDisk = 0;
// else if (disks[1] == fd)
// currentDisk = 1;
// else
// {
// // this happens when the top-level folder is selected (i.e. neither disk)
// System.out.println ("Disk not found: " + fd);
// // Utility.printStackTrace ();
// }
}
// ---------------------------------------------------------------------------------//
@ -162,7 +170,7 @@ public class DualDosDisk implements FormattedDisk
public FormattedDisk getCurrentDisk ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk];
return disks.get (currentDisk);
}
// ---------------------------------------------------------------------------------//
@ -170,7 +178,7 @@ public class DualDosDisk implements FormattedDisk
public void writeFile (AbstractFile file)
// ---------------------------------------------------------------------------------//
{
disks[currentDisk].writeFile (file);
disks.get (currentDisk).writeFile (file);
}
// ---------------------------------------------------------------------------------//
@ -178,10 +186,18 @@ public class DualDosDisk implements FormattedDisk
public AppleFileSource getCatalog ()
// ---------------------------------------------------------------------------------//
{
return new DefaultAppleFileSource ("text",
disks[0].getCatalog ().getDataSource ().getText () + "\n\n"
+ disks[1].getCatalog ().getDataSource ().getText (),
this);
StringBuilder text = new StringBuilder ();
for (FormattedDisk disk : disks)
{
text.append (disk.getCatalog ().getDataSource ().getText ());
text.append ("\n\n");
}
text.deleteCharAt (text.length () - 1);
text.deleteCharAt (text.length () - 1);
return new DefaultAppleFileSource ("text", text.toString (), this);
}
// ---------------------------------------------------------------------------------//
@ -189,7 +205,7 @@ public class DualDosDisk implements FormattedDisk
public AppleFileSource getFile (String uniqueName)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getFile (uniqueName);
return disks.get (currentDisk).getFile (uniqueName);
}
// ---------------------------------------------------------------------------------//
@ -197,7 +213,7 @@ public class DualDosDisk implements FormattedDisk
public int clearOrphans ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].clearOrphans ();
return disks.get (currentDisk).clearOrphans ();
}
// ---------------------------------------------------------------------------------//
@ -205,7 +221,7 @@ public class DualDosDisk implements FormattedDisk
public boolean isSectorFree (DiskAddress da)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].isSectorFree (da);
return disks.get (currentDisk).isSectorFree (da);
}
// ---------------------------------------------------------------------------------//
@ -213,7 +229,7 @@ public class DualDosDisk implements FormattedDisk
public void verify ()
// ---------------------------------------------------------------------------------//
{
disks[currentDisk].verify ();
disks.get (currentDisk).verify ();
}
// ---------------------------------------------------------------------------------//
@ -221,7 +237,7 @@ public class DualDosDisk implements FormattedDisk
public boolean stillAvailable (DiskAddress da)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].stillAvailable (da);
return disks.get (currentDisk).stillAvailable (da);
}
// ---------------------------------------------------------------------------------//
@ -229,7 +245,7 @@ public class DualDosDisk implements FormattedDisk
public void setSectorType (int block, SectorType type)
// ---------------------------------------------------------------------------------//
{
disks[currentDisk].setSectorType (block, type);
disks.get (currentDisk).setSectorType (block, type);
}
// ---------------------------------------------------------------------------------//
@ -237,7 +253,7 @@ public class DualDosDisk implements FormattedDisk
public void setSectorFree (int block, boolean free)
// ---------------------------------------------------------------------------------//
{
disks[currentDisk].setSectorFree (block, free);
disks.get (currentDisk).setSectorFree (block, free);
}
// ---------------------------------------------------------------------------------//
@ -245,7 +261,7 @@ public class DualDosDisk implements FormattedDisk
public int falseNegativeBlocks ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].falseNegativeBlocks ();
return disks.get (currentDisk).falseNegativeBlocks ();
}
// ---------------------------------------------------------------------------------//
@ -253,7 +269,7 @@ public class DualDosDisk implements FormattedDisk
public int falsePositiveBlocks ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].falsePositiveBlocks ();
return disks.get (currentDisk).falsePositiveBlocks ();
}
// ---------------------------------------------------------------------------------//
@ -261,7 +277,7 @@ public class DualDosDisk implements FormattedDisk
public Dimension getGridLayout ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getGridLayout ();
return disks.get (currentDisk).getGridLayout ();
}
// ---------------------------------------------------------------------------------//
@ -269,7 +285,7 @@ public class DualDosDisk implements FormattedDisk
public boolean isSectorFree (int block)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].isSectorFree (block);
return disks.get (currentDisk).isSectorFree (block);
}
// ---------------------------------------------------------------------------------//
@ -277,7 +293,7 @@ public class DualDosDisk implements FormattedDisk
public boolean stillAvailable (int block)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].stillAvailable (block);
return disks.get (currentDisk).stillAvailable (block);
}
// ---------------------------------------------------------------------------------//
@ -285,7 +301,7 @@ public class DualDosDisk implements FormattedDisk
public void setOriginalPath (Path path)
// ---------------------------------------------------------------------------------//
{
disks[currentDisk].setOriginalPath (path);
disks.get (currentDisk).setOriginalPath (path);
}
// ---------------------------------------------------------------------------------//
@ -293,7 +309,7 @@ public class DualDosDisk implements FormattedDisk
public String getAbsolutePath ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getAbsolutePath ();
return disks.get (currentDisk).getAbsolutePath ();
}
// ---------------------------------------------------------------------------------//
@ -301,7 +317,7 @@ public class DualDosDisk implements FormattedDisk
public String getDisplayPath ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getDisplayPath ();
return disks.get (currentDisk).getDisplayPath ();
}
// ---------------------------------------------------------------------------------//
@ -309,7 +325,7 @@ public class DualDosDisk implements FormattedDisk
public FormattedDisk getParent ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getParent ();
return disks.get (currentDisk).getParent ();
}
// ---------------------------------------------------------------------------------//
@ -317,7 +333,7 @@ public class DualDosDisk implements FormattedDisk
public void setParent (FormattedDisk disk)
// ---------------------------------------------------------------------------------//
{
disks[currentDisk].setParent (disk);
disks.get (currentDisk).setParent (disk);
}
// ---------------------------------------------------------------------------------//
@ -325,7 +341,7 @@ public class DualDosDisk implements FormattedDisk
public String getSectorFilename (DiskAddress da)
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getSectorFilename (da);
return disks.get (currentDisk).getSectorFilename (da);
}
// ---------------------------------------------------------------------------------//
@ -333,7 +349,7 @@ public class DualDosDisk implements FormattedDisk
public String getName ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getName ();
return disks.get (currentDisk).getName ();
}
// ---------------------------------------------------------------------------------//
@ -341,7 +357,7 @@ public class DualDosDisk implements FormattedDisk
public boolean isTempDisk ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].isTempDisk ();
return disks.get (currentDisk).isTempDisk ();
}
// ---------------------------------------------------------------------------------//
@ -349,6 +365,6 @@ public class DualDosDisk implements FormattedDisk
public Path getOriginalPath ()
// ---------------------------------------------------------------------------------//
{
return disks[currentDisk].getOriginalPath ();
return disks.get (currentDisk).getOriginalPath ();
}
}

View File

@ -7,13 +7,25 @@ import com.bytezone.diskbrowser.utilities.Utility;
public class Prefix2mg
// -----------------------------------------------------------------------------------//
{
String[] creators = { "!nfc", "B2TR", "CTKG", "CdrP", "ShIm", "WOOF", "XGS!" };
String[] images = { "Dos3.3", "Prodos", "Nibbized" };
String prefix;
String creator;
int headerSize;
int version;
byte format;
int diskData;
int format;
int flags;
int length;
int blocks;
int offset;
int commentOffset;
int commentLength;
int creatorOffset;
int creatorLength;
boolean flagsLocked;
int flagsVolume;
// ---------------------------------------------------------------------------------//
public Prefix2mg (byte[] buffer)
@ -21,14 +33,29 @@ public class Prefix2mg
{
prefix = new String (buffer, 0, 4);
creator = new String (buffer, 4, 4);
headerSize = Utility.getWord (buffer, 8);
version = Utility.getWord (buffer, 10);
format = buffer[12];
headerSize = Utility.getShort (buffer, 0x08);
version = Utility.getShort (buffer, 0x0A);
format = Utility.getLong (buffer, 0x0C);
flags = Utility.getLong (buffer, 0x10);
blocks = Utility.getLong (buffer, 0x14); // 1600
offset = Utility.getLong (buffer, 0x18);
length = Utility.getLong (buffer, 0x1C);
commentOffset = Utility.getLong (buffer, 0x20);
commentLength = Utility.getLong (buffer, 0x24);
creatorOffset = Utility.getLong (buffer, 0x28);
creatorLength = Utility.getLong (buffer, 0x2C);
diskData = Utility.getLong (buffer, 28);
blocks = Utility.intValue (buffer[20], buffer[21]); // 1600
flagsLocked = (flags & 0x80000000) != 0;
if ((flags & 0x0100) != 0)
flagsVolume = flags & 0xFF;
if (format == 0 && flagsVolume == 0)
flagsVolume = 254;
if (length == 0)
length = 512 * blocks;
// see /Asimov disks/images/gs/os/prodos16/ProDOS 16v1_3.2mg
// System.out.println (this);
}
// ---------------------------------------------------------------------------------//
@ -38,14 +65,21 @@ public class Prefix2mg
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Prefix : %s%n", prefix));
text.append (String.format ("Creator : %s%n", creator));
text.append (String.format ("Header : %d%n", headerSize));
text.append (String.format ("Version : %d%n", version));
text.append (String.format ("Format : %02X%n", format));
text.append (String.format ("Data size : %08X (%<,d)%n", diskData));
text.append (String.format ("Blocks : %,d%n", blocks));
text.append (String.format ("Prefix : %s%n", prefix));
text.append (String.format ("Creator : %s%n", creator));
text.append (String.format ("Header : %d%n", headerSize));
text.append (String.format ("Version : %d%n", version));
text.append (String.format ("Format : %02X%n", format));
text.append (String.format ("Flags : %,d%n", flags));
text.append (String.format ("Locked : %s%n", flagsLocked));
text.append (String.format ("DOS Volume : %,d%n", flagsVolume));
text.append (String.format ("Blocks : %,d%n", blocks));
text.append (String.format ("Offset : %,d%n", offset));
text.append (String.format ("Length : %08X (%<,d)%n", length));
text.append (String.format ("Comment Offset : %,d%n", commentOffset));
text.append (String.format ("Comment Length : %08X (%<,d)%n", commentLength));
text.append (String.format ("Creator Offset : %,d%n", creatorOffset));
text.append (String.format ("Creator Length : %08X (%<,d)", creatorLength));
return text.toString ();
}

View File

@ -4,6 +4,7 @@ import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
// https://www.discferret.com/wiki/Apple_DiskCopy_4.2
// Apple II File Type Notes $E0/0005 (macintosh)
// -----------------------------------------------------------------------------------//
public class PrefixDiskCopy
// -----------------------------------------------------------------------------------//
@ -11,9 +12,11 @@ public class PrefixDiskCopy
private String name;
private int dataSize;
private int tagSize;
private int encoding;
private int dataChecksum;
private int tagChecksum;
private int diskFormat;
private int format;
private int id;
private int id; // should be 0x0100
// ---------------------------------------------------------------------------------//
public PrefixDiskCopy (byte[] buffer)
@ -24,9 +27,11 @@ public class PrefixDiskCopy
name = HexFormatter.getPascalString (buffer, 0);
dataSize = Utility.getLongBigEndian (buffer, 0x40);
tagSize = Utility.getLongBigEndian (buffer, 0x44);
encoding = buffer[0x50] & 0xFF;
dataChecksum = Utility.getLongBigEndian (buffer, 0x48);
tagChecksum = Utility.getLongBigEndian (buffer, 0x4C);
diskFormat = buffer[0x50] & 0xFF;
format = buffer[0x51] & 0xFF;
id = Utility.getWordBigEndian (buffer, 0x52);
id = Utility.getShortBigEndian (buffer, 0x52);
}
// ---------------------------------------------------------------------------------//
@ -43,12 +48,14 @@ public class PrefixDiskCopy
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Name : %s%n", name));
text.append (String.format ("Data size : %08X (%<,d)%n", dataSize));
text.append (String.format ("Tag size : %08X (%<,d)%n", tagSize));
text.append (String.format ("Encoding : %02X%n", encoding));
text.append (String.format ("Format : %02X%n", format));
text.append (String.format ("ID : %04X%n", id));
text.append (String.format ("Name : %s%n", name));
text.append (String.format ("Data size : %08X (%<,d)%n", dataSize));
text.append (String.format ("Tag size : %08X (%<,d)%n", tagSize));
text.append (String.format ("Data checksum : %08X (%<,d)%n", dataChecksum));
text.append (String.format ("Tag checksum : %08X (%<,d)%n", tagChecksum));
text.append (String.format ("Disk format : %02X%n", diskFormat));
text.append (String.format ("Format byte : %02X%n", format));
text.append (String.format ("ID : %04X%n", id));
return text.toString ();
}

View File

@ -17,8 +17,7 @@ public class SectorListConverter
sectors = new ArrayList<> ();
sectorText = text;
String[] blocks = text.split (";");
for (String s : blocks)
for (String s : text.split (";"))
{
int pos = s.indexOf ('-');
if (pos > 0)

Some files were not shown because too many files have changed in this diff Show More