Update id48lib

* Add its own CMakeLists.txt
* Add id48 unit tests for its recovery API
* Add id48 unit tests for its generator API
* Add id48 espresso files, generator for those files, and validation test

* Proxmark3-specific:
  * update client/Makefile
  * update client/deps/id48lib.cmake
  *
This commit is contained in:
Henry Gabryjelski 2025-10-20 17:58:59 -07:00
parent c1441eb2ab
commit 563abf66ed
33 changed files with 2284 additions and 75 deletions

6
.gitignore vendored
View file

@ -45,13 +45,15 @@ Makefile.platform.*
# cmake
CMakeFiles/
deps/
client/CMakeFiles/
client/deps/
client/deps/**
!client/deps/id48/
!client/deps/id48/**
client/build/
client/android/build/
CMakeCache.txt
*.cmake
.cache
# Coverity
cov-int/

View file

@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Updated id48lib (adds unit tests, better build management)
- Added two trace files for Ultralight AES (@iceman1001)
- Added support for Ultralight AES secure messaing in `hf 14a raw` (@iceman1001)
- Added support for Ultralight AES secure messaging in `hf mfu info/rdbl/wrbl/dump/ndefread/wipe/setkey` (@iceman1001)

View file

@ -26,6 +26,9 @@ endif
ifeq ($(DEFSBEENHERE),)
-include ../../../Makefile.defs
endif
ifeq ($(DEFSBEENHERE),)
-include ../../../../Makefile.defs
endif
ifeq ($(DEFSBEENHERE),)
$(error Can't find Makefile.defs)
endif

View file

@ -74,8 +74,9 @@ HARDNESTEDLIB = $(HARDNESTEDLIBPATH)/libhardnested.a
HARDNESTEDLIBLD =
## ID48
ID48LIBPATH = ./deps/id48
ID48LIBINC = -I$(ID48LIBPATH)
## must be manually kept in sync with updates to deps/id48/CMakeLists.txt
ID48LIBPATH = ./deps/id48/src
ID48LIBINC = -I$(ID48LIBPATH)/../public
ID48LIB = $(ID48LIBPATH)/libid48.a
ID48LIBLD =

52
client/deps/id48/.gitignore vendored Normal file
View file

@ -0,0 +1,52 @@
# Now ignore any "build" directory anywhere under this directory
build/
**/build/
*.log
*.eml
*.html
*.o
*.a
*.d
*.elf
*.s19
*.map
*.bin
*.dll
*.moc.cpp
*.z
*.gz
*.Td
*.DS_Store
*.exe
*.dsym
*.json
*.old
*.swp
*.json.bak
*.pyc
*.bmp
# cmake
CMakeFiles/
client/CMakeFiles/
client/deps/**
!client/deps/id48/
!client/deps/id48/**
client/build/
client/android/build/
CMakeCache.txt
*.cmake
.cache
# Coverity
cov-int/
.coverity.conf
# .tmp files are created during compilation
*.tmp
# local codeql
_codeql*
/codeql

View file

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.21)
project(id48_project LANGUAGES C CXX ASM)
# Project-wide language standards (subdirectories inherit if not overridden)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(src)
add_subdirectory(tests)

View file

@ -48,3 +48,25 @@ provided the first half of the key, and at least one successful
authentication trio of nonce, challenge, and response, then
the library can recover all potentially valid values for the
second half of the key.
## Building
This project uses CMake, and thus should be easy to integrate
into any existing project using CMake.
The following commands can be run from the root of this depot,
and places all build artifacts into `./build`:
```bash
# Configure cmake for the current environment
# -S: explicitly specify the source directory
# -B: explicitly specify the directory for build artifacts
cmake -S . -B build
# Optionally (recommended) do a clean build each time
cmake --build ./build --parallel --target clean
# build everything
cmake --build ./build --parallel --target all
```

View file

@ -160,63 +160,63 @@ of the key influences a given output bit.
Start state + key bit --> output
| Start state | Key bit | Output |
|-------|-------|----------------|
| `s₀₀` | `K₃₉` | << hidden >> |
| `s₀₁` | `K₃₈` | << hidden >> |
| `s₀₂` | `K₃₇` | << hidden >> |
| `s₀₃` | `K₃₆` | << hidden >> |
| `s₀₄` | `K₃₅` | << hidden >> |
| `s₀₅` | `K₃₄` | << hidden >> |
| `s₀₆` | `K₃₃` | << hidden >> |
| `s₀₇` | `K₃₂` | `O₀₀ == frn₀₀` |
| `s₀₈` | `K₃₁` | `O₀₁ == frn₀₁` |
| `s₀₉` | `K₃₀` | `O₀₂ == frn₀₂` |
| `s₁₀` | `K₂₉` | `O₀₃ == frn₀₃` |
| `s₁₁` | `K₂₈` | `O₀₄ == frn₀₄` |
| `s₁₂` | `K₂₇` | `O₀₅ == frn₀₅` |
| `s₁₃` | `K₂₆` | `O₀₆ == frn₀₆` |
| `s₁₄` | `K₂₅` | `O₀₇ == frn₀₇` |
| `s₁₅` | `K₂₄` | `O₀₈ == frn₀₈` |
| `s₁₆` | `K₂₃` | `O₀₉ == frn₀₉` |
| `s₁₇` | `K₂₂` | `O₁₀ == frn₁₀` |
| `s₁₈` | `K₂₁` | `O₁₁ == frn₁₁` |
| `s₁₉` | `K₂₀` | `O₁₂ == frn₁₂` |
| `s₂₀` | `K₁₉` | `O₁₃ == frn₁₃` |
| `s₂₁` | `K₁₈` | `O₁₄ == frn₁₄` |
| `s₂₂` | `K₁₇` | `O₁₅ == frn₁₅` |
| `s₂₃` | `K₁₆` | `O₁₆ == frn₁₆` |
| `s₂₄` | `K₁₅` | `O₁₇ == frn₁₇` |
| `s₂₅` | `K₁₄` | `O₁₈ == frn₁₈` |
| `s₂₆` | `K₁₃` | `O₁₉ == frn₁₉` |
| `s₂₇` | `K₁₂` | `O₂₀ == frn₂₀` |
| `s₂₈` | `K₁₁` | `O₂₁ == frn₂₁` |
| `s₂₉` | `K₁₀` | `O₂₂ == frn₂₂` |
| `s₃₀` | `K₀₉` | `O₂₃ == frn₂₃` |
| `s₃₁` | `K₀₈` | `O₂₄ == frn₂₄` |
| `s₃₂` | `K₀₇` | `O₂₅ == frn₂₅` |
| `s₃₃` | `K₀₆` | `O₂₆ == frn₂₆` |
| `s₃₄` | `K₀₅` | `O₂₇ == frn₂₇` |
| `s₃₅` | `K₀₄` | `O₂₈ == grn₀₀` |
| `s₃₆` | `K₀₃` | `O₂₉ == grn₀₁` |
| `s₃₇` | `K₀₂` | `O₃₀ == grn₀₂` |
| `s₃₈` | `K₀₁` | `O₃₁ == grn₀₃` |
| `s₃₉` | `K₀₀` | `O₃₂ == grn₀₄` |
| `s₄₀` | `0₁₄` | `O₃₃ == grn₀₅` |
| `s₄₁` | `0₁₃` | `O₃₄ == grn₀₆` |
| `s₄₂` | `0₁₂` | `O₃₅ == grn₀₇` |
| `s₄₃` | `0₁₁` | `O₃₆ == grn₀₈` |
| `s₄₄` | `0₁₀` | `O₃₇ == grn₀₉` |
| `s₄₅` | `0₀₉` | `O₃₈ == grn₁₀` |
| `s₄₆` | `0₀₈` | `O₃₉ == grn₁₁` |
| `s₄₇` | `0₀₇` | `O₄₀ == grn₁₂` |
| `s₄₈` | `0₀₆` | `O₄₁ == grn₁₃` |
| `s₄₉` | `0₀₅` | `O₄₂ == grn₁₄` |
| `s₅₀` | `0₀₄` | `O₄₃ == grn₁₅` |
| `s₅₁` | `0₀₃` | `O₄₄ == grn₁₆` |
| `s₅₂` | `0₀₂` | `O₄₅ == grn₁₇` |
| `s₅₃` | `0₀₁` | `O₄₆ == grn₁₈` |
| `s₅₄` | `0₀₀` | `O₄₇ == grn₁₉` |
| Start state | Key bit | Output | Notes |
|-------|-------|----------------|-------|
| `s₀₀` | `K₃₉` | << hidden >> | Seven ... |
| `s₀₁` | `K₃₈` | << hidden >> | .. iterations ... |
| `s₀₂` | `K₃₇` | << hidden >> | .. before ... |
| `s₀₃` | `K₃₆` | << hidden >> | .. any ... |
| `s₀₄` | `K₃₅` | << hidden >> | .. output ... |
| `s₀₅` | `K₃₄` | << hidden >> | .. bits ... |
| `s₀₆` | `K₃₃` | << hidden >> | .. seen. |
| `s₀₇` | `K₃₂` | `O₀₀ == frn₀₀` | |
| `s₀₈` | `K₃₁` | `O₀₁ == frn₀₁` | |
| `s₀₉` | `K₃₀` | `O₀₂ == frn₀₂` | |
| `s₁₀` | `K₂₉` | `O₀₃ == frn₀₃` | |
| `s₁₁` | `K₂₈` | `O₀₄ == frn₀₄` | |
| `s₁₂` | `K₂₇` | `O₀₅ == frn₀₅` | |
| `s₁₃` | `K₂₆` | `O₀₆ == frn₀₆` | |
| `s₁₄` | `K₂₅` | `O₀₇ == frn₀₇` | |
| `s₁₅` | `K₂₄` | `O₀₈ == frn₀₈` | |
| `s₁₆` | `K₂₃` | `O₀₉ == frn₀₉` | |
| `s₁₇` | `K₂₂` | `O₁₀ == frn₁₀` | |
| `s₁₈` | `K₂₁` | `O₁₁ == frn₁₁` | |
| `s₁₉` | `K₂₀` | `O₁₂ == frn₁₂` | |
| `s₂₀` | `K₁₉` | `O₁₃ == frn₁₃` | |
| `s₂₁` | `K₁₈` | `O₁₄ == frn₁₄` | |
| `s₂₂` | `K₁₇` | `O₁₅ == frn₁₅` | |
| `s₂₃` | `K₁₆` | `O₁₆ == frn₁₆` | |
| `s₂₄` | `K₁₅` | `O₁₇ == frn₁₇` | |
| `s₂₅` | `K₁₄` | `O₁₈ == frn₁₈` | |
| `s₂₆` | `K₁₃` | `O₁₉ == frn₁₉` | |
| `s₂₇` | `K₁₂` | `O₂₀ == frn₂₀` | |
| `s₂₈` | `K₁₁` | `O₂₁ == frn₂₁` | |
| `s₂₉` | `K₁₀` | `O₂₂ == frn₂₂` | |
| `s₃₀` | `K₀₉` | `O₂₃ == frn₂₃` | |
| `s₃₁` | `K₀₈` | `O₂₄ == frn₂₄` | |
| `s₃₂` | `K₀₇` | `O₂₅ == frn₂₅` | |
| `s₃₃` | `K₀₆` | `O₂₆ == frn₂₆` | |
| `s₃₄` | `K₀₅` | `O₂₇ == frn₂₇` | Last key bit that affects `frn` |
| `s₃₅` | `K₀₄` | `O₂₈ == grn₀₀` | Five bits of key affect `grn` |
| `s₃₆` | `K₀₃` | `O₂₉ == grn₀₁` | |
| `s₃₇` | `K₀₂` | `O₃₀ == grn₀₂` | |
| `s₃₈` | `K₀₁` | `O₃₁ == grn₀₃` | |
| `s₃₉` | `K₀₀` | `O₃₂ == grn₀₄` | |
| `s₄₀` | `0₁₄` | `O₃₃ == grn₀₅` | Zero-fill start |
| `s₄₁` | `0₁₃` | `O₃₄ == grn₀₆` | |
| `s₄₂` | `0₁₂` | `O₃₅ == grn₀₇` | |
| `s₄₃` | `0₁₁` | `O₃₆ == grn₀₈` | |
| `s₄₄` | `0₁₀` | `O₃₇ == grn₀₉` | |
| `s₄₅` | `0₀₉` | `O₃₈ == grn₁₀` | |
| `s₄₆` | `0₀₈` | `O₃₉ == grn₁₁` | |
| `s₄₇` | `0₀₇` | `O₄₀ == grn₁₂` | |
| `s₄₈` | `0₀₆` | `O₄₁ == grn₁₃` | |
| `s₄₉` | `0₀₅` | `O₄₂ == grn₁₄` | |
| `s₅₀` | `0₀₄` | `O₄₃ == grn₁₅` | |
| `s₅₁` | `0₀₃` | `O₄₄ == grn₁₆` | |
| `s₅₂` | `0₀₂` | `O₄₅ == grn₁₇` | |
| `s₅₃` | `0₀₁` | `O₄₆ == grn₁₈` | |
| `s₅₄` | `0₀₀` | `O₄₇ == grn₁₉` | |
@ -525,7 +525,7 @@ FPGA logic if desired.
There are sixteen (`16x`) blocks of memory, each storing a 16-bit value.
Blocks may be protected (`P` column) as read-only (`RO`) or write-only (`WO`).
`Lock₀` === Disable writingPermanent write-prevention when set to 1.
`Lock₀` === Disable writing ... (?) write-prevention when set to 1.
`Lock₁` === Pin-code lock
| `Block` | `P` | `Purpose` | Bits (LSB first) | Bits (MSB first) |

View file

@ -0,0 +1,46 @@
# ###################
# Generating .PLA
# ###################
.i 5
.o 1
.ilb a l2 l3 l0 l6
.ob g1
00000 1
00001 1
00010 1
00011 1
00100 0
00101 0
00110 1
00111 0
01000 0
01001 1
01010 0
01011 0
01100 1
01101 0
01110 0
01111 1
10000 1
10001 0
10010 0
10011 0
10100 0
10101 1
10110 0
10111 1
11000 0
11001 0
11010 1
11011 1
11100 1
11101 1
11110 1
11111 0
.e
# ###################
# END OF .PLA
# ###################

View file

@ -0,0 +1,46 @@
# ###################
# Generating .PLA
# ###################
.i 5
.o 1
.ilb l5 b m0 l4 m1
.ob g2
00000 1
00001 1
00010 1
00011 1
00100 0
00101 0
00110 0
00111 1
01000 0
01001 1
01010 0
01011 0
01100 1
01101 0
01110 1
01111 0
10000 1
10001 0
10010 0
10011 0
10100 0
10101 1
10110 1
10111 0
11000 0
11001 0
11010 1
11011 1
11100 1
11101 1
11110 0
11111 1
.e
# ###################
# END OF .PLA
# ###################

View file

@ -0,0 +1,46 @@
# ###################
# Generating .PLA
# ###################
.i 5
.o 1
.ilb m5 c r1 m3 r3
.ob g3
00000 1
00001 1
00010 1
00011 1
00100 0
00101 0
00110 0
00111 1
01000 0
01001 0
01010 1
01011 0
01100 1
01101 1
01110 0
01111 0
10000 1
10001 0
10010 0
10011 0
10100 0
10101 1
10110 1
10111 0
11000 0
11001 1
11010 0
11011 1
11100 1
11101 0
11110 1
11111 1
.e
# ###################
# END OF .PLA
# ###################

View file

@ -0,0 +1,46 @@
# ###################
# Generating .PLA
# ###################
.i 5
.o 1
.ilb r2 r4 r6 r0 r5
.ob g4
00000 1
00001 1
00010 1
00011 1
00100 0
00101 0
00110 0
00111 1
01000 0
01001 0
01010 1
01011 0
01100 1
01101 1
01110 0
01111 0
10000 0
10001 1
10010 0
10011 0
10100 1
10101 0
10110 1
10111 0
11000 1
11001 0
11010 0
11011 1
11100 0
11101 1
11110 1
11111 1
.e
# ###################
# END OF .PLA
# ###################

View file

@ -0,0 +1,270 @@
# ###################
# Generating .PLA
# ###################
.i 8
.o 1
.ilb l0 l4 l6 m1 m3 r0 r3 r5
.ob XX
00000000 0
00000001 0
00000010 1
00000011 0
00000100 0
00000101 0
00000110 1
00000111 0
00001000 1
00001001 1
00001010 1
00001011 1
00001100 1
00001101 1
00001110 1
00001111 1
00010000 1
00010001 1
00010010 1
00010011 1
00010100 0
00010101 0
00010110 1
00010111 0
00011000 1
00011001 1
00011010 1
00011011 1
00011100 0
00011101 0
00011110 1
00011111 0
00100000 1
00100001 0
00100010 1
00100011 0
00100100 1
00100101 0
00100110 1
00100111 0
00101000 1
00101001 1
00101010 1
00101011 1
00101100 1
00101101 1
00101110 1
00101111 1
00110000 1
00110001 0
00110010 1
00110011 0
00110100 1
00110101 0
00110110 1
00110111 0
00111000 1
00111001 0
00111010 1
00111011 0
00111100 1
00111101 0
00111110 1
00111111 0
01000000 0
01000001 0
01000010 1
01000011 1
01000100 0
01000101 0
01000110 1
01000111 1
01001000 1
01001001 1
01001010 1
01001011 1
01001100 1
01001101 1
01001110 1
01001111 1
01010000 1
01010001 1
01010010 1
01010011 1
01010100 0
01010101 0
01010110 1
01010111 1
01011000 1
01011001 1
01011010 1
01011011 1
01011100 0
01011101 0
01011110 1
01011111 0
01100000 0
01100001 0
01100010 1
01100011 1
01100100 0
01100101 0
01100110 1
01100111 1
01101000 1
01101001 1
01101010 1
01101011 1
01101100 1
01101101 1
01101110 1
01101111 1
01110000 0
01110001 0
01110010 1
01110011 1
01110100 0
01110101 0
01110110 1
01110111 1
01111000 0
01111001 0
01111010 1
01111011 0
01111100 0
01111101 0
01111110 1
01111111 0
10000000 0
10000001 0
10000010 0
10000011 0
10000100 0
10000101 0
10000110 0
10000111 0
10001000 0
10001001 0
10001010 0
10001011 0
10001100 0
10001101 0
10001110 0
10001111 0
10010000 1
10010001 1
10010010 1
10010011 1
10010100 0
10010101 0
10010110 0
10010111 0
10011000 1
10011001 1
10011010 1
10011011 1
10011100 0
10011101 0
10011110 0
10011111 0
10100000 1
10100001 0
10100010 1
10100011 0
10100100 1
10100101 0
10100110 1
10100111 0
10101000 1
10101001 0
10101010 1
10101011 0
10101100 1
10101101 0
10101110 1
10101111 0
10110000 1
10110001 0
10110010 1
10110011 0
10110100 1
10110101 0
10110110 1
10110111 0
10111000 1
10111001 0
10111010 1
10111011 0
10111100 1
10111101 0
10111110 1
10111111 0
11000000 0
11000001 0
11000010 1
11000011 1
11000100 0
11000101 0
11000110 1
11000111 1
11001000 0
11001001 0
11001010 0
11001011 0
11001100 0
11001101 0
11001110 0
11001111 0
11010000 1
11010001 1
11010010 1
11010011 1
11010100 0
11010101 0
11010110 1
11010111 1
11011000 1
11011001 1
11011010 1
11011011 1
11011100 0
11011101 0
11011110 0
11011111 0
11100000 0
11100001 0
11100010 1
11100011 1
11100100 0
11100101 0
11100110 1
11100111 1
11101000 0
11101001 0
11101010 0
11101011 0
11101100 0
11101101 0
11101110 0
11101111 0
11110000 0
11110001 0
11110010 1
11110011 1
11110100 0
11110101 0
11110110 1
11110111 1
11111000 0
11111001 0
11111010 0
11111011 0
11111100 0
11111101 0
11111110 0
11111111 0
.e
# ###################
# END OF .PLA
# ###################

View file

@ -31,13 +31,13 @@
#include <string.h>
#include <stdbool.h>
#if defined(NDEBUG)
#define ASSERT(x) ((void)0)
#define ASSERT(x) ((void)0)
#elif defined(ID48_NO_STDIO)
#define ASSERT(x) ((void)0)
#define ASSERT(x) ((void)0)
#else // neither NDEBUG nor ID48_NO_STDIO defined
#include <stdio.h>
#include <assert.h>
#define ASSERT(x) assert((x))
#include <stdio.h>
#include <assert.h>
#define ASSERT(x) assert((x))
#endif

View file

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.21)
project(id48lib LANGUAGES C CXX ASM)
add_library(id48lib STATIC
id48_data.c
id48_generator.c
id48_recover.c
)
# Compile options/warnings for C sources only (use generator expression)
target_compile_options(id48lib PRIVATE
$<$<COMPILE_LANGUAGE:C>:-Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function>
)
# Prefer compile_definitions over embedding -D in compile options
target_compile_definitions(id48lib PRIVATE ID48_NO_STDIO)
# Private include dirs for internal headers, PUBLIC for the library API
target_include_directories(id48lib
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/id48
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../public
)
set_property(TARGET id48lib PROPERTY POSITION_INDEPENDENT_CODE ON)

View file

@ -1,6 +1,6 @@
# Makefile for ID48LIB library
# Proxmark3-specific Makefile for ID48LIB library
MYSRCPATHS =
MYINCLUDES = -I.
MYINCLUDES = -I../public
MYCFLAGS = -Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function
MYDEFS = -DID48_NO_STDIO
MYSRCS = \
@ -10,4 +10,4 @@ MYSRCS = \
LIB_A = libid48.a
include ../../../Makefile.host
include ../../../../Makefile.host

View file

@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.21)
add_subdirectory(espresso)
add_subdirectory(generator)
add_subdirectory(recovery)

View file

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.21)
add_executable(
id48lib_test_espresso
main.c
verify_espresso.c
)
target_link_libraries(id48lib_test_espresso PRIVATE id48lib)
target_include_directories(id48lib_test_espresso PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)
target_compile_options(id48lib_test_espresso PRIVATE
$<$<COMPILE_LANGUAGE:C>:-O0>
$<$<COMPILE_LANGUAGE:CXX>:-O0>
)

View file

@ -0,0 +1,157 @@
/**
The MIT License (MIT)
Copyright (c) SimpleHacks, Henry Gabryjelski
https://github.com/SimpleHacks/UtilHeaders
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef ARRAYSIZE2_H
#define ARRAYSIZE2_H
/**
The following, if defined prior to inclusion of this header file,
will modify its behavior as noted:
ARRAYSIZE2_SHOW_VERSION_MESSAGE
-- if defined, will show which version of ARRAY_SIZE2 macro is selected
*/
/**
see example source at:
https://godbolt.org/z/zzYoeK6Mf
*/
#ifndef __has_feature
#define __has_feature(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (defined(__cplusplus) && __cplusplus >= 201103L) || /* any compiler claiming C++11 support */ \
(defined(__cplusplus) && _MSC_VER >= 1900 && __cplusplus != 199711L) || /* Visual C++ 2015 or higher */ \
__has_feature(cxx_constexpr) /* CLang versions supporting constexp */
#include <stddef.h> /* required for size_t */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using C++11 version" )
#endif
namespace detail
{
template <typename T, size_t N>
constexpr size_t ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(T const (&)[N]) noexcept
{
return N;
}
} /* namespace detail */
#define ARRAY_SIZE2(arr) detail::ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(arr)
#elif defined(__cplusplus) && __cplusplus >= 199711L && ( /* C++ 98 trick */ \
defined(__INTEL_COMPILER) || \
defined(__clang__) || \
(defined(__GNUC__) && ( \
(__GNUC__ > 4) || \
(__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \
)))
#include <stddef.h> /* required for size_t */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message "ARRAY_SIZE2 -- Using C++98 version"
#endif
template <typename T, size_t N>
char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N];
#define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x))
#elif defined(__cplusplus) /* && ((__cplusplus >= 199711L) || defined(__INTEL_COMPILER) || defined(__clang__)) */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using Ivan J. Johnson's C++ version" )
#endif
/*
Works on older compilers, even Visual C++ 6....
Created by Ivan J. Johnson, March 06, 2007
See http://drdobbs.com/cpp/197800525?pgno=1
Full description is in markdown file array_size2.md
*/
#define ARRAY_SIZE2(arr) ( \
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_ARRAY_SIZE2*>(arr)) + /*check1*/ \
0 * sizeof(::Bad_arg_to_ARRAY_SIZE2::check_type((arr), &(arr))) + /*check2*/ \
sizeof(arr) / sizeof((arr)[0]) /* eval */ \
)
struct Bad_arg_to_ARRAY_SIZE2 {
class Is_pointer; /* incomplete */
class Is_array {};
template <typename T>
static Is_pointer check_type(T const *, T const * const *);
static Is_array check_type(void const *, void const *);
};
#elif !defined(__cplusplus) && defined(__GNUC__)
#include <stdint.h>
/**
Even C can have type-safety for equivalent of ARRAY_SIZE() macro,
when using the following two GCC extensions:
typeof()
__builtin_types_compatible_p()
*/
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using GNUC version" )
#endif
/**
validated using:
MSP430 gcc 4.5.3
x86-64 icc 16.0.3
x86-64 gcc 4.1.2
x86-64 clang 3.0.0
AVR gcc 4.5.4
ARM gcc 4.5.4
*/
#define __SIMPLEHACKS_COMPATIBLE_TYPES__(a,b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) /* GCC extensions */
#define __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(x) (sizeof(struct { uint8_t q: (-!!(x)*0x1ee7)+1u;})-1u) /* if x is zero, reports "error: negative width in bit-field '<anonymous>'" */
#define __SIMPLEHACKS_MUST_BE_ARRAY__(x) __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(__SIMPLEHACKS_COMPATIBLE_TYPES__((x), &(*x)))
#define ARRAY_SIZE2(_arr) ( (sizeof(_arr) / sizeof((_arr)[0])) + __SIMPLEHACKS_MUST_BE_ARRAY__(_arr) ) /* compile-time error if not an array */
#else
/**
The good news is that all compilers (as of 20202-05-08)
on godbolt.org are fully supported. Therefore, if some
other compiler does not support any of the above method,
it's important to force a compile-time error, to avoid
any suggestion that this provides a safe macro.
*/
#error "Unable to provide type-safe ARRAY_SIZE2 macro"
#endif
#endif // ARRAYSIZE2_H

