strotmann.de strotmann.de http://strotmann.de/ 2018年6月19日 13:58:45 +0000 Gophermoon 0.3 - a Gopher-Server in Lua – Part 4

Logging from within Gophermoon

Version 0.2 of Gophermoon did send all output from stdout (standard out) and stderr (standard error) back to the socket. For error messages, that was not ideal, as Lua stack traces do not honor the Gopher protocol :)

In this version 0.3 I have added a logging channel for Gophermoon. Writing a log-file into the filesystem does not work, as Gophermoon runs inside an ephermeral container, and all changes to the file system are lost once the client request is done and the session terminates.

So logging is done via syslog. While it is possible to have a syslog-daemon (or systemd-journald) running inside the container, that would make the container more complex. My solution is to have the systemd unit catch all data from the stderr file descriptor and send that to syslog (and implicit to the systemd journal as well).

A small change in the Gophermoon systemd service unit (/etc/systemd/system/gophermoon@.service):

[Unit]
Description=GopherMoon Gopher Server in Lua
[Service]
ExecStart=/bin/systemd-nspawn -q -x --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon
StandardInput=socket
StandardError=syslog

Standard input and standard output are still connected to the socket (from Systemd socket activation, see Part 1), but all data written into standard error will now end up in the Syslog/Journal.

Systemd needs to be made aware of the changes:

systemctl daemon-reload

The function write_log in Gophermoon is used to write a log entry:

--- write a log entry
function write_log(severity,msg)
 io.stderr:write(severity .. ": " .. msg .. "\n")
end

Example use from the source code:

[...]
write_log("INFO", "Selector is " .. selector)
[...]
 if isReadable(filepath) then
 send_textfile(filepath)
 else
 send_error("Selector [" .. selector .. "] not found")
 write_log("ERROR","Selector not found")
 end
[...]

Log-Entries and some statistics

The systemctl status command for the socket activation unit of Gophermoon gives a count of connections (successful and unsuccessful):

 * gophermoon.socket - GopherMoon - Gopher-Server in Lua
 Loaded: loaded (/etc/systemd/system/gophermoon.socket; enabled; vendor preset: disabled)
 Active: active (listening) since Sa 2017年12月23日 22:54:44 CET; 2 days ago
 Listen: [::]:70 (Stream)
 Accepted: 209; Connected: 0
Dez 23 22:54:44 gopher.defaultroutes.de systemd[1]: Stopping GopherMoon - Gopher-Server in Lua.
Dez 23 22:54:44 gopher.defaultroutes.de systemd[1]: Listening on GopherMoon - Gopher-Server in Lua.
Dez 23 22:54:44 gopher.defaultroutes.de systemd[1]: Starting GopherMoon - Gopher-Server in Lua.

Systemd creates a new service unit for each new connection, the unit name contains

  • a counting number (number of connections since restart of the socket unit)
  • local IP-Address and port of the current connection
  • remote IP-Address and port of the current connection

Here is a Lua-Error message and stack trace from stderr in the Systemd journal:

# journalctl -u "gophermoon@*"
Dez 26 19:21:28 gopher.defaultroutes.de systemd[1]: Started GopherMoon Gopher Server in Lua (185.22.143.172:49828).
Dez 26 19:21:28 gopher.defaultroutes.de systemd[1]: Starting GopherMoon Gopher Server in Lua (185.22.143.172:49828)...
Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: Info: Selector is /0/filedoesnotexists
Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: /bin/lua: /bin/gophermoon:37: attempt to index a nil value (global 'f')
Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: stack traceback:
Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: /bin/gophermoon:37: in function 'isDir'
Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: /bin/gophermoon:134: in main chunk
Dez 26 19:21:29 gopher.defaultroutes.de systemd-nspawn[29973]: [C]: in ?
Dez 26 19:21:29 gopher.defaultroutes.de systemd[1]: gophermoon@172-5.45.107.88:70-185.22.143.172:49828.service: main process exited, code=exited, status=1/FAILURE
Dez 26 19:21:29 gopher.defaultroutes.de systemd[1]: Unit gophermoon@172-5.45.107.88:70-185.22.143.172:49828.service entered failed state.
Dez 26 19:21:29 gopher.defaultroutes.de systemd[1]: gophermoon@172-5.45.107.88:70-185.22.143.172:49828.service failed.

A list of all Gophermoon service names and connection information.

# journalctl -o verbose -u "gophermoon@*" | grep "_SYSTEMD_UNIT" | tail
 _SYSTEMD_UNIT=gophermoon@199-5.45.107.88:70-212.111.43.252:60215.service
 _SYSTEMD_UNIT=gophermoon@200-5.45.107.88:70-212.111.43.252:47726.service
 _SYSTEMD_UNIT=gophermoon@201-5.45.107.88:70-212.111.43.252:18139.service
 _SYSTEMD_UNIT=gophermoon@202-5.45.107.88:70-212.111.43.252:14576.service
 _SYSTEMD_UNIT=gophermoon@203-5.45.107.88:70-212.111.43.252:62963.service
 _SYSTEMD_UNIT=gophermoon@204-5.45.107.88:70-212.111.43.252:21729.service
 _SYSTEMD_UNIT=gophermoon@205-5.45.107.88:70-185.22.143.172:50432.service
 _SYSTEMD_UNIT=gophermoon@206-5.45.107.88:70-185.22.143.172:50433.service
 _SYSTEMD_UNIT=gophermoon@207-5.45.107.88:70-185.22.143.172:50434.service
 _SYSTEMD_UNIT=gophermoon@208-5.45.107.88:70-185.22.143.172:50436.service

In a later blog post I will show how to parse and aggrgate the connection information for some nice statistics (available via Gopher protocol of course).

Git commit for today: https://github.com/cstrotm/gophermoon/commit/ead8b366c639034b34884b17fb5c4fb28211c012

]]>
http://strotmann.de/gophermoon-04.html 2017年12月26日 00:00:00 +0000
Gophermoon 0.2 - a Gopher-Server in Lua – Part 3

Gophermoon on Github

the sourcecode is now on Github @ https://github.com/cstrotm/gophermoon

With every blog post, I will post the link to the commit associated with the blog post.

Serving static text files

Today Gophermoon learned how to serve static text files. The program first looks at the selector send by the client. If the selector is empty, and uses the root directory.

--- read the selector from the client
selector = io.read()
--- remove CR (13/0ドルD) from input
selector=selector:sub(1, -2)
--- if an empty selector has been send, use the root
if selector == '' then
 selector = "/"
end
--- make selector relative to the gopher root
filepath = gopherroot .. selector

Next, if the selector points to a directory, it looks if there is a file names gophermap in the direcory. A gophermap file is a Gopher-Document describing the resources available in this directory (as well as links outside the directory). Gophermoon uses the same gophermap format as Gophernicus (see https://github.com/prologic/gophernicus/blob/master/README.Gophermap)

Welcome to GopherMoon @ defaultroutes.de
----------------------------------------
0Gophermoon 0.1 - a Gopher-Server in Lua -- Part 1 /gophermoon01.txt blog.defaultroutes.de 70
0Gophermoon 0.1 - a Gopher-Server in Lua -- Part 2 /gophermoon02.txt blog.defaultroutes.de 70
0Gophermoon 0.2 - a Gopher-Server in Lua -- Part 3 /gophermoon03.txt blog.defaultroutes.de 70
0Gophermoon Sourcecode /gophermoon blog.defaultroutes.de 70

Lines that do not contain a tabulator are formatted as "i" (Information) file type.

--- if the selector points to a directory,
--- do we have a "gophermap" file? If "yes",
--- send that file. Else, if the selector
--- points to a (text-) file, send it
--- if both are false, send an error message
if isDir(filepath) then
 gophermap = filepath .. "/gophermap"
 if isReadable(gophermap) then
 send_gophermap(gophermap)
 end
else
 if isReadable(filepath) then
 send_textfile(filepath)
 else
 send_info("Welcome to GopherMoon @ defaultroutes.de")
 send_info("----------------------------------------")
 send_info("Error: Selector not found")
 send_end()
 end
end

If the selector isn't a directory, the program checks the selector is a readable file, and if it is, it sends the file gopher-style (with CR/LF and terminating the file with ".").

--- send a Textfile Gopher-style
function send_textfile(filepath)
 local f=io.open(filepath,"r")
 if f~=nil then
 for line in io.lines(filepath) do
	 io.write(line .. "\r\n")
 end
 io.close(f)
 end
 send_end()
end

The commit from today is https://github.com/cstrotm/gophermoon/commit/f300e28677059c9364151a89b36afe552d858700

]]>
http://strotmann.de/gophermoon-03.html 2017年12月25日 00:00:00 +0000
Gophermoon 0.2 - a Gopher-Server in Lua – Part 2

What's in it today

I will not change the (minimal) Gopher server (this time), but will prepare the execution environment for Gophermoon: the result will be a container containing, only one executeable and one Lua-Script besides the Gopher content.

The container will be isolated from the network and the filesystem and process-space of the host machine.

So any coding errors I will create while expanding the Gophermoon-Server will have limited security impact on the host system (unless there is a security issue in the container code of the Linux-Kernel, which there have been in the past. Nothing is fully secure).

I'm testing on a RedHat EL 7 machine, but the same result should be possible on other modern Linux systems with a container manager (systemd-nspawn, docker, rkt, LXC …).

Building a static Lua

One feature that makes the Go programming language popular among users of Linux-Containers is the fact that Go produces static binaries. Static binaries have no code runtime dependencies, they are self contained and do not need to be installed, but can just copied around and "just work".

Lua is written in C, but we can create static binaries in C too.

It is recommended to compile the static Lua binary on an development machine, not on the production Gopher server.

For Red Hat based systems, we need to install few build tools and the static library files for the GNU-Libc:

yum install make gcc wget glibc-static

Next, we're downloading the Lua sourcecode from https://www.lua.org/download.html

mkdir ~/src
cd ~/src
wget https://www.lua.org/ftp/lua-5.3.4.tar.gz
tar xfz lua-5.3.4.tar.gz
cd lua-5.3.4/src

The option -static lets the gcc compiler and linker build a static binary. Change the MYCFLAGS and MYLDFLAGS lines in Makefile:

[...]
MYCFLAGS= -static
MYLDFLAGS= -static
[...]

Now we can build the new Lua interpreter. I'm building the posix flavor, which is more generic than the Linux flavor and links less external libraries:

make posix
strip lua

The resulting lua binary should be a static ELF executable:

