News:

Want an XLerator? Please participate in the market research thread: https://lisalist2.com/index.php/topic,594.msg4180.html

Main Menu

A Lisa Inside An FPGA

Started by AlexTheCat123, September 04, 2025, 05:20:35 PM

Previous topic - Next topic

AlexTheCat123

I'm happy to announce that the ESFloppy repo is now public and the emulator is fully-functional for both Sony and Twiggy drives! I haven't updated the LisaFPGA readme to reflect this, and program_board.sh doesn't program your board with the new code yet, but you can install the firmware manually using the Arduino IDE if you want to try it out right now. I'm hoping to have the program_board script updated by tonight or tomorrow, along with the Twiggy fix.

https://github.com/alexthecat123/ESFloppy/

AlexTheCat123

Okay, program_board.sh is updated with the new ESFloppy firmware, so clone the latest version of the LisaFPGA repo, connect your LisaFPGA to your computer, run the script, and full floppy emulator functionality will be added to your board!

I still need to do the Twiggy fix that @sigma7 discovered, but that's taking a bit longer than expected because one of the fans in my Linux laptop that I run Vivado on completely killed itself and sounds like a jet engine. It's been a little weird for a while, but it just got 100x worse...

ried

#302
Quote from: AlexTheCat123 on August 18, 2026, 05:47:35 PMOkay, program_board.sh is updated with the new ESFloppy firmware, so clone the latest version of the LisaFPGA repo, connect your LisaFPGA to your computer, run the script, and full floppy emulator functionality will be added to your board!

I jumped in an all went well until:

══ Discover ESP32 Serial Ports ══
[FAIL]  Failed to find serial port for ESProFile (hub port 2)!
justin@MacBookPro LisaFPGA-main %

Eep! What did I do?  The built-in ESProFile still works fine (boots the Lisa normally) and its ACT LED lights up and remains lit. Hmmm...

P.S. https://www.ebay.com/itm/278293940405

 :-X

AlexTheCat123

Quote from: ried on August 18, 2026, 09:45:04 PMEep! What did I do?  The built-in ESProFile still works fine (boots the Lisa normally) and its ACT LED lights up and remains lit. Hmmm...

Try pressing and holding the RESET and BOOT buttons for ESProFile simultaneously, and then releasing RESET, followed by BOOT a second or two later. And then right after you do that, run the script again and see if you have better luck. If ESFloppy fails the same way, try the same trick with it.

Quote from: ried on August 18, 2026, 09:45:04 PMP.S. https://www.ebay.com/itm/278293940405

 :-X

Wow, that's expensive! I wonder if anyone will be willing to pay that much?

ried

#304
Quote from: AlexTheCat123 on August 19, 2026, 12:18:11 AMTry pressing and holding the RESET and BOOT buttons for ESProFile simultaneously, and then releasing RESET, followed by BOOT a second or two later. And then right after you do that, run the script again and see if you have better luck.

First, thank you for the lightning fast reply. Unfortunately, however, no change here. Still fails at that step. Trying a backup laptop just in case.

Edit: My old backup machines are Intel-based and while they installed Xcode properly when prompted, the remaining arguments and utilities called by the script did not install. They're just too old, I'm afraid (Big Sur 11.7.11 and Monterey 12.7.6).

Back to my Apple Silicon MBP, same result after trying different laptop ports and USB cables. Hmmmm.

Lisa2

Quote from: AlexTheCat123 on August 19, 2026, 12:18:11 AM
Quote from: ried on August 18, 2026, 09:45:04 PMP.S. https://www.ebay.com/itm/278293940405

 :-X

Wow, that's expensive! I wonder if anyone will be willing to pay that much?

Sold!

In my opinion these should be trading in the $1500 range..  ;) 

slewis1962

I'm still failing to install the new firmware on my board. On Debian I installed CURL then I had to install Arduino-CLI. After that it still couldn't find Arduino-CLI. I gave up on Debian and installed Ubuntu on a second partition. Same failures as Debian although after installing CURL and Arduino-CLI it was able to get a lot further before failing. Don't remember what failed but then I noticed you updated again with the new floppy firmware. Downloaded that and now this is the error I get:
scott@scott-ThinkPad-T520:~/Downloads/LisaFPGA-main$ sudo ./program_board.sh
[INFO]  Platform: linux

