Compare commits
10 commits
Author | SHA1 | Date | |
---|---|---|---|
3d8ae03a2a | |||
ee4b17e1ca | |||
ed2d4bd2b3 | |||
d4ed662cdf | |||
8c16278ffc | |||
3b2c0dd92d | |||
25b701e10a | |||
1fe0245e1f | |||
3395f3edac | |||
8b7103180c |
|
@ -43,7 +43,7 @@ data_sources:
|
|||
# same as the items root, but applies to layouts rather than items.
|
||||
layouts_root: /
|
||||
|
||||
base_url: 'http://www.wezm.net'
|
||||
base_url: 'https://www.wezm.net'
|
||||
deploy:
|
||||
default:
|
||||
kind: rsync
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
User-Agent: *
|
||||
Disallow:
|
||||
Sitemap: <%= @config[:base_url] %>/sitemap.xml
|
||||
Sitemap: <%= @config[:base_url] %>/v2/sitemap.xml
|
||||
|
|
39
v2/content/posts/2023/anz-youtube-makers.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
+++
|
||||
title = "Australian and New Zealand Makers on YouTube"
|
||||
date = 2023-04-11T10:04:27+10:00
|
||||
|
||||
[extra]
|
||||
updated = 2023-04-14T12:16:17+10:00
|
||||
+++
|
||||
|
||||
I decided I wanted to add some more local folks into my YouTube subscriptions.
|
||||
I [put the call out on Mastodon][toot] for suggestions for folks doing videos
|
||||
about machining, woodworking, electronics, software, that type of thing. I
|
||||
received a number of helpful replies and thought it might be useful to collect
|
||||
the list (as well as ones I'm already subscribed to) on this page in case
|
||||
others are looking for new channels to check out.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
* [Andrew Newton](https://www.youtube.com/@AndrewNewton)
|
||||
* [Artisan Makes](https://www.youtube.com/@artisanmakes)
|
||||
* [BensWorx](https://www.youtube.com/@BensWorx)
|
||||
* [Clickspring](https://www.youtube.com/@Clickspring)
|
||||
* [Cutting Edge Engineering Australia](https://www.youtube.com/@CuttingEdgeEngineering)
|
||||
* [EEVblog](https://www.youtube.com/@EEVblog)
|
||||
* [iforce2d](https://www.youtube.com/@iforce2d)
|
||||
* [Luke Towan](https://www.youtube.com/@LukeTowan)
|
||||
* [Mach Super](https://www.youtube.com/@machsuper)
|
||||
* [Maker's Muse](https://www.youtube.com/@MakersMuse)
|
||||
* [MattKC](https://www.youtube.com/@MattKC) (Matt moved to the US but he still counts 🙂)
|
||||
* [MooreTech](https://www.youtube.com/@MooreTech) -- It's me!
|
||||
* [Pask Makes](https://www.youtube.com/@PaskMakes)
|
||||
* [Primitive Technology](https://www.youtube.com/@primitivetechnology9550)
|
||||
* [Psivewri](https://www.youtube.com/@psivewri)
|
||||
* [SuperHouseTV](https://www.youtube.com/@SuperHouseTV)
|
||||
* [Unexpected Maker](https://www.youtube.com/@UnexpectedMaker)
|
||||
|
||||
That's the list for now. If you have other suggestions (especially if they can
|
||||
mix up the diversity of the list a bit) please let me know.
|
||||
|
||||
[toot]: https://mastodon.decentralised.social/@wezm/110165869937514088
|
BIN
v2/content/posts/2023/derez/DITL.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
v2/content/posts/2023/derez/Dialog.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
v2/content/posts/2023/derez/MPW.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
182
v2/content/posts/2023/derez/index.md
Normal file
|
@ -0,0 +1,182 @@
|
|||
+++
|
||||
title = "How to use DeRez"
|
||||
date = 2023-03-20T12:44:38+10:00
|
||||
|
||||
#[extra]
|
||||
#updated = 2023-03-01T21:54:06+10:00
|
||||
+++
|
||||
|
||||
After my post on trying to run
|
||||
[Rust on Classic Mac OS post](@/posts/2023/rust-on-ppc-classic-mac-os/index.md) I continued trying to
|
||||
find a modern language that I can use to build classic Mac OS software. I've
|
||||
had some success with [Nim] and built a little
|
||||
[temperature converter application][nim-temp-src]. As part of this I wanted to be able to use
|
||||
[ResEdit] to edit the layout of the dialog. The problem was that I need a way
|
||||
to convert the modified resources back into the textual representation used in
|
||||
the source code. In this post I describe how I did this with `DeRez`.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
{{ video(video="posts/2023/derez/mac-nim-temp.mp4", height=227, poster="png", preload="auto", alt="Video of the temperature converter application.", caption="Video of the temperature converter application.") }}
|
||||
|
||||
To build the temperature converter I started with the [Dialog sample] from
|
||||
[Retro68], which looks like this:
|
||||
|
||||
{{ figure(image="posts/2023/derez/Dialog.png", link="posts/2023/derez/Dialog.png", pixelated=true, alt="Screenshot of the Dialog sample from Retro68. It has a static text item, edit text item, check box, two radio buttons, and a Quit button.", caption="Dialog Sample") }}
|
||||
|
||||
I opened it up in ResEdit and edited the `DITL` (Dialog Template) resource to
|
||||
add the icon and temperature fields. I also added a new `ICON` resource and
|
||||
drew a little thermometer:
|
||||
|
||||
{{ figure(image="posts/2023/derez/DITL.png", link="posts/2023/derez/DITL.png", pixelated=true, alt="Screenshot of the ResEdit DITL editor editing the DITL resource for my temperature converter.", caption="Editing DITL resource in ResEdit") }}
|
||||
|
||||
With the changes made, I now wanted to convert the binary resources stored in
|
||||
the resource fork back into the [textual format used in the source code][dialog.r].
|
||||
I believe the format is called `Rez`, here's a snippet of it:
|
||||
|
||||
```
|
||||
resource 'DITL' (128) {
|
||||
{
|
||||
{ 190-10-20, 320-10-80, 190-10, 320-10 },
|
||||
Button { enabled, "Quit" };
|
||||
|
||||
{ 190-10-20-5, 320-10-80-5, 190-10+5, 320-10+5 },
|
||||
UserItem { enabled };
|
||||
|
||||
{ 10, 10, 30, 310 },
|
||||
StaticText { enabled, "Static Text Item" };
|
||||
|
||||
{ 40, 10, 56, 310 },
|
||||
EditText { enabled, "Edit Text Item" };
|
||||
|
||||
{ 70, 10, 86, 310 },
|
||||
CheckBox { enabled, "Check Box" };
|
||||
|
||||
{ 90, 10, 106, 310 },
|
||||
RadioButton { enabled, "Radio 1" };
|
||||
|
||||
{ 110, 10, 126, 310 },
|
||||
RadioButton { enabled, "Radio 2" };
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
This turned out to be a bit of journey and the motivation for this blog post.
|
||||
As part of the [Macintosh Programmers Workshop][mpw] (MPW) theres is a tool
|
||||
called `DeRez` that does what I want. First up I had to work out how to operate
|
||||
MPW. It's an editable shell where you run commands with ⌘-Return. Once I worked
|
||||
that out I could run `DeRez` on my edited application but I only got the
|
||||
fullback hexadecimal representation of the resources, not the structured output
|
||||
I wanted:
|
||||
|
||||
```
|
||||
data 'DITL' (128) {
|
||||
$"0007 0000 0000 00A0 00E6 00B4 0136 0404" /* ....... .æ.´.6.. */
|
||||
$"5175 6974 0000 0000 009B 00E1 00B9 013B" /* Quit......á.¹.; */
|
||||
$"0000 0000 0000 0046 000A 005A 0136 0808" /* .......F...Z.6.. */
|
||||
$"4865 6C6C 6F20 5E30 0000 0000 001E 000A" /* Hello ^0........ */
|
||||
$"003E 002A A002 0597 0000 0000 0014 0032" /* .>.* .........2 */
|
||||
$"0024 007D 8807 4365 6C73 6975 7300 0000" /* .$.}.Celsius... */
|
||||
$"0000 0014 00AA 0024 00F5 8809 4661 7265" /* .....ª.$.õÆFare */
|
||||
$"6E68 6569 7400 0000 0000 0029 0036 0039" /* nheit......).6.9 */
|
||||
$"0081 1009 4564 6974 2054 6578 7400 0000" /* ..ÆEdit Text... */
|
||||
$"0000 002B 00AE 003B 00F9 1009 4564 6974" /* ...+.®.;.ù.ÆEdit */
|
||||
$"2054 6578 7400" /* Text. */
|
||||
};
|
||||
```
|
||||
|
||||
`Help DeRez` in MPW didn't shed much light on the problem but after a lot of
|
||||
searching online I eventually found some extra details in the [man page for
|
||||
`DeRez`][DeRez man] shipped on Mac OS X. Specifically:
|
||||
|
||||
> The type declarations for the standard
|
||||
> Macintosh resources are contained in the `Carbon.r` resource header file,
|
||||
> contained in the Carbon framework. You may use the ${RIncludes} shell
|
||||
> environment variable to define a default path to resource header files.
|
||||
> If you do not specify any type declaration files, `DeRez` produces data
|
||||
> statements in hexadecimal form.
|
||||
|
||||
and
|
||||
|
||||
> You can also specify resource description
|
||||
> files containing type declarations. For each type declaration file on
|
||||
> the command line, DeRez applies the following search rules:
|
||||
>
|
||||
> 1. DeRez tries to open the file with the name specified as is.
|
||||
>
|
||||
> 2. If rule 1 fails and the filename contains no colons or begins with a
|
||||
> colon, DeRez appends the filename to each of the pathnames specified by
|
||||
> the {RIncludes} environment variable and tries to open the file.
|
||||
|
||||
With this information I was able to construct a command that worked:
|
||||
|
||||
|
||||
```
|
||||
DeRez -i 'Macintosh HD:MPW-GM:Interfaces&Libraries:Interfaces:RIncludes:' "Macintosh HD:Retro68:Retro68App" Carbon.r
|
||||
```
|
||||
|
||||
`-i` sets the include path for type declarations and `Carbon.r` tells it to use
|
||||
that file for resource descriptions. Running the command I was now rewarded
|
||||
with textual resources:
|
||||
|
||||
{{ figure(image="posts/2023/derez/MPW.png", link="posts/2023/derez/MPW.png", pixelated=true, alt="Screenshot of an MPW worksheet on Mac OS 8 showing the output of running DeRez on an application.", caption="DeRez output in MPW") }}
|
||||
|
||||
To get the text out of the VM I copied and pasted it into a new document in
|
||||
[BBEdit] (version 5.0) and saved it with Unix line endings to the Unix folder
|
||||
that [SheepShaver] shares with the host and with that I was able to update the
|
||||
[resource file in my temperature converter project][my dialog.r].
|
||||
|
||||
### Honorable Mention
|
||||
|
||||
Whilst trying to work out how to do all this I was also reminded of Ninji's
|
||||
[mpw-emu] project ([detailed write-up on their blog][mpw-emu-blog]). It
|
||||
combines an emulator with implementations of library functions in order to be
|
||||
able to run MPW tools directly (outside a Mac OS emulator). It has gained
|
||||
support for `DeRez` so you can run `DeRez` directly on a host system like
|
||||
Linux.
|
||||
|
||||
I [MacBinaried][MacBinary] `DeRez` in SheepShaver and copied it to my Linux host. Then with a bit
|
||||
of fussing with `mpw-emu` Rust code I was able to run it:
|
||||
|
||||
```
|
||||
$ mpw-emu ~/Documents/Classic\ Mac/Shared\ 2/DeRez.bin
|
||||
[2023-03-20T02:11:07Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C
|
||||
[2023-03-20T02:11:07Z ERROR stdio] Unimplemented format character: P
|
||||
[2023-03-20T02:11:07Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4
|
||||
### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - No filename to de-compile was specified.
|
||||
### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - Usage: /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin resourceFile [-c] [-d name[=value]] [-e] [-i path] [-m n] [-noResolve [output | include]] [-only type[(id[:id])]] [-p] [-rd] [-s type[(id[:id])]] [-script japanese | tradChinese | simpChinese | korean] [-u name] [file…].
|
||||
```
|
||||
|
||||
Amazing!
|
||||
|
||||
Unfortunately I don't think `DeRez` will work this way outside a macOS host. It
|
||||
needs to be able to read the resource fork of the application I edited with
|
||||
ResEdit and that is not preserved on Linux:
|
||||
|
||||
```
|
||||
$ mpw-emu ~/Documents/Classic\ Mac/Shared\ 2/DeRez.bin Dialog.APPL
|
||||
[2023-03-20T02:14:05Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C
|
||||
[2023-03-20T02:14:05Z ERROR stdio] Unimplemented format character: P
|
||||
[2023-03-20T02:14:05Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4
|
||||
### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - The resource fork of "Dialog.APPL" is empty and uninitialized.
|
||||
```
|
||||
|
||||
If you're on macOS I think that this would actually work. Although now I think
|
||||
about it Xcode ships (or at least used to) a native version of `DeRez` so now
|
||||
I'm not sure what Ninji's motivation for making it work in `mpw-emu` was.
|
||||
Perhaps it is possible to use on Linux somehow...
|
||||
|
||||
[Nim]: https://nim-lang.org/
|
||||
[ResEdit]: https://en.wikipedia.org/wiki/ResEdit
|
||||
[Retro68]: https://github.com/autc04/Retro68
|
||||
[Dialog sample]: https://github.com/autc04/Retro68/tree/5f882506013a0a8a4335350197a1b7c91763494e/Samples/Dialog
|
||||
[dialog.r]: https://github.com/autc04/Retro68/blob/5f882506013a0a8a4335350197a1b7c91763494e/Samples/Dialog/dialog.r
|
||||
[DeRez man]: https://www.manpagez.com/man/1/DeRez/
|
||||
[BBEdit]: http://www.barebones.com/products/bbedit/index.html
|
||||
[SheepShaver]: https://sheepshaver.cebix.net/
|
||||
[my dialog.r]: https://github.com/wezm/classic-mac-nim/blob/39e6ed7c2b31c20b775782319cde8ae5a43e1512/dialog.r
|
||||
[mpw-emu]: https://github.com/Treeki/mpw-emu
|
||||
[mpw-emu-blog]: https://wuffs.org/blog/emulating-mac-compilers
|
||||
[mpw]: https://en.wikipedia.org/wiki/Macintosh_Programmer%27s_Workshop
|
||||
[MacBinary]: https://en.wikipedia.org/wiki/MacBinary
|
||||
[nim-temp-src]: https://github.com/wezm/classic-mac-nim/tree/39e6ed7c2b31c20b775782319cde8ae5a43e1512
|
BIN
v2/content/posts/2023/derez/mac-nim-temp.mp4
Normal file
BIN
v2/content/posts/2023/derez/mac-nim-temp.png
Normal file
After Width: | Height: | Size: 24 KiB |
|
@ -0,0 +1,44 @@
|
|||
+++
|
||||
title = "Fixing OpenBSD panic dc_atapi_start: not ready in KVM"
|
||||
date = 2023-09-17T12:13:24+10:00
|
||||
|
||||
[extra]
|
||||
updated = 2023-09-22T11:56:57+10:00
|
||||
+++
|
||||
|
||||
I tried creating an OpenBSD 7.3 virtual machine on my new computer (Arch Linux
|
||||
host) and the installer kept crashing with the error:
|
||||
|
||||
{{ figure(image="posts/2023/openbsd-db-atapi-start-not-ready/openbsd_panic_dc_atapi_start_not_ready.png", link="posts/2023/openbsd-db-atapi-start-not-ready/openbsd_panic_dc_atapi_start_not_ready.png", alt="Screenshot of the installer crash.", caption="Screenshot of the installer crash.") }}
|
||||
|
||||
```
|
||||
dc_atapi_start: not ready, st = 50
|
||||
fatal protection fault in supervisor mode trap type 4 code 0 rip ffffffff810089d9 cs 8 rflags 10282 cr2 287eb3000 cpl 6 rsp ffff800014fd11a0
|
||||
gssbase Oxffffffff818fbff0 kgsbase Ox0
|
||||
panic: trap type 4, code=0, pc=ffffffff810089d9
|
||||
syncing disks...12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 _
|
||||
```
|
||||
|
||||
<!-- more -->
|
||||
|
||||
I did a bunch of searching online and tried a several different suggestions but that one that
|
||||
worked for me was from [this Reddid thread][reddit]:
|
||||
|
||||
> **tinneriw31**
|
||||
>
|
||||
> Switch the virtual cd drive from ide to sata. Worked for me. Exact same issue.
|
||||
|
||||
I use [virt-manager] to manage VMs. These are the steps to do that when creating the VM:
|
||||
|
||||
1. Create the VM and at the last step choose "Customize configuration before install"
|
||||
2. Click on the "IDE CDROM 1" tab and change "Disk bus" to SATA
|
||||
3. Then click Apply, and then Begin installation in the top left.
|
||||
|
||||
After that the VM installed successfully.
|
||||
|
||||
{{ figure(image="posts/2023/openbsd-db-atapi-start-not-ready/virt-manager-customize-configuration.png", link="posts/2023/openbsd-db-atapi-start-not-ready/virt-manager-customize-configuration.png", alt="Screenshot of step 5 in the new virtual machine wizard in virt-manager showing the 'Customize configuration before install' option checked.", caption="Customize configuration before install.", width=504) }}
|
||||
|
||||
{{ figure(image="posts/2023/openbsd-db-atapi-start-not-ready/virt-manager-sata-cd-rom.png", link="posts/2023/openbsd-db-atapi-start-not-ready/virt-manager-sata-cd-rom.png", alt="Screenshot of the virt-manager CD ROM tab showing 'Disk bus: SATA' selected.", caption="Disk bus: SATA", width=1028) }}
|
||||
|
||||
[reddit]: https://www.reddit.com/r/openbsd/comments/12jzg2y/when_i_tried_to_install_openbsd_73_in_qemu_i/jhhk1gx/
|
||||
[virt-manager]: https://virt-manager.org/
|
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 153 KiB |
BIN
v2/content/posts/2023/rust-classic-mac-os-app/Ferris Weather.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 78 KiB |
297
v2/content/posts/2023/rust-classic-mac-os-app/index.md
Normal file
|
@ -0,0 +1,297 @@
|
|||
+++
|
||||
title = "Building a Classic Mac OS App in Rust"
|
||||
date = 2023-03-31T13:26:07+10:00
|
||||
|
||||
#[extra]
|
||||
#updated = 2023-03-26T14:27:05+10:00
|
||||
+++
|
||||
|
||||
Instead of using my funemployment to build useful things I have continued to
|
||||
build things for old versions of Mac OS. Through some luck and a little
|
||||
persistence I have actually managed to get Rust code running on classic Mac OS
|
||||
(I've tried Mac OS 7.5 and 8.1). In this post I'll cover how I got here and
|
||||
show a little network connected demo application I built—just in time for
|
||||
the end of [#MARCHintosh].
|
||||
|
||||
<!-- more -->
|
||||
|
||||
Before I get into the details this is where we're headed:
|
||||
|
||||
{{ video(video="posts/2023/rust-classic-mac-os-app/ferris-weather-2023-03-31_12.37.49_edit.mp4", height=480, poster="png", preload="auto", alt="Video showing the Ferris Weather application in operation. Initially there is a window with a Ferris icon, the text, 'An application that exercises my Open Transport Rust bindings and HTTP client', and a button 'Get Weather'. Clicking the button results in an alert that says: 'The temperature in Brisbane is 26.9°C'.", caption="Ferris Weather, a weather app built with Rust (and some C)") }}
|
||||
|
||||
### DeRez Redux
|
||||
|
||||
In [the last post](@/posts/2023/derez/index.md) I got Nim code running on Mac
|
||||
OS and toyed with DeRez. [The author of `mpu-emu` replied on Mastodon][Ninji]
|
||||
letting me know that DeRez _should_ run via `mpw-emu` on Linux as the
|
||||
filesystem layer transparently handles [MacBinary] files.
|
||||
|
||||
I spent some time in the debugger and worked out that `mpw-emu` supported
|
||||
MacBinary III but [Retro68] produced MacBinary II files. I contributed code
|
||||
to `mpw-emu` to add MacBinary II support and enabled some latent support for
|
||||
UNIX paths. After that DeRez did work (almost):
|
||||
|
||||
```
|
||||
$ mpw-emu ~/Documents/Classic\ Mac/Shared\ 2/DeRez.bin Root:home:wmoore:Projects:classic-mac-rust:cmake-build-retro68ppc:Dialog.bin
|
||||
[2023-03-31T04:51:34Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C
|
||||
[2023-03-31T04:51:34Z ERROR stdio] Unimplemented format character: P
|
||||
[2023-03-31T04:51:34Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4
|
||||
data 'DITL' (128) {
|
||||
$"0007 0000 0000 00A0 00E6 00B4 0136 0404" /* .......†.Ê.¥.6.. */
|
||||
$"5175 6974 0000 0000 009B 00E1 00B9 013B" /* Quit.....õ.·.π.; */
|
||||
$"0000 0000 0000 0046 000A 005A 0136 0818" /* .......F...Z.6.. */
|
||||
$"436F 6E76 6572 7369 6F6E 2070 6F77 6572" /* Conversion power */
|
||||
$"6564 2062 7920 5E30 0000 0000 001E 000A" /* ed by ^0........ */
|
||||
$"003E 002A A002 0080 0000 0000 0014 0032" /* .>.*†..Ä.......2 */
|
||||
$"0024 007D 8807 4365 6C73 6975 7300 0000" /* .$.}à.Celsius... */
|
||||
$"0000 0014 00AA 0024 00F5 8809 4661 7265" /* .....™.$.ıà∆Fare */
|
||||
$"6E68 6569 7400 0000 0000 0029 0036 0039" /* nheit......).6.9 */
|
||||
$"0081 1002 3235 0000 0000 002B 00AE 003B" /* .Å..25.....+.Æ.; */
|
||||
$"00F9 1002 3737" /* .˘..77 */
|
||||
};
|
||||
|
||||
/* etc */
|
||||
```
|
||||
|
||||
Success! DeRez running on Linux… only thing is that when you point at the type
|
||||
definitions to get structured output instead of hex dumps it hits an
|
||||
unimplemented function in `mpw-emu`. It's on my to-do list to fix that:
|
||||
|
||||
```
|
||||
$ mpw-emu ~/Documents/Classic\ Mac/Shared\ 2/DeRez.bin Root:home:wmoore:Projects:classic-mac-rust:cmake-build-retro68ppc:Dialog.bin Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces\&Libraries:Interfaces:RIncludes:Carbon.r
|
||||
[2023-03-31T04:53:07Z ERROR emulator] Unimplemented call to InterfaceLib::SetFScaleDisable @10012C6C
|
||||
[2023-03-31T04:53:07Z ERROR stdio] Unimplemented format character: P
|
||||
[2023-03-31T04:53:07Z ERROR emulator] Unimplemented call to InterfaceLib::SecondsToDate @1000B2A4
|
||||
[2023-03-31T04:53:07Z ERROR emulator] Unimplemented call to StdCLib::fseek @10006A8C
|
||||
File "Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces&Libraries:Interfaces:RIncludes:CoreServices.r"; Line 0; ### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - Can't FSeek on file Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces&Libraries:Interfaces:RIncludes:CoreServices.r.
|
||||
File "Root:home:wmoore:Source:github.com:autc04:Retro68:InterfacesAndLibraries:Interfaces&Libraries:Interfaces:RIncludes:CoreServices.r"; Line 0; ### /home/wmoore/Documents/Classic Mac/Shared 2/DeRez.bin - Fatal Error, can't recover.
|
||||
```
|
||||
|
||||
### MacBinary
|
||||
|
||||
Poking at the MacBinary code in `mpw-emu` got me wondering if there was already
|
||||
a MacBinary crate that could be used. Turns out there wasn't so I somehow
|
||||
nerd-sniped myself into [building one][macbinary-crate].
|
||||
|
||||
The first challenge was finding a decent specification for the three versions
|
||||
of MacBinary. I was eventually I was able to dig up the following:
|
||||
|
||||
- [MacBinary I]
|
||||
- [MacBinary II]
|
||||
- [MacBinary III]
|
||||
|
||||
I then set about building the parser. I reused the binary parser code from
|
||||
[Allsorts] since I was already familiar with that code. I hit another roadblock
|
||||
when it came to the CRC in the header. Nothing describes the actual CRC
|
||||
algorithm used. I tried the CRC reversing tool [CRC RevEng][RevEng] without
|
||||
success. A lot of existing code seemed to use an implementation that originated
|
||||
in a late 80's UNIX utility, [mcvert], that has unclear licensing. I wanted to
|
||||
use the Rust [crc crate] instead.
|
||||
|
||||
I eventually stumbled on the blog post,
|
||||
[Detecting MacBinary format](https://entropymine.wordpress.com/2019/02/13/detecting-macbinary-format/),
|
||||
which included the line:
|
||||
|
||||
> Note that the spec does not even tell you what CRC algorithm to use — you
|
||||
> have to be a detective to figure it out. (It’s the one sometimes called
|
||||
> CRC16-CCITT.)
|
||||
|
||||
That was the tip I needed and with a little trial an error I eventually worked
|
||||
out that it was [CRC-16/XMODEM] also known as `CRC-16/CCITT-FALSE`. In
|
||||
hindsight I could probably have worked this out from the discussion of XMODEM
|
||||
in the [MacBinary I spec][MacBinary I].
|
||||
|
||||
With that sorted I was able to wrap up the parser and do some testing. I could
|
||||
now read the resource and data forks and figured it would be interesting to be
|
||||
able to parse the resource data too, so I added a resource fork parser as well.
|
||||
|
||||
I wrote the parsers in a way that does not require heap allocation—only
|
||||
borrowing from the underlying data. Due to this it was straightforward to make
|
||||
the crate compatible with `no_std`, which allows it to be used in embedded
|
||||
environments and WebAssembly.
|
||||
|
||||
As something of a test-bed I created some
|
||||
WebAssembly bindings and built a page that allows you inspect MacBinary
|
||||
files online, with all parsing done client-side via the crate. You can
|
||||
try it out at: <https://7bit.org/macbinary/>
|
||||
|
||||
### Rust on Mac OS
|
||||
|
||||
Now that I was well and truly in the classic Mac space again I took another
|
||||
stab at compiling Rust for PPC Mac OS
|
||||
([see this post for my previous attempt](@/posts/2023/rust-on-ppc-classic-mac-os/index.md)).
|
||||
It seemed that using the
|
||||
`powerpc-ibm-aix` LLVM target was most likely to produce a compatible library
|
||||
(Apple used AIX conventions for PPC Mac OS). Problem was that it was hitting
|
||||
unimplemented code in LLVM:
|
||||
|
||||
> LLVM ERROR: relocation for paired relocatable term is not yet supported
|
||||
|
||||
I set about trying to work out how this code path was being hit and ran `rustc`
|
||||
in a debugger. Unsurprisingly there were no debug symbols so I built `rustc`
|
||||
and LLVM from source. This was my `config.toml` for the Rust repo:
|
||||
|
||||
```toml
|
||||
[llvm]
|
||||
release-debuginfo = true
|
||||
download-ci-llvm = false
|
||||
link-jobs = 4
|
||||
```
|
||||
|
||||
After repeatedly running out of disk space and memory compiling LLVM (the
|
||||
binaries with debug info are huge) I eventually had new Rust compiler.
|
||||
|
||||
Some of the LLVM binaries:
|
||||
|
||||
```
|
||||
.rwxr-xr-x 2.0G wmoore 26 Mar 20:05 llc
|
||||
.rwxr-xr-x 2.1G wmoore 26 Mar 20:10 llvm-opt-fuzzer
|
||||
.rwxr-xr-x 2.1G wmoore 26 Mar 20:04 bugpoint
|
||||
.rwxr-xr-x 2.2G wmoore 26 Mar 20:09 llvm-lto2
|
||||
.rwxr-xr-x 2.2G wmoore 26 Mar 20:06 llvm-lto
|
||||
.rwxr-xr-x 2.2G wmoore 26 Mar 20:11 opt
|
||||
.rwxr-xr-x 2.3G wmoore 26 Mar 20:11 llvm-reduce
|
||||
```
|
||||
|
||||
I linked the new compiler into `rustup` and then repeated my previous steps in
|
||||
the debugger… except this time the code compiled and did not hit the
|
||||
unimplemented LLVM code. This was my first lucky break. I'm not sure what
|
||||
changed but it was now happily compiling the code. I switched to a recent
|
||||
nightly compiler and that worked too! No need to build from source.
|
||||
|
||||
I repeated the step described in my original post of using
|
||||
`powerpc-linux-gnu-objcopy` to convert the static library archive (`.a`) to a
|
||||
format that Retro68 would accept. After some fighting with `binutils` I was
|
||||
finally able to get it to link!
|
||||
|
||||
<iframe src="https://mastodon.decentralised.social/@wezm/110098546361010915/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"></iframe><script src="https://mastodon.decentralised.social/embed.js" async="async"></script>
|
||||
|
||||
I rebuilt the temperature converter that I'd built in Nim in Rust ([source code][rust-temp]) and ran into
|
||||
more linker/`binutils` issues. After a _lot_ of trial-and-error and some more
|
||||
luck I was able to solve that by using the updated `binutils` on the
|
||||
`gcc12-update branch` branch of Retro68. I now had a working temperature
|
||||
converter:
|
||||
|
||||
{{ video(video="posts/2023/rust-classic-mac-os-app/classic-mac-rust-2023-03-28_20.00.31.mp4", height=480, poster="png", preload="auto", alt="Video of the temperature converter converting values to and from Celsius, running on Mac OS 8.1 (in emulator).", caption="The temperature converter application ported to Rust") }}
|
||||
|
||||
It worked on Mac OS 7.5 too:
|
||||
|
||||
{{ figure(image="posts/2023/rust-classic-mac-os-app/rust-on-mac-os-7.png", link="posts/2023/rust-classic-mac-os-app/rust-on-mac-os-7.png", pixelated=true, border=1, alt="Screenshot of the temperature converter application running on Mac OS 7.5 (in emulator).", caption="Temperature converter application running on Mac OS 7.5") }}
|
||||
|
||||
The Rust version is a bit more efficient than the [Nim version][nim-version] as
|
||||
it avoids some copying and heap allocation. That latter of which because I'm
|
||||
coding in a `no_std` environment without a heap.
|
||||
|
||||
The Rust standard library is divided into three main parts (crates):
|
||||
|
||||
1. `core` for things that do not require heap allocation, I/O, etc.
|
||||
2. `alloc` for things that use heap allocation but not I/O etc.
|
||||
3. `std`, the rest: files, networking, threads, etc. `std` re-exports the
|
||||
other two.
|
||||
|
||||
By defining a custom allocator that called `malloc` and `free` provided by the
|
||||
Retro68 environment I was able to use the `alloc` crate in addition to `core`.
|
||||
This gained me access `String`, `Vec`, and friends.
|
||||
|
||||
#### Networking
|
||||
|
||||
I now wanted to build something a little more involved than a single dialog. I
|
||||
set about building bindings to Open Transport, Apple's network stack introduced
|
||||
with PCI Power Macs (like my 9500).
|
||||
|
||||
Due to its heritage most of the Mac OS toolbox functions use the Pascal calling
|
||||
convention, [which LLVM does not support][llvm-pascal]. To bridge the C (and
|
||||
Rust) world to this Pascal world I had to create [trampoline functions] in C
|
||||
for each toolbox function that I wanted to call from Rust (if there's a better
|
||||
way to do this I'd love to know how). This works because `gcc` in Retro68
|
||||
understands both C and Pascal calling conventions. I appended an underscore to
|
||||
each of the wrapper functions. For example:
|
||||
|
||||
```c
|
||||
OSStatus OTConnect_(EndpointRef ref, TCall *sndCall, TCall *rcvCall) {
|
||||
return OTConnect(ref, sndCall, rcvCall);
|
||||
}
|
||||
```
|
||||
|
||||
I used the "Downloading a URL With HTTP" example from the [Networking With Open Transport]
|
||||
book as a guide for the functions I needed. Once the bindings were created I
|
||||
implemented the `TcpClientStack` trait from the [embedded-nal] \(embedded network abstraction layer)
|
||||
crate against Open Transport. Next I used this with the [http_io] crate to be able
|
||||
to make HTTP requests.
|
||||
|
||||
As an initial test I wrote an app to fetch a friend's website (since it's
|
||||
available over plain HTTP) and show an alert with the number of bytes read.
|
||||
Amazingly this worked on the first try: the Open Transport bindings, the
|
||||
`TcpClientStack` implementation, the HTTP client, and my test code all worked!
|
||||
|
||||
Finally I used my newfound networking abilities to build [Ferris
|
||||
Weather][ferris-weather], the application shown at the start of the post. This
|
||||
uses the HTTP client to fetch a JSON file containing weather observations,
|
||||
parses it with [serde] and then shows an alert with the most recent
|
||||
observation. I also drew a little 1-bit [Ferris the Rustacean][Ferris] in
|
||||
[ResEdit] for it.
|
||||
|
||||
The idea for this was prompted by the [Australian Bureau of Meteorology][bom]
|
||||
still being accessible over HTTP. Unfortunately it wasn't working and after a
|
||||
lot of debugging I eventually discovered that I triggering their anti-scraping
|
||||
blocker for some reason. To work around this I copied a snapshot of the JSON to
|
||||
my own server. So, unfortunately the data shown by the application does not
|
||||
update but you still get the idea.
|
||||
|
||||
{{ figure(image="posts/2023/rust-classic-mac-os-app/Ferris%20Weather.png", link="posts/2023/rust-classic-mac-os-app/Ferris%20Weather.png", pixelated=true, border=1, alt="Screenshot of the Ferris Weather application showing an alert with the temperature in Brisbane.", caption="Ferris Weather") }}
|
||||
|
||||
So there you have it, that's how I built an application in Rust (and some C)
|
||||
for classic Mac OS. The [source code to Ferris Weather][ferris-weather] is on
|
||||
GitHub.
|
||||
|
||||
### Next
|
||||
|
||||
My intention is to take a bit of a break from classic Mac OS for a bit and work
|
||||
on some other projects—ones that might be useful to people in this century—but
|
||||
there are some things I want to look at when I come back to it:
|
||||
|
||||
First is TLS support for the HTTP client. I think this should be relatively
|
||||
straightforward with the [embedded-tls] crate.
|
||||
|
||||
Next I'd like to improve how Open Transport is used. I think with either
|
||||
the synchronous, non-blocking mode I'm using now or the asynchronous mode it
|
||||
should be possible to tie it into the async Rust ecosystem, which would allow
|
||||
it to play nicer with the event loop and cooperative multi-tasking.
|
||||
|
||||
Finally, so far I've been working without the full Rust standard library, only
|
||||
`core` and `alloc`. It seems like it should be possible to implement a lot of
|
||||
the remaining standard library (io, networking), on top of the Mac OS toolbox,
|
||||
but that's a lot of work and will have to wait for another time.
|
||||
|
||||
### Hire Me
|
||||
|
||||
As mentioned at the start of this post I'm currently taking a break from
|
||||
employment but I will be looking for a new role next month, so if you're looking
|
||||
for a Rust developer get in touch.
|
||||
|
||||
[#MARCHintosh]: https://www.marchintosh.com/
|
||||
[Ninji]: https://vulpine.club/@Ninji/110053455721324087
|
||||
[mcvert]: https://web.mit.edu/~mkgray/jik/sipbsrc/src/mcvert/mcvert.c
|
||||
[Networking With Open Transport]: https://developer.apple.com/library/archive/documentation/mac/NetworkingOT/NetworkingOpenTransport.pdf
|
||||
[ferris-weather]: https://github.com/wezm/ferris-weather
|
||||
[Retro68]: https://github.com/autc04/Retro68
|
||||
[MacBinary]: https://en.wikipedia.org/wiki/MacBinary
|
||||
[macbinary-crate]: https://lib.rs/crates/macbinary
|
||||
[Allsorts]: https://github.com/yeslogic/allsorts
|
||||
[RevEng]: https://reveng.sourceforge.io/
|
||||
[crc crate]: https://lib.rs/crates/crc
|
||||
[CRC-16/XMODEM]: https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-ibm-3740
|
||||
[MacBinary I]: https://web.archive.org/web/20050307030202/http://www.lazerware.com/formats/macbinary/macbinary.html
|
||||
[MacBinary II]: https://web.archive.org/web/20050305042909/http://www.lazerware.com/formats/macbinary/macbinary_ii.html
|
||||
[MacBinary III]: https://web.archive.org/web/20050305044255/http://www.lazerware.com/formats/macbinary/macbinary_iii.html
|
||||
[llvm-pascal]: https://github.com/llvm/llvm-project/blob/bd20a344bbf813b2c39b57ad1a5248bff915ce25/clang/lib/CodeGen/CGCall.cpp#L60
|
||||
[trampoline functions]: https://en.wikipedia.org/wiki/Trampoline_(computing)
|
||||
[embedded-nal]: https://docs.rs/embedded-nal/latest/embedded_nal/
|
||||
[http_io]: https://lib.rs/crates/http_io
|
||||
[serde]: https://serde.rs/
|
||||
[bom]: http://www.bom.gov.au/
|
||||
[Ferris]: https://rustacean.net/
|
||||
[ResEdit]: https://en.wikipedia.org/wiki/ResEdit
|
||||
[embedded-tls]: https://lib.rs/crates/embedded-tls
|
||||
[rust-temp]: https://github.com/wezm/classic-mac-rust
|
||||
[nim-version]: https://github.com/wezm/classic-mac-nim
|
After Width: | Height: | Size: 6.7 KiB |
|
@ -2,8 +2,8 @@
|
|||
title = "Trying to Run Rust on Classic Mac OS"
|
||||
date = 2023-02-27T10:06:28+10:00
|
||||
|
||||
#[extra]
|
||||
#updated = 2023-01-11T21:11:28+10:00
|
||||
[extra]
|
||||
updated = 2023-03-26T14:27:05+10:00
|
||||
+++
|
||||
|
||||
I recently acquired a Power Macintosh 9500/150 and after cleaning it up and
|
||||
|
@ -366,7 +366,7 @@ my `.a`, and it worked:
|
|||
```
|
||||
docker run --rm -it -v $(pwd):/src debian:testing
|
||||
apt update
|
||||
apt install powerpc-linux-gnu-binutils
|
||||
apt install binutils-powerpc-linux-gnu
|
||||
powerpc-linux-gnu-objcopy -O aixcoff-rs6000 /src/target/powerpc-apple-macos/release/libclassic_mac_rust.a /src/target/powerpc-apple-macos/release/libclassic_mac_rust.obj
|
||||
```
|
||||
|
||||
|
|
|
@ -183,6 +183,9 @@ figure {
|
|||
.figure-border img {
|
||||
border: 1px solid #AAA;
|
||||
}
|
||||
.figure-pixelated img {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
figcaption {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
|
@ -387,6 +390,9 @@ ul.projects {
|
|||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.mastodon-embed {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.tagline {
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
{% set figure_class = ["text-center"] %}
|
||||
{% if border %}
|
||||
<figure class="text-center figure-border">
|
||||
{% else %}
|
||||
<figure class="text-center">
|
||||
{% set figure_class = figure_class | concat(with="figure-border") %}
|
||||
{% endif %}
|
||||
{% if pixelated %}
|
||||
{% set figure_class = figure_class | concat(with="figure-pixelated") %}
|
||||
{% endif %}
|
||||
<figure class="{{ figure_class | join(sep=" ") }}">
|
||||
<a href="{{ config.base_url }}/{{ link }}">
|
||||
{% if resize_width %}
|
||||
{% set image = resize_image(path=image, width=resize_width, op="fit_width", quality=quality | default(value=75)) %}
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
{% if poster %}
|
||||
{% else %}
|
||||
{% set poster = "jpg" %}
|
||||
{% endif %}
|
||||
{% if preload %}
|
||||
{% else %}
|
||||
{% set preload = "none" %}
|
||||
{% endif %}
|
||||
|
||||
<figure class="text-center">
|
||||
<video controls preload="none" src="{{ config.base_url }}/{{ video }}" poster="{{ config.base_url }}/{{ video }}.jpg" style="max-height: {{ height }}px"></video>
|
||||
<video controls preload="{{ preload }}" src="{{ config.base_url }}/{{ video }}" poster="{{ config.base_url }}/{{ video }}.{{ poster }}" style="max-height: {{ height }}px" aria-label="{{ alt }}"></video>
|
||||
<figcaption>{{ caption }}</figcaption>
|
||||
</figure>
|
||||
|
|