# file lua
lua: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=0631bb0d50ad1209dbf55f2b1dd0eb60fc388df7, stripped
# ls -lh lua
-rwxr-xr-x. 1 root root 1,7M 23. Dez 23:04 lua

Gophermoon inside a systemd-nspawn container

Next the container root directory is created and the static lua interpreter together with the gophermoon script is placed into that container directory:

mkdir -p /srv/gophermoon/bin
cp lua /srv/gophermoon/bin/
mv /usr/local/sbin/gophermoon /srv/gophermoon/bin/

Without timezone information files within the container directory, systemd-nspawn will complain about not being able to update the timezone in the container

Timezone <your-host-timezone> does not exist in container, not updating container timezone.

So far, I found no better way than to copy the timezone information into the container root (only one file is enough if you know the timezone of your host machine):

mkdir -p /srv/gophermoon/usr/share/zoneinfo
cp -a /usr/share/zoneinfo/* /srv/gophermoon/usr/share/zoneinfo/

A quick manual test starting the container from the commandline:

# /bin/systemd-nspawn -q --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon
iWelcome to GopherMoon @ defaultroutes.de defaultroutes.de 70
i---------------------------------------- defaultroutes.de 70
.

Here is the updated Systemd-Service-Unit (/etc/systemd/system/gophermoon@.service):

[Unit]
Description=GopherMoon Gopher Server in La
[Service]
ExecStart=/bin/systemd-nspawn -q -x --private-network -D /srv/gophermoon /bin/lua /bin/gophermoon
StandardInput=socket

The -x option creates an ephemeral container that is started from an BTRFS snapshot each time a new gopher connection comes in. The snapshot is destroyed as soon as the container terminates. An intruder will not be able to store new files into the container system.

At last we reload the new unit into Systemd and restart the Socket-Activation:

systemctl daemon-reload
systemctl restart gophermoon.socket

Now gophermoon runs in a (more) secure environment.

]]>
http://strotmann.de/gophermoon-02.html 2017年12月23日 00:00:00 +0000
Gophermoon 0.1 - a Gopher-Server in Lua – Part 1

About

I'm writing a simple Gopher-Server in Lua. I want to play around with the Gopher Protocol, Systemd and Linux-Container and learn some Lua-Programming on the way.

Gopher is a document retrieval protocol that had been around the same time the world-wide-web was born, but it is much simpler that HTTP and HTML. It works with plain text and directories, and the protocol on the wire is ultra-simple.

The server will be named gophermoon.

Gopher (the protocol) is documented in RFC 1436 https://www.ietf.org/rfc/rfc1436.txt with some extension defined as Gopher+ in http://iubio.bio.indiana.edu/soft/util/gopher/Gopher+-spec.text.

Preparing systemd Socket-Activation

In the beginning, I will use Systemd to do the network part of the protocol (listening on port 70). The Lua program will just read and write to standard-input and -output.

Here are the Service-Unit and the Socket-Activation-Unit for Systemd. The Gophermoon Service is in /etc/systemd/system/gophermoon@.service

[Unit]
Description=GopherMoon Gopher Server in La
[Service]
ExecStart=-/bin/lua /usr/local/sbin/gophermoon
StandardInput=socket
  • and the Socket-Unit is stored in /etc/systemd/system/gophermoon.socket
[Unit]
Description=GopherMoon - Gopher-Server in Lua
[Socket]
ListenStream=70
Accept=yes
[Install]
WantedBy=sockets.target

the Gopher-Server

Below is a very simple Lua-Script implementing a Hello-World Gopher-Service. It just emits the lines for a static welcome message.

A Gopher-Server waits for a connection and reads the path the client sends (ignored for now). Then it writes the Gopher-Menue out. Each Menu-Line has five fields. The first field is 1 character wide, the other fields are separated by the tabulator character (/t). Each line is terminated by a CRLF sequence.

A line with a single dot "." marks the end of the communication, server and client will close the connection.

--- Gopher Moon Version 0.1
--- a simple Gopher Server in Lua
--- with some help from Systemd socket activation
path = io.read()
io.write("iWelcome to GopherMoon @ defaultroutes.de\t\tblog.defaultroutes.de\t70\r\n")
io.write("i----------------------------------------\t\tblog.defaultroutes.de\t70\r\n")
io.write(".\r\n")

starting the thing …

Systemd needs to know about the new unit-files, so we do a reload:

systemctl daemon-reload

Now we can enable and start the socket (no need to start/enable the service, as it will be started once a connection to Port 70 is made).

systemctl enable --now gophermoon.socket

The open socket on the Gopher-Port 70 now visible:

# lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 40u IPv6 3989631 0t0 TCP *:gopher (LISTEN)
[...]

And test …

Gopher is simple, it can be tested with the telnet command:

# telnet blog.defaultroutes.de 70
Trying 5.45.107.88...
Connected to blog.defaultroutes.de.
Escape character is '^]'.
iWelcome to GopherMoon @ defaultroutes.de blog.defaultroutes.de 70
i---------------------------------------- blog.defaultroutes.de 70
.
Connection closed by foreign host.

gophermoon-01-lynx.png

Figure 1: test with the lynx https://lynx.browser.org/

gophermoon-01-omniweb.png

Figure 2: test with OmniWeb https://www.omnigroup.com/more (MacOS X)

]]>
http://strotmann.de/gophermoon-01.html 2017年12月21日 00:00:00 +0000
Cloning MacOS X from Linux I just finished to work out a way to clone a MacOS X installation from Linux (for automatic installation of MacOS X with fai – fully automatic installation). All tools discussed in the article are available in Debian 7.x (wheezy).

Backup

First we save the GUID (Globally Unique Identifier) Partition Table (GPT), so that we can restore the same partition table on the target machine. We use the commandline version of gdisk for this, named sgdisk

sgdisk --backup=/srv/macos/parttable.gpt.sgdisk /dev/sda

The content of the partitions can be cloned using the partclone tool. A default MacOS X installation has three partitions:

  1. EFI system partiton
  2. MacOS X (named "Customer")
  3. Recovery partition
Model: ATA APPLE HDD HTS547 (scsi)
Disk /dev/sda: 976773168s
Sector size (logical/physical): 512B/4096B
Partition Table: gpt
Number Start End Size File system Name Flags
 1 40s 409639s 409600s fat32 EFI system partition boot
 2 409640s 975503591s 975093952s hfs+ Customer
 3 975503592s 976773127s 1269536s hfs+ Recovery HD

First we create backup images of all partitions:

partclone.vfat -I -c -s /dev/sda1 | gzip > /srv/macos/sda1.partclone.gz
partclone.hfsplus -I -c -s /dev/sda2 | gzip > /srv/macos/sda2.partclone.gz
partclone.hfsplus -I -c -s /dev/sda3 | gzip > /srv/macos/sda3.partclone.gz

Restore

To restore the saved MacOS X installation, boot another Mac using Linux (a Knoppix live Linux DVD will work).

Next we restore the partition table (GPT) using sgdisk (/dev/sda is the target disk, all data on that disk will be erased, be warned!):

sgdisk --load-backup=/srv/macos/parttable.gpt.sgdisk /dev/sda
partprobe

as an alternative, the partitions can be created using parted and the GUID type codes set by sgdisk (important!):

sgdisk --zap /dev/sda
parted -s /dev/sda mklabel gpt
parted -s /dev/sda mkpart primary 40s 409639s
parted -s /dev/sda name 1 "'EFI system partition'"
parted -s /dev/sda set 1 boot on
parted -s /dev/sda mkpart primary 409640s 975503591s
parted -s /dev/sda name 2 "'MacOS X System'"
parted -s /dev/sda mkpart primary 975503592s 976773127s
parted -s /dev/sda name 3 "'Recovery HD'"
sgdisk -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B /dev/sda
sgdisk -t 2:48465300-0000-11AA-AA11-00306543ECAC /dev/sda
sgdisk -t 3:48465300-0000-11AA-AA11-00306543ECAC /dev/sda
partprobe

Restore the partition content:

zcat /srv/macos/sda1.partclone.gz | partclone.vfat -r -o /dev/sda1
zcat /srv/macos/sda2.partclone.gz | partclone.hfsplus -r -o /dev/sda2
zcat /srv/macos/sda3.partclone.gz | partclone.hfsplus -r -o /dev/sda3

If all went well, the new disk is ready to boot MacOS X.

For reference, here are the GUID details of a default MacOS X 10.8 MacBook Pro install:

root@(none):~# sgdisk -p /dev/sda
Disk /dev/sda: 976773168 sectors, 465.8 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 497736B0-7EA0-4C45-AB5F-8841CD773D24
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 976773134
Partitions will be aligned on 8-sector boundaries
Total free space is 262157 sectors (128.0 MiB)
root@(none):~# sgdisk -i1 /dev/sda
Partition GUID code: C12A7328-F81F-11D2-BA4B-00A0C93EC93B (EFI System)
Partition unique GUID: 6749C82F-02C9-4FF5-A889-F31A21726F8E
First sector: 40 (at 20.0 KiB)
Last sector: 409639 (at 200.0 MiB)
Partition size: 409600 sectors (200.0 MiB)
Attribute flags: 0000000000000000
Partition name: 'EFI system partition'
root@(none):~# sgdisk -i2 /dev/sda
Partition GUID code: 48465300-0000-11AA-AA11-00306543ECAC (Apple HFS/HFS+)
Partition unique GUID: 60F7A3AA-69B3-4E59-87A0-3A47BB659255
First sector: 409640 (at 200.0 MiB)
Last sector: 975241447 (at 465.0 GiB)
Partition size: 974831808 sectors (464.8 GiB)
Attribute flags: 0000000000000000
Partition name: 'MacOS X System'
root@(none):~# sgdisk -i3 /dev/sda
Partition GUID code: 48465300-0000-11AA-AA11-00306543ECAC (Apple HFS/HFS+)
Partition unique GUID: FD007AA4-CF3A-42F6-BFC6-B3BC25521FC2
First sector: 975503592 (at 465.2 GiB)
Last sector: 976773127 (at 465.8 GiB)
Partition size: 1269536 sectors (619.9 MiB)
Attribute flags: 0000000000000000
Partition name: 'Recovery HD'
]]>
http://strotmann.de/cloning-macosx.html 2013年5月08日 00:00:00 +0000
One-Liner: dump screens from a Forth block file I occasionally need to convert Forth block files (from VolksForth) to plain text files (to use diff or other tools). This one-liner using gforth does the trick:

#!/bin/sh
gforth -e "use ${1} : dump-blocks 0 do i list loop ; : blocks get-block-fid file-size drop d>s 1024 / ; blocks dump-blocks bye"

Save in a script file (dump-blocks), make it executable, and then you can use

# dump-blocks 6502f83.fb > 6502f83.fs
]]>
http://strotmann.de/dump-forth.html 2013年4月02日 00:00:00 +0000
FireBee Diary 2: from MC68000 to ColdFire To get volksForth running on the FireBee, the machine code instructions that are available on the MC68000, but not in the ColdFire CPU need to be replaced with a series of instructions that perform the same action.

I found the "Digger V8" dissassembler on Heenk Robbers webpage along with the AHCC C-Compiler. The Digger is able to disassemble both ColdFire and MC68000, and it can switch between the two CPU types with a simple switch. Finding instructions that cannot be executed on the ColdFire is now only a matter of switching the CPU type and watching the assembler code change.

The Digger package also contains a list of all M68000 assembler instructions, with information on the instructions available on each CPU type (MC68000, MC68020, MC68030, ColdFire …).

04923b6d-7a78-4d0f-b6b2-ff229b8a77da.jpg bc7a1b88-5039-4a04-8c68-9c3ceecada90.jpg

I made the table below as a quick look-up tool to find MC68000 instructions that need replacement on the ColdFire (this table is still work in progress, the column "workaround" will be filled while I work on the ColdFire porting project):

Opcode possible workaround Description
ori -> ccr   OR immediate to CCR
ori -> sr   OR immediate to Status Register
andi -> ccr   AND immediate to CCR
andi -> sr   AND immediate to Status Register
eori -> ccr   EOR immediate to CCR
eori -> sr   EOR immediate to Status Register
movep   Move peripheral data
trapv   Trap on Overflow
rtr   Return and restore condition codes
chk.w   Check register against bounds
dbcc   Test condition, decrement and branch
sbcd Dx,Dy   Subtract decimal with extend
sbcd -(Ax),-(Ay)   Subtract decimal with extend
suba.w   Subtract address
subx -(Ax),-(Ay)   Subtract extended
linea   LINE-A access
abcd Dx,Dy   Add decimal with extend
abcd -(Ax),-(Ay)   Exchange registers
exg Dx,Dy   Exchange registers
exg Ax,Ay   Exchange registers
exg Dx,Ay   Exchange registers
adda.w   Add address
addx -(Ax),-(Ay)   Add extended
roxl   Rotate left with extend
roxr   Rotate right with extend
rol   Rotate left
ror   Rotate right
asl   Arithmetic shift left
asr   Arithmetic shift right
lsl   Logical shift left
lsr   Logical shoft right
]]>
http://strotmann.de/FireBeeDiary2.html 2013年1月17日 00:00:00 +0000
Firebee Developer Diary 1 On the last day of 2012, Stefan (aka Beetle) brought me the FireBee machine that we both share. I've been programming Atari 8bit machines for over 25 years now, but this is the first time that I enter the realm of the Atari ST family with a 68K or now ColdFire. There is much to learn.

My FireBee Development setup
ceadfdef-d95e-400a-92ad-ee2d49b54a23.jpg

The FireBee is a modern incarnation of the Atari ST computer line, including Ethernet, USB, CF-Card and SD-Card interfaces. A modern computer. And it still runs Atari TOS programs. Or almost …

The ColdFire CPU is a somewhat CISC morphed to RISC system, where some "not so often used" CPU instructions have been removed to make the CPU core simpler but faster. The "not so often used" does refer to common compilers, mostly C compilers. It does not seem true for Forth. The Atari ST VolksForth refuses to work on the FireBee. It probably uses some M68K instructions not available on the FireBee.

That will be my first target on the FireBee system: getting VolksForth to run. After that is done, VolksForth ST needs a log due overhaul. The last version was from around 1988, before Atari TT and Falcon and all the new GEM API. The old version does not know about MiNT or EmuTOS. That has to change.

The FireBee board
9285a77b-5d2a-4b12-9da6-045271ec1095.jpg

I will report my progress here. The sourcecode can be found, along with the developer documentation in the repository over at http://fossil.forth-ev.de/volksforth.

Books to read …
5f7375e9-8181-4cb5-9581-2da3c5e60931.jpg

]]>
http://strotmann.de/FireBeeDiary1.html 2013年1月05日 00:00:00 +0000
CamelForth auf dem TI Launchpad MSP430 unter Linux Sunday Mar 18, 2012

Dirk Brühl und Michael Kalus haben das CamelForth vom Brad Rodriguez für das TI Launchpad Board angepasst. Das 44ドルth Projekt hat eine eigene Webseite auf http://www.4e4th.eu. Auf der Webseite der Forth-Gesellschaft e.V. gibt es Neuigkeiten zum TI Launchpad und CamelForth (aka 44ドルth): http://www.forth-ev.de/index.php?topic=launchpad. Die Quellen und die aktuelle HEX-Download Datei findet sich im SVN Repository der Forth-Gesellschaft unter http://forth-ev.de/repos/CF430G2553/ in der Datei CF430G2553.a43.

65f30a67-b71c-4d87-91be-f78d6f7c83f5.jpg

Unter Linux kann diese HEX-Datei mit dem Flasher-Programm mspdebug auf das Launchpad geladen werden (Version 0.19 oder später):

$ sudo /usr/local/bin/mspdebug rf2500 "prog CF430G2553.a43"

Unter Linux meldet sich das TI Launchpad meist unter /dev/ttyACM0, jedoch kann es sein das bei modernen Linux Systemen Hintergrundprogramme (wie der ModemManager) sofort diese Schnittstelle belegen und versuchen das LaunchPad als Modem anzusprechen (Siehe Linux today: too much plug and play).

Das CamelForth auf dem TI Launchpad kann mit einem Terminal-Programm abgesprochen werden.

Anleitung zum Bauen des CamelForth aus den Quellen, und die Programmierung des TI Launchpad unter MacOS X oder Windows befinden sich im Wiki der Forth Gesellschaft unter http://www.forth-ev.de/wiki/doku.php/projects:4e4th:start.

Das CamelForth für das LaunchPad befindet sich derzeit (März 2012) in aktiver Entwicklung, es sind in den kommenden Wochen noch Verbesserungen und Änderungen zu erwarten.

Hilfe zum CamelForth auf dem Launchpad und zu Forth allgemein gibt es in deutscher Sprache im Usenet unter de.comp.lang.forth.

]]>
http://strotmann.de/CamelForthMSP430.html 2012年3月18日 00:00:00 +0000
Terminal Emulatoren für Forth Systeme über serielle USB-Schnittstellen (amForth) Wednesday Dec 14, 2011

screen

Screen ist der Allrounder unter den Terminal-Programmen. Eigendlich mehr ein Fenster-Manager für die Kommandozeile. Keine Konfiguration, einfach loslegen. Um den vollen Funktionsumfang ausnutzen zu können unbedingt einen Blick in die Man-Pages werfen.

da94c492-d3de-4a80-8c61-b00c9975bdbd.png

  • Screen in den unterbrechen mit "CTRL+A-d"
  • Screen beenden mit "CTRL+A-\"
  • Backspace / Delete mit "CTRL+H"
  • Split-Screen mit "CTRL+A+S"
  • Bildschirm welchseln mit "CTRL+A-[TAB]"
  • neuer Screen mit Unix-Shell "CTRL+A+c"
$ screen /dev/ttyUSB1

Webseite: GNU screen

Emacs

Bei Programmierprojekten ist Emacs die erste Wahl. Per "M-x serial-term" wird das eingebaute Terminalprogramm aufgerufen, Geräteschnittstelle und Baud-Rate einstellen, und flupps, ist die Verbindung da. Quelltexte kann man auch editieren.

c1d3ee58-b439-4938-a5b0-06e0892539d5.png

Webseite: http://www.gnu.org/software/emacs/

cKermit

Da alt-ehrwürdige Kermit, läuft fast auf so vielen Plattformen wie Forth. Funktioniert immer noch, kommt mit Scripting-Funktionen. Gut für Automatisierungsaufgaben.

/home/cas$ kermit
C-Kermit 9.0.301 OPEN SOURCE:, 11 Jul 2011, for Linux (64-bit)
 Copyright (C) 1985, 2011,
 Trustees of Columbia University in the City of New York.
Type ? or HELP for help.
(/home/cas/) C-Kermit>set line /dev/ttyUSB1
(/home/cas/) C-Kermit>set serial 8n1
(/home/cas/) C-Kermit>set flow-control none
(/home/cas/) C-Kermit>set carrier-watch off
(/home/cas/) C-Kermit>connect
Connecting to /dev/ttyUSB1, speed 9600
 Escape character: Ctrl-\ (ASCII 28, FS): enabled
Type the escape character followed by C to get back,
or followed by ? to see other options.
----------------------------------------------------
> amforth 4.2 ATmega328P
> 
 ok
> 
 ok
> 
 ok
> 
(Back at csmobile4.home.strotmann.de)
----------------------------------------------------
(/home/cas/) C-Kermit>quit
Closing /dev/ttyUSB1...OK

Webseite: Kermit Projekt

minicom

Wir von vielen Anwendern benutzt, ist aber nicht meine erste Wahl.

358cc086-7d55-4fbc-b6aa-6366e623de52.png

a156cddd-254c-4c6b-ad32-36a06296b405.png

7e607b39-8d43-4acc-99b6-c9c0af71b7dc.png

Webseite: MiniCom

cu (uucp utils)

Einfach und simpel, ohne Ballast.

/home/cas$ cu -s 9600 -l /dev/ttyUSB1

gtkterm

98665169-3e88-4161-af67-9e1dddd33b79.png

Webseite: GTK Term

Putty

Putty kennen viele unter Windows. Aber es gibt auch einen Port für Linux/Unix, der funktioniert auch gut. Ideal für Programmierer, die zwischen den Welten wandern.

6d59ee61-e6b8-4b6e-9202-dc2cea2d59bf.png

4a7cf8df-f010-480f-97af-898dbce4774e.png

Webseite: Putty

]]>
http://strotmann.de/ForthTerminalEmulator.html 2011年12月14日 00:00:00 +0000
amForth auf den Arduino Duemilanove aufspielen Wie bekommt man das amForth auf den Arduino Duemilanove um das Morse-Programm laufen zu lassen. Dies sind die Schritte die ich für jedes Arduino-Board vor Beginn eines Workshops durchführe:

Schritt 1: den Programmer an das Arduino Board anschliessen

Ich benutze einen avrisp MKII Programmer (ca. 30 Euro) um den Arduino neu zu programmieren.

a5642706-0905-4e4a-a0f6-a99423568437.jpg

Sowohl der Programmer als auch das Arduino-Board müssen über USB mit Strom versorgt werden.

Schritt 2: Die "fuses" setzen

Dieser Schritt muss pro Board nur einmal durchgeführt werden, es schaded aber nicht, wenn dieser Schritt bei einem Board ausgeführt wird, welches schon einmal für amForth vorbereitet war- Das amForth benötigt eine ganz bestimmte Konfiguration der "Fuses" auf dem Arduino-Board (siehe amForth Handbuch). Geschrieben werden die Fuses mit avrdude unter Linux:

sudo avrdude -q -c avrispmkII -P usb -p atmega328p -U efuse:w:0x05:m \
 -U hfuse:w:0xD9:m -U lfuse:w:0xFF:m
avrdude: AVR device initialized and ready to accept instructions
avrdude: Device signature = 0x1e950f
avrdude: reading input file "0x05"
avrdude: writing efuse (1 bytes):
avrdude: 1 bytes of efuse written
avrdude: verifying efuse memory against 0x05:
avrdude: load data efuse data from input file 0x05:
avrdude: input file 0x05 contains 1 bytes
avrdude: reading on-chip efuse data:
avrdude: verifying ...
avrdude: 1 bytes of efuse verified
avrdude: reading input file "0xD9"
avrdude: writing hfuse (1 bytes):
avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xD9:
avrdude: load data hfuse data from input file 0xD9:
avrdude: input file 0xD9 contains 1 bytes
avrdude: reading on-chip hfuse data:
avrdude: verifying ...
avrdude: 1 bytes of hfuse verified
avrdude: reading input file "0xFF"
avrdude: writing lfuse (1 bytes):
avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xFF:
avrdude: load data lfuse data from input file 0xFF:
avrdude: input file 0xFF contains 1 bytes
avrdude: reading on-chip lfuse data:
avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude: safemode: Fuses OK
avrdude done. Thank you.

Schritt 3: amForth Schreiben

Sind die Fuses richtig gesetzt, kann nun das amForth auf den Arduino programmiert werden:

sudo avrdude -q -c avrispmkII -P usb -p atmega328p -e -U flash:w:duemilanove.hex:i \
 -U eeprom:w:duemilanove.eep.hex:i
avrdude: AVR device initialized and ready to accept instructions
avrdude: Device signature = 0x1e950f
avrdude: erasing chip
avrdude: reading input file "duemilanove.hex"
avrdude: writing flash (32300 bytes):
avrdude: 32300 bytes of flash written
avrdude: verifying flash memory against duemilanove.hex:
avrdude: load data flash data from input file duemilanove.hex:
avrdude: input file duemilanove.hex contains 32300 bytes
avrdude: reading on-chip flash data:
avrdude: verifying ...
avrdude: 32300 bytes of flash verified
avrdude: reading input file "duemilanove.eep.hex"
avrdude: writing eeprom (64 bytes):
avrdude: 64 bytes of eeprom written
avrdude: verifying eeprom memory against duemilanove.eep.hex:
avrdude: load data eeprom data from input file duemilanove.eep.hex:
avrdude: input file duemilanove.eep.hex contains 64 bytes
avrdude: reading on-chip eeprom data:
avrdude: verifying ...
avrdude: 64 bytes of eeprom verified
avrdude: safemode: Fuses OK
avrdude done. Thank you.

An dieser Stelle sollte schon der amForth Prompt erscheinen, wenn der Arduino mit einem Terminalprogramm über die USB-serielle Schnittstelle angesprochen wird.

Schritt 4: weiteren Quellcode laden

Zum 'nackten' amForth wird nun noch einiges an Quellcode in den Arduino geladen. Hier benutze ich das "amforth-upload.py" Tool. Die Dateien sind:

  • misc.frt - Einige nützliche Definitionen
  • bitnames.frt - Wörter zum definieren von neuen Wörtern, die Ports per Namen ansprechen
  • marker.frt - das Wort "marker", zum Setzen eine Lösch-Marke
  • atmega328p.frt - die Definitionen für die Atmega 328P CPU

Diese Dateien sind Bestandteil von amForth, befinden sich aber auch in dieser ZIP-Datei.

amforth-upload.py -t /dev/ttyUSB0 lib/misc.frt lib/bitnames.frt lib/ans94/marker.frt ../amforth/releases/4.2/core/devices/atmega328p/atmega328p.frt
lsof: WARNING: can't stat() fuse.gvfs-fuse-daemon file system /home/cas/.gvfs
 Output information may be incomplete.
\ some definitions that may be useful
 ok
> : erase 
 ok 0 fill
 ok;
 ok
> : .( \ (s -- )
 ok [char] ) word count type
 ok; immediate
 ok
> \ dump free ressources
 ok
> : .res 
 ok base @ >r
 ok decimal
 ok ver ." running at " f_cpu 1000 um/mod . drop ." kHz " cr
 ok ." free FLASH cells " unused u. cr
[...]

Schritt 5: Programmieren …

Der Arduino ist nun vorbereitet und kann für den amForth Workshop, oder für jedes andere amForth Projekt, benutzt werden. Viel Spass!

]]>
http://strotmann.de/amforth_duemilanove.html 2011年12月12日 00:00:00 +0000
amForth Morse Code Konfiguration der Arduino Pins und die Basis-Wörter des Morse-Programms. Dieser Teil wird für jedes der drei Morse-Programme benutzt:

\ 2011年10月26日 EW
\ 2011年11月02日 CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)
\ make marker loads:
\ lib/misc.frt
\ lib/bitnames.frt
\ lib/ans94/marker.frt
\ ../../amforth/releases/4.2/core/devices/atmega328p/atmega328p.frt
marker --base--
decimal
PORTD 5 portpin: led1
PORTD 6 portpin: led2
PORTD 3 portpin: bz
\ --- buzzer -------------------------------------------------
\ 2 ms T_period =^= 500 Hz
\ Ton-Ausgabe
: buzz ( cycles -- )
 0 ?do 
 bz low 
 1ms 
 bz high 
 1ms 
 loop
;
\ Warten ohne Ton
: gap ( cycles -- )
 0 ?do 
 bz high 
 1ms 
 bz high 
 1ms 
 loop
;
\ LED fuer 3ms blinken lassen
: blink ( cycles -- )
 led1 high
 0 ?do 
 1ms 1ms 1ms 
 loop
 led1 low
;
\ Umschalter zwischen Morse-Ton und Morse-LED
Edefer transmit
: piepser ['] buzz is transmit ;
: blinker ['] blink is transmit ;
decimal
: kurz 50 transmit 50 gap ; \ Signal kurz
: lang 150 transmit 50 gap ; \ Signal lang
: Zend 100 gap ; \ Pause zwischen Zeichen
: Wend 300 gap ; \ Pause zwischen Worten
: init
 led1 pin_output led1 low
 led2 pin_output led2 low
 bz pin_output
 piepser
;

Simple Implementierung

Die erste Implementierung ist sehr gradlinig mit einem Forth-Wort für jeden Morse-Buchstaben und einer grossen IF-THEN Konstruktion im Hauptteil:

\ 2011年10月26日 EW
\ 2011年11月02日 CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)
marker --morse--
\ je ein Forth-Wort pro Morse-Buchstabe
: _A kurz lang Zend ;
: _B lang kurz kurz kurz Zend ;
: _C lang kurz lang kurz ;
: _D lang kurz kurz Zend ;
: _E kurz Zend ;
: _F kurz kurz lang kurz Zend ;
: _G lang lang kurz Zend ;
: _H kurz kurz kurz kurz Zend ;
: _I kurz kurz Zend ;
: _J kurz lang lang lang Zend ;
: _K lang kurz lang Zend ;
: _L kurz lang kurz kurz Zend ;
: _M lang lang Zend ;
: _N lang kurz Zend ;
: _O lang lang lang Zend ;
: _P kurz lang lang kurz Zend ;
: _Q lang lang kurz lang Zend ;
: _R kurz lang kurz Zend ;
: _S kurz kurz kurz Zend ;
: _T lang Zend ;
: _U kurz kurz lang Zend ;
: _V kurz kurz kurz lang Zend ;
: _W kurz lang lang Zend ;
: _X lang kurz kurz lang Zend ;
: _Y lang kurz lang lang Zend ;
: _Z lang lang kurz kurz Zend ;
\ Variable zum Speichern des original 'emit'
variable o-emit
\ neues Emit zum Ausgeben von Zeichen als Morse-Code
: morseemit ( key -- )
 dup o-emit @ execute ( original 'emit' aufrufen )
 dup [char] a = if _A then
 dup [char] b = if _B then
 dup [char] c = if _C then
 dup [char] d = if _D then
 dup [char] e = if _E then
 dup [char] f = if _F then
 dup [char] g = if _G then
 dup [char] h = if _H then
 dup [char] i = if _I then
 dup [char] j = if _J then
 dup [char] k = if _K then
 dup [char] l = if _L then
 dup [char] m = if _M then
 dup [char] n = if _N then
 dup [char] o = if _O then
 dup [char] p = if _P then
 dup [char] q = if _Q then
 dup [char] r = if _R then
 dup [char] s = if _S then
 dup [char] t = if _T then
 dup [char] u = if _U then
 dup [char] v = if _V then
 dup [char] w = if _W then
 dup [char] x = if _X then
 dup [char] y = if _Y then
 dup [char] z = if _Z then
 dup bl = if Wend then ( Leerzeichen = Wortende )
 drop
 Zend ( Zeichenende )
;
\ neues 'morseemit' in 'emit' eintragen
: morse
 ['] emit defer@ o-emit !
 ['] morseemit is emit
;
\ original 'emit' wieder herstellen
: endmorse
 o-emit @ is emit 
;

Implementierung mit einer Sprungtabelle

In dieser Version des Morse-Programms nutzen wir immer noch Forth-Wörter für die Signale, aber ersetzten die Grosse IF-THEN Verzweigung durch eine Spring-Tabelle. Die Sprung-Tabelle enhält 256 Einträge für die 256 ASCII-Zeichen. In den Tabellen-Einträgen befindet sich die Adresse des jeweiligen Forth-Worts (Execution Token = xt), welches die Signale für den Morse-Code ausgibt. Ist ein Tabelleneintrag '0' so ist dieser Eintrag nicht gefüllt und es wird kein Morse-Code ausgegeben.

\ 2011年10月26日 EW
\ 2011年11月02日 CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)
\ 2nd version
marker --morse--
: _A kurz lang Zend ;
: _B lang kurz kurz kurz Zend ;
: _C lang kurz lang kurz ;
: _D lang kurz kurz Zend ;
: _E kurz Zend ;
: _F kurz kurz lang kurz Zend ;
: _G lang lang kurz Zend ;
: _H kurz kurz kurz kurz Zend ;
: _I kurz kurz Zend ;
: _J kurz lang lang lang Zend ;
: _K lang kurz lang Zend ;
: _L kurz lang kurz kurz Zend ;
: _M lang lang Zend ;
: _N lang kurz Zend ;
: _O lang lang lang Zend ;
: _P kurz lang lang kurz Zend ;
: _Q lang lang kurz lang Zend ;
: _R kurz lang kurz Zend ;
: _S kurz kurz kurz Zend ;
: _T lang Zend ;
: _U kurz kurz lang Zend ;
: _V kurz kurz kurz lang Zend ;
: _W kurz lang lang Zend ;
: _X lang kurz kurz lang Zend ;
: _Y lang kurz lang lang Zend ;
: _Z lang lang kurz kurz Zend ;
\ erstelle Tabelle fuer execution token
variable mtable 256 cells allot
\ loesche Tabelle
mtable 256 cells erase
\ Hilfswort zum fuellen der Tabelle
: >mtable ( xt c -- )
 cells mtable + !
;
\ Tabelle zur kompilierzeit fuellen
' _A char a >mtable
' _B char b >mtable
' _C char c >mtable
' _D char d >mtable
' _E char e >mtable
' _F char f >mtable
' _G char g >mtable
' _H char h >mtable
' _I char i >mtable
' _J char j >mtable
' _K char k >mtable
' _L char l >mtable
' _M char m >mtable
' _N char n >mtable
' _O char o >mtable
' _P char p >mtable
' _Q char q >mtable
' _R char r >mtable
' _S char s >mtable
' _T char t >mtable
' _U char u >mtable
' _V char v >mtable
' _W char w >mtable
' _X char x >mtable
' _Y char y >mtable
' _Z char z >mtable
' Wend bl >mtable
variable o-emit
: morseemit ( key -- )
 dup o-emit @ execute ( altes emit ausfuehren )
 255 and ( sicherstellen das wir nur 0-255 Werte bekommen )
 cells mtable + @ ( execution token holen ) 
 dup if execute ( wenn > 0 dann ausfuehren ) 
 else drop then ( ansonsten wegwerfen )
;
: morse
 ['] emit defer@ o-emit !
 ['] morseemit is emit
;
: endmorse
 o-emit @ is emit 
;

gepackte Morse-Code Signale

Morse-Code Signale als Forth-Wörter speichern ist einfach, verbraucht aber recht viel Flash-Speicher. Morse-Code Signale können in je einem Byte (8 bit) gespeichert werden. In der dritten Version des Morse-Programms werden bis zu fünf Morse-Signale in den oberen 5 Bits gespeichert ( 0 = kurz, 1 = lang ), die unteren 3 Bits speichern die Anzahl der Signale im Morse-Code. Hierdurch reduziert sich der Speicherverbrauch des Programms.

Die Tabelle mit dem gepackten Morse-Code wird vom Forth-Interpreter zur Kompilierzeit erstellt und verbraucht bei der Ausführung keine extra Rechenzeit:

\ 2011年10月26日 EW
\ 2011年11月04日 CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)
\ 3nd version
marker --morse--
\ erstelle Tabelle fuer gepackte morse daten
variable mtable 256 allot
\ loesche Tabelle
mtable 256 erase
\ Hilfswort zum fuellen der Tabelle
: >mtable ( gepackter-morsecode c -- )
 mtable + c!
;
\ Hilfswort zum packen vor morse code
: pack ( #zeichen code -- pcode )
 3 lshift swap 7 and or
;
: unpack ( pcode -- #zeichen code )
 dup 7 and swap 3 rshift 
;
: binary 2 base ! ;
\ Tabelle zur kompilierzeit fuellen
binary 00010 decimal 2 pack char a >mtable
binary 00001 decimal 4 pack char b >mtable
binary 01010 decimal 4 pack char c >mtable
binary 00001 decimal 3 pack char d >mtable
binary 00000 decimal 1 pack char e >mtable
binary 00100 decimal 4 pack char f >mtable
binary 00011 decimal 3 pack char g >mtable
binary 00000 decimal 4 pack char h >mtable
binary 00000 decimal 2 pack char i >mtable
binary 01110 decimal 4 pack char j >mtable
binary 00101 decimal 3 pack char k >mtable
binary 00010 decimal 4 pack char l >mtable
binary 00011 decimal 2 pack char m >mtable
binary 00001 decimal 2 pack char n >mtable
binary 00111 decimal 3 pack char o >mtable
binary 00110 decimal 4 pack char p >mtable
binary 01101 decimal 4 pack char q >mtable
binary 00010 decimal 3 pack char r >mtable
binary 00000 decimal 3 pack char s >mtable
binary 00001 decimal 1 pack char t >mtable
binary 00001 decimal 3 pack char u >mtable
binary 00001 decimal 4 pack char v >mtable
binary 00011 decimal 3 pack char w >mtable
binary 01001 decimal 4 pack char x >mtable
binary 01011 decimal 4 pack char y >mtable
binary 00011 decimal 4 pack char z >mtable
variable o-emit
: domorse ( code #signale -- ) 
 0 ?do ( Schleife Anzahl der Signale )
 dup 1 and ( erstes bit maskieren )
 if lang ( bit gesetzt = lang )
 else kurz then ( sonst kurz )
 2/ ( bits nach rechts schieben )
 loop
 drop Zend ( Zeichenende )
;
: morseemit ( key -- )
 dup o-emit @ execute ( altes emit ausfuehren )
 255 and ( sicherstellen das wir nur 0-255 Werte bekommen )
 dup bl = if ( leerzeichen? )
 Wend ( Wortende zeit warten )
 then 
 mtable + c@ ( gepackten morse code holen )
 unpack ( entpacken )
 domorse ( signale ausgeben )
;
: morse
 ['] emit defer@ o-emit !
 ['] morseemit is emit
;
: endmorse
 o-emit @ is emit 
;

Aufgaben:

  • Wie kann das Morse-Code Programm weiter optimiert werden (weniger Speicherverbrauch)?
  • erweitere das Programm, so das auch Zifferns als Morse-Code ausgegeben werden
  • erweitere das Programm, so das auch Sonderzeichen als Morse-Code ausgegeben werden

Antworten auf diese Aufgaben können auf Mastodon gepostet werden. Bitte Geduld wenn ich nicht sofort antworte, ich kann leider nicht immer online sein.

]]>
http://strotmann.de/amForth-MorseCode.html 2011年11月06日 00:00:00 +0000
Recovering Atari ST 1st Word+ Documents I've been asked to recover some 1st Word+ Documents from 31⁄2" disk drives. Before starting up one of my old Atari ST machines, I was trying to get the job done using Linux. And it worked quite well. Here is how:

Atari ST disk are formatted using a flavor of the FAT file system. Most of the time, it is possible to read this disks under Linux. The 'mtools' package contains tools to read and write FAT formatted floppy disks, without the need to rely on a file-system driver (the 'mtools' package is available in most Linux distributions. For Ubuntu Linux, execute "sudo apt-get install mtools" to install).

For reading old floppy disks, I recommended to use old style floppy disks, no modern USB-Floppy disks which can often only read standard PC formats. I've used a build-in 31⁄2" floppy drive inside a trusty old IBM ThinkPad T20 running Ubuntu 11.04.

Before reading the files from an Atari ST disk, I configure 'mtools' to skip the check for the floppy format. The FAT format on Atari ST disks is somewhat non-standard, which can trick the 'mtools':

Add to /etc/mtools.conf

MTOOLS_SKIP_CHECK=1

The mtools manual page documents:

MTOOLS_SKIP_CHECK: If this is set to 1, mtools skips most of its sanity checks. This is needed to read some Atari disks which have been made with the earlier ROMs, and which would not be recognized otherwise.

Next, make sure that the floppy kernel driver module is loaded. It seems that modern Linux systems do not load the driver for floppy disk anymore:

# sudo lsmod | grep floppy
# sudo modprobe floppy
# sudo lsmod | grep floppy
floppy 60032 0 

Then I set the floppy drive parameters fixed for a 720kb floppy. The 'setfdprm' command is part of the 'fdutils' package:

# sudo setfdprm /dev/fd0 "720/720"

The command "mdir a:" should now show the directory content of the floppy disk. The floppy disk files can now be copies over to the Linux harddrive using 'mcopy':

# mkdir atari-st-floppy
# cd atari-st-floppy
# mcopy a:*.* .

Converting 1st Word+ documents into RTF

Rich Text Format (RTF) is a standard format for text documents. Using tools like UnRTF, RTF documents can be easly converted into other formats. RTF can be read my most modern text-processing packages such like LibreOffice and AbiWord.

'wpls2rtf' is a small commandline tools that can translate 1st Word+ files into RTF documents. The original source can be found at ftp://ftp.funet.fi/pub/atari/util/wpls2rtf.zip. Using this tool, I was able to convert the old Atari ST documents into files that can be read and edited on modern systems.

]]>
http://strotmann.de/RecoveringAtariSTFirstWordPlus.html 2011年8月16日 00:00:00 +0000
batch rotate and image processing in GIMP Wednesday Jun 15, 2011

The GIMP Script-Fu scheme snippet will take a file wildcard pattern ("*.JPG") and an rotate-type value (0 = 90° / 1 = 180° / 2 = 270°), level out the colors in the picture and save the changed image as a PNG file.

(define (batch-rotate pattern rotate-type)
 (let* ((filelist (cadr (file-glob pattern 1))))
 (while (not (null? filelist))
 (let* ((filename (car filelist))
 (image (car (gimp-file-load RUN-NONINTERACTIVE
 filename filename)))
 (drawable (car (gimp-image-get-active-layer image))))
 (set! filename (strbreakup filename "."))
 (set! filename (butlast filename))
 (set! filename (string-append (unbreakupstr filename ".") ".png"))
 (gimp-image-rotate image rotate-type)
 (gimp-levels-stretch drawable)
 (gimp-file-save RUN-NONINTERACTIVE
 image drawable filename filename)
 (gimp-image-delete image))
 (set! filelist (cdr filelist)))))

The scheme function above can be stored into the users GIMP scripting directory (~/.gimp-2.6/scripts/) as "rotate-and-level.scm".

The shell script below can be used to start the batch rotate process on a directory of JPEG pictures.

#!/bin/sh
# file rotate.sh
gimp -i -b "(batch-rotate \"${1}\" ${2})" -b '(gimp-quit 0)'

On a Unix shell, the glob file pattern needs to be quoted so that the shell will not expand it:

# ./rotate.sh "*.JPG" 0
]]>
http://strotmann.de/GIMP-batch-rotate.html 2011年7月15日 00:00:00 +0000
Spring cleaning MacPorts The MacPorts projects offers a fine, easy way to install Unix tools and applications on MacOS X. Over time however, MacPorts can accumulate amounts of dead data, as when applications get updated, the old versions stay until they are removed manually. In addition, after compiling a port from source, both the source and the intermediate object code remains on the harddisk.

Two commands can spring clean the MacPorts installation.

sudo port clean --all installed

will run "make clean" on all installed ports, removing the temporary object code generated during compilation.

sudo port -f uninstall inactive

will remove 'inactive' ports, mainly older versions of applications that have been replaced by a more recent version. Running these two commands can free up some gigabyte of space on a harddisk (depending on the amount of MacPort applications installed).

]]>
http://strotmann.de/MacPortsSprintCleaning.html 2011年4月18日 00:00:00 +0000
Managing the MacOS X IPv6 firewall MacOS X (10.3 and up) contains an IPv6 firewall (ip6fw), which has been inherited from FreeBSD and the KAME project. However there are no configuration or startup scripts, nor any other support available in a stock MacOS X system to manage this firewall.

The script presented here will read a firewall configuration from /etc/ip6fw.conf and will apply the IPv6 firewall rules to the MacOS X firewall.

#!/bin/sh
ip6fwconf="/etc/ip6fw.conf"
ip6fw=`which ip6fw` 
logcmd="logger -p local0.info -t IP6FW"
if [ -z "$ip6fw" ]; then
 $logcmd "no ip6fw binary found in path"
fi
$ip6fw -fq flush
while read line; do
 # remove comments and normalize to lowercase
 line=`echo $line | sed '/^ *#/d;s/#.*//' | tr "[:upper:]" "[:lower:]"`
 if [[ $line == *logging=* ]]; then
 value=${line#*=}
 if [[ $value == 'yes' ]]; then
 $logcmd "logging on"
 sysctl -w 'net.inet6.ip6.fw.verbose=2'
 else
 $logcmd "logging off"
 sysctl -w 'net.inet6.ip6.fw.verbose=0'
 fi
 fi
 action=`echo $line | awk '{ print 2ドル }'`
 rulenum=`echo $line | awk '{ print 1ドル }'`
 rule=`echo $line | awk '{ for (i = 3; i <= NF; i++) { printf "%s ",$i } }'`
 case $action in 
 allow | deny | unreach )
 $logcmd -s "add $rulenum $action $rule"
 $ip6fw add $rulenum $action $rule
 ;;
 "")
 ;; 
 *) $logcmd "unsupported action $action"
 ;;
 esac