══ Checking / Installing Dependencies ══
[ OK ]  git: git version 2.17.1
[ OK ]  pyusb: 1.0.0
[ OK ]  ftdi_eeprom: v0.17
[ OK ]  arduino-cli: arduino-cli  Version: 1.5.1 Commit: 01f3d4f2b Date: 2026-06-05T10:22:17Z
[ OK ]  ESP32 Arduino core: esp32:esp32 3.3.11    3.3.11 esp32
[ OK ]  SdFat: SdFat 2.3.0
[ OK ]  U8g2: U8g2 2.36.19
[INFO]  Installing openfpgaloader...
Reading package lists... Done
Building dependency tree       
Reading state information... Done
[INFO]  Building openFPGALoader from source...
Cloning into '/tmp/tmp.TbY2rt7ha4'...
remote: Enumerating objects: 299, done.
remote: Counting objects: 100% (299/299), done.
remote: Compressing objects: 100% (257/257), done.
remote: Total 299 (delta 54), reused 167 (delta 36), pack-reused 0 (from 0)
Receiving objects: 100% (299/299), 3.92 MiB | 15.55 MiB/s, done.
Resolving deltas: 100% (54/54), done.
CMake Error: The source directory "/tmp/tmp.TbY2rt7ha4/build" does not exist.
Specify --help for usage, or press the help button on the CMake GUI.
scott@scott-ThinkPad-T520:~/Downloads/LisaFPGA-main$

I googled the error and it told me to create that directory. Simple enough. Ran again and it chose another directory to claim it didn't exist. Do I just keep creating directories manually or is there something I'm doing wrong earlier in the process to cause those errors?
Also, I haven't tried to connect via terminal on my MacBook pro yet. Any tricks to that?

slewis1962

Is this a simple case of me not having the right operating systems or computer hardware? Or is it I don't have the right applications installed as in the failure with CURL and Arduino-CLI that I had to manually install?

AlexTheCat123

Quote from: slewis1962 on August 19, 2026, 01:32:20 PMI'm still failing to install the new firmware on my board. On Debian I installed CURL then I had to install Arduino-CLI. After that it still couldn't find Arduino-CLI. I gave up on Debian and installed Ubuntu on a second partition. Same failures as Debian although after installing CURL and Arduino-CLI it was able to get a lot further before failing. Don't remember what failed but then I noticed you updated again with the new floppy firmware. Downloaded that and now this is the error I get:
scott@scott-ThinkPad-T520:~/Downloads/LisaFPGA-main$ sudo ./program_board.sh
[INFO]  Platform: linux

══ Checking / Installing Dependencies ══
[ OK ]  git: git version 2.17.1
[ OK ]  pyusb: 1.0.0
[ OK ]  ftdi_eeprom: v0.17
[ OK ]  arduino-cli: arduino-cli  Version: 1.5.1 Commit: 01f3d4f2b Date: 2026-06-05T10:22:17Z
[ OK ]  ESP32 Arduino core: esp32:esp32 3.3.11    3.3.11 esp32
[ OK ]  SdFat: SdFat 2.3.0
[ OK ]  U8g2: U8g2 2.36.19
[INFO]  Installing openfpgaloader...
Reading package lists... Done
Building dependency tree       
Reading state information... Done
[INFO]  Building openFPGALoader from source...
Cloning into '/tmp/tmp.TbY2rt7ha4'...
remote: Enumerating objects: 299, done.
remote: Counting objects: 100% (299/299), done.
remote: Compressing objects: 100% (257/257), done.
remote: Total 299 (delta 54), reused 167 (delta 36), pack-reused 0 (from 0)
Receiving objects: 100% (299/299), 3.92 MiB | 15.55 MiB/s, done.
Resolving deltas: 100% (54/54), done.
CMake Error: The source directory "/tmp/tmp.TbY2rt7ha4/build" does not exist.
Specify --help for usage, or press the help button on the CMake GUI.
scott@scott-ThinkPad-T520:~/Downloads/LisaFPGA-main$

I googled the error and it told me to create that directory. Simple enough. Ran again and it chose another directory to claim it didn't exist. Do I just keep creating directories manually or is there something I'm doing wrong earlier in the process to cause those errors?
Also, I haven't tried to connect via terminal on my MacBook pro yet. Any tricks to that?

