Приветствую всех.

Продолжим тему разработки под ушедшие в историю платформы. Сегодня расскажем про органайзеры Casio серии Pocket Viewer, бывшие популярными в узких кругах в первой половине нулевых.



В ходе статьи узнаем, как и на чём под него писать, где взять софт, какие остались тематические сайты, а также много чего интересного.

Что это такое?


Casio Pocket Viewer позиционировался как электронный органайзер (при этом стоил он куда дешевле КПК «старших» моделей, который были основаны уже на Pocket PC и выпускались под линейкой Cassiopeia). Выпускалось их весьма большое количество моделей, но наибольшую популярность получили PV-S250 (PV-S450) и PV-S460 (PV-S660). Все они основаны на встраиваемой системе (на базе процессора NEC V30), имели монохромный экран разрешением 160х160, подключались к компьютеру по RS-232, работали на проприетарной ОС Pocket Viewer OS (или просто PVOS). Отличительной чертой был накопитель на флеш-памяти (первая цифра номера модели означает число её мегабайт), благодаря которому не требовалось постоянно следить за зарядом батареек, чтобы не потерять данные. Также поддерживалось и обновление ОС. Минусом этого варианта было низкое быстродействие — сохранение чего-либо занимало видимые секунды.
Таким образом, Casio PV был едва ли не последним массово производившимся КПК на базе процессора с архитектурой X86 (различные околопромышленные устройства вроде ТСД Symbol PDT или Psion Workabout в расчёт не берём). Последняя выпущенная модель, PV-S1600 имела подключение по USB и работала на процессоре SH-3, чем сильно отличалась от «классического» Pocket Viewer.

Главным конкурентом Pocket Viewer'а (в частности, модели PV-S450) был КПК Palm M100/M105, с которым чаще всего и сравнивался данный аппарат. PV имел ряд выгодных преимуществ: энергонезависимая память, колёсико прокрутки, удобное для чтения книг, большой экран, небывалое даже тогда время работы от батарей, стильный тонкий дизайн. Palm в ответ «брал» большим количеством приложений и лучшим, нежели PV, быстродействием.

В мои руки попал Casio PV-S450. Именно о нём и пойдёт речь дальше. Впрочем, всё, что будет сказано далее, справедливо для всех остальных моделей Casio Pocket Viewer на базе процессора x86. Устройства модели PV-S1600 у меня нет, так что тему разработки под него оставим до лучших времён. Также под управлением PVOS работали калькуляторы Casio ClassPad. За неимением у меня такого устройства, упоминание его тоже пока что скромно опустим.

Почему именно PV-S450?


На самом деле ставку на какую бы то ни было популярность этой модели я не делал. Всё дело в том, что PV-S450 был единственным экземпляром, который мне удалось достать в комплектном состоянии. На всем известном сайте объявлений можно найти устройства вместе с подставкой, но на момент написания статьи цена на все из них выходит далеко за рамки приличия.

На всякий случай напомню: единственно возможная связь с внешним миром обеспечивается через интерфейс RS-232. Casio PV не оснащён ни ИК-портом (за исключением одной-единственной модели, но установка приложений там не поддерживается, увы), ни каким-либо другим беспроводным интерфейсом, так что подключить его как-то иначе вряд ли выйдет. Кроме того, разъём подключения проприетарный (не думаю, что в обычном магазине радиодеталей вы его найдёте), так что покупка Casio PV без кабеля или подставки — крайне такое себе решение.
Также стоит учесть, что установка приложений возможна только на те КПК, в названии модели которых есть буква S (что, видимо, означает «Software»).

Обзор оборудования


Прежде чем переходить к средствам разработки, рассмотрим вначале сам органайзер.
У моего экземпляра интересное прошлое: ранее он принадлежал Дмитрию Newbilius Моисееву. Засветившись в его обзоре на YouTube, он был выставлен на вторичку и позже был приобретён мною. Ну что же, пришло его время снова оказаться продемонстрированным на просторах сети.



Экран КПК защищает пластиковая крышечка, которая у моего экземпляра даже умудрилась не отломаться и дожить до наших дней.



Помимо сенсорного экрана с сенсорными же подэкранными кнопками никаких элементов управления на передней панели нет.