done < $ip6fwconf

The default configuration for the MacOS X firewall is

# MacOS X 10.x ip6fw configuration
logging=yes
# loopback 
10000 allow ipv6 from any to any via lo0
# Duplicate Address detection (DAD)
20000 allow ipv6-icmp from any to FF02::/16
# Neighborhood discovery (ND)
20010 allow ipv6-icmp from FE80::/10 to FE80::/10
20015 allow ipv6-icmp from FE80::/10 to FF02::/16
20016 allow ipv6-icmp from any to any icmptype 135,136
# ICMPv6 from the network
20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129
# Router Advertisements 
20050 allow ipv6-icmp from any to any icmptype 134
20055 allow ipv6-icmp from any to any icmptype 133
# allow established TCP connections
20100 allow tcp from any to any established
# NTP multicast
20210 allow udp from any to ff02::1010 123 via en0
# mDNS (Rendezvous/Bonjour)
20220 allow udp from any to ff02::fb 5353 via en0
# allow all outgoing IPv6 connections
20230 allow ipv6 from any to any out
# allow DNS in and out
20240 allow udp from any to any 53 out
20241 allow udp from any 53 to any in
# Secure Shell
20500 allow tcp from any to any 22
# Web-Server
#20510 allow tcp from any to any 80
# Postfix SMTP
#20520 allow tcp from any to any 25
# IPP / CUPS
20530 allow tcp from any to any 631
# Apple Filing Protocol
20540 allow tcp from any to any 548
20541 allow tcp from any to any 427
# block all not otherwise allowed and log
65534 unreach admin log ipv6 from any to any