View file

@ -0,0 +1,25 @@
#include <stdio.h>
#include <stdbool.h>
#include "verify_espresso.h"
#include "id48.h"
int main(void) {
bool result = true;
if (result) {
printf("Verifying Espresso Results...\n");
result = verify_espresso_results();
printf("Verifying Espresso Results: %s\n",
result ? "SUCCESS" : "----> FAILURE <----"
);
}
// if (result) {
// printf("Hello2...\n");
// generate_all_lut_espresso_files();
// }
return result ? 0 : -120;
}

View file

@ -0,0 +1,817 @@
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "array_size2.h"
#include "id48.h"
#include "verify_espresso.h"
#if !defined true
#define true ((bool)1u)
#endif
#if !defined false
#define false ((bool)0u)
#endif
#if 1 // constant names for use in LUT_PLA_NAMES_xx
static const char * const NAME_A = "a";
static const char * const NAME_B = "b";
static const char * const NAME_C = "c";
static const char * const NAME_L0 = "l0";
static const char * const NAME_L2 = "l2";
static const char * const NAME_L3 = "l3";
static const char * const NAME_L4 = "l4";
static const char * const NAME_L5 = "l5";
static const char * const NAME_L6 = "l6";
static const char * const NAME_M0 = "m0";
static const char * const NAME_M1 = "m1";
static const char * const NAME_M3 = "m3";
static const char * const NAME_M5 = "m5";
static const char * const NAME_R0 = "r0";
static const char * const NAME_R1 = "r1";
static const char * const NAME_R2 = "r2";
static const char * const NAME_R3 = "r3";
static const char * const NAME_R4 = "r4";
static const char * const NAME_R5 = "r5";
static const char * const NAME_R6 = "r6";
#endif // constant names for use in LUT_PLA_NAMES_xx
#if 1 // LUT_PLA_NAMES_xx -- name arrays for generating espresso input files
// NOTE: these arrays are in order of the most significant to least significant bit.
// did i get confused by that?
const char * const LUT_PLA_NAMES_g1[5] = { NAME_A , NAME_L2, NAME_L3, NAME_L0, NAME_L6, };
const char * const LUT_PLA_NAMES_g2[5] = { NAME_L5, NAME_B , NAME_M0, NAME_L4, NAME_M1, };
const char * const LUT_PLA_NAMES_g3[5] = { NAME_M5, NAME_C , NAME_R1, NAME_M3, NAME_R3, };
const char * const LUT_PLA_NAMES_g4[5] = { NAME_R2, NAME_R4, NAME_R6, NAME_R0, NAME_R5, };
const char * const LUT_PLA_NAMES_XX[8] = {
NAME_L0, NAME_L4, NAME_L6, NAME_M1,
NAME_M3, NAME_R0, NAME_R3, NAME_R5,
};
#endif // LUT_PLA_NAMES_xx -- name arrays for generating espresso input files
#if 1 // actual lookup tables
// LUTs were internal-only, and not even exposed to library internals.
// so just copy/paste here for now for initial testing
/// initial_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0)
static const uint8_t small_lut_XX[32] = { // aka 256 bits
0x44, 0xff, 0x4f, 0x4f, 0x55, 0xff, 0x55, 0x55,
0xcc, 0xff, 0xcf, 0x4f, 0xcc, 0xff, 0xcc, 0x44,
0x00, 0x00, 0x0f, 0x0f, 0x55, 0x55, 0x55, 0x55,
0xcc, 0x00, 0xcf, 0x0f, 0xcc, 0x00, 0xcc, 0x00,
};
/// g1_idx = (a << 4) | (l2 << 3) | (l3 << 2) | (l0 << 1) | (l6 << 0)
static const uint8_t small_lut_group1[4] = { // aka 32 bits
0x4f, 0x92, 0xa1, 0x7c,
};
/// g2_idx = (l5 << 4) | (b << 3) | (m0 << 2) | (l4 << 1) | (m1 << 0)
static const uint8_t small_lut_group2[4] = { // aka 32 bits
0x8f, 0x52, 0x61, 0xbc,
};
/// g3_idx = (m5 << 4) | (c << 3) | (r1 << 2) | (m3 << 1) | (r3 << 0)
static const uint8_t small_lut_group3[4] = { // aka 32 bits
0x8f, 0x34, 0x61, 0xda,
};
/// g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0)
static const uint8_t small_lut_group4[4] = { // aka 32 bits
0x8f, 0x34, 0x52, 0xe9,
};
static inline bool get_bit(const uint8_t *table_start, uint32_t bit_idx) {
const uint32_t byte = bit_idx / 8u;
const uint8_t mask = 1u << (bit_idx % 8u);
return (table_start[byte] & mask) != 0;
}
#endif // actual lookup tables
// 1. validated by hand that LUT --> PLA generation was accurate for all five LUTs
// 2. Ran the generated PLA through WASM-based espresso:
// https://nudelerde.github.io/Espresso-Wasm-Web/index.html
// 3. Copied the output to each validation function
// 4. Converted the output to C-style boolean logic, and column-aligned formatting for easier review
//
// Same process used for each, but only g1 validates against the lookup table.
// Maybe just use the LUTs in an FPGA instead of trying to minimize to two-level logic?
// However, performance will be best with two-level, glitch-free logic....
//
// WHAT IS GOING WRONG WITH ESPRESSO OUTPUT (OR MY VALIDATION)?
//
// After adding a whole set of secondary validation functions... the root cause was ....
// Accidentally initializing any_failures to true instead of false in the other functions.
//
// :-P
//
// I'll check this comment into my dev branch, even if I squash-merge it out later.
//
typedef struct _OUTPUT_INDEX3 {
// the index is calculated from unstable ID48LIBX_STATE_REGISTERS
// by function calculate_output_index(), which creates a single
// 20-bit result:
// Fₒ( abc l₀l₂l₃l₄l₅l₆ m₀m₁m₃m₅ r₀r₁r₂r₃r₄r₅r₆ )
// msb 19 ---^ lsb 00 ---^^
// This union allows debugger to view the individual bit values more easily.
union {
uint32_t Raw32;
struct {
// seven bits from r: r₀..r₆
uint32_t r6 : 1; // bit 00
uint32_t r5 : 1; // bit 01
uint32_t r4 : 1; // bit 02
uint32_t r3 : 1; // bit 03
uint32_t r2 : 1; // bit 04
uint32_t r1 : 1; // bit 05
uint32_t r0 : 1; // bit 06
// four bits from m: m₀m₁m₃m₅
uint32_t m5 : 1; // bit 07
uint32_t m3 : 1; // bit 08
uint32_t m1 : 1; // bit 09
uint32_t m0 : 1; // bit 10
// six bits from l: l₀l₂l₃l₄l₅l₆
uint32_t l6 : 1; // bit 11
uint32_t l5 : 1; // bit 12
uint32_t l4 : 1; // bit 13
uint32_t l3 : 1; // bit 14
uint32_t l2 : 1; // bit 15
uint32_t l0 : 1; // bit 16
// and finally, the three bits a, b, c
uint32_t c : 1; // bit 17
uint32_t b : 1; // bit 18
uint32_t a : 1; // bit 19
};
};
} OUTPUT_INDEX3;
#if 1 // structures for mapping LUT indices to named fields
/// g1_idx = (a << 4) | (l2 << 3) | (l3 << 2) | (l0 << 1) | (l6 << 0)
typedef struct _INDEX_G1 {
union {
uint8_t as_uint8_t;
struct {
uint8_t l6 : 1; // least significant bit
uint8_t l0 : 1;
uint8_t l3 : 1;
uint8_t l2 : 1;
uint8_t a : 1;
};
};
} INDEX_G1;
/// g2_idx = (l5 << 4) | (b << 3) | (m0 << 2) | (l4 << 1) | (m1 << 0)
typedef struct _INDEX_G2 {
union {
uint8_t as_uint8_t;
struct {
uint8_t m1 : 1;
uint8_t l4 : 1;
uint8_t m0 : 1;
uint8_t b : 1;
uint8_t l5 : 1;
};
};
} INDEX_G2;
/// g3_idx = (m5 << 4) | (c << 3) | (r1 << 2) | (m3 << 1) | (r3 << 0)
typedef struct _INDEX_G3 {
union {
uint8_t as_uint8_t;
struct {
uint8_t r3 : 1;
uint8_t m3 : 1;
uint8_t r1 : 1;
uint8_t c : 1;
uint8_t m5 : 1;
};
};
} INDEX_G3;
/// g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0)
typedef struct _INDEX_G4 {
union {
uint8_t as_uint8_t;
struct {
uint8_t r5 : 1;
uint8_t r0 : 1;
uint8_t r6 : 1;
uint8_t r4 : 1;
uint8_t r2 : 1;
};
};
} INDEX_G4;
/// initial_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0)
typedef struct _INDEX_XX {
union {
uint8_t as_uint8_t;
struct {
uint8_t r5 : 1;
uint8_t r3 : 1;
uint8_t r0 : 1;
uint8_t m3 : 1;
uint8_t m1 : 1;
uint8_t l6 : 1;
uint8_t l4 : 1;
uint8_t l0 : 1;
};
};
} INDEX_XX;
#endif // structures for mapping LUT indices to named fields
typedef struct _ESPRESSO_GENERATOR_ARGUMENTS {
uint8_t const input_count;
char const * const * names;
char const * const output_name;
uint8_t const * lut_start;
} ESPRESSO_GENERATOR_ARGUMENTS;
static bool verify_lut_g1(void);
static bool verify_lut_g2(void);
static bool verify_lut_g3(void);
static bool verify_lut_g4(void);
static bool verify_lut_XX(void);
// Later, define a struct with functions for each thing to be validated
typedef bool (*lookuptable_func_t)(uint8_t);
/// g1_idx = (a << 4) | (l2 << 3) | (l3 << 2) | (l0 << 1) | (l6 << 0)
static bool espresso_implementation_g1_alt0(uint8_t idx) {
// output_g1 = (a&l2&l0&!l6) | (a&l3&!l0&l6) | (!a&!l2&!l3) | (!l2&!l3&!l0&!l6) | (l2&l3&!l0&!l6) | (!a&!l2&l0&!l6) | (!a&!l3&!l0&l6) | (a&l2&!l3&l0) | (a&!l2&l3&l6) | (!a&l2&l3&l0&l6);
static const bool _ = true;
bool a = idx & (1u << 4);
bool l2 = idx & (1u << 3);
bool l3 = idx & (1u << 2);
bool l0 = idx & (1u << 1);
bool l6 = idx & (1u << 0);
return
( a && l2 && _ && l0 && !(l6) ) ||
( a && _ && l3 && !(l0) && l6 ) ||
( !(a) && !(l2) && !(l3) && _ && _ ) ||
( _ && !(l2) && !(l3) && !(l0) && !(l6) ) ||
( _ && l2 && l3 && !(l0) && !(l6) ) ||
( !(a) && !(l2) && _ && l0 && !(l6) ) ||
( !(a) && _ && !(l3) && !(l0) && l6 ) ||
( a && l2 && !(l3) && l0 && _ ) ||
( a && !(l2) && l3 && _ && l6 ) ||
( !(a) && l2 && l3 && l0 && l6 ) ;
}
static bool espresso_implementation_g1_alt1(uint8_t idx) {
// output_g1 = (a&l2&l0&!l6) | (a&l3&!l0&l6) | (!a&!l2&!l3) | (!l2&!l3&!l0&!l6) | (l2&l3&!l0&!l6) | (!a&!l2&l0&!l6) | (!a&!l3&!l0&l6) | (a&l2&!l3&l0) | (a&!l2&l3&l6) | (!a&l2&l3&l0&l6);
static const bool _ = true;
INDEX_G1 t = { .as_uint8_t = idx };
bool a = t.a;
bool l2 = t.l2;
bool l3 = t.l3;
bool l0 = t.l0;
bool l6 = t.l6;
return
( a && l0 && l2 && _ && !(l6) ) ||
( a && !(l0) && _ && l3 && l6 ) ||
( !(a) && _ && !(l2) && !(l3) && _ ) ||
( _ && !(l0) && !(l2) && !(l3) && !(l6) ) ||
( _ && !(l0) && l2 && l3 && !(l6) ) ||
( !(a) && l0 && !(l2) && _ && !(l6) ) ||
( !(a) && !(l0) && _ && !(l3) && l6 ) ||
( a && l0 && l2 && !(l3) && _ ) ||
( a && _ && !(l2) && l3 && l6 ) ||
( !(a) && l0 && l2 && l3 && l6 ) ;
}
static const lookuptable_func_t alt_for_g1[] = {
espresso_implementation_g1_alt0,
espresso_implementation_g1_alt1,
};
static bool espresso_implementation_g2_alt0(uint8_t idx) {
// output_g2 = (b&m0&!l4&!m1) | (l5&b&l4&m1) | (!l5&!b&!m0) | (!b&!m0&!l4&!m1) | (l5&b&!m0&l4) | (l5&!b&m0&l4&!m1) | (!l5&b&m0&!m1) | (!l5&!m0&!l4&m1) | (l5&m0&!l4&m1) | (!l5&!b&l4&m1);
// g2_idx = (l5 << 4) | (b << 3) | (m0 << 2) | (l4 << 1) | (m1 << 0)
bool l5 = idx & (1u << 4);
bool b = idx & (1u << 3);
bool m0 = idx & (1u << 2);
bool l4 = idx & (1u << 1);
bool m1 = idx & (1u << 0);
static const bool _ = true;
return
( _ && b && m0 && !(l4) && !(m1) ) |
( l5 && b && _ && l4 && m1 ) |
( !(l5) && !(b) && !(m0) && _ && _ ) |
( _ && !(b) && !(m0) && !(l4) && !(m1) ) |
( l5 && b && !(m0) && l4 && _ ) |
( l5 && !(b) && m0 && l4 && !(m1) ) |
( !(l5) && b && m0 && _ && !(m1) ) |
( !(l5) && _ && !(m0) && !(l4) && m1 ) |
( l5 && _ && m0 && !(l4) && m1 ) |
( !(l5) && !(b) && _ && l4 && m1 ) ;
}
static bool espresso_implementation_g2_alt1(uint8_t idx) {
// output_g2 = (b&m0&!l4&!m1) | (l5&b&l4&m1) | (!l5&!b&!m0) | (!b&!m0&!l4&!m1) | (l5&b&!m0&l4) | (l5&!b&m0&l4&!m1) | (!l5&b&m0&!m1) | (!l5&!m0&!l4&m1) | (l5&m0&!l4&m1) | (!l5&!b&l4&m1);
INDEX_G2 t = { .as_uint8_t = idx };
bool l5 = t.l5;
bool b = t.b;
bool m0 = t.m0;
bool l4 = t.l4;
bool m1 = t.m1;
static const bool _ = true;
return
( _ && b && m0 && !(l4) && !(m1) ) |
( l5 && b && _ && l4 && m1 ) |
( !(l5) && !(b) && !(m0) && _ && _ ) |
( _ && !(b) && !(m0) && !(l4) && !(m1) ) |
( l5 && b && !(m0) && l4 && _ ) |
( l5 && !(b) && m0 && l4 && !(m1) ) |
( !(l5) && b && m0 && _ && !(m1) ) |
( !(l5) && _ && !(m0) && !(l4) && m1 ) |
( l5 && _ && m0 && !(l4) && m1 ) |
( !(l5) && !(b) && _ && l4 && m1 ) ;
}
static const lookuptable_func_t alt_for_g2[] = {
espresso_implementation_g2_alt0,
espresso_implementation_g2_alt1,
// espresso_implementation_g1_alt2, // fails ... generated via https://github.com/omritriki/espresso-py ...
};
static bool espresso_implementation_g3_alt0(uint8_t idx) {
// output_g3 = (c&r1&!m3&!r3) | (m5&c&m3&r3) | (!m5&!c&!r1) | (!c&!r1&!m3&!r3) | (!m5&!r1&m3&!r3) | (m5&r1&m3&!r3) | (m5&c&!r1&r3) | (m5&!c&r1&!m3&r3) | (!m5&c&r1&!m3) | (!m5&!c&m3&r3);
// g3_idx = (m5 << 4) | (c << 3) | (r1 << 2) | (m3 << 1) | (r3 << 0)
bool m5 = idx & (1u << 4);
bool c = idx & (1u << 3);
bool r1 = idx & (1u << 2);
bool m3 = idx & (1u << 1);
bool r3 = idx & (1u << 0);
static const bool _ = true;
return
( _ && c && r1 && !(m3) && !(r3) ) ||
( m5 && c && _ && m3 && r3 ) ||
( !(m5) && !(c) && !(r1) && _ && _ ) ||
( _ && !(c) && !(r1) && !(m3) && !(r3) ) ||
( !(m5) && _ && !(r1) && m3 && !(r3) ) ||
( m5 && _ && r1 && m3 && !(r3) ) ||
( m5 && c && !(r1) && _ && r3 ) ||
( m5 && !(c) && r1 && !(m3) && r3 ) ||
( !(m5) && c && r1 && !(m3) && _ ) ||
( !(m5) && !(c) && _ && m3 && r3 ) ;
}
static bool espresso_implementation_g3_alt1(uint8_t idx) {
// output_g3 = (c&r1&!m3&!r3) | (m5&c&m3&r3) | (!m5&!c&!r1) | (!c&!r1&!m3&!r3) | (!m5&!r1&m3&!r3) | (m5&r1&m3&!r3) | (m5&c&!r1&r3) | (m5&!c&r1&!m3&r3) | (!m5&c&r1&!m3) | (!m5&!c&m3&r3);
INDEX_G3 t = { .as_uint8_t = idx };
bool c = t.c;
bool r1 = t.r1;
bool r3 = t.r3;
bool m3 = t.m3;
bool m5 = t.m5;
static const bool _ = true;
return
( _ && c && r1 && !(m3) && !(r3) ) ||
( m5 && c && _ && m3 && r3 ) ||
( !(m5) && !(c) && !(r1) && _ && _ ) ||
( _ && !(c) && !(r1) && !(m3) && !(r3) ) ||
( !(m5) && _ && !(r1) && m3 && !(r3) ) ||
( m5 && _ && r1 && m3 && !(r3) ) ||
( m5 && c && !(r1) && _ && r3 ) ||
( m5 && !(c) && r1 && !(m3) && r3 ) ||
( !(m5) && c && r1 && !(m3) && _ ) ||
( !(m5) && !(c) && _ && m3 && r3 ) ;
}
static const lookuptable_func_t alt_for_g3[] = {
espresso_implementation_g3_alt0,
espresso_implementation_g3_alt1,
};
static bool espresso_implementation_g4_alt0(uint8_t idx) {
// output_g4 = (r2&r6&r0&!r5) | (r4&r6&!r0&r5) | (!r2&!r4&!r6) | (r2&r4&!r6&!r0&!r5) | (r2&!r4&r6&!r5) | (!r2&r4&r6&!r0) | (!r2&!r6&r0&!r5) | (!r4&!r6&!r0&r5) | (r2&r4&r0&r5) | (!r2&!r4&r0&r5);
// g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0)
bool r2 = idx & (1u << 4);
bool r4 = idx & (1u << 3);
bool r6 = idx & (1u << 2);
bool r0 = idx & (1u << 1);
bool r5 = idx & (1u << 0);
static const bool _ = true;
return
( r2 && _ && r6 && r0 && !(r5) ) |
( _ && r4 && r6 && !(r0) && r5 ) |
( !(r2) && !(r4) && !(r6) && _ && _ ) |
( r2 && r4 && !(r6) && !(r0) && !(r5) ) |
( r2 && !(r4) && r6 && _ && !(r5) ) |
( !(r2) && r4 && r6 && !(r0) && _ ) |
( !(r2) && _ && !(r6) && r0 && !(r5) ) |
( _ && !(r4) && !(r6) && !(r0) && r5 ) |
( r2 && r4 && _ && r0 && r5 ) |
( !(r2) && !(r4) && _ && r0 && r5 ) ;
}
static bool espresso_implementation_g4_alt1(uint8_t idx) {
// output_g4 = (r2&r6&r0&!r5) | (r4&r6&!r0&r5) | (!r2&!r4&!r6) | (r2&r4&!r6&!r0&!r5) | (r2&!r4&r6&!r5) | (!r2&r4&r6&!r0) | (!r2&!r6&r0&!r5) | (!r4&!r6&!r0&r5) | (r2&r4&r0&r5) | (!r2&!r4&r0&r5);
// g4_idx = (r2 << 4) | (r4 << 3) | (r6 << 2) | (r0 << 1) | (r5 << 0)
INDEX_G4 t = { .as_uint8_t = idx };
bool r2 = t.r2;
bool r4 = t.r4;
bool r6 = t.r6;
bool r0 = t.r0;
bool r5 = t.r5;
static const bool _ = true;
return
( r2 && _ && r6 && r0 && !(r5) ) |
( _ && r4 && r6 && !(r0) && r5 ) |
( !(r2) && !(r4) && !(r6) && _ && _ ) |
( r2 && r4 && !(r6) && !(r0) && !(r5) ) |
( r2 && !(r4) && r6 && _ && !(r5) ) |
( !(r2) && r4 && r6 && !(r0) && _ ) |
( !(r2) && _ && !(r6) && r0 && !(r5) ) |
( _ && !(r4) && !(r6) && !(r0) && r5 ) |
( r2 && r4 && _ && r0 && r5 ) |
( !(r2) && !(r4) && _ && r0 && r5 ) ;
}
static const lookuptable_func_t alt_for_g4[] = {
espresso_implementation_g4_alt0,
espresso_implementation_g4_alt1,
};
static bool espresso_implementation_xx_alt0(uint8_t idx) {
// output_xx = (!l6&m1&!r0) | (l4&!m3&r3) | (!l4&l6&!r5) | (!l0&!m1&m3) | (!l0&r3&!r5);
/// xx_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0)
static const bool _ = true;
bool l0 = idx & (1u << 7);
bool l4 = idx & (1u << 6);
bool l6 = idx & (1u << 5);
bool m1 = idx & (1u << 4);
bool m3 = idx & (1u << 3);
bool r0 = idx & (1u << 2);
bool r3 = idx & (1u << 1);
bool r5 = idx & (1u << 0);
return
( _ && _ && !(l6) && m1 && _ && !(r0) && _ && _ ) ||
( _ && l4 && _ && _ && !(m3) && _ && r3 && _ ) ||
( _ && !(l4) && l6 && _ && _ && _ && _ && !(r5) ) ||
( !(l0) && _ && _ && !(m1) && m3 && _ && _ && _ ) ||
( !(l0) && _ && _ && _ && _ && _ && r3 && !(r5) ) ;
}
static bool espresso_implementation_xx_alt1(uint8_t idx) {
// output_XX = (!l6&m1&!r0) | (l4&!m3&r3) | (!l4&l6&!r5) | (!l0&!m1&m3) | (!l0&r3&!r5);
static const bool _ = true;
/// xx_idx = (l0 << 7) | (l4 << 6) | (l6 << 5) | (m1 << 4) | (m3 << 3) | (r0 << 2) | (r3 << 1) | (r5 << 0)
INDEX_XX t = { .as_uint8_t = idx };
bool l0 = t.l0;
bool l4 = t.l4;
bool l6 = t.l6;
bool m1 = t.m1;
bool m3 = t.m3;
bool r0 = t.r0;
bool r3 = t.r3;
bool r5 = t.r5;
return
( _ && _ && !(l6) && m1 && _ && !(r0) && _ && _ ) ||
( _ && l4 && _ && _ && !(m3) && _ && r3 && _ ) ||
( _ && !(l4) && l6 && _ && _ && _ && _ && !(r5) ) ||
( !(l0) && _ && _ && !(m1) && m3 && _ && _ && _ ) ||
( !(l0) && _ && _ && _ && _ && _ && r3 && !(r5) ) ;
}
static const lookuptable_func_t alt_for_xx[] = {
espresso_implementation_xx_alt0,
espresso_implementation_xx_alt1,
};
typedef struct _VERIFICATION_T {
uint8_t const input_count;
char const * const * names;
char const * const output_name;
uint8_t const * lut_start;
lookuptable_func_t const * const altFns;
size_t const altFnCount;
} VERIFICATION_T;
VERIFICATION_T const verification_table[] = {
{ .input_count = 5, .names = LUT_PLA_NAMES_g1, .output_name = "g1", .lut_start = small_lut_group1, .altFns = alt_for_g1, .altFnCount = ARRAY_SIZE2(alt_for_g1) },
{ .input_count = 5, .names = LUT_PLA_NAMES_g2, .output_name = "g2", .lut_start = small_lut_group2, .altFns = alt_for_g2, .altFnCount = ARRAY_SIZE2(alt_for_g2) },
{ .input_count = 5, .names = LUT_PLA_NAMES_g3, .output_name = "g3", .lut_start = small_lut_group3, .altFns = alt_for_g3, .altFnCount = ARRAY_SIZE2(alt_for_g3) },
{ .input_count = 5, .names = LUT_PLA_NAMES_g4, .output_name = "g4", .lut_start = small_lut_group4, .altFns = alt_for_g4, .altFnCount = ARRAY_SIZE2(alt_for_g4) },
{ .input_count = 8, .names = LUT_PLA_NAMES_XX, .output_name = "xx", .lut_start = small_lut_XX , .altFns = alt_for_xx, .altFnCount = ARRAY_SIZE2(alt_for_xx) },
};
static bool espresso_implementation_g1(bool a, bool l0, bool l2, bool l3, bool l6) {
// output_g1 = (a&l2&l0&!l6) | (a&l3&!l0&l6) | (!a&!l2&!l3) | (!l2&!l3&!l0&!l6) | (l2&l3&!l0&!l6) | (!a&!l2&l0&!l6) | (!a&!l3&!l0&l6) | (a&l2&!l3&l0) | (a&!l2&l3&l6) | (!a&l2&l3&l0&l6);
static const bool _ = true;
return
( a && l0 && l2 && _ && !(l6) ) |
( a && !(l0) && _ && l3 && l6 ) |
( !(a) && _ && !(l2) && !(l3) && _ ) |
( _ && !(l0) && !(l2) && !(l3) && !(l6) ) |
( _ && !(l0) && l2 && l3 && !(l6) ) |
( !(a) && l0 && !(l2) && _ && !(l6) ) |
( !(a) && !(l0) && _ && !(l3) && l6 ) |
( a && l0 && l2 && !(l3) && _ ) |
( a && _ && !(l2) && l3 && l6 ) |
( !(a) && l0 && l2 && l3 && l6 ) ;
}
static bool espresso_implementation_g2(bool b, bool l4, bool l5, bool m0, bool m1) {
// output_g2 = (b&m0&!l4&!m1) | (l5&b&l4&m1) | (!l5&!b&!m0) | (!b&!m0&!l4&!m1) | (l5&b&!m0&l4) | (l5&!b&m0&l4&!m1) | (!l5&b&m0&!m1) | (!l5&!m0&!l4&m1) | (l5&m0&!l4&m1) | (!l5&!b&l4&m1);
static const bool _ = true;
return
( _ && b && m0 && !(l4) && !(m1) ) |
( l5 && b && _ && l4 && m1 ) |
( !(l5) && !(b) && !(m0) && _ && _ ) |
( _ && !(b) && !(m0) && !(l4) && !(m1) ) |
( l5 && b && !(m0) && l4 && _ ) |
( l5 && !(b) && m0 && l4 && !(m1) ) |
( !(l5) && b && m0 && _ && !(m1) ) |
( !(l5) && _ && !(m0) && !(l4) && m1 ) |
( l5 && _ && m0 && !(l4) && m1 ) |
( !(l5) && !(b) && _ && l4 && m1 ) ;
}
static bool espresso_implementation_g3(bool c, bool r1, bool r3, bool m3, bool m5) {
// output_g3 = (c&r1&!m3&!r3) | (m5&c&m3&r3) | (!m5&!c&!r1) | (!c&!r1&!m3&!r3) | (!m5&!r1&m3&!r3) | (m5&r1&m3&!r3) | (m5&c&!r1&r3) | (m5&!c&r1&!m3&r3) | (!m5&c&r1&!m3) | (!m5&!c&m3&r3);
static const bool _ = true;
return
( _ && c && r1 && !(m3) && !(r3) ) |
( m5 && c && _ && m3 && r3 ) |
( !(m5) && !(c) && !(r1) && _ && _ ) |
( _ && !(c) && !(r1) && !(m3) && !(r3) ) |
( !(m5) && _ && !(r1) && m3 && !(r3) ) |
( m5 && _ && r1 && m3 && !(r3) ) |
( m5 && c && !(r1) && _ && r3 ) |
( m5 && !(c) && r1 && !(m3) && r3 ) |
( !(m5) && c && r1 && !(m3) && _ ) |
( !(m5) && !(c) && _ && m3 && r3 ) ;
}
static bool espresso_implementation_g4(bool r0, bool r2, bool r4, bool r5, bool r6) {
// output_g4 = (r2&r6&r0&!r5) | (r4&r6&!r0&r5) | (!r2&!r4&!r6) | (r2&r4&!r6&!r0&!r5) | (r2&!r4&r6&!r5) | (!r2&r4&r6&!r0) | (!r2&!r6&r0&!r5) | (!r4&!r6&!r0&r5) | (r2&r4&r0&r5) | (!r2&!r4&r0&r5);
static const bool _ = true;
return
( r2 && _ && r6 && r0 && !(r5) ) |
( _ && r4 && r6 && !(r0) && r5 ) |
( !(r2) && !(r4) && !(r6) && _ && _ ) |
( r2 && r4 && !(r6) && !(r0) && !(r5) ) |
( r2 && !(r4) && r6 && _ && !(r5) ) |
( !(r2) && r4 && r6 && !(r0) && _ ) |
( !(r2) && _ && !(r6) && r0 && !(r5) ) |
( _ && !(r4) && !(r6) && !(r0) && r5 ) |
( r2 && r4 && _ && r0 && r5 ) |
( !(r2) && !(r4) && _ && r0 && r5 ) ;
}
static bool espresso_implementation_XX(bool l0, bool l4, bool l6, bool m1, bool m3, bool r0, bool r3, bool r5) {
// output_XX = (!l6&m1&!r0) | (l4&!m3&r3) | (!l4&l6&!r5) | (!l0&!m1&m3) | (!l0&r3&!r5);
static const bool _ = true;
return
( _ && _ && !(l6) && m1 && _ && !(r0) && _ && _ ) |
( _ && l4 && _ && _ && !(m3) && _ && r3 && _ ) |
( _ && !(l4) && l6 && _ && _ && _ && _ && !(r5) ) |
( !(l0) && _ && _ && !(m1) && m3 && _ && _ && _ ) |
( !(l0) && _ && _ && _ && _ && _ && r3 && !(r5) );
}
static bool verify_alternatives(void)
{
bool any_failures_overall = false;
for (uint16_t q = 0; q < ARRAY_SIZE2(verification_table); ++q) {
bool any_failures_this_table = false;
VERIFICATION_T const * vt = &verification_table[q];
const char* fstr = vt->output_name;
uint16_t limit = 1u << vt->input_count;
for (uint16_t fn_idx = 0; fn_idx < vt->altFnCount; ++fn_idx ) {
bool any_failures_this_table_alts = false;
const lookuptable_func_t fn = vt->altFns[fn_idx];
for (uint16_t ix = 0; ix < limit; ++ix) {
uint8_t i = (uint8_t)ix;
bool lut_result = get_bit(vt->lut_start, i);
bool other_result = fn(i);
if (lut_result != other_result) {
printf("%s: verification failed for fn[ %d ]( %3d )\n", fstr, fn_idx, i);
any_failures_this_table_alts = true;
}
}
if (any_failures_this_table_alts) {
printf("FAILURE - %s alt %d\n", fstr, fn_idx);
} else {
printf("SUCCESS - %s alt %d\n", fstr, fn_idx);
}
// percolate to outer variable....
any_failures_this_table |= any_failures_this_table_alts;
}
if (vt->altFnCount == 0) {
// nothing was tested ...
} else if (any_failures_this_table) {
printf("FAILURE ---------> At least one of the alt functions for %s \n", fstr);
} else {
printf("SUCCESS ---------> All alternate for %s \n", fstr);
}
any_failures_overall |= any_failures_this_table;
}
if (any_failures_overall) {
printf("=====> FAILURE: Some of the alternate generators are still failing\n");
} else {
printf("=====> SUCCESS: All alternate generators are succeeding\n");
}
return !any_failures_overall;
}
static bool verify_lut_g1(void)
{
static const char* fstr = "verify_lut_g1";
bool any_failures = false;
for (uint8_t i = 0; i < 32; ++i) {
INDEX_G1 t = { .as_uint8_t = i };
bool lut_result = get_bit(small_lut_group1, i);
bool other_result = espresso_implementation_g1(t.a, t.l0, t.l2, t.l3, t.l6);
if (lut_result != other_result) {
printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t);
any_failures = true;
}
}
if (any_failures) {
printf("FAILURE - %s\n", fstr);
} else {
printf("SUCCESS - %s\n", fstr);
}
return !any_failures;
}
static bool verify_lut_g2(void)
{
static const char* fstr = "verify_lut_g2";
bool any_failures = false;
for (uint8_t i = 0; i < 32; ++i) {
INDEX_G2 t = { .as_uint8_t = i };
bool lut_result = get_bit(small_lut_group2, i);
bool other_result = espresso_implementation_g2(t.b, t.l4, t.l5, t.m0, t.m1);
if (lut_result != other_result) {
printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t);
any_failures = true;
}
}
if (any_failures) {
printf("FAILURE - %s\n", fstr);
} else {
printf("SUCCESS - %s\n", fstr);
}
return !any_failures;
}
static bool verify_lut_g3(void)
{
static const char* fstr = "verify_lut_g3";
bool any_failures = false;
for (uint8_t i = 0; i < 32; ++i) {
INDEX_G3 t = { .as_uint8_t = i };
bool lut_result = get_bit(small_lut_group3, i);
bool other_result = espresso_implementation_g3(t.c, t.r1, t.r3, t.m3, t.m5);
if (lut_result != other_result) {
printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t);
any_failures = true;
}
}
if (any_failures) {
printf("FAILURE - %s\n", fstr);
} else {
printf("SUCCESS - %s\n", fstr);
}
return !any_failures;
}
static bool verify_lut_g4(void)
{
static const char* fstr = "verify_lut_g4";
bool any_failures = false;
for (uint8_t i = 0; i < 32; ++i) {
INDEX_G4 t = { .as_uint8_t = i };
bool lut_result = get_bit(small_lut_group4, i);
bool other_result = espresso_implementation_g4(t.r0, t.r2, t.r4, t.r5, t.r6);
if (lut_result != other_result) {
printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t);
any_failures = true;
}
}
if (any_failures) {
printf("FAILURE - %s\n", fstr);
} else {
printf("SUCCESS - %s\n", fstr);
}
return !any_failures;
}
static bool verify_lut_XX(void)
{
static const char* fstr = "verify_lut_XX";
bool any_failures = false;
for (uint16_t ix = 0; ix < 256; ++ix) {
uint8_t i = (uint8_t)ix;
INDEX_XX t = { .as_uint8_t = i };
bool lut_result = get_bit(small_lut_XX, i);
bool other_result = espresso_implementation_XX(t.l0, t.l4, t.l6, t.m1, t.m3, t.r0, t.r3, t.r5);
if (lut_result != other_result) {
printf("%s: verification failed for index %d\n", fstr, t.as_uint8_t);
any_failures = true;
}
}
if (any_failures) {
printf("FAILURE - %s\n", fstr);
} else {
printf("SUCCESS - %s\n", fstr);
}
return !any_failures;
}
// order of input_names is based on resulting .PLA output,
// so least significant bit is at input_names[input_count-1]
static bool generate_espresso_file(uint8_t input_count, const char * const * input_names, const char * output_name, const uint8_t * lut_table_start, FILE* f_output) {
if (input_count > 8) {
printf("FAILURE - Cannot generate espresso file for more than 8 inputs (code would need update)\n");
}
fprintf(f_output, "# ###################\n");
fprintf(f_output, "# Generating .PLA\n");
fprintf(f_output, "# ###################\n");
fprintf(f_output, "\n\n");
fprintf(f_output, ".i %d\n", input_count);
fprintf(f_output, ".o 1\n");
const uint16_t iterations = 1u << input_count;
fprintf(f_output, ".ilb");
for (uint16_t i = 0; i < input_count; ++i) {
const char* name = input_names[i];
fprintf(f_output, " %s", name);
}
fprintf(f_output, "\n");
fprintf(f_output, ".ob %s\n", output_name);
for (uint16_t ix = 0; ix < iterations; ++ix) {
uint8_t i = (uint8_t)ix;
// NOTE: Seems we've handled it properly here, by shifting the bit to be tested appropriately
// based on the total number of inputs. So input_names[0] is the most significant bit....
for (uint8_t bitmask = (1u << (input_count - 1u)); bitmask != 0; bitmask >>= 1) {
fputc(((i & bitmask) != 0) ? '1' : '0', f_output);
}
bool bit = get_bit(lut_table_start, i);
fprintf(f_output, " %c\n", bit ? '1' : '0');
}
fprintf(f_output, ".e\n\n");
fprintf(f_output, "# ###################\n");
fprintf(f_output, "# END OF .PLA\n");
fprintf(f_output, "# ###################\n");
return true;
}
bool generate_all_lut_espresso_files(void)
{
bool any_failures = false;
for (size_t i = 0; i < ARRAY_SIZE2(verification_table); ++i) {
VERIFICATION_T const * args = &verification_table[i];
bool current_result = false;
FILE* file = NULL;
char filename[20] = {0}; // needs: 14 for two-letter output name
// First create the filename to be written
// Yes, presumes "safe" output_name ...
if (snprintf(NULL, 0, "output_%s.pla", args->output_name) < ARRAY_SIZE2(filename)) {
snprintf(filename, ARRAY_SIZE2(filename), "output_%s.pla", args->output_name);
file = fopen(filename, "w");
if (file == NULL) {
printf("failed to open output file '%s'", filename);
}
}
if (file != NULL) {
current_result = generate_espresso_file(
args->input_count,
args->names,
args->output_name,
args->lut_start,
file
);
if (!current_result) {
printf("Failed to generate espresso file for %s\n", args->output_name);
}
}
if (file != NULL) {
fclose(file);
file = NULL;
}
any_failures |= !current_result;
}
return !any_failures;
}
bool verify_espresso_results(void)
{
//generate_all_lut_espresso_files();
bool unused = verify_alternatives();
bool r_g1 = verify_lut_g1();
bool r_g2 = verify_lut_g2();
bool r_g3 = verify_lut_g3();
bool r_g4 = verify_lut_g4();
bool r_xx = verify_lut_XX();
return r_g1 && r_g2 && r_g3 && r_g4 && r_xx;
}