On my Linux install, cmake automatically makes the build directory for you when you run it. But perhaps that doesn't happen on your distro for whatever reason. And I bet the reason why manually creating it and then rerunning doesn't work is because it chooses another temp directory the next time around, not the old one that had the build directory in it.

Try adding the line mkdir "$TMP_OFL/build" in between the git clone --depth 1 https://github.com/trabucayre/openFPGALoader.git "$TMP_OFL" and the cmake -S "$TMP_OFL" -B "$TMP_OFL/build" -DCMAKE_BUILD_TYPE=Release in the script. If that helps, then I'll add it to the official version of the script.

Quote from: slewis1962 on August 19, 2026, 01:32:20 PMAlso, I haven't tried to connect via terminal on my MacBook pro yet. Any tricks to that?

No, not really. Just make sure the SERIAL B SOURCE switch is set to USB and make sure that whatever OS you're running on the Lisa is properly configured for serial communications. And that that point it should pretty much just work.

Quote from: ried on August 19, 2026, 12:29:04 AMFirst, thank you for the lightning fast reply. Unfortunately, however, no change here. Still fails at that step. Trying a backup laptop just in case.

Can you try running the command system_profiler SPUSBDataType in your terminal (with the board connected and turned on) and sharing the results? We can check to make sure that your computer is seeing the ESP32 devices properly.

Quote from: Lisa2 on August 19, 2026, 10:27:49 AMSold!

In my opinion these should be trading in the $1500 range..  ;)

Shows how little I know about pricing things!

ried

Quote from: AlexTheCat123 on August 19, 2026, 02:10:12 PMCan you try running the command system_profiler SPUSBDataType in your terminal (with the board connected and turned on) and sharing the results?

Emailed you the results.

coffeemuse

#310
Quote from: ried on August 18, 2026, 09:45:04 PMI jumped in an all went well until:

══ Discover ESP32 Serial Ports ══
[FAIL]  Failed to find serial port for ESProFile (hub port 2)!
justin@MacBookPro LisaFPGA-main %

Eep! What did I do?  The built-in ESProFile still works fine (boots the Lisa normally) and its ACT LED lights up and remains lit. Hmmm...

P.S. https://www.ebay.com/itm/278293940405

I am having the same issue on macOS Tahoe 26.5.2. 

MacStudio LisaFPGA % system_profiler SPUSBDataType -json
{
  "SPUSBDataType" : [

  ]
}

It looks like system_profiler is not returning results to locate the devices.  I was able to work around it using ioreg to enumerate the devices.  After making changes listed in the following patch, I successfully programmed my LisaFPGA under macOS.

diff --git a/program_board.sh b/program_board.sh
index b1cfc4c..61840b2 100755
--- a/program_board.sh
+++ b/program_board.sh
@@ -310,69 +310,64 @@ PYEOF
 
 # ── macOS: find /dev/cu.* for ESP32 at hub port N ────────────────────────────
 # We use locationID: each nibble encodes a hub port level.