To execute this script everytime MacOS X boots, a LaunchDaemon configuration is installed in /Library/LaunchDaemons:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
 <key>Disabled</key>
 <false/>
 <key>Label</key>
 <string>de.strotmann.ip6fw</string>
 <key>OnDemand</key>
 <false/>
 <key>ProgramArguments</key>
 <array>
 <string>/usr/local/sbin/ip6fw-load</string>
 </array>
 <key>LaunchOnlyOnce</key>
 <true/>
 <key>RunAtLoad</key>
 <true/>
 <key>KeepAlive</key>
 <false/>
 <key>ServiceDescription</key>
 <string>loads ip6fw firewall rules from /etc/ip6fw.conf</string>
</dict>
</plist>

The installer package will install the script into /usr/local/sbin/ip6fw-load, a default configuration file into /etc/ip6fw.conf/default and if no file /etc/ip6fw.conf exists, it will create this file containing the default configuration. It will then load the LaunchDaemon configuration with

launchctl load -w /Library/LaunchDaemons/de.strotmann.ip6fw.plist

MacOS X 10.5/10.6 installer package

With sudo ip6fw list on the terminal commandline we can check that all the rules are loaded and active:

# sudo ip6fw list
Password:
10000 allow ipv6 from any to any via lo0
20000 allow ipv6-icmp from any to ff02::/16
20010 allow ipv6-icmp from fe80::/10 to fe80::/10
20015 allow ipv6-icmp from fe80::/10 to ff02::/16
20016 allow ipv6-icmp from any to any icmptype 135,136
20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129
20050 allow ipv6-icmp from any to any icmptype 134
20055 allow ipv6-icmp from any to any icmptype 133
20100 allow tcp from any to any established
20210 allow udp from any to ff02::1010 123 via en0
20220 allow udp from any to ff02::fb 5353 via en0
20230 allow ipv6 from any to any out
20240 allow udp from any to any 53 out
20241 allow udp from any 53 to any in
20500 allow tcp from any to any 22
20530 allow tcp from any to any 631
20540 allow tcp from any to any 548
20541 allow tcp from any to any 427
65534 unreach admin log ipv6 from any to any
65535 allow ipv6 from any to any
]]>
http://strotmann.de/ManagingMacOSXIPv6Firewall.html 2011年4月07日 00:00:00 +0000
Fixing the IPv6 Firewall on MacOS X 10.6 On MacOS X 10.6 (Snow Leopard), the IPv6 firewall command line utility 'ip6fw' is broken. It does not store filter rules for ICMPv6 types above type 127:

# sudo ip6fw add 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129
20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4

The command still worked on MacOS X 10.5. It turned out that Apple has compiled ip6fw for x86_64, a 64bit target.

# file /sbin/ip6fw
/sbin/ip6fw: Mach-O universal binary with 3 architectures
/sbin/ip6fw (for architecture x86_64): Mach-O 64-bit executable x86_64
/sbin/ip6fw (for architecture i386): Mach-O executable i386
/sbin/ip6fw (for architecture ppc7400): Mach-O executable ppc

The code, forked from the FreeBSD project in 2001 (which got the original code from the KAME project), is not 64bit clean.

But there is an easy solution. As the file contains in an Universal binary the code for i386 (besides ppc and x86_64), it is possible to strip away the 64bit code (and the ppc code not needed on Intel MacOS X) to get a working copy of ip6fw:

# sudo ditto --rsrc --arch i386 /sbin/ip6fw /sbin/ip6fw.i386
# sudo mv /sbin/ip6fw.i386 /sbin/ip6fw

Now it is possible to enter ICMPv6 filter rules for ICMPv6 types above 127:

# sudo ip6fw add 20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129 
20020 allow ipv6-icmp from any to any in icmptype 1,2,3,4,128,129
]]>
http://strotmann.de/FixingTheMacOSXIPv6Firewall.html 2011年4月03日 00:00:00 +0000
DNS information in IPv6 Router Advertisement on MacOS X Saturday Mar 26, 2011