View file

@ -0,0 +1,7 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
bool generate_all_lut_espresso_files(void);
bool verify_espresso_results(void);

View file

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.21)
add_executable(
id48lib_test_generator
main.c
)
target_link_libraries(id48lib_test_generator PRIVATE id48lib)
target_compile_options(id48lib_test_generator PRIVATE
$<$<COMPILE_LANGUAGE:C>:-O0>
)

View file

@ -0,0 +1,157 @@
/**
The MIT License (MIT)
Copyright (c) SimpleHacks, Henry Gabryjelski
https://github.com/SimpleHacks/UtilHeaders
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef ARRAYSIZE2_H
#define ARRAYSIZE2_H
/**
The following, if defined prior to inclusion of this header file,
will modify its behavior as noted:
ARRAYSIZE2_SHOW_VERSION_MESSAGE
-- if defined, will show which version of ARRAY_SIZE2 macro is selected
*/
/**
see example source at:
https://godbolt.org/z/zzYoeK6Mf
*/
#ifndef __has_feature
#define __has_feature(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (defined(__cplusplus) && __cplusplus >= 201103L) || /* any compiler claiming C++11 support */ \
(defined(__cplusplus) && _MSC_VER >= 1900 && __cplusplus != 199711L) || /* Visual C++ 2015 or higher */ \
__has_feature(cxx_constexpr) /* CLang versions supporting constexp */
#include <stddef.h> /* required for size_t */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using C++11 version" )
#endif
namespace detail
{
template <typename T, size_t N>
constexpr size_t ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(T const (&)[N]) noexcept
{
return N;
}
} /* namespace detail */
#define ARRAY_SIZE2(arr) detail::ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(arr)
#elif defined(__cplusplus) && __cplusplus >= 199711L && ( /* C++ 98 trick */ \
defined(__INTEL_COMPILER) || \
defined(__clang__) || \
(defined(__GNUC__) && ( \
(__GNUC__ > 4) || \
(__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \
)))
#include <stddef.h> /* required for size_t */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message "ARRAY_SIZE2 -- Using C++98 version"
#endif
template <typename T, size_t N>
char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N];
#define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x))
#elif defined(__cplusplus) /* && ((__cplusplus >= 199711L) || defined(__INTEL_COMPILER) || defined(__clang__)) */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using Ivan J. Johnson's C++ version" )
#endif
/*
Works on older compilers, even Visual C++ 6....
Created by Ivan J. Johnson, March 06, 2007
See http://drdobbs.com/cpp/197800525?pgno=1
Full description is in markdown file array_size2.md
*/
#define ARRAY_SIZE2(arr) ( \
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_ARRAY_SIZE2*>(arr)) + /*check1*/ \
0 * sizeof(::Bad_arg_to_ARRAY_SIZE2::check_type((arr), &(arr))) + /*check2*/ \
sizeof(arr) / sizeof((arr)[0]) /* eval */ \
)
struct Bad_arg_to_ARRAY_SIZE2 {
class Is_pointer; /* incomplete */
class Is_array {};
template <typename T>
static Is_pointer check_type(T const *, T const * const *);
static Is_array check_type(void const *, void const *);
};
#elif !defined(__cplusplus) && defined(__GNUC__)
#include <stdint.h>
/**
Even C can have type-safety for equivalent of ARRAY_SIZE() macro,
when using the following two GCC extensions:
typeof()
__builtin_types_compatible_p()
*/
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using GNUC version" )
#endif
/**
validated using:
MSP430 gcc 4.5.3
x86-64 icc 16.0.3
x86-64 gcc 4.1.2
x86-64 clang 3.0.0
AVR gcc 4.5.4
ARM gcc 4.5.4
*/
#define __SIMPLEHACKS_COMPATIBLE_TYPES__(a,b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) /* GCC extensions */
#define __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(x) (sizeof(struct { uint8_t q: (-!!(x)*0x1ee7)+1u;})-1u) /* if x is zero, reports "error: negative width in bit-field '<anonymous>'" */
#define __SIMPLEHACKS_MUST_BE_ARRAY__(x) __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(__SIMPLEHACKS_COMPATIBLE_TYPES__((x), &(*x)))
#define ARRAY_SIZE2(_arr) ( (sizeof(_arr) / sizeof((_arr)[0])) + __SIMPLEHACKS_MUST_BE_ARRAY__(_arr) ) /* compile-time error if not an array */
#else
/**
The good news is that all compilers (as of 20202-05-08)
on godbolt.org are fully supported. Therefore, if some
other compiler does not support any of the above method,
it's important to force a compile-time error, to avoid
any suggestion that this provides a safe macro.
*/
#error "Unable to provide type-safe ARRAY_SIZE2 macro"
#endif
#endif // ARRAYSIZE2_H

