Módulo Bytes
El módulo Bytes crea bloques de memoria de tamaño fijo, que se leen y se escriben byte a byte o en grupos de 16 y 32 bits. Sirven para los datos binarios que otros módulos esperan así: los píxeles de draw.bytes, las muestras de Sound.loadSample y la música de Sound.loadMusic, o una trama para mandar a un aparato.
use Bytes
Funciona igual en Windows y en todas las placas. Los valores de 16 y 32 bits se guardan con el byte bajo primero (little endian), que es como los guardan el PC y los chips ESP32.
Funciones
| Función | Qué hace | Devuelve |
|---|---|---|
Bytes.new(n) | Crea un bloque de n bytes | El bloque |
Bytes.clone(bloque) | Copia un bloque | Un bloque nuevo, con el mismo contenido |
new(n)
Crea un bloque de n bytes. n tiene que ser un entero; con 0, el bloque no tiene ningún byte.
El contenido de un bloque nuevo no está a cero: puede traer cualquier cosa. Llama a fill(0) antes de usarlo.
| Error | Cuándo |
|---|---|
ERR_TYPE_MISMATCH | n no es un entero. |
ERR_INVALID_VALUE | n es negativo. |
ERR_INVALID_ARG_NUMBER | No le pasas un argumento. |
use Bytes
var bloque = Bytes.new(64) // 64 bytes
bloque.fill(0) // a cero
println(bloque.size()) // 64
Si no hay memoria para tantos bytes, la VM se cierra (en Windows, con el mensaje ReallocAlloc failed), y try no lo evita. Comprueba el tamaño antes de crear el bloque.
clone(bloque)
Devuelve un bloque nuevo, del mismo tamaño y con el mismo contenido que bloque. Cambiar uno no cambia el otro. Si bloque no es un bloque de Bytes, lanza ERR_TYPE_MISMATCH.
use Bytes
var original = Bytes.new(4)
original.write(0, 0xAB)
var copia = Bytes.clone(original)
copia.write(0, 0) // no cambia el original
println(original.read(0)) // 171
println(copia.read(0)) // 0
Objetos que devuelve
Bytes.new y Bytes.clone devuelven un bloque, con estos métodos:
| Método | Qué hace | Devuelve |
|---|---|---|
fill(v) | Pone todos los bytes a v | nil |
size() | Cuántos bytes tiene el bloque | Un entero |
read(i) | Lee el byte i | De 0 a 255 |
write(i, v) | Escribe el byte i | nil |
read16(i) | Lee el valor de 16 bits número i | De 0 a 65535 |
write16(i, v) | Escribe el valor de 16 bits número i | nil |
read32(i) | Lee el valor de 32 bits número i | Un entero con signo |
write32(i, v) | Escribe el valor de 32 bits número i | nil |
El índice cuenta en unidades del tamaño que lees o escribes, no en bytes:
| Métodos | Unidad del índice | Bytes que toca el índice i | Índices de un bloque de n bytes |
|---|---|---|---|
read y write | 1 byte | i | De 0 a n - 1 |
read16 y write16 | 2 bytes | 2i y 2i + 1 | De 0 a n div 2 - 1 |
read32 y write32 | 4 bytes | De 4i a 4i + 3 | De 0 a n div 4 - 1 |
Así, read16(1) lee los bytes 2 y 3, no el byte 1. En un bloque de tamaño impar, el último byte solo se alcanza con read y write.
Errores de todos los métodos de lectura y escritura (excepciones, que se capturan con try):
ERR_INDEX_OUT_OF_RANGEsi el índice se sale del bloque (también si es negativo).ERR_TYPE_MISMATCHsi el índice no es un entero.ERR_INVALID_ARG_NUMBERsi no les pasas los argumentos que piden.
fill(v)
Pone todos los bytes del bloque a v. v tiene que ser un entero (si no, ERR_TYPE_MISMATCH), y se queda su byte bajo: fill(300) pone 44 en cada byte.
use Bytes
var bloque = Bytes.new(8)
bloque.fill(0xFF) // todos a 255
bloque.fill(0) // todos a 0
size()
Devuelve cuántos bytes tiene el bloque.
read(i)
Devuelve el byte i, de 0 a 255.
use Bytes
var bloque = Bytes.new(4)
bloque.write(2, 0x42)
println(bloque.read(2)) // 66
write(i, v)
Escribe v en el byte i. Se queda el byte bajo de v: 300 se guarda como 44, y -1, como 255.
v no se comprueba: tiene que ser un entero. Un decimal, true, nil o una cadena no dan error y escriben un byte sin sentido. Ojo con /, que siempre da un decimal: usa div o int() (ver «Trampas y problemas conocidos»).
use Bytes
var bloque = Bytes.new(4)
bloque.write(0, 0xDE)
bloque.write(1, 0xAD)
bloque.write(2, 300)
println(bloque.read(2)) // 44
read16(i)
Devuelve el valor de 16 bits que forman los bytes 2i (el bajo) y 2i + 1 (el alto), de 0 a 65535.
use Bytes
var bloque = Bytes.new(4)
bloque.write16(0, 0x1234)
println(bloque.read16(0)) // 4660 (0x1234)
println(bloque.read(0)) // 52 (0x34): el byte bajo va primero
println(bloque.read(1)) // 18 (0x12)
write16(i, v)
Escribe v en los bytes 2i y 2i + 1, el byte bajo primero. Se quedan los 16 bits bajos: 70000 se guarda como 4464. Como en write, v tiene que ser un entero.
read32(i)
Devuelve el valor de 32 bits que forman los bytes de 4i a 4i + 3, el bajo primero.
Los enteros de JARU tienen signo: los valores de 0x80000000 en adelante salen negativos (0xDEADBEEF se lee como -559038737). toUnsigned() no lo cambia; para verlo en hexadecimal, usa toHex().
use Bytes
var bloque = Bytes.new(8)
bloque.write32(0, 0xDEADBEEF)
bloque.write32(1, 0x12345678)
println(bloque.read32(0)) // -559038737
println(bloque.read32(0).toHex()) // DEADBEEF
println(bloque.read32(1)) // 305419896
write32(i, v)
Escribe v en los bytes de 4i a 4i + 3, el bajo primero. Como en write, v tiene que ser un entero.
Un entero de JARU llega hasta 2147483647. Los valores de 32 bits más altos, escríbelos en hexadecimal: 0xFFFFFFFF es un entero (vale -1) y se guarda bien. Escrito en decimal, 4294967295 es un decimal, y write32 guarda un valor sin sentido sin avisar.
use Bytes
var bloque = Bytes.new(16)
bloque.write32(0, 0x00FF00FF)
bloque.write32(1, 0xFFFFFFFF)
println(bloque.read(4)) // 255
Ejemplo completo
Una trama para mandar a un aparato: una cabecera de 16 bits, la longitud, los datos y una suma de control de un byte.
use Bytes
// Trama: cabecera (2 bytes), longitud (1), datos y suma de control (1)
var datos = [0x2A, 0x7F, 0x10]
var trama = Bytes.new(2 + 1 + datos.size() + 1)
trama.fill(0)
trama.write16(0, 0xAA55) // bytes 0 y 1
trama.write(2, datos.size()) // byte 2
for (var i = 0; i < datos.size(); i++)
trama.write(3 + i, datos[i])
end
// La suma de control: el byte bajo de la suma de todos los anteriores
var suma = 0
for (var i = 0; i < trama.size() - 1; i++)
suma = suma + trama.read(i)
end
trama.write(trama.size() - 1, suma % 256)
for (var i = 0; i < trama.size(); i++)
print(trama.read(i).toHex(), " ")
end
println("")
println(trama.read16(0).toHex()) // la cabecera, otra vez en 16 bits
55 AA 3 2A 7F 10 BB
AA55
La cabecera 0xAA55 sale como 55 AA: el byte bajo va primero.
Una imagen para draw.bytes
draw.bytes pinta un bloque como una imagen: un byte por píxel, con el número de su color en una paleta. La paleta es un array {…} de colores 0xRRGGBB; con una lista […] no pinta nada (ver Draw). Este programa pinta un degradado del negro al rojo durante dos segundos:
use Bytes, Display
const ANCHO = 160
const ALTO = 120
Display.open(ANCHO, ALTO)
var draw = Display.draw
Display.fps = 30
// La imagen: un byte por píxel, con el número de su color en la paleta
var imagen = Bytes.new(ANCHO * ALTO)
for (var y = 0; y < ALTO; y++)
for (var x = 0; x < ANCHO; x++)
imagen.write(y * ANCHO + x, (x * 255) div (ANCHO - 1))
end
end
// La paleta: 256 colores, del negro al rojo
var paleta[256]
for (var i = 0; i < 256; i++)
paleta[i] = i << 16
end
var fotogramas = 0
while (fotogramas < 60)
draw.bytes(0, 0, ANCHO, ALTO, imagen, paleta)
Display.update()
fotogramas++
end
Display.close()
Trampas y problemas conocidos
- Un bloque nuevo no está a cero. Llama a
fill(0)antes de usarlo. writeno comprueba el valor. Un decimal se escribe como un byte sin sentido, sin error./da siempre un decimal (10 / 4es2.5), así quebloque.write(0, total / 4)no guarda lo que esperas: usatotal div 4oint(total / 4).read32tiene signo. Los valores de0x80000000en adelante salen negativos.- Los valores de 32 bits altos, en hexadecimal.
4294967295escrito en decimal no es un entero: escribe0xFFFFFFFF. - Índices en unidades.
read16(1)yread32(1)no empiezan en el byte 1, sino en el 2 y en el 4.
Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.