На обратной стороне переключатель блокировки крышки отсека батареек (а заодно и выключатель питания), кнопка перезагрузки (у данного экземпляра она запала и не нажимается. Это особенность конкретного устройства: Pocket Viewer'ов у меня два, и на втором всё отлично работает), этикетка с моделью.



Слева джойстик прокрутки. Именно за этот элемент управления многие любили PV — читать на этом устройстве было очень удобно. Это не колёсико, аналогичное JogDial на КПК Sony, это именно джойстик, который можно нажимать или наклонять в две стороны.
На фото для сравнения джойстик у PV и у Sony Clié.



Снизу тот самый проприетарный разъём для подключения к компьютеру.



А вот и подставка. Весьма удобная, кстати.



Разбирать КПК я не стал: побоялся сломать защёлки. Но на просторах обнаружились фото внутренностей.
Отчётливо видны процессор системы «китайская капля», микросхема памяти (на фото представлена модель PV-S250, рядом видно место для ещё одной, которая установлена в модели PV-S450), ОЗУ, шлейфы, катушка преобразователя для ЭЛИ-подсветки.

Pocket Viewer SDK


Casio приняла правильное решение: средства разработки под данный КПК находились полностью в открытом доступе. В лучшие времена SDK можно было скачать с официального сайта.

Хотя для древнего софта правило «интернет ничего не забывает» традиционно не работает, SDK удалось найти вообще без проблем — находился он тут. Internet Archive продолжает радовать: помимо SDK обнаружились и архивы некоторых ныне ушедших в историю тематических сайтов.

Итак, для начала разработки под Casio PV понадобится примерно следующее:

  • Компьютер с ОС Windows 98/NT/2000 или виртуальная машина с таковой. На современные ОС установить SDK не выходит: инсталлятор банально не запускается. Впрочем, ничто не мешает поставить его на старой ОС, а затем перенести папки на новую, все компоненты запускаются даже на Windows 10 x64.
  • Собственно, сам Pocket Viewer SDK. Где его взять, уже было сказано чуть выше. Для вашего удобства все ссылки будут продублированы в конце поста.
  • Любой удобный для вас редактор кода, например, тот же Notepad++. Впрочем, при желании можно использовать и банальный «Блокнот».
  • Casio PVOS Application Manager. Позволяет устанавливать приложения в формате *.bin на реальный КПК. Также есть версия на русском языке.
  • КПК с подставкой для подключения к компьютеру. На нём будем запускать протестированные в эмуляторе приложения. Также следует отметить, что компьютер должен быть оснащён COM-портом. Различные переходники USB2COM работают, но крайне плохо.

Ставим софт


Установка SDK каких-любо нюансов не имеет, ставится он как и любое другое Windows-приложение. Так что документировать данный процесс смысла не вижу. После установки в корне системного диска появятся папки LSIJ и CASIO. В первой находится компилятор LSIC86PV, позволяющий собрать приложение для Pocket Viewer OS. Во второй будет находиться вложенная папка (название зависит от версии установленного вами SDK), где лежат все остальные средства разработки: библиотеки, примеры программ (впрочем, для введения в разработку под PVOS они сложноваты), документация, эмулятор, Application Manager.
Больше никаких манипуляций типа установки PATH или чего-то вроде этого не требуется.

Симулятор


В папке SIM находится симулятор данного КПК. Это не полный его эмулятор, то есть вполне возможно, что поведение программы в нём и на реальном устройстве будет отличаться. С этим ничего не поделать.



Для запуска открываем имеющийся в папке рядом с ним проект PV-S450 и всё, можно запускать.
Для того, чтобы поменять установленное приложение, жмём на панели меню «Project», далее «Configuration», в открывшемся окне переходим на вкладку «Chips».



Далее ищем в списке «SAMPLE», жмякаем правой кнопкой мыши, выбираем «Proparties» (угу, так и написано), выбираем нужный нам бинарник вместо sample.bin.



Не забываем после каждого изменения (например, после очередной компиляции) перезагрузить наш виртуальный КПК.

Разумеется, можно использовать данную прогу не только для разработки, а ещё и для того, чтобы запустить софт для PVOS при отсутствии самого КПК.

Application Manager


Для установки и удаления приложений на реальном КПК существует Application Manager. Пользоваться им предельно просто: запускаем программу, насаживаем КПК на подставку, инициируем загрузку (через кнопку «Menu bar» из главного экрана, а не кнопкой на подставке, иначе будет ошибка связи!), запускаем обмен данными крайней левой кнопочкой на панели меню Application Manager.



Установив связь и получив список программ, добавляем или удаляем нужные, а затем производим обмен данными (той же самой кнопкой или «Execute\Update PV»). Всё, приложение установлено (ну, или снесено).

Структура проекта


Итак, заглянем в папку C и посмотрим, что же у нас там лежит. В папке Bin хранятся скомпилированные бинарники, пригодные для загрузки в КПК или запуска в симуляторе. Также есть несколько папок с библиотеками, а также разделы Sample и Sample1. Это и есть проекты. Откроем какой-нибудь из них. В каждой из этих двух папок лежит Makefile, батник для сборки, каталоги с исходниками. Также есть папка MENUICON — там хранится иконка приложения для отображения в списке в главном меню.
Запустим батник и убедимся, что проект успешно компилируется.

Пишем первую программу


Ну что, со сборкой разобрались. Пришло время написать что-то своё. Итак, создаём в папке C ещё какую-нибудь папку, скажем, Test. А в ней — такую же структуру папок, какую видели в других проектах. Копируем также содержимое MENUICON.
Берём MAKEFILE из уже имеющегося проекта и слегка модифицируем его под наши задачи. Примерно так:

#Makefile for PocketViewer2 Sample Program

include ..\COM_LNK\MakeSDK.1

### -------- Define Make Application -------- ###

#== TargetName ==
TARGET = test

#== Program Name ==
PROGNAME = "test"

#== ProgramVersion(EX. 0100->Ver1.00) ==
VERSION = 0100

#== MenuIcon (Xsize=45dot,Ysize=28dot) ==
MICON = menuicon\icon.bmp

#== ListMenuIcon (Xsize=27dot,Ysize=20dot) ==
LICON = menuicon\Licon.bmp

#== CompileObjectFile ==
APLOBJS = $(ODIR)\test.obj

### ----------------------------------------- ###

include ..\COM_LNK\MakeSDK.2

В папке C создаём какой-нибудь файл, скажем, test.c. И пишем там следующее:

#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"

TCHTBL TchList[1] = 
{
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

void main()
{
	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]);


	LibClrDisp();
	LibPutDisp();
	LibTchInit();

	while(1)
	{
		LibTchWait(&tsts);           
	}

	LibTchStackClr();
	LibJumpMenu();

}

