Casio PD‑700 — Часть 8: содержимое внешней ROM и секреты синтаксиса

    На данный момент не существует известного способа считать программу, содержащуюся в процессоре HD62034 или HD62002 - по крайней мере мне таковой пока что найти не удалось. Зато есть возможность считать содержимое внешней микросхемы ROM.
    Как было видно при анализе содержимого RAM, тут используются 3-х байтовые адреса и содержимое самого RAM адресуется в диапазоне &H000000-&H00FFFF. Хотя оператор PEEK принимает аргумент не более 65535, то есть в пределах 64 кБ, благодаря оператору DEFSEG можно изменить начальный адрес и заглянуть в другие области.
    Размер одного сегмента для DEFSEG равен 16 байтам, и если установить DEFSEG, к примеру, равным 4096 (65536/16), то с помощью PEEK уже можно будет получить доступ к диапазону &H010000-&H01FFFF. Эксперименты показали, что ROM находится в диапазоне &H040000-&H05FFFF, и дублируется в диапазоне &H060000-&H07FFFF. Я немного изменил программу, с помощью которой считывал RAM:
5 POKE &H0E5D,&H60
10 DEFSEG=16384
20 OPEN "COM0:" FOR OUTPUT AS #1
30 FOR I=0 TO 65535
40 PRINT #1, RIGHT$(HEX$(PEEK I),2);
50 NEXT I
60 CLOSE #1
70 BEEP:BEEP1:BEEP:BEEP1:BEEP:BEEP1
    В строке номер 5 я заменяю байт, отвечающий за скорость связи по "COM0:", чтобы установить максимальную допустимую скорость – 19200 bps. Но даже так передача одного блока занимает более получаса. Для считывания блока 04 значение DEFSEG нужно установить в 16384 (4096*4), а для блока 05 – в 20480 (4096*5). И вот, наконец-то, у меня полный дамп содержимого внешней ROM, и можно приступать к исследованию.

    Общая структура содержимого внешней ROM:
  • [040000] - Заголовок ROM с основной информацией
  • [040040] - Таблицы назначения клавиш для разных режимов
  • [040250] - Имена файлов
  • [040300] - Адрес программы "K0"
  • [040400] - Программа "K0"
  • [043A00] - Битовая карта символов 6х8 режима WIDTH,4
  • [044000] - Битовая карта символов 8х16 режима WIDTH,2
  • [044E00] - Битовая карта верхних символов 8х16 режима простого калькулятора
  • [044F00] - Битовая карта цифр 8х16 режима простого калькулятора
  • [045000] - Битовая карта двухбайтовых символов 16x16 кодировки Shift JIS

    Заголовок содержит следующую информацию:
  • 43 — StartByte?
  • 4D04
  • 000002 — ROM upper bound: 131072 => 128 kB
  • 0101 — Version for ROM Card
  • 00003532322D3100 — Program name and version: 522-1
  • 870303 — Program date: 1987, March 3
  • FFFFFFFFFFFFFFFF
  • 000304
  • 000304 — address of "K0" program address
  • 000304
    Отсюда можно узнать, что программа называется "522-1" и была написана 3 марта 1987 года. Версия для совместимости ROM-карты имеет номер 0101, а так как у PB-2000C она 0104, можно предположить, что между ними были выпущены ещё 2 модели с подобным форматом карт.

    Для наглядного представления очерёдности при адресации клавиш нужно переместить некоторые клавиши в таблице распиновки из правой части вверх:

    После этого адресация начинается с правой нижней клавиши и продолжается снизу вверх в каждом столбце справа налево.


    Содержание таблиц назначения клавиш для каждого режима, и соответствующие клавишам функции:

    Стандартный режим (EN)
38
32
44
42
43
31
37
18
39
33
58
47
4D
30
36
00
8
2
D
B
C
1
7
C
9
3
X
G
M
0
6
 
2A
34
53
54
4A
50
35
00
2F
2E
57
56
55
4C
11
00
*
4
S
T
J
P
5
 
/
.
W
V
U
L
DEL
 