View file

@ -0,0 +1,93 @@
#include "id48.h"
#include <stdio.h>
#include <string.h>
#include "array_size2.h"
static bool bytes_equal(const uint8_t *lhs, const uint8_t *rhs, size_t len) {
return memcmp(lhs, rhs, len) == 0;
}
typedef struct _TEST_VECTOR_T {
const char *description;
const ID48LIB_KEY key;
const ID48LIB_NONCE nonce;
const ID48LIB_FRN expected_frn;
const ID48LIB_GRN expected_grn;
} TEST_VECTOR_T;
// Test vectors used in various Proxmark3 client commands:
static const TEST_VECTOR_T test_vectors[] = {
// PM3 test key
{
// --key F32AA98CF5BE4ADFA6D3480B
// --rnd 45F54ADA252AAC
// --frn 4866BB70
// --grn 9BD180
.description = "PM3 test key",
.key = { .k = { 0xF3, 0x2A, 0xA9, 0x8C, 0xF5, 0xBE, 0x4A, 0xDF, 0xA6, 0xD3, 0x48, 0x0B, } },
.nonce = { .rn = { 0x45, 0xF5, 0x4A, 0xDA, 0x25, 0x2A, 0xAC, } },
.expected_frn = { .frn = { 0x48, 0x66, 0xBB, 0x70, } },
.expected_grn = { .grn = { 0x9B, 0xD1, 0x80, } },
},
// Research paper key
{
// --key A090A0A02080000000000000
// --rnd 3FFE1FB6CC513F
// --frn F355F1A0
// --grn 609D60
.description = "Research paper key",
.key = { .k = { 0xA0, 0x90, 0xA0, 0xA0, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } },
.nonce = { .rn = { 0x3F, 0xFE, 0x1F, 0xB6, 0xCC, 0x51, 0x3F, } },
.expected_frn = { .frn = { 0xF3, 0x55, 0xF1, 0xA0, } },
.expected_grn = { .grn = { 0x60, 0x9D, 0x60, } },
},
// Autorecovery test key
{
// --key 022A028C02BE000102030405
// --rnd 7D5167003571F8
// --frn 982DBCC0
// --grn 36C0E0
.description = "Autorecovery test key",
.key = { .k = { 0x02, 0x2A, 0x02, 0x8C, 0x02, 0xBE, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, } },
.nonce = { .rn = { 0x7D, 0x51, 0x67, 0x00, 0x35, 0x71, 0xF8, } },
.expected_frn = { .frn = { 0x98, 0x2D, 0xBC, 0xC0, } },
.expected_grn = { .grn = { 0x36, 0xC0, 0xE0, } },
},
};
bool generator_succeeds(const TEST_VECTOR_T *test_vector) {
ID48LIB_FRN actual_frn = {0};
ID48LIB_GRN actual_grn = {0};
id48lib_generator(&test_vector->key, &test_vector->nonce, &actual_frn, &actual_grn);
if (!bytes_equal(actual_frn.frn, test_vector->expected_frn.frn, sizeof(test_vector->expected_frn.frn))) {
printf("id48lib_generator: unexpected FRN for test vector '%s'\n", test_vector->description);
return false;
}
if (!bytes_equal(actual_grn.grn, test_vector->expected_grn.grn, sizeof(test_vector->expected_grn.grn))) {
printf("id48lib_generator: unexpected GRN for test vector '%s'\n", test_vector->description);
return false;
}
return true;
}
int main(void) {
bool any_failures = false;
for (size_t i = 0; i < ARRAY_SIZE2(test_vectors); i++) {
printf("Testing generator for test vector '%s'\n", test_vectors[i].description);
if (!generator_succeeds(&test_vectors[i])) {
printf("FAILURE: id48lib_generator: test vector '%s'\n", test_vectors[i].description);
any_failures = true;
}
}
if (any_failures) {
printf("id48lib_generator: some tests failed\n");
} else {
printf("id48lib_generator: all tests passed\n");
}
return any_failures ? 120 : 0;
}