С батником для сборки заморачиваться не будем — позаимствуем его у того же предыдущего проекта. Запускаем его. Если всё было сделано правильно, компиляция должна пройти успешно, а в папке Bin появится наш бинарник test.bin. Загружаем его в симулятор и пробуем запускать. И получаем совершенно пустой экран. Да, всё так и должно быть. Что важно, КПК при этом не завис, не перезагрузился и никак иначе не заглючил. Нажмём «Menu», и программа закроется.

Что тут происходит?


Теперь разберёмся, как оно вообще работает.

TCHSTS tsts; — создание структуры для работы с сенсорным экраном. Именно из неё мы будем получать все данные о касаниях.

Следующие четыре строки — инициализация стека сенсорного экрана. В ходе данной процедуры мы очищаем стек и загружаем туда две таблицы — TchHardIcon и TchList. Таблица касаний — это сведения о каждом объекте на сенсорном экране, доступном для касания. Первая из них — это подэкранные кнопки (Menu, Esc и все остальные), вторая — пользовательские объекты. Их определяет созданный в начале текста программы массив:

TCHTBL TchList[1] = 
{
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

Именно в него должны будут записываться данные обо всех размещённых нами на экране элементах управления. Для каждого элемента необходимо указать координаты, в пределах которых на него можно нажать, а также ряд параметров вроде ID этого объекта. Подробнее об этом поговорим чуть позже.

Следом идут ещё две команды — LibClrDisp() и LibPutDisp(). Первая из них производит очистку, вторая — выводит на дисплей содержимое экранного буфера. Без вызова LibPutDisp() изображение на экране останется без изменений.

Далее идёт бесконечный цикл, в котором мы вызываем функцию LibTchWait(&tsts). Дело в том, что для получения данных сенсорного экрана его необходимо постоянно опрашивать. Если мы уберём эту функцию, при запуске программы она зависнет, не в силах считать даже нажатие кнопки «Menu». После опроса мы можем получить данные о том, что было нажато на экране.

И, напоследок, очистка стека тачскрина и выход в меню.

Hello, world!


Попробуем вывести что-то на экран. Для этого предусмотрена функция LibStringDisp().
Добавим её в нашу первую программу:

#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"

TCHTBL TchList[1] = 
{
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

void main()
{
	TCHSTS tsts;
	char * str = "Hello, Habrahabr!";

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibPutDisp();
	LibTchInit();

	LibStringDsp(str,0,0,100,IB_PFONT2);
	LibPutDisp();
	
	while(1)
	{
		LibTchWait(&tsts);           
	}

	LibTchStackClr();
	LibJumpMenu();

}


Первый аргумент данной функции — указатель на строку, далее идут координаты, максимальный размер (в пикселях) и шрифт.

Компилируем, загружаем в симулятор:



Работает. Кто бы сомневался. Время загрузить в «железный» девайс. Открываем Application Manager, соединяемся…



Жмём на иконку нашего приложения, и на экране появляется примерно следующее:



Отлично. Работает.

BMP


Вывели текст — попробуем вывести и картинку. Такую, например:



Разумеется, нельзя просто так взять и загрузить её в память PV. Для этого необходимо сделать ряд нехитрых манипуляций.

Перво-наперво, откроем Photoshop и сделаем её такой:



Обесцветим её, а заодно и подгоним под размер экрана.

Теперь откроем папку Tools из комплекта SDK, где лежит утилита для преобразования BMPшек в массив. Запускаем её, открываем в ней нашу картинку, конвертируем:



На выходе получаем файл с расширением *.BMT. Не буду копировать всё его содержимое, покажу только первые несколько строк:

	GSIZE(152, 160),
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x83, 0x8F, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFC, 0x01, 0x0F, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xDF, 0xFE, 0x82, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD9, 0xFF, 0x80, 0xF7, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD9, 0x8F, 0x80, 0xD7, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xBE, 0xCF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFC, 0x60, 0x0F, 0x80, 0x27, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x3B, 0xCF, 0xFF, 0xFC, 0xFF, 0xFD, 0xFC, 0x00, 0x0F, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,

Первые байты массива отражают размер картинки, далее идёт стандартный bitmap.
Программа в итоге получается такая:

#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_ICN1 0x9000

TCHTBL TchList[2] = 
{
		0, 0, 
		152, 160,
		ACT_ICON,
		OBJ_ICN1,
		0x0000,
		
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {
/*Вот здесь должно быть полное содержимое *.BMT-файла*/
};

T_ICON newIcon = {&TchList[0], bitmap, NULL, 0x01};

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&newIcon);
	LibPutDisp();
	LibTchInit();
	
	while(1)
	{
		LibTchWait(&tsts);
	}

	LibTchStackClr();
	LibJumpMenu();

}

OBJ_ICN1 — это ID элемента, находящегося на экране. В качестве него можно взять любое значение в диапазоне 0x8000-0xFFFF. Далее пропишем нашу картинку в TchList. Укажем координаты (x1, y1, x2, y2), а также то, что этот объект — иконка. Создадим массив байт, куда скопируем наш файл *.BMT, а также объект newIcon — он связывает элемент из TchList и изображение. В функции main() добавится строка LibIconPrint(&newIcon); — вывод картинки на экран.

Компилируем, и всё, можно запускать!

Кнопки


Рассмотрим теперь работу с кнопками. Вообще, в PVOS нет понятия кнопок, есть просто область на экране, нажатия на которую мы отслеживаем. Но тыкать в пустое место не хочется, поэтому обозначим место кнопки соответствующей BMPшкой.

Откроем папку DOC\GRAPHICS. Там лежит целая куча картинок, что называется, на все случаи жизни. Берём какую-нибудь из них и перегоняем в BMT:



И пишем вот такую программу:

#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_BTN1 0x9000

TCHTBL TchList[2] = 
{
		25, 25, 
		25 + 45, 25 + 28,
		ACT_ICON,
		OBJ_BTN1,
		0x0000,

		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {

	GSIZE(45, 28),
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x1C, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x27, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x20, 0xE0, 0x30, 0x07,
	0x00, 0x40, 0x58, 0x38, 0x30, 0x07,
	0x00, 0x40, 0x5E, 0x14, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0x94, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xD4, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xAC, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0xA8, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x28, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x58, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x50, 0x30, 0x07,
	0x00, 0x42, 0xFE, 0x50, 0x30, 0x07,
	0x00, 0x42, 0x7E, 0xB0, 0x30, 0x07,
	0x00, 0x42, 0x1C, 0xA0, 0x30, 0x07,
	0x00, 0x42, 0x05, 0x60, 0x30, 0x07,
	0x00, 0x43, 0x01, 0x40, 0x30, 0x07,
	0x00, 0x41, 0xC2, 0xC0, 0x30, 0x07,
	0x00, 0x40, 0x73, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x1F, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07,
	0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07,

};

T_ICON IconButton1 = {&TchList[0], bitmap, NULL, 0x01};

void buttonPressed() {
	char * str = "Button pressed";
	LibStringDsp(str, 0, 0, 100, IB_PFONT2); 
	LibPutDisp();
}

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&IconButton1);
	LibPutDisp();
	LibTchInit();
	
	while(1)
	{
		LibTchWait(&tsts);
		if(tsts.obj == OBJ_BTN1) {
		buttonPressed();
		}
	}

	LibTchStackClr();
	LibJumpMenu();

}