F3
2B
5A
46
4E
4F
3A
0E
0F
2D
41
52
48
4B
2C
1E
MODE
+
Z
F
N
O
:
SHIFT
CAPS
-
A
R
H
K
,
UP
1C
0D
03
00
59
49
3D
1D
E0
E5
51
45
91
18
20
1F
RIGHT
EXE
CAL
 
Y
I
=
LEFT
P0
P5
Q
E
%
C
SPACE
DOWN
E1
E6
EB
93
37
34
31
30
E2
E7
95
92
38
35
32
9E
P1
P6
CONT.
MC
7
4
1
0
P2
P7
M-
MR
8
5
2
000
E3
E8
F0
94
39
36
33
2E
E4
E9
F1
2F
2A
2D
2B
0D
P3
P8
YES
M+
9
6
3
.
P4
P9
NO
/
*
-
+
=

    Стандартный режим с Shift (EN + SHIFT)
60
A3
88
82
85
A2
5F
18
7C
A1
81
8C
87
5C
7B
00
'
 
 
 
_
C
¦
 
 
 
¥
{
 
7E
5B
8A
25
8E
40
5D
00
3F
5E
22
86
27
84
12
00
~
[
 
%
 
@
]
 
?
^
"
 
'
 
INS
 
EA
A4
80
8B
83
29
3E
04
1A
7D
89
24
8D
8F
3B
0B
PA
 
 
 
)
>
SH.RL
KANA
}
 
$
 
 
;
HOME
06
0D
03
00
26
28
3C
02
E0
E5
21
23
91
18
20
0C
R.END
EXE
CAL
 