On my IPv6 router, I have the router advertisement daemon 'radvd' running:

blackbox# more /etc/radvd.conf 
interface eth0
{
 AdvOtherConfigFlag on;
 AdvManagedFlag off;
 AdvSendAdvert on;
 RDNSS 2001:db8:2b6::1 {
 AdvRDNSSLifetime 84600;
 };
 prefix 2001:db8:2b6::/64
 {
 AdvOnLink on;
 AdvAutonomous on;
 };
};

The Line 'RDNSS 2001:db8:2b6::1' configures the advertisement daemon to send the DNS Servers IPv6 address '2001:db8:2b6::1' with a lifetime of 24 hours (86400 seconds).

When the 'rdnssd' service is then started on a MacOS X client, it will receive the DNS server information and write it into the file '/usr/local/var/run/rdnssd/resolv.conf' (this file location can be changed with the commandline option '–resolv-file' (however 'rdnssd' should never write directly into '/etc/resolv.conf', as this will overwrite any data received via DHCP). The content of this file after receiving the DNS information is

more /usr/local/var/run/rdnssd/resolv.conf
nameserver 2001:db8:2b6::1

Whenever the 'rdnssd' daemon updates its 'resolv.conf' file, it can start a script (the script location can be configured with '–merge-hook merge-hook', the default location is '/usr/local/etc/rdnssd/merge-hook' ('rdnssd' comes with an example script for Linux, but this will not work on MacOS X).

simple 'rdnssd' script hook:

#! /bin/sh
#
# resolv.conf merge hook for rdnssd
# MacOS X 10.6 version
# Carsten Strotmann, March 2011 
# partly based on a scipt by Stephan Oeste 
set -e
system_resolv="/etc/resolv.conf"
rdnssd_resolv="/usr/local/var/run/rdnssd/resolv.conf"
# make sure that each IP only appears once
cat $system_resolv $rdnssd_resolv | sort | uniq > /tmp/resolv.conf.tmp 
# get the IP addresses of all DNS servers
ip=`cat /tmp/resolv.conf.tmp | grep "nameserver" | awk '{printf "%s ", 2ドル}'`
# get the local domain
domain=`cat /tmp/resolv.conf.tmp | grep "domain" | awk '{printf "%s ", 2ドル}'`
rm /tmp/resolv.conf.tmp
PSID=$( (scutil | grep PrimaryService | sed -e 's/.*PrimaryService : //')<< EOF
open
get State:/Network/Global/IPv4
d.show
quit
EOF
)
scutil << EOF
open
d.init
d.add ServerAddresses * $ip 
d.add DomainName $domain
set State:/Network/Service/$PSID/DNS
quit
EOF

This is the LaunchDaemon description for the 'rdnssd' service:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
<plist version='1.0'>
<dict>
<key>Label</key><string>net.linkfanel.ndisc6</string>
<key>ServiceDescription</key>
<string>Recursive DNS Solicitation Daemon</string>
<key>ProgramArguments</key>
<array>
 <string>/usr/local/sbin/rdnssd</string>
 <string>-f</string>
 <string>-H/usr/local/etc/rdnssd/merge-hook</string>
 <string>-r/usr/local/var/run/rdnssd/resolv.conf</string>
</array>
<key>WorkingDirectory</key>
<string>/</string>
<key>Debug</key><false/>
<key>Disabled</key><false/>
<key>OnDemand</key><false/>
<key>RunAtLoad</key><true/>
</dict>
</plist>

To start the 'rdnssd' service, copy the above plist into /Library/LaunchDaemons/net.linkfanel.ndisc6.rdnssd.plist and reconfigure launchd with sudo launchctl load -w /Library/LaunchDaemons/net.linkfanel.ndisc6.rdnssd.plist.

]]>
http://strotmann.de/DNSInfoInIPv6RouterAdvMacOSX.html 2011年3月26日 00:00:00 +0000
Debugging a shortlived MacOS application Yesterday I had to debug a MacOS X commandline program that segfaulted immediatly after starting. This program is 'rdnssd' (Recursive DNS Servers discovery Daemon, http://rdnssd.linkfanel.net/). 'rdnssd' implements the client part of RFC 5006 - IPv6 Router Advertisement Option for DNS Configuration. This function lets an IPv6 router send out DNS server IP address information as part of the Router Advertisment messages, helping client finding a DNS server without the need of DHCP or local configuration.

The 'rdnssd' source compiled without issues, but when started, it stopped immediatly with a segfault:

# sudo /usr/local/sbin/rdnssd -f
[1] 1243 segmentation fault sudo /usr/local/sbin/rdnssd -f

On Linux I'm used to strace (and Solaris has truss) to trace the sysemcalls of a process, hoping that would point me to the error. MacOS X 10.5/10.6 has several very useful tools build on top of DTrace. Apple Technical Note 2124 lists a good part of these tools.

A run of the process using dtrace only showed that the process died after forking, with no additional information. errinfo also didn't show any suspicious syscalls failing. But the use of opensnoop (traces the files opened by a process) showed that acutally MacOS X is writing a crash dump for the failing process with additional information:

# opensnoop | grep rdnssd 
 0 1446 rdnssd 3 /dev/urandom 
 0 1446 rdnssd 3 /dev/dtracehelper 
 0 1446 rdnssd 3 /dev/urandom 
 0 1446 rdnssd 3 /dev/urandom 
 0 1446 rdnssd 3 /usr/share/locale/en_US.UTF-8/LC_CTYPE 
 0 1448 rdnssd -1 /etc/sysinfo.conf 
 0 1447 taskgated 4 /usr/local/sbin/rdnssd 
 0 1446 rdnssd 3 /usr/local/var/run/rdnssd.pid 
 0 1449 ReportCrash 5 /usr/local/sbin/rdnssd 
 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/rdnssd.o 
 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/icmp.o 
 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/rdnssd.o 
 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd 
 0 1449 ReportCrash 5 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/icmp.o 
 0 1449 ReportCrash -1 /Developer/source/ipv6/ndisc6-1.0.1/rdnssd/../compat/libcompat.a(ppoll.o) 
 0 1449 ReportCrash 5 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 
 0 1449 ReportCrash -1 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 
 0 1449 ReportCrash 6 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618-1_localhost.crash 
 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 
 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 
 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 
 0 1449 ReportCrash 5 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 
 89 1450 mdworker 6 /usr/local/var/run/rdnssd.pid 
 89 1450 mdworker 6 /usr/local/var/run/rdnssd.pid 
 0 34 mds 57 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618-1_localhost.crash 
 89 34 mds -1 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618-1_localhost.crash 
 0 34 mds 57 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 
 89 34 mds -1 /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 
 89 1450 mdworker -1 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 
 0 34 mds 7 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 
 89 34 mds -1 /Library/Application Support/CrashReporter/rdnssd_1472A851-7F63-5DAA-BB39-7AD15590893E.plist 

A look into the crash report file revealed the information I was looking for:

 more /Library/Logs/DiagnosticReports/rdnssd_2011年03月10日-190618_localhost.crash 
Process: rdnssd [1446]
Path: /usr/local/sbin/rdnssd
Identifier: rdnssd
Version: ??? (???)
Code Type: X86-64 (Native)
Parent Process: zsh [1161]
Date/Time: 2011年03月10日 19:06:17.998 +0100
OS Version: Mac OS X 10.6.6 (10J567)
Report Version: 6
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000008
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Thread 0 Crashed: Dispatch queue: com.apple.main-thread
0 rdnssd 0x00000001000023d2 ppoll + 66
1 rdnssd 0x0000000100001fc2 main + 2418 (rdnssd.c:377)
2 rdnssd 0x00000001000010a4 start + 52
Thread 0 crashed with X86 Thread State (64-bit):
 rax: 0x0000000000000000 rbx: 0x00000000ffffffff rcx: 0x00007fff80b1ea02 rdx: 0x0000000000000000
 rdi: 0x0000000000000003 rsi: 0x00007fff5fbffa4c rbp: 0x00007fff5fbff910 rsp: 0x00007fff5fbff8d0
 r8: 0x00007fff70125f0c r9: 0x0000000000000000 r10: 0x00007fff5fbffa4c r11: 0x0000000000000202
 r12: 0x0000000000000000 r13: 0x0000000000000001 r14: 0x00007fff5fbffa30 r15: 0x00007fff5fbff8dc
 rip: 0x00000001000023d2 rfl: 0x0000000000010246 cr2: 0x0000000000000008
Binary Images:
 0x100000000 - 0x100002ff7 +rdnssd ??? (???) /usr/local/sbin/rdnssd
 0x7fff5fc00000 - 0x7fff5fc3bdef dyld 132.1 (???) /usr/lib/dyld
 0x7fff80abc000 - 0x7fff80c7dfff libSystem.B.dylib 125.2.1 (compatibility 1.0.0) <71E6D4C9-F945-6EC2-998C-D61AD590DAB6> /usr/lib/libSystem.B.dylib
 0x7fff82f2c000 - 0x7fff82f30ff7 libmathCommon.A.dylib 315.0.0 (compatibility 1.0.0) <95718673-FEEE-B6ED-B127-BCDBDB60D4E5> /usr/lib/system/libmathCommon.A.dylib
 0x7fffffe00000 - 0x7fffffe01fff libSystem.B.dylib ??? (???) <71E6D4C9-F945-6EC2-998C-D61AD590DAB6> /usr/lib/libSystem.B.dylib

The culprit is in the sourcefile rdnssd.c, line 377, in the function call 'ppoll'. The reason was a bad memory access (EXC_BAD_ACCESS). This should be all information needed to find and fix the issue.

Stay tuned…

]]>
http://strotmann.de/ShortlivedMacOSApplication.html 2011年3月10日 00:00:00 +0000
JASSPA MicroEmacs for TinyCore Linux JASSPA MicroEmacs is a lean, but very powerfull text editor for many different operating systems. It is not driven by Lisp as GNU/Emacs, but with a powerful macro programming language.

