diff --git a/emb/internal/binary/binary.go b/emb/internal/binary/binary.go new file mode 100644 index 0000000..25f8d39 --- /dev/null +++ b/emb/internal/binary/binary.go @@ -0,0 +1,37 @@ +// Package binary is a lightweight replacement package for encoding/binary. +package binary + +// This file contains small helper functions for working with binary data. + +var LittleEndian = littleEndian{} + +type littleEndian struct{} + +// Encode data like encoding/binary.LittleEndian.Uint16. +func (littleEndian) Uint16(b []byte) uint16 { + return uint16(b[0]) | uint16(b[1])<<8 +} + +// Store data like binary.LittleEndian.PutUint16. +func (littleEndian) PutUint16(b []byte, v uint16) { + b[0] = byte(v) + b[1] = byte(v >> 8) +} + +// Append data like binary.LittleEndian.AppendUint16. +func (littleEndian) AppendUint16(b []byte, v uint16) []byte { + return append(b, + byte(v), + byte(v>>8), + ) +} + +// Encode data like encoding/binary.LittleEndian.Uint32. +func (littleEndian) Uint32(b []byte) uint32 { + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func (littleEndian) Uint64(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} diff --git a/emb/internal/bytealg/bytealg.go b/emb/internal/bytealg/bytealg.go new file mode 100644 index 0000000..33ece2b --- /dev/null +++ b/emb/internal/bytealg/bytealg.go @@ -0,0 +1,352 @@ +package bytealg + +// Some code in this file has been copied from the Go source code, and has +// copyright of their original authors: +// +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// This is indicated specifically in the file. + +const ( + // Index can search any valid length of string. + + MaxLen = int(-1) >> 31 + MaxBruteForce = MaxLen +) + +// Compare two byte slices. +// Returns -1 if the first differing byte is lower in a, or 1 if the first differing byte is greater in b. +// If the byte slices are equal, returns 0. +// If the lengths are different and there are no differing bytes, compares based on length. +func Compare(a, b []byte) int { + // Compare for differing bytes. + for i := 0; i < len(a) && i < len(b); i++ { + switch { + case a[i] < b[i]: + return -1 + case a[i] > b[i]: + return 1 + } + } + + // Compare lengths. + switch { + case len(a) > len(b): + return 1 + case len(a) < len(b): + return -1 + default: + return 0 + } +} + +// This function was copied from the Go 1.23 source tree (with runtime_cmpstring +// manually inlined). +func CompareString(a, b string) int { + l := len(a) + if len(b) < l { + l = len(b) + } + for i := 0; i < l; i++ { + c1, c2 := a[i], b[i] + if c1 < c2 { + return -1 + } + if c1 > c2 { + return +1 + } + } + if len(a) < len(b) { + return -1 + } + if len(a) > len(b) { + return +1 + } + return 0 +} + +// Count the number of instances of a byte in a slice. +func Count(b []byte, c byte) int { + // Use a simple implementation, as there is no intrinsic that does this like we want. + n := 0 + for _, v := range b { + if v == c { + n++ + } + } + return n +} + +// Count the number of instances of a byte in a string. +func CountString(s string, c byte) int { + // Use a simple implementation, as there is no intrinsic that does this like we want. + // Currently, the compiler does not generate zero-copy byte-string conversions, so this needs to be separate from Count. + n := 0 + for i := 0; i < len(s); i++ { + if s[i] == c { + n++ + } + } + return n +} + +// Cutover is not reachable in TinyGo, but must exist as it is referenced. +func Cutover(n int) int { + // Setting MaxLen and MaxBruteForce should force a different path to be taken. + // This should never be called. + panic("cutover is unreachable") +} + +// Equal checks if two byte slices are equal. +// It is equivalent to bytes.Equal. +func Equal(a, b []byte) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +} + +// Index finds the base index of the first instance of the byte sequence b in a. +// If a does not contain b, this returns -1. +func Index(a, b []byte) int { + for i := 0; i <= len(a)-len(b); i++ { + if Equal(a[i:i+len(b)], b) { + return i + } + } + return -1 +} + +// Index finds the index of the first instance of the specified byte in the slice. +// If the byte is not found, this returns -1. +func IndexByte(b []byte, c byte) int { + for i, v := range b { + if v == c { + return i + } + } + return -1 +} + +// Index finds the index of the first instance of the specified byte in the string. +// If the byte is not found, this returns -1. +func IndexByteString(s string, c byte) int { + for i := 0; i < len(s); i++ { + if s[i] == c { + return i + } + } + return -1 +} + +// Index finds the base index of the first instance of a substring in a string. +// If the substring is not found, this returns -1. +func IndexString(str, sub string) int { + for i := 0; i <= len(str)-len(sub); i++ { + if str[i:i+len(sub)] == sub { + return i + } + } + return -1 +} + +// The following code has been copied from the Go 1.15 release tree. + +// PrimeRK is the prime base used in Rabin-Karp algorithm. +const PrimeRK = 16777619 + +// HashStrBytes returns the hash and the appropriate multiplicative +// factor for use in Rabin-Karp algorithm. +// +// This function was removed in Go 1.22. +func HashStrBytes(sep []byte) (uint32, uint32) { + hash := uint32(0) + for i := 0; i < len(sep); i++ { + hash = hash*PrimeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, PrimeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + +// HashStr returns the hash and the appropriate multiplicative +// factor for use in Rabin-Karp algorithm. +// +// This function was removed in Go 1.22. +func HashStr[T string | []byte](sep T) (uint32, uint32) { + hash := uint32(0) + for i := 0; i < len(sep); i++ { + hash = hash*PrimeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, PrimeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + +// HashStrRevBytes returns the hash of the reverse of sep and the +// appropriate multiplicative factor for use in Rabin-Karp algorithm. +// +// This function was removed in Go 1.22. +func HashStrRevBytes(sep []byte) (uint32, uint32) { + hash := uint32(0) + for i := len(sep) - 1; i >= 0; i-- { + hash = hash*PrimeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, PrimeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + +// HashStrRev returns the hash of the reverse of sep and the +// appropriate multiplicative factor for use in Rabin-Karp algorithm. +// +// Copied from the Go 1.22rc1 source tree. +func HashStrRev[T string | []byte](sep T) (uint32, uint32) { + hash := uint32(0) + for i := len(sep) - 1; i >= 0; i-- { + hash = hash*PrimeRK + uint32(sep[i]) + } + var pow, sq uint32 = 1, PrimeRK + for i := len(sep); i > 0; i >>= 1 { + if i&1 != 0 { + pow *= sq + } + sq *= sq + } + return hash, pow +} + +// IndexRabinKarpBytes uses the Rabin-Karp search algorithm to return the index of the +// first occurrence of substr in s, or -1 if not present. +// +// This function was removed in Go 1.22. +func IndexRabinKarpBytes(s, sep []byte) int { + // Rabin-Karp search + hashsep, pow := HashStrBytes(sep) + n := len(sep) + var h uint32 + for i := 0; i < n; i++ { + h = h*PrimeRK + uint32(s[i]) + } + if h == hashsep && Equal(s[:n], sep) { + return 0 + } + for i := n; i < len(s); { + h *= PrimeRK + h += uint32(s[i]) + h -= pow * uint32(s[i-n]) + i++ + if h == hashsep && Equal(s[i-n:i], sep) { + return i - n + } + } + return -1 +} + +// IndexRabinKarp uses the Rabin-Karp search algorithm to return the index of the +// first occurrence of sep in s, or -1 if not present. +// +// Copied from the Go 1.22rc1 source tree. +func IndexRabinKarp[T string | []byte](s, sep T) int { + // Rabin-Karp search + hashss, pow := HashStr(sep) + n := len(sep) + var h uint32 + for i := 0; i < n; i++ { + h = h*PrimeRK + uint32(s[i]) + } + if h == hashss && string(s[:n]) == string(sep) { + return 0 + } + for i := n; i < len(s); { + h *= PrimeRK + h += uint32(s[i]) + h -= pow * uint32(s[i-n]) + i++ + if h == hashss && string(s[i-n:i]) == string(sep) { + return i - n + } + } + return -1 +} + +// MakeNoZero makes a slice of length and capacity n without zeroing the bytes. +// It is the caller's responsibility to ensure uninitialized bytes +// do not leak to the end user. +func MakeNoZero(n int) []byte { + // Note: this does zero the buffer even though that's not necessary. + // For performance reasons we might want to change this (similar to the + // malloc function implemented in the runtime). + return make([]byte, n) +} + +// Copied from the Go 1.22rc1 source tree. +func LastIndexByte(s []byte, c byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == c { + return i + } + } + return -1 +} + +// Copied from the Go 1.22rc1 source tree. +func LastIndexByteString(s string, c byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == c { + return i + } + } + return -1 +} + +// LastIndexRabinKarp uses the Rabin-Karp search algorithm to return the last index of the +// occurrence of sep in s, or -1 if not present. +// +// Copied from the Go 1.22rc1 source tree. +func LastIndexRabinKarp[T string | []byte](s, sep T) int { + // Rabin-Karp search from the end of the string + hashss, pow := HashStrRev(sep) + n := len(sep) + last := len(s) - n + var h uint32 + for i := len(s) - 1; i >= last; i-- { + h = h*PrimeRK + uint32(s[i]) + } + if h == hashss && string(s[last:]) == string(sep) { + return last + } + for i := last - 1; i >= 0; i-- { + h *= PrimeRK + h += uint32(s[i]) + h -= pow * uint32(s[i+n]) + if h == hashss && string(s[i:i+n]) == string(sep) { + return i + } + } + return -1 +} diff --git a/emb/machine/_usb/adc/doc.go b/emb/machine/usb/adc/doc.go similarity index 100% rename from emb/machine/_usb/adc/doc.go rename to emb/machine/usb/adc/doc.go diff --git a/emb/machine/_usb/adc/midi/buffer.go b/emb/machine/usb/adc/midi/buffer.go similarity index 96% rename from emb/machine/_usb/adc/midi/buffer.go rename to emb/machine/usb/adc/midi/buffer.go index 39cab2c..0ffefe5 100644 --- a/emb/machine/_usb/adc/midi/buffer.go +++ b/emb/machine/usb/adc/midi/buffer.go @@ -1,7 +1,7 @@ package midi import ( - "runtime/volatile" + "github.com/goplus/lib/emb/runtime/volatile" ) const bufferSize = 128 diff --git a/emb/machine/_usb/adc/midi/messages.go b/emb/machine/usb/adc/midi/messages.go similarity index 100% rename from emb/machine/_usb/adc/midi/messages.go rename to emb/machine/usb/adc/midi/messages.go diff --git a/emb/machine/_usb/adc/midi/midi.go b/emb/machine/usb/adc/midi/midi.go similarity index 94% rename from emb/machine/_usb/adc/midi/midi.go rename to emb/machine/usb/adc/midi/midi.go index 8ead2e9..e099f66 100644 --- a/emb/machine/_usb/adc/midi/midi.go +++ b/emb/machine/usb/adc/midi/midi.go @@ -1,9 +1,9 @@ package midi import ( - "machine" - "machine/usb" - "machine/usb/descriptor" + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/machine/usb/descriptor" ) const ( diff --git a/emb/machine/_usb/adc/midi/notes.go b/emb/machine/usb/adc/midi/notes.go similarity index 100% rename from emb/machine/_usb/adc/midi/notes.go rename to emb/machine/usb/adc/midi/notes.go diff --git a/emb/machine/_usb/cdc/buffer.go b/emb/machine/usb/cdc/buffer.go similarity index 98% rename from emb/machine/_usb/cdc/buffer.go rename to emb/machine/usb/cdc/buffer.go index ad5eb36..b34df9d 100644 --- a/emb/machine/_usb/cdc/buffer.go +++ b/emb/machine/usb/cdc/buffer.go @@ -1,7 +1,7 @@ package cdc import ( - "runtime/volatile" + "github.com/goplus/lib/emb/runtime/volatile" ) const rxRingBufferSize = 128 diff --git a/emb/machine/_usb/cdc/cdc.go b/emb/machine/usb/cdc/cdc.go similarity index 100% rename from emb/machine/_usb/cdc/cdc.go rename to emb/machine/usb/cdc/cdc.go diff --git a/emb/machine/_usb/cdc/doc.go b/emb/machine/usb/cdc/doc.go similarity index 100% rename from emb/machine/_usb/cdc/doc.go rename to emb/machine/usb/cdc/doc.go diff --git a/emb/machine/_usb/cdc/usbcdc.go b/emb/machine/usb/cdc/usbcdc.go similarity index 97% rename from emb/machine/_usb/cdc/usbcdc.go rename to emb/machine/usb/cdc/usbcdc.go index 5b5ffbf..ca0cd83 100644 --- a/emb/machine/_usb/cdc/usbcdc.go +++ b/emb/machine/usb/cdc/usbcdc.go @@ -2,9 +2,10 @@ package cdc import ( "errors" - "machine" - "machine/usb" - "runtime/interrupt" + + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/runtime/interrupt" ) var ( diff --git a/emb/machine/_usb/config.go b/emb/machine/usb/config.go similarity index 100% rename from emb/machine/_usb/config.go rename to emb/machine/usb/config.go diff --git a/emb/machine/_usb/descriptor/cdc.go b/emb/machine/usb/descriptor/cdc.go similarity index 100% rename from emb/machine/_usb/descriptor/cdc.go rename to emb/machine/usb/descriptor/cdc.go diff --git a/emb/machine/_usb/descriptor/classspecific.go b/emb/machine/usb/descriptor/classspecific.go similarity index 100% rename from emb/machine/_usb/descriptor/classspecific.go rename to emb/machine/usb/descriptor/classspecific.go diff --git a/emb/machine/_usb/descriptor/configuration.go b/emb/machine/usb/descriptor/configuration.go similarity index 94% rename from emb/machine/_usb/descriptor/configuration.go rename to emb/machine/usb/descriptor/configuration.go index efb9ab1..fb0fad0 100644 --- a/emb/machine/_usb/descriptor/configuration.go +++ b/emb/machine/usb/descriptor/configuration.go @@ -1,7 +1,7 @@ package descriptor import ( - "internal/binary" + "github.com/goplus/lib/emb/internal/binary" ) const ( diff --git a/emb/machine/_usb/descriptor/descriptor.go b/emb/machine/usb/descriptor/descriptor.go similarity index 96% rename from emb/machine/_usb/descriptor/descriptor.go rename to emb/machine/usb/descriptor/descriptor.go index 852ddab..ec60719 100644 --- a/emb/machine/_usb/descriptor/descriptor.go +++ b/emb/machine/usb/descriptor/descriptor.go @@ -1,7 +1,7 @@ package descriptor import ( - "runtime/volatile" + "github.com/goplus/lib/emb/runtime/volatile" ) const ( diff --git a/emb/machine/_usb/descriptor/device.go b/emb/machine/usb/descriptor/device.go similarity index 96% rename from emb/machine/_usb/descriptor/device.go rename to emb/machine/usb/descriptor/device.go index 0c3ee92..c6d0773 100644 --- a/emb/machine/_usb/descriptor/device.go +++ b/emb/machine/usb/descriptor/device.go @@ -1,7 +1,7 @@ package descriptor import ( - "internal/binary" + "github.com/goplus/lib/emb/internal/binary" ) const ( diff --git a/emb/machine/_usb/descriptor/doc.go b/emb/machine/usb/descriptor/doc.go similarity index 100% rename from emb/machine/_usb/descriptor/doc.go rename to emb/machine/usb/descriptor/doc.go diff --git a/emb/machine/_usb/descriptor/endpoint.go b/emb/machine/usb/descriptor/endpoint.go similarity index 98% rename from emb/machine/_usb/descriptor/endpoint.go rename to emb/machine/usb/descriptor/endpoint.go index 57a1706..dcb9209 100644 --- a/emb/machine/_usb/descriptor/endpoint.go +++ b/emb/machine/usb/descriptor/endpoint.go @@ -1,7 +1,7 @@ package descriptor import ( - "internal/binary" + "github.com/goplus/lib/emb/internal/binary" ) /* Endpoint Descriptor diff --git a/emb/machine/_usb/descriptor/hid.go b/emb/machine/usb/descriptor/hid.go similarity index 98% rename from emb/machine/_usb/descriptor/hid.go rename to emb/machine/usb/descriptor/hid.go index 06b9801..c2d3c41 100644 --- a/emb/machine/_usb/descriptor/hid.go +++ b/emb/machine/usb/descriptor/hid.go @@ -2,8 +2,9 @@ package descriptor import ( "errors" - "internal/binary" - "internal/bytealg" + + "github.com/goplus/lib/emb/internal/binary" + "github.com/goplus/lib/emb/internal/bytealg" ) var configurationCDCHID = [configurationTypeLen]byte{ diff --git a/emb/machine/_usb/descriptor/hidreport.go b/emb/machine/usb/descriptor/hidreport.go similarity index 100% rename from emb/machine/_usb/descriptor/hidreport.go rename to emb/machine/usb/descriptor/hidreport.go diff --git a/emb/machine/_usb/descriptor/interface.go b/emb/machine/usb/descriptor/interface.go similarity index 100% rename from emb/machine/_usb/descriptor/interface.go rename to emb/machine/usb/descriptor/interface.go diff --git a/emb/machine/_usb/descriptor/interfaceassociation.go b/emb/machine/usb/descriptor/interfaceassociation.go similarity index 100% rename from emb/machine/_usb/descriptor/interfaceassociation.go rename to emb/machine/usb/descriptor/interfaceassociation.go diff --git a/emb/machine/_usb/descriptor/joystick.go b/emb/machine/usb/descriptor/joystick.go similarity index 100% rename from emb/machine/_usb/descriptor/joystick.go rename to emb/machine/usb/descriptor/joystick.go diff --git a/emb/machine/_usb/descriptor/midi.go b/emb/machine/usb/descriptor/midi.go similarity index 100% rename from emb/machine/_usb/descriptor/midi.go rename to emb/machine/usb/descriptor/midi.go diff --git a/emb/machine/_usb/descriptor/msc.go b/emb/machine/usb/descriptor/msc.go similarity index 100% rename from emb/machine/_usb/descriptor/msc.go rename to emb/machine/usb/descriptor/msc.go diff --git a/emb/machine/_usb/doc.go b/emb/machine/usb/doc.go similarity index 100% rename from emb/machine/_usb/doc.go rename to emb/machine/usb/doc.go diff --git a/emb/machine/_usb/hid/buffer.go b/emb/machine/usb/hid/buffer.go similarity index 96% rename from emb/machine/_usb/hid/buffer.go rename to emb/machine/usb/hid/buffer.go index 2674bbd..3bcf60a 100644 --- a/emb/machine/_usb/hid/buffer.go +++ b/emb/machine/usb/hid/buffer.go @@ -1,7 +1,7 @@ package hid import ( - "runtime/volatile" + "github.com/goplus/lib/emb/runtime/volatile" ) const bufferSize = 128 diff --git a/emb/machine/_usb/hid/doc.go b/emb/machine/usb/hid/doc.go similarity index 100% rename from emb/machine/_usb/hid/doc.go rename to emb/machine/usb/hid/doc.go diff --git a/emb/machine/_usb/hid/hid.go b/emb/machine/usb/hid/hid.go similarity index 93% rename from emb/machine/_usb/hid/hid.go rename to emb/machine/usb/hid/hid.go index 791fd06..b72a26b 100644 --- a/emb/machine/_usb/hid/hid.go +++ b/emb/machine/usb/hid/hid.go @@ -2,9 +2,10 @@ package hid import ( "errors" - "machine" - "machine/usb" - "machine/usb/descriptor" + + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/machine/usb/descriptor" ) // from usb-hid.go diff --git a/emb/machine/_usb/hid/joystick/joystick.go b/emb/machine/usb/hid/joystick/joystick.go similarity index 93% rename from emb/machine/_usb/hid/joystick/joystick.go rename to emb/machine/usb/hid/joystick/joystick.go index 2c2f719..ad0ea32 100644 --- a/emb/machine/_usb/hid/joystick/joystick.go +++ b/emb/machine/usb/hid/joystick/joystick.go @@ -1,10 +1,10 @@ package joystick import ( - "machine" - "machine/usb" - "machine/usb/descriptor" - "machine/usb/hid" + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/machine/usb/descriptor" + "github.com/goplus/lib/emb/machine/usb/hid" ) var Joystick *joystick diff --git a/emb/machine/_usb/hid/joystick/state.go b/emb/machine/usb/hid/joystick/state.go similarity index 98% rename from emb/machine/_usb/hid/joystick/state.go rename to emb/machine/usb/hid/joystick/state.go index 08265ab..a70081b 100644 --- a/emb/machine/_usb/hid/joystick/state.go +++ b/emb/machine/usb/hid/joystick/state.go @@ -1,9 +1,9 @@ package joystick import ( - "machine/usb/descriptor" - "encoding/binary" + + "github.com/goplus/lib/emb/machine/usb/descriptor" ) type HatDirection uint8 diff --git a/emb/machine/_usb/hid/keyboard/keyboard.go b/emb/machine/usb/hid/keyboard/keyboard.go similarity index 99% rename from emb/machine/_usb/hid/keyboard/keyboard.go rename to emb/machine/usb/hid/keyboard/keyboard.go index 9f5f420..66f5eb5 100644 --- a/emb/machine/_usb/hid/keyboard/keyboard.go +++ b/emb/machine/usb/hid/keyboard/keyboard.go @@ -2,8 +2,9 @@ package keyboard import ( "errors" - "machine" - "machine/usb/hid" + + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb/hid" ) // from usb-hid-keyboard.go diff --git a/emb/machine/_usb/hid/keyboard/keycode.go b/emb/machine/usb/hid/keyboard/keycode.go similarity index 100% rename from emb/machine/_usb/hid/keyboard/keycode.go rename to emb/machine/usb/hid/keyboard/keycode.go diff --git a/emb/machine/_usb/hid/mouse/mouse.go b/emb/machine/usb/hid/mouse/mouse.go similarity index 96% rename from emb/machine/_usb/hid/mouse/mouse.go rename to emb/machine/usb/hid/mouse/mouse.go index d790bdb..4fc92f4 100644 --- a/emb/machine/_usb/hid/mouse/mouse.go +++ b/emb/machine/usb/hid/mouse/mouse.go @@ -1,8 +1,8 @@ package mouse import ( - "machine" - "machine/usb/hid" + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb/hid" ) var Mouse *mouse diff --git a/emb/machine/_usb/msc/cbw.go b/emb/machine/usb/msc/cbw.go similarity index 92% rename from emb/machine/_usb/msc/cbw.go rename to emb/machine/usb/msc/cbw.go index 7ebf346..26e4790 100644 --- a/emb/machine/_usb/msc/cbw.go +++ b/emb/machine/usb/msc/cbw.go @@ -2,8 +2,9 @@ package msc import ( "encoding/binary" - "machine/usb/msc/csw" - "machine/usb/msc/scsi" + + "github.com/goplus/lib/emb/machine/usb/msc/csw" + "github.com/goplus/lib/emb/machine/usb/msc/scsi" ) const ( diff --git a/emb/machine/_usb/msc/csw/csw.go b/emb/machine/usb/msc/csw/csw.go similarity index 100% rename from emb/machine/_usb/msc/csw/csw.go rename to emb/machine/usb/msc/csw/csw.go diff --git a/emb/machine/_usb/msc/disk.go b/emb/machine/usb/msc/disk.go similarity index 99% rename from emb/machine/_usb/msc/disk.go rename to emb/machine/usb/msc/disk.go index 6624d38..60fc9af 100644 --- a/emb/machine/_usb/msc/disk.go +++ b/emb/machine/usb/msc/disk.go @@ -4,8 +4,9 @@ import ( "encoding/binary" "errors" "fmt" - "machine" "time" + + "github.com/goplus/lib/emb/machine" ) var ( diff --git a/emb/machine/_usb/msc/msc.go b/emb/machine/usb/msc/msc.go similarity index 97% rename from emb/machine/_usb/msc/msc.go rename to emb/machine/usb/msc/msc.go index d3bf8d6..f894892 100644 --- a/emb/machine/_usb/msc/msc.go +++ b/emb/machine/usb/msc/msc.go @@ -1,12 +1,13 @@ package msc import ( - "machine" - "machine/usb" - "machine/usb/descriptor" - "machine/usb/msc/csw" - "machine/usb/msc/scsi" "time" + + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/machine/usb/descriptor" + "github.com/goplus/lib/emb/machine/usb/msc/csw" + "github.com/goplus/lib/emb/machine/usb/msc/scsi" ) type mscState uint8 diff --git a/emb/machine/_usb/msc/scsi.go b/emb/machine/usb/msc/scsi.go similarity index 98% rename from emb/machine/_usb/msc/scsi.go rename to emb/machine/usb/msc/scsi.go index d7266ed..5f915ab 100644 --- a/emb/machine/_usb/msc/scsi.go +++ b/emb/machine/usb/msc/scsi.go @@ -2,9 +2,10 @@ package msc import ( "encoding/binary" - "machine/usb" - "machine/usb/msc/csw" - "machine/usb/msc/scsi" + + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/machine/usb/msc/csw" + "github.com/goplus/lib/emb/machine/usb/msc/scsi" ) func (m *msc) scsiCmdBegin() { diff --git a/emb/machine/_usb/msc/scsi/scsi.go b/emb/machine/usb/msc/scsi/scsi.go similarity index 100% rename from emb/machine/_usb/msc/scsi/scsi.go rename to emb/machine/usb/msc/scsi/scsi.go diff --git a/emb/machine/_usb/msc/scsi_inquiry.go b/emb/machine/usb/msc/scsi_inquiry.go similarity index 100% rename from emb/machine/_usb/msc/scsi_inquiry.go rename to emb/machine/usb/msc/scsi_inquiry.go diff --git a/emb/machine/_usb/msc/scsi_readwrite.go b/emb/machine/usb/msc/scsi_readwrite.go similarity index 100% rename from emb/machine/_usb/msc/scsi_readwrite.go rename to emb/machine/usb/msc/scsi_readwrite.go diff --git a/emb/machine/_usb/msc/scsi_unmap.go b/emb/machine/usb/msc/scsi_unmap.go similarity index 100% rename from emb/machine/_usb/msc/scsi_unmap.go rename to emb/machine/usb/msc/scsi_unmap.go diff --git a/emb/machine/_usb/msc/setup.go b/emb/machine/usb/msc/setup.go similarity index 97% rename from emb/machine/_usb/msc/setup.go rename to emb/machine/usb/msc/setup.go index 3d5bef2..52745a0 100644 --- a/emb/machine/_usb/msc/setup.go +++ b/emb/machine/usb/msc/setup.go @@ -1,9 +1,9 @@ package msc import ( - "machine" - "machine/usb" - "machine/usb/msc/csw" + "github.com/goplus/lib/emb/machine" + "github.com/goplus/lib/emb/machine/usb" + "github.com/goplus/lib/emb/machine/usb/msc/csw" ) func setupPacketHandler(setup usb.Setup) bool { diff --git a/emb/machine/_usb/usb.go b/emb/machine/usb/usb.go similarity index 100% rename from emb/machine/_usb/usb.go rename to emb/machine/usb/usb.go diff --git a/emb/runtime/interrupt/checkpoint.go b/emb/runtime/interrupt/checkpoint.go new file mode 100644 index 0000000..f02a2be --- /dev/null +++ b/emb/runtime/interrupt/checkpoint.go @@ -0,0 +1,62 @@ +package interrupt + +// A checkpoint is a setjmp like buffer, that can be used as a flag for +// interrupts. +// +// It can be used as follows: +// +// // global var +// var c Checkpoint +// +// // to set up the checkpoint and wait for it +// if c.Save() { +// setupInterrupt() +// for { +// waitForInterrupt() +// } +// } +// +// // Inside the interrupt handler: +// if c.Saved() { +// c.Jump() +// } +type Checkpoint struct { + jumpSP uintptr + jumpPC uintptr +} + +// Save the execution state in the given checkpoint, overwriting a previous +// saved checkpoint. +// +// This function returns twice: once the normal way after saving (returning +// true) and once after jumping (returning false). +// +// This function is a compiler intrinsic, it is not implemented in Go. +func (c *Checkpoint) Save() bool + +// Returns whether a jump point was saved (and not erased due to a jump). +func (c *Checkpoint) Saved() bool { + return c.jumpPC != 0 +} + +// Jump to the point where the execution state was saved, and erase the saved +// jump point. This must *only* be called from inside an interrupt. +// +// This method does not return in the conventional way, it resumes execution at +// the last point a checkpoint was saved. +func (c *Checkpoint) Jump() { + if !c.Saved() { + panic("runtime/interrupt: no checkpoint was saved") + } + jumpPC := c.jumpPC + jumpSP := c.jumpSP + c.jumpPC = 0 + c.jumpSP = 0 + if jumpPC == 0 { + panic("jumping to 0") + } + checkpointJump(jumpSP, jumpPC) +} + +//export tinygo_checkpointJump +func checkpointJump(jumpSP, jumpPC uintptr) diff --git a/emb/runtime/interrupt/interrupt.go b/emb/runtime/interrupt/interrupt.go new file mode 100644 index 0000000..e0376a5 --- /dev/null +++ b/emb/runtime/interrupt/interrupt.go @@ -0,0 +1,36 @@ +// Package interrupt provides access to hardware interrupts. It provides a way +// to define interrupts and to enable/disable them. +package interrupt + +import "unsafe" + +// Interrupt provides direct access to hardware interrupts. You can configure +// this interrupt through this interface. +// +// Do not use the zero value of an Interrupt object. Instead, call New to obtain +// an interrupt handle. +type Interrupt struct { + // Make this number unexported so it cannot be set directly. This provides + // some encapsulation. + num int +} + +// New is a compiler intrinsic that creates a new Interrupt object. You may call +// it only once, and must pass constant parameters to it. That means that the +// interrupt ID must be a Go constant and that the handler must be a simple +// function: closures are not supported. +func New(id int, handler func(Interrupt)) Interrupt + +// handle is used internally, between IR generation and interrupt lowering. The +// frontend will create runtime/interrupt.handle objects, cast them to an int, +// and use that in an Interrupt object. That way the compiler will be able to +// optimize away all interrupt handles that are never used in a program. +// This system only works when interrupts need to be enabled before use and this +// is done only through calling Enable() on this object. If interrupts cannot +// individually be enabled/disabled, the compiler should create a pseudo-call +// (like runtime/interrupt.use()) that keeps the interrupt alive. +type handle struct { + context unsafe.Pointer + funcPtr uintptr + Interrupt +} diff --git a/emb/runtime/interrupt/interrupt_avr.go b/emb/runtime/interrupt/interrupt_avr.go new file mode 100644 index 0000000..f27da8a --- /dev/null +++ b/emb/runtime/interrupt/interrupt_avr.go @@ -0,0 +1,45 @@ +//go:build avr + +package interrupt + +import "device" + +// State represents the previous global interrupt state. +type State uint8 + +// Disable disables all interrupts and returns the previous interrupt state. It +// can be used in a critical section like this: +// +// state := interrupt.Disable() +// // critical section +// interrupt.Restore(state) +// +// Critical sections can be nested. Make sure to call Restore in the same order +// as you called Disable (this happens naturally with the pattern above). +func Disable() (state State) { + // SREG is at I/O address 0x3f. + return State(device.AsmFull(` + in {}, 0x3f + cli + `, nil)) +} + +// Restore restores interrupts to what they were before. Give the previous state +// returned by Disable as a parameter. If interrupts were disabled before +// calling Disable, this will not re-enable interrupts, allowing for nested +// critical sections. +func Restore(state State) { + // SREG is at I/O address 0x3f. + device.AsmFull("out 0x3f, {state}", map[string]interface{}{ + "state": state, + }) +} + +// In returns whether the system is currently in an interrupt. +// +// Warning: this always returns false on AVR, as there does not appear to be a +// reliable way to determine whether we're currently running inside an interrupt +// handler. +func In() bool { + return false +} diff --git a/emb/runtime/interrupt/interrupt_cortexm.go b/emb/runtime/interrupt/interrupt_cortexm.go new file mode 100644 index 0000000..2b44106 --- /dev/null +++ b/emb/runtime/interrupt/interrupt_cortexm.go @@ -0,0 +1,65 @@ +//go:build cortexm + +package interrupt + +import ( + "device/arm" +) + +// Enable enables this interrupt. Right after calling this function, the +// interrupt may be invoked if it was already pending. +func (irq Interrupt) Enable() { + // Clear the ARM pending bit, an asserting device may still + // trigger the interrupt once enabled. + arm.ClearPendingIRQ(uint32(irq.num)) + arm.EnableIRQ(uint32(irq.num)) +} + +// Disable disables this interrupt. +func (irq Interrupt) Disable() { + arm.DisableIRQ(uint32(irq.num)) +} + +// SetPriority sets the interrupt priority for this interrupt. A lower number +// means a higher priority. Additionally, most hardware doesn't implement all +// priority bits (only the uppoer bits). +// +// Examples: 0xff (lowest priority), 0xc0 (low priority), 0x00 (highest possible +// priority). +func (irq Interrupt) SetPriority(priority uint8) { + arm.SetPriority(uint32(irq.num), uint32(priority)) +} + +// State represents the previous global interrupt state. +type State uintptr + +// Disable disables all interrupts and returns the previous interrupt state. It +// can be used in a critical section like this: +// +// state := interrupt.Disable() +// // critical section +// interrupt.Restore(state) +// +// Critical sections can be nested. Make sure to call Restore in the same order +// as you called Disable (this happens naturally with the pattern above). +func Disable() (state State) { + return State(arm.DisableInterrupts()) +} + +// Restore restores interrupts to what they were before. Give the previous state +// returned by Disable as a parameter. If interrupts were disabled before +// calling Disable, this will not re-enable interrupts, allowing for nested +// critical sections. +func Restore(state State) { + arm.EnableInterrupts(uintptr(state)) +} + +// In returns whether the system is currently in an interrupt. +func In() bool { + // The VECTACTIVE field gives the instruction vector that is currently + // active (in handler mode), or 0 if not in an interrupt. + // Documentation: + // https://developer.arm.com/documentation/dui0497/a/cortex-m0-peripherals/system-control-block/interrupt-control-and-state-register + vectactive := uint8(arm.SCB.ICSR.Get()) + return vectactive != 0 +} diff --git a/emb/runtime/interrupt/interrupt_esp32c3.go b/emb/runtime/interrupt/interrupt_esp32c3.go new file mode 100644 index 0000000..5725cd3 --- /dev/null +++ b/emb/runtime/interrupt/interrupt_esp32c3.go @@ -0,0 +1,236 @@ +//go:build esp32c3 + +package interrupt + +import ( + "device/esp" + "device/riscv" + "errors" + "runtime/volatile" + "unsafe" +) + +// Enable register CPU interrupt with interrupt.Interrupt. +// The ESP32-C3 has 31 CPU independent interrupts. +// The Interrupt.New(x, f) (x = [1..31]) attaches CPU interrupt to function f. +// Caller must map the selected interrupt using following sequence (for example using id 5): +// +// // map interrupt 5 to my XXXX module +// esp.INTERRUPT_CORE0.XXXX_INTERRUPT_PRO_MAP.Set( 5 ) +// _ = Interrupt.New(5, func(interrupt.Interrupt) { +// ... +// }).Enable() +func (i Interrupt) Enable() error { + if i.num < 1 && i.num > 31 { + return errors.New("interrupt for ESP32-C3 must be in range of 1 through 31") + } + mask := riscv.DisableInterrupts() + defer riscv.EnableInterrupts(mask) + + // enable CPU interrupt number i.num + esp.INTERRUPT_CORE0.CPU_INT_ENABLE.SetBits(1 << i.num) + + // Set pulse interrupt type (rising edge detection) + esp.INTERRUPT_CORE0.CPU_INT_TYPE.SetBits(1 << i.num) + + // Set default threshold to defaultThreshold + reg := (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0), i.num*4)) + reg.Set(defaultThreshold) + + // Reset interrupt before reenabling + esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(1 << i.num) + esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(1 << i.num) + + // we must wait for any pending write operations to complete + riscv.Asm("fence") + return nil +} + +// Adding pseudo function calls that is replaced by the compiler with the actual +// functions registered through interrupt.New. +// +//go:linkname callHandlers runtime/interrupt.callHandlers +func callHandlers(num int) + +const ( + IRQNUM_1 = 1 + iota + IRQNUM_2 + IRQNUM_3 + IRQNUM_4 + IRQNUM_5 + IRQNUM_6 + IRQNUM_7 + IRQNUM_8 + IRQNUM_9 + IRQNUM_10 + IRQNUM_11 + IRQNUM_12 + IRQNUM_13 + IRQNUM_14 + IRQNUM_15 + IRQNUM_16 + IRQNUM_17 + IRQNUM_18 + IRQNUM_19 + IRQNUM_20 + IRQNUM_21 + IRQNUM_22 + IRQNUM_23 + IRQNUM_24 + IRQNUM_25 + IRQNUM_26 + IRQNUM_27 + IRQNUM_28 + IRQNUM_29 + IRQNUM_30 + IRQNUM_31 +) + +const ( + defaultThreshold = 5 + disableThreshold = 10 +) + +//go:inline +func callHandler(n int) { + switch n { + case IRQNUM_1: + callHandlers(IRQNUM_1) + case IRQNUM_2: + callHandlers(IRQNUM_2) + case IRQNUM_3: + callHandlers(IRQNUM_3) + case IRQNUM_4: + callHandlers(IRQNUM_4) + case IRQNUM_5: + callHandlers(IRQNUM_5) + case IRQNUM_6: + callHandlers(IRQNUM_6) + case IRQNUM_7: + callHandlers(IRQNUM_7) + case IRQNUM_8: + callHandlers(IRQNUM_8) + case IRQNUM_9: + callHandlers(IRQNUM_9) + case IRQNUM_10: + callHandlers(IRQNUM_10) + case IRQNUM_11: + callHandlers(IRQNUM_11) + case IRQNUM_12: + callHandlers(IRQNUM_12) + case IRQNUM_13: + callHandlers(IRQNUM_13) + case IRQNUM_14: + callHandlers(IRQNUM_14) + case IRQNUM_15: + callHandlers(IRQNUM_15) + case IRQNUM_16: + callHandlers(IRQNUM_16) + case IRQNUM_17: + callHandlers(IRQNUM_17) + case IRQNUM_18: + callHandlers(IRQNUM_18) + case IRQNUM_19: + callHandlers(IRQNUM_19) + case IRQNUM_20: + callHandlers(IRQNUM_20) + case IRQNUM_21: + callHandlers(IRQNUM_21) + case IRQNUM_22: + callHandlers(IRQNUM_22) + case IRQNUM_23: + callHandlers(IRQNUM_23) + case IRQNUM_24: + callHandlers(IRQNUM_24) + case IRQNUM_25: + callHandlers(IRQNUM_25) + case IRQNUM_26: + callHandlers(IRQNUM_26) + case IRQNUM_27: + callHandlers(IRQNUM_27) + case IRQNUM_28: + callHandlers(IRQNUM_28) + case IRQNUM_29: + callHandlers(IRQNUM_29) + case IRQNUM_30: + callHandlers(IRQNUM_30) + case IRQNUM_31: + callHandlers(IRQNUM_31) + } +} + +//export handleInterrupt +func handleInterrupt() { + mcause := riscv.MCAUSE.Get() + exception := mcause&(1<<31) == 0 + interruptNumber := uint32(mcause & 0x1f) + + if !exception && interruptNumber > 0 { + // save MSTATUS & MEPC, which could be overwritten by another CPU interrupt + mstatus := riscv.MSTATUS.Get() + mepc := riscv.MEPC.Get() + // Using threshold to temporary disable this interrupts. + // FYI: using CPU interrupt enable bit make runtime to loose interrupts. + reg := (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.INTERRUPT_CORE0.CPU_INT_PRI_0), interruptNumber*4)) + thresholdSave := reg.Get() + reg.Set(disableThreshold) + riscv.Asm("fence") + + interruptBit := uint32(1 << interruptNumber) + + // reset pending status interrupt + if esp.INTERRUPT_CORE0.CPU_INT_TYPE.Get()&interruptBit != 0 { + // this is edge type interrupt + esp.INTERRUPT_CORE0.CPU_INT_CLEAR.SetBits(interruptBit) + esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(interruptBit) + } else { + // this is level type interrupt + esp.INTERRUPT_CORE0.CPU_INT_CLEAR.ClearBits(interruptBit) + } + + // enable CPU interrupts + riscv.MSTATUS.SetBits(riscv.MSTATUS_MIE) + + // Call registered interrupt handler(s) + callHandler(int(interruptNumber)) + + // disable CPU interrupts + riscv.MSTATUS.ClearBits(riscv.MSTATUS_MIE) + + // restore interrupt threshold to enable interrupt again + reg.Set(thresholdSave) + riscv.Asm("fence") + + // restore MSTATUS & MEPC + riscv.MSTATUS.Set(mstatus) + riscv.MEPC.Set(mepc) + + // do not enable CPU interrupts now + // the 'MRET' in src/device/riscv/handleinterrupt.S will copies the state of MPIE back into MIE, and subsequently clears MPIE. + // riscv.MSTATUS.SetBits(riscv.MSTATUS_MIE) + } else { + // Topmost bit is clear, so it is an exception of some sort. + // We could implement support for unsupported instructions here (such as + // misaligned loads). However, for now we'll just print a fatal error. + handleException(mcause) + } +} + +func handleException(mcause uintptr) { + println("*** Exception: pc:", riscv.MEPC.Get()) + println("*** Exception: code:", uint32(mcause&0x1f)) + println("*** Exception: mcause:", mcause) + switch uint32(mcause & 0x1f) { + case riscv.InstructionAccessFault: + println("*** virtual address:", riscv.MTVAL.Get()) + case riscv.IllegalInstruction: + println("*** opcode:", riscv.MTVAL.Get()) + case riscv.LoadAccessFault: + println("*** read address:", riscv.MTVAL.Get()) + case riscv.StoreOrAMOAccessFault: + println("*** write address:", riscv.MTVAL.Get()) + } + for { + riscv.Asm("wfi") + } +} diff --git a/emb/runtime/interrupt/interrupt_gameboyadvance.go b/emb/runtime/interrupt/interrupt_gameboyadvance.go new file mode 100644 index 0000000..2224066 --- /dev/null +++ b/emb/runtime/interrupt/interrupt_gameboyadvance.go @@ -0,0 +1,104 @@ +//go:build gameboyadvance + +package interrupt + +// This is good documentation of the GBA: https://www.akkit.org/info/gbatek.htm + +import ( + "device/gba" +) + +// Enable enables this interrupt. Right after calling this function, the +// interrupt may be invoked if it was already pending. +func (irq Interrupt) Enable() { + gba.INTERRUPT.IE.SetBits(1 << uint(irq.num)) +} + +var inInterrupt bool + +//export handleInterrupt +func handleInterrupt() { + inInterrupt = true + flags := gba.INTERRUPT.IF.Get() + for i := 0; i < 14; i++ { + if flags&(1<