&
(
<
L.TOP
P0
P5
!
#
%
C
SPACE
CLS
E1
E6
EB
93
37
34
31
30
E2
E7
95
92
38
35
32
9E
P1
P6
CONT.
MC
7
4
1
0
P2
P7
M-
MR
8
5
2
000
E3
E8
F0
94
39
36
33
2E
E4
E9
F1
2F
2A
2D
2B
0D
P3
P8
YES
M+
9
6
3
.
P4
P9
NO
/
*
-
+
=
    Клавиши с кодом "8X" - это Box Drawing по таблице символов, но это не имеет значения, так как далее они переназначены для быстрого ввода команд BASIC.

    Режим простого калькулятора (CALC)
38
32
00
00
00
31
37
9D
39
33
00
00
00
30
36
00
8
2
 
 
 
1
7
AC
9
3
 
 
 
0
6
 
9A
34
00
00
00
00
35
00
9B
3A
00
00
00
00
00
00
*
4
 
 
 
 
5
 
/
.
 
 
 
 
 
 
F3
98
00
00
00
00
00
00
00
99
00
00
00
00
00
00
MODE
+
 
 
 
 
 
 
 
-
 
 
 
 
 
 
00
90
03
00
00
00
00
00
E0
E5
00
00
91
9C
00
00
 
EXE
CAL
 
 
 
 
 
P0
P5
 
 
%
C
 
 
E1
E6
EB
93
37
34
31
30
E2
E7
95
92
38
35
32
9E
P1
P6
CONT.
MC
7
4
1
0
P2
P7
M-
MR
8
5
2
000
E3
E8
F0
94
39
36
33
3A
E4
E9
F1
9B
9A
99
98
90
P3
P8
YES
M+
9
6
3
.
P4
P9
NO
/
*
-
+
=
    Сюда внесены оба набора клавиш цифр. Кода цифр и математических операторов не соответствуют таблице ASCII, а клавиши "C" и "AC" только в этом режиме имеют разные кода ("9C" и "9D" соответственно).

    Режим ввода символов Кана (KANA)
38
32
BD
C9
C7
31
37
18
39
33
C6
BF
B0
30
36
00
8
2
1
7
C
9
3
ソ
0
6
 
2A
34
BC
B5
C1
BA
35
00
2F
2E
B2
C8
B7
C3
11
00
*
4
5
 
/
.
DEL
 
F3
2B
C5
BE
DD
B9
DF
0E
0F
2D
BB
B4
C0
C2
C4
1E
MODE
+
SHIFT
CAPS
-
UP
1C
0D
03
00
B6
B8
DE
1D
E0
E5
B1
B3
91
18
20
1F
RIGHT
EXE
CAL
 
LEFT
P0
P5
%
C
SPACE
DOWN
E1
E6
EB
93
37
34
31
30
E2
E7
95
92
38
35
32
9E
P1
P6
CONT.
MC
7
4
1
0
P2
P7
M-
MR
8
5
2
000
E3
E8
F0
94
39
36
33
2E
E4
E9
F1
2F
2A
2D
2B
0D
P3
P8
YES
M+
9
6
3
.
P4
P9
NO
/
*
-
+
=

    Режим ввода символов Кана с Shift (KANA + SHIFT)
60
A3
D1
DB
D9
A2
5F
18
7C
A1
D8
D3
AD
5C
7B
00
'
_
C
¦
¥
{
 
7E
5B
D0
AB
D5
CE
5D
00
3F
5E
A8
DA
CB
DC
12
00
~
[
]
 
?
^
INS
 
EA
A4
D7
D2
AC
CD
AF
04
1B
7D
CF
AA
D4
D6
A6
0B
PA
SH.RL
EN
}
HOME
06
0D
03
00
CA
CC
AE
02
E0
E5
A7
A9
91
18
A5
0C
R.END
EXE
CAL
 
L.TOP
P0
P5
%
C
CLS
E1
E6
EB
93
37
34
31
30
E2
E7
95
92
38
35
32
9E
P1
P6
CONT.
MC
7
4
1
0
P2
P7
M-
MR
8
5
2
000
E3
E8
F0
94
39
36
33
2E
E4
E9
F1
2F
2A
2D
2B
0D
P3
P8
YES
M+
9
6
3
.
P4
P9
NO
/
*
-
+
=

    Назначение команд BASIC для быстрого ввода через Shift (BASIC COMMANDS)
14A3
169B
1459
146C
1457
146A
147C
1473
 
PRINT
INPUT
LOAD
SAVE
LIST
CLEAR
DIM
WIDTH
 
Z
X
B
N
L
C
V
M
 
1449
144A
144B
1481
17C1
1482
148D
1747
0000
GOTO
GOSUB
RETURN
FOR
TO
NEXT
IF
THEN
 
D
A
S
F
G
H
J
K
 

    Несуществующим клавишам, неиспользуемым клавишам каждого режима, а также клавише "RESET" выставлены нули, то есть во включённом состоянии калькулятор никак не мог бы реагировать на сброс.


    По адресу &H040250 лежат использующиеся в калькуляторе имена файлов, и вот их полный список:
RAM0: CARD: COM0: P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 PA D0 D1 D2 D3 D4 D5 D6 D7 D8 D9
Назначение и способ применения имён "RAM0" и "CARD" пока не обнаружены (выполнение GOTO "RAM0:P2" приводит к ошибке).

    Далее, по адресу &H040300, хранятся адреса начала и конца единственной программы - "K0". Той самой, которая запускается при нажатии в отверстие "CONT." и используется для регулировки контраста и остальных скрытых функций, таких как приём и отправка образа RAM и проверка чексумм. Теперь понятно, о чём калькулятор пытался сообщить ошибкой "SN error K0‑20" до правильного выполнения RESET.
    Адрес этой программы хранится в таком же виде, как и адреса пользовательских программ в RAM. Да и сама программа написана на BASIC. Сопоставляя байт за байтом я детокенизировал программу по тому же принципу, как и пользовательские программы.
10 WIDTH 12,2 20 KPRINT "955C,8EA6,945A,82AD,3A,59,45,53,20,20,20,20,9496,82AD,3A,4E,4F"; 30 •$=INPUT$(1,@) 40 IF ASC(•$)=240 THEN CONTUP 50 IF ASC(•$)=241 THEN CONTDOWN 60 IF ASC(•$)=43 THEN 100 70 IF ASC(•$)=45 THEN 200 80 GOTO 30 100 CLS : PRINT "LOAD RAM","OK ?"; 110 •$=INPUT$(1) 120 IF ASC(•$)=42 THEN GOSUB 300 : GOTO 100 130 IF ASC(•$)=47 THEN 400 140 IF ASC(•$)=240 OR ASC(•$)=13 THEN PRINT CHR$(24); : LOAD @ :ELSE END 200 CLS : PRINT "SAVE RAM","OK ?"; 210 •$=INPUT$(1) 220 IF ASC(•$)=42 THEN GOSUB 300 : GOTO 200 230 IF ASC(•$)=47 THEN 400 235 ON ERROR GOTO 1000 240 IF ASC(•$)=240 OR ASC(•$)=13 THEN PRINT CHR$(24); : SAVE @ :ELSE END 250 ON ERROR GOTO 0 300 IF PEEK(&HD5F)=255 THEN CLS : PRINT "19200 BPS" : •$=INPUT$(1,@) : IF ASC(•$)=42 THEN POKE &HD5F,0 : GOTO 310 :ELSE RETURN 310 IF PEEK(&HD5F)><255 THEN CLS : PRINT "9600 BPS" : •$=INPUT$(1,@) : IF ASC(•$)=42 THEN POKE &HD5F,255 : GOTO 320 :ELSE RETURN 320 GOTO 300 400 CLS : PRINT," CHECK SUM"," PRESS(1-3)"; 410 •$=INPUT$(1,@) 420 DEFSEG=0 430 ON ASC(•$)-48 GOTO 500,600,700 440 END 500 CLS:PRINT"PROG:";LRC$(PEEK&HCC8*256+PEEK&HCC7-1,&HFA0)," DIR:";LRC$(PEEK&HCEF*256+PEEK&HCEE+87,PEEK&HCEF*256+PEEK&HCEE); 510 •$=INPUT$(1,@) 520 GOTO 400 600 CLS:PRINT"FILE:";LRC$(PEEK&HCCB*256+PEEK&HCCA-1,&HFA0)," DIR:";LRC$(PEEK&HCEC*256+PEEK&HCEB+167,PEEK&HCEC*256+PEEK&HCEB); 610 •$=INPUT$(1,@) 620 GOTO 400 700 INPUT ""+CHR$(24)+"START?";@5;⌜ 710 IF ⌜ < -32768 OR ⌜ > 65535 THEN 700 720 INPUT " END?";@5;⌟ 730 IF ⌟ < -32768 OR ⌟ > 65535 THEN 720 740 IF ⌜ >= ⌟ THEN 700 750 CLS : PRINT " ";HEX$(⌜);"-";HEX$(⌟)," SUM:";LRC$(⌟,⌜); 760 •$=INPUT$(1,@) 770 GOTO 400 1000 PRINT,"PR error";:•$=INPUT$(1,@):END

    Хотя можно было прописать эти адреса для "P0", а затем отправить "P0" на компьютер командой SAVE "COM0:". Как бы там ни было, теперь, имея программу, написанную производителем, можно увидеть в ней некоторые скрытые особенности синтаксиса:

  • INPUT (скрытые особенности синтаксиса)
    Пример 1: A$=INPUT$(1,@) - вводимый текст не отображается, выполнение продолжается после ввода указанного количества символов. Символ "@" отключает отображение курсора. Это очень полезно, так как избавляет от необходимости в использовании костыля вроде "100 GOTO 100" для остановки программы после выполнения!
    Пример 2: INPUT "TEXT";@5;A - водимый текст отображается, выполнение продолжается только после нажатия "EXE", но ввод в данном случае ограничен 5-ю символами (при дальнейшем вводе заменяется последний). Хотя в режиме WIDTH,4 параметр, ограничивающий количество символов, может принимать и большее значение, в такой конструкции INPUT выдаёт "OV error" при вводе более 100 символов.
    В первом случае нажатие клавиши "STOP" останавливает программу, а все остальные клавиши, такие как "000", "C", "AC" и тд, вводят код соответствующей клавиши. Во втором случае все клавиши выполняют свои функции: "STOP" выполняет роль "CAPS", "000" вводит три нуля, "C" и "AC" очищают ввод.
    Интересно, что такие же скрытые возможности обнаружились и в FX‑860Pvc.

  • ON ERROR GOTO 0
    Отключает ранее объявленный в программе перехват ошибок и возвращает программу к стандартному поведению при их возникновении.

  • SAVE @ и LOAD @
    Для приёма и передачи полного образа RAM указывается символ "@" после SAVE и LOAD.

  • KPRINT (разделители)
    Данные для KPRINT для удобства можно писать через запятую. Причём оказалось, что вывод не изменится, даже если указать её между байтами двухбайтового символа. Главное, чтобы запятая не стояла между 16-ричными знаками одного байта.

    Кроме этого, разработчики почему-то указывают лишний аргумент для количества символов в строке: WIDTH 12,2. Для очистки строки они используют вывод символа 24, выполняющего функцию "LINE cancel". Для сообщения об ошибке во время отправки образа RAM на компьютер выводят "PR error" в виде текста - вероятно, в прошивке CPU не предусмотрена ошибка на такой случай.
    И теперь можно с большей уверенностью предположить, что выбор скорости для "COM0:" выполняется только заменой байта, ведь скорость приёма/передачи образа RAM в "K0" тоже меняется таким путём.


    Начиная с адреса &H043A00 хранятся битовые карты символов. Очерёдность тетрад внутри каждого байта отличается от используемой для DEFCHR$ и соответствует принципу расположения пикселей в Display Area.

    Область символов 6x8 пикселей для режима WIDTH,4 для наглядности можно разбить на блоки по 6 байт:
. . . . . 408001020400 C7A8292AC700 0024EF200000 2468A8292600 48282A2DC800 . . . . .
    Эта область занимает 1536 байт - объём, требуемый ровно для 256 символов. Но, так как графических символов фактически 224, в нижней части остаётся пустая область в размере 32 символов.
    Для поиска начала этой области я написал программу, превращающую данные, начиная с введённого адреса, в код для DEFCHR$ (с перестановкой тетрад), и выводящую на экран. Но удобнее было бы сразу копировать байты в Display Area. Например, следующий код выводит первые 64 символа:
10 FOR I=0 TO 383 20 DEFSEG=16384:A=PEEK(&H3A00+I) 30 DEFSEG=0:POKE(&H300+I),A 40 NEXT I 50 PRINT:K$=INPUT$(1,@)


    Область символов 8x16 пикселей для режима WIDTH,2 можно разбить на блоки по 16 байт, каждый из которых в свою очередь разделить пополам, так как символ состоит из двух строк:
. . . . . . . 00F001020201F000 000F804040800F00 00008001F3000000 00000040CF400000 00C001020211E000 00C142444840C100 00C001121221C000 0003804040880700 0000106081F30000 00060A0242CF4200 . . . . . . .
    Эта область также занимает объём, требуемый для 256 символов. Но в данном случае последние 32 ячейки хранят символы для режима простого калькулятора: часть первых 16-и содержит верхние символы, такие как математические операторы, "K", "E" и тд, а часть следующих 16-и - цифры.
    Битовые карты символов однобайтовой части кодировки, кода которых попадают в диапазон начальных байтов двухбайтовой части, тут заполнены нулями.

    Область символов 16x16 пикселей для двухбайтовых символов кодировки Shift JIS можно разбить на блоки по 30 байт (а не 32, как можно было бы подумать), каждый из которых в свою очередь также разделить пополам:
. . . . . . . 0074444444F7444444F74444447400 101F121212FF121212FF1212121F10 F30202F300744444F74444F7444474 0F02021E101E1212FF1212FF12121E 8090EF8080F0A0222222EF22222220 162A41814E2010121212FF12121210 F70444A5160004F48484F40404F704 FF0001010E00000F02020E0010FF00 020272424242424E42424242720202 909019E92A2C484C0B8848A8291210 . . . . . . .
    Дело в том, что правый столбец пикселей в каждом символе, оставляемый пустым для отступов, не записан. Логично, что при большом количестве символов экономия 2-х байт на каждом позволяет в сумме сэкономить много памяти. Поэтому символ фактически хранится как 15x16 пикселей.

        Если убрать пустую область с конца, получается, что для символов двухбайтовой кодировки выделено 108690 байт, а это значит, что всего таких символов 3623. Но, возможно, среди них тоже есть пустые, и реальное количество немного меньше.

Комментарии