screenshot_0307170513.png

Below are packages for the TinyCore Linux System, compiled from the 11 October 2009 release sourcecode from the JASSPA Website.

There are three different packages:

]]>
http://strotmann.de/JASSPA-Microemacs-TinyCoreLinux.html 2011年3月07日 00:00:00 +0000
Once upon a time at – Hobbytronic Monday May 11, 2009

Yesterday, I was looking for a 3 1⁄2" floppy disk for an Atari ST. I found one disk without label, and before formatting it, I tool a look what's stored on it. To my surprise, I found some of the first digitized pictures (from me) made on Hobbytronic 1992 (a computer fair in Dortmund) where ABBUC had a booth opposite a company doing scanner and digitizer cameras. So below we have the ABBUC Hobbytronic booth crew of 1992:

Wolfgang.gif burkhard.gif
Wolfgang Burger Burghard Wagner
joachim.gif johann.gif
Joachim Siegmund Johannes Barth
joost.gif stephan.gif
Joost Küp Stephan Düsterhöft (BenjiSoft)
juergen.gif karsten.gif
Jürgen Schildmann (BenjiSoft) Karsten Schmidt (CSS)
rolf.gif theo.gif
Rolf A. Specht (Analog Research) Theo Schwacke
carsten.gif test1.gif
Carsten Strotmann Wolfgang Burger (again)
]]>
http://strotmann.de/Hobbytronic1992.html 2009年5月11日 00:00:00 +0000
Forth on the Vintage Computer Festival Europe 2009 On Booth 23 (me with entry ticket 42, for all numerologicals) I had two RTX2000 single board computer on display.

The theme of the exhibition has been "In space, no one can hear you scream", and for the RTX 2000 machines there was materials collected about NASA, AMSAT and ESA space missions that make use of the RTX 2000 CPU. Both RTX2000 Boards (a "kleiner Muck" and a "Wiesel 2000") were shown working, connected via RS232 to an Amstrad NC100 that was used as a terminal and Forth Editor.

Especially the VME Bus on the "kleiner Muck" got attention, and there will be a follow up project to connect the Muck with an Atari TT (which also has an VME Bus internally).

To be able to judge the processing speed of the RTX2000 compared with a regular CPU, Stefan "Beetle" Niestegge from the Atari TT exhibition helped running a simple benchmark (Primes) on both boards and on an Atari TT. The Atari TT was running BigForth ST on MINT on an Motorola 68030 with 30 Mhz.