View file

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.21)
add_executable(
id48lib_test_recovery
main.c
)
target_link_libraries(id48lib_test_recovery PRIVATE id48lib)
target_compile_options(id48lib_test_recovery PRIVATE
$<$<COMPILE_LANGUAGE:C>:-O0>
)

View file

@ -0,0 +1,157 @@
/**
The MIT License (MIT)
Copyright (c) SimpleHacks, Henry Gabryjelski
https://github.com/SimpleHacks/UtilHeaders
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef ARRAYSIZE2_H
#define ARRAYSIZE2_H
/**
The following, if defined prior to inclusion of this header file,
will modify its behavior as noted:
ARRAYSIZE2_SHOW_VERSION_MESSAGE
-- if defined, will show which version of ARRAY_SIZE2 macro is selected
*/
/**
see example source at:
https://godbolt.org/z/zzYoeK6Mf
*/
#ifndef __has_feature
#define __has_feature(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (defined(__cplusplus) && __cplusplus >= 201103L) || /* any compiler claiming C++11 support */ \
(defined(__cplusplus) && _MSC_VER >= 1900 && __cplusplus != 199711L) || /* Visual C++ 2015 or higher */ \
__has_feature(cxx_constexpr) /* CLang versions supporting constexp */
#include <stddef.h> /* required for size_t */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using C++11 version" )
#endif
namespace detail
{
template <typename T, size_t N>
constexpr size_t ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(T const (&)[N]) noexcept
{
return N;
}
} /* namespace detail */
#define ARRAY_SIZE2(arr) detail::ARRAY_SIZE2_ARGUMENT_CANNOT_BE_POINTER(arr)
#elif defined(__cplusplus) && __cplusplus >= 199711L && ( /* C++ 98 trick */ \
defined(__INTEL_COMPILER) || \
defined(__clang__) || \
(defined(__GNUC__) && ( \
(__GNUC__ > 4) || \
(__GNUC__ == 4 && __GNUC_MINOR__ >= 4) \
)))
#include <stddef.h> /* required for size_t */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message "ARRAY_SIZE2 -- Using C++98 version"
#endif
template <typename T, size_t N>
char(&_ArraySizeHelperRequiresArray(T(&)[N]))[N];
#define ARRAY_SIZE2(x) sizeof(_ArraySizeHelperRequiresArray(x))
#elif defined(__cplusplus) /* && ((__cplusplus >= 199711L) || defined(__INTEL_COMPILER) || defined(__clang__)) */
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using Ivan J. Johnson's C++ version" )
#endif
/*
Works on older compilers, even Visual C++ 6....
Created by Ivan J. Johnson, March 06, 2007
See http://drdobbs.com/cpp/197800525?pgno=1
Full description is in markdown file array_size2.md
*/
#define ARRAY_SIZE2(arr) ( \
0 * sizeof(reinterpret_cast<const ::Bad_arg_to_ARRAY_SIZE2*>(arr)) + /*check1*/ \
0 * sizeof(::Bad_arg_to_ARRAY_SIZE2::check_type((arr), &(arr))) + /*check2*/ \
sizeof(arr) / sizeof((arr)[0]) /* eval */ \
)
struct Bad_arg_to_ARRAY_SIZE2 {
class Is_pointer; /* incomplete */
class Is_array {};
template <typename T>
static Is_pointer check_type(T const *, T const * const *);
static Is_array check_type(void const *, void const *);
};
#elif !defined(__cplusplus) && defined(__GNUC__)
#include <stdint.h>
/**
Even C can have type-safety for equivalent of ARRAY_SIZE() macro,
when using the following two GCC extensions:
typeof()
__builtin_types_compatible_p()
*/
#if defined(ARRAYSIZE2_SHOW_VERSION_MESSAGE)
#pragma message( "ARRAY_SIZE2 -- Using GNUC version" )
#endif
/**
validated using:
MSP430 gcc 4.5.3
x86-64 icc 16.0.3
x86-64 gcc 4.1.2
x86-64 clang 3.0.0
AVR gcc 4.5.4
ARM gcc 4.5.4
*/
#define __SIMPLEHACKS_COMPATIBLE_TYPES__(a,b) __builtin_types_compatible_p(__typeof__(a), __typeof__(b)) /* GCC extensions */
#define __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(x) (sizeof(struct { uint8_t q: (-!!(x)*0x1ee7)+1u;})-1u) /* if x is zero, reports "error: negative width in bit-field '<anonymous>'" */
#define __SIMPLEHACKS_MUST_BE_ARRAY__(x) __SIMPLEHACKS_BUILD_ERROR_ON_NONZERO__(__SIMPLEHACKS_COMPATIBLE_TYPES__((x), &(*x)))
#define ARRAY_SIZE2(_arr) ( (sizeof(_arr) / sizeof((_arr)[0])) + __SIMPLEHACKS_MUST_BE_ARRAY__(_arr) ) /* compile-time error if not an array */
#else
/**
The good news is that all compilers (as of 20202-05-08)
on godbolt.org are fully supported. Therefore, if some
other compiler does not support any of the above method,
it's important to force a compile-time error, to avoid
any suggestion that this provides a safe macro.
*/
#error "Unable to provide type-safe ARRAY_SIZE2 macro"
#endif
#endif // ARRAYSIZE2_H