Создаём функции обработчика кнопок. Думаю, в пояснении они не нуждаются.

Собственно, всё происходит так же, как и с картинками, за исключением функции main(). Помимо постоянного опроса тачскрина мы проверяем, были ли нажаты кнопки (точнее говоря, был ли тык в отчерченную координатами область). Для этого используются всё те же заданные нами ID.
Ну что, компилируем и запускаем? Работает? Отлично.



Ну, где одна кнопка, там и две. И программа, в общем-то, похожа... Bitmap оставил один на двоих, если что.
#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_BTN1 0x9000

#define OBJ_BTN2 0x9001

TCHTBL TchList[3] = 
{
		25, 25, 
		25 + 45, 25 + 28,
		ACT_ICON,
		OBJ_BTN1,
		0x0000,
		
		25, 80, 
		25 + 45, 80 + 28,
		ACT_ICON,
		OBJ_BTN2,
		0x0000,

		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {

	GSIZE(45, 28),
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x1C, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x27, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x20, 0xE0, 0x30, 0x07,
	0x00, 0x40, 0x58, 0x38, 0x30, 0x07,
	0x00, 0x40, 0x5E, 0x14, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0x94, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xD4, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xAC, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0xA8, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x28, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x58, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x50, 0x30, 0x07,
	0x00, 0x42, 0xFE, 0x50, 0x30, 0x07,
	0x00, 0x42, 0x7E, 0xB0, 0x30, 0x07,
	0x00, 0x42, 0x1C, 0xA0, 0x30, 0x07,
	0x00, 0x42, 0x05, 0x60, 0x30, 0x07,
	0x00, 0x43, 0x01, 0x40, 0x30, 0x07,
	0x00, 0x41, 0xC2, 0xC0, 0x30, 0x07,
	0x00, 0x40, 0x73, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x1F, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07,
	0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07,

};

T_ICON IconButton1 = {&TchList[0], bitmap, NULL, 0x01};
T_ICON IconButton2 = {&TchList[1], bitmap, NULL, 0x01};

void button1Pressed() {
	char * str = "Button 1 pressed";
	LibStringDsp(str, 0, 0, 100, IB_PFONT2); 
	LibPutDisp();
}

void button2Pressed() {
	char * str = "Button 2 pressed";
	LibStringDsp(str, 0, 0, 100, IB_PFONT2); 
	LibPutDisp();
}

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&IconButton1);
	LibIconPrint(&IconButton2);
	LibPutDisp();
	LibTchInit();
	
	while(1)
	{
		LibTchWait(&tsts);
		if(tsts.obj == OBJ_BTN1) {
		button1Pressed();
		}
		if(tsts.obj == OBJ_BTN2) {
		button2Pressed();
		}
	}

	LibTchStackClr();
	LibJumpMenu();

}