+# Reads the USB tree from ioreg instead of system_profiler, because
+# SPUSBDataType sometimes returns an empty list even while every device
+# is enumerated and visible in ioreg.
 macos_tty_for_hub_port() {
     local port="$1"   # 2 or 3
     python3 - "$HUB_VID" "$HUB_PID" "$port" <<'PYEOF'
-import subprocess, sys, re, json
+import subprocess, sys, plistlib
 
-hub_vid_str = "0x" + sys.argv[1].upper()
-hub_pid_str = "0x" + sys.argv[2].upper()
+hub_vid = int(sys.argv[1], 16)
+hub_pid = int(sys.argv[2], 16)
 target_port = int(sys.argv[3])
 
 try:
     raw = subprocess.check_output(
-        ['system_profiler', 'SPUSBDataType', '-json'],
-        text=True, stderr=subprocess.DEVNULL
+        ['ioreg', '-a', '-r', '-c', 'IOUSBHostDevice', '-l'],
+        stderr=subprocess.DEVNULL
     )
-    data = json.loads(raw)
+    roots = plistlib.loads(raw)
 except Exception:
     sys.exit(1)
 
-def hub_port_from_location(hub_loc_str, child_loc_str):
-    """Derive the physical hub port from location IDs.
-    macOS encodes each port level as a nibble: hub at 0x01100000 has
-    children at 0x0111xxxx (port 1), 0x0112xxxx (port 2), etc.
-    system_profiler does NOT list _items in port order, so we must
-    compute the port from the location_id rather than using the array index."""
-    try:
-        hub_loc   = int(hub_loc_str.split()[0], 16)
-        child_loc = int(child_loc_str.split()[0], 16)
-        trailing  = (hub_loc & -hub_loc).bit_length() - 1  # trailing zero bits
-        return (child_loc >> (trailing - 4)) & 0xF
-    except Exception:
-        return -1
-
-def scan(items):
-    for item in items:
-        vid = item.get('vendor_id', '').upper().replace('0X', '0x')
-        pid = item.get('product_id', '').upper().replace('0X', '0x')
-        if hub_vid_str in vid and hub_pid_str in pid:
-            hub_loc_str = item.get('location_id', '')
-            for child in item.get('_items', []):
-                child_loc_str = child.get('location_id', '')
-                if hub_port_from_location(hub_loc_str, child_loc_str) != target_port:
-                    continue
-                child_serial = child.get('serial_num', '')
-                if child_serial:
-                    try:
-                        out = subprocess.check_output(
-                            ['ioreg', '-r', '-c', 'IOUSBHostDevice', '-l'],
-                            text=True, stderr=subprocess.DEVNULL
-                        )
-                        m = re.search(
-                            r'"IOCalloutDevice"\s*=\s*"(/dev/[^"]+)"',
-                            out[out.find(child_serial):]
-                        )
-                        if m:
-                            print(m.group(1))
-                            sys.exit(0)
-                    except Exception:
-                        pass
-        sub = item.get('_items', [])
-        if sub:
-            scan(sub)
-
-scan(data.get('SPUSBDataType', []))
+def walk(node):
+    yield node
+    for child in node.get('IORegistryEntryChildren', []):
+        yield from walk(child)
+
+def port_nibble_shift(hub_loc):
+    """macOS locationIDs encode one hub level per nibble. A child on
+    port N of a hub at 0x08320000 sits at 0x0832N000. The port nibble
+    is the one just below the hub's lowest nonzero nibble. Working in
+    whole nibbles (not trailing zero bits) keeps the math right when
+    the hub's own port number is even."""
+    shift = 0
+    while shift < 32 and ((hub_loc >> shift) & 0xF) == 0:
+        shift += 4
+    return shift - 4
+
+for root in roots:
+    for hub in walk(root):
+        if hub.get('idVendor') != hub_vid or hub.get('idProduct') != hub_pid:
+            continue
+        hub_loc = hub.get('locationID')
+        if hub_loc is None:
+            continue
+        shift = port_nibble_shift(hub_loc)
+        if shift < 0:
+            continue
+        want_loc = hub_loc | (target_port << shift)
+        for child in walk(hub):
+            if child is hub or child.get('locationID') != want_loc:
+                continue
+            if 'idVendor' not in child:
+                continue
+            for sub in walk(child):
+                dev = sub.get('IOCalloutDevice')
+                if dev:
+                    print(dev)
+                    sys.exit(0)
 sys.exit(1)
 PYEOF
 }




If opened PR #2 with my changes if it helps.
Former Apple Lisa 2/5 owner.
Current Retro Apple Gear:  LisaFPGA owner; LisaEm; Macintosh Color Classic; BlueSCSI 2 External w/ WiFi; BlueSCSI 2 internal; FloppyEmu; USB Wombat; WiRSa v3 Wifi RS232 Modem; 3x M0100 mice; Apple MessagePad 2100

slewis1962

#311
Quick question Alex, what Linux Distro are you running? My Thinkpad T520 is running an i7 with 4GB RAM so I'm not running the latest Debian or Ubuntu. I thought I'd install a VM in my M4 Apple Silicon Mac and have better luck. Should the script complete properly if I'm running the latest version of Linux? I'll have to try tomorrow as I'm currently at work. If none of that works I'll try some of the other suggestions. I'll probably try them anyway as I'm curious. I suppose I should give up trying to compile on my old Thinkpad.