As a result we found that the "Muck" with an 6Mhz RTX2000 is about the same processing speed (when it comes to Forth) as the 30Mhz Atari TT. Interestingly the 10Mhz "Wiesel" was about two times slower than the "Muck" and the "TT", which is probably due to some errors in our benchmarking (If someone has worked with the "Wiesel" board and has an idea what can cause this slowdown, please leave a comment).

The VCFe was again a very enjoyable weekend. Unfortunately the weather in Munich was too good, so there were not too much visitors finding their way into the VCFe Exhibition grounds.

The topic for next years VCFe will be "Online Communication". If you have nostalgia for UUCP, Mailboxing and acoustic couplers, please join next year!

]]>
http://strotmann.de/ForthVCFE2009.html 2009年5月09日 00:00:00 +0000
Learning Clojure Clojure had been overtaken Scala as the next programming language to learn (for me, at least, on the list of languages to learn which is almost as long as the list of places to visit). Both, Scala and Clojure, are languages for the JVM, or Java Virtual machine, and fit well in most work that I'm doing.

Learning Groovy last year and using it with Grails this year was great fun.

The cause is that Pragmatic Programmers have just release a beta book about Clojure, while there Scala book will arrive later this year.

While learning Clojure, my plan is also to give Emacs another try as my IDE of choice. So far, my flirt with Emacs always did not last very long, for various reasons.

But because Clojure and Emacs seems to be a nice fit, I will give Emacs another try.

Below some links (mainly for myself to remember):

]]>
http://strotmann.de/LearningClojure.html 2008年11月09日 00:00:00 +0000
'Timemachine'ish backup with ZFS and rsync Tuesday Aug 05, 2008

Apple MacOS X Timemachine is a nice piece of software. However it does not compress the data, and it only works on MacOS X. I was looking to a similar solution that works also on other Unix Systems (as well as MacOS X) and does transparent compression of the data. The idea is to have multiple backups on one disk, each backup showing the state of the source hard disk or directory at the time of the backup, browsable by the normal file system tools, without storing any data duplicated. I found a solution using the ZFS file system and 'rsync' (rsync is pre-installed on most Unixish operating systems).

Requirements

My tutorial is for MacOS X, but it can be adapted to any of the Systems that support the ZFS file system

Step 1: preparing a ZFS file system

I used the steps from the ZFS on MacOS-Forge site to create a ZFS pool on an external USB drive: Finding the disk:

# diskutil list . . . /dev/disk2 #: type name size identifier 0:
Apple\_partition\_scheme *9.4 GB disk2 1: Apple\_partition\_map 31.5 KB
disk2s1 2: Apple\_HFS FW 9.2 GB disk2s3

writing a GPT label on the external disk (be sure to not format your 'main' disk here!):

# diskutil partitiondisk /dev/disk2 GPTFormat ZFS %noformat% 100%
Started partitioning on disk disk2 Creating partition map 
[ +0%..10%..20%..30%..40%..50%..60%..70%..80%..90%..100% ] 
Finished partitioning on disk disk2 /dev/disk2 
#: type name size identifier 0: GUID\_partition\_scheme *9.4 GB disk2 1: EFI 200.0 MB disk2s1 2: ZFS 9.0 GB disk2s2

creating a ZFS pool on the disk called 'macbook-backup':

# zpool create macbook-backup /dev/disk2s2

enable compression on the new pool and disable ATIME:

# zfs set compression=on macbook-backup 
# zfs set atime=off macbook-backup

the hard drive is now prepared.

Step 2: creating the first (the 'base') backup

now I create the first full backup, which I call the 'base' backup. For this I create a new file system called 'base' in the ZFS pool 'macbook-backup':

# zfs create macbook-backup/base

next I copy all files from my backup-source directory (or the whole source disk) to the backup:

# rsync -avh --progress --delete /Users/myuser /Volumes/macbook-backup/base/

depending on the size of the data to backup, this will take a while.

Once the backup is finished, we can access all files under /Volumes/macbook-backup/base. With the ZFS command I can check the compression-ratio of our backup:

# zfs get all macbook-backup/base NAME PROPERTY VALUE SOURCE
macbook-backup/base type filesystem - 
macbook-backup/base creation Wed Jan 31 9:08 2007 - 
macbook-backup/base used 2.21G - 
macbook-backup/base available 1.76G - 
macbook-backup/base referenced 2.21G - 
macbook-backup/base compressratio 1.38x - 
macbook-backup/base mounted yes - 
macbook-backup/base quota none default 
macbook-backup/base reservation none default 
macbook-backup/base recordsize 128K default 
macbook-backup/base mountpoint /Volumes/macbook-backup/base default 
macbook-backup/base sharenfs off default 
macbook-backup/base shareiscsi off default 
macbook-backup/base checksum on default 
macbook-backup/base compression on local 
macbook-backup/base atime off local 
macbook-backup/base devices on default 
macbook-backup/base exec on default 
macbook-backup/base setuid on default 
macbook-backup/base readonly off default 
macbook-backup/base zoned off default 
macbook-backup/base snapdir hidden default 
macbook-backup/base aclmode groupmask default 
macbook-backup/base aclinherit secure default 
macbook-backup/base canmount on default 
macbook-backup/base xattr on default

Step 3: creating an incremental backup

Now, a few weeks later, I want to make a new, incremental backup. So I create a new snapshot of the base file system and then clone that snapshot into a new file system:

zfs snapshot macbook-backup/base@20080804 
zfs clone macbook-backup/base@20080804 macbook-backup/20080804

The directory for my new, incremental backup will be /Volumes/macbook-backup/20080804. So far the new file system does not use any space on the hard drive. Now I do the new backup with 'rsync':

# rsync -avh --progress --delete /Users/myuser /Volumes/macbook-backup/20080804/

The new backup will only take as much new space on the backup hard drive as there were changes compared to the base backup. But still I am able to browse through the file system at /Volumes/macbook-backup/20080804 and see all files that were available at the date of the 2nd backup.

Any subsequent snapshots for more backups will be done from the 20080804 file system.

]]>
http://strotmann.de/timemachine-zfs-rsync.html 2008年8月05日 00:00:00 +0000
uEmacs fuer Solaris und OS2 (alt) Dies ist ein Eintrag aus meinem alten Wiki und der alten "strotmann.de" Homepage von 1999, welcher aber immer wieder einmal nachgefragt wurde und den es daher auch im neuen Blog gibt.

  • Der Texteditor MicroEmacs. Eine 32 Bit EMX Version des MicroEmacs für OS/2 2.x und höher (Binärdateien und Sourcen für OS/2).
  • MicroEmacs 99 (JASSPA); Binärdatei (ELF) für Solaris 7 x86.
  • MicroEmacs 4.00 (orig); Binärdatei (ELF) für Solaris 7 x86.

MicroEmacs Solaris 7 (x86)

Binärdateien für SUN Solaris Unix Version 7 (SunOS 5.7) für Intel Prozessoren (nicht SPARC). Es ist die original MicroEmacs 4.00 Version und die JASSPA MicroEmacs 99 Version verfügbar. Unbedingt die benötigten Resourcedateien von der jeweiligen Homepage besorgen.

mEmacs für OS/2 2.x, Warp (3), Warp 4, Aurora, eComStation

Dies ist die 32 Bit Version des MicroEmacs 4.00 Texteditor. Ich habe die Quelldateien der 3.12 EMX/GCC Version von Ralf Seidel (siehe Datei readme.os2) mit dem Quellcode 4.00 von Daniel Lawrence zusammengeführt. Die verwendete EMX Version ist 0.9d, es wird also die EMX Runtimeumgebung emxrt.zip ( http://hobbes.nmsu.edu oder http://www.leo.org) benötigt.

Ich habe einige Änderungen an dem Quellcode in os2npm.c durchgeführt um auf eine erweiterte Bildschirmauflösung von 50x132 Zeichen (XGA) schalten zu können. Diese Auflöung wird durch den Befehl "set $sres XGA" in der emacs.rc eingestellt. Die Bildschirmauflösung kann auch durch den OS/2 Befehl "mode con1 coYY,XX" vor dem Start von MicroEmacs eingestellt werden. XX ist die Anzahl der Spalten, YY ist die Anzahl der Zeilen.

Einige Änderungen in den Quelldateien file.c und german.h verbessern die Unterstützung der deutschen Sprache.

Bei Fragen oder Anregungen zur OS/2 Version schreibt mir eine Mail via Internet-Mail: carsten@strotmann.de.

English translation

  • Port of the texteditor MicroEmacs for OS/2. This is the 32bit EMX version of MicroEmacs for OS/2 2.x or above. Binaries and Sources.
  • MicroEmacs 99 (JASSPA); binaries (ELF) for SUN Solaris 7 (SunOS 5.7) x86.
  • MicroEmacs 4.00 (orig); binaries (ELF) for SUN Solaris 7 (SunOS 5.7) x86.

mEmacs binaries for Solaris 7 x86

Binaries for SUN Solaris Unix Version 7 (SunOS 5.7) / Intel (not SPARC). The original MicroEmacs 4.00 version and the JASSPA MicroEmacs 99 versions are available. Please don't forget to download the resourcefiles from the distribution-homepages!

mEmacs for OS/2 2.x, Warp (3), Warp 4, Aurora

This is a 32 Bit version of MicroEmacs 4.00. I used the MicroEmacs 3.12 emx/gcc port of Ralf Seidel (see file readme.os2) and merged it with the 4.00 Source from Daniel Lawrence. The emx version used is 0.9d, so you need emxrt.zip ( http://hobbes.nmsu.edu or http://www.leo.org).

I changed the Source os2npm.c to use a extended screen resolution XGA 50x132. You can set this resolution with "set $sres XGA" in the emacs.rc file. You can also change the screen resolution before starting MicroEmacs with the OS/2 command "mode con1 coYY,XX, where YY are the rows and XX are the columns.

I also changed file.c and german.h for german language support.

If you have questions about this port, feel free to contact me via Internet-Mail: carsten@strotmann.de.

Screenshots

  • MicroEmacs 4.00 (OS/2) Screenshot:
    memacs.png
  • JASSPA MicroEmacs (Solaris OpenLOOK):
    memacs_solaris.png
]]>
http://strotmann.de/uemacs-solaris.html 2008年6月28日 00:00:00 +0000

AltStyle によって変換されたページ (->オリジナル) /