RS-232


Несколько неожиданно, но имеющийся у КПК COM-порт можно использовать и в своих целях, а не только лишь для связи с ПК.

Итак, для начала порт надо открыть:

byte openPort()
{
	SRL_STAT srl;

	srl.port = IB_SRL_COM2;
	srl.speed = IB_SRL_9600BPS; 
	srl.parit = IX_SRL_NONE;
	srl.datab = IX_SRL_8DATA;
	srl.stopb = IX_SRL_1STOP;
	srl.fctrl = IX_SRL_NOFLOW;

	if (LibSrlPortOpen(&srl) != IW_SRL_NOERR) 
	{
		LibPutMsgDlg("LibSrlPortOpen error");
		return 1;
	}

	return 0;
}

Здесь мы задаём параметры порта и записываем их.

Пропишем в функции main():

if(!openPort()) {
		LibStringDsp("Port opened", 0, 0, 100, IB_PFONT2); 
		LibPutDisp();
	}
	else LibJumpMenu();

Теперь разберёмся с тем, как производить сам обмен данными. Для этого существуют функции LibSrlRecvByte() и LibSrlSendByte(). Используются они примерно так:

byte ReadCOM(byte * data)
{
	byte sReceivedByte;
	if (LibSrlRecvByte(&sReceivedByte) == IW_SRL_NODATA) return 1;
	*data = sReceivedByte;
	return 0;
}