View file

@ -0,0 +1,173 @@
#include "id48.h"
#include <stdio.h>
#include <string.h>
#include "array_size2.h"
static bool bytes_equal(const uint8_t *lhs, const uint8_t *rhs, size_t len) {
return memcmp(lhs, rhs, len) == 0;
}
typedef struct _TEST_VECTOR_T {
const char *description;
const ID48LIB_KEY key;
const ID48LIB_NONCE nonce;
const ID48LIB_FRN expected_frn;
const ID48LIB_GRN expected_grn;
} TEST_VECTOR_T;
// Test vectors used in various Proxmark3 client commands:
static const TEST_VECTOR_T test_vectors[] = {
// PM3 test key
{
// --key F32AA98CF5BE4ADFA6D3480B
// --rnd 45F54ADA252AAC
// --frn 4866BB70
// --grn 9BD180
.description = "PM3 test key",
.key = { .k = { 0xF3, 0x2A, 0xA9, 0x8C, 0xF5, 0xBE, 0x4A, 0xDF, 0xA6, 0xD3, 0x48, 0x0B, } },
.nonce = { .rn = { 0x45, 0xF5, 0x4A, 0xDA, 0x25, 0x2A, 0xAC, } },
.expected_frn = { .frn = { 0x48, 0x66, 0xBB, 0x70, } },
.expected_grn = { .grn = { 0x9B, 0xD1, 0x80, } },
},
// Research paper key
{
// --key A090A0A02080000000000000
// --rnd 3FFE1FB6CC513F
// --frn F355F1A0
// --grn 609D60
.description = "Research paper key",
.key = { .k = { 0xA0, 0x90, 0xA0, 0xA0, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } },
.nonce = { .rn = { 0x3F, 0xFE, 0x1F, 0xB6, 0xCC, 0x51, 0x3F, } },
.expected_frn = { .frn = { 0xF3, 0x55, 0xF1, 0xA0, } },
.expected_grn = { .grn = { 0x60, 0x9D, 0x60, } },
},
// Autorecovery test key
{
// --key 022A028C02BE000102030405
// --rnd 7D5167003571F8
// --frn 982DBCC0
// --grn 36C0E0
.description = "Autorecovery test key",
.key = { .k = { 0x02, 0x2A, 0x02, 0x8C, 0x02, 0xBE, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, } },
.nonce = { .rn = { 0x7D, 0x51, 0x67, 0x00, 0x35, 0x71, 0xF8, } },
.expected_frn = { .frn = { 0x98, 0x2D, 0xBC, 0xC0, } },
.expected_grn = { .grn = { 0x36, 0xC0, 0xE0, } },
},
};
// Comment
/// <summary>
/// Initializes to allow iterative recovery
/// of multiple potential keys. After calling
/// this init() function, can repeatedly call
/// the next() function until it returns false
/// to obtain all potential keys.
/// </summary>
/// <param name="input_partial_key">
/// Top 48 bits of the key, such as those discovered
/// using the proxmark3 command `lf em 4x70 brute`.
/// Only k[0..5] are used from this parameter,
/// corresponding to K₉₅..K₄₈.
/// </param>
/// <param name="input_nonce">
/// The nonce value.
/// Typically from a sniffed authentication.
/// </param>
/// <param name="input_frn">
/// The challenge sent from the reader (e.g., car)
/// to the tag (e.g., key).
/// Typically from a sniffed authentication.
/// </param>
/// <param name="input_grn">
/// The response sent from the tag (e.g., key)
/// to the car (e.g., car).
/// Typically from a sniffed authentication.
/// </param>
/// <remarks>
/// Note: In C++, each parameter would be a reference (not pointer).
/// </remarks>
// void id48lib_key_recovery_init(
// const ID48LIB_KEY *input_partial_key,
// const ID48LIB_NONCE *input_nonce,
// const ID48LIB_FRN *input_frn,
// const ID48LIB_GRN *input_grn
// );
// Comment
/// <summary>
/// This can be repeated called (after calling init())
/// to find the next potential key for the given
/// partial key + nonce + frn + grn values.
/// I've seen combinations that have up to six
/// potential keys available, although typically
/// there are 1-3 results.
/// Each call to this function will return a single
/// value. Call repeatedly until the function returns
/// false to get all potential keys.
/// </summary>
/// <param name="potential_key_output">
/// When the function returns true, this caller-provided
/// value will be filled with the 96-bit key that, when
/// programmed to the tag, should authenticate against
/// the nonce+frn values, with tag returning the grn value.
/// </param>
/// <returns>
/// true when another potential key has been found.
/// false if no additional potential keys have been found.
/// </returns>
/// <remarks>
/// Note: In C++, each parameter would be a reference (not pointer).
/// </remarks>
// bool id48lib_key_recovery_next(
// ID48LIB_KEY *potential_key_output
// );
bool recovery_succeeds(const TEST_VECTOR_T *test_vector, bool zero_partial_key) {
ID48LIB_KEY partial_key = {0};
memcpy(&partial_key, &test_vector->key, sizeof(partial_key));
if (zero_partial_key) {
// API is only supposed to be looking at the first 6 bytes of the key
memset(&partial_key.k[6], 0, sizeof(partial_key.k) - 6);
}
id48lib_key_recovery_init(&partial_key, &test_vector->nonce, &test_vector->expected_frn, &test_vector->expected_grn);
bool key_found = false;
uint32_t potential_keys_found = 0;
ID48LIB_KEY potential_key = {0};
while (id48lib_key_recovery_next(&potential_key)) {
potential_keys_found++;
// just verify that the potential key matches the test vector
if (bytes_equal(potential_key.k, test_vector->key.k, sizeof(test_vector->key.k))) {
key_found = true;
}
}
return key_found;
}
int main(void) {
bool any_failures = false;
for (size_t i = 0; i < ARRAY_SIZE2(test_vectors); i++) {
printf("Testing recovery for test vector '%s'\n", test_vectors[i].description);
if (!recovery_succeeds(&test_vectors[i], false)) {
printf("FAILURE: id48lib_recovery: test vector '%s'\n", test_vectors[i].description);
any_failures = true;
}
printf("Testing recovery for test vector '%s' (partially-zero'd key)\n", test_vectors[i].description);
if (!recovery_succeeds(&test_vectors[i], true)) {
printf("FAILURE: id48lib_recovery: test vector '%s' (partially-zero'd key)\n", test_vectors[i].description);
any_failures = true;
}
}
if (any_failures) {
printf("id48lib_recovery: some tests failed\n");
} else {
printf("id48lib_recovery: all tests passed\n");
}
return any_failures ? 120 : 0;
}

View file

@ -1,9 +1,11 @@
# This is manually re-creating the contents of id48/src/CMakeLists.txt
# and thus must be manually kept in sync with updates to id48/src.
add_library(pm3rrg_rdv4_id48 STATIC
id48/id48_data.c
id48/id48_generator.c
id48/id48_recover.c
id48/src/id48_data.c
id48/src/id48_generator.c
id48/src/id48_recover.c
)
target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO)
target_include_directories(pm3rrg_rdv4_id48 PRIVATE id48)
target_include_directories(pm3rrg_rdv4_id48 INTERFACE id48)
target_include_directories(pm3rrg_rdv4_id48 PRIVATE id48/public)
target_include_directories(pm3rrg_rdv4_id48 INTERFACE id48/public)
set_property(TARGET pm3rrg_rdv4_id48 PROPERTY POSITION_INDEPENDENT_CODE ON)