├── .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 |
--------------------------------------------------------------------------------