byte WriteCOM(byte data)
{
	if(LibSrlSendByte(IB_FOLLOW_BUSY, data) == IW_SRL_NOERR) return 0;
	else return 1;	
}

Ну а теперь посмотрим, как это применять на практике. Возьмём нашу программу с двумя кнопками и добавим ранее упомянутые функции:

#include	<stdrom.h>
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_BTN1 0x9000

#define OBJ_BTN2 0x9001

TCHTBL TchList[3] = 
{
		25, 25, 
		25 + 45, 25 + 28,
		ACT_ICON,
		OBJ_BTN1,
		0x0000,
		
		25, 80, 
		25 + 45, 80 + 28,
		ACT_ICON,
		OBJ_BTN2,
		0x0000,

		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {

	GSIZE(45, 28),
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x1C, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x27, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x20, 0xE0, 0x30, 0x07,
	0x00, 0x40, 0x58, 0x38, 0x30, 0x07,
	0x00, 0x40, 0x5E, 0x14, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0x94, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xD4, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xAC, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0xA8, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x28, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x58, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x50, 0x30, 0x07,
	0x00, 0x42, 0xFE, 0x50, 0x30, 0x07,
	0x00, 0x42, 0x7E, 0xB0, 0x30, 0x07,
	0x00, 0x42, 0x1C, 0xA0, 0x30, 0x07,
	0x00, 0x42, 0x05, 0x60, 0x30, 0x07,
	0x00, 0x43, 0x01, 0x40, 0x30, 0x07,
	0x00, 0x41, 0xC2, 0xC0, 0x30, 0x07,
	0x00, 0x40, 0x73, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x1F, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07,
	0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07,

};

T_ICON IconButton1 = {&TchList[0], bitmap, NULL, 0x01};
T_ICON IconButton2 = {&TchList[1], bitmap, NULL, 0x01};

byte openPort()
{
	SRL_STAT srl;

	srl.port = IB_SRL_COM2;
	srl.speed = IB_SRL_9600BPS; 
	srl.parit = IX_SRL_NONE;
	srl.datab = IX_SRL_8DATA;
	srl.stopb = IX_SRL_1STOP;
	srl.fctrl = IX_SRL_NOFLOW;

	if (LibSrlPortOpen(&srl) != IW_SRL_NOERR) 
	{
		LibPutMsgDlg("LibSrlPortOpen error");
		return 1;
	}

	return 0;
}

byte ReadCOM(byte * data)
{
	byte sReceivedByte;
	if (LibSrlRecvByte(&sReceivedByte) == IW_SRL_NODATA) return 1;
	*data = sReceivedByte;
	return 0;
}

byte WriteCOM(byte data)
{
	if(LibSrlSendByte(IB_FOLLOW_BUSY, data) == IW_SRL_NOERR) return 0;
	else return 1;	
}

void button1Pressed() {
	LibStringDsp("Serial data sent", 0, 0, 100, IB_PFONT2); 
	WriteCOM('1');
	LibPutDisp();
}

void button2Pressed() {
	byte serialData = 0;
	if(!ReadCOM(&serialData)) {
		LibStringDsp("New serial data!", 0, 0, 100, IB_PFONT2);
		WriteCOM(serialData);
		}
	LibPutDisp();
}

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&IconButton1);
	LibIconPrint(&IconButton2);
	LibPutDisp();
	LibTchInit();
	
	if(!openPort()) {
		LibStringDsp("Port opened", 0, 0, 100, IB_PFONT2); 
		LibPutDisp();
	}
	else LibJumpMenu();
	
	while(1)
	{
		LibTchWait(&tsts);
		if(tsts.obj == OBJ_BTN1) {
		button1Pressed();
		}
		if(tsts.obj == OBJ_BTN2) {
		button2Pressed();
		}
	}

	LibTchStackClr();
	LibJumpMenu();

}