AlexTheCat123

Quote from: coffeemuse on August 19, 2026, 03:02:37 PM
Quote from: ried on August 18, 2026, 09:45:04 PMI jumped in an all went well until:

══ Discover ESP32 Serial Ports ══
[FAIL]  Failed to find serial port for ESProFile (hub port 2)!
justin@MacBookPro LisaFPGA-main %

Eep! What did I do?  The built-in ESProFile still works fine (boots the Lisa normally) and its ACT LED lights up and remains lit. Hmmm...

P.S. https://www.ebay.com/itm/278293940405

I am having the same issue on macOS Tahoe 26.5.2. 

MacStudio LisaFPGA % system_profiler SPUSBDataType -json
{
  "SPUSBDataType" : [

  ]
}

It looks like system_profiler is not returning results to locate the devices.  I was able to work around it using ioreg to enumerate the devices.  After making changes listed in the following patch, I successfully programmed my LisaFPGA under macOS.

diff --git a/program_board.sh b/program_board.sh
index b1cfc4c..61840b2 100755
--- a/program_board.sh
+++ b/program_board.sh
@@ -310,69 +310,64 @@ PYEOF
 
 # ── macOS: find /dev/cu.* for ESP32 at hub port N ────────────────────────────
 # We use locationID: each nibble encodes a hub port level.
+# Reads the USB tree from ioreg instead of system_profiler, because
+# SPUSBDataType sometimes returns an empty list even while every device
+# is enumerated and visible in ioreg.
 macos_tty_for_hub_port() {
     local port="$1"   # 2 or 3
     python3 - "$HUB_VID" "$HUB_PID" "$port" <<'PYEOF'
-import subprocess, sys, re, json
+import subprocess, sys, plistlib
 
-hub_vid_str = "0x" + sys.argv[1].upper()
-hub_pid_str = "0x" + sys.argv[2].upper()
+hub_vid = int(sys.argv[1], 16)
+hub_pid = int(sys.argv[2], 16)
 target_port = int(sys.argv[3])
 
 try:
     raw = subprocess.check_output(
-        ['system_profiler', 'SPUSBDataType', '-json'],
-        text=True, stderr=subprocess.DEVNULL
+        ['ioreg', '-a', '-r', '-c', 'IOUSBHostDevice', '-l'],
+        stderr=subprocess.DEVNULL
     )
-    data = json.loads(raw)
+    roots = plistlib.loads(raw)
 except Exception:
     sys.exit(1)
 
-def hub_port_from_location(hub_loc_str, child_loc_str):
-    """Derive the physical hub port from location IDs.
-    macOS encodes each port level as a nibble: hub at 0x01100000 has
-    children at 0x0111xxxx (port 1), 0x0112xxxx (port 2), etc.
-    system_profiler does NOT list _items in port order, so we must
-    compute the port from the location_id rather than using the array index."""
-    try:
-        hub_loc   = int(hub_loc_str.split()[0], 16)
-        child_loc = int(child_loc_str.split()[0], 16)
-        trailing  = (hub_loc & -hub_loc).bit_length() - 1  # trailing zero bits
-        return (child_loc >> (trailing - 4)) & 0xF
-    except Exception:
-        return -1
-
-def scan(items):
-    for item in items:
-        vid = item.get('vendor_id', '').upper().replace('0X', '0x')
-        pid = item.get('product_id', '').upper().replace('0X', '0x')
-        if hub_vid_str in vid and hub_pid_str in pid:
-            hub_loc_str = item.get('location_id', '')
-            for child in item.get('_items', []):
-                child_loc_str = child.get('location_id', '')
-                if hub_port_from_location(hub_loc_str, child_loc_str) != target_port:
-                    continue
-                child_serial = child.get('serial_num', '')
-                if child_serial:
-                    try:
-                        out = subprocess.check_output(
-                            ['ioreg', '-r', '-c', 'IOUSBHostDevice', '-l'],
-                            text=True, stderr=subprocess.DEVNULL
-                        )
-                        m = re.search(
-                            r'"IOCalloutDevice"\s*=\s*"(/dev/[^"]+)"',
-                            out[out.find(child_serial):]
-                        )
-                        if m:
-                            print(m.group(1))
-                            sys.exit(0)
-                    except Exception:
-                        pass
-        sub = item.get('_items', [])
-        if sub:
-            scan(sub)
-
-scan(data.get('SPUSBDataType', []))
+def walk(node):
+    yield node
+    for child in node.get('IORegistryEntryChildren', []):
+        yield from walk(child)
+
+def port_nibble_shift(hub_loc):
+    """macOS locationIDs encode one hub level per nibble. A child on
+    port N of a hub at 0x08320000 sits at 0x0832N000. The port nibble
+    is the one just below the hub's lowest nonzero nibble. Working in
+    whole nibbles (not trailing zero bits) keeps the math right when
+    the hub's own port number is even."""
+    shift = 0
+    while shift < 32 and ((hub_loc >> shift) & 0xF) == 0:
+        shift += 4
+    return shift - 4
+
+for root in roots:
+    for hub in walk(root):
+        if hub.get('idVendor') != hub_vid or hub.get('idProduct') != hub_pid:
+            continue
+        hub_loc = hub.get('locationID')
+        if hub_loc is None:
+            continue
+        shift = port_nibble_shift(hub_loc)
+        if shift < 0:
+            continue
+        want_loc = hub_loc | (target_port << shift)
+        for child in walk(hub):
+            if child is hub or child.get('locationID') != want_loc:
+                continue
+            if 'idVendor' not in child:
+                continue
+            for sub in walk(child):
+                dev = sub.get('IOCalloutDevice')
+                if dev:
+                    print(dev)
+                    sys.exit(0)
 sys.exit(1)
 PYEOF
 }




