├── .gitignore ├── rom ├── CMakeLists.txt └── se30 │ ├── declrom_macros.inc │ ├── README.md │ ├── CMakeLists.txt │ ├── declrom.S │ └── gencrc.py ├── pld ├── se │ ├── CMakeLists.txt │ ├── README.md │ └── se-u2.pld ├── se30 │ ├── CMakeLists.txt │ ├── README.md │ └── se30-u3.pld ├── .gitignore ├── README.md └── CMakeLists.txt ├── boards ├── se │ ├── se.pdf │ ├── fp-lib-table │ └── sym-lib-table ├── se30 │ ├── se30.pdf │ ├── fp-lib-table │ └── sym-lib-table ├── breakout │ ├── breakout.pdf │ └── fp-lib-table ├── .gitignore └── shared │ └── PDS-Ethernet.pretty │ ├── Wurth_9774025360R.kicad_mod │ └── Kycon-GDTX-S-88_Vertical.kicad_mod ├── software ├── tools │ ├── showDrivers │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── showDrivers.c │ ├── CMakeLists.txt │ ├── testMemory │ │ ├── CMakeLists.txt │ │ └── testMemory.c │ └── programROM │ │ ├── CMakeLists.txt │ │ ├── flash.h │ │ ├── flash.c │ │ └── programROM.c ├── shared │ ├── memtest │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── include │ │ │ └── memtest.h │ │ └── memtest.c │ ├── board_defs │ │ ├── CMakeLists.txt │ │ └── include │ │ │ ├── sethernet_board_defs.h │ │ │ └── sethernet30_board_defs.h │ ├── buserror │ │ ├── CMakeLists.txt │ │ ├── include │ │ │ └── buserror.h │ │ └── buserror.S │ ├── CMakeLists.txt │ ├── enc624j600 │ │ ├── CMakeLists.txt │ │ └── include │ │ │ └── enc624j600.h │ └── version │ │ └── CMakeLists.txt ├── driver │ ├── readpacket.h │ ├── SEthernet.r │ ├── multicast.h │ ├── protocolhandler.h │ ├── SEthernet30.r │ ├── isr.h │ ├── macsbug.inc │ ├── README.md │ ├── util.h │ ├── util.c │ ├── isrwrapper.S │ ├── CMakeLists.txt │ ├── include │ │ └── sethernet.h │ ├── multicast.c │ ├── driver.h │ ├── protocolhandler.c │ ├── header.S │ └── readpacket.S └── CMakeLists.txt ├── installer ├── images │ ├── buildenv.dsk.gz │ └── template.dsk.gz ├── UserStartup.mpw ├── text2mac.py ├── Build.mpw ├── README.md ├── comments.r ├── filespecs.r ├── build.sh ├── identifiers.r ├── packages.r └── misc.r ├── .gitattributes ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ -------------------------------------------------------------------------------- /rom/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(se30) 2 | -------------------------------------------------------------------------------- /pld/se/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_cpld(se-u2.pld p1502t44) 2 | -------------------------------------------------------------------------------- /pld/se30/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_cpld(se30-u3.pld p1502t44) 2 | -------------------------------------------------------------------------------- /boards/se/se.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhalkyard/SEthernet/HEAD/boards/se/se.pdf -------------------------------------------------------------------------------- /boards/se30/se30.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhalkyard/SEthernet/HEAD/boards/se30/se30.pdf -------------------------------------------------------------------------------- /software/tools/showDrivers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_application("showDrivers" CONSOLE showDrivers.c) 2 | -------------------------------------------------------------------------------- /boards/breakout/breakout.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhalkyard/SEthernet/HEAD/boards/breakout/breakout.pdf -------------------------------------------------------------------------------- /installer/images/buildenv.dsk.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhalkyard/SEthernet/HEAD/installer/images/buildenv.dsk.gz -------------------------------------------------------------------------------- /installer/images/template.dsk.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhalkyard/SEthernet/HEAD/installer/images/template.dsk.gz -------------------------------------------------------------------------------- /software/tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(showDrivers) 2 | add_subdirectory(testMemory) 3 | add_subdirectory(programROM) -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.bin binary 4 | *.pdf binary 5 | 6 | *.bat text eol=crlf 7 | *.pld text eol=crlf 8 | -------------------------------------------------------------------------------- /software/shared/memtest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(memtest STATIC memtest.c) 2 | target_include_directories(memtest PUBLIC include/) 3 | -------------------------------------------------------------------------------- /software/shared/board_defs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(board_defs INTERFACE) 2 | target_include_directories(board_defs INTERFACE include/) 3 | -------------------------------------------------------------------------------- /software/shared/buserror/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(buserror STATIC buserror.S) 2 | target_include_directories(buserror PUBLIC include/) 3 | -------------------------------------------------------------------------------- /software/shared/memtest/README.md: -------------------------------------------------------------------------------- 1 | Public-domain memory-test library from 2 | https://barrgroup.com/embedded-systems/how-to/memory-test-suite-c. 3 | -------------------------------------------------------------------------------- /software/tools/testMemory/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_application("testMemory" testMemory.c CONSOLE) 2 | target_link_libraries(testMemory board_defs driver_control) 3 | -------------------------------------------------------------------------------- /software/tools/programROM/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_application("programROM" programROM.c flash.c CONSOLE) 2 | target_link_libraries(programROM buserror declrom_header enc624j600) 3 | -------------------------------------------------------------------------------- /boards/se/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (version 7) 3 | (lib (name "PDS-Ethernet")(type "KiCad")(uri "${KIPRJMOD}/../shared/PDS-Ethernet.pretty")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /software/shared/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(board_defs) 2 | add_subdirectory(buserror) 3 | add_subdirectory(enc624j600) 4 | add_subdirectory(memtest) 5 | add_subdirectory(version) -------------------------------------------------------------------------------- /boards/se/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (version 7) 3 | (lib (name "PDS-Ethernet")(type "KiCad")(uri "${KIPRJMOD}/../shared/PDS-Ethernet.kicad_sym")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /boards/se30/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (version 7) 3 | (lib (name "PDS-Ethernet")(type "KiCad")(uri "${KIPRJMOD}/../shared/PDS-Ethernet.pretty")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /boards/breakout/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (version 7) 3 | (lib (name "PDS-Ethernet")(type "KiCad")(uri "${KIPRJMOD}/../shared/PDS-Ethernet.pretty")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /boards/se30/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (version 7) 3 | (lib (name "PDS-Ethernet")(type "KiCad")(uri "${KIPRJMOD}/../shared/PDS-Ethernet.kicad_sym")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /installer/UserStartup.mpw: -------------------------------------------------------------------------------- 1 | # Set directory to where we put our sources 2 | Directory 'Build Environment:Sources' 3 | 4 | # Run the build script 5 | Build.mpw 6 | 7 | # Exit without saving any changes 8 | Quit -n 9 | -------------------------------------------------------------------------------- /pld/.gitignore: -------------------------------------------------------------------------------- 1 | # CUPL outputs 2 | *.abs 3 | *.chp 4 | *.doc 5 | *.fit 6 | *.fus 7 | *.im 8 | *.io 9 | *.jed 10 | *.lst 11 | *.mx 12 | *.pin 13 | *.pla 14 | *.pdf 15 | *.sim 16 | *.so 17 | *.tt2 18 | *.tt3 19 | *.wo 20 | 21 | # ATMISP outputs 22 | *.svf 23 | jtag.log 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25) 2 | project("SEthernet" C CXX ASM) 3 | 4 | if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Retro68") 5 | message(FATAL_ERROR "SEthernet must be built using Retro68!") 6 | endif() 7 | 8 | add_subdirectory(pld) 9 | add_subdirectory(rom) 10 | add_subdirectory(software) 11 | -------------------------------------------------------------------------------- /boards/.gitignore: -------------------------------------------------------------------------------- 1 | # For PCBs designed using KiCad: https://www.kicad.org/ 2 | # Format documentation: https://kicad.org/help/file-formats/ 3 | 4 | # Temporary files 5 | *.000 6 | *.bak 7 | *.bck 8 | *.kicad_pcb-bak 9 | *.kicad_pro-bak 10 | *.kicad_sch-bak 11 | *-backups 12 | *.kicad_prl 13 | *.sch-bak 14 | *~ 15 | _autosave-* 16 | *.tmp 17 | *-save.pro 18 | *-save.kicad_pcb 19 | fp-info-cache 20 | 21 | # Netlist files (exported from Eeschema) 22 | *.net 23 | 24 | # Autorouter files (exported from Pcbnew) 25 | *.dsn 26 | *.ses 27 | 28 | # Exported BOM files 29 | *.xml 30 | *.csv 31 | 32 | gerbers/* 33 | -------------------------------------------------------------------------------- /rom/se30/declrom_macros.inc: -------------------------------------------------------------------------------- 1 | /* Some of my own macros to augment the Apple ones provided in ROMDefs.inc */ 2 | 3 | /* Terminator for sResource lists */ 4 | .macro EndLstEntry 5 | DatLstEntry 0xff, 0 6 | .endm 7 | 8 | /* sRsrcType data */ 9 | .macro RsrcType category, type, swInterface, hwDevice 10 | .word \category 11 | .word \type 12 | .word \swInterface 13 | .word \hwDevice 14 | .endm 15 | 16 | /* Generate a valid ByteLanes byte given a 4-bit bitfield indicating the lanes 17 | in use */ 18 | .macro ByteLanes lanes 19 | .byte (\lanes & 0xf) + ((~\lanes & 0xf) << 4) 20 | .endm 21 | -------------------------------------------------------------------------------- /software/shared/buserror/include/buserror.h: -------------------------------------------------------------------------------- 1 | /* 2 | 680x0 Bus Error handler 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | int buserror_probe(void * addr); 20 | -------------------------------------------------------------------------------- /software/driver/readpacket.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* ReadPacket callback function that we pass to protocol handlers, defined in 4 | readpacket.S. We don't (and shouldn't) call it directly. */ 5 | extern void readPacket(); 6 | 7 | /* C wrapper to read data from receive buffer */ 8 | static inline void readBuf(struct enc624j600 * chip, void * dest, 9 | unsigned short len) { 10 | asm volatile( 11 | " MOVE.L %[chip], %%a1 \n" 12 | " MOVE.L %[dest], %%a3 \n" 13 | " MOVE.W %[len], %%d3 \n" 14 | " MOVE.W %%d3, %%d1 \n" 15 | " JSR _readBuf" 16 | : 17 | : [chip] "g" (chip), 18 | [dest] "g" (dest), 19 | [len] "g" (len) 20 | : "d0", "d1", "d2", "a0", "a1", /* Registers that we normally expect to be 21 | trashed across calls */ 22 | "d3", "a3" /* Extra registers that we change */ 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /software/shared/enc624j600/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Since portions of the enc624j600 library are called in performance-critical 2 | # parts of the driver, build separate versions tuned for the SE and SE/30. The 3 | # 68000-tuned SE version is still 'good enough' to be used by diagnostics etc. 4 | # on both machines. 5 | 6 | option(REV0_SUPPORT "Enable workarounds for rev0 hardware bugs" OFF) 7 | 8 | add_library(enc624j600 STATIC enc624j600.c) 9 | target_link_libraries(enc624j600 PRIVATE memtest) 10 | target_include_directories(enc624j600 PUBLIC include/) 11 | target_compile_options(enc624j600 PRIVATE -m68000 -mtune=68000) 12 | target_compile_definitions(enc624j600 PUBLIC "$<$:REV0_SUPPORT>") 13 | 14 | add_library(enc624j600_030 STATIC enc624j600.c) 15 | target_link_libraries(enc624j600_030 PRIVATE memtest) 16 | target_include_directories(enc624j600_030 PUBLIC include/) 17 | target_compile_options(enc624j600_030 PRIVATE -m68030 -mtune=68030) 18 | target_compile_definitions(enc624j600_030 PUBLIC "$<$:REV0_SUPPORT>") 19 | -------------------------------------------------------------------------------- /pld/se/README.md: -------------------------------------------------------------------------------- 1 | # PLD Equations for 68000 PDS Interface 2 | 3 | U2 on the SEthernet board is an ATF16V8/GAL16V8 programmable logic device that 4 | provides address decoding and miscellaneous glue logic. 5 | 6 | Address decoding is straightforward - when `/AS` is asserted and address lines 7 | `A24..A16` indicate an address in the range `0x80 0000`-`0x80 FFFF`, we assert 8 | the ENC624J600's chip-select pin. 9 | 10 | Glue logic is also minimal - the `B0SEL` and `B1SEL` byte-select lines on the 11 | ENC624J600 are just active-high equivalents to the active-low `/UDS` and `/LDS` 12 | lines on the 68000 bus. The BBU chip on the SE logic board automatically asserts 13 | `/DTACK` for us on a schedule that we can live with, so there is no need to 14 | generate it ourselves (if we needed additional wait states, we could assert 15 | `/EXT.DTK` to suppress the BBU's `/DTACK` signal until we were ready). 16 | 17 | Finally, the push-pull interrupt output of the ENC624J600 is buffered and turned 18 | into an open-drain output, allowing it to interface with the wired-OR interrupt 19 | logic of the SE. 20 | -------------------------------------------------------------------------------- /rom/se30/README.md: -------------------------------------------------------------------------------- 1 | # Declaration ROM 2 | 3 | To make use of the Slot Manager for hardware probing, interrupts, etc. we need 4 | to do two things: 5 | 6 | - Map our device into a memory region corresponding to a NuBus slot (on the 7 | SE/30, valid 'pseudoslots' are `9`, `A`, or `B`). 8 | 9 | - Have a declaration ROM header at the very end of the slot's address space, 10 | pointing to a valid sResource directory. 11 | 12 | This directory contains a suitable declaration ROM laid out in GNU Assembler 13 | format, with a Python script to perform some basic validity checks and 14 | post-processing. 15 | 16 | ## Files 17 | 18 | `declrom.s`: Source for declaration ROM. 19 | 20 | `ROMDefs.inc`: Assorted constants and macros for declaration ROMs, converted to 21 | GNU Assembler format from Universal Interfaces file `AIncludes/ROMDefs.a`. 22 | 23 | `declrom_macros.inc`: Additional macros for declaration ROMs. 24 | 25 | `gencrc.py`: Quick 'n dirty Python script to sanity-check a declaration ROM 26 | header, calculate and write the CRC field, and pad it to a suitable length to 27 | program into an EPROM. 28 | -------------------------------------------------------------------------------- /installer/text2mac.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Quick n' dirty tool to convert a UTF-8 text file with Unix (LF) or Windows 5 | (CRLF) line endings into MacRoman with old-Mac (CR) line-endings. 6 | """ 7 | 8 | import argparse 9 | import os 10 | import sys 11 | 12 | def main(): 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument('input') 15 | parser.add_argument('output') 16 | args = parser.parse_args() 17 | 18 | if args.input == '-': 19 | infile = sys.stdin 20 | else: 21 | # Assume system default input encoding 22 | infile = open(args.input, 'r') 23 | 24 | if args.output == '-': 25 | outfile = os.fdopen(sys.stdout.fileno(), 'wb') 26 | else: 27 | outfile = open(args.output, 'wb') 28 | 29 | data = infile.read() 30 | infile.close() 31 | 32 | if '\r\n' in data: 33 | data = data.replace('\r\n', '\r') 34 | else: 35 | data = data.replace('\n', '\r') 36 | 37 | # Encode output data as macroman 38 | outfile.write(data.encode('macroman')) 39 | 40 | outfile.close() 41 | 42 | if __name__ == '__main__': 43 | main() 44 | -------------------------------------------------------------------------------- /installer/Build.mpw: -------------------------------------------------------------------------------- 1 | # MPW script to build our Installer script. 2 | Set TYPECREATOR "-t bbkr -c bbkr" 3 | Set REZFLAGS "{TYPECREATOR} -I '{RIncludes}' -d currentDate=`Date -n`" 4 | Set LINKFLAGS "{TYPECREATOR}" 5 | 6 | # Compile our script resources. For some reason, Retro68's Rez does not like the 7 | # InstallerTypes.r include file, so we have to do this bit in MPW. 8 | Rez {REZFLAGS} -a -o 'Installer Script' filespecs.r 9 | Rez {REZFLAGS} -a -o 'Installer Script' atoms.r 10 | Rez {REZFLAGS} -a -o 'Installer Script' packages.r 11 | Rez {REZFLAGS} -a -o 'Installer Script' comments.r 12 | Rez {REZFLAGS} -a -o 'Installer Script' rules.r 13 | Rez {REZFLAGS} -a -o 'Installer Script' code.r 14 | 15 | # Post-process the installer script 16 | ScriptCheck -r -l 2 -p 'Installer Script' 17 | 18 | # Append non-script resources (e.g. DLOG and STR# resources used by code 19 | # resources) AFTER running ScriptCheck, otherwise it complains that they're not 20 | # referenced by any installer resources. 21 | Rez {REZFLAGS} -a -o 'Installer Script' misc.r 22 | 23 | # Copy built install script to installer disk 24 | Duplicate 'Installer Script' 'SEthernet Installer:' 25 | -------------------------------------------------------------------------------- /software/driver/SEthernet.r: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "Retro68.r" 20 | #include "Types.r" 21 | #include "version.h" 22 | 23 | data 'DRVR' (69, locked, sysheap, ".ENET0") { 24 | $$read("SE_driver.resource") 25 | }; 26 | 27 | resource 'vers' (1, purgeable) { 28 | VERSION_MAJOR, VERSION_MINOR; 29 | VERSION_DEVSTAGE, VERSION_INTERNAL_ID; 30 | 0; 31 | VERSION_SHORT; 32 | VERSION_LONG; 33 | }; 34 | -------------------------------------------------------------------------------- /software/driver/multicast.h: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "driver.h" 24 | 25 | multicastEntry* findMulticastEntry(const driverGlobalsPtr theGlobals, 26 | const hwAddr *address); 27 | OSStatus doEAddMulti(driverGlobalsPtr theGlobals, const EParamBlkPtr pb); 28 | OSStatus doEDelMulti(driverGlobalsPtr theGlobals, const EParamBlkPtr pb); 29 | -------------------------------------------------------------------------------- /software/tools/showDrivers/README.md: -------------------------------------------------------------------------------- 1 | # showDrivers 2 | 3 | A quick and dirty tool to list the entries in the system's driver table. 4 | 5 | ## Table columns 6 | 7 | **Unit**: Driver unit number 8 | 9 | **RefNum**: Driver reference number used for Device Manager calls (1's complement 10 | of unit number) 11 | 12 | **Flags**: Driver flags (explained below) 13 | 14 | **DRVRAddr**: Address of driver's `DRVR` resource in memory 15 | 16 | **Name**: Driver name 17 | 18 | ## Driver flags 19 | 20 | Lowercase letter means flag is not set, uppercase letter means flag is set. 21 | 22 | **L**: Driver is locked in memory 23 | 24 | **T**: Driver performs periodic tasks 25 | 26 | **G**: Driver needs to be called before application heap initialization 27 | 28 | **S**: Driver responds to `Status` call 29 | 30 | **C**: Driver responds to `Control` call 31 | 32 | **W**: Driver responds to `Write` call 33 | 34 | **R**: Driver responds to `Read` call 35 | 36 | **A**: Driver is currently executing a request 37 | 38 | **H**: Driver is referenced through a Handle, rather than a Pointer 39 | 40 | **O**: Driver is open 41 | 42 | **V**: Driver has marked itself as virtual-memory safe 43 | -------------------------------------------------------------------------------- /software/driver/protocolhandler.h: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "driver.h" 24 | 25 | protocolHandlerEntry* findPH(const driverGlobalsPtr theGlobals, 26 | const unsigned short theProtocol); 27 | OSStatus doEAttachPH(driverGlobalsPtr theGlobals, const EParamBlkPtr pb); 28 | OSStatus doEDetachPH(driverGlobalsPtr theGlobals, const EParamBlkPtr pb); 29 | void InitPHTable(driverGlobalsPtr theGlobals); 30 | -------------------------------------------------------------------------------- /software/driver/SEthernet30.r: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "Retro68.r" 20 | #include "Types.r" 21 | #include "sethernet30_board_defs.h" 22 | #include "version.h" 23 | 24 | data 'enet' (SETHERNET30_BOARDID, locked, sysheap, "SEthernet/30") { 25 | $$read("SE30_driver.resource") 26 | }; 27 | 28 | resource 'vers' (1, purgeable) { 29 | VERSION_MAJOR, VERSION_MINOR; 30 | VERSION_DEVSTAGE, VERSION_INTERNAL_ID; 31 | 0; 32 | VERSION_SHORT; 33 | VERSION_LONG; 34 | }; 35 | -------------------------------------------------------------------------------- /boards/shared/PDS-Ethernet.pretty/Wurth_9774025360R.kicad_mod: -------------------------------------------------------------------------------- 1 | (footprint "Wurth_9774025360R" (version 20221018) (generator pcbnew) 2 | (layer "F.Cu") 3 | (descr "Mounting Hole 3.5mm") 4 | (tags "mounting hole 3.5mm") 5 | (attr exclude_from_pos_files exclude_from_bom) 6 | (fp_text reference "REF**" (at 0 -4.5) (layer "F.SilkS") 7 | (effects (font (size 1 1) (thickness 0.15))) 8 | (tstamp 9a227518-a125-4d55-9587-0c1499ce41a7) 9 | ) 10 | (fp_text value "Wurth_9774025360R" (at 0 4.5) (layer "F.Fab") 11 | (effects (font (size 1 1) (thickness 0.15))) 12 | (tstamp 7b8e5c4e-d69b-4184-a234-a02ce7780e15) 13 | ) 14 | (fp_text user "${REFERENCE}" (at 0 0) (layer "F.Fab") 15 | (effects (font (size 1 1) (thickness 0.15))) 16 | (tstamp 5d67ecc7-3875-4799-aceb-34a5f82fb9b4) 17 | ) 18 | (fp_circle (center 0 0) (end 3 0) 19 | (stroke (width 0.15) (type solid)) (fill none) (layer "Cmts.User") (tstamp 004026e5-1625-40f4-9daa-75e998c99afd)) 20 | (fp_circle (center 0 0) (end 3.25 0) 21 | (stroke (width 0.05) (type solid)) (fill none) (layer "F.CrtYd") (tstamp da86dd0d-6b99-46c2-b3fe-6038c50edf29)) 22 | (pad "1" thru_hole circle (at 0 0) (size 7.4 7.4) (drill 4.4) (layers "*.Cu" "*.Mask") (tstamp 309b03f1-dad1-43ee-982a-d14d7cd84475)) 23 | ) 24 | -------------------------------------------------------------------------------- /software/shared/memtest/include/memtest.h: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * 3 | * Filename: memtest.h 4 | * 5 | * Description: Memory-testing module API. 6 | * 7 | * Notes: The memory tests can be easily ported to systems with 8 | * different data bus widths by redefining 'datum' type. 9 | * 10 | * 11 | * Copyright (c) 2000 by Michael Barr. This software is placed into 12 | * the public domain and may be used for any purpose. However, this 13 | * notice must not be changed or removed and no warranty is either 14 | * expressed or implied by its publication or distribution. 15 | **********************************************************************/ 16 | 17 | #ifndef _memtest_h 18 | #define _memtest_h 19 | 20 | 21 | /* 22 | * Define NULL pointer value. 23 | */ 24 | #ifndef NULL 25 | #define NULL (void *) 0 26 | #endif 27 | 28 | /* 29 | * Set the data bus width. 30 | */ 31 | typedef unsigned short datum; 32 | 33 | /* 34 | * Function prototypes. 35 | */ 36 | datum memTestDataBus(volatile datum * address); 37 | datum * memTestAddressBus(volatile datum * baseAddress, unsigned long nBytes); 38 | datum * memTestDevice(volatile datum * baseAddress, unsigned long nBytes); 39 | 40 | 41 | #endif /* _memtest_h */ -------------------------------------------------------------------------------- /software/driver/isr.h: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #include "driver.h" 24 | 25 | #if defined(TARGET_SE) 26 | /* The original level-1 interrupt vector. If the interrupt fires but we don't 27 | have a pending interrupt flag, we pass the interrupt through to it*/ 28 | extern void (*originalInterruptVector)(); 29 | 30 | /* Pointer to driver globals so our ISR can reference them */ 31 | extern driverGlobalsPtr isrGlobals; 32 | #endif 33 | 34 | /* Assembly-language wrapper for our ISRs written in C; implementation in 35 | isrwrapper.S */ 36 | void isrWrapper(void); 37 | -------------------------------------------------------------------------------- /pld/README.md: -------------------------------------------------------------------------------- 1 | # Programmable Logic Device Sources 2 | 3 | The sources in this dirctory are in CUPL format, and can be built using the 4 | Windows-based 5 | [WinCUPL](https://www.microchip.com/en-us/products/fpgas-and-plds/spld-cplds/pld-design-resources) 6 | software. Unfortunately, using WinCUPL is a retrocomputing exercise in its own 7 | right - the GUI does not work on anything newer than Windows XP. 8 | 9 | However, it is possible to run WinCUPL under Wine, and the CMake monstrosity in 10 | this directory uses Wine and WinCUPL to build CUPL sources without having to 11 | mess with Windows XP. It looks for WinCUPL in `~/.wine/drive_c/Wincupl`, which 12 | is the default installation path. If your Wine data is in a different place, or 13 | you installed WinCUPL elsewhere, the `WINCUPL_PATH` cache variable will need to 14 | be set to point to the correct location. If Wine and/or Wincupl are not 15 | available, then the PLD build target is skipped. 16 | 17 | The typical WinCUPL workflow is to then use the ATMISP software to either drive 18 | an Atmel/Microchip programmer directly, or generate an SVF file that can be used 19 | by a generic JTAG programmer. This workflow, frankly, stinks, and we instead use 20 | the `fuseconv` utility from [prjbureau](https://github.com/whitequark/prjbureau) 21 | to generate an SVF. Setup of prjbureau is automatic, but Python 3 and the 22 | `virtualenv` module are required. 23 | -------------------------------------------------------------------------------- /software/shared/board_defs/include/sethernet_board_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | Board-level defintions for SEthernet. 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #ifndef SETHERNET_BOARD_DEFS_H 20 | #define SETHERNET_BOARD_DEFS_H 21 | 22 | /* These values must be kept in sync with the glue logic in pld/se/se-u2.pld */ 23 | 24 | /* Base address of SEthernet board. Anything between 0x600000 and 0x8fffff is 25 | fair game, the example board in Designing Cards And Devices uses 0x800000, so we 26 | might as well follow its example. */ 27 | #define SETHERNET_BASEADDR (0x800000) 28 | 29 | /* Interrupt priority level used by SEthernet board. Level 1 is shared with the 30 | VIA. */ 31 | #define SETHERNET_INTERRUPT (1) 32 | 33 | #endif /* SETHERNET_BOARD_DEFS_H */ 34 | -------------------------------------------------------------------------------- /software/driver/macsbug.inc: -------------------------------------------------------------------------------- 1 | /* 2 | Define a MacsBug symbol. Place immediately after the function to be labeled, but 3 | before any constant data accompanying the function. 4 | 5 | Arguments: 6 | Name: Symbol name that will appear in MacsBug (1-255 chars). Does 7 | not necessarily have to match the label (although it 8 | probably should.) 9 | ConstantLength: Number of bytes of constant data to follow. (optional) 10 | 11 | Example: 12 | SomeFunction: 13 | ... 14 | RTS 15 | MacsbugSymbol SomeFunction, 0 16 | 17 | FunctionWithConstants: 18 | ... 19 | RTS 20 | MacsbugSymbol FunctionWithConstants, 3 21 | FunctionWithConstants_constant1: 22 | .word 420 23 | FunctionWithConstants_constant2: 24 | .byte 69 25 | .align 2 26 | 27 | AnotherFunction: 28 | ... 29 | RTS 30 | MacsbugSymbol AnotherFunction 31 | 32 | See Macsbug Reference and Debugging Guide, Appendix D for more details. 33 | */ 34 | 35 | .macro MacsbugSymbol Name, ConstantLength=0 36 | .byte 0x80 /* 'long' variable-length name (1-255 chars) follows */ 37 | .byte _macsbugSymbol\@ - (. + 1) /* Length of name */ 38 | .ascii "\Name" 39 | _macsbugSymbol\@: 40 | .align 2 41 | .word \ConstantLength 42 | .endm 43 | 44 | .macro _Debugger 45 | .word 0xa9ff 46 | .endm 47 | 48 | .macro _DebugStr 49 | .word 0xabff 50 | .endm 51 | -------------------------------------------------------------------------------- /rom/se30/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_compile_options(-nodefaultlibs -nostartfiles -ffreestanding) 2 | 3 | # Compile to an object file only, no linking 4 | add_library(declrom OBJECT declrom.S) 5 | 6 | # Use sethernet30_board_defs.h from shared library to keep hardware IDs in sync 7 | # with software 8 | target_link_libraries(declrom board_defs version) 9 | 10 | # Generate a 'library' containing the ROM data formatted as a C header, for use 11 | # by firmware update tools. 12 | add_library(declrom_header INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/se30-u2.h) 13 | # The header is generated, so we have to tell CMake to find it in our build 14 | # tree, not the source 15 | target_include_directories(declrom_header INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/) 16 | 17 | # Build the ROM image 18 | add_custom_command( 19 | OUTPUT se30-u2.rom 20 | # Use objcopy to flatten the object file into a binary image 21 | COMMAND ${CMAKE_SYSTEM_PREFIX_PATH}/bin/objcopy 22 | --output-target=binary 23 | $ 24 | se30-u2.rom 25 | # Pad ROM to mapped size (64K), sanity-check header, and generate CRC field 26 | COMMAND python3 ${CMAKE_CURRENT_SOURCE_DIR}/gencrc.py 27 | --output se30-u2.rom 28 | --pad 65536 29 | se30-u2.rom 30 | DEPENDS declrom 31 | ) 32 | 33 | # Build a header-ified version of the ROM image 34 | add_custom_command( 35 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/include/se30-u2.h 36 | COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/include 37 | COMMAND xxd -i se30-u2.rom ${CMAKE_CURRENT_BINARY_DIR}/include/se30-u2.h 38 | DEPENDS se30-u2.rom 39 | ) 40 | 41 | add_custom_target(declrom_image ALL 42 | DEPENDS se30-u2.rom 43 | ${CMAKE_CURRENT_BINARY_DIR}/include/se30-u2.h) 44 | -------------------------------------------------------------------------------- /software/driver/README.md: -------------------------------------------------------------------------------- 1 | # Driver 2 | 3 | The source this directory can build a Slot Manager `.ENET` driver for an SE/30 4 | card, or a non-Slot Manager `.ENET0` driver for an SE card. 5 | 6 | While the code for the two drivers is identical save for some differences in 7 | device initialization and interrupt handling, the way Mac OS ethernet drivers 8 | work means that they need to be separate driver resources. 9 | 10 | ## SEthernet/30 11 | 12 | Slot Manager drivers such as the SEthernet/30 driver use `.ENET` as the driver 13 | name in their resource header, and are installed as `enet` resources with a 14 | Resource ID corresponding to the Board ID that they operate with. These drivers 15 | can either be installed directly into the System File, or (with Network Software 16 | Installer 1.5 or later) as Extensions with the type `comd`. 17 | 18 | When an ethernet device is opened with `OpenSlot()`, the system `.ENET` 'driver 19 | shell' searches `enet` resources for a driver whose resource ID matches the 20 | Board ID of the slot, and loads that driver instead. 21 | 22 | ## SEthernet 23 | 24 | Non-Slot Manager drivers must be installed as traditional drivers (i.e. a `DRVR` 25 | resource) called `.ENET0`. This can either be done by manually installing the 26 | resource into the System file, or loading the driver into the driver table at 27 | run-time, for example, using an `INIT`. 28 | 29 | Software that uses ethernet devices must try to load a `.ENET0` driver if no 30 | Slot Manager devices are available. See Listing 1-11 in [IM: 31 | Networking](https://www.vintageapple.org/inside_r/pdf/Networking_1994.pdf) for 32 | details (and "Installing a Device Driver" in [IM: 33 | Devices](https://www.vintageapple.org/inside_r/pdf/Devices_1994.pdf) for the 34 | absurd song-and-dance routine that is installing and opening a device driver). 35 | -------------------------------------------------------------------------------- /software/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Try to keep code size down. This is especially important as the driver header 2 | # uses 16 bit signed offsets - i.e. our driver entrypoints must be within 32kb 3 | # of the header. Compiling with -ffunction-sections and linking with 4 | # --gc-sections means that unused functions can be identified and removed at 5 | # link-time. 6 | add_compile_options(-ffunction-sections -fno-exceptions) 7 | add_link_options(-Wl,--gc-sections) 8 | 9 | # Set up compiler warnings 10 | add_compile_options(-Wall -Wextra -Wpedantic) 11 | 12 | # Save asm output and add verbose annotations to aid debugging 13 | add_compile_options(-save-temps=obj -fverbose-asm) 14 | 15 | # Define DEBUG for debug builds 16 | add_compile_definitions("$<$:DEBUG>") 17 | 18 | add_compile_options(# Add 'debug-safe' optimisations for debug builds - the 19 | # unoptimized output is enormous and extremely stupid. Since 20 | # we're doing all our debugging in machine code, disable a 21 | # few more optimizations below to generate more readable 22 | # code. 23 | "$<$:-Og>" 24 | # Emit LINK/UNLK even when a frame pointer is not used, to 25 | # make Macsbug's SC6 command more useful. 26 | "$<$:-fno-omit-frame-pointer>" 27 | # Always remove function call args from the stack 28 | # immediately after the call, so that the stack is easier to 29 | # follow. 30 | "$<$:-fno-defer-pop>" 31 | # Keep all function-prologue stuff at the beginning of the 32 | # function, rather than deferring it to time-of-use. 33 | "$<$:-fno-shrink-wrap>") 34 | 35 | add_link_options("-Wl,--no-warn-rwx-segments") 36 | 37 | add_subdirectory(driver) 38 | add_subdirectory(tools) 39 | add_subdirectory(shared) 40 | -------------------------------------------------------------------------------- /software/shared/board_defs/include/sethernet30_board_defs.h: -------------------------------------------------------------------------------- 1 | /* 2 | Board-level defintions for SEthernet/30. 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #ifndef SETHERNET30_BOARD_DEFS_H 20 | #define SETHERNET30_BOARD_DEFS_H 21 | 22 | /* 23 | These constants would originally have been assigned to hardware developers by 24 | Apple Developer Technical Support, but these days I guess we can just pick 25 | numbers that we like and hope they don't collide with anything. 26 | 27 | The license plate on my first car (rust in peace, 1976 Triumph 2500TC, you 28 | served me well) was ID9635, so I've chosen an ID of decimal 9635 :) 29 | 30 | This file is also used to populate the definitions in the declaration ROM, any 31 | changes here will require new builds of both ROM and software. 32 | */ 33 | 34 | /* Board identifier */ 35 | #define SETHERNET30_BOARDID (9635) 36 | 37 | /* Driver software interface ID. All hardware that 38 | implements the same software interface should use this identifier. */ 39 | #define SETHERNET30_DRSW (9635) 40 | #define SETHERNET30_ROM_DRSW (SETHERNET30_DRSW) 41 | 42 | /* Driver hardware ID. This identifier allows software to tell apart different 43 | hardware that implements the same software interface. */ 44 | #define SETHERNET30_DRHW (1) 45 | #define SETHERNET30_ROM_DRHW (SETHERNET30_DRHW) 46 | 47 | #endif /* SETHERNET30_BOARD_DEFS_H */ 48 | -------------------------------------------------------------------------------- /software/shared/version/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | execute_process(COMMAND git describe --dirty=+ --always 2 | OUTPUT_VARIABLE GIT_REV 3 | ERROR_QUIET) 4 | string(STRIP ${GIT_REV} GIT_REV) 5 | 6 | if ("${GIT_REV}" STREQUAL "") 7 | set(GIT_REV "N/A") 8 | endif() 9 | 10 | # Version information set here should be compatible with 'vers' resource type: 11 | # 12 | # VERSION_MAJOR: major version number in BCD format 13 | # VERSION_MINOR: minor version number components in BCD format 14 | # VERSION_DEVSTAGE: development stage (see VERSION_DEVSTAGE_* for valid values) 15 | # VERSION_INTERNAL_ID: build ID for non-final development stages, should be 0 16 | # for final builds 17 | # VERSION_SHORT: short version string of form 18 | # .. 19 | # where is 'd', 'a', 'b', or empty for final builds 20 | # 21 | # For example: 22 | # VERSION_MAJOR 0x05 23 | # VERSION_MINOR 0x42 24 | # VERSION_DEVSTAGE VERSION_DEVSTAGE_BETA 25 | # VERSION_INTERNAL_ID 0x4 26 | # should have a VERSION_SHORT of 5.4.2b4 27 | 28 | set(VERSION " 29 | #define GIT_REV \"${GIT_REV}\" 30 | #define VERSION_SHORT \"1.0.0b1\" 31 | #define VERSION_LONG VERSION_SHORT \" git:\" GIT_REV 32 | #define VERSION_MAJOR 0x01 33 | #define VERSION_MINOR 0x00 34 | #define VERSION_DEVSTAGE VERSION_DEVSTAGE_BETA 35 | #define VERSION_INTERNAL_ID 0x01 36 | 37 | #define VERSION_DEVSTAGE_DEVELOPMENT 0x20 38 | #define VERSION_DEVSTAGE_ALPHA 0x40 39 | #define VERSION_DEVSTAGE_BETA 0x60 40 | #define VERSION_DEVSTAGE_FINAL 0x80 41 | ") 42 | 43 | if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/include/version.h) 44 | file(READ ${CMAKE_CURRENT_BINARY_DIR}/include/version.h VERSION_) 45 | else() 46 | set(VERSION_ "") 47 | endif() 48 | 49 | if (NOT "${VERSION}" STREQUAL "${VERSION_}") 50 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/include/version.h "${VERSION}") 51 | endif() 52 | 53 | add_library(version INTERFACE) 54 | target_include_directories(version INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/include/) 55 | -------------------------------------------------------------------------------- /pld/se30/README.md: -------------------------------------------------------------------------------- 1 | # PLD Equations for 68030 PDS Interface 2 | 3 | U3 on the SEthernet/30 board is an ATF1502 Complex Programmable Logic 4 | Devices that provides address decoding and glue logic. 5 | 6 | ## Address Decoding 7 | 8 | The SEthernet/30 board uses a Nubus 'pseudo-slot' design, occupying an address 9 | corresponding to a NuBus slot, and appearing to the Slot Manager just like a 10 | NuBus card would on a workstation Mac. To allow for multiple expansion cards, 11 | jumpers JP1 and JP2 can be used to configure the card to act as though it is 12 | installed in slots 9, A, B, or E. 13 | 14 | Each slot has an interrupt line dedicated to it on the PDS connector, U3 routes 15 | the ENC624J600's interrupt to the appropriate line based on the selection made 16 | on JP1 and JP2. 17 | 18 | Within the card's address space, minimal decoding is performed on address line 19 | A16, so that the card's declaration ROM shows up at the top of the address space 20 | (as expected by the Slot Manager), with the ENC624J600's address space 21 | immediately below it. 22 | 23 | Note that the Slot E configuration is not compatible with the SE/30 or IIsi. 24 | This option exists to allow the card to be used in the PDS slot of a Macintosh 25 | IIfx. This configuration has been tested and works, but the IIfx case does not 26 | provide a place to mount the breakout board. 27 | 28 | ## Bus Control 29 | 30 | While the ENC624J600 in PSP Mode 4 maps neatly onto the 68000's control lines, 31 | glue logic is required to interface with the 68030. Page 7-26 of the [M68030 32 | User's Manual](https://www.nxp.com/docs/en/reference-manual/MC68030UM-P1.pdf) 33 | describes logic to interface the 68030 to peripherals using 68000-style 34 | byte-select lines, a 16-bit subset of which is implemented in U4. 35 | 36 | Additionally, U4 generates the `/DSACKx` signals for the 68030 bus handshaking 37 | scheme. To accommodate the timing characteristics of the ENC624J600 (75 ns from 38 | selection to data valid on read), a counter is used to delay assertion of 39 | `/DSACKx` for 1 or 2 clock cycles after the chip is selected. 40 | -------------------------------------------------------------------------------- /software/driver/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | #if defined(DEBUG) 24 | #include 25 | #include 26 | #include 27 | #endif 28 | 29 | #include "driver.h" 30 | 31 | Boolean trapAvailable(const unsigned short trap); 32 | 33 | #if defined(DEBUG) 34 | /* Write an event to the debugging event log */ 35 | void debug_log(driverGlobals* theGlobals, unsigned short eventType, 36 | unsigned short eventData); 37 | 38 | /* Break into MacsBug with a printf-formatted string */ 39 | void DebugPrintf(const char * format, ...); 40 | 41 | #define DBGP(...) DebugPrintf(__VA_ARGS__) 42 | #define DBGS(str) DebugStr(str) 43 | #define DBG() Debugger() 44 | #else 45 | #define debug_log(theGlobals, eventType, eventData) 46 | #define DBGP(...) 47 | #define DBGS(str) 48 | #define DBG() 49 | #endif 50 | 51 | /* Compare two ethernet addresses for equality */ 52 | static inline Boolean ethAddrsEqual(const hwAddr *addr1, const hwAddr *addr2) { 53 | /* Compare first 4 bytes */ 54 | if (addr1->first4 != addr2->first4) { 55 | return false; 56 | } 57 | 58 | /* Compare remaining 2 bytes */ 59 | if (addr1->last2 != addr2->last2) { 60 | return false; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | /* Copy an ethernet address */ 67 | static inline void copyEthAddrs(hwAddr *dest, const hwAddr *source) { 68 | dest->first4 = source->first4; 69 | dest->last2 = source->last2; 70 | } 71 | -------------------------------------------------------------------------------- /software/tools/programROM/flash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Utility functions for programming JEDEC parallel flash memories 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | struct flash_id { 20 | unsigned char manufacturer; 21 | unsigned char device; 22 | }; 23 | 24 | typedef struct flash_id flash_id; 25 | 26 | /* 27 | Probe for flash device 28 | Returns 0 if probable flash device found, -1 if not found. 29 | 30 | Arguments: 31 | base_address - base address to probe 32 | id - out-parameter (may be NULL) - JEDEC manufacturer and device ID 33 | bytes 34 | */ 35 | int flash_probe(volatile unsigned char* base_address, flash_id* id); 36 | 37 | /* Erase flash device at base_address */ 38 | void flash_erase(volatile unsigned char* base_address); 39 | 40 | /* 41 | Write data to a flash device 42 | 43 | Arguments: 44 | base_address - base address of flash device 45 | offset - offset within flash device to write to 46 | data - buffer containing data to write 47 | len - length of data to write 48 | */ 49 | int flash_write(volatile unsigned char* base_address, unsigned int offset, 50 | unsigned char* data, unsigned int len); 51 | 52 | /* 53 | Erase and program a flash device (equivalent to calling flash_erase followed by 54 | flash_write) 55 | 56 | Arguments: 57 | base_address - base address of flash device 58 | offset - offset within flash device to write to 59 | data - buffer containing data to write 60 | len - length of data to write 61 | */ 62 | int flash_program(volatile unsigned char* base_address, unsigned int offset, 63 | unsigned char* data, unsigned int len); 64 | -------------------------------------------------------------------------------- /installer/README.md: -------------------------------------------------------------------------------- 1 | # Installer 2 | 3 | ## Building an installer 4 | 5 | You will need: 6 | 7 | - [hfsutils](https://www.mars.org/home/rob/proj/hfs/) 8 | 9 | - [Python 3](https://www.python.org) 10 | 11 | - [Mini vMac](https://www.gryphel.com/c/minivmac/index.html) 12 | 13 | - A relatively modern (4.0 or newer) version of the Bash shell 14 | 15 | Build the SEthernet source tree as per usual, and then run `build.sh`, giving it 16 | the location of the build tree, and the name of the disk image to produce, e.g. 17 | `./build.sh ../build install.dsk`. The build script will launch MPW within Mini 18 | vMac, build an installer disk image, and (hopefully) quit without any 19 | intervention. If something goes wrong, Mini vMac will remain open, and the MPW 20 | Worksheet will show the diagnostic output from the build. Good luck figuring it 21 | out! 22 | 23 | ## Files 24 | 25 | ### Installer resources 26 | - `atoms.r`: Specifications for installer 'atoms' - an atom can add or remove a 27 | file (`infa`) or a resource (`inra`), or run custom-code 'actions' at install 28 | time (`inaa`) 29 | 30 | - `code.r`: Code resources run by action atoms and install rules 31 | 32 | - `comments.r`: Installer 'comments' providing information about install 33 | packages 34 | 35 | - `filespecs.r`: Specifications of source and destination files, referenced by 36 | file and resource atoms 37 | 38 | - `misc.r`: Miscellaneous resources not directly referenced by the Installer 39 | 40 | - `packages.r`: Packages are collections of atoms that can be selected either 41 | automatically by rules, or manually through the Custom Install interface 42 | 43 | - `rules.r`: Rules processed at startup that select a default set of packages 44 | 45 | ### Build System 46 | 47 | - `Build.mpw`: MPW shell script that compiles the Installer script resources and 48 | post-processes them with ScriptCheck 49 | 50 | - `UserStartup.mpw`: MPW UserStartup script used to trigger autommated builds 51 | 52 | - `build.sh`: Bash script that automates the installer build process using Mini 53 | vMac 54 | 55 | - `text2mac.py`: Helper script to convert line endings and encodings of text 56 | files 57 | 58 | - `images/buildenv.dsk.gz`: Build environment - System 6.0.8, MPW 3.1, Installer 59 | SDK 3.4 60 | 61 | - `images/template.dsk.gz`: Installer disk template - contains AppleTalk support 62 | components from Network Software Installer 1.4.4, and Installer application. 63 | 64 | ### Output files 65 | 66 | - `installer.dsk.gz`: SEthernet install disk 67 | -------------------------------------------------------------------------------- /rom/se30/declrom.S: -------------------------------------------------------------------------------- 1 | #include "ROMDefs.inc" 2 | #include "declrom_macros.inc" 3 | #include "sethernet30_board_defs.h" 4 | #include "version.h" 5 | 6 | /* Declaration ROM starts here. The sResource directory should be at the lowest 7 | address */ 8 | directory: 9 | OSLstEntry 1, board_sResource 10 | OSLstEntry 128, eth_functional_sResource 11 | EndLstEntry 12 | 13 | board_sResource: 14 | OSLstEntry sRsrcType, board_sResource_rsrctype 15 | OSLstEntry sRsrcName, board_name 16 | DatLstEntry boardId, SETHERNET30_BOARDID 17 | OSLstEntry vendorInfo, vendor_info 18 | EndLstEntry 19 | 20 | board_sResource_rsrctype: 21 | RsrcType catBoard, typeBoard, drSwBoard, drHwBoard 22 | 23 | board_name: 24 | .ascii "SEthernet/30" 25 | .byte 0 26 | 27 | vendor_info: 28 | OSLstEntry vendorId, vendor_name 29 | OSLstEntry revLevel, vendor_rev 30 | OSLstEntry partNum, vendor_partnum 31 | OSLstEntry date, vendor_date 32 | EndLstEntry 33 | 34 | vendor_name: 35 | .ascii "https://github.com/rhalkyard" 36 | .byte 0 37 | 38 | vendor_rev: 39 | .ascii "Revision 0" 40 | .ascii " " 41 | .ascii "git:" 42 | .ascii GIT_REV 43 | .byte 0 44 | 45 | vendor_partnum: 46 | .ascii "SEthernet/30" 47 | .byte 0 48 | 49 | vendor_date: 50 | .ascii __DATE__ 51 | .ascii " " 52 | .ascii __TIME__ 53 | .byte 0 54 | 55 | eth_functional_sResource: 56 | OSLstEntry sRsrcType, eth_functional_sResource_rsrctype 57 | OSLstEntry sRsrcName, eth_functional_sResource_rsrcname 58 | OSLstEntry minorBaseOS, eth_functional_sResource_baseoffset 59 | EndLstEntry 60 | 61 | eth_functional_sResource_rsrctype: 62 | RsrcType catNetwork, typeEtherNet, SETHERNET30_DRSW, SETHERNET30_DRHW 63 | 64 | eth_functional_sResource_rsrcname: 65 | .ascii "Network_Ethernet_SEthernet_SE30" 66 | .byte 0 67 | 68 | eth_functional_sResource_baseoffset: 69 | .long 0x00000000 70 | 71 | /* Decalration ROM header */ 72 | OSLstEntry 0 directory /* Offset to sResource directory */ 73 | .long declRomEnd - directory /* Length from sResource directory to end of ROM */ 74 | .space 4 /* CRC - this will get patched in later */ 75 | .byte 1 /* ROM revision (values 1-9 are valid) */ 76 | .byte 1 /* Declaration ROM format (must be 1 for Apple) */ 77 | .long 0x5a932bc7 /* Magic number */ 78 | .byte 0 /* Reserved zero byte */ 79 | ByteLanes 0b1111 /* Byte lanes used by ROM */ 80 | /* ROM ends here, header must be at the highest address in ROM*/ 81 | declRomEnd: 82 | -------------------------------------------------------------------------------- /software/driver/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "util.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "driver.h" 27 | 28 | #if defined(DEBUG) 29 | void DebugPrintf(const char * format, ...) { 30 | static char strbuf[256]; 31 | va_list args; 32 | va_start(args, format); 33 | strbuf[0] = vsprintf(strbuf+1, format, args); 34 | DebugStr((unsigned char *)strbuf); 35 | } 36 | #endif 37 | 38 | /* Check whether a given trap is available. Adapted from IM: Devices listing 39 | 8-1 */ 40 | Boolean trapAvailable(const unsigned short trap) { 41 | TrapType type; 42 | /* First determine whether it is an OS or Toolbox routine */ 43 | if (trap & 0x800) { 44 | type = OSTrap; 45 | } else { 46 | type = ToolTrap; 47 | } 48 | 49 | /* filter cases where older systems mask with 0x1ff rather than 0x3ff */ 50 | if (type == ToolTrap && (trap & 0x3ff) >= 0x200 && 51 | GetToolboxTrapAddress(0xa86e) == GetToolboxTrapAddress(0xaa6e)) { 52 | return false; 53 | } else { 54 | return NGetTrapAddress(trap, type) != GetToolboxTrapAddress(_Unimplemented); 55 | } 56 | } 57 | 58 | #if defined(DEBUG) 59 | void debug_log(driverGlobals *theGlobals, unsigned short eventType, 60 | unsigned short eventData) { 61 | eventLog *log = &theGlobals->log; 62 | 63 | /* Disable interrupts so that log operations are atomic */ 64 | unsigned short srSave; 65 | asm("MOVE.W %%sr, %[srSave] \n\t" 66 | "ORI.W %[srMaskInterrupts], %%sr \n\t" 67 | : [srSave] "=dm"(srSave) 68 | : [srMaskInterrupts] "i"(0x700)); 69 | 70 | log->entries[log->head].ticks = TickCount(); 71 | log->entries[log->head].eventType = eventType; 72 | log->entries[log->head].eventData = eventData; 73 | 74 | log->head = (log->head + 1) % LOG_LEN; 75 | /* Make head of log buffer look distinctive so that we can spot it in a dump */ 76 | log->entries[log->head].ticks = 0xffffffff; 77 | log->entries[log->head].eventType = 0xffff; 78 | log->entries[log->head].eventData = 0xffff; 79 | 80 | /* Restore processor state */ 81 | asm volatile("MOVE.W %[srSave], %%sr \n\t" : : [srSave] "dm"(srSave)); 82 | } 83 | #endif 84 | -------------------------------------------------------------------------------- /software/shared/buserror/buserror.S: -------------------------------------------------------------------------------- 1 | /* 2 | 680x0 Bus Error handler 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | .global buserror_probe 20 | 21 | busErrVector = 0x00000008 /* 68000 vector table entry for Bus Error */ 22 | 23 | .text 24 | /* 25 | Probe a given address for validity by temporarily hijacking the Bus Error 26 | vector and then trying to read from the address. 27 | 28 | int probeAddress(void * addr); 29 | 30 | Returns the byte at addr if successful, or a negative value if reading from addr 31 | caused a Bus Error. 32 | 33 | On entry: 34 | Stack: address to probe 35 | 36 | On exit: 37 | A0: destroyed 38 | D0: <0 if we took a Bus Error while reading from the address 39 | >=0 (the byte at the given address) if reading was successful 40 | */ 41 | buserror_probe: 42 | LINK %a6, #-6 43 | MOVE.W %sr,-6(%a6) /* Save processor state */ 44 | ORI.W #0x700, %sr /* Disable interrupts while we're messing with system 45 | vectors */ 46 | 47 | MOVE.L busErrVector, -4(%a6) /* Save old Bus Error vector */ 48 | LEA busErrReturn(%pc), %a0 49 | MOVE.L %a0, busErrVector /* Jump to BusErrReturn on Bus Error */ 50 | 51 | MOVEQ #-1,%d0 /* Initialize D0.L to -1 */ 52 | MOVE.L 8(%a6), %a0 /* Get address argument */ 53 | MOVE.B (%a0), %d0 /* Try reading a byte from the address */ 54 | 55 | /* If the address in A0 is not valid, the MOVE.B above will cause a Bus 56 | Error and jump directly to busErrReturn, skipping the instructions below. 57 | The NOPs are there to ensure that the AND will not enter the pipeline until 58 | after the read-attempt might fault */ 59 | NOP 60 | NOP 61 | AND.L #0xff, %d0 /* If we get here, we didn't take a Bus Error. Mask off 62 | everything but the byte we read, so we return a 63 | positive value */ 64 | 65 | busErrReturn: 66 | MOVE.L -4(%a6), busErrVector /* Restore original bus error vector */ 67 | MOVE.W -6(%a6), %sr /* Restore processor state */ 68 | UNLK %a6 /* If we took a Bus Error, there will be an exception frame 69 | left on the stack, but this will conveniently get rid of it 70 | for us */ 71 | RTS 72 | -------------------------------------------------------------------------------- /pld/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | find_package(Git REQUIRED) 4 | find_program(WINE wine) 5 | set(WINCUPL_PATH ~/.wine/drive_c/Wincupl CACHE PATH "WinCUPL installation path") 6 | 7 | find_file(CUPL_EXE cupl.exe ${WINCUPL_PATH}/Shared/) 8 | find_file(CUPL_DL cupl.dl ${WINCUPL_PATH}/Shared/) 9 | find_file(FIT1502_EXE fit1502.exe ${WINCUPL_PATH}/WinCupl/Fitters/) 10 | 11 | # Drive WinCUPL through WINE to compile CPLD equations. Yeah. Really. 12 | # Arguments: 13 | # pldfile: CUPL source file 14 | # device: -device argument given to FIT1502.exe 15 | function(add_cpld pldfile device) 16 | cmake_path(GET pldfile STEM stem) 17 | 18 | # Compile CUPL to netlist 19 | add_custom_command( 20 | OUTPUT ${stem}.tt2 21 | BYPRODUCTS ${stem}.lst ${stem}.pla ${pldfile} 22 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${pldfile} 23 | 24 | # CUPL tends to crash if the source is not in the working directory 25 | COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${pldfile} ./ 26 | COMMAND ${WINE} ${CUPL_EXE} -l -b -m4 -u ${CUPL_DL} ${pldfile} 27 | ) 28 | 29 | # Run the fitter on the netlist 30 | add_custom_command( 31 | OUTPUT ${stem}.jed 32 | BYPRODUCTS ${stem}.fit ${stem}.io ${stem}.pin ${stem}.tt3 33 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${stem}.tt2 34 | COMMAND ${WINE} ${FIT1502_EXE} ${stem}.tt2 -CUPL -device ${device} 35 | ) 36 | 37 | # Use prjbureau to generate a JTAG SVF file without having to mess with ATMISP 38 | add_custom_command( 39 | OUTPUT ${stem}.svf 40 | DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${stem}.jed prjbureau 41 | COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${PRJBUREAU_DIR} 42 | ${PRJBUREAU_DIR}/venv/bin/python3 -m util.fuseconv ${stem}.jed ${stem}.svf 43 | ) 44 | 45 | add_custom_target(pld_${stem} DEPENDS ${stem}.svf) 46 | add_dependencies(pld pld_${stem}) 47 | endfunction() 48 | 49 | if(${WINE} STREQUAL "WINE-NOTFOUND") 50 | message(NOTICE "Wine not found, skipping PLD targets") 51 | else() 52 | message(STATUS "Found Wine: ${WINE}") 53 | if(${CUPL_EXE} STREQUAL "CUPL_EXE-NOTFOUND") 54 | message(NOTICE "WinCUPL not found, skipping PLD targets") 55 | else() 56 | message(STATUS "Found CUPL: ${CUPL_EXE}") 57 | add_custom_target(pld ALL) 58 | 59 | # prjbureau is 'experimental' and deliberately not packaged. Clone the 60 | # repo and set up a virtualenv to run it in 61 | set(PRJBUREAU_DIR ${CMAKE_CURRENT_BINARY_DIR}/prjbureau) 62 | ExternalProject_Add( 63 | prjbureau 64 | SOURCE_DIR ${PRJBUREAU_DIR} 65 | GIT_REPOSITORY https://github.com/whitequark/prjbureau.git 66 | GIT_TAG 82b94c6776144ceb315e0879a0222b7bea24a067 67 | CONFIGURE_COMMAND "" 68 | BUILD_COMMAND 69 | python3 -m virtualenv ${PRJBUREAU_DIR}/venv && 70 | ${PRJBUREAU_DIR}/venv/bin/pip install bitarray 71 | INSTALL_COMMAND "" 72 | ) 73 | endif() 74 | endif() 75 | 76 | add_subdirectory(se) 77 | add_subdirectory(se30) 78 | -------------------------------------------------------------------------------- /software/tools/programROM/flash.c: -------------------------------------------------------------------------------- 1 | /* 2 | Utility functions for programming JEDEC parallel flash memories 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "flash.h" 20 | 21 | #include 22 | 23 | enum { 24 | cmd_chiperase = 0x10, 25 | cmd_byteerase = 0x30, 26 | cmd_erase = 0x80, 27 | cmd_id = 0x90, 28 | cmd_bytewrite = 0xa0, 29 | cmd_idexit = 0xf0 30 | }; 31 | 32 | /* Send JEDEC '3-byte write' command to device at base_address */ 33 | static void flash_3byte(volatile unsigned char* base_address, 34 | unsigned char command) { 35 | *(base_address + 0x5555) = 0xaa; 36 | *(base_address + 0x2aaa) = 0x55; 37 | *(base_address + 0x5555) = command; 38 | } 39 | 40 | int flash_probe(volatile unsigned char* base_address, flash_id* id) { 41 | unsigned char origdata_0 = *base_address; 42 | unsigned char origdata_1 = *(base_address + 1); 43 | unsigned char manufacturer, device; 44 | 45 | flash_3byte(base_address, cmd_id); 46 | manufacturer = *base_address; 47 | device = *(base_address + 1); 48 | flash_3byte(base_address, cmd_idexit); 49 | 50 | if (manufacturer == origdata_0 && device == origdata_1) { 51 | return -1; 52 | } 53 | 54 | if (id != NULL) { 55 | id->manufacturer = manufacturer; 56 | id->device = device; 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | void flash_erase(volatile unsigned char* base_address) { 63 | flash_3byte(base_address, cmd_erase); 64 | flash_3byte(base_address, cmd_chiperase); 65 | while (*base_address != 0xff) { 66 | }; 67 | } 68 | 69 | static void flash_writebyte(volatile unsigned char* base_address, 70 | unsigned int offset, unsigned char data) { 71 | flash_3byte(base_address, cmd_bytewrite); 72 | *(base_address + offset) = data; 73 | 74 | while (*(base_address + offset) != data) { 75 | }; 76 | } 77 | 78 | int flash_write(volatile unsigned char* base_address, unsigned int offset, 79 | unsigned char* data, unsigned int len) { 80 | for (unsigned int i = 0; i < len; i++) { 81 | flash_writebyte(base_address, offset + i, data[i]); 82 | } 83 | for (unsigned int i = 0; i < len; i++) { 84 | if (*(base_address + offset + i) != data[i]) { 85 | return -1; 86 | } 87 | } 88 | return 0; 89 | } 90 | 91 | int flash_program(volatile unsigned char* base_address, unsigned int offset, 92 | unsigned char* data, unsigned int len) { 93 | flash_erase(base_address); 94 | return flash_write(base_address, offset, data, len); 95 | } -------------------------------------------------------------------------------- /software/driver/isrwrapper.S: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "macsbug.inc" 20 | 21 | #if defined(TARGET_SE) 22 | #include "sethernet_board_defs.h" 23 | #include "enc624j600_registers.h" 24 | #endif 25 | 26 | /* 27 | Assembly-language wrappers for calling ISRs. 28 | */ 29 | 30 | .global isrWrapper 31 | 32 | RTS /* Extra RTS to ensure that MacsBug's label starts in the right place */ 33 | 34 | isrWrapper: 35 | #if defined(TARGET_SE30) 36 | /* SE/30: isrWrapper is called by the Slot Manager to service card interrupts. 37 | All it needs to do is preserve a few registers that C doesn't save (Slot Manager 38 | ISRs must preserve all registers except A1 and D0), and call our 'real' ISR */ 39 | MOVEM.L %a0/%d1-%d2, -(%sp) 40 | JSR driverISR 41 | MOVEM.L (%sp)+, %a0/%d1-%d2 42 | RTS 43 | 44 | #elif defined(TARGET_SE) 45 | /* SE: isrWrapper has to do a bit more. Because we don't have the Slot Manager 46 | to handle interrupts for us, we 'hijack' the Level 1 interrupt vector to point 47 | to isrWrapper, which has the job of checking if the interrupt was for us or not, 48 | and calling either our driverISR routine, or jumping through the original 49 | interrupt vector that we saved. */ 50 | 51 | /* 52 | Check to see if the interrupt came from our card 53 | 54 | This is kinda ugly. Bit 7 of the 16-bit ESTAT register indicates interrupt 55 | status (the datasheet says bit 15, but register bytes are swapped because 56 | the ENC624J600 is little endian). For memory operands, the BTST instruction 57 | only operates on bytes, not whole words, so we actually want to test bit 7 58 | of the byte at ESTAT+1. Trust me. 59 | */ 60 | BTST #7, SETHERNET_BASEADDR + ESTAT + 1 61 | JEQ not_us 62 | 63 | /* We got an interrupt! Save registers and call our handler. In this case we 64 | must return with ALL registers preserved, so we need to save all of C's 65 | unsaved registers */ 66 | MOVEM.L %a0-%a1/%d0-%d2, -(%sp) 67 | MOVE.L isrGlobals, %a1 /* driverISR takes pointer to globals in A1 */ 68 | JSR driverISR 69 | MOVEM.L (%sp)+, %a0-%a1/%d0-%d2 70 | RTE /* We're returning from an interrupt, so we use RTE, not RTS */ 71 | 72 | not_us: 73 | /* The ESTAT_INT bit was clear - this means that the interrupt came from 74 | something else, presumably the VIA or SCSI controller. 75 | 76 | Call the original interrupt handler through the vector that we saved. The 77 | "push address and RTS" trick saves us having to use (and subsequently 78 | restore) a register. */ 79 | MOVE.L originalInterruptVector, -(%sp) 80 | RTS 81 | #endif 82 | MacsbugSymbol isrWrapper 83 | -------------------------------------------------------------------------------- /installer/comments.r: -------------------------------------------------------------------------------- 1 | #include "InstallerTypes.r" 2 | #include "Types.r" 3 | #include "identifiers.r" 4 | #include "version.h" 5 | 6 | resource 'icmt' (cmAppleTalkSystem6) { 7 | 0xA9B68880, 8 | 5183, 9 | 9000, 10 | "This installation will update System 6 (version 6.0.4 and higher) to AppleTalk P" 11 | "hase 2 version 58.1.3. " 12 | }; 13 | 14 | resource 'icmt' (cmAppleTalkSystem7) { 15 | 0xA9B68880, 16 | 5813, 17 | 9000, 18 | "This installation will update System 7 to AppleTalk Phase 2 version 58.1.3." 19 | }; 20 | 21 | #if 0 22 | resource 'icmt' (cmEtherTalkNB) { 23 | 0xA9B68880, 24 | 605, 25 | 9110, 26 | "This installs EtherTalk Phase 2 (version 2.5.6) and the Ethernet driver for the " 27 | "EtherTalk NB Card. If you are doing a custom install, be sure to install AppleT" 28 | "alk version 58.1.3 as well." 29 | }; 30 | #endif 31 | 32 | resource 'icmt' (cmSEthernet30) { 33 | currentDate, 34 | VERSION_MAJOR * 100 + ((VERSION_MINOR & 0xf0) >> 4) * 10 + (VERSION_MINOR & 0x0f), 35 | 9110, 36 | "This installs EtherTalk Phase 2 (version 2.5.6) and the Ethernet driver for the " 37 | "SEthernet/30 Card. If you are doing a custom install, be sure to install AppleT" 38 | "alk version 58.1.3 as well." 39 | }; 40 | 41 | resource 'icmt' (cmSEthernet) { 42 | currentDate, 43 | VERSION_MAJOR * 100 + ((VERSION_MINOR & 0xf0) >> 4) * 10 + (VERSION_MINOR & 0x0f), 44 | 9110, 45 | "This installs EtherTalk Phase 2 (version 2.5.6) and the Ethernet driver for the " 46 | "SEthernet Card. If you are doing a custom install, be sure to install AppleTalk" 47 | " version 58.1.3 as well." 48 | }; 49 | 50 | resource 'icmt' (cmMacTCP) { 51 | 2977967700, 52 | 210, 53 | 13000, 54 | "This installs MacTCP (version 2.1)." 55 | }; 56 | 57 | resource 'ICON' (9000) { 58 | $"0000 0000 07FF FFE0 0800 0010 0800 0010 08FF FF10 0900 0090 0955 5090 0900 0090" 59 | $"0955 0090 0900 0090 0950 0090 0900 0090 0950 0090 0900 0090 0954 0090 0900 0090" 60 | $"0900 0090 08FF FF10 0800 0010 0800 0010 0800 0010 0800 0010 0800 FF10 0800 0010" 61 | $"0800 0010 0800 0010 0800 0010 07FF FFE0 0400 0020 0400 0020 0400 0020 07FF FFE0" 62 | }; 63 | 64 | resource 'ICON' (9110) { 65 | $"0000 0000 0000 0000 0000 2200 0000 5500 0000 8880 0000 4440 0000 2220 37BF F110" 66 | $"0000 0088 0DE0 0044 0000 0088 037B F110 0000 2220 0000 4440 0000 8880 0044 5500" 67 | $"00AA 2200 0111 0000 0222 0000 0444 0000 088F FDEC 1100 0000 2200 07B0 1100 0000" 68 | $"088F DEC0 0444 0000 0222 0000 0111 0000 00AA 0000 0044" 69 | }; 70 | 71 | /* 72 | resource 'ICON' (9130) { 73 | $"0000 0000 0000 0000 0000 0000 1E07 F000 1238 0E00 1240 0100 1280 0000 1301 C040" 74 | $"120E 3120 1010 0490 1020 0240 107E 0108 1002 0020 1002 0080 1FFE 0000 0000 0000" 75 | $"0000 0000 0000 7FF8 0100 4008 0400 4008 1080 7E08 0240 0408 0920 0808 048C 7048" 76 | $"0203 80C8 0000 0148 0080 0248 0070 1C48 000F E078" 77 | }; 78 | 79 | resource 'ICON' (9463) { 80 | $"FFFF FFFF 8008 0001 8014 0001 8022 0001 8041 0001 8080 8001 81E3 C001 8222 0001" 81 | $"8622 0001 8A22 0001 93E3 C001 A022 3001 C022 0801 A022 0801 93E3 8441 8A22 4461" 82 | $"8622 4451 8221 C7C9 8010 4405 8010 4403 800C 4405 8003 C7C9 8000 4451 8000 4461" 83 | $"8000 4441 8003 C781 8001 0101 8000 8201 8000 4401 8000 2801 8000 1001 FFFF FFFF" 84 | }; 85 | */ 86 | 87 | resource 'ICON' (13000) { 88 | $"00F8 4000 0306 6000 06FF D000 0D80 0800 0AFF D000 1541 6000 1288 C000 1105 4000" 89 | $"120A C000 3545 4000 5FFA 8000 800D 8360 5FF9 0490 3346 0248 10DB FF24 0040 0012" 90 | $"0056 E009 0050 0012 0051 BF24 0050 0248 0050 0490 0051 B360 0052 4800 0054 9000" 91 | $"00E9 3FF6 0092 0000 00A4 01D8 00D2 0000 00A9 3F60 BF24 9000 0052 4800 BF8D B0", 92 | }; 93 | -------------------------------------------------------------------------------- /software/tools/showDrivers/showDrivers.c: -------------------------------------------------------------------------------- 1 | /* Quick 'n dirty tool to show the contents of the driver table */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Convert a length-prefixed Pascal string at pstr into a null-terminated C 10 | string at cstr */ 11 | unsigned char pToCStr(Str255 pstr, char *cstr) { 12 | const unsigned char len = pstr[0]; 13 | memmove (cstr, pstr+1, len); 14 | cstr[len] = '\0'; 15 | return len; 16 | } 17 | 18 | int main(int argc, char ** argv) { 19 | (void) argc; 20 | (void) argv; 21 | 22 | short tableSize = LMGetUnitTableEntryCount(); 23 | Ptr tableBase = LMGetUTableBase(); 24 | DCtlHandle dCtlH; 25 | short numDrivers = 0; 26 | char strbuf[256]; 27 | 28 | printf("Driver table at 0x%08x, length %d entries\n", (unsigned int) tableBase, tableSize); 29 | printf("Unit RefNum Flags DRVRAddr Name\n"); 30 | for (short unitNum = 0; unitNum < tableSize; unitNum++) { 31 | short refNum = ~unitNum; 32 | char flagStr[] = "ltgscwrahov"; 33 | dCtlH = GetDCtlEntry(refNum); 34 | if (dCtlH != nil) { 35 | short flags = (*dCtlH)->dCtlFlags; 36 | Ptr driver; 37 | numDrivers++; 38 | 39 | if (flags & dNeedLockMask) { 40 | flagStr[0] = 'L'; 41 | } 42 | if (flags & dNeedTimeMask) { 43 | flagStr[1] = 'T'; 44 | } 45 | if (flags & dNeedGoodByeMask) { 46 | flagStr[2] = 'G'; 47 | } 48 | if (flags & dStatEnableMask) { 49 | flagStr[3] = 'S'; 50 | } 51 | if (flags & dCtlEnableMask) { 52 | flagStr[4] = 'C'; 53 | } 54 | if (flags & dWritEnableMask) { 55 | flagStr[5] = 'W'; 56 | } 57 | if (flags & dReadEnableMask) { 58 | flagStr[6] = 'R'; 59 | } 60 | if (flags & drvrActiveMask) { 61 | flagStr[7] = 'A'; 62 | } 63 | if (flags & dRAMBasedMask) { 64 | /* Does not seem to necessarily indicate whether driver is in 65 | RAM or not!!! Should be interpreted as whether the driver is 66 | referenced by a pointer (0) or a handle (1) */ 67 | flagStr[8] = 'H'; 68 | driver = * (Handle) ((*dCtlH)->dCtlDriver); 69 | 70 | if (flags & dNeedLockMask) { 71 | /* If the handle is not locked, the driver code resource may 72 | have been purged. Make sure it's in memory before we examine 73 | it! */ 74 | LoadResource((Handle) driver); 75 | } 76 | } else { 77 | driver = (*dCtlH)->dCtlDriver; 78 | } 79 | if (flags & dOpenedMask) { 80 | flagStr[9] = 'O'; 81 | } 82 | if (flags & dVMImmuneMask) { 83 | flagStr[10] = 'V'; 84 | } 85 | 86 | /* Driver name is 18 bytes from the start of the driver */ 87 | pToCStr((unsigned char *) driver + 18, strbuf); 88 | printf("%4d %4d %s 0x%08x %s\n", unitNum, refNum, flagStr, (unsigned int) driver, strbuf); 89 | 90 | if (numDrivers > 0 && numDrivers % 20 == 0) { 91 | printf("Press RETURN for more...\n"); 92 | while (getchar() != 'n') {} 93 | } 94 | } 95 | } 96 | 97 | printf("\n%d total drivers\n", numDrivers); 98 | printf("Press RETURN to exit...\n"); 99 | while (getchar() != 'n') {} 100 | 101 | return 0; 102 | } -------------------------------------------------------------------------------- /software/driver/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | INCLUDE(CheckIncludeFile) 2 | CHECK_INCLUDE_FILE("ENET.h" HAVE_ENET_H) 3 | IF(NOT HAVE_ENET_H) 4 | message(FATAL_ERROR "ENET.h not found. Are you sure you built Retro68 with Apple Universal Interfaces?") 5 | ENDIF() 6 | 7 | # Build drivers as code resources; PC-relative (no relocation necessary) flat 8 | # binaries with no startup code 9 | add_link_options(-Wl,--mac-flat -nostartfiles -e header_start) 10 | 11 | set(DRIVER_SOURCES 12 | driver.c 13 | header.S 14 | isr.c 15 | isrwrapper.S 16 | multicast.c 17 | protocolhandler.c 18 | readpacket.S 19 | util.c 20 | ) 21 | 22 | add_library(driver_control INTERFACE) 23 | target_include_directories(driver_control INTERFACE include/) 24 | 25 | # With the SE, the ethernet driver needs to be a 'DRVR' code resource. In theory 26 | # we could create an INIT to load this into the driver table at startup, but for 27 | # now we can just yeet it into the System file with ResEdit. 28 | set(SE_RESOURCE SE_driver.resource) 29 | add_executable(${SE_RESOURCE} ${DRIVER_SOURCES}) 30 | # Tune SE driver optimizations for 68000 specifically 31 | target_compile_options(${SE_RESOURCE} PRIVATE -m68000 -mtune=68000) 32 | # Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 causing a 33 | # spurious warning (introduced in GCC 12, fixed in GCC 14) 34 | if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12 AND 35 | CMAKE_C_COMPILER_VERSION VERSION_LESS 14) 36 | target_compile_options(${SE_RESOURCE} PRIVATE --param=min-pagesize=0) 37 | endif() 38 | target_compile_definitions(${SE_RESOURCE} PRIVATE TARGET_SE) 39 | target_link_libraries(${SE_RESOURCE} enc624j600 driver_control board_defs version) 40 | add_custom_command( 41 | OUTPUT SEthernet.bin 42 | COMMAND ${REZ} -I ${REZ_INCLUDE_PATH} 43 | -I "$" 44 | ${CMAKE_CURRENT_SOURCE_DIR}/SEthernet.r 45 | -o SEthernet.bin 46 | -t rsrc 47 | -c RSED 48 | 49 | DEPENDS ${SE_RESOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/SEthernet.r) 50 | add_custom_target(SEthernet ALL DEPENDS SEthernet.bin) 51 | 52 | # SE30 driver is a lot more civilised. We create a code resource of type 'enet' 53 | # with an ID matching our Board ID, and the Slot Manager and .ENET driver will 54 | # take care of the rest. If we put the resource in a file of type 'comd', we can 55 | # just drop it in the Extensions folder and (so long as we have run Network 56 | # Software Installer 1.5 to update our .ENET driver) it'll get loaded. 57 | set(SE30_RESOURCE SE30_driver.resource) 58 | add_executable(${SE30_RESOURCE} ${DRIVER_SOURCES}) 59 | # Tune SE/30 driver optimizations for 68030 specifically 60 | target_compile_options(${SE30_RESOURCE} PRIVATE -m68030 -mtune=68030) 61 | # Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105523 causing a 62 | # spurious warning (introduced in GCC 12, fixed in GCC 14) 63 | if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12 AND 64 | CMAKE_C_COMPILER_VERSION VERSION_LESS 14) 65 | target_compile_options(${SE30_RESOURCE} PRIVATE --param=min-pagesize=0) 66 | endif() 67 | target_compile_definitions(${SE30_RESOURCE} PRIVATE TARGET_SE30) 68 | # Link SE/30 driver against 68030-optimized version of enc624j600 library 69 | target_link_libraries(${SE30_RESOURCE} enc624j600_030 driver_control board_defs version) 70 | add_custom_command( 71 | OUTPUT SEthernet30.bin 72 | COMMAND ${REZ} -I ${REZ_INCLUDE_PATH} 73 | -I "$" 74 | -I "$" 75 | ${CMAKE_CURRENT_SOURCE_DIR}/SEthernet30.r 76 | -o SEthernet30.bin 77 | -t comd 78 | -c Enet 79 | 80 | DEPENDS ${SE30_RESOURCE} ${CMAKE_CURRENT_SOURCE_DIR}/SEthernet30.r) 81 | add_custom_target(SEthernet30 ALL DEPENDS SEthernet30.bin) 82 | -------------------------------------------------------------------------------- /software/driver/include/sethernet.h: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include 20 | #pragma once 21 | 22 | /* Non-standard Control csCodes used by our driver */ 23 | enum { 24 | ENCReadReg = 0x7000, /* Read register, csParam is encRegister* */ 25 | ENCWriteReg = 0x7001, /* Write register, csParam is encRegister* */ 26 | ENCReadPhy = 0x7002, /* Read PHY register, csParam is encRegister* */ 27 | ENCWritePhy = 0x7003, /* Write PHY register, csParam is encRegister* */ 28 | 29 | ENCEnableLoopback = 0x7004, /* Set PHY to loopback mode, csParam unused */ 30 | ENCDisableLoopback = 0x7005 /* Take PHY out of loopback, csParam unused */ 31 | }; 32 | 33 | /* Register address-value pair used for register-access Control calls */ 34 | struct encRegister { 35 | unsigned short reg; 36 | unsigned short value; 37 | }; 38 | typedef struct encRegister encRegister; 39 | 40 | /* 41 | Information returned by EGetInfo 42 | 43 | 'Standard' EGetInfo responses are defined for EtherTalk NB and SONIC cards. We 44 | use the more detailed SONIC format, plus some of our own fields tacked on to the 45 | end. 46 | */ 47 | struct driverInfo { 48 | Byte ethernetAddress[6]; /* Our ethernet address */ 49 | 50 | Byte unused[12]; 51 | 52 | /* Fields supported by SONIC-type cards */ 53 | unsigned long txFrameCount; /* Frames transmitted without error */ 54 | unsigned long 55 | singleCollisionFrames; /* Frames transmitted with 1 collision retry */ 56 | unsigned long multiCollisionFrames; /* Frames transmitted with multiple 57 | collision retries */ 58 | unsigned long collisionFrames; /* Total frames transmitted with collisions */ 59 | unsigned long deferredFrames; /* Total frames transmitted with deferrals */ 60 | unsigned long lateCollisions; /* Transmit failures due to late collision */ 61 | unsigned long 62 | excessiveCollisions; /* Transmit failures due to excessive collisions */ 63 | unsigned long 64 | excessiveDeferrals; /* Transmit failures due to excessive deferrals */ 65 | unsigned long internalTxErrors; /* Transmit failures due to internal errors 66 | (anything other than the above) */ 67 | unsigned long rxFrameCount; /* Frames received without error */ 68 | unsigned long multicastRxFrameCount; /* Multicast frames received */ 69 | unsigned long broadcastRxFrameCount; /* Broadcast frames received */ 70 | unsigned long fcsErrors; /* Receive failure due to checksum errors (not 71 | reported by default on ENC624J600) */ 72 | unsigned long alignmentErrors; /* Receive failures due to alignment errors 73 | (not reported by ENC624J600) */ 74 | unsigned long internalRxErrors; /* Receive failures due to internal errors 75 | (buffer overflow or receive canceled) */ 76 | 77 | /* Fields specific to SEthernet */ 78 | unsigned long 79 | rxPendingBytesHWM; /* Maximum number of bytes pending in receive FIFO */ 80 | unsigned long rxPendingPacketsHWM; /* Maximum number of packets pending in 81 | receive FIFO */ 82 | unsigned long rxBadLength; /* 802.2 Type 1 frames with bad length field */ 83 | unsigned long rxRunt; /* Runt frames (<64 bytes) received */ 84 | unsigned long rxTooLong; /* Too-long frames (>1518 bytes) received */ 85 | unsigned long 86 | rxUnwanted; /* Frames received with an 'unwanted' destination address 87 | (likely hash collisions in the multicast table) */ 88 | unsigned long rxUnknownProto; /* Packets received with an unknown protocol */ 89 | } __attribute__((packed)); 90 | typedef struct driverInfo driverInfo; 91 | -------------------------------------------------------------------------------- /rom/se30/gencrc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import itertools 5 | import struct 6 | import sys 7 | 8 | SRESOURCE_HEADER_MAGIC = 0x5a932bc7 9 | 10 | def checksum(data): 11 | sum = 0 12 | for byte in data: 13 | sum = ((sum << 1) & 0xffffffff) | (sum >> (31)) # 32-bit Rotate Left 14 | sum = (sum + byte) & 0xffffffff # Ensure 32-bit add with overflow 15 | 16 | return sum 17 | 18 | def main(): 19 | parser = argparse.ArgumentParser(description='Tool for sanity-checking declaration ROM headers and writing the CRC field.') 20 | parser.add_argument('rom_image', help='Declaration ROM image') 21 | parser.add_argument('--output', '-o', type=str, help='Output ROM image') 22 | parser.add_argument('--pad', '-p', type=int, metavar='SIZE', help='Prepend zeros to pad image out to SIZE bytes') 23 | parser.add_argument('--verbose', '-v', action='store_true', help='Show ROM information.') 24 | args = parser.parse_args() 25 | 26 | if args.pad is not None and not args.output: 27 | parser.error('Cannot add padding (--pad/-p) without an output file (--output/-o) to write to!') 28 | 29 | with open(args.rom_image, 'rb') as f: 30 | rom = f.read() 31 | header = struct.unpack('>LLLBBLBB', rom[-20:]) # Unpack as big-endian 32 | file_end = len(rom) 33 | 34 | # directory_offset is a 24 bit signed value; do some bit twiddling to sign-extend it 35 | directory_offset = header[0] if not (header[0] & 0x800000) else header[0] - 0x1000000 36 | crc_length = header[1] 37 | crc = header[2] 38 | romRevision = header[3] 39 | romFormat = header[4] 40 | magic = header[5] 41 | reservedZero = header[6] 42 | byteLanes = header[7] 43 | 44 | if magic != SRESOURCE_HEADER_MAGIC: 45 | print("Magic number is wrong (0x{:08x}, should be 0x{:08x})".format(magic, SRESOURCE_HEADER_MAGIC), file=sys.stderr) 46 | sys.exit(1) 47 | 48 | if reservedZero != 0: 49 | print("Reserved zero value before ByteLanes is nonzero.", file=sys.stderr) 50 | sys.exit(1) 51 | 52 | if (byteLanes & 0x0f) != ((~byteLanes >> 4) & 0x0f): 53 | print("Invalid ByteLanes value 0x{:02x}.".format(byteLanes), file=sys.stderr) 54 | print("Low and high nybbles must be the inverse of each other", file=sys.stderr) 55 | sys.exit(1) 56 | 57 | # directory_offset value is relative to its location in the header (20 bytes from the end) 58 | directory_location = (file_end - 20) + directory_offset 59 | 60 | if directory_location < 0 or directory_location > file_end: 61 | print("Bad directory offset {} points to address outside of ROM image.".format(directory_offset), file=sys.stderr) 62 | sys.exit(1) 63 | 64 | if (directory_location + crc_length) != file_end: 65 | print("CRC length value {} does not match directory location and size of ROM image ({}).".format(crc_length, file_end), file=sys.stderr) 66 | sys.exit(1) 67 | 68 | if args.verbose or not args.output: 69 | print("Directory at address 0x{:x} (offset {})".format(directory_location, directory_offset)) 70 | print("CRC length: {} bytes".format(crc_length)) 71 | print("CRC: 0x{:08x}".format(crc)) 72 | print("ROM revision: {}".format(romRevision)) 73 | print("ROM format: {}".format(romFormat)) 74 | print("Byte lanes: 0x{:02x}".format(byteLanes)) 75 | 76 | crcdata = rom[file_end - crc_length:] 77 | # CRC calculation treats checksum field as zero bytes 78 | calc_crc = checksum(itertools.chain(crcdata[0:-12], [0]*4, crcdata[-8:])) 79 | 80 | if crc != calc_crc and not args.output: 81 | print("CRC field 0x{:08x} does not match calculated CRC 0x{:08x}".format(crc, calc_crc), file=sys.stderr) 82 | print("Use --output argument to write new CRC value", file=sys.stderr) 83 | sys.exit(1) 84 | 85 | if args.output and args.verbose and crc != calc_crc: 86 | print("CRC field updated to 0x{:08x}".format(calc_crc)) 87 | 88 | if args.pad is not None and args.pad < len(rom): 89 | print('Cannot pad a ROM image to smaller than its actual size!', file=sys.stderr) 90 | sys.exit(1) 91 | 92 | if args.pad is not None and args.verbose: 93 | print('Adding {} bytes of padding to {} bytes of data'.format(args.pad - len(rom), len(rom))) 94 | 95 | if args.output: 96 | with open(args.output, 'wb') as f: 97 | if args.pad is not None: 98 | # Prepend with zero padding to reach desired size 99 | f.seek(args.pad - len(rom)) 100 | 101 | f.write(rom[0:-12]) 102 | f.write(struct.pack('>L', calc_crc)) 103 | f.write(rom[-8:]) 104 | 105 | if __name__ == '__main__': 106 | main() 107 | -------------------------------------------------------------------------------- /software/tools/programROM/programROM.c: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet/30 declaration ROM utility 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "buserror.h" 24 | #include "enc624j600.h" 25 | #include "flash.h" 26 | #include "se30-u2.h" 27 | 28 | /* Start of SEthernet/30 declaration ROM within slot space */ 29 | #define DECLROM_OFFSET 0xff0000 30 | 31 | /* Turn a slot number and an offset into slot space, into a pointer */ 32 | static inline unsigned char *slotptr(unsigned char slot, unsigned int offset) { 33 | if (GetMMUMode() == true32b) { 34 | /* 32 bit mode: slot space is 16MB, addressed at 0xFSxxxxxx*/ 35 | return (unsigned char *)0xf0000000 + (slot << 24) + offset; 36 | } else { 37 | /* 24 bit mode: slot space is 1MB, addressed at 0xSxxxxx*/ 38 | return (unsigned char *)(slot << 20) + (offset & 0xfffff); 39 | } 40 | } 41 | 42 | /* 43 | Check to see which slots appear to have something in them. We can't use the 44 | regular Slot Manager calls for this because we need to support the case where a 45 | card has a blank or invalid declaration ROM 46 | 47 | TODO: be smarter about this. We should only offer slots that we KNOW are 48 | SEthernet/30s (i.e. have an SEthernet/30 board ID), or occupied slots that the 49 | Slot Manager has no data on (likely an SEthernet/30 with a blank ROM) 50 | */ 51 | unsigned int probe_slots(void) { 52 | unsigned int result = 0; 53 | 54 | for (unsigned int slot = 9; slot < 0xf; slot++) { 55 | /* A valid slot will have the first byte of its declaration rom 56 | somewhere in the top of the slot address space */ 57 | for (int offset = DECLROM_OFFSET+0xffff; offset >= DECLROM_OFFSET+0xfff0; 58 | offset--) { 59 | if (buserror_probe(slotptr(slot, offset)) >= 0) { 60 | result |= 1 << slot; 61 | break; 62 | } 63 | } 64 | } 65 | return result; 66 | } 67 | 68 | /* Try to detect an ENC624J600 */ 69 | int check_slot(int slot) { 70 | enc624j600 chip = {.base_address = slotptr(slot, 0)}; 71 | 72 | return enc624j600_detect(&chip); 73 | } 74 | 75 | int main(__attribute__((unused)) int argc, 76 | __attribute__((unused)) char **argv) { 77 | unsigned int occupied_slots; 78 | unsigned int slot; 79 | unsigned char ch; 80 | printf("**** SEthernet/30 ROM programming tool ****\n\n"); 81 | 82 | redo: 83 | occupied_slots = probe_slots(); 84 | printf("Occupied slots: "); 85 | for (slot = 9; slot < 0xf; slot++) { 86 | if (occupied_slots & (1 << slot)) { 87 | printf("%x ", slot); 88 | } 89 | } 90 | printf("\n"); 91 | printf("Select a slot to program, or Q to quit:\n"); 92 | ch = getchar(); 93 | switch (ch) { 94 | case '9': 95 | slot = 0x9; 96 | break; 97 | case 'a': 98 | case 'A': 99 | slot = 0xa; 100 | break; 101 | case 'b': 102 | case 'B': 103 | slot = 0xb; 104 | break; 105 | case 'c': 106 | case 'C': 107 | slot = 0xc; 108 | break; 109 | case 'd': 110 | case 'D': 111 | slot = 0xd; 112 | break; 113 | case 'e': 114 | case 'E': 115 | slot = 0xe; 116 | break; 117 | case 'q': 118 | case 'Q': 119 | return 0; 120 | default: 121 | goto redo; 122 | } 123 | 124 | if (!(occupied_slots & (1 << slot))) { 125 | goto redo; 126 | } 127 | 128 | printf( 129 | "Are you SURE you want to program the Declaration ROM for Slot %x? " 130 | "(Y/N)\n", 131 | slot); 132 | do { 133 | ch = toupper(getchar()); 134 | } while (ch != 'Y' && ch != 'N'); 135 | if (ch != 'Y') { 136 | goto redo; 137 | } 138 | getchar(); /* eat newline */ 139 | 140 | if (check_slot(slot) != 0) { 141 | printf( 142 | "Slot %x does not seem to have an ENC624J600 chip present. Bailing " 143 | "out.\n", 144 | slot); 145 | goto redo; 146 | } 147 | 148 | if (flash_program(slotptr(slot, DECLROM_OFFSET), 0, se30_u2_rom, 149 | se30_u2_rom_len) == 0) { 150 | printf("Programming completed.\n"); 151 | } else { 152 | printf("Programming failed!\n"); 153 | } 154 | printf("Please restart your computer after running this application.\n"); 155 | 156 | printf("Press RETURN to exit\n"); 157 | getchar(); 158 | return 0; 159 | } 160 | -------------------------------------------------------------------------------- /installer/filespecs.r: -------------------------------------------------------------------------------- 1 | #include "InstallerTypes.r" 2 | #include "identifiers.r" 3 | 4 | resource 'infs' (fsNetworkResources) { 5 | 'rsrc', 6 | 'RSED', 7 | 0xA9B73140, 8 | noSearchForFile, 9 | TypeCrMustMatch, 10 | "SEthernet Installer:AppleTalk Files:Network Resources" 11 | }; 12 | 13 | resource 'infs' (fsTgtSystem) { 14 | 'zsys', 15 | 'MACS', 16 | 0x0, 17 | noSearchForFile, 18 | TypeCrNeedNotMatch, 19 | "special-macs:System" 20 | }; 21 | 22 | resource 'infs' (fsTgtEnabler201) { 23 | 'gbly', 24 | 'MACS', 25 | 0x0, 26 | noSearchForFile, 27 | TypeCrMustMatch, 28 | "special-macs:System Enabler 201" 29 | }; 30 | 31 | resource 'infs' (fsAppleTalk) { 32 | 'ZSYS', 33 | 'MACS', 34 | 0xA9B73140, 35 | noSearchForFile, 36 | TypeCrMustMatch, 37 | "SEthernet Installer:AppleTalk Files:AppleTalk" 38 | }; 39 | 40 | resource 'infs' (fsResponder) { 41 | 'INIT', 42 | 'wma.', 43 | 0xA9B73140, 44 | noSearchForFile, 45 | TypeCrMustMatch, 46 | "SEthernet Installer:AppleTalk Files:Responder" 47 | }; 48 | 49 | resource 'infs' (fsTgtAppleTalk) { 50 | 'ZSYS', 51 | 'MACS', 52 | 0x0, 53 | noSearchForFile, 54 | TypeCrMustMatch, 55 | "special-macs:AppleTalk" 56 | }; 57 | 58 | resource 'infs' (fsTgtResponder) { 59 | 'INIT', 60 | 'wma.', 61 | 0x0, 62 | noSearchForFile, 63 | TypeCrNeedNotMatch, 64 | "special-macs:Responder" 65 | }; 66 | 67 | resource 'infs' (fsTgtADSP) { 68 | 'INIT', 69 | 'MACS', 70 | 0x0, 71 | noSearchForFile, 72 | TypeCrMustMatch, 73 | "special-macs:ADSP" 74 | }; 75 | 76 | resource 'infs' (fsTgtADSPExtension) { 77 | 'INIT', 78 | 'MACS', 79 | 0x0, 80 | noSearchForFile, 81 | TypeCrMustMatch, 82 | "special-extn:ADSP" 83 | }; 84 | 85 | resource 'infs' (fsTgtAppleTalkExtension) { 86 | 'ZSYS', 87 | 'MACS', 88 | 0x0, 89 | noSearchForFile, 90 | TypeCrMustMatch, 91 | "special-extn:AppleTalk" 92 | }; 93 | 94 | resource 'infs' (fsNetwork) { 95 | 'cdev', 96 | 'atdv', 97 | 0xA9B73140, 98 | noSearchForFile, 99 | TypeCrMustMatch, 100 | "SEthernet Installer:AppleTalk Files:Network" 101 | }; 102 | 103 | resource 'infs' (fsTgtNetworkControl) { 104 | 'cdev', 105 | 'atdv', 106 | 0x0, 107 | noSearchForFile, 108 | TypeCrMustMatch, 109 | "special-ctrl:Network" 110 | }; 111 | 112 | resource 'infs' (fsEtherTalkPhase2) { 113 | 'adev', 114 | 'et20', 115 | 0xA9B73140, 116 | noSearchForFile, 117 | TypeCrMustMatch, 118 | "SEthernet Installer:AppleTalk Files:EtherTalk Phase 2" 119 | }; 120 | 121 | resource 'infs' (fsTgtEtherTalkPhase2Extension) { 122 | 'adev', 123 | 'et20', 124 | 0x0, 125 | noSearchForFile, 126 | TypeCrMustMatch, 127 | "special-extn:EtherTalk Phase 2" 128 | }; 129 | 130 | resource 'infs' (fsTgtEtherTalk) { 131 | 'adev', 132 | 'etlk', 133 | 0x0, 134 | noSearchForFile, 135 | TypeCrMustMatch, 136 | "special-macs:EtherTalk" 137 | }; 138 | 139 | resource 'infs' (fsTgtEtherTalk20) { 140 | 'adev', 141 | 'et20', 142 | 0x0, 143 | noSearchForFile, 144 | TypeCrMustMatch, 145 | "special-macs:EtherTalk2.0" 146 | }; 147 | 148 | resource 'infs' (fsTgtEtherTalk21) { 149 | 'adev', 150 | 'et20', 151 | 0x0, 152 | noSearchForFile, 153 | TypeCrMustMatch, 154 | "special-macs:EtherTalk2.1" 155 | }; 156 | 157 | resource 'infs' (fsTgtEtherTalkExtension) { 158 | 'adev', 159 | 'etlk', 160 | 0x0, 161 | noSearchForFile, 162 | TypeCrMustMatch, 163 | "special-extn:EtherTalk" 164 | }; 165 | 166 | resource 'infs' (fsTgtEtherTalk20Extension) { 167 | 'adev', 168 | 'et20', 169 | 0x0, 170 | noSearchForFile, 171 | TypeCrMustMatch, 172 | "special-extn:EtherTalk2.0" 173 | }; 174 | 175 | resource 'infs' (fsTgtEtherTalk21Extension) { 176 | 'adev', 177 | 'et20', 178 | 0x0, 179 | noSearchForFile, 180 | TypeCrMustMatch, 181 | "special-extn:EtherTalk2.1" 182 | }; 183 | 184 | resource 'infs' (fsSEthernetResources) { 185 | 'rsrc', 186 | 'RSED', 187 | 0, 188 | noSearchForFile, 189 | TypeCrMustMatch, 190 | "SEthernet Installer:SEthernet Files:SEthernet Resources" 191 | }; 192 | 193 | resource 'infs' (fsSEthernet30) { 194 | 'comd', 195 | 'Enet', 196 | 0, 197 | noSearchForFile, 198 | TypeCrMustMatch, 199 | "SEthernet Installer:SEthernet Files:SEthernet/30" 200 | }; 201 | 202 | resource 'infs' (fsTgtSEthernet30) { 203 | 'comd', 204 | 'Enet', 205 | 0, 206 | noSearchForFile, 207 | TypeCrMustMatch, 208 | "special-extn:SEthernet/30" 209 | }; 210 | 211 | resource 'infs' (fsTgtSEthernet30Old) { 212 | 'comd', 213 | 'Enet', 214 | 0, 215 | noSearchForFile, 216 | TypeCrMustMatch, 217 | "special-extn:SEthernet30" 218 | }; 219 | 220 | resource 'infs' (fsMacTCP) { 221 | 'cdev', 222 | 'ztcp', 223 | 0, 224 | noSearchForFile, 225 | TypeCrMustMatch, 226 | "SEthernet Installer:SEthernet Files:MacTCP" 227 | }; 228 | 229 | resource 'infs' (fsTgtMacTCP) { 230 | 'cdev', 231 | 'ztcp', 232 | 0, 233 | noSearchForFile, 234 | TypeCrMustMatch, 235 | "special-ctrl:MacTCP" 236 | }; 237 | 238 | resource 'infs' (fsTgtTCPIP) { 239 | 'cdev', 240 | '????', 241 | 0, 242 | noSearchForFile, 243 | TypeCrNeedNotMatch, 244 | "special-ctrl:TCP/IP" 245 | }; 246 | -------------------------------------------------------------------------------- /software/driver/multicast.c: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "multicast.h" 20 | 21 | #include "enc624j600.h" 22 | #include "util.h" 23 | 24 | #include 25 | 26 | /* Naive CRC32 implementation, used in calculating the multicast-filter hash 27 | table. Doesn't need to be fast or fancy since it's only called when we add or 28 | remove a multicast address */ 29 | static unsigned long crc32(const Byte *data, const unsigned short len) { 30 | const unsigned long polynomial = 0x04c11db7; 31 | unsigned short i, j; 32 | unsigned long byte, crc; 33 | 34 | crc = 0xffffffff; 35 | for (i = 0; i < len; i++) { 36 | byte = data[i]; 37 | for (j = 0; j < 8; j++) { 38 | if ((byte & 1) ^ (crc >> 31)) { 39 | crc <<= 1; 40 | crc ^= polynomial; 41 | } else 42 | crc <<= 1; 43 | byte >>= 1; 44 | } 45 | } 46 | return crc; 47 | } 48 | 49 | /* Find a free entry in the multicast table. Returns a pointer to the first free 50 | entry found, nil if no free entries */ 51 | static multicastEntry* findFreeMulticastEntry( 52 | const driverGlobalsPtr theGlobals) { 53 | for (unsigned short i = 0; i < numberofMulticasts; i++) { 54 | if (theGlobals->multicasts[i].refCount == 0) { 55 | return &theGlobals->multicasts[i]; 56 | } 57 | } 58 | return nil; 59 | } 60 | 61 | /* Generate the 8-byte multicast hash table used by the ENC624J600 and load it 62 | into the chip. See the data sheet for hash table format. */ 63 | static void updateMulticastHashTable(const driverGlobalsPtr theGlobals) { 64 | unsigned short hashTable[4]; 65 | unsigned long hashValue; 66 | 67 | /* Zero out hash table */ 68 | memset(hashTable, 0, 4 * sizeof(unsigned short)); 69 | 70 | for (unsigned short i = 0; i < numberofMulticasts; i++) { 71 | if (theGlobals->multicasts[i].refCount > 0) { 72 | hashValue = crc32(theGlobals->multicasts[i].address.bytes, 6); 73 | 74 | /* Use bits 28:23 of CRC32 as bitwise index into hash table */ 75 | hashValue = (hashValue >> 23) & 0x3f; 76 | hashTable[hashValue >> 4] |= (1 << (hashValue & 0x0f)); 77 | } 78 | } 79 | enc624j600_write_multicast_table(&theGlobals->chip, hashTable); 80 | } 81 | 82 | /* Look up an address in our table of multicast addresses. Returns a pointer to 83 | the multicast table entry if found, nil if no match */ 84 | multicastEntry* findMulticastEntry(const driverGlobalsPtr theGlobals, 85 | const hwAddr *address) { 86 | for (unsigned short i = 0; i < numberofMulticasts; i++) { 87 | if (theGlobals->multicasts[i].refCount > 0) { 88 | if (ethAddrsEqual(address, &theGlobals->multicasts[i].address)) { 89 | return &theGlobals->multicasts[i]; 90 | } 91 | } 92 | } 93 | return nil; 94 | } 95 | 96 | /* 97 | EAddMulti call (a.k.a Control with csCode=ENetAddMulti) 98 | 99 | Add an address to our multicast list 100 | */ 101 | OSStatus doEAddMulti(driverGlobalsPtr theGlobals, const EParamBlkPtr pb) { 102 | multicastEntry* multicastSlot; 103 | hwAddr* addressPtr; 104 | 105 | addressPtr = (hwAddr*)&pb->u.EParms2.eMultiAddr; 106 | /* Check that it is a valid multicast address (bit 0 of first byte is 1) */ 107 | if ((addressPtr->bytes[0] & 0x01) == 0) { 108 | return eMultiErr; 109 | } 110 | /* See if the address is already in our multicast list */ 111 | multicastSlot = findMulticastEntry(theGlobals, addressPtr); 112 | if (multicastSlot != nil) { 113 | /* Yes, increment the refcount of the existing entry */ 114 | multicastSlot->refCount++; 115 | return noErr; 116 | } else { 117 | /* No, try to add it to the list */ 118 | multicastSlot = findFreeMulticastEntry(theGlobals); 119 | if (multicastSlot == nil) { 120 | return eMultiErr; 121 | } 122 | /* Mark entry as in use */ 123 | multicastSlot->refCount = 1; 124 | copyEthAddrs(&multicastSlot->address, addressPtr); 125 | 126 | /* Update the hash table to include new entry */ 127 | updateMulticastHashTable(theGlobals); 128 | return noErr; 129 | } 130 | } 131 | 132 | /* 133 | EDelMulti call (a.k.a. Control with csCode=ENetDelMulti) 134 | 135 | Remove an address from our multicast list 136 | */ 137 | OSStatus doEDelMulti(driverGlobalsPtr theGlobals, const EParamBlkPtr pb) { 138 | multicastEntry* multicastSlot; 139 | hwAddr* addressPtr; 140 | 141 | addressPtr = (hwAddr*)&pb->u.EParms2.eMultiAddr; 142 | /* Find the entry in the list */ 143 | multicastSlot = findMulticastEntry(theGlobals, addressPtr); 144 | if (multicastSlot == nil) { 145 | return eMultiErr; 146 | } 147 | /* Decrement reference count */ 148 | multicastSlot->refCount--; 149 | 150 | /* If no longer in use, update the hash table */ 151 | if (multicastSlot->refCount <= 0) { 152 | updateMulticastHashTable(theGlobals); 153 | } 154 | return (noErr); 155 | } 156 | -------------------------------------------------------------------------------- /boards/shared/PDS-Ethernet.pretty/Kycon-GDTX-S-88_Vertical.kicad_mod: -------------------------------------------------------------------------------- 1 | (footprint "Kycon-GDTX-S-88_Vertical" (version 20221018) (generator pcbnew) 2 | (layer "F.Cu") 3 | (tags "RJ45") 4 | (attr through_hole) 5 | (fp_text reference "REF**" (at 7.87 1.13) (layer "F.SilkS") 6 | (effects (font (size 1 1) (thickness 0.15))) 7 | (tstamp c2d7c3da-8cff-460d-b772-e98f71f655b6) 8 | ) 9 | (fp_text value "Kycon-GDTX-S-88_Vertical" (at 8.01 -7.68) (layer "F.Fab") 10 | (effects (font (size 1 1) (thickness 0.15))) 11 | (tstamp cf949972-c207-45d4-be69-3b4969cb5539) 12 | ) 13 | (fp_text user "${REFERENCE}" (at 8.12 -3.87) (layer "F.Fab") 14 | (effects (font (size 1 1) (thickness 0.15))) 15 | (tstamp b39449b3-d903-4a77-9f16-154591619449) 16 | ) 17 | (fp_line (start -0.58 -10.72) (end -0.58 -9.72) 18 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp ee7abd5b-223f-4ee0-b446-6659506bdeb8)) 19 | (fp_line (start -0.58 -9.72) (end -0.08 -10.22) 20 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 963a5ad1-d94e-4a32-805d-576ecc473817)) 21 | (fp_line (start -0.08 -10.22) (end -0.58 -10.72) 22 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp e87ac24c-6282-4b0f-ae4b-d9cb1c49e72d)) 23 | (fp_line (start 0 -14.01) (end 0 -8.4) 24 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 77171b4f-1049-419b-89c0-9c14979ebe18)) 25 | (fp_line (start 0 -4.8) (end 0 0) 26 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 8eac57f1-469b-4efa-9611-4cb057d4e657)) 27 | (fp_line (start 15.72 -14.01) (end 0 -14.01) 28 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp ad1f3dfb-244c-485d-8744-7c694d6458d4)) 29 | (fp_line (start 15.73 -14.01) (end 15.715 -8.42) 30 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 57c4ab4a-a3e7-40cd-892f-af54add56904)) 31 | (fp_line (start 15.73 -4.92) (end 15.73 0) 32 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 84fc5763-7947-4f14-a1fa-7a999a2f3fc1)) 33 | (fp_line (start 15.73 0) (end 0 0) 34 | (stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 7837b038-319e-4dd0-b118-e4bc723c128d)) 35 | (fp_line (start -1.135 -14.39) (end -1.135 0.36) 36 | (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp a1b38957-24eb-430e-a877-5e54513b9629)) 37 | (fp_line (start -1.135 -14.39) (end 16.865 -14.39) 38 | (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp b2f022fb-d5a9-4825-b5c1-93321a741d44)) 39 | (fp_line (start -1.135 0.36) (end 16.865 0.36) 40 | (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 19f208c5-bd1b-4c51-bcff-ea315b2cc01e)) 41 | (fp_line (start 16.865 -14.39) (end 16.865 0.36) 42 | (stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 6ae92561-40f6-439d-af65-9fafa059ef64)) 43 | (fp_line (start -0.58 -10.72) (end -0.58 -9.72) 44 | (stroke (width 0.12) (type solid)) (layer "F.Fab") (tstamp 8460ac2d-afb1-4f7a-86db-7efc317862c0)) 45 | (fp_line (start -0.58 -9.72) (end -0.08 -10.22) 46 | (stroke (width 0.12) (type solid)) (layer "F.Fab") (tstamp aa4924a4-3e1c-4de7-a103-17a031997b71)) 47 | (fp_line (start -0.08 -10.22) (end -0.58 -10.72) 48 | (stroke (width 0.12) (type solid)) (layer "F.Fab") (tstamp 78354015-844a-46b8-9496-2533ebcc6667)) 49 | (fp_line (start 0.115 -13.89) (end 0.115 -0.12) 50 | (stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 0e37f3f4-9ceb-4cc1-800d-70623f5abf57)) 51 | (fp_line (start 15.615 -13.89) (end 0.115 -13.89) 52 | (stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 25f7ff3c-627c-46bf-a32c-380c0192c4d0)) 53 | (fp_line (start 15.615 -13.89) (end 15.615 -0.12) 54 | (stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 3f110ac1-a29a-44fd-ac1e-e7eb04baee09)) 55 | (fp_line (start 15.615 -0.12) (end 0.115 -0.12) 56 | (stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 8f632cdb-2249-41be-8f14-55d4dabbd3b8)) 57 | (pad "" np_thru_hole circle (at 2.15 -3.87) (size 3.65 3.65) (drill 3.45) (layers "F&B.Cu" "*.Mask") (tstamp 5a397243-1824-4421-84ca-3b152b945f37)) 58 | (pad "" np_thru_hole circle (at 13.58 -3.87) (size 3.65 3.65) (drill 3.45) (layers "F&B.Cu" "*.Mask") (tstamp d616aeb0-7fe9-4ab5-b1ed-73fdc7934f9e)) 59 | (pad "1" thru_hole rect (at 3.42 -10.22) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp f338d81b-ae70-4201-b0cd-15563daf854b)) 60 | (pad "2" thru_hole circle (at 4.69 -12.76) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp 2dc6c613-4cfc-4974-b7bf-b524ca1ce9fc)) 61 | (pad "3" thru_hole circle (at 5.96 -10.22) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp c5e3e9a2-1016-43b2-8c96-1e5b52a48e3f)) 62 | (pad "4" thru_hole circle (at 7.23 -12.76) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp d43eafc6-b42a-4f7f-9ef6-2b5a1b794eed)) 63 | (pad "5" thru_hole circle (at 8.5 -10.22) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp 99f1e348-1d43-49b3-b26e-83eb2efa952f)) 64 | (pad "6" thru_hole circle (at 9.77 -12.76) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp 50e6956a-73e6-43da-ac1f-a93e51ff46a0)) 65 | (pad "7" thru_hole circle (at 11.04 -10.22) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp ae5e1935-0440-4bfb-ae6e-13f6d006a78d)) 66 | (pad "8" thru_hole circle (at 12.31 -12.76) (size 1.5 1.5) (drill 0.9) (layers "*.Cu" "*.Mask") (tstamp 1b4c9259-717d-4d7d-bd6c-eae1789fd432)) 67 | (pad "SH" thru_hole oval (at 0.08 -6.62) (size 2 3) (drill oval 1 2) (layers "*.Cu" "*.Mask") (tstamp 169cf143-adce-45ff-a3e1-ea561d62c576)) 68 | (pad "SH" thru_hole oval (at 15.615 -6.64) (size 2 3) (drill oval 1 2) (layers "*.Cu" "*.Mask") (tstamp 6d8ec07a-4c5c-432b-ab4c-70f2065264db)) 69 | (model "${KIPRJMOD}/../shared/PDS-Ethernet.pretty/3D/GDTX-S-88_rA7.STEP" 70 | (offset (xyz 7.89 0.37 16)) 71 | (scale (xyz 1 1 1)) 72 | (rotate (xyz 0 0 0)) 73 | ) 74 | ) 75 | -------------------------------------------------------------------------------- /installer/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | # hfsutils' text-conversion mode mangles special characters, even on files that 6 | # are already in MacRoman encoding. Work around this by converting line endings 7 | # and encoding externally (see text2mac.py) and then copying the file in 'raw' 8 | # binary mode. Python's encoding conversion is clever enough that it can 9 | # translate Unicode characters like •, ©, ƒ into their MacRoman equivalents. 10 | # 11 | # Usage: hcopy_text 12 | function hcopy_text() { 13 | local source="$1" 14 | local dest="$2" 15 | python3 text2mac.py "$source" - | hcopy -r - "$dest" 16 | # Have to manually set file type to 'TEXT' since we're not using 17 | # text-conversion mode. MPW won't touch source files that aren't of type 18 | # 'TEXT'. 19 | hattrib -t 'TEXT' "$dest" 20 | } 21 | 22 | function errecho() { 23 | echo $@ 1>&2 24 | } 25 | 26 | function usage() { 27 | errecho "Usage: $(basename $0) [-h] [] " 28 | } 29 | 30 | function help() { 31 | usage 32 | errecho "Options:" 1>&2 33 | errecho " -h Show this help text" 34 | errecho " -d Debug mode (disable UserStartup and AutoQuit)" 35 | errecho " -m Mini vMac executable" 36 | errecho " -b System boot image" 37 | errecho " -t Template image for install disk" 38 | } 39 | 40 | DEBUG=0 41 | 42 | # Path to Mini vMac Executable 43 | MINIVMAC=minivmac 44 | 45 | # Base image for build environment 46 | # Assumptions: 47 | # - System 6.0.8 installed to ':System Folder' 48 | # - MPW 3.1 installed to ':MPW 3.1' 49 | # - InstallerTypes.r from Installer 3.4.3 SDK copied to ':MPW 3.1:Interfaces:RIncludes' 50 | # - ScriptCheck tool from Installer 3.4.3 SDK copied to ':MPW 3.1:Tools' 51 | # - MPW Shell set as startup application 52 | # - AutoQuit application at ':AutoQuit' 53 | BOOT_IMAGE="images/buildenv.dsk.gz" 54 | 55 | # 'Template' image for install disk 56 | # Must contain: 57 | # - Installer version 3.4 application 58 | # - Network Software Installer 1.4.4 files under 'AppleTalk Files' 59 | # - MacTCP control panel under 'SEthernet Files' 60 | TEMPLATE_IMAGE="images/template.dsk.gz" 61 | 62 | while getopts "dhm:s:t:" opt; do 63 | case $opt in 64 | d) 65 | DEBUG=1 66 | ;; 67 | h) 68 | help 69 | exit 0 70 | ;; 71 | m) 72 | MINIVMAC="${OPTARG}" 73 | ;; 74 | s) 75 | BOOT_IMAGE="${OPTARG}" 76 | ;; 77 | t) 78 | TEMPLATE_IMAGE="${OPTARG}" 79 | ;; 80 | *) 81 | usage 82 | exit 1 83 | ;; 84 | esac 85 | done 86 | 87 | shift $((OPTIND-1)) 88 | 89 | if [[ $# -ne 2 ]] ; then 90 | usage 91 | exit 1 92 | fi 93 | 94 | BUILDDIR="$1" 95 | OUTPUTIMAGE="$2" 96 | 97 | if [ ! -d "$BUILDDIR" ] ; then 98 | usage 99 | exit 1 100 | fi 101 | 102 | if [[ "${BOOT_IMAGE}" == *.gz ]]; then 103 | gunzip -c "${BOOT_IMAGE}" > buildenv.dsk 104 | else 105 | cp "${BOOT_IMAGE}" buildenv.dsk 106 | fi 107 | 108 | if [[ "${TEMPLATE_IMAGE}" == *.gz ]]; then 109 | gunzip -c "${TEMPLATE_IMAGE}" > "${OUTPUTIMAGE}" 110 | else 111 | cp "${TEMPLATE_IMAGE}" "${OUTPUTIMAGE}" 112 | fi 113 | 114 | # Sources to copy into the build environment 115 | SOURCE_FILES=( 116 | "atoms.r" 117 | "code.r" 118 | "comments.r" 119 | "filespecs.r" 120 | "identifiers.r" 121 | "misc.r" 122 | "packages.r" 123 | "rules.r" 124 | "Build.mpw" 125 | "$BUILDDIR/software/shared/version/include/version.h") 126 | 127 | # Retro68's build system won't let us create files with spaces in their names, 128 | # let alone special characters. Instead of just a list of files to copy, use an 129 | # associative array mapping source MacBinary files to the filename we want it to 130 | # have. Mapping a key to a blank name causes it to be copied as-is without any 131 | # renaming. 132 | declare -A BINARY_FILES=( 133 | ["$BUILDDIR/software/driver/SEthernet.bin"]=':SEthernet Files:SEthernet Resources' 134 | ["$BUILDDIR/software/driver/SEthernet30.bin"]=':SEthernet Files:SEthernet/30' 135 | ["$BUILDDIR/software/tools/programROM/programROM.bin"]=':ProgramROM') 136 | 137 | hmount buildenv.dsk >/dev/null 138 | 139 | if [ ${DEBUG} -ne 1 ]; then 140 | # Replace the Finder with Mini vMac's AutoQuit tool, which will shut down and 141 | # quit Mini vMac when MPW exits 142 | hdel ':System Folder:Finder' 143 | hrename ':AutoQuit' ':System Folder:Finder' 144 | 145 | # Delete the MPW worksheet to clear all the introductory stuff out it; we use 146 | # the worksheet to grab our script output 147 | hdel ':MPW 3.1:Worksheet' 148 | 149 | # Delete the existing MPW UserStartup and replace it with our own 150 | hdel ':MPW 3.1:UserStartup' 151 | hcopy_text UserStartup.mpw ':MPW 3.1:UserStartup' 152 | fi 153 | 154 | # Copy sources into the :Sources directory in the build environment 155 | hmkdir ':Sources' 156 | for f in ${SOURCE_FILES[@]}; do 157 | hcopy_text "$f" ":Sources:$(basename $f)" 158 | done 159 | 160 | humount buildenv.dsk 161 | 162 | # Copy our built artifacts into the installer disk 163 | hmount "${OUTPUTIMAGE}" >/dev/null 164 | # hmkdir 'SEthernet Files' 165 | for f in ${!BINARY_FILES[@]}; do 166 | hcopy -m "$f" "${BINARY_FILES[$f]}" 167 | done 168 | humount "${OUTPUTIMAGE}" 169 | 170 | # Run the build! 171 | echo "Launching Mini vMac. Note that Mini vMac suspends itself if it is not the" 172 | echo "foreground window! Use Control-S-A to speed it up a bit." 173 | "${MINIVMAC}" buildenv.dsk "${OUTPUTIMAGE}" 174 | 175 | if [ ${DEBUG} -ne 1 ]; then 176 | echo "Script output:" 177 | hmount buildenv.dsk >/dev/null 178 | hcopy -t ':MPW 3.1:Worksheet' - 179 | fi 180 | humount buildenv.dsk 181 | 182 | hmount "${OUTPUTIMAGE}" >/dev/null 183 | echo 184 | echo ""${OUTPUTIMAGE}" info:" 185 | hvol 186 | hls -lR 187 | humount "${OUTPUTIMAGE}" 188 | 189 | if [ ${DEBUG} -ne 1 ]; then 190 | rm buildenv.dsk 191 | fi 192 | -------------------------------------------------------------------------------- /software/driver/driver.h: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "enc624j600.h" 28 | #include "sethernet.h" 29 | 30 | #if !(defined(TARGET_SE30) || defined(TARGET_SE)) 31 | #error Must define a target! (TARGET_SE or TARGET_SE30) 32 | #endif 33 | 34 | #if defined(TARGET_SE) 35 | #define ENC624J600_BASE 0x800000 36 | #endif 37 | 38 | /* Number of protocol handlers to support */ 39 | #define numberOfPhs 16 40 | 41 | /* Number of multicast addresses to support */ 42 | #define numberofMulticasts 8 43 | 44 | /* ENC624J600 buffer configuration. Allocate 1536 bytes for a transmit buffer 45 | (just enough for one frame), leaving the remainder (23040 bytes) as a receive 46 | buffer. */ 47 | #define ENC_TX_BUF_START 0x0000 48 | #define ENC_RX_BUF_START 0x0600 49 | 50 | /* Protocol-handler protocol numbers are usually Ethernet II ethertypes except 51 | for: */ 52 | enum { 53 | phProtocolPhaseII = 0, /* 802.2 Type 1 (ethertype < 0x600) */ 54 | phProtocolFree = 1, /* Invalid value used to signal a free 55 | protocol-handler table entry */ 56 | }; 57 | 58 | /* Entry in our list of protocol handlers */ 59 | struct protocolHandlerEntry { 60 | unsigned short 61 | ethertype; /* Protocol number (ethertype except for those above) */ 62 | void* handler; /* Pointer to protocol handler routine (see IM: Networking 63 | chapter on ethernet protocol handlers) */ 64 | #if 0 65 | void* readPB; /* Parameter block for pending ERead operation. */ 66 | #endif 67 | }; 68 | typedef struct protocolHandlerEntry protocolHandlerEntry; 69 | 70 | union hwAddr { 71 | Byte bytes[6]; 72 | struct { 73 | unsigned long first4; 74 | unsigned short last2; 75 | }; 76 | }; 77 | typedef union hwAddr hwAddr; 78 | 79 | /* Entry in our list of multicast addresses */ 80 | struct multicastEntry { 81 | hwAddr address; /* Ethernet address */ 82 | unsigned char refCount; /* Reference count */ 83 | } __attribute__((aligned (2))); 84 | typedef struct multicastEntry multicastEntry; 85 | 86 | /* Ethernet packet header */ 87 | struct ethernetHeader { 88 | hwAddr dest; /* Destination Ethernet address */ 89 | hwAddr source; /* Source Ethernet address */ 90 | unsigned short protocol; /* Ethernet protocol/length field */ 91 | }; 92 | typedef struct ethernetHeader ethernetHeader; 93 | 94 | /* Packet header as it appears in the ENC624J600's ring buffer */ 95 | struct ringbufEntry { 96 | /* Metadata from ENC624J600 */ 97 | unsigned short nextPkt_le; /* pointer to next packet (little-endian, relative 98 | to chip address space) */ 99 | enc624j600_rsv rsv; /* Receive status vector */ 100 | ethernetHeader pktHeader; /* Ethernet packet header */ 101 | }; 102 | typedef struct ringbufEntry ringbufEntry; 103 | 104 | /* Protocol handlers expect the packet header to be read into a RAM buffer (the 105 | Receive Header Area) that includes 8 free bytes of workspace for their use */ 106 | struct receiveHeaderArea { 107 | ringbufEntry header; /* Packet metadata and header */ 108 | Byte workspace[8]; /* Protocol handler workspace */ 109 | }; 110 | typedef struct receiveHeaderArea receiveHeaderArea; 111 | 112 | #if defined(DEBUG) 113 | /* 114 | Logging using MacsBug DebugStr() calls is *really* slow, and the scrollback 115 | buffer is tiny. Instead, log interesting events in a circular buffer in memory. 116 | 117 | Debug builds define the MacsBug macro 'dumpLog' that dumps memory from the start 118 | of the log buffer to its current head position. 119 | */ 120 | 121 | #define LOG_LEN 2048 122 | 123 | typedef enum logEvent { 124 | txEvent = 0x8000, 125 | txCompleteEvent = 0x8001, 126 | txCallIODoneEvent = 0x8002, 127 | txReturnIODoneEvent = 0x8003, 128 | txTaskAlreadyDeferred = 0x8004, 129 | txTaskAlreadyDeferredReturn = 0x8005, 130 | rxEvent = 0x8010, 131 | rxDoneEvent = 0x8011, 132 | readRxBufEvent = 0x8020 133 | } logEvent; 134 | 135 | typedef struct logEntry { 136 | unsigned long ticks; 137 | unsigned short eventType; 138 | unsigned short eventData; 139 | } logEntry; 140 | 141 | typedef struct eventLog { 142 | unsigned long head; 143 | logEntry entries[LOG_LEN]; 144 | } eventLog; 145 | #endif 146 | 147 | /* Global state used by the driver */ 148 | typedef struct driverGlobals { 149 | enc624j600 chip; /* Ethernet chip state */ 150 | 151 | SlotIntQElement theSInt; /* Our slot interrupt queue entry */ 152 | AuxDCEPtr driverDCE; /* Our device control entry */ 153 | 154 | /* Flags */ 155 | unsigned short hasGestalt : 1; /* Gestalt Manager is available */ 156 | unsigned short hasSlotMgr : 1; /* Slot Manager is available */ 157 | unsigned short vmEnabled : 1; /* Virtual Memory is enabled */ 158 | unsigned short macSE : 1; /* Running on a Macintosh SE */ 159 | 160 | protocolHandlerEntry 161 | protocolHandlers[numberOfPhs]; /* Protocol handler table */ 162 | multicastEntry multicasts[numberofMulticasts]; /* Multicast address table */ 163 | 164 | receiveHeaderArea rha; /* Buffer for receved packet headers */ 165 | 166 | /* The driverInfo struct is packed (dictated by the Ethernet driver API). 167 | Align its start point to avoid awkwardness in accessing its longword counter 168 | fields. */ 169 | driverInfo info __attribute__((aligned (2))); 170 | 171 | #if defined(DEBUG) 172 | eventLog log; 173 | #endif 174 | } driverGlobals, *driverGlobalsPtr; 175 | -------------------------------------------------------------------------------- /installer/identifiers.r: -------------------------------------------------------------------------------- 1 | /* Source filespecs */ 2 | #define fsNetworkResources 9000 3 | #define fsAppleTalk 9008 4 | #define fsResponder 9009 5 | #define fsNetwork 9101 6 | #define fsEtherTalkPhase2 9113 7 | #define fsSEthernetResources 13000 8 | #define fsSEthernet30 13002 9 | #define fsMacTCP 13010 10 | 11 | /* Target filespecs */ 12 | #define fsTgtSystem 9001 13 | #define fsTgtEnabler201 9010 14 | #define fsTgtAppleTalk 9002 15 | #define fsTgtResponder 9003 16 | #define fsTgtADSP 9004 17 | #define fsTgtADSPExtension 9005 18 | #define fsTgtAppleTalkExtension 9006 19 | #define fsTgtNetworkControl 9102 20 | #define fsTgtEtherTalkPhase2Extension 9114 21 | #define fsTgtEtherTalk 9115 22 | #define fsTgtEtherTalk20 9116 23 | #define fsTgtEtherTalk21 9117 24 | #define fsTgtEtherTalkExtension 9118 25 | #define fsTgtEtherTalk20Extension 9119 26 | #define fsTgtEtherTalk21Extension 9120 27 | #define fsTgtEtherTalkPrepExtension 9201 28 | #define fsTgtSEthernet30 13003 29 | #define fsTgtSEthernet30Old 13004 30 | #define fsTgtMacTCP 13011 31 | #define fsTgtTCPIP 13012 32 | 33 | /* File Atoms */ 34 | #define faAppleTalk 9002 35 | #define faResponder 9003 36 | #define faDeleteADSP 9004 37 | #define faDeleteADSPExtension 9005 38 | #define faDeleteAppleTalkExtension 9006 39 | #define faDeleteAppleTalk 9007 40 | #define faNetworkControl 9102 41 | #define faEtherTalkPhase2Extension 9114 42 | #define faDeleteEtherTalk 9115 43 | #define faDeleteEtherTalk20 9116 44 | #define faDeleteEtherTalk21 9117 45 | #define faDeleteEtherTalkExtension 9118 46 | #define faDeleteEtherTalk20Extension 9119 47 | #define faDeleteEtherTalk21Extension 9120 48 | #define faSEthernet30 13002 49 | #define faDeleteOldSEthernet30 13004 50 | #define faMacTCP 13010 51 | 52 | /* Action Atoms */ 53 | #define aaCheckSystem6 9001 54 | #define aaCheckSystem7 9000 55 | #define aaFROV6 9002 56 | #define aaFROV7 9003 57 | #define aaFFMP7 9004 58 | #define aaSGBL7 9005 59 | #define aaTestENETDRVR 9112 60 | #define aaTestEnetNB 9202 61 | #define aaUPDE0 9540 62 | #define aaTestSEthernet30 13001 63 | 64 | /* Resource Atoms */ 65 | #define raSystem_INIT18 9000 66 | #define raSystem_iopc128 9001 67 | #define raSystem_ltlk2 9005 68 | #define raSystem_ltlk3 9006 69 | #define raSystem_ltlk4 9031 70 | #define raSystem_ltlk5 9032 71 | #define raSystem_atlk3 9008 72 | #define raSystem_iopc127 9002 73 | #define raSystem_ltlk0 9003 74 | #define raSystem_ltlk1 9004 75 | #define raSystem_atlk1 9007 76 | #define raSystem_DRVR9_MPP 9009 77 | #define raSystem_DRVR10_ATP 9010 78 | #define raSystem_DRVR40_XPP 9011 79 | #define raSystem_lmgr0 9017 80 | #define raSystem_DRVR126_DSP 9012 81 | #define raSystem_drvr9 9013 82 | #define raSystem_AINI30 9014 83 | #define raSystem_AINI30000 9015 84 | #define raSystem_AINI32000 9016 85 | #define raDeleteSystem_AINI20 9026 86 | #define raDeleteSystem_DRVR40_XPP 9018 87 | #define raDeleteSystem_DITL_4030 9019 88 | #define raDeleteSystem_DITL_4031 9020 89 | #define raDeleteSystem_DITL_4032 9021 90 | #define raDeleteSystem_ALRT_4030 9022 91 | #define raDeleteSystem_ALRT_4031 9023 92 | #define raDeleteSystem_ALRT_4032 9024 93 | #define raDeleteSystem_STR__4032 9025 94 | #define raSystem_STR__16408 9027 95 | #define raSystem_STR__16504 9028 96 | #define raSystem_STR__16503 9029 97 | #define raEnabler201_lefT0 9030 98 | #define raSystem_DRVR127_ENET 9112 99 | #define raSystem_enet8 9202 100 | #define raSystem_enet9635 13001 101 | #define raSystem_DRVR69_ENET0 13002 102 | 103 | /* Packages */ 104 | #define pkAppleTalkSystem6 9002 105 | #define pkAppleTalkSystem7 9003 106 | #define pkNetworkControl 9100 107 | #define pkAppleTalkSystem6Custom 14016 108 | #define pkAppleTalkSystem7Custom 14017 109 | #define pkEnabler201Patch 9004 110 | #define pkDivider 9500 111 | #define pkENETDriver 9111 112 | #define pkEtherTalkPhase2 9110 113 | #define pkEtherTalkNB 9202 114 | #define pkENETDriverUpdate 9519 115 | #define pkEtherTalkNBCustom 14000 116 | #define pkMacTCP 13000 117 | #define pkSEthernet30 13001 118 | #define pkSEthernet30Custom 13002 119 | #define pkSEthernet 13003 120 | #define pkSEthernetCustom 13004 121 | 122 | /* Package Comments */ 123 | #define cmAppleTalkSystem6 14016 124 | #define cmAppleTalkSystem7 14017 125 | #define cmEtherTalkNB 14000 126 | #define cmSEthernet30 13000 127 | #define cmSEthernet 13001 128 | #define cmMacTCP 13010 129 | 130 | /* Rules */ 131 | #define rlInstallENETDriver 9504 132 | #define rlEtherTalkPhase2_2_5_6 9524 133 | #define rlNetworkControl3_0_2 9527 134 | #define rlHasSystem 9541 135 | #define rlSystem6_0_4 9542 136 | #define rlSystem7 9543 137 | #define rlEtherTalkNB 9550 138 | #define rlBaseDescription 11000 139 | #define rlNoSystemError 11001 140 | #define rlLaterAppleTalkError 11002 141 | #define rlOldSystemError 11003 142 | #define rlEtherTalkUpdateDescription 11004 143 | #define rlNetworkControlPanelDescription 11006 144 | #define rlAppleTalkDescription 11007 145 | #define rlEtherTalkNBDescription 11008 146 | #define rlInstallEtherTalk 12000 147 | #define rlInstallAppleTalk6 12003 148 | #define rlInstallAppleTalk7 12004 149 | #define rlInstallNetworkControlPanel 12008 150 | #define rlCCRD1_2 9521 151 | #define rlCCRD1_0 9522 152 | #define rlCCRD1_1 9523 153 | #define rlInstallingENETDriver 9528 154 | #define rlHasEtherTalkPhase2 9532 155 | #define rlNetworkControlRequested 9531 156 | #define rlCHKA6_58130000 9511 157 | #define rlCHKD0_3A130000 9512 158 | #define rlInstallAppleTalk 9503 159 | #define rlInstallingEtherTalk 9536 160 | #define rlHasNetworkControl 9535 161 | #define rlUpdateNetworkControl 9537 162 | 163 | #define rlSEthernet30 13001 164 | #define rlSEthernet30Description 13002 165 | 166 | #define rlSEthernet 13003 167 | #define rlSEthernetDescription 13004 168 | 169 | #define rlAlreadyHasMacTCP 13010 170 | #define rlMacTCP 13011 171 | #define rlMacTCPDescription 13012 172 | #define rlOpenTransport 13013 173 | 174 | /* Assertions */ 175 | #define asInstallAppleTalkSystem6 9511 176 | #define asInstallAppleTalkSystem7 9512 177 | #define asInstallENETDriver 9514 178 | #define asInstallNetworkControl 9520 179 | #define asHasSystem 9541 180 | #define asSystem6_0_4 9542 181 | #define asSystem7 9543 182 | #define asEtherTalkNB 9550 183 | #define asInstallAppleTalk 9503 184 | #define asInstallEtherTalk 9507 185 | #define asInstallingNetworkControl 9510 186 | #define asHasNetworkControl 9535 187 | 188 | #define asSEthernet30 13001 189 | #define asSEthernet 13003 190 | #define asMacTCP 13010 191 | -------------------------------------------------------------------------------- /pld/se/se-u2.pld: -------------------------------------------------------------------------------- 1 | Name se-u2 ; 2 | PartNo 00 ; 3 | Date 2023-10-23 ; 4 | Revision 01 ; 5 | Designer rhalkyard ; 6 | Company none ; 7 | Assembly SEthernet Macintosh SE PDS Ethernet Card ; 8 | Location U2 ; 9 | Device f1502isptqfp44 ; 10 | 11 | /* Enable JTAG interface with internal 10K pullups on TDI and TMS */ 12 | PROPERTY ATMEL { JTAG = ON } ; 13 | PROPERTY ATMEL { TDI_PULLUP = ON } ; 14 | PROPERTY ATMEL { TMS_PULLUP = ON } ; 15 | 16 | /* Consider pin assignments to be immutable; don't try to rearrange the design 17 | if it does not fit with the given pin assignments */ 18 | PROPERTY ATMEL { PREASSIGN = KEEP } ; 19 | 20 | /* CONTROL INPUTS */ 21 | PIN 6 = !ETH_INT ; /* Interrupt input from ENC624J600 */ 22 | PIN 8 = ETH_CLK ; /* Software-configurable clock from ENC624J600 */ 23 | PIN 23 = !RESET ; /* Reset input from system */ 24 | PIN 25 = !AS ; /* Address strobe from 68000 */ 25 | PIN 27 = !UDS ; /* Upper data strobe from 68000 (high-order byte, low address) */ 26 | PIN 28 = !LDS ; /* Lower data strobe from 68000 (low-order byte, high address) */ 27 | PIN 31 = RW ; /* Read/!Write from 68000 */ 28 | 29 | /* ADDRESS INPUTS */ 30 | PIN [13..15,18..22] = [A23..16] ; 31 | FIELD ADDR = [A23..16] ; 32 | 33 | /* OUTPUTS */ 34 | PIN 2 = B0SEL ; /* Low-address byte select to ENC624J600 */ 35 | PIN 3 = B1SEL ; /* High-address byte select to ENC624J600 */ 36 | PIN 5 = ETH_CS ; /* Chip select to ENC624J600 */ 37 | PIN 10 = !IPL0 ; /* Bit 0 of interrupt-priority lines to 68000 */ 38 | PIN 11 = !IPL1 ; /* Bit 1 of interrupt-priority lines to 68000 */ 39 | PIN 12 = !EXT_DTK ; /* Auto-DTACK suppression control to BBU */ 40 | PIN 30 = !DTACK ; /* Data acknowledge to 68000 */ 41 | 42 | /* IPL*, EXT_DTK and DTACK are wired-or; set output drivers to open-collector 43 | mode for these pins (note that this does NOT invert sense, they must still be 44 | declared above as inverted) */ 45 | PROPERTY ATMEL { OPEN_COLLECTOR = IPL0, IPL1, EXT_DTK, DTACK } ; 46 | 47 | /* 48 | UNUSED 49 | PIN 33 50 | PIN 34 51 | PIN 35 52 | PIN 37 (Input only) 53 | PIN 38 (Input only, VPP) 54 | PIN 39 (Input only) 55 | PIN 40 (Input only) 56 | PIN 42 57 | PIN 43 58 | PIN 44 59 | 60 | RESERVED 61 | PIN 1 = TDI ; 62 | PIN 7 = TMS ; 63 | PIN 26 = TCK ; 64 | PIN 32 = TDO ; 65 | 66 | PIN 9 = VCC ; 67 | PIN 17 = VCC ; 68 | PIN 29 = VCC ; 69 | PIN 41 = VCC ; 70 | 71 | PIN 4 = GND ; 72 | PIN 16 = GND ; 73 | PIN 24 = GND ; 74 | PIN 36 = GND ; 75 | */ 76 | 77 | /* Counter to delay asserting DTACK on read operations */ 78 | NODE [dtack_count1..0] ; 79 | FIELD dtack_counter = [dtack_count1..0] ; 80 | 81 | /* Counter to generate holdoff period between consecutive selections */ 82 | NODE [holdoff_count1..0] ; 83 | FIELD holdoff_counter = [holdoff_count1..0] ; 84 | 85 | NODE holdoff_state ; /* Latch for selection-holdoff state */ 86 | 87 | /* Counter to inhibit interrupts until ENC624J600 has been written to and 88 | presumably reset */ 89 | NODE [write_count1..0] ; 90 | FIELD write_counter = [write_count1..0] ; 91 | 92 | /* Ocuppy a 64k space at 0x800000 - 0x80ffff */ 93 | addressed = AS & ADDR:'h'80xxxx ; 94 | 95 | /* Only select device after holdoff period has cleared */ 96 | select = addressed & !holdoff_state ; 97 | ETH_CS = select ; /* ENC624J600 is the only device */ 98 | 99 | /* We generate our own DTACK; when we are addressed, assert EXT_DTK to tristate 100 | the BBU's DTACK output */ 101 | EXT_DTK = addressed ; 102 | DTACK = select & !wait ; 103 | 104 | /* Generate byte-select lines for ENC624J600 */ 105 | B0SEL = UDS & ETH_CS ; 106 | B1SEL = LDS & ETH_CS ; 107 | 108 | interrupt = ETH_INT & interrupt_enable ; 109 | IPL0 = interrupt ; 110 | 111 | HOLDOFF_TP = holdoff_state ; 112 | 113 | /* 114 | Counters for enforcing timing requirements, clocked from the ENC624J600's clock 115 | output (4MHz by default, configured to 25MHz by the driver). 116 | 117 | holdoff_counter counts time elapsed since the end of the last bus transaction 118 | that addressed us. 119 | 120 | dtack_counter counts time elapsed since the start of a bus transaction 121 | addressing us. 122 | */ 123 | 124 | sequence holdoff_counter { 125 | present 'd'0 126 | next 'd'1 ; 127 | present 'd'1 128 | next 'd'2 ; 129 | present 'd'2 130 | next 'd'3 ; 131 | present 'd'3 132 | next 'd'3 ; 133 | } 134 | holdoff_counter.ck = ETH_CLK ; 135 | holdoff_counter.ar = select ; /* Hold counter in reset while we are selected */ 136 | 137 | sequence dtack_counter { 138 | present 'd'0 139 | next 'd'1 ; 140 | present 'd'1 141 | next 'd'2 ; 142 | present 'd'2 143 | next 'd'3 ; 144 | present 'd'3 145 | next 'd'3 ; 146 | } 147 | dtack_counter.ck = ETH_CLK ; 148 | dtack_counter.ar = !select ; /* Hold counter in reset when we're not selected */ 149 | 150 | /* 151 | Enter holdoff state at the end of a write access, remain in holdoff until 152 | counter expires. ENC624J600 requires 40ns. 153 | 154 | Holdoff for 1 full clock period = 40-80ns @ 25MHz 155 | */ 156 | holdoff_state.d = 'b'1 ; 157 | holdoff_state.ck = !(addressed & !RW) ; 158 | holdoff_state.ar = RESET # holdoff_counter:['d'2..'d'3] ; 159 | 160 | /* 161 | Delay DTACK during read accesses. ENC624J600 requires 75ns 162 | 163 | 2 full clock periods = 80-120ns @ 25MHz 164 | */ 165 | wait = RW & dtack_counter:['d'0..'d'2] ; 166 | 167 | /* The ENC624J600 does not have a hardware reset input - only power-on reset and 168 | a software-controlled reset operation, so if the system is reset without 169 | 'cleanly' shutting down, it will continue generating interrupts, which can cause 170 | Sad Macs and crashes in the subsquent boot. To get around this deficiency, we 171 | gate the interrupt output until 3 writes have been made to the chip address 172 | space, which should serve as an adequate indication that software is in control 173 | of the chip and is ready to handle interrupts */ 174 | sequence write_counter { 175 | present 'd'0 176 | next 'd'1 ; 177 | present 'd'1 178 | next 'd'2 ; 179 | present 'd'2 180 | next 'd'3 ; 181 | present 'd'3 182 | next 'd'3 ; 183 | } 184 | write_counter.ck = !(ETH_CS & !RW) ; /* Increment counter on each write access */ 185 | write_counter.ar = RESET ; 186 | interrupt_enable = write_counter:'d'3 ; 187 | -------------------------------------------------------------------------------- /software/driver/protocolhandler.c: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "protocolhandler.h" 20 | #include "driver.h" 21 | #include "enc624j600.h" 22 | #include "util.h" 23 | 24 | /* 25 | Find a protocol-handler table entry matching protocol number 'theProtocol'. 26 | Returns a pointer to the entry or nil if no match found. 27 | 28 | We (arbitrarily) support 16 protocol handler entries, but in practice, most 29 | systems will only ever have a maximum of 3 handlers active - the LAP manager, 30 | and the ARP and IP handlers for MacTCP. This is too few to justify the overhead 31 | of a proper hash table over linear search. 32 | 33 | As an optimization, we maintain the protocol handler table as contiguous - new 34 | handlers are installed to the first free entry, and the table is compacted when 35 | handlers are uninstalled. 36 | */ 37 | protocolHandlerEntry *findPH(const driverGlobalsPtr theGlobals, 38 | const unsigned short theProtocol) { 39 | for (unsigned short i = 0; i < numberOfPhs; i++) { 40 | if (theGlobals->protocolHandlers[i].ethertype == theProtocol) { 41 | return &theGlobals->protocolHandlers[i]; 42 | } else if (theGlobals->protocolHandlers[i].ethertype == phProtocolFree) { 43 | return nil; 44 | } 45 | } 46 | return nil; 47 | } 48 | 49 | /* Find a free entry in the protocol-handler table. Returns a pointer to the 50 | entry or nil if no free entries available. */ 51 | static protocolHandlerEntry *findFreePH(const driverGlobalsPtr theGlobals) { 52 | for (unsigned short i = 0; i < numberOfPhs; i++) { 53 | if (theGlobals->protocolHandlers[i].ethertype == phProtocolFree) { 54 | return &theGlobals->protocolHandlers[i]; 55 | } 56 | } 57 | return nil; 58 | } 59 | 60 | /* 61 | EAttachPH call (a.k.a. Control with csCode=ENetAttachPH) 62 | 63 | Attach a protocol handler to our driver. 64 | 65 | Note that software MUST provide a protocol-handler routine for us to call here. 66 | It is legal for software to pass in a nil pointer for this routine, indicating 67 | that they will use ERead to read packets instead. However this feature seems to 68 | be little-used, and this driver does not implement ERead. 69 | */ 70 | OSStatus doEAttachPH(driverGlobalsPtr theGlobals, const EParamBlkPtr pb) { 71 | unsigned short theProtocol; 72 | protocolHandlerEntry *thePHSlot; 73 | OSErr error = noErr; 74 | 75 | /* Disable ethernet interrupts so that the ISR won't see the protocol handler 76 | table in an inconsistent state */ 77 | unsigned short old_eie = enc624j600_disable_irq(&theGlobals->chip, IRQ_ENABLE); 78 | 79 | theProtocol = pb->u.EParms1.eProtType; 80 | if (theProtocol > 0 && theProtocol <= 1500) { 81 | /* Not a valid ethertype (note that we reserve the invalid ethertype 0 for 82 | handling 802.2 Type 1 packets) */ 83 | DBGP("Failed to install handler for protocol %04x. Invalid.", theProtocol); 84 | error = lapProtErr; 85 | goto done; 86 | } 87 | if (findPH(theGlobals, theProtocol) != nil) { 88 | /* Protocol handler already installed*/ 89 | DBGP("Failed to install handler for protocol %04x. Protocol in use.", 90 | theProtocol); 91 | error = lapProtErr; 92 | goto done; 93 | } 94 | if (pb->u.EParms1.ePointer == nil) { 95 | /* TODO: support ERead */ 96 | 97 | DBGP("Failed to install ENetRead handler for protocol %04x. Not implemented.", 98 | theProtocol); 99 | error = lapProtErr; 100 | goto done; 101 | } 102 | 103 | /* Find an empty slot in the protocol handler table */ 104 | thePHSlot = findFreePH(theGlobals); 105 | 106 | if (thePHSlot == nil) { 107 | DBGP("Failed to install handler for protocol %04x. No free slots.", 108 | theProtocol); 109 | error = lapProtErr; 110 | goto done; 111 | } else { 112 | /* install the handler */ 113 | thePHSlot->ethertype = theProtocol; 114 | thePHSlot->handler = (void *)pb->u.EParms1.ePointer; 115 | // thePHSlot->readPB = -1; /* not used */ 116 | } 117 | done: 118 | 119 | enc624j600_enable_irq(&theGlobals->chip, old_eie); 120 | return error; 121 | } 122 | 123 | /* 124 | EDetachPH call (a.k.a. Control with csCode=ENetDetachPH) 125 | 126 | Detach a protocol handler from our driver. 127 | 128 | Once again, if we supported ERead we would need to do some cleanup of pending 129 | calls, but we don't, so we don't. 130 | */ 131 | OSStatus doEDetachPH(driverGlobalsPtr theGlobals, const EParamBlkPtr pb) { 132 | protocolHandlerEntry * thePHSlot; 133 | OSErr error = noErr; 134 | 135 | /* Disable ethernet interrupts so that the ISR won't see the protocol handler 136 | table in an inconsistent state */ 137 | unsigned short old_eie = enc624j600_disable_irq(&theGlobals->chip, IRQ_ENABLE); 138 | 139 | thePHSlot = findPH(theGlobals, pb->u.EParms1.eProtType); 140 | if (thePHSlot != nil) { 141 | thePHSlot->ethertype = phProtocolFree; 142 | thePHSlot->handler = nil; 143 | // if (thePHSlot->readPB != -1){ 144 | // /* Cancel pending ERead calls */ 145 | // } 146 | 147 | /* Compact the protocol-handler table */ 148 | for (unsigned short i = 0; i < numberOfPhs - 1; i++) { 149 | if (theGlobals->protocolHandlers[i].ethertype == phProtocolFree) { 150 | if (theGlobals->protocolHandlers[i+1].ethertype != phProtocolFree) { 151 | theGlobals->protocolHandlers[i] = theGlobals->protocolHandlers[i+1]; 152 | theGlobals->protocolHandlers[i+1].ethertype = phProtocolFree; 153 | } 154 | } 155 | } 156 | } else { 157 | error = lapProtErr; 158 | } 159 | enc624j600_enable_irq(&theGlobals->chip, old_eie); 160 | 161 | return error; 162 | } 163 | 164 | /* 165 | Initialize the protocol handler table, called during initial Open routine. 166 | */ 167 | void InitPHTable(driverGlobalsPtr theGlobals) { 168 | for (unsigned short i = 0; i < numberOfPhs; i++) { 169 | theGlobals->protocolHandlers[i].ethertype = phProtocolFree; 170 | theGlobals->protocolHandlers[i].handler = nil; 171 | // theGlobals->protocolHandlers[i].readPB = -1; /* not used */ 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /installer/packages.r: -------------------------------------------------------------------------------- 1 | #include "InstallerTypes.r" 2 | #include "identifiers.r" 3 | 4 | resource 'inpk' (pkAppleTalkSystem6) { 5 | format0 { 6 | doesntShowOnCustom, 7 | notRemovable, 8 | forceRestart, 9 | 0, 10 | 96868, 11 | "", 12 | { 13 | 'inaa', aaCheckSystem6, 14 | 'infa', faDeleteADSP, 15 | 'inra', raDeleteSystem_DITL_4030, 16 | 'inra', raDeleteSystem_DITL_4031, 17 | 'inra', raDeleteSystem_DITL_4032, 18 | 'inra', raDeleteSystem_ALRT_4030, 19 | 'inra', raDeleteSystem_ALRT_4031, 20 | 'inra', raDeleteSystem_ALRT_4032, 21 | 'inra', raDeleteSystem_STR__4032, 22 | 'inra', raDeleteSystem_DRVR40_XPP, 23 | 'infa', faAppleTalk, 24 | 'inra', raSystem_INIT18, 25 | 'inra', raSystem_ltlk0, 26 | 'inra', raSystem_ltlk1, 27 | 'inra', raSystem_ltlk2, 28 | 'inra', raSystem_ltlk3, 29 | 'inra', raSystem_ltlk4, 30 | 'inra', raSystem_ltlk5, 31 | 'inra', raSystem_atlk1, 32 | 'inra', raSystem_atlk3, 33 | 'inra', raSystem_iopc128, 34 | 'inra', raSystem_iopc127, 35 | 'inaa', aaFROV6 36 | } 37 | } 38 | }; 39 | 40 | resource 'inpk' (pkAppleTalkSystem7) { 41 | format0 { 42 | doesntShowOnCustom, 43 | notRemovable, 44 | forceRestart, 45 | 0, 46 | 97486, 47 | "", 48 | { 49 | 'inaa', aaCheckSystem7, 50 | 'infa', faDeleteADSP, 51 | 'infa', faDeleteADSPExtension, 52 | 'infa', faDeleteAppleTalkExtension, 53 | 'infa', faDeleteAppleTalk, 54 | 'inra', raDeleteSystem_AINI20, 55 | 'inra', raSystem_DRVR9_MPP, 56 | 'inra', raSystem_DRVR10_ATP, 57 | 'inra', raSystem_DRVR40_XPP, 58 | 'inra', raSystem_DRVR126_DSP, 59 | 'inra', raSystem_drvr9, 60 | 'inra', raSystem_lmgr0, 61 | 'inra', raSystem_AINI30, 62 | 'inra', raSystem_AINI30000, 63 | 'inra', raSystem_AINI32000, 64 | 'inra', raSystem_ltlk0, 65 | 'inra', raSystem_ltlk1, 66 | 'inra', raSystem_ltlk2, 67 | 'inra', raSystem_ltlk3, 68 | 'inra', raSystem_ltlk4, 69 | 'inra', raSystem_ltlk5, 70 | 'inra', raSystem_atlk1, 71 | 'inra', raSystem_atlk3, 72 | 'inra', raSystem_iopc128, 73 | 'inra', raSystem_iopc127, 74 | 'inaa', aaFROV7, 75 | 'inaa', aaFFMP7 76 | } 77 | } 78 | }; 79 | 80 | resource 'inpk' (pkNetworkControl) { 81 | format0 { 82 | doesntShowOnCustom, 83 | notRemovable, 84 | dontForceRestart, 85 | 0, 86 | 43008, 87 | "", 88 | { 89 | 'infa', faNetworkControl 90 | } 91 | } 92 | }; 93 | 94 | resource 'inpk' (pkAppleTalkSystem6Custom) { 95 | format0 { 96 | showsOnCustom, 97 | notRemovable, 98 | forceRestart, 99 | cmAppleTalkSystem6, 100 | 147556, 101 | "AppleTalk v58.1.3 for System 6", 102 | { 103 | 'inpk', pkAppleTalkSystem6, 104 | 'inpk', pkNetworkControl, 105 | 'infa', faResponder 106 | } 107 | } 108 | }; 109 | 110 | resource 'inpk' (pkAppleTalkSystem7Custom) { 111 | format0 { 112 | showsOnCustom, 113 | notRemovable, 114 | forceRestart, 115 | cmAppleTalkSystem7, 116 | 141704, 117 | "AppleTalk v58.1.3 for System 7", 118 | { 119 | 'inpk', pkAppleTalkSystem7, 120 | 'inpk', pkEnabler201Patch, 121 | 'inpk', pkNetworkControl, 122 | 'inra', raSystem_STR__16408, 123 | 'inra', raSystem_STR__16503, 124 | 'inra', raSystem_STR__16504 125 | } 126 | } 127 | }; 128 | 129 | resource 'inpk' (pkEnabler201Patch) { 130 | format0 { 131 | doesntShowOnCustom, 132 | notRemovable, 133 | forceRestart, 134 | 0, 135 | 69, 136 | "", 137 | { 138 | 'inra', raEnabler201_lefT0, 139 | 'inaa', aaSGBL7 140 | } 141 | } 142 | }; 143 | 144 | resource 'inpk' (pkMacTCP) { 145 | format0 { 146 | showsOnCustom, 147 | removable, 148 | forceRestart, 149 | cmMacTCP, 150 | 0, 151 | "MacTCP", 152 | { 153 | 'infa', faMacTCP 154 | } 155 | } 156 | }; 157 | 158 | 159 | resource 'inpk' (pkDivider) { 160 | format0 { 161 | showsOnCustom, 162 | notRemovable, 163 | dontForceRestart, 164 | 0, 165 | 0, 166 | "-", 167 | { 168 | ' ', 0 169 | } 170 | } 171 | }; 172 | 173 | resource 'inpk' (pkENETDriver) { 174 | format0 { 175 | doesntShowOnCustom, 176 | removable, 177 | forceRestart, 178 | 0, 179 | 2380, 180 | "", 181 | { 182 | 'inaa', aaTestENETDRVR, 183 | 'inra', raSystem_DRVR127_ENET 184 | } 185 | } 186 | }; 187 | 188 | resource 'inpk' (pkEtherTalkPhase2) { 189 | format0 { 190 | doesntShowOnCustom, 191 | removable, 192 | forceRestart, 193 | 0, 194 | 15360, 195 | "", 196 | { 197 | 'infa', faDeleteEtherTalk, 198 | 'infa', faDeleteEtherTalk20, 199 | 'infa', faDeleteEtherTalk21, 200 | 'infa', faDeleteEtherTalkExtension, 201 | 'infa', faDeleteEtherTalk20Extension, 202 | 'infa', faDeleteEtherTalk21Extension, 203 | 'infa', faEtherTalkPhase2Extension 204 | } 205 | } 206 | }; 207 | 208 | #if 0 209 | /* EtherTalk NB driver package */ 210 | resource 'inpk' (pkEtherTalkNB) { 211 | format0 { 212 | doesntShowOnCustom, 213 | removable, 214 | forceRestart, 215 | 0, 216 | 4420, 217 | "", 218 | { 219 | 'inaa', aaTestEnetNB, 220 | 'inra', raSystem_enet8 221 | } 222 | } 223 | }; 224 | #endif 225 | 226 | resource 'inpk' (pkENETDriverUpdate) { 227 | format0 { 228 | doesntShowOnCustom, 229 | notRemovable, 230 | forceRestart, 231 | 0, 232 | 0, 233 | "", 234 | { 235 | 'inaa', aaUPDE0 236 | } 237 | } 238 | }; 239 | 240 | #if 0 241 | /* EtherTalk NB Custom Install entry */ 242 | resource 'inpk' (pkEtherTalkNBCustom) { 243 | format0 { 244 | showsOnCustom, 245 | removable, 246 | forceRestart, 247 | cmEtherTalkNB, 248 | 65168, 249 | "EtherTalk for EtherTalk NB Card", 250 | { 251 | 'inpk', pkEtherTalkNB, 252 | 'inpk', pkEtherTalkPhase2, 253 | 'inpk', pkNetworkControl, 254 | 'inpk', pkENETDriver 255 | } 256 | } 257 | }; 258 | #endif 259 | 260 | /* Package selectable for Custom Install - installs both EtherTalk bits and driver */ 261 | resource 'inpk' (pkSEthernet30Custom) { 262 | format0 { 263 | showsOnCustom, 264 | removable, 265 | forceRestart, 266 | cmSEthernet30, 267 | 0, 268 | "EtherTalk for SEthernet/30 Card", 269 | { 270 | 'inpk', pkSEthernet30, 271 | 'inpk', pkEtherTalkPhase2, 272 | 'inpk', pkNetworkControl, 273 | 'inpk', pkENETDriver 274 | } 275 | } 276 | }; 277 | 278 | /* Package selectable for Easy Install - driver only */ 279 | resource 'inpk' (pkSEthernet30) { 280 | format0 { 281 | doesntShowOnCustom, 282 | removable, 283 | forceRestart, 284 | 0, 285 | 0, 286 | "", 287 | { 288 | 'infa', faSEthernet30, 289 | 'infa', faDeleteOldSEthernet30 290 | } 291 | } 292 | }; 293 | 294 | /* Package selectable for Easy Install - driver only */ 295 | resource 'inpk' (pkSEthernet) { 296 | format0 { 297 | doesntShowOnCustom, 298 | removable, 299 | forceRestart, 300 | 0, 301 | 0, 302 | "", 303 | { 304 | 'inra', raSystem_DRVR69_ENET0 305 | } 306 | } 307 | }; 308 | 309 | /* Package selectable for Custom Install - installs both EtherTalk bits and driver */ 310 | resource 'inpk' (pkSEthernetCustom) { 311 | format0 { 312 | showsOnCustom, 313 | removable, 314 | forceRestart, 315 | cmSEthernet, 316 | 0, 317 | "EtherTalk for SEthernet Card", 318 | { 319 | 'inpk', pkSEthernet, 320 | 'inpk', pkEtherTalkPhase2, 321 | 'inpk', pkNetworkControl 322 | } 323 | } 324 | }; 325 | -------------------------------------------------------------------------------- /software/driver/header.S: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "version.h" 20 | #include "macsbug.inc" 21 | 22 | /* Header for 'enet' driver resource */ 23 | 24 | /* output a length-prefixed "pascal" style string */ 25 | .macro pstring str1, str2, str3, str4, str5, str6 26 | .byte pstring_end_\@ - . - 1 27 | .ascii "\str1" 28 | .ascii "\str2" 29 | .ascii "\str3" 30 | .ascii "\str4" 31 | .ascii "\str5" 32 | .ascii "\str6" 33 | pstring_end_\@: 34 | .endm 35 | 36 | /* Retro68 doesn't have assembler includes :( Define our own constants here */ 37 | 38 | /* DRVR resource flags */ 39 | dNeedLockMask = 0x4000 /* Lock code resource in memory */ 40 | dCtlEnableMask = 0x0400 /* Driver responds to Control call */ 41 | 42 | /* Offsets to fields in IOParam struct */ 43 | ioTrap = 6 /* System trap that issued this call */ 44 | ioResult = 16 /* Result of call */ 45 | csCode = 26 /* Call function code */ 46 | 47 | killCode = 1 /* csCode for KillIO */ 48 | noQueueBit = 9 /* Index for no-queue bit in ioTrap */ 49 | 50 | JIODone = 0x08fc /* Low-memory jump vector for IODone */ 51 | 52 | .global header_start 53 | 54 | /* The .rsrcheader section is placd before .text and is normally left empty, so 55 | anything put here shows up right at the start of the binary, which is exactly 56 | what we want for a code-resource header */ 57 | .section .rsrcheader 58 | header_start: 59 | .short dNeedLockMask + dCtlEnableMask /* drvrFlags - lock driver in 60 | memory, we respond to Control 61 | calls */ 62 | .short 0 /* drvrDelay - ticks between poll intervals. 0 = Don't poll */ 63 | .short 0 /* drvrEMask - event mask for DAs. Not used. */ 64 | .short 0 /* drvrMenu - menu resource ID for DAs. Not used */ 65 | 66 | /* offsets to entrypoints */ 67 | .short doOpen-header_start /* Open */ 68 | .short 0 /* Prime (not used) */ 69 | .short doControl-header_start /* Control */ 70 | .short 0 /* Status (not used) */ 71 | .short doClose-header_start /* Close */ 72 | 73 | #if defined(TARGET_SE30) 74 | pstring ".ENET" 75 | #elif defined(TARGET_SE) 76 | pstring ".ENET0" 77 | #endif 78 | .align 2 79 | 80 | /* version number in 'vers' resource format */ 81 | .byte VERSION_MAJOR /* major version */ 82 | .byte VERSION_MINOR /* minor version in BCD format */ 83 | .byte VERSION_DEVSTAGE /* release stage */ 84 | .byte VERSION_INTERNAL_ID /* release number for non-final build */ 85 | .word 0x0000 /* region */ 86 | pstring VERSION_SHORT /* short version */ 87 | /* Long version string */ 88 | #if defined(TARGET_SE30) 89 | pstring "SEthernet/30 Driver ", VERSION_SHORT, " git:", GIT_REV 90 | #elif defined(TARGET_SE) 91 | pstring "SEthernet Driver ", VERSION_SHORT, " git:", GIT_REV 92 | #endif 93 | .align 2 94 | 95 | /* 96 | Driver entry points have a 'special' calling convention - they are called with 97 | their arguments (pointers to the parameter block and device control structure) 98 | in A0 and A1 respectively, and must preserve these two registers on exit. 99 | 100 | Additionally, the Control entrypoint needs special return handling to support 101 | the Mac OS's async IO model (the Prime and Status entrypoints would also need 102 | this if we supported them). Control, Prime and Status handlers should return >0 103 | if an async operation has been started. 104 | 105 | These wrappers (adapted from IM: Devices listing 1-8) allow the actual 106 | implementations to be written in C. 107 | 108 | The C impls should be declared with: 109 | #pragma parameter __D0 driverOpen(__A0, __A1) 110 | OSErr driverOpen(EParamBlkPtr pb, DCtlPtr dce) 111 | #pragma parameter __D0 driverControl(__A0, __A1) 112 | OSErr driverControl(EParamBlkPtr pb, DCtlPtr dce) 113 | #pragma parameter __D0 driverClose(__A0, __A1) 114 | OSErr driverClose(EParamBlkPtr pb, DCtlPtr dce) 115 | 116 | Note that BSR must be used here rather than JSR because our code may not have 117 | been relocated yet! 118 | */ 119 | doOpen: 120 | MOVEM.L %A0-%A1,-(%SP) /* Save registers */ 121 | BSR driverOpen 122 | MOVEM.L (%SP)+,%A0-%A1 /* Restore registers */ 123 | RTS 124 | MacsbugSymbol doOpen 125 | 126 | doControl: 127 | MOVEM.L %A0-%A1,-(%SP) /* Save registers */ 128 | BSR driverControl 129 | MOVEM.L (%SP)+,%A0-%A1 /* Restore registers */ 130 | /* Any Control call except KillIO may return asynchronously. */ 131 | CMP.W #killCode, csCode(%A0) 132 | JNE IOReturn /* Not KillIO - handle async return */ 133 | RTS /* KillIO - just return */ 134 | MacsbugSymbol doControl 135 | 136 | IOReturn: 137 | MOVE.W ioTrap(%A0), %D1 138 | BTST #noQueueBit, %D1 /* Was IO operation queued or immediate? */ 139 | JEQ Queued 140 | 141 | /* Immediate calls return directly through RTS */ 142 | TST.W %D0 /* test asynchronous return result */ 143 | JLE ImmedRTS /* result must be <= 0 */ 144 | CLR.W %D0 /* in-progress result (> 0) returned as 0 */ 145 | ImmedRTS: 146 | MOVE.W %D0,ioResult(%A0) /* for immediate calls you must explicitly 147 | place the result in the ioResult field */ 148 | RTS 149 | MacsbugSymbol IOReturn 150 | 151 | Queued: 152 | /* Queued calls must call IODone on completion */ 153 | TST.W %D0 /* test asynchronous return result */ 154 | JLE MyIODone /* I/O is complete if result <= 0 */ 155 | CLR.W %D0 /* in-progress result (> 0) returned as 0 */ 156 | RTS 157 | MacsbugSymbol QueuedIOReturn 158 | 159 | MyIODone: 160 | /* For tail calls through a vector, it's faster to push the address onto 161 | the stack and 'return' to it than to do an indirect jump */ 162 | MOVE.L JIODone,-(%SP) 163 | RTS 164 | MacsbugSymbol MyIODone 165 | 166 | doClose: 167 | MOVEM.L %A0-%A1,-(%SP) /* Save registers */ 168 | BSR driverClose 169 | MOVEM.L (%SP)+,%A0-%A1 /* Restore registers */ 170 | RTS 171 | MacsbugSymbol doClose 172 | -------------------------------------------------------------------------------- /software/shared/memtest/memtest.c: -------------------------------------------------------------------------------- 1 | /********************************************************************** 2 | * 3 | * Filename: memtest.c 4 | * 5 | * Description: General-purpose memory testing functions. 6 | * 7 | * Notes: This software can be easily ported to systems with 8 | * different data bus widths by redefining 'datum'. 9 | * 10 | * 11 | * Copyright (c) 1998 by Michael Barr. This software is placed into 12 | * the public domain and may be used for any purpose. However, this 13 | * notice must not be changed or removed and no warranty is either 14 | * expressed or implied by its publication or distribution. 15 | **********************************************************************/ 16 | 17 | 18 | #include "memtest.h" 19 | 20 | 21 | /********************************************************************** 22 | * 23 | * Function: memTestDataBus() 24 | * 25 | * Description: Test the data bus wiring in a memory region by 26 | * performing a walking 1's test at a fixed address 27 | * within that region. The address (and hence the 28 | * memory region) is selected by the caller. 29 | * 30 | * Notes: 31 | * 32 | * Returns: 0 if the test succeeds. 33 | * A non-zero result is the first pattern that failed. 34 | * 35 | **********************************************************************/ 36 | datum 37 | memTestDataBus(volatile datum * address) 38 | { 39 | datum pattern; 40 | 41 | 42 | /* 43 | * Perform a walking 1's test at the given address. 44 | */ 45 | for (pattern = 1; pattern != 0; pattern <<= 1) 46 | { 47 | /* 48 | * Write the test pattern. 49 | */ 50 | *address = pattern; 51 | 52 | /* 53 | * Read it back (immediately is okay for this test). 54 | */ 55 | if (*address != pattern) 56 | { 57 | return (pattern); 58 | } 59 | } 60 | 61 | return (0); 62 | 63 | } /* memTestDataBus() */ 64 | 65 | 66 | /********************************************************************** 67 | * 68 | * Function: memTestAddressBus() 69 | * 70 | * Description: Test the address bus wiring in a memory region by 71 | * performing a walking 1's test on the relevant bits 72 | * of the address and checking for aliasing. This test 73 | * will find single-bit address failures such as stuck 74 | * -high, stuck-low, and shorted pins. The base address 75 | * and size of the region are selected by the caller. 76 | * 77 | * Notes: For best results, the selected base address should 78 | * have enough LSB 0's to guarantee single address bit 79 | * changes. For example, to test a 64-Kbyte region, 80 | * select a base address on a 64-Kbyte boundary. Also, 81 | * select the region size as a power-of-two--if at all 82 | * possible. 83 | * 84 | * Returns: NULL if the test succeeds. 85 | * A non-zero result is the first address at which an 86 | * aliasing problem was uncovered. By examining the 87 | * contents of memory, it may be possible to gather 88 | * additional information about the problem. 89 | * 90 | **********************************************************************/ 91 | datum * 92 | memTestAddressBus(volatile datum * baseAddress, unsigned long nBytes) 93 | { 94 | unsigned long addressMask = (nBytes/sizeof(datum) - 1); 95 | unsigned long offset; 96 | unsigned long testOffset; 97 | 98 | datum pattern = (datum) 0xAAAAAAAA; 99 | datum antipattern = (datum) 0x55555555; 100 | 101 | 102 | /* 103 | * Write the default pattern at each of the power-of-two offsets. 104 | */ 105 | for (offset = 1; (offset & addressMask) != 0; offset <<= 1) 106 | { 107 | baseAddress[offset] = pattern; 108 | } 109 | 110 | /* 111 | * Check for address bits stuck high. 112 | */ 113 | testOffset = 0; 114 | baseAddress[testOffset] = antipattern; 115 | 116 | for (offset = 1; (offset & addressMask) != 0; offset <<= 1) 117 | { 118 | if (baseAddress[offset] != pattern) 119 | { 120 | return ((datum *) &baseAddress[offset]); 121 | } 122 | } 123 | 124 | baseAddress[testOffset] = pattern; 125 | 126 | /* 127 | * Check for address bits stuck low or shorted. 128 | */ 129 | for (testOffset = 1; (testOffset & addressMask) != 0; testOffset <<= 1) 130 | { 131 | baseAddress[testOffset] = antipattern; 132 | 133 | if (baseAddress[0] != pattern) 134 | { 135 | return ((datum *) &baseAddress[testOffset]); 136 | } 137 | 138 | for (offset = 1; (offset & addressMask) != 0; offset <<= 1) 139 | { 140 | if ((baseAddress[offset] != pattern) && (offset != testOffset)) 141 | { 142 | return ((datum *) &baseAddress[testOffset]); 143 | } 144 | } 145 | 146 | baseAddress[testOffset] = pattern; 147 | } 148 | 149 | return (NULL); 150 | 151 | } /* memTestAddressBus() */ 152 | 153 | 154 | /********************************************************************** 155 | * 156 | * Function: memTestDevice() 157 | * 158 | * Description: Test the integrity of a physical memory device by 159 | * performing an increment/decrement test over the 160 | * entire region. In the process every storage bit 161 | * in the device is tested as a zero and a one. The 162 | * base address and the size of the region are 163 | * selected by the caller. 164 | * 165 | * Notes: 166 | * 167 | * Returns: NULL if the test succeeds. 168 | * 169 | * A non-zero result is the first address at which an 170 | * incorrect value was read back. By examining the 171 | * contents of memory, it may be possible to gather 172 | * additional information about the problem. 173 | * 174 | **********************************************************************/ 175 | datum * 176 | memTestDevice(volatile datum * baseAddress, unsigned long nBytes) 177 | { 178 | unsigned long offset; 179 | unsigned long nWords = nBytes / sizeof(datum); 180 | 181 | datum pattern; 182 | datum antipattern; 183 | 184 | 185 | /* 186 | * Fill memory with a known pattern. 187 | */ 188 | for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) 189 | { 190 | baseAddress[offset] = pattern; 191 | } 192 | 193 | /* 194 | * Check each location and invert it for the second pass. 195 | */ 196 | for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) 197 | { 198 | if (baseAddress[offset] != pattern) 199 | { 200 | return ((datum *) &baseAddress[offset]); 201 | } 202 | 203 | antipattern = ~pattern; 204 | baseAddress[offset] = antipattern; 205 | } 206 | 207 | /* 208 | * Check each location for the inverted pattern and zero it. 209 | */ 210 | for (pattern = 1, offset = 0; offset < nWords; pattern++, offset++) 211 | { 212 | antipattern = ~pattern; 213 | if (baseAddress[offset] != antipattern) 214 | { 215 | return ((datum *) &baseAddress[offset]); 216 | } 217 | } 218 | 219 | return (NULL); 220 | 221 | } /* memTestDevice() */ 222 | -------------------------------------------------------------------------------- /installer/misc.r: -------------------------------------------------------------------------------- 1 | #include "Types.r" 2 | #include "SysTypes.r" 3 | #include "version.h" 4 | 5 | resource 'vers' (1, purgeable) { 6 | VERSION_MAJOR, 7 | VERSION_MINOR, 8 | VERSION_DEVSTAGE, 9 | VERSION_INTERNAL_ID, 10 | verUS, 11 | VERSION_SHORT, 12 | VERSION_LONG " © Richard Halkyard, Apple Computer, Inc. 1984-2024" 13 | }; 14 | 15 | resource 'vers' (2, purgeable) { 16 | VERSION_MAJOR, 17 | VERSION_MINOR, 18 | VERSION_DEVSTAGE, 19 | VERSION_INTERNAL_ID, 20 | verUS, 21 | VERSION_SHORT, 22 | "SEthernet Installer " VERSION_LONG 23 | }; 24 | 25 | 26 | resource 'ICON' (1) { 27 | $"" 28 | }; 29 | 30 | resource 'DLOG' (9000) { 31 | {40, 70, 220, 470}, 32 | dBoxProc, 33 | invisible, 34 | noGoAway, 35 | 0x0, 36 | 9000, 37 | "Message dialog" 38 | }; 39 | 40 | resource 'DLOG' (9001) { 41 | {40, 70, 220, 470}, 42 | dBoxProc, 43 | invisible, 44 | noGoAway, 45 | 0x0, 46 | 9001, 47 | "Message dialog" 48 | }; 49 | 50 | resource 'DLOG' (9002) { 51 | {68, 70, 248, 470}, 52 | dBoxProc, 53 | invisible, 54 | noGoAway, 55 | 0x0, 56 | 9002, 57 | "Message dialog" 58 | }; 59 | 60 | resource 'DLOG' (9003) { 61 | {40, 70, 220, 470}, 62 | dBoxProc, 63 | invisible, 64 | noGoAway, 65 | 0x0, 66 | 9003, 67 | "Message dialog" 68 | }; 69 | 70 | resource 'DLOG' (9004) { 71 | {40, 70, 220, 470}, 72 | dBoxProc, 73 | invisible, 74 | noGoAway, 75 | 0x0, 76 | 9004, 77 | "Message dialog" 78 | }; 79 | 80 | resource 'DLOG' (9005) { 81 | {40, 70, 220, 470}, 82 | dBoxProc, 83 | invisible, 84 | noGoAway, 85 | 0x0, 86 | 9005, 87 | "Message dialog" 88 | }; 89 | 90 | resource 'DLOG' (9006) { 91 | {40, 70, 220, 470}, 92 | dBoxProc, 93 | invisible, 94 | noGoAway, 95 | 0x0, 96 | 9006, 97 | "Message dialog" 98 | }; 99 | 100 | resource 'DLOG' (9007) { 101 | {40, 70, 220, 470}, 102 | dBoxProc, 103 | invisible, 104 | noGoAway, 105 | 0x0, 106 | 9007, 107 | "Message dialog" 108 | }; 109 | 110 | resource 'DLOG' (9186) { 111 | {40, 70, 166, 370}, 112 | dBoxProc, 113 | invisible, 114 | noGoAway, 115 | 0x0, 116 | 9186, 117 | "Message dialog" 118 | }; 119 | 120 | resource 'DITL' (9000) { 121 | { 122 | {137, 250, 157, 310}, 123 | Button { 124 | enabled, 125 | "OK" 126 | }, 127 | {0, 0, 0, 0}, 128 | UserItem { 129 | disabled 130 | }, 131 | {36, 91, 121, 382}, 132 | StaticText { 133 | disabled, 134 | "The selected disk has no installed System. AppleTalk cannot be installed on thi" 135 | "s disk. Please click Switch Disk to choose a different disk or insert a floppy " 136 | "disk." 137 | }, 138 | {39, 16, 103, 80}, 139 | Icon { 140 | disabled, 141 | 1000 142 | } 143 | } 144 | }; 145 | 146 | resource 'DITL' (9001) { 147 | { 148 | {137, 250, 157, 310}, 149 | Button { 150 | enabled, 151 | "OK" 152 | }, 153 | {0, 0, 0, 0}, 154 | UserItem { 155 | disabled 156 | }, 157 | {20, 101, 133, 389}, 158 | StaticText { 159 | disabled, 160 | "The selected disk has System 7 installed. The custom installation you have selec" 161 | "ted is only for use on disks where System 6 is installed. \nPlease click Switch D" 162 | "isk to choose a different disk or insert a floppy disk." 163 | }, 164 | {44, 18, 108, 82}, 165 | Icon { 166 | disabled, 167 | 1000 168 | } 169 | } 170 | }; 171 | 172 | resource 'DITL' (9002) { 173 | { 174 | {137, 250, 157, 310}, 175 | Button { 176 | enabled, 177 | "OK" 178 | }, 179 | {0, 0, 0, 0}, 180 | UserItem { 181 | disabled 182 | }, 183 | {26, 99, 126, 397}, 184 | StaticText { 185 | disabled, 186 | "The selected disk has a System version of 6.0.3 or earlier. AppleTalk can only " 187 | "be installed on System version 6.0.4 or later. Please click Switch Disk to choo" 188 | "se a different disk or insert a floppy disk." 189 | }, 190 | {37, 16, 101, 80}, 191 | Icon { 192 | disabled, 193 | 1000 194 | } 195 | } 196 | }; 197 | 198 | resource 'DITL' (9003) { 199 | { 200 | {137, 250, 157, 310}, 201 | Button { 202 | enabled, 203 | "OK" 204 | }, 205 | {0, 0, 0, 0}, 206 | UserItem { 207 | disabled 208 | }, 209 | {20, 108, 127, 389}, 210 | StaticText { 211 | disabled, 212 | "The selected disk has System 6 installed. The custom installation you have selec" 213 | "ted is only for use on disks where System 7 is installed.\nPlease click Switch Di" 214 | "sk to choose a different disk or insert a floppy disk." 215 | }, 216 | {37, 18, 101, 82}, 217 | Icon { 218 | disabled, 219 | 1000 220 | } 221 | } 222 | }; 223 | 224 | resource 'DITL' (9004) { 225 | { 226 | {137, 290, 157, 350}, 227 | Button { 228 | enabled, 229 | "Cancel" 230 | }, 231 | {0, 0, 0, 0}, 232 | UserItem { 233 | disabled 234 | }, 235 | {137, 200, 157, 260}, 236 | Button { 237 | enabled, 238 | "Continue" 239 | }, 240 | {20, 117, 124, 390}, 241 | StaticText { 242 | disabled, 243 | "The selected disk has a later version of AppleTalk than what you are about to in" 244 | "stall. Click 'Cancel' to stop the installation, then click Switch Disk to choos" 245 | "e a different disk or insert a floppy disk. Click 'Continue' to install anway." 246 | }, 247 | {36, 22, 100, 86}, 248 | Icon { 249 | disabled, 250 | 1001 251 | } 252 | } 253 | }; 254 | 255 | resource 'DITL' (9005) { 256 | { 257 | {137, 290, 157, 350}, 258 | Button { 259 | enabled, 260 | "Cancel" 261 | }, 262 | {0, 0, 0, 0}, 263 | UserItem { 264 | disabled 265 | }, 266 | {137, 200, 157, 260}, 267 | Button { 268 | enabled, 269 | "Continue" 270 | }, 271 | {9, 115, 122, 388}, 272 | StaticText { 273 | disabled, 274 | "The selected disk has a later version of ^0 than what you are about to install. " 275 | " Click 'Cancel' to stop the installation, then click Switch Disk to choose a dif" 276 | "ferent disk or insert a floppy disk. Click 'Continue' to install anway." 277 | }, 278 | {36, 22, 100, 86}, 279 | Icon { 280 | disabled, 281 | 1001 282 | } 283 | } 284 | }; 285 | 286 | resource 'DITL' (9006) { 287 | { 288 | {137, 250, 157, 310}, 289 | Button { 290 | enabled, 291 | "MORE" 292 | }, 293 | {0, 0, 0, 0}, 294 | UserItem { 295 | disabled 296 | }, 297 | {35, 98, 130, 358}, 298 | StaticText { 299 | disabled, 300 | "Your machine has a non-Apple Ethernet card in addition to an Apple Ethernet card" 301 | ". Continuing this installation will destroy the driver for the non-Apple card." 302 | }, 303 | {44, 18, 108, 82}, 304 | Icon { 305 | disabled, 306 | 1000 307 | } 308 | } 309 | }; 310 | 311 | resource 'DITL' (9007) { 312 | { 313 | {137, 250, 157, 310}, 314 | Button { 315 | enabled, 316 | "OK" 317 | }, 318 | {0, 0, 0, 0}, 319 | UserItem { 320 | disabled 321 | }, 322 | {12, 70, 131, 345}, 323 | StaticText { 324 | disabled, 325 | "QUIT the installer, remove any Apple Ethernet cards from your machine, and rerun" 326 | " the installer to update your non-Apple drivers. \nThen reinstall the Apple Ether" 327 | "net cards and rerun the installer to install the Apple drivers." 328 | } 329 | } 330 | }; 331 | 332 | resource 'STR#' (0) { 333 | { 334 | "the EtherTalk NB card driver", 335 | "the Ethernet NB card driver", 336 | "the Ethernet LC card driver", 337 | "the Quadra 900 Built-in Ethernet driver", 338 | "the Quadra 700 Built-in Ethernet driver", 339 | "the Quadra 950 Built-in Ethernet driver", 340 | "the TokenTalk NB Card driver", 341 | "the Token Ring 4/16 NB Card driver", 342 | "the multi-vendor TokenRing driver", 343 | "the multi-vendor Ethernet driver", 344 | "the Centris 650, Centris 610 or Quadra 800 Built-in Ethernet driver", 345 | "the Quadra 840AV Built-in Ethernet driver", 346 | "the Centris 660AV Built-in Ethernet driver", 347 | "the Ethernet NB Twisted-Pair card driver", 348 | "the Ethernet LC card driver", 349 | "the Ethernet Comm Slot card driver", 350 | "the Ethernet Comm Slot card driver" 351 | } 352 | }; 353 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SEthernet and SEthernet/30 2 | 3 | Modern, low-cost 10/100 Ethernet for the Macintosh SE and SE/30. 4 | 5 | ## Introduction 6 | 7 | It's an unfortunate reality of retrocomputing that, as time goes on, original 8 | hardware only ever becomes more scarce, and usually more expensive. 9 | 10 | While PDS ethernet cards are certainly not _scarce_ yet, the collector appeal of 11 | Compact Macs means that cards are in high demand, with consequent high prices. 12 | As of this writing (October 2023), the going eBay rate for them seems to be of 13 | the order of $US 150-200, which is a bit steep for those of us who remember 14 | finding them in electronics-recycler dollar bins. 15 | 16 | While other projects have aimed to clone these original cards, such an approach 17 | comes with a downside - the ethernet controller chips used in them have been out 18 | of production for many years, and the controllers and (even more so) their 19 | supporting components, are difficult to find. 20 | 21 | While cloned cards trade off parts sourcing difficutly (and cost) for the 22 | significant advantage of being able to use existing drivers, this project takes 23 | the opposite approach: design an all-new card using a modern, readily-available 24 | ethernet controller, and write a new driver for it. How hard can it be? 25 | [^howhard] 26 | 27 | ## Hardware Overview 28 | 29 | Both the SEthernet and SEthernet/30 use the Microchip 30 | [ENC624J600](https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/39935c.pdf) 31 | all-in-one embedded ethernet controller. It's cheap, readily available, has a 32 | straightforward software interface without too many glaring misfeatures (I'm 33 | looking at you, RTL8019AS), and amongst its multitude of configurable host-bus 34 | modes, it has one that matches the 68k processor bus nicely. 35 | 36 | Since the Macintosh SE lacks any real infrastructure for expansion beyond 37 | "here's a connector with the CPU bus on it," the SEthernet board is a very dumb 38 | device. It simply maps the ENC624J600 into a vacant chunk of memory at `0x80 39 | 0000`-`0x80 ffff` and connects it to an interrupt line. Hardware detection, 40 | interrupt vector interception, and device configuration are all left to the 41 | driver to implement. 42 | 43 | The Macintosh SE/30 is a more civilised machine, and the SEthernet/30 provides a 44 | [declaration ROM](rom/se30) and multiple addressing options (configurable by 45 | jumper) to take full advantage of the Slot Manager and coexist with multiple PDS 46 | cards. Potentially, the driver could even be built into the declaration ROM, 47 | allowing for a truly plug-and-play solution. To facilitate driver updates, the 48 | declaration ROM is a flash chip, with logic to allow for in-system programming. 49 | 50 | ## Current project status 51 | 52 | ### Hardware 53 | 54 | Hardware Revision 1 is largely 'done', and corrects the numerous issues of my 55 | initial "Revision 0" boards. A few mechanical tweaks remain to get mounting 56 | holes in exactly the right spot, and relocate connectors for easier access. 57 | 58 | ### Software 59 | 60 | The core driver is functional and stable enough for everyday use. Real-world 61 | performance is marginally better than a comparable vintage card, but there is 62 | likely room to improve - especially in the rather convoluted receive routine. 63 | 64 | Support for promiscuous mode, and the `ERead` call are not yet implemented. 65 | 66 | The installer has some rough edges (no hardware detection or driver version 67 | detection on the SE), but functions as a way to get drivers onto a system. 68 | 69 | The SE/30 board's Declaration ROM must be programmed before the card can be 70 | recognized. This can be done in-system using the 71 | [programROM](software/tools/programROM/) application provided on the installer 72 | disk, but right now this is a very ugly and user-unfriendly console application, 73 | and really should be turned into a proper GUI app. 74 | 75 | Testing has been done on my unmodified SE and SE/30 under Systems 6.0.8, 7.1, 76 | and 7.5.5. 77 | 78 | A driver for A/UX 2 and 3 can be found at 79 | https://github.com/rhalkyard/SEthernet-AUX. 80 | 81 | ## Project files 82 | 83 | Schematics: [SEthernet](boards/se/se.pdf) - 84 | [SEthernet/30](boards/se30/se30.pdf) - [Breakout Board](boards/breakout/breakout.pdf) 85 | 86 | Glue Logic: [SEthernet](pld/se) - [SEthernet/30](pld/se30) 87 | 88 | [SEthernet30 Declaration ROM](rom/se30) 89 | 90 | [Driver](software/driver) 91 | 92 | ## Required tools 93 | 94 | ### Schematics, board layout and BOM 95 | 96 | [KiCad 7](https://www.kicad.org/) 97 | 98 | ### Programmable Logic 99 | 100 | [WinCUPL and ATMISP](https://www.microchip.com/en-us/products/fpgas-and-plds/spld-cplds/pld-design-resources) 101 | (see [`pld/README.md`](pld/README.md) for notes on WinCUPL) 102 | 103 | ### Declaration ROM, driver and software 104 | 105 | - [Retro68](https://github.com/autc04/Retro68) built with [Apple Universal 106 | Interfaces](https://github.com/autc04/Retro68#apple-universal-interfaces-vs-multiversal-interfaces) 107 | (at the time of writing, the default free-software Multiversal Interfaces do 108 | not include some necessary headers) 109 | 110 | - [CMake](https://cmake.org/) 111 | 112 | - [Python 3](https://www.python.org) 113 | 114 | ### Installer 115 | 116 | - [hfsutils](https://www.mars.org/home/rob/proj/hfs/) 117 | 118 | - [Python 3](https://www.python.org) 119 | 120 | - [Mini vMac](https://www.gryphel.com/c/minivmac/index.html) 121 | 122 | ## License 123 | 124 | The hardware components of this project ([board designs](boards/) and 125 | [programmable logic](pld/)) are licensed under the [Strongly-Reciprocal CERN 126 | Open Hardware License, version 2](CERN-OHL-S-2.0). 127 | 128 | The [software components](software/) of this project are licensed under the [GNU 129 | General Public License v3.0](COPYING). 130 | 131 | While my choice to make this project open-source is very much a deliberate one, 132 | I have a few further requests, on the basis of "I can't and won't enforce these, 133 | but I'd appreciate if you respected them." It's unfortunate that I have to 134 | mention this, but given how a few recent projects in the retro-hardware 135 | community have splintered, I feel that it's necessary to say: 136 | 137 | - Please don't sell cards commercially/for profit without checking with me 138 | first. I have plans for a sales and manufacturing partnership that will sell 139 | boards affordably, with full support. I don't mind if you order a batch of 140 | boards for yourself and sell the extras on a break-even basis, but please make 141 | it clear that these are not 'official' SEthernet products. 142 | 143 | - Please contribute back, rather than simply forking the project. I strongly 144 | encourage people to modify the design, adapt it to other systems, integrate it 145 | into other devices. But unless what you're working on is a radical departure 146 | from the basic SEthernet design, I'd much rather see a unified, collaborative 147 | effort that is supported by a single software stack, rather than having it be 148 | fragmented. 149 | 150 | If you have any questions, feel free to reach out to me, I don't bite! 151 | 152 | ## Useful reference material 153 | 154 | - [ENC624J600 datasheet](https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/39935c.pdf) 155 | 156 | - [Designing Cards and Drivers for the Macintosh Family (3rd 157 | Edition)](https://www.vintageapple.org/inside_o/pdf/Designing_Cards_and_Drivers_for_the_Macintosh_Family_3rd_Edition_1992.pdf) 158 | 159 | - [Inside Macintosh (1994): Networking](https://www.vintageapple.org/inside_r/pdf/Networking_1994.pdf) 160 | 161 | - [Inside Macintosh (1994): Devices](https://www.vintageapple.org/inside_r/pdf/Devices_1994.pdf) 162 | 163 | - [Glenn Anderson's ethernet driver page](https://www.mactcp.net/ethernet.html) - 164 | contains C source for a DP8390-based card. 165 | 166 | - [Mac Driver Museum: Network 167 | Drivers](https://vintageapple.org/macdrivers/network.shtml) - good material 168 | for reverse-engineering. In particular, the Dayna ethernet drivers contain 169 | debug symbols, which are quite handy for our purposes. 170 | 171 | - [Rob Braun's declaration ROM page](http://www.synack.net/~bbraun/declrom.html) 172 | has useful information about declaration ROMs and how they work. 173 | 174 | [^howhard]: [Arrested Development narrator voice]: It was, in fact, quite hard 175 | -------------------------------------------------------------------------------- /software/driver/readpacket.S: -------------------------------------------------------------------------------- 1 | /* 2 | SEthernet and SEthernet/30 Driver 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #include "macsbug.inc" 20 | 21 | .global readPacket 22 | .global _readBuf 23 | 24 | /* Offsets of fields within enc624j600 struct */ 25 | enc624j600_base_address = 0 /* Base address of chip (also start of transmit 26 | buffer) */ 27 | enc624j600_rxbuf_start = 4 /* Pointer to start of receive buffer */ 28 | enc624j600_rxbuf_end = 8 /* Pointer to end of receive buffer */ 29 | enc624j600_rxptr = 12 /* Receive buffer read pointer */ 30 | enc624j600_link_state = 16 /* Current link state (updated by calls to 31 | enc624j600_duplex_sync) */ 32 | /* 33 | BlockMove A-traps 34 | 35 | Arguments: 36 | A0 Source 37 | A1 Destination 38 | D0.L Count 39 | 40 | Changes A0-A1, D0-D2. Returns with D0=0 41 | 42 | BlockMove flushes processor caches. In later versions of the Memory Manager 43 | (which?), BlockMoveData does not flush caches, in early versions it is a synonym 44 | for BlockMove. 45 | */ 46 | .macro _BlockMove 47 | .word 0xa02e 48 | .endm 49 | 50 | .macro _BlockMoveData 51 | .word 0xa22e 52 | .endm 53 | 54 | /* 55 | readPacket and readRest are called by protocol handlers to copy data from 56 | the receive FIFO into their own buffers. Their calling conventions and register 57 | usage are particularly awkward, hence implementing them in asm. 58 | 59 | readPacket reads a given number of bytes from the packet into a buffer provided 60 | by the protocol handler. readRest does the same, but signals completion to the 61 | driver and discards any remaining unread data. Protocol handlers can call 62 | readPacket any number of times, but they MUST call readRest once (and only once) 63 | when they are finished with a packet. 64 | 65 | The address of readPacket must be in A4 when the protocol handler is called. 66 | DO NOT CALL THIS FUNCTION DIRECTLY FROM THE DRIVER. 67 | 68 | On entry: 69 | A0: ours (unused) 70 | A1: ours (pointer to chip data structure) 71 | A3: buffer to write into 72 | D1: number of bytes remaining 73 | D3: maximum number of bytes to read 74 | 75 | On exit: 76 | A0: changed 77 | A1: preserved 78 | A2: preserved 79 | A3: buffer pointer (updated to point to byte following last byte written) 80 | A4-A5: preserved 81 | D0: changed (OSErr value) 82 | D1: number of bytes remaining (updated to reflect bytes read) 83 | D2: preserved 84 | D3: 0 if requested number of bytes read 85 | <0 if packet was (-D3) bytes too large to fit in buffer (readRest only) 86 | >0 if D3 bytes weren't read 87 | D4-D7: preserved 88 | 89 | CCR Z flag indicates error state (set if no error, clear if error) 90 | */ 91 | 92 | readPacket: 93 | /* readRest is called as readPacket+2, this branch must fit into 2 bytes! */ 94 | BRA.B realReadPacket 95 | 96 | readRest: 97 | .if . != readPacket+2 /* just to make sure */ 98 | .err 99 | .endif 100 | TST.W %d3 /* check for zero-size read */ 101 | JEQ readRest_done /* nothing to do */ 102 | JBSR _readBuf /* read the data */ 103 | /* 104 | ReadBuf returns with: 105 | D1 = number of bytes remaining in packet 106 | D3 = difference between requested bytes and actual bytes read 107 | D3 = requested - MIN(available, requested) 108 | Possible outcomes: 109 | - We asked for more data than was left in the packet 110 | D1 = 0 (no data remaining in packet) 111 | D3 > 0 (number of bytes that we asked for but didn't get) 112 | => D3 = D3 - 0 = D3 113 | - We got exactly as much data as was available 114 | D1 = 0 (no data remaining) 115 | D3 = 0 (we got as many bytes as we asked for) 116 | => D3 = 0 - 0 = 0 117 | - There was more data left in the packet than we asked for 118 | D1 > 0 (bytes remaining in packet) 119 | D3 = 0 (we got as many bytes as we asked for) 120 | => D3 = 0 - D1 = -D1 121 | */ 122 | readRest_done: 123 | SUB.W %d1, %d3 /* D3 = D3 - D1 (see comment above) */ 124 | MOVEQ #0, %d0 /* set Z flag, no failure no matter what */ 125 | RTS 126 | 127 | realReadPacket: 128 | JBSR _readBuf /* otherwise, read the data */ 129 | JNE readPacket_err /* Z flag set if OK, clear if error */ 130 | RTS 131 | 132 | readPacket_err: 133 | #ifdef DEBUG 134 | _Debugger 135 | #endif 136 | MOVEQ #-92, %d0 /* eLenErr */ 137 | RTS 138 | MacsbugSymbol "readPacket" 139 | 140 | /* 141 | Copy data out of the ENC624J600's receive ring buffer 142 | 143 | On entry: 144 | A1: Address of chip data structure (struct enc624j600) 145 | A3: Destination pointer 146 | D1: Remaining bytes in packet 147 | D3: Number of bytes to read 148 | 149 | On exit: 150 | A0: Changed 151 | A1: Preserved 152 | A2: Preserved 153 | A3: Pointer to one byte past last byte written 154 | A4-A5: Preserved 155 | D0: Number of bytes actually read 156 | D1: Remaining bytes in packet (updated) 157 | D2: Preserved 158 | D3: Difference between requested bytes and actual bytes read 159 | (requested - MIN(available, requested)) 160 | 161 | CCR Z flag reflects value of D3 (set = no delta, clear = fewer bytes were 162 | read than requested) 163 | */ 164 | _readBuf: 165 | MOVE.W %d3, %d0 /* D0 = number of bytes to read */ 166 | CMP.W %d1, %d0 167 | JLS 1f /* D0 = MIN(D1, D0) */ 168 | MOVE.W %d1, %d0 169 | 1: 170 | #ifdef DEBUG 171 | MOVEM.L %a1/%d0-%d2, -(%sp) /* Preserve registers changed by C */ 172 | MOVE.W %d0, -(%sp) 173 | MOVE.W #0x8020, -(%sp) 174 | MOVE.L %a1, -(%sp) 175 | JSR debug_log /* debug_log(A1, #0x8020, D0.W) */ 176 | ADDQ #8, %sp /* Remove args from stack */ 177 | MOVEM.L (%sp)+, %a1/%d0-%d2 /* Restore registers */ 178 | #endif 179 | 180 | MOVEM.L %a1/%d0-%d2, -(%sp) /* BlockMove changes A0-A1, D0-D2 */ 181 | 182 | /* Clear the top half of D0 since BlockMove takes its byte count as a 183 | longword quantity, and we've been calculating it as a word so far. The 184 | SWAP/CLR.W trick is apparently faster than AND.L #ffff */ 185 | SWAP %d0 186 | CLR.W %d0 187 | SWAP %d0 188 | 189 | MOVE.L enc624j600_rxptr(%a1), %a0 /* A0 = read ptr */ 190 | MOVE.L %a0, %d2 191 | ADD.W %d0, %d2 /* D2 = read ptr + bytes to copy */ 192 | CMP.L enc624j600_rxbuf_end(%a1), %d2 /* Check against buffer bounds */ 193 | JGT readBuf_wrap /* Handle wraparound case */ 194 | 195 | readBuf_main: 196 | MOVE.L %d2, enc624j600_rxptr(%a1) /* Save updated read pointer */ 197 | MOVE.L %a3, %a1 /* A1 = write pointer */ 198 | ADD.W %d0, %a3 /* Advance write pointer */ 199 | _BlockMoveData 200 | MOVEM.L (%sp)+, %a1/%d0-%d2 201 | 202 | SUB.W %d0, %d1 /* Update remaining-byte count */ 203 | /* This operation must come last so that a successful read sets the Z flag */ 204 | SUB.W %d0, %d3 /* Calculate remaining bytes in pkt */ 205 | RTS 206 | 207 | readBuf_wrap: 208 | /* Read to end of buffer, then jump to readBuf_main to read remaining data 209 | from the start of the buffer */ 210 | MOVE.L enc624j600_rxbuf_end(%a1), %d1 211 | SUB.L %a0, %d1 /* D1 = bytes to end of buffer */ 212 | SUB.W %d1, %d0 /* D0 = size of 2nd chunk */ 213 | MOVEM.L %a1/%d0, -(%sp) /* Save struct ptr and 2nd chunk size */ 214 | 215 | MOVE.W %d1, %d0 /* D0 = bytes to end of buffer */ 216 | MOVE.L %a3, %a1 /* A1 = write pointer */ 217 | ADD.W %d0, %a3 /* Advance write pointer */ 218 | _BlockMoveData 219 | 220 | MOVEM.L (%sp)+, %a1/%d0 /* Restore struct ptr and 2nd chunk size */ 221 | MOVE.L enc624j600_rxbuf_start(%a1), %a0 222 | MOVE.L %a0, %d2 223 | ADD.W %d0, %d2 /* D2 = buffer start + 2nd chunk size */ 224 | JRA readBuf_main /* Finish up second chunk */ 225 | 226 | RTS /* Dummy RTS for Macsbug symbol */ 227 | MacsbugSymbol "_readBuf" 228 | 229 | -------------------------------------------------------------------------------- /pld/se30/se30-u3.pld: -------------------------------------------------------------------------------- 1 | Name se30-u3 ; 2 | PartNo 00 ; 3 | Date 2023-10-23 ; 4 | Revision 01 ; 5 | Designer rhalkyard ; 6 | Company none ; 7 | Assembly SEthernet/30 Macintosh SE/30 Ethernet card ; 8 | Location U3 ; 9 | Device f1502isptqfp44 ; 10 | 11 | /* Enable JTAG interface with internal 10K pullups on TDI and TMS */ 12 | PROPERTY ATMEL { JTAG = ON } ; 13 | PROPERTY ATMEL { TDI_PULLUP = ON } ; 14 | PROPERTY ATMEL { TMS_PULLUP = ON } ; 15 | 16 | /* Consider pin assignments to be immutable; don't try to rearrange the design 17 | if it does not fit with the given pin assignments */ 18 | PROPERTY ATMEL { PREASSIGN = KEEP } ; 19 | 20 | /* 21 | Bus-control logic for SEthernet/30 22 | 23 | Signal names in ALL CAPS are inputs or outputs. Signal names in lowercase are 24 | internal. 25 | */ 26 | 27 | /* CONTROL INPUTS */ 28 | PIN 11 = !ETH_INT ; /* Interrupt input from ENC624J600 */ 29 | PIN 23 = !AS ; /* Address strobe from 68030 */ 30 | PIN 25 = SIZ0 ; /* Access-size flag from 68030 */ 31 | PIN 27 = SIZ1 ; /* Access-size flag from 68030 */ 32 | PIN 28 = RW ; /* Read/!Write from 68030 */ 33 | PIN 33 = !DS ; /* Data strobe from 68030 */ 34 | PIN 35 = !SLOTID_0 ; /* Slot ID jumper */ 35 | PIN 37 = !SLOTID_1 ; /* Slot ID jumper */ 36 | PIN 38 = VPP ; /* Spare, input-only, shared with +12V VPP */ 37 | PIN 39 = !RESET ; /* Reset input from system */ 38 | PIN 40 = ETH_CLK ; /* Software-configurable clock from ENC624J600 */ 39 | 40 | /* ADDRESS INPUTS */ 41 | PIN 3 = A0 ; 42 | PIN 12 = A16 ; 43 | PIN [22..18,15..13] = [A31..24] ; 44 | 45 | /* OUTPUTS */ 46 | PIN 2 = !ROM_OE ; /* Output enable to flash ROM */ 47 | PIN 5 = !ROM_WE ; /* Write enable to flash ROM */ 48 | PIN 6 = B0SEL ; /* Low-address byte select to ENC624J600 */ 49 | PIN 8 = ETH_CS ; /* Chip select to ENC624J600 */ 50 | PIN 10 = B1SEL ; /* High-address byte select to ENC624J600 */ 51 | PIN 30 = DSACK0 ; /* Data acknowledge flag to 68030 */ 52 | PIN 31 = DSACK1 ; /* Data acknowledge flag to 68030 */ 53 | PIN 34 = TP6 ; /* Spare, I/O, connected to test point */ 54 | PIN 42 = !IRQ3 ; /* Interrupt output to 68030 */ 55 | PIN 43 = !IRQ2 ; /* Interrupt output to 68030 */ 56 | PIN 44 = !IRQ1 ; /* Interrupt output to 68030 */ 57 | 58 | /* Note that DSACK0 and DSACK1 are active-low signals but we do NOT declare them 59 | inverted here, as the ATF1502 fitter seems to ignore output enables on pins that 60 | are declared inverted */ 61 | 62 | /* IRQ outputs are wired-or; set output drivers to open-collector mode for these 63 | pins (note that this does NOT invert sense, they must still be declared above as 64 | inverted). Note that DSACK outputs are also open-collector, but we treat them 65 | specially in order to get around rise-time issues. */ 66 | PROPERTY ATMEL { OPEN_COLLECTOR = IRQ1, IRQ2, IRQ3 } ; 67 | 68 | /* 69 | RESERVED 70 | PIN 1 = TDI ; 71 | PIN 7 = TMS ; 72 | PIN 26 = TCK ; 73 | PIN 32 = TDO ; 74 | 75 | PIN 9 = VCC ; 76 | PIN 17 = VCC ; 77 | PIN 29 = VCC ; 78 | PIN 41 = VCC ; 79 | 80 | PIN 4 = GND ; 81 | PIN 16 = GND ; 82 | PIN 24 = GND ; 83 | PIN 36 = GND ; 84 | */ 85 | 86 | /* Counter to delay asserting DSACK on read operations */ 87 | NODE [dsack_count1..0] ; 88 | FIELD dsack_counter = [dsack_count1..0] ; 89 | 90 | /* Counter to generate holdoff period between consecutive accessses */ 91 | NODE [holdoff_count1..0] ; 92 | FIELD holdoff_counter = [holdoff_count1..0] ; 93 | 94 | NODE holdoff_state ; /* Latch indicating holdoff state */ 95 | 96 | /* Counter to inhibit interrupts until ENC624J600 has been written to and presumably reset */ 97 | NODE [write_count1..0] ; 98 | FIELD write_counter = [write_count1..0] ; 99 | 100 | /* Latch for extended DSACK enable */ 101 | NODE dsack_extend ; 102 | 103 | FIELD ADDR_H = [A31..24] ; /* High nybble of ADDR, used in simulator only */ 104 | FIELD ADDR = [A31..0] ; 105 | FIELD SLOTID = [SLOTID_1..0] ; 106 | FIELD SIZE = [SIZ1..0] ; 107 | 108 | /* Occupy different slot address based on jumper selection */ 109 | addressed = AS & SLOTID:'b'00 & ADDR:'h'F9xxxxxx ; 110 | APPEND addressed = AS & SLOTID:'b'01 & ADDR:'h'FAxxxxxx ; 111 | APPEND addressed = AS & SLOTID:'b'10 & ADDR:'h'FBxxxxxx ; 112 | APPEND addressed = AS & SLOTID:'b'11 & ADDR:'h'FExxxxxx ; 113 | 114 | /* Only select devices after holdoff period has cleared */ 115 | select = addressed & !holdoff_state ; 116 | 117 | /* Generate chip selects based on A16 address bit */ 118 | ETH_CS = select & !A16 ; /* A16 low: ENC624J600 */ 119 | rom_cs = select & A16 ; /* A16 high: ROM */ 120 | 121 | /* Turn internal rom_cs signal into output-enable and write-enable */ 122 | ROM_OE = rom_cs & RW ; 123 | ROM_WE = rom_cs & !RW ; 124 | 125 | /* 126 | Generate !DSACKx transaction-complete signals - these also indicate the size of 127 | the port 128 | 129 | !DSACK1 !DSACK0 130 | L L 32 bit bus cycle complete 131 | L H 16 bit bus cycle complete 132 | H L 8 bit bus cycle complete 133 | H H Bus cycle in progress, insert wait states 134 | */ 135 | DSACK0 = !(rom_cs & !wait) ; /* ROM data bus is 8-bit (DSACK='b'01) */ 136 | DSACK1 = !(ETH_CS & !wait) ; /* ENC624J600 data bus is 16 bit (DSACK='b'10) */ 137 | 138 | /* 139 | Note the 'dsack_extend' term here. DSACK must be tristated when we are not 140 | addressed, but we are also expected to explicitly drive it high once /AS is 141 | deasserted - dsack_extend extends the output-enable period until /AS is asserted 142 | at the start of the next bus transaction. 143 | */ 144 | DSACK0.oe = addressed # dsack_extend ; 145 | DSACK1.oe = addressed # dsack_extend ; 146 | 147 | dsack_extend.d = 'b'1 ; 148 | dsack_extend.ck = !addressed ; /* Enter extend state when we are deselected */ 149 | dsack_extend.ar = AS # RESET ; /* Exit on start of next bus cycle */ 150 | 151 | /* 152 | Generate byte strobes for ENC624J600 accesses, based on low address bit and data 153 | size requested. 154 | 155 | The SIZE field (SIZ1, SIZ0) indicates the *remaining* size to be read/written: 156 | 157 | 00 32 bits 158 | 01 8 bits 159 | 10 16 bits 160 | 11 24 bits (32-bit accesses to odd addresses are broken up as 8+24) 161 | 162 | e.g. for a 32 bit write to an 8-bit port, there would be 4 bus cycles, with SIZE 163 | going 00-11-10-01. 164 | */ 165 | B0SEL = ETH_CS & DS & !A0 ; /* B0SEL on all even accesses */ 166 | B1SEL = ETH_CS & DS & (A0 # ! (SIZE:'b'01)) ; /* B1SEL on odd byte accesses, 167 | and all >byte accesses */ 168 | 169 | /* Suppress interrupts until write counter asserts interrupt-enable signal */ 170 | interrupt = ETH_INT & interrupt_enable ; 171 | 172 | /* Each slot has a different interrupt line */ 173 | IRQ1 = interrupt & (SLOTID:'b'00 # SLOTID:'b'11) ; /* IRQ1: slot 9 or E */ 174 | IRQ2 = interrupt & SLOTID:'b'01 ; /* IRQ2: Slot A */ 175 | IRQ3 = interrupt & SLOTID:'b'10 ; /* IRQ3: Slot B */ 176 | 177 | /* 178 | Counters for enforcing timing requirements, clocked from the ENC624J600's clock 179 | output (4MHz by default, configured to 25MHz by the driver). 180 | 181 | holdoff_counter counts time elapsed since the end of the last bus transaction 182 | that addressed us. 183 | 184 | dtack_counter counts time elapsed since the start of a bus transaction 185 | addressing us. 186 | */ 187 | 188 | sequence holdoff_counter { 189 | present 'd'0 190 | next 'd'1 ; 191 | present 'd'1 192 | next 'd'2 ; 193 | present 'd'2 194 | next 'd'3 ; 195 | present 'd'3 196 | next 'd'3 ; 197 | } 198 | holdoff_counter.ck = ETH_CLK ; 199 | holdoff_counter.ar = select ; /* Hold counter in reset while we are selected */ 200 | 201 | sequence dsack_counter { 202 | present 'd'0 203 | next 'd'1 ; 204 | present 'd'1 205 | next 'd'2 ; 206 | present 'd'2 207 | next 'd'3 ; 208 | present 'd'3 209 | next 'd'3 ; 210 | } 211 | dsack_counter.ck = ETH_CLK ; 212 | dsack_counter.ar = !select ; /* Hold counter in reset when we're not selected */ 213 | 214 | /* 215 | Enter holdoff state at the end of a write access, remain in holdoff until 216 | counter expires. ENC624J600 requires 40ns. 217 | 218 | Holdoff for 1 full clock period = 40-80ns @ 25MHz 219 | */ 220 | holdoff_state.d = 'b'1 ; 221 | holdoff_state.ck = !(addressed & !RW) ; 222 | holdoff_state.ar = RESET # holdoff_counter:['d'2..'d'3] ; 223 | 224 | /* 225 | Delay DSACK during read accesses. ENC624J600 requires 75ns 226 | 227 | 2 full clock periods = 80-120ns @ 25MHz 228 | */ 229 | wait = RW & dsack_counter:['d'0..'d'2] ; 230 | 231 | /* The ENC624J600 does not have a hardware reset input - only power-on reset and 232 | a software-controlled reset operation, so if the system is reset without 233 | 'cleanly' shutting down, it will continue generating interrupts, which can cause 234 | Sad Macs and crashes in the subsquent boot. To get around this deficiency, we 235 | gate the interrupt output until 3 writes have been made to the chip address 236 | space, which should serve as an adequate indication that software is in control 237 | of the chip and is ready to handle interrupts */ 238 | sequence write_counter { 239 | present 'd'0 240 | next 'd'1 ; 241 | present 'd'1 242 | next 'd'2 ; 243 | present 'd'2 244 | next 'd'3 ; 245 | present 'd'3 246 | next 'd'3 ; 247 | } 248 | write_counter.ck = !(ETH_CS & !RW) ; /* Increment counter on each write access */ 249 | write_counter.ar = RESET ; 250 | interrupt_enable = write_counter:'d'3 ; 251 | -------------------------------------------------------------------------------- /software/shared/enc624j600/include/enc624j600.h: -------------------------------------------------------------------------------- 1 | /* 2 | Low-level interface to Microchip ENC624J600 Ethernet controller 3 | 4 | Copyright (C) 2023-2024 Richard Halkyard 5 | 6 | This program is free software: you can redistribute it and/or modify it under 7 | the terms of the GNU General Public License as published by the Free Software 8 | Foundation, either version 3 of the License, or (at your option) any later 9 | version. 10 | 11 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 13 | PARTICULAR PURPOSE. See the GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program. If not, see . 17 | */ 18 | 19 | #ifndef _ENC624J600_H_ 20 | #define _ENC624J600_H_ 21 | 22 | #include "enc624j600_registers.h" 23 | 24 | #if !defined(REV0_SUPPORT) 25 | #include 26 | #endif 27 | 28 | /* Link state value is mirrored from the SPDDPX field of the PHSTAT3 register, 29 | with the addition of a 0 value indicating a down link. */ 30 | enum enc624j600_link_state { 31 | LINK_DOWN = 0x0, 32 | LINK_10M = 0x1, 33 | LINK_100M = 0x2, 34 | LINK_FULLDPX = 0x4, 35 | LINK_10M_FULLDPX = 0x5, /* LINK_10M | LINK_FULLDPX */ 36 | LINK_100M_FULLDPX = 0x6 /* LINK_100M | LINK_FULLDPX */ 37 | }; 38 | 39 | struct enc624j600 { 40 | unsigned char *base_address; /* Base address of chip (also start of transmit 41 | buffer) */ 42 | const unsigned char *rxbuf_start; /* Pointer to start of receive buffer */ 43 | const unsigned char *rxbuf_end; /* Pointer to end of receive buffer */ 44 | const unsigned char *rxptr; /* Receive buffer read pointer */ 45 | unsigned char link_state; /* Current link state (updated by calls to 46 | enc624j600_duplex_sync) */ 47 | }; 48 | typedef struct enc624j600 enc624j600; 49 | 50 | /* Receive Status Vector */ 51 | union enc624j600_rsv { 52 | struct { 53 | unsigned short pkt_len_le; /* packet length - LITTLE ENDIAN VALUE */ 54 | unsigned char bits_23_16; /* Bits 23-16 */ 55 | unsigned char bits_31_24; /* Bits 31-24 */ 56 | unsigned char bits_39_32; /* Bits 39-32 */ 57 | unsigned char bits_47_40; /* Bits 47-40 (all zeroes) */ 58 | } __attribute__((packed)); 59 | unsigned char bytes[6]; /* Access as a byte array, for use with RSV_BIT() 60 | macro */ 61 | }; 62 | typedef union enc624j600_rsv enc624j600_rsv; 63 | 64 | /* Note: While the ENC624J600 is a natively little-endian device, all the 65 | functions below automatically byte-swap values as necessary. Manual 66 | byte-swapping is only required when reading the next-packet pointer and the 67 | pkt_len_le Receive Status Vector field from the receive buffer. */ 68 | 69 | /* Reset the chip. Must wait at least 25+256us before accessing chip again*/ 70 | short enc624j600_reset(const enc624j600 *chip); 71 | 72 | /* Synchronise MAC duplex settings with PHY values. Call after link-change 73 | * event. */ 74 | void enc624j600_duplex_sync(enc624j600 *chip); 75 | 76 | /* Initialize the chip, with the given transmit buffer size. The receive buffer 77 | begins immediately after thee transmit buffer and continues to end of memory. 78 | Transmit buffer size must be an even number of bytes. */ 79 | short enc624j600_init(enc624j600 *chip, const unsigned short txbuf_size); 80 | 81 | /* Start accepting packets */ 82 | void enc624j600_start(enc624j600 *chip); 83 | 84 | /* Read device-ID and revision registers 85 | device_id: out-parameter, 1 byte, NULL allowed 86 | revision: out-parameter, 1 byte, NULL allowed 87 | */ 88 | void enc624j600_read_id(const enc624j600 *chip, unsigned char *device_id, 89 | unsigned char *revision); 90 | 91 | /* Read ethernet address into addrbuf */ 92 | void enc624j600_read_hwaddr(const enc624j600 *chip, unsigned char addrbuf[6]); 93 | 94 | /* Set ethernet address */ 95 | void enc624j600_write_hwaddr(const enc624j600 *chip, 96 | const unsigned char addrbuf[6]); 97 | 98 | /* Enable promiscuous mode */ 99 | void enc624j600_enable_promiscuous(const enc624j600 *chip); 100 | 101 | /* Disable promiscuous mode */ 102 | void enc624j600_disable_promiscuous(const enc624j600 *chip); 103 | 104 | /* Update the multicast hash table */ 105 | void enc624j600_write_multicast_table(const enc624j600 *chip, 106 | const unsigned short table[4]); 107 | 108 | /* Enable interrupts. Bits of irqmask are defined below */ 109 | static inline void enc624j600_enable_irq(const enc624j600 *chip, 110 | const unsigned short irqmask) { 111 | ENC624J600_SET_BITS(chip->base_address, EIE, irqmask); 112 | } 113 | 114 | /* Disable interrupts, returning old EIE register value for restoration with 115 | enc624j600_enable_irq. Bits of irqmask are defined below */ 116 | static inline unsigned short enc624j600_disable_irq(const enc624j600 *chip, 117 | const unsigned short irqmask) { 118 | unsigned short oldeie = ENC624J600_READ_REG(chip->base_address, EIE); 119 | ENC624J600_CLEAR_BITS(chip->base_address, EIE, irqmask); 120 | return oldeie; 121 | } 122 | 123 | /* Read interrupt state. Bits of return value are defined below */ 124 | static inline unsigned short enc624j600_read_irqstate(const enc624j600 *chip) { 125 | /* CRYPTEN bit of EIR is not an interrupt flag! mask it out */ 126 | return ENC624J600_READ_REG(chip->base_address, EIR) & (~EIR_CRYPTEN); 127 | } 128 | 129 | /* Clear interrupts. Bits of irqmask are defined below */ 130 | static inline void enc624j600_clear_irq(const enc624j600 *chip, 131 | const unsigned short irqmask) { 132 | /* CRYPTEN bit of EIR is not an interrupt flag! mask it out */ 133 | ENC624J600_CLEAR_BITS(chip->base_address, EIR, irqmask & (~EIR_CRYPTEN)); 134 | } 135 | 136 | /* Read count of pending frames in receive buffer */ 137 | static inline unsigned char enc624j600_read_rx_pending_count(const enc624j600 *chip) { 138 | return (ENC624J600_READ_REG(chip->base_address, ESTAT) & 139 | ESTAT_PKTCNT_MASK) >> ESTAT_PKTCNT_SHIFT; 140 | } 141 | 142 | /* Decrement pending frame count */ 143 | static inline void enc624j600_decrement_rx_pending_count(const enc624j600 *chip) { 144 | ENC624J600_SET_BITS(chip->base_address, ECON1, ECON1_PKTDEC); 145 | } 146 | 147 | /* Update tail of receive ring buffer. tail is a 'real' pointer into the receive 148 | buffer (as opposed to an offset relative to the chip base address). 149 | Automatically aligns to word boundaries */ 150 | void enc624j600_update_rxptr(enc624j600 *chip, const unsigned char *rxptr); 151 | 152 | /* Read the number of pending bytes in the receive FIFO */ 153 | unsigned short enc624j600_read_rx_fifo_level(const enc624j600 *chip); 154 | 155 | /* Convert a chip address into a real pointer */ 156 | static inline unsigned char *enc624j600_addr_to_ptr(const enc624j600 *chip, 157 | const unsigned short addr) { 158 | return chip->base_address + addr; 159 | } 160 | 161 | /* Convert a pointer into a chip address */ 162 | static inline unsigned short enc624j600_ptr_to_addr(const enc624j600 *chip, 163 | const unsigned char *ptr) { 164 | return ptr - chip->base_address; 165 | } 166 | 167 | /* Transmit a packet. src must be within the chip transmit buffer */ 168 | void enc624j600_transmit(const enc624j600 *chip, const unsigned char *src, 169 | unsigned short length); 170 | 171 | /* Read PHY register */ 172 | unsigned short enc624j600_read_phy_reg(const enc624j600 *chip, 173 | const unsigned char phyreg); 174 | 175 | /* Write PHY register */ 176 | void enc624j600_write_phy_reg(const enc624j600 *chip, 177 | const unsigned char phyreg, 178 | const unsigned short value); 179 | 180 | /* Enable PHY loopback */ 181 | void enc624j600_enable_phy_loopback(const enc624j600 *chip); 182 | 183 | /* Disable PHY loopback */ 184 | void enc624j600_disable_phy_loopback(const enc624j600 *chip); 185 | 186 | /* Our own memcpy implementation that avoids longword writes */ 187 | #if defined(REV0_SUPPORT) 188 | void enc624j600_memcpy(volatile unsigned char *dest, 189 | const unsigned char *source, const unsigned short len); 190 | #endif 191 | 192 | /* Check to see if an ENC624J600 is present and functioning at baseaddress */ 193 | short enc624j600_detect(const enc624j600 * chip); 194 | 195 | /* Test ENC624J600 memory */ 196 | short enc624j600_memtest(const enc624j600 * chip); 197 | 198 | /* Exchange the bytes in a word value */ 199 | #define SWAPBYTES(value) ((((value) & 0xff00) >> 8) | (((value) & 0x00ff) << 8)) 200 | 201 | /* An integer with bit n set */ 202 | #define BIT(n) (1 << (n)) 203 | 204 | /* Accessor macro for bits in the Receive Status Vector */ 205 | #define RSV_BIT(rsv, bitnum) \ 206 | ((rsv).bytes[((bitnum) >> 3)] & (BIT((bitnum) & 0x7))) 207 | 208 | /* Flag bits for irq functions */ 209 | 210 | /* Master IRQ enable (only valid for enc624j600_enable/disable_irq) */ 211 | #define IRQ_ENABLE BIT(7) 212 | /* Modular exponentiation intrrupt */ 213 | #define IRQ_MODEX BIT(6) 214 | /* Hashing engine interrupt */ 215 | #define IRQ_HASH BIT(5) 216 | /* AES engine interrupt */ 217 | #define IRQ_AES BIT(4) 218 | /* Link state change interrupt */ 219 | #define IRQ_LINK BIT(3) 220 | /* Packet receive interrupt */ 221 | #define IRQ_PKT BIT(14) 222 | /* DMA engine interrupt */ 223 | #define IRQ_DMA BIT(13) 224 | /* Transmit-complete interrupt */ 225 | #define IRQ_TX BIT(11) 226 | /* Transmit-abort inteerrupt */ 227 | #define IRQ_TX_ABORT BIT(10) 228 | /* Receive-abort (a.k.a buffer overflow) interrupt */ 229 | #define IRQ_RX_ABORT BIT(9) 230 | /* Receive packet counter overflow interrupt */ 231 | #define IRQ_PCNT_FULL BIT(8) 232 | 233 | /* Useful bits in the Receive Status Vector, for use with RSV_BIT() macro */ 234 | 235 | /* Packet was received with CRC error */ 236 | #define RSV_BIT_CRC_ERR (20) 237 | /* Packet length check error */ 238 | #define RSV_BIT_LENGTH_CHECK_ERR (21) 239 | /* Packet length out of range */ 240 | #define RSV_BIT_LENGTH_RANGE_ERR (22) 241 | /* Packet was received without errors */ 242 | #define RSV_BIT_OK (23) 243 | /* Destination is multicast */ 244 | #define RSV_BIT_MULTICAST (24) 245 | /* Destination is broadcast */ 246 | #define RSV_BIT_BROADCAST (25) 247 | /* Destination matched a hash table entry */ 248 | #define RSV_BIT_HASH_MATCH (33) 249 | /* Destination is unicast to us */ 250 | #define RSV_BIT_UNICAST (36) 251 | 252 | #endif /* _ENC624J600_H_ */ 253 | -------------------------------------------------------------------------------- /software/tools/testMemory/testMemory.c: -------------------------------------------------------------------------------- 1 | /* 2 | Simple tool to test card memory and calculate approximate memory bandwidth 3 | 4 | Separate tests for byte, word, and long-sized accesses in order to check for 5 | back-to-back access timing issues. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "sethernet30_board_defs.h" 18 | #include "sethernet_board_defs.h" 19 | 20 | #define TICK_SECONDS (1 / 60.0) 21 | 22 | static int memtest_byte(volatile char *addr, unsigned int len, 23 | unsigned int repeat) { 24 | volatile char *ptr; 25 | char pattern = 0x55; 26 | 27 | for (unsigned int i = 0; i < repeat; i++) { 28 | for (ptr = addr; ptr < addr + len; ptr++) { 29 | *ptr = pattern; 30 | } 31 | for (ptr = addr; ptr < addr + len; ptr++) { 32 | if (*ptr != pattern) { 33 | printf("Miscompare at 0x%08x: wrote %02x, read %02x\n", 34 | (unsigned int)ptr, pattern, *ptr); 35 | return 1; 36 | } 37 | } 38 | pattern = ~pattern; 39 | } 40 | return 0; 41 | } 42 | 43 | static int memspeed_bytewrite(volatile char *addr, unsigned int len, 44 | unsigned int repeat) { 45 | volatile char *ptr; 46 | char pattern = 0x55; 47 | unsigned int start, end; 48 | 49 | start = TickCount(); 50 | for (unsigned int i = 0; i < repeat; i++) { 51 | for (ptr = addr; ptr < addr + len; ptr++) { 52 | *ptr = pattern; 53 | } 54 | pattern = ~pattern; 55 | } 56 | end = TickCount(); 57 | return end - start; 58 | } 59 | 60 | static int memspeed_byteread(volatile char *addr, unsigned int len, 61 | unsigned int repeat) { 62 | volatile char *ptr; 63 | char pattern; 64 | unsigned int start, end; 65 | 66 | start = TickCount(); 67 | for (unsigned int i = 0; i < repeat; i++) { 68 | for (ptr = addr; ptr < addr + len; ptr++) { 69 | pattern = *ptr; 70 | (void)pattern; 71 | } 72 | } 73 | end = TickCount(); 74 | return end - start; 75 | } 76 | 77 | static int memtest_word(volatile char *addr, unsigned int len, 78 | unsigned int repeat) { 79 | volatile char *ptr; 80 | unsigned short pattern = 0x55aa; 81 | 82 | for (unsigned int i = 0; i < repeat; i++) { 83 | for (ptr = addr; ptr < addr + len; ptr += sizeof(unsigned short)) { 84 | *(unsigned short *)ptr = pattern; 85 | } 86 | for (ptr = addr; ptr < addr + len; ptr += sizeof(unsigned short)) { 87 | if (*(unsigned short *)ptr != pattern) { 88 | printf("Miscompare at 0x%08x: wrote %04x, read %04x\n", 89 | (unsigned int)ptr, pattern, *(unsigned short *)ptr); 90 | return 1; 91 | } 92 | } 93 | pattern = ~pattern; 94 | } 95 | return 0; 96 | } 97 | 98 | static int memspeed_wordwrite(volatile char *addr, unsigned int len, 99 | unsigned int repeat) { 100 | volatile char *ptr; 101 | unsigned short pattern = 0x55aa; 102 | unsigned int start, end; 103 | 104 | start = TickCount(); 105 | for (unsigned int i = 0; i < repeat; i++) { 106 | for (ptr = addr; ptr < addr + len; ptr += sizeof(unsigned short)) { 107 | *(unsigned short *)ptr = pattern; 108 | } 109 | pattern = ~pattern; 110 | } 111 | end = TickCount(); 112 | return end - start; 113 | } 114 | 115 | static int memspeed_wordread(volatile char *addr, unsigned int len, 116 | unsigned int repeat) { 117 | volatile char *ptr; 118 | unsigned short pattern; 119 | unsigned int start, end; 120 | 121 | start = TickCount(); 122 | for (unsigned int i = 0; i < repeat; i += sizeof(unsigned short)) { 123 | for (ptr = addr; ptr < addr + len; ptr++) { 124 | pattern = *(unsigned short *)ptr; 125 | (void)pattern; 126 | } 127 | } 128 | end = TickCount(); 129 | return end - start; 130 | } 131 | 132 | static int memtest_long(volatile char *addr, unsigned int len, 133 | unsigned int repeat) { 134 | volatile char *ptr; 135 | unsigned int pattern = 0x5555aaaa; 136 | 137 | for (unsigned int i = 0; i < repeat; i++) { 138 | for (ptr = addr; ptr < addr + len; ptr += sizeof(unsigned int)) { 139 | *(unsigned int *)ptr = pattern; 140 | } 141 | for (ptr = addr; ptr < addr + len; ptr += sizeof(unsigned int)) { 142 | if (*(unsigned int *)ptr != pattern) { 143 | printf("Miscompare at 0x%08x: wrote %08x, read %08x\n", 144 | (unsigned int)ptr, pattern, *(unsigned int *)ptr); 145 | return 1; 146 | } 147 | } 148 | pattern = ~pattern; 149 | } 150 | return 0; 151 | } 152 | 153 | static int memspeed_longwrite(volatile char *addr, unsigned int len, 154 | unsigned int repeat) { 155 | volatile char *ptr; 156 | unsigned long pattern = 0x55aa; 157 | unsigned int start, end; 158 | 159 | start = TickCount(); 160 | for (unsigned int i = 0; i < repeat; i++) { 161 | for (ptr = addr; ptr < addr + len; ptr += sizeof(unsigned long)) { 162 | *(unsigned long *)ptr = pattern; 163 | } 164 | pattern = ~pattern; 165 | } 166 | end = TickCount(); 167 | return end - start; 168 | } 169 | 170 | static int memspeed_longread(volatile char *addr, unsigned int len, 171 | unsigned int repeat) { 172 | volatile char *ptr; 173 | unsigned int pattern; 174 | unsigned int start, end; 175 | 176 | start = TickCount(); 177 | for (unsigned int i = 0; i < repeat; i += sizeof(unsigned int)) { 178 | for (ptr = addr; ptr < addr + len; ptr++) { 179 | pattern = *(unsigned int *)ptr; 180 | (void)pattern; 181 | } 182 | } 183 | end = TickCount(); 184 | return end - start; 185 | } 186 | 187 | void test_memory(Ptr addr, unsigned int len, int iterations) { 188 | int err; 189 | 190 | unsigned int nTicks; 191 | float speed; 192 | 193 | printf("Testing byte access...\n"); 194 | err = memtest_byte(addr, len, 1); 195 | if (!err) { 196 | printf("Memory check passed using byte read and write\n"); 197 | nTicks = memspeed_bytewrite(addr, len, iterations); 198 | speed = (len * iterations) / (nTicks * TICK_SECONDS); 199 | printf("Byte write: %d iterations in %d ticks = %f bytes/s\n", iterations, 200 | nTicks, speed); 201 | nTicks = memspeed_byteread(addr, len, iterations); 202 | speed = (len * iterations) / (nTicks * TICK_SECONDS); 203 | printf("Byte read: %d iterations in %d ticks = %f bytes/s\n", iterations, 204 | nTicks, speed); 205 | } 206 | 207 | printf("Testing word access...\n"); 208 | err = memtest_word(addr, len, 1); 209 | if (!err) { 210 | printf("Memory check passed using word read and write\n"); 211 | nTicks = memspeed_wordwrite(addr, len, iterations); 212 | speed = (len * iterations) / (nTicks * TICK_SECONDS); 213 | printf("Word write: %d iterations in %d ticks = %f bytes/s\n", iterations, 214 | nTicks, speed); 215 | nTicks = memspeed_wordread(addr, len, iterations); 216 | speed = (len * iterations) / (nTicks * TICK_SECONDS); 217 | printf("Word read: %d iterations in %d ticks = %f bytes/s\n", iterations, 218 | nTicks, speed); 219 | } 220 | 221 | printf("Testing long access...\n"); 222 | err = memtest_long(addr, len, 1); 223 | if (!err) { 224 | printf("Memory check passed using long read and write\n"); 225 | nTicks = memspeed_longwrite(addr, len, iterations); 226 | speed = (len * iterations) / (nTicks * TICK_SECONDS); 227 | printf("Long write: %d iterations in %d ticks = %f bytes/s\n", iterations, 228 | nTicks, speed); 229 | nTicks = memspeed_longread(addr, len, iterations); 230 | speed = (len * iterations) / (nTicks * TICK_SECONDS); 231 | printf("Long read: %d iterations in %d ticks = %f bytes/s\n", iterations, 232 | nTicks, speed); 233 | } 234 | } 235 | 236 | int findCards(Ptr cardAddresses[16]) { 237 | int cardCount = 0; 238 | long result; 239 | OSErr err; 240 | 241 | for (int i = 0; i < 16; i++) { 242 | cardAddresses[i] = nil; 243 | } 244 | 245 | SpBlock spb = {.spParamData = 1 << fall, 246 | .spCategory = catNetwork, 247 | .spCType = typeEtherNet, 248 | .spDrvrSW = SETHERNET30_DRSW, 249 | .spDrvrHW = SETHERNET30_DRHW, 250 | .spTBMask = 0, 251 | .spSlot = 1, 252 | .spID = 1, 253 | .spExtDev = 0}; 254 | 255 | err = SGetTypeSRsrc(&spb); 256 | while (err == noErr) { 257 | result = SFindDevBase(&spb); 258 | if (result == noErr) { 259 | printf("Found SEthernet/30 board in slot %x, base address %08x\n", 260 | spb.spSlot, (unsigned int)spb.spResult); 261 | cardAddresses[cardCount++] = (Ptr)spb.spResult; 262 | } else { 263 | printf( 264 | "Found SEthernet/30 board in slot %x, but SFindDevBase() " 265 | "returned an error: %d\n", 266 | spb.spSlot, (int)result); 267 | } 268 | err = SGetTypeSRsrc(&spb); 269 | } 270 | printf("SGetTypeSRsrc: %d\n", err); 271 | return cardCount; 272 | } 273 | 274 | void help(void) { 275 | printf( 276 | "SEthernet Memory Tester\n" 277 | "Choose an option:\n" 278 | " C: Search for and test installed cards\n" 279 | " R: Test and benchmark system RAM\n" 280 | " Q: Quit\n"); 281 | } 282 | 283 | int main(int argc, char **argv) { 284 | (void)argc; 285 | (void)argv; 286 | 287 | char choice; 288 | const unsigned int memlen = 0x6000; 289 | const int iterations = 1000; 290 | Ptr cardAddresses[16]; 291 | Ptr memstart; 292 | 293 | help(); 294 | while (1) { 295 | choice = getchar(); 296 | 297 | switch (toupper(choice)) { 298 | case 'C': 299 | printf("Searching for cards...\n"); 300 | int nCards = findCards(cardAddresses); 301 | // int nCards = 1; 302 | // cardAddresses[0] = 0xfe000000; 303 | 304 | if (nCards > 0) { 305 | printf("Found %d cards...\n", nCards); 306 | for (int i = 0; i < nCards; i++) { 307 | printf("Testing card at %08x...\n", (unsigned int)cardAddresses[i]); 308 | test_memory(cardAddresses[i], memlen, iterations); 309 | } 310 | } else { 311 | printf("No cards found\n"); 312 | } 313 | break; 314 | case 'R': 315 | memstart = NewPtr(memlen); 316 | printf("Testing RAM...\n"); 317 | test_memory(memstart, memlen, iterations); 318 | DisposePtr(memstart); 319 | break; 320 | case 'Q': 321 | return 0; 322 | case '\r': 323 | case '\n': 324 | continue; 325 | default: 326 | help(); 327 | break; 328 | } 329 | printf("\nTest [C]ard, Test [R]am, [Q]uit?\n"); 330 | } 331 | } 332 | --------------------------------------------------------------------------------