Теперь при нажатии первой кнопки в порт будет отправляться «1», а при нажатии второй — только что считанный байт (если в буфере он есть).

Проверим:



Кто бы сомневался.

Вот как-то так...


Увы, материалов по программированию под PVOS в сети исчезающе мало. Раньше их было больше, но теперь большая часть ссылок не работает.

Понятное дело, вряд ли это может быть хоть как-то полезно на практике. Но я совершенно уверен, что кого-то это заинтересует, и где-то станет одной программой для PVOS больше. Такие дела.

Ссылки


Увы, большинство тематических сайтов на данный момент уже невозможно открыть. Какого-либо крупного и ещё живого, наподобие palmdb.com, но для PV, просто нет. Даже на ещё живых сайтах большая часть ссылок уже не открывается. Если вы знаете какие-то ресурсы, которые не отражены здесь — пишите, я их обязательно добавлю.

Живые сайты (те, которые на момент написания статьи всё ещё нормально работают и содержат минимум битых ссылок):


Мёртвые сайты (представлены для ознакомительных целей, доступны для изучения в Internet Archive):

Комментарии (18)


  1. gears
    30.10.2022 22:05
    +4

    Офигенно!
    как раз недавно стал обладателем сего устройства и не знал в какую сторону копать и чего с ним делать.

    И тут такой шикарный подарок


    1. MaFrance351 Автор
      30.10.2022 22:10
      +5

      Вообще, этот девайс лежал у меня почти полтора года (с перерывами на "достать и поиграться"). Пару месяцев назад решил попробовать что-то под него написать и обнаружил, что толковых мануалов по программированию под него чуть менее, чем нисколько. А документация из комплекта к PVOS SDK очень вяло отражает всякие специфические черты API Pocket Viewer вроде необходимости постоянного опроса экрана и тому подобного.

      Пришлось разбираться. Решил вот написать, как начать разрабатывать под него, как это всё работает и всё такое. Такие дела.


      1. gears
        30.10.2022 23:31
        +1

        В теории наверно и дос можно запустить...


  1. dlinyj
    30.10.2022 22:45
    +3

    Громадное спасибо за статью. Всё хожу облизываюсь вокруг этого аппарата. Вообще, то что там х86 даёт большое поле для экспериментов, в том числе дизассемблирования (да, с другими тоже можно, но сложнее).

    Вопрос по СОМ-порту, есть ли возможность работать с ним по событиям (прерываниям)? Приняли байт, событие (прерывание), отправили байт, событие (прерывание)?


    1. MaFrance351 Автор
      30.10.2022 22:52
      +1

      Увы. Только постоянно опрашивать его. Иначе никак. С прерываниями тут печально.

      Под этот девайс, кстати, можно было писать и на ассемблере. Файлу с кодом присваиваем расширение *.A86 и закидываем в папку с исходниками, компилятор разберётся. Но примеров я не очень много нашёл.


      1. dlinyj
        31.10.2022 14:12

        Практически уверен, что тут проблема документации. А прерывания на самом деле есть, хотя могут быть и недоступны программам пользователя. В любом случае, интересно поизучать тему. Может быть отпаять eeprom и полазить дебагером по прошивке, вряд ли там есть какая-то защита от реверса.


        1. MaFrance351 Автор
          31.10.2022 17:46
          +1

          Да даже отпаивать не надо. BIOS и ОС есть в комплекте с SDK.


  1. steanlab
    30.10.2022 23:56
    +4

    Заслуженный плюс вам и спасибо за статью от обладателя этого чУдного устройства.
    Вы правы, информации исчезающе мало, а девайс очень интересный, уже хотя бы из-за управляющего элемента колесика и очень длительной работы от обычных ААА-батарей (месяцыыыы). Мне лично не хватает возможности работы с базами данных, хотя Periodic (таблица менделеева) есть и уже счастье :)


  1. InikonI
    31.10.2022 04:27
    +2

    Владел похожим девайсом в 2005(±1)г, без колеса прокрутки с джойстиком под экраном.
    Назывался вроде так PV-S460 (с 4Мб).
    Через несколько лет (2008~10?), помню закрытие одного из крупнейших (немецких?) форумом с ПО для данного КПК.
    Память там разделялась на две части: 2Мб под приложения, 2Мб под данные пользователя.
    Количество ячеек для приложений было фиксированным, по моему 16шт.
    Эл. таблицы занимали две ячейки (но это уже точно не помню).
    Эл. таблицы были достаточно продвинутыми, поддерживали формулы и «растягивание» формул на смежные ячейки.
    Изначально софт можно было ставить только при подключении к ПК.
    Но была программа которая работала на КПК и могла, без подключения к большому ПК, как удалять, так и ставить софт из архива, хранившегося в пользовательском разделе.
    Софта было достаточно много, читалки книг (с символами кириллицы проблем не было), игры, интерпретатор бэйсика.


    1. dlinyj
      31.10.2022 14:13

      И теперь ничего не достать…


      1. InikonI
        31.10.2022 15:48
        +1

        К сожалению да, ушло его время.
        Сейчас я тоже уже ничего в интернете не нахожу.
        На домашних ПК уже тоже нет ничего для этого КПК, да и мой девайс уже вряд ли жив.
        Да и где он…


  1. koreec
    31.10.2022 08:33

    Точно помню, что ровно 20 лет назад писал какой-то софт для своего PV-S660. Но в упор не помню какой. Читалку, что-ли ...


  1. vda19999
    31.10.2022 13:08

    Интересно, а сегментные регистры типа cs, ss и тп реально на этом устройстве использовались, или тоже не нужны особо?


  1. Newbilius
    31.10.2022 14:18
    +2

    Вот так история :) Спасибо, было крайне интересно прочитать про разработку под этот девайс ;-)


  1. Alex-111
    31.10.2022 14:20
    +2

    Круто! В начале не помешало бы еще перечислить основные характеристики девайса: ЦП, частота, память.
    Из языков только с? Какого стандарта?


    1. MaFrance351 Автор
      31.10.2022 17:58
      +2

      Процессор там называется NC3022, представляющий собой заказной чип на ядре NEC V30, работающий на частоте двадцать мегагерц. Что же до объёма памяти, то он там в районе ста двадцати восьми килобайт. Разбирать свой экземпляр, чтобы выяснить точно, я не стал из боязни его сломать (защёлки наверняка бы отломались).

      Программируется он на языке ANSI C, есть также возможность писать на ассемблере x86.


  1. ciuafm
    31.10.2022 15:45
    +1

    Был у меня подобный девайс (PV-S460). Прочитал на нем сотни книг. Особенно доставлял LCD с возможностью загружать шрифты разного размера - на улице отлично видно было, подсветка в темное время тоже выручала. До программирования не добрался, хотя какой-то Бейсик там вроде бы был. Резистивный тач со стилусом не давал расслабиться - все время боялся поцарапать. При активном чтении, особенно с подсветкой батареи держали не так и долго, хотя конечно существенно больше чем современные смартфоны. Использовал как букридер до замены на Kindle в ~2010.


  1. sacai
    31.10.2022 18:24
    +1

    до сих пор лежит в ящике вполне живой PV-660 (6 Мб, джойстик под экраном). ждет, когда младший читать научится, подсуну ему играться.

    использовал как записную книжку и читалку, даже загружал архивы с книжками через usb-com переходник.