If opened PR #2 with my changes if it helps.

Yeah, I think I can explain why that's the case! I'm still running Sequoia and never tested with Tahoe, so I bet they changed how system_profiler returns the results in between the two. I'll test your code here in a few minutes. Let's just hope that this fix doesn't break things for me on Sequoia. I wanted to hold out for as long as possible, but perhaps this is my cue to upgrade...

Quote from: slewis1962 on August 19, 2026, 04:25:20 PMQuick question Alex, what Linux Distro are you running? My Thinkpad T520 is running an i7 with 4GB RAM so I'm not running the latest Debian or Ubuntu. I thought I'd install a VM in my M4 Apple Silicon Mac and have better luck. Should the script complete properly if I'm running the latest version of Linux? I'll have to try tomorrow as I'm currently at work. If none of that works I'll try some of the other suggestions. I'll probably try them anyway as I'm curious. I suppose I should give up trying to compile on my old Thinkpad.

I'm running Linux Mint 22 on actual x86 hardware (no VM). Mint should be pretty much identical to Ubuntu, so either of those should work fine. Hopefully we can get the Mac problems solved and you won't even need to worry about this though!

AlexTheCat123

@coffeemuse's modified script seems to work great, so I went ahead and pushed it. Hopefully that solves everyone's problems; give it a try and let me know. I also added the line that we talked about earlier that might fix the openFPGALoader build problems on Linux, but this of course hasn't been tested yet. It can't hurt to be added though, so might as well.

ried

#314
Works for me now, woohoo! Thanks gang.

Edit: Trying out the new ESFloppy now. Is it intentional that it remains powered on by the current coming from the attached HDMI display? If I pull the LisaFPGA's USB-C power cable, the ESFloppy remains on unless I also remove the power cable from the attached monitor.

Edit 2: This is wild. Twiggy emulation totally works! I used Selector to create a new 5MB drive image and am now installing LOS 2.0 from the Twiggy disk images using the ESFloppy. Super cool.

Edit 3: The second Twiggy disk threw an error during the installation process ("The Lisa Office System startup software could not be installed because of difficulties reading the master diskette. Try running the disk drive diagnostic or using another Office System diskette. Refer to the Lisa Owner's Guide, Section G, Troubleshooting, under LisaTest."). The disk image should be fine, as its physical source disk worked just fine to install LOS 2.0 on a real Lisa 1. Hmmm.

Edit 4: The issue was resolved after rebooting the LisaFPGA. I wonder if this is an issue with the microSD card? The one in the ESFloppy is a SanDisk card but it's over 10 years old, so that may have been the cause. LOS 2.0 is now up and running after the Twiggy installation. ESProFile image attached in case it's helpful to someone (no apps installed yet).