working wasm
This commit is contained in:
@@ -9,7 +9,7 @@ QEMU_WASM_SRC ?= _scratch/qemu-wasm-src
|
||||
WASM_OUT ?= dist/wasm
|
||||
VARIANT ?= x3
|
||||
|
||||
.PHONY: firmware sdimage qemu qemu-wasm web web-test run run-upstream chip clean
|
||||
.PHONY: firmware sdimage blank-sd-asset qemu qemu-wasm web web-test run run-upstream chip clean
|
||||
|
||||
firmware:
|
||||
esptool --chip esp32c3 merge-bin -o flash.bin \
|
||||
@@ -23,11 +23,17 @@ firmware:
|
||||
sdimage:
|
||||
scripts/mksd.sh $(SD_SRC) sd.img
|
||||
|
||||
blank-sd-asset:
|
||||
@tmpdir=$$(mktemp -d); trap 'rm -rf "$$tmpdir"' EXIT; \
|
||||
mkdir "$$tmpdir/empty"; \
|
||||
SIZE_MB=64 scripts/mksd.sh "$$tmpdir/empty" "$$tmpdir/sd.img"; \
|
||||
gzip -n -9 -c "$$tmpdir/sd.img" > web/assets/blank-sd.img.gz
|
||||
|
||||
qemu:
|
||||
QEMU_SRC=$(QEMU_SRC) scripts/build-qemu.sh
|
||||
|
||||
qemu-wasm:
|
||||
QEMU_WASM_SRC=$(QEMU_WASM_SRC) WASM_OUT=$(WASM_OUT) JOBS=2 scripts/build-qemu-wasm.sh
|
||||
QEMU_WASM_SRC=$(QEMU_WASM_SRC) WASM_OUT=$(WASM_OUT) scripts/build-qemu-wasm.sh
|
||||
|
||||
# Serve over HTTPS by default so headless/LAN access is cross-origin isolated. Override with WEB_TLS=.
|
||||
WEB_TLS ?= --tls
|
||||
@@ -39,9 +45,16 @@ web-test:
|
||||
|
||||
# Select X4 with `make run VARIANT=x4`. Add `-display none` for headless.
|
||||
run: qemu
|
||||
$(QEMU_BIN) -machine xteink,variant=$(VARIANT) -L $(QEMU_SRC)/pc-bios \
|
||||
-serial stdio -drive file=flash.bin,if=mtd,format=raw \
|
||||
$(if $(wildcard sd.img),-drive file=sd.img,if=sd,format=raw)
|
||||
@sd=sd.img; tmpdir=; \
|
||||
if [ ! -f "$$sd" ]; then \
|
||||
tmpdir=$$(mktemp -d); mkdir "$$tmpdir/empty"; sd="$$tmpdir/sd.img"; \
|
||||
SIZE_MB=64 scripts/mksd.sh "$$tmpdir/empty" "$$sd" >/dev/null; \
|
||||
echo "Using a blank ephemeral SD card; writes will be discarded when QEMU exits."; \
|
||||
fi; \
|
||||
trap 'rm -rf "$$tmpdir"' EXIT; \
|
||||
$(QEMU_BIN) -machine xteink,variant=$(VARIANT) -L $(QEMU_SRC)/pc-bios \
|
||||
-serial stdio -drive file=flash.bin,if=mtd,format=raw \
|
||||
-drive file="$$sd",if=sd,format=raw
|
||||
|
||||
# Unpatched release binary retained as the fast generic ESP32-C3 baseline.
|
||||
run-upstream:
|
||||
|
||||
+251
-153
@@ -1,7 +1,7 @@
|
||||
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c
|
||||
diff --git c/accel/tcg/cputlb.c w/accel/tcg/cputlb.c
|
||||
index b76a4eac4e..a673eb2eb7 100644
|
||||
--- a/accel/tcg/cputlb.c
|
||||
+++ b/accel/tcg/cputlb.c
|
||||
--- c/accel/tcg/cputlb.c
|
||||
+++ w/accel/tcg/cputlb.c
|
||||
@@ -225,7 +225,7 @@ static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast,
|
||||
rate = desc->window_max_entries * 100 / old_size;
|
||||
|
||||
@@ -11,10 +11,10 @@ index b76a4eac4e..a673eb2eb7 100644
|
||||
} else if (rate < 30 && window_expired) {
|
||||
size_t ceil = pow2ceil(desc->window_max_entries);
|
||||
size_t expected_rate = desc->window_max_entries * 100 / ceil;
|
||||
diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc
|
||||
diff --git c/accel/tcg/ldst_common.c.inc w/accel/tcg/ldst_common.c.inc
|
||||
index ebbf380d76..3ff0b97f27 100644
|
||||
--- a/accel/tcg/ldst_common.c.inc
|
||||
+++ b/accel/tcg/ldst_common.c.inc
|
||||
--- c/accel/tcg/ldst_common.c.inc
|
||||
+++ w/accel/tcg/ldst_common.c.inc
|
||||
@@ -12,32 +12,52 @@
|
||||
* Load helpers for tcg-ldst.h
|
||||
*/
|
||||
@@ -143,11 +143,41 @@ index ebbf380d76..3ff0b97f27 100644
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c
|
||||
index cc0f5afd47..575be0e359 100644
|
||||
--- a/accel/tcg/tb-maint.c
|
||||
+++ b/accel/tcg/tb-maint.c
|
||||
@@ -854,7 +854,11 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
|
||||
diff --git c/accel/tcg/tb-maint.c w/accel/tcg/tb-maint.c
|
||||
index cc0f5afd47..1095dfd912 100644
|
||||
--- c/accel/tcg/tb-maint.c
|
||||
+++ w/accel/tcg/tb-maint.c
|
||||
@@ -755,6 +755,18 @@ static void tb_remove(TranslationBlock *tb)
|
||||
}
|
||||
#endif /* CONFIG_USER_ONLY */
|
||||
|
||||
+#if !defined(CONFIG_TCG_INTERPRETER) && defined(EMSCRIPTEN)
|
||||
+#include "../../tcg/wasm32.h"
|
||||
+
|
||||
+static void wasm_remove_tb_instance(void *p, uint32_t hash, void *userp)
|
||||
+{
|
||||
+ TranslationBlock *tb = p;
|
||||
+ (void)hash;
|
||||
+ (void)userp;
|
||||
+ remove_tb(tb->tc.ptr);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
/* flush all the translation blocks */
|
||||
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||
{
|
||||
@@ -771,6 +783,10 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
|
||||
tcg_flush_jmp_cache(cpu);
|
||||
}
|
||||
|
||||
+#if defined(EMSCRIPTEN) && !defined(CONFIG_TCG_INTERPRETER)
|
||||
+ qht_iter(&tb_ctx.htable, wasm_remove_tb_instance, NULL);
|
||||
+ flush_tb_instances();
|
||||
+#endif
|
||||
qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE);
|
||||
tb_remove_all();
|
||||
|
||||
@@ -854,7 +870,11 @@ static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig)
|
||||
void tb_reset_jump(TranslationBlock *tb, int n)
|
||||
{
|
||||
uintptr_t addr = (uintptr_t)(tb->tc.ptr + tb->jmp_reset_offset[n]);
|
||||
@@ -159,15 +189,7 @@ index cc0f5afd47..575be0e359 100644
|
||||
}
|
||||
|
||||
/* remove any jumps to the TB */
|
||||
@@ -875,10 +879,18 @@ static inline void tb_jmp_unlink(TranslationBlock *dest)
|
||||
qemu_spin_unlock(&dest->jmp_lock);
|
||||
}
|
||||
|
||||
+#if !defined(CONFIG_TCG_INTERPRETER) && defined(EMSCRIPTEN)
|
||||
+#include "../../tcg/wasm32.h"
|
||||
+#endif
|
||||
+
|
||||
static void tb_jmp_cache_inval_tb(TranslationBlock *tb)
|
||||
@@ -879,6 +899,10 @@ static void tb_jmp_cache_inval_tb(TranslationBlock *tb)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
@@ -178,10 +200,10 @@ index cc0f5afd47..575be0e359 100644
|
||||
if (tb_cflags(tb) & CF_PCREL) {
|
||||
/* A TB may be at any virtual address */
|
||||
CPU_FOREACH(cpu) {
|
||||
diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c
|
||||
diff --git c/accel/tcg/tcg-accel-ops-mttcg.c w/accel/tcg/tcg-accel-ops-mttcg.c
|
||||
index 49814ec4af..0715610d37 100644
|
||||
--- a/accel/tcg/tcg-accel-ops-mttcg.c
|
||||
+++ b/accel/tcg/tcg-accel-ops-mttcg.c
|
||||
--- c/accel/tcg/tcg-accel-ops-mttcg.c
|
||||
+++ w/accel/tcg/tcg-accel-ops-mttcg.c
|
||||
@@ -61,12 +61,18 @@ static void mttcg_force_rcu(Notifier *notify, void *data)
|
||||
* variable current_cpu can be used deep in the code to find the
|
||||
* current CPUState for a given thread.
|
||||
@@ -202,10 +224,10 @@ index 49814ec4af..0715610d37 100644
|
||||
assert(tcg_enabled());
|
||||
g_assert(!icount_enabled());
|
||||
|
||||
diff --git a/backends/meson.build b/backends/meson.build
|
||||
diff --git c/backends/meson.build w/backends/meson.build
|
||||
index da714b93d1..9b88d22685 100644
|
||||
--- a/backends/meson.build
|
||||
+++ b/backends/meson.build
|
||||
--- c/backends/meson.build
|
||||
+++ w/backends/meson.build
|
||||
@@ -12,8 +12,10 @@ system_ss.add([files(
|
||||
|
||||
if host_os != 'windows'
|
||||
@@ -219,10 +241,10 @@ index da714b93d1..9b88d22685 100644
|
||||
endif
|
||||
if host_os == 'linux'
|
||||
system_ss.add(files('hostmem-memfd.c'))
|
||||
diff --git a/block/file-posix.c b/block/file-posix.c
|
||||
diff --git c/block/file-posix.c w/block/file-posix.c
|
||||
index 90fa54352c..0fa9cc31b6 100644
|
||||
--- a/block/file-posix.c
|
||||
+++ b/block/file-posix.c
|
||||
--- c/block/file-posix.c
|
||||
+++ w/block/file-posix.c
|
||||
@@ -110,6 +110,10 @@
|
||||
#include <sys/diskslice.h>
|
||||
#endif
|
||||
@@ -256,17 +278,17 @@ index 90fa54352c..0fa9cc31b6 100644
|
||||
|
||||
/*
|
||||
* parse_zone - Fill a zone descriptor
|
||||
diff --git a/configs/devices/riscv32-softmmu/xteink.mak b/configs/devices/riscv32-softmmu/xteink.mak
|
||||
diff --git c/configs/devices/riscv32-softmmu/xteink.mak w/configs/devices/riscv32-softmmu/xteink.mak
|
||||
new file mode 100644
|
||||
index 0000000000..a9179b4119
|
||||
--- /dev/null
|
||||
+++ b/configs/devices/riscv32-softmmu/xteink.mak
|
||||
+++ w/configs/devices/riscv32-softmmu/xteink.mak
|
||||
@@ -0,0 +1 @@
|
||||
+CONFIG_RISCV_ESP32C3=y
|
||||
diff --git a/configure b/configure
|
||||
diff --git c/configure w/configure
|
||||
index 18336376bf..4a4b9e7754 100755
|
||||
--- a/configure
|
||||
+++ b/configure
|
||||
--- c/configure
|
||||
+++ w/configure
|
||||
@@ -340,7 +340,9 @@ int main(void) { return 0; }
|
||||
EOF
|
||||
}
|
||||
@@ -298,10 +320,10 @@ index 18336376bf..4a4b9e7754 100755
|
||||
! test -d "$source_path/linux-user/include/host/$host_arch" ||
|
||||
! test -d "$source_path/common-user/host/$host_arch"; }; then
|
||||
error_exit "linux-user/include/host/$host_arch does not exist." \
|
||||
diff --git a/disas/disas-host.c b/disas/disas-host.c
|
||||
diff --git c/disas/disas-host.c w/disas/disas-host.c
|
||||
index 8146fafe80..2ee0605dba 100644
|
||||
--- a/disas/disas-host.c
|
||||
+++ b/disas/disas-host.c
|
||||
--- c/disas/disas-host.c
|
||||
+++ w/disas/disas-host.c
|
||||
@@ -42,7 +42,7 @@ static void initialize_debug_host(CPUDebug *s)
|
||||
#else
|
||||
s->info.endian = BFD_ENDIAN_LITTLE;
|
||||
@@ -311,10 +333,10 @@ index 8146fafe80..2ee0605dba 100644
|
||||
s->info.print_insn = print_insn_tci;
|
||||
#elif defined(__i386__)
|
||||
s->info.mach = bfd_mach_i386_i386;
|
||||
diff --git a/hw/adc/esp32c3_adc.c b/hw/adc/esp32c3_adc.c
|
||||
diff --git c/hw/adc/esp32c3_adc.c w/hw/adc/esp32c3_adc.c
|
||||
index 89087429c7..4d38b3e13c 100644
|
||||
--- a/hw/adc/esp32c3_adc.c
|
||||
+++ b/hw/adc/esp32c3_adc.c
|
||||
--- c/hw/adc/esp32c3_adc.c
|
||||
+++ w/hw/adc/esp32c3_adc.c
|
||||
@@ -27,7 +27,7 @@ static uint64_t esp32c3_adc_read(void *opaque, hwaddr addr, unsigned size)
|
||||
}
|
||||
if (addr == ADC_DATA1 || addr == ADC_DATA2) {
|
||||
@@ -324,11 +346,30 @@ index 89087429c7..4d38b3e13c 100644
|
||||
: ADC_MAX;
|
||||
}
|
||||
return s->regs[addr / 4];
|
||||
diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
|
||||
index 46dbe7c521..4f05caf34e 100644
|
||||
--- a/hw/block/m25p80.c
|
||||
+++ b/hw/block/m25p80.c
|
||||
@@ -856,28 +856,18 @@ static void complete_collecting_data(Flash *s)
|
||||
diff --git c/hw/block/m25p80.c w/hw/block/m25p80.c
|
||||
index 46dbe7c521..1ee6f8ce7e 100644
|
||||
--- c/hw/block/m25p80.c
|
||||
+++ w/hw/block/m25p80.c
|
||||
@@ -222,7 +222,8 @@ static const FlashPartInfo known_devices[] = {
|
||||
{ INFO("is25lp016d", 0x9d6015, 0, 64 << 10, 32, ER_4K) },
|
||||
{ INFO("is25lp032", 0x9d6016, 0, 64 << 10, 64, ER_4K) },
|
||||
{ INFO("is25lp064", 0x9d6017, 0, 64 << 10, 128, ER_4K) },
|
||||
- { INFO("is25lp128", 0x9d6018, 0, 64 << 10, 256, ER_4K) },
|
||||
+ { INFO("is25lp128", 0x9d6018, 0, 64 << 10, 256, ER_4K),
|
||||
+ .sfdp_read = m25p80_sfdp_is25lp128 },
|
||||
{ INFO("is25lp256", 0x9d6019, 0, 64 << 10, 512, ER_4K) },
|
||||
{ INFO("is25wp032", 0x9d7016, 0, 64 << 10, 64, ER_4K) },
|
||||
{ INFO("is25wp064", 0x9d7017, 0, 64 << 10, 128, ER_4K) },
|
||||
@@ -385,6 +386,8 @@ typedef enum {
|
||||
READ_FSR = 0x70,
|
||||
RDCR = 0x15,
|
||||
RDSFDP = 0x5a,
|
||||
+ PROGRAM_ERASE_SUSPEND = 0x75,
|
||||
+ PROGRAM_ERASE_RESUME = 0x7a,
|
||||
|
||||
READ = 0x03,
|
||||
READ4 = 0x13,
|
||||
@@ -856,28 +859,18 @@ static void complete_collecting_data(Flash *s)
|
||||
break;
|
||||
case RDID_90:
|
||||
case RDID_AB:
|
||||
@@ -367,10 +408,49 @@ index 46dbe7c521..4f05caf34e 100644
|
||||
break;
|
||||
|
||||
case RDSFDP:
|
||||
diff --git a/hw/display/xteink_x3_eink.c b/hw/display/xteink_x3_eink.c
|
||||
@@ -1414,6 +1407,9 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
}
|
||||
break;
|
||||
case NOP:
|
||||
+ case PROGRAM_ERASE_SUSPEND:
|
||||
+ case PROGRAM_ERASE_RESUME:
|
||||
+ /* Program and erase complete synchronously in this model. */
|
||||
break;
|
||||
case EN_4BYTE_ADDR:
|
||||
s->four_bytes_address_mode = true;
|
||||
diff --git c/hw/block/m25p80_sfdp.c w/hw/block/m25p80_sfdp.c
|
||||
index a03a291a09..6e01178299 100644
|
||||
--- c/hw/block/m25p80_sfdp.c
|
||||
+++ w/hw/block/m25p80_sfdp.c
|
||||
@@ -479,3 +479,12 @@ static const uint8_t sfdp_is25wp256[] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
};
|
||||
define_sfdp_read(is25wp256);
|
||||
+
|
||||
+uint8_t m25p80_sfdp_is25lp128(uint32_t addr)
|
||||
+{
|
||||
+ /* The sibling parts share SFDP data except for the density field. */
|
||||
+ if (addr == 0x37) {
|
||||
+ return 0x07;
|
||||
+ }
|
||||
+ return m25p80_sfdp_is25wp256(addr);
|
||||
+}
|
||||
diff --git c/hw/block/m25p80_sfdp.h w/hw/block/m25p80_sfdp.h
|
||||
index 35785686a0..e6bfdfa7e8 100644
|
||||
--- c/hw/block/m25p80_sfdp.h
|
||||
+++ w/hw/block/m25p80_sfdp.h
|
||||
@@ -28,6 +28,7 @@ uint8_t m25p80_sfdp_w25q512jv(uint32_t addr);
|
||||
uint8_t m25p80_sfdp_w25q80bl(uint32_t addr);
|
||||
uint8_t m25p80_sfdp_w25q01jvq(uint32_t addr);
|
||||
|
||||
+uint8_t m25p80_sfdp_is25lp128(uint32_t addr);
|
||||
uint8_t m25p80_sfdp_is25wp256(uint32_t addr);
|
||||
|
||||
#endif
|
||||
diff --git c/hw/display/xteink_x3_eink.c w/hw/display/xteink_x3_eink.c
|
||||
index b699abae4e..8a177e9159 100644
|
||||
--- a/hw/display/xteink_x3_eink.c
|
||||
+++ b/hw/display/xteink_x3_eink.c
|
||||
--- c/hw/display/xteink_x3_eink.c
|
||||
+++ w/hw/display/xteink_x3_eink.c
|
||||
@@ -9,6 +9,60 @@
|
||||
#include "hw/irq.h"
|
||||
#include "hw/display/xteink_x3_eink.h"
|
||||
@@ -448,10 +528,10 @@ index b699abae4e..8a177e9159 100644
|
||||
dpy_gfx_update(s->console, 0, 0, 480, 800);
|
||||
}
|
||||
|
||||
diff --git a/hw/gpio/esp32_gpio.c b/hw/gpio/esp32_gpio.c
|
||||
diff --git c/hw/gpio/esp32_gpio.c w/hw/gpio/esp32_gpio.c
|
||||
index 9ec8bd9244..754990968c 100644
|
||||
--- a/hw/gpio/esp32_gpio.c
|
||||
+++ b/hw/gpio/esp32_gpio.c
|
||||
--- c/hw/gpio/esp32_gpio.c
|
||||
+++ w/hw/gpio/esp32_gpio.c
|
||||
@@ -38,10 +38,20 @@ static void esp32_gpio_drive_outputs(Esp32GpioState *s)
|
||||
}
|
||||
}
|
||||
@@ -493,10 +573,10 @@ index 9ec8bd9244..754990968c 100644
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
diff --git a/hw/misc/esp32c3_cache.c b/hw/misc/esp32c3_cache.c
|
||||
diff --git c/hw/misc/esp32c3_cache.c w/hw/misc/esp32c3_cache.c
|
||||
index 1879b5c2df..e3f09cc51b 100644
|
||||
--- a/hw/misc/esp32c3_cache.c
|
||||
+++ b/hw/misc/esp32c3_cache.c
|
||||
--- c/hw/misc/esp32c3_cache.c
|
||||
+++ w/hw/misc/esp32c3_cache.c
|
||||
@@ -58,7 +58,6 @@ static inline uint32_t esp32c3_read_mmu_value(ESP32C3CacheState *s, hwaddr reg_a
|
||||
|
||||
static inline void esp32c3_write_mmu_value(ESP32C3CacheState *s, hwaddr reg_addr, uint32_t value)
|
||||
@@ -533,10 +613,10 @@ index 1879b5c2df..e3f09cc51b 100644
|
||||
}
|
||||
|
||||
static void esp32c3_cache_init(Object *obj)
|
||||
diff --git a/hw/misc/esp_sha.c b/hw/misc/esp_sha.c
|
||||
diff --git c/hw/misc/esp_sha.c w/hw/misc/esp_sha.c
|
||||
index c9fa2e610a..cc186595eb 100644
|
||||
--- a/hw/misc/esp_sha.c
|
||||
+++ b/hw/misc/esp_sha.c
|
||||
--- c/hw/misc/esp_sha.c
|
||||
+++ w/hw/misc/esp_sha.c
|
||||
@@ -22,46 +22,71 @@
|
||||
#define SHA_WARNING 0
|
||||
#define SHA_DEBUG 0
|
||||
@@ -626,10 +706,10 @@ index c9fa2e610a..cc186595eb 100644
|
||||
.len = sizeof(struct sha512_state)
|
||||
},
|
||||
};
|
||||
diff --git a/hw/riscv/esp32c3.c b/hw/riscv/esp32c3.c
|
||||
diff --git c/hw/riscv/esp32c3.c w/hw/riscv/esp32c3.c
|
||||
index 2f1ccbfbb0..febc96bfe3 100644
|
||||
--- a/hw/riscv/esp32c3.c
|
||||
+++ b/hw/riscv/esp32c3.c
|
||||
--- c/hw/riscv/esp32c3.c
|
||||
+++ w/hw/riscv/esp32c3.c
|
||||
@@ -57,6 +57,10 @@
|
||||
#include "hw/sd/sd.h"
|
||||
#include "hw/net/can/esp32c3_twai.h"
|
||||
@@ -752,10 +832,10 @@ index 2f1ccbfbb0..febc96bfe3 100644
|
||||
|
||||
/* X3 exposes the fingerprint chips; X4 omits them so stock firmware's
|
||||
* all-NAK probe selects X4. The X4 panel is a blank protocol stub. */
|
||||
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
|
||||
diff --git c/include/exec/exec-all.h w/include/exec/exec-all.h
|
||||
index 2e4c4cc4b4..99e6da08c9 100644
|
||||
--- a/include/exec/exec-all.h
|
||||
+++ b/include/exec/exec-all.h
|
||||
--- c/include/exec/exec-all.h
|
||||
+++ w/include/exec/exec-all.h
|
||||
@@ -453,7 +453,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last);
|
||||
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr);
|
||||
|
||||
@@ -765,20 +845,20 @@ index 2e4c4cc4b4..99e6da08c9 100644
|
||||
extern __thread uintptr_t tci_tb_ptr;
|
||||
# define GETPC() tci_tb_ptr
|
||||
#else
|
||||
diff --git a/include/hw/gpio/esp32_gpio.h b/include/hw/gpio/esp32_gpio.h
|
||||
diff --git c/include/hw/gpio/esp32_gpio.h w/include/hw/gpio/esp32_gpio.h
|
||||
index e2f52ad702..721f5de5df 100644
|
||||
--- a/include/hw/gpio/esp32_gpio.h
|
||||
+++ b/include/hw/gpio/esp32_gpio.h
|
||||
--- c/include/hw/gpio/esp32_gpio.h
|
||||
+++ w/include/hw/gpio/esp32_gpio.h
|
||||
@@ -33,3 +33,5 @@ typedef struct Esp32GpioState {
|
||||
typedef struct Esp32GpioClass {
|
||||
SysBusDeviceClass parent_class;
|
||||
} Esp32GpioClass;
|
||||
+
|
||||
+void esp32_gpio_set_input_level(Esp32GpioState *s, int pin, bool level);
|
||||
diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h
|
||||
diff --git c/include/qemu/atomic.h w/include/qemu/atomic.h
|
||||
index 7a3f2e6576..50dc235043 100644
|
||||
--- a/include/qemu/atomic.h
|
||||
+++ b/include/qemu/atomic.h
|
||||
--- c/include/qemu/atomic.h
|
||||
+++ w/include/qemu/atomic.h
|
||||
@@ -75,7 +75,6 @@
|
||||
#else
|
||||
# define ATOMIC_REG_SIZE sizeof(void *)
|
||||
@@ -856,10 +936,10 @@ index 7a3f2e6576..50dc235043 100644
|
||||
qatomic_cmpxchg__nocheck(ptr, old, new); \
|
||||
})
|
||||
|
||||
diff --git a/include/qemu/cacheflush.h b/include/qemu/cacheflush.h
|
||||
diff --git c/include/qemu/cacheflush.h w/include/qemu/cacheflush.h
|
||||
index ae20bcda73..3f414fb7c9 100644
|
||||
--- a/include/qemu/cacheflush.h
|
||||
+++ b/include/qemu/cacheflush.h
|
||||
--- c/include/qemu/cacheflush.h
|
||||
+++ w/include/qemu/cacheflush.h
|
||||
@@ -19,7 +19,7 @@
|
||||
* mappings of the same physical page(s).
|
||||
*/
|
||||
@@ -869,10 +949,10 @@ index ae20bcda73..3f414fb7c9 100644
|
||||
|
||||
static inline void flush_idcache_range(uintptr_t rx, uintptr_t rw, size_t len)
|
||||
{
|
||||
diff --git a/include/qemu/int128.h b/include/qemu/int128.h
|
||||
diff --git c/include/qemu/int128.h w/include/qemu/int128.h
|
||||
index 174bd7dafb..0305e262ef 100644
|
||||
--- a/include/qemu/int128.h
|
||||
+++ b/include/qemu/int128.h
|
||||
--- c/include/qemu/int128.h
|
||||
+++ w/include/qemu/int128.h
|
||||
@@ -8,7 +8,7 @@
|
||||
* But libffi does not support __int128_t, and therefore cannot pass
|
||||
* or return values of this type, force use of the Int128 struct.
|
||||
@@ -882,10 +962,10 @@ index 174bd7dafb..0305e262ef 100644
|
||||
typedef __int128_t Int128;
|
||||
typedef __int128_t __attribute__((aligned(16))) Int128Aligned;
|
||||
|
||||
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
|
||||
diff --git c/include/qemu/osdep.h w/include/qemu/osdep.h
|
||||
index fdff07fd99..e95faa4478 100644
|
||||
--- a/include/qemu/osdep.h
|
||||
+++ b/include/qemu/osdep.h
|
||||
--- c/include/qemu/osdep.h
|
||||
+++ w/include/qemu/osdep.h
|
||||
@@ -133,7 +133,7 @@ QEMU_EXTERN_C int daemon(int, int);
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
@@ -920,10 +1000,10 @@ index fdff07fd99..e95faa4478 100644
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
size_t iov_len;
|
||||
diff --git a/include/tcg/helper-info.h b/include/tcg/helper-info.h
|
||||
diff --git c/include/tcg/helper-info.h w/include/tcg/helper-info.h
|
||||
index 909fe73afa..c5064473ac 100644
|
||||
--- a/include/tcg/helper-info.h
|
||||
+++ b/include/tcg/helper-info.h
|
||||
--- c/include/tcg/helper-info.h
|
||||
+++ w/include/tcg/helper-info.h
|
||||
@@ -9,7 +9,7 @@
|
||||
#ifndef TCG_HELPER_INFO_H
|
||||
#define TCG_HELPER_INFO_H
|
||||
@@ -949,10 +1029,10 @@ index 909fe73afa..c5064473ac 100644
|
||||
|
||||
unsigned typemask : 32;
|
||||
unsigned flags : 8;
|
||||
diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h
|
||||
diff --git c/include/tcg/tcg-opc.h w/include/tcg/tcg-opc.h
|
||||
index 546eb49c11..834757c3a4 100644
|
||||
--- a/include/tcg/tcg-opc.h
|
||||
+++ b/include/tcg/tcg-opc.h
|
||||
--- c/include/tcg/tcg-opc.h
|
||||
+++ w/include/tcg/tcg-opc.h
|
||||
@@ -305,7 +305,7 @@ DEF(last_generic, 0, 0, 0, TCG_OPF_NOT_PRESENT)
|
||||
#include "tcg-target.opc.h"
|
||||
#endif
|
||||
@@ -962,10 +1042,10 @@ index 546eb49c11..834757c3a4 100644
|
||||
/* These opcodes are only for use between the tci generator and interpreter. */
|
||||
DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT)
|
||||
DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT)
|
||||
diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h
|
||||
diff --git c/include/tcg/tcg.h w/include/tcg/tcg.h
|
||||
index 9b82844dab..2cdfe0617b 100644
|
||||
--- a/include/tcg/tcg.h
|
||||
+++ b/include/tcg/tcg.h
|
||||
--- c/include/tcg/tcg.h
|
||||
+++ w/include/tcg/tcg.h
|
||||
@@ -1010,7 +1010,7 @@ static inline size_t tcg_current_code_size(TCGContext *s)
|
||||
#define TB_EXIT_IDXMAX 1
|
||||
#define TB_EXIT_REQUESTED 3
|
||||
@@ -975,10 +1055,10 @@ index 9b82844dab..2cdfe0617b 100644
|
||||
uintptr_t tcg_qemu_tb_exec(CPUArchState *env, const void *tb_ptr);
|
||||
#else
|
||||
typedef uintptr_t tcg_prologue_fn(CPUArchState *env, const void *tb_ptr);
|
||||
diff --git a/meson.build b/meson.build
|
||||
diff --git c/meson.build w/meson.build
|
||||
index 7f28871776..6e558969d4 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
--- c/meson.build
|
||||
+++ w/meson.build
|
||||
@@ -45,9 +45,9 @@ genh = []
|
||||
qapi_trace_events = []
|
||||
|
||||
@@ -1067,10 +1147,10 @@ index 7f28871776..6e558969d4 100644
|
||||
|
||||
# slirp < 4.7 is incompatible with CFI support in QEMU. This is because
|
||||
# it passes function pointers within libslirp as callbacks for timers.
|
||||
diff --git a/meson_options.txt b/meson_options.txt
|
||||
diff --git c/meson_options.txt w/meson_options.txt
|
||||
index 5eeaf3eee5..76f7a65b40 100644
|
||||
--- a/meson_options.txt
|
||||
+++ b/meson_options.txt
|
||||
--- c/meson_options.txt
|
||||
+++ w/meson_options.txt
|
||||
@@ -34,7 +34,7 @@ option('fuzzing_engine', type : 'string', value : '',
|
||||
option('trace_file', type: 'string', value: 'trace',
|
||||
description: 'Trace file prefix for simple backend')
|
||||
@@ -1080,10 +1160,10 @@ index 5eeaf3eee5..76f7a65b40 100644
|
||||
value: 'auto', description: 'coroutine backend to use')
|
||||
|
||||
# Everything else can be set via --enable/--disable-* option
|
||||
diff --git a/os-posix.c b/os-posix.c
|
||||
diff --git c/os-posix.c w/os-posix.c
|
||||
index 43f9a43f3f..595fa47866 100644
|
||||
--- a/os-posix.c
|
||||
+++ b/os-posix.c
|
||||
--- c/os-posix.c
|
||||
+++ w/os-posix.c
|
||||
@@ -148,11 +148,13 @@ static void change_process_uid(void)
|
||||
exit(1);
|
||||
}
|
||||
@@ -1098,10 +1178,10 @@ index 43f9a43f3f..595fa47866 100644
|
||||
} else {
|
||||
if (setgroups(1, &user_gid) < 0) {
|
||||
error_report("Failed to setgroups(1, [%d])",
|
||||
diff --git a/qom/object.c b/qom/object.c
|
||||
diff --git c/qom/object.c w/qom/object.c
|
||||
index 9edc06d391..c3d657e6cb 100644
|
||||
--- a/qom/object.c
|
||||
+++ b/qom/object.c
|
||||
--- c/qom/object.c
|
||||
+++ w/qom/object.c
|
||||
@@ -1197,7 +1197,8 @@ GSList *object_class_get_list(const char *implements_type,
|
||||
return list;
|
||||
}
|
||||
@@ -1121,10 +1201,10 @@ index 9edc06d391..c3d657e6cb 100644
|
||||
}
|
||||
|
||||
Object *object_ref(void *objptr)
|
||||
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
|
||||
diff --git c/scripts/meson-buildoptions.sh w/scripts/meson-buildoptions.sh
|
||||
index a8066aab03..bd3ae90008 100644
|
||||
--- a/scripts/meson-buildoptions.sh
|
||||
+++ b/scripts/meson-buildoptions.sh
|
||||
--- c/scripts/meson-buildoptions.sh
|
||||
+++ w/scripts/meson-buildoptions.sh
|
||||
@@ -80,7 +80,7 @@ meson_options_help() {
|
||||
printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string'
|
||||
printf "%s\n" ' [NORMAL]'
|
||||
@@ -1134,10 +1214,10 @@ index a8066aab03..bd3ae90008 100644
|
||||
printf "%s\n" ' --with-pkgversion=VALUE use specified string as sub-version of the'
|
||||
printf "%s\n" ' package'
|
||||
printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories'
|
||||
diff --git a/system/memory.c b/system/memory.c
|
||||
diff --git c/system/memory.c w/system/memory.c
|
||||
index 85f6834cb3..3958314765 100644
|
||||
--- a/system/memory.c
|
||||
+++ b/system/memory.c
|
||||
--- c/system/memory.c
|
||||
+++ w/system/memory.c
|
||||
@@ -1636,7 +1636,7 @@ bool memory_region_init_resizeable_ram(MemoryRegion *mr,
|
||||
return true;
|
||||
}
|
||||
@@ -1147,10 +1227,10 @@ index 85f6834cb3..3958314765 100644
|
||||
bool memory_region_init_ram_from_file(MemoryRegion *mr,
|
||||
Object *owner,
|
||||
const char *name,
|
||||
diff --git a/system/physmem.c b/system/physmem.c
|
||||
diff --git c/system/physmem.c w/system/physmem.c
|
||||
index 75389064a8..addd5d0c4b 100644
|
||||
--- a/system/physmem.c
|
||||
+++ b/system/physmem.c
|
||||
--- c/system/physmem.c
|
||||
+++ w/system/physmem.c
|
||||
@@ -1235,7 +1235,7 @@ long qemu_maxrampagesize(void)
|
||||
return pagesize;
|
||||
}
|
||||
@@ -1178,10 +1258,10 @@ index 75389064a8..addd5d0c4b 100644
|
||||
} else if (block->fd >= 0) {
|
||||
qemu_ram_munmap(block->fd, block->host, block->max_length);
|
||||
close(block->fd);
|
||||
diff --git a/tcg/meson.build b/tcg/meson.build
|
||||
diff --git c/tcg/meson.build w/tcg/meson.build
|
||||
index 69ebb4908a..0894a577c2 100644
|
||||
--- a/tcg/meson.build
|
||||
+++ b/tcg/meson.build
|
||||
--- c/tcg/meson.build
|
||||
+++ w/tcg/meson.build
|
||||
@@ -15,11 +15,18 @@ tcg_ss.add(files(
|
||||
'tcg-op-vec.c',
|
||||
))
|
||||
@@ -1201,10 +1281,10 @@ index 69ebb4908a..0894a577c2 100644
|
||||
endif
|
||||
|
||||
tcg_ss.add(when: libdw, if_true: files('debuginfo.c'))
|
||||
diff --git a/tcg/region.c b/tcg/region.c
|
||||
diff --git c/tcg/region.c w/tcg/region.c
|
||||
index 478ec051c4..41895c68fa 100644
|
||||
--- a/tcg/region.c
|
||||
+++ b/tcg/region.c
|
||||
--- c/tcg/region.c
|
||||
+++ w/tcg/region.c
|
||||
@@ -568,7 +568,7 @@ static int alloc_code_gen_buffer_anon(size_t size, int prot,
|
||||
return prot;
|
||||
}
|
||||
@@ -1232,10 +1312,10 @@ index 478ec051c4..41895c68fa 100644
|
||||
if (tcg_splitwx_diff == 0) {
|
||||
need_prot |= host_prot_read_exec();
|
||||
}
|
||||
diff --git a/tcg/tcg.c b/tcg/tcg.c
|
||||
diff --git c/tcg/tcg.c w/tcg/tcg.c
|
||||
index 6dc3072f92..214e66e9d6 100644
|
||||
--- a/tcg/tcg.c
|
||||
+++ b/tcg/tcg.c
|
||||
--- c/tcg/tcg.c
|
||||
+++ w/tcg/tcg.c
|
||||
@@ -131,6 +131,10 @@ static void tcg_out_goto_tb(TCGContext *s, int which);
|
||||
static void tcg_out_op(TCGContext *s, TCGOpcode opc,
|
||||
const TCGArg args[TCG_MAX_OP_ARGS],
|
||||
@@ -1734,12 +1814,12 @@ index 6dc3072f92..214e66e9d6 100644
|
||||
return tcg_current_code_size(s);
|
||||
}
|
||||
|
||||
diff --git a/tcg/wasm32.c b/tcg/wasm32.c
|
||||
diff --git c/tcg/wasm32.c w/tcg/wasm32.c
|
||||
new file mode 100644
|
||||
index 0000000000..dfcbf1493b
|
||||
index 0000000000..55ad790f17
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32.c
|
||||
@@ -0,0 +1,1230 @@
|
||||
+++ w/tcg/wasm32.c
|
||||
@@ -0,0 +1,1246 @@
|
||||
+/*
|
||||
+ * Tiny Code Generator for QEMU
|
||||
+ *
|
||||
@@ -1794,7 +1874,7 @@ index 0000000000..dfcbf1493b
|
||||
+ const import_vec_size = memory_v.getInt32(wasm_begin + wasm_size, true);
|
||||
+ const import_vec_begin = wasm_begin + wasm_size + 4;
|
||||
+
|
||||
+ const wasm = HEAP8.subarray(wasm_begin, wasm_begin + wasm_size);
|
||||
+ const wasm = Uint8Array.from(HEAP8.subarray(wasm_begin, wasm_begin + wasm_size));
|
||||
+ var helper = {};
|
||||
+ for (var i = 0; i < import_vec_size / 4; i++) {
|
||||
+ helper[i] = wasmTable.get(memory_v.getInt32(import_vec_begin + i * 4, true));
|
||||
@@ -1821,6 +1901,19 @@ index 0000000000..dfcbf1493b
|
||||
+ return 0;
|
||||
+});
|
||||
+
|
||||
+EM_JS(void, flush_tb_instances, (), {
|
||||
+ const tb = Module.__wasm32_tb;
|
||||
+ if (!tb) {
|
||||
+ return;
|
||||
+ }
|
||||
+ const memory_v = new DataView(HEAP8.buffer);
|
||||
+ const remove_n = memory_v.getInt32(tb.to_remove_instance_idx_ptr, true);
|
||||
+ for (var i = 0; i < remove_n * 4; i += 4) {
|
||||
+ removeFunction(memory_v.getInt32(tb.to_remove_instance_ptr + i, true));
|
||||
+ }
|
||||
+ memory_v.setInt32(tb.to_remove_instance_idx_ptr, 0, true);
|
||||
+});
|
||||
+
|
||||
+__thread bool initdone = false;
|
||||
+__thread int cur_core_num = -1;
|
||||
+__thread int export_vec_off = -1;
|
||||
@@ -1838,6 +1931,9 @@ index 0000000000..dfcbf1493b
|
||||
+ return;
|
||||
+ }
|
||||
+ *slot = 0;
|
||||
+ if (to_remove_instance_idx == TO_REMOVE_INSTANCE_SIZE) {
|
||||
+ flush_tb_instances();
|
||||
+ }
|
||||
+ to_remove_instance[to_remove_instance_idx++] = f;
|
||||
+}
|
||||
+
|
||||
@@ -2970,12 +3066,12 @@ index 0000000000..dfcbf1493b
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
diff --git a/tcg/wasm32.h b/tcg/wasm32.h
|
||||
diff --git c/tcg/wasm32.h w/tcg/wasm32.h
|
||||
new file mode 100644
|
||||
index 0000000000..1861510f0b
|
||||
index 0000000000..1527bf4875
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32.h
|
||||
@@ -0,0 +1,44 @@
|
||||
+++ w/tcg/wasm32.h
|
||||
@@ -0,0 +1,46 @@
|
||||
+#ifndef TCG_WASM32_H
|
||||
+#define TCG_WASM32_H
|
||||
+
|
||||
@@ -3015,16 +3111,18 @@ index 0000000000..1861510f0b
|
||||
+
|
||||
+void remove_tb(void *tb_ptr);
|
||||
+
|
||||
+void flush_tb_instances(void);
|
||||
+
|
||||
+void init_wasm32();
|
||||
+
|
||||
+#define INSTANTIATE_NUM 1500
|
||||
+
|
||||
+#endif
|
||||
diff --git a/tcg/wasm32/tcg-target-con-set.h b/tcg/wasm32/tcg-target-con-set.h
|
||||
diff --git c/tcg/wasm32/tcg-target-con-set.h w/tcg/wasm32/tcg-target-con-set.h
|
||||
new file mode 100644
|
||||
index 0000000000..f50a6793b7
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32/tcg-target-con-set.h
|
||||
+++ w/tcg/wasm32/tcg-target-con-set.h
|
||||
@@ -0,0 +1,15 @@
|
||||
+/*
|
||||
+ * C_On_Im(...) defines a constraint set with <n> outputs and <m> inputs.
|
||||
@@ -3041,22 +3139,22 @@ index 0000000000..f50a6793b7
|
||||
+C_O2_I1(r, r, r)
|
||||
+C_O2_I2(r, r, r, r)
|
||||
+C_O2_I4(r, r, r, r, r, r)
|
||||
diff --git a/tcg/wasm32/tcg-target-con-str.h b/tcg/wasm32/tcg-target-con-str.h
|
||||
diff --git c/tcg/wasm32/tcg-target-con-str.h w/tcg/wasm32/tcg-target-con-str.h
|
||||
new file mode 100644
|
||||
index 0000000000..56d7aff291
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32/tcg-target-con-str.h
|
||||
+++ w/tcg/wasm32/tcg-target-con-str.h
|
||||
@@ -0,0 +1,5 @@
|
||||
+/*
|
||||
+ * Define constraint letters for register sets:
|
||||
+ * REGS(letter, register_mask)
|
||||
+ */
|
||||
+REGS('r', MAKE_64BIT_MASK(0, TCG_TARGET_NB_REGS))
|
||||
diff --git a/tcg/wasm32/tcg-target-reg-bits.h b/tcg/wasm32/tcg-target-reg-bits.h
|
||||
diff --git c/tcg/wasm32/tcg-target-reg-bits.h w/tcg/wasm32/tcg-target-reg-bits.h
|
||||
new file mode 100644
|
||||
index 0000000000..196395aef9
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32/tcg-target-reg-bits.h
|
||||
+++ w/tcg/wasm32/tcg-target-reg-bits.h
|
||||
@@ -0,0 +1,6 @@
|
||||
+#ifndef TCG_TARGET_REG_BITS_H
|
||||
+#define TCG_TARGET_REG_BITS_H
|
||||
@@ -3064,11 +3162,11 @@ index 0000000000..196395aef9
|
||||
+#define TCG_TARGET_REG_BITS 64
|
||||
+
|
||||
+#endif
|
||||
diff --git a/tcg/wasm32/tcg-target.c.inc b/tcg/wasm32/tcg-target.c.inc
|
||||
diff --git c/tcg/wasm32/tcg-target.c.inc w/tcg/wasm32/tcg-target.c.inc
|
||||
new file mode 100644
|
||||
index 0000000000..9e82297a3f
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32/tcg-target.c.inc
|
||||
+++ w/tcg/wasm32/tcg-target.c.inc
|
||||
@@ -0,0 +1,3824 @@
|
||||
+/*
|
||||
+ * Tiny Code Generator for QEMU
|
||||
@@ -6894,11 +6992,11 @@ index 0000000000..9e82297a3f
|
||||
+static inline void tcg_target_qemu_prologue(TCGContext *s)
|
||||
+{
|
||||
+}
|
||||
diff --git a/tcg/wasm32/tcg-target.h b/tcg/wasm32/tcg-target.h
|
||||
diff --git c/tcg/wasm32/tcg-target.h w/tcg/wasm32/tcg-target.h
|
||||
new file mode 100644
|
||||
index 0000000000..7346c3a76e
|
||||
--- /dev/null
|
||||
+++ b/tcg/wasm32/tcg-target.h
|
||||
+++ w/tcg/wasm32/tcg-target.h
|
||||
@@ -0,0 +1,153 @@
|
||||
+/*
|
||||
+ * Tiny Code Generator for QEMU
|
||||
@@ -7053,10 +7151,10 @@ index 0000000000..7346c3a76e
|
||||
+#define TCG_TARGET_HAS_MEMORY_BSWAP 0
|
||||
+
|
||||
+#endif /* TCG_TARGET_H */
|
||||
diff --git a/util/cacheflush.c b/util/cacheflush.c
|
||||
diff --git c/util/cacheflush.c w/util/cacheflush.c
|
||||
index a08906155a..babbb644e5 100644
|
||||
--- a/util/cacheflush.c
|
||||
+++ b/util/cacheflush.c
|
||||
--- c/util/cacheflush.c
|
||||
+++ w/util/cacheflush.c
|
||||
@@ -225,7 +225,7 @@ static void __attribute__((constructor)) init_cache_info(void)
|
||||
* Architecture (+ OS) specific cache flushing mechanisms.
|
||||
*/
|
||||
@@ -7066,11 +7164,11 @@ index a08906155a..babbb644e5 100644
|
||||
|
||||
/* Caches are coherent and do not require flushing; symbol inline. */
|
||||
|
||||
diff --git a/util/coroutine-fiber.c b/util/coroutine-fiber.c
|
||||
diff --git c/util/coroutine-fiber.c w/util/coroutine-fiber.c
|
||||
new file mode 100644
|
||||
index 0000000000..2ce1575362
|
||||
--- /dev/null
|
||||
+++ b/util/coroutine-fiber.c
|
||||
+++ w/util/coroutine-fiber.c
|
||||
@@ -0,0 +1,123 @@
|
||||
+/*
|
||||
+ * emscripten fiber coroutine initialization code
|
||||
@@ -7195,10 +7293,10 @@ index 0000000000..2ce1575362
|
||||
+
|
||||
+ return self && self->caller;
|
||||
+}
|
||||
diff --git a/util/int128.c b/util/int128.c
|
||||
diff --git c/util/int128.c w/util/int128.c
|
||||
index df6c6331bd..c442ff4da8 100644
|
||||
--- a/util/int128.c
|
||||
+++ b/util/int128.c
|
||||
--- c/util/int128.c
|
||||
+++ w/util/int128.c
|
||||
@@ -144,7 +144,7 @@ Int128 int128_rems(Int128 a, Int128 b)
|
||||
return r;
|
||||
}
|
||||
@@ -7208,10 +7306,10 @@ index df6c6331bd..c442ff4da8 100644
|
||||
|
||||
Int128 int128_divu(Int128 a_s, Int128 b_s)
|
||||
{
|
||||
diff --git a/util/meson.build b/util/meson.build
|
||||
diff --git c/util/meson.build w/util/meson.build
|
||||
index 5d8bef9891..e107fbfb69 100644
|
||||
--- a/util/meson.build
|
||||
+++ b/util/meson.build
|
||||
--- c/util/meson.build
|
||||
+++ w/util/meson.build
|
||||
@@ -11,7 +11,9 @@ if host_os != 'windows'
|
||||
endif
|
||||
util_ss.add(files('compatfd.c'))
|
||||
@@ -7223,10 +7321,10 @@ index 5d8bef9891..e107fbfb69 100644
|
||||
freebsd_dep = []
|
||||
if host_os == 'freebsd'
|
||||
freebsd_dep = util
|
||||
diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c
|
||||
diff --git c/util/mmap-alloc.c w/util/mmap-alloc.c
|
||||
index ed14f9c64d..8c8708ca4d 100644
|
||||
--- a/util/mmap-alloc.c
|
||||
+++ b/util/mmap-alloc.c
|
||||
--- c/util/mmap-alloc.c
|
||||
+++ w/util/mmap-alloc.c
|
||||
@@ -250,6 +250,11 @@ void *qemu_ram_mmap(int fd,
|
||||
uint32_t qemu_map_flags,
|
||||
off_t map_offset)
|
||||
@@ -7247,10 +7345,10 @@ index ed14f9c64d..8c8708ca4d 100644
|
||||
}
|
||||
|
||||
void qemu_ram_munmap(int fd, void *ptr, size_t size)
|
||||
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
|
||||
diff --git c/util/oslib-posix.c w/util/oslib-posix.c
|
||||
index 11b35e48fb..760c3d4f4c 100644
|
||||
--- a/util/oslib-posix.c
|
||||
+++ b/util/oslib-posix.c
|
||||
--- c/util/oslib-posix.c
|
||||
+++ w/util/oslib-posix.c
|
||||
@@ -58,6 +58,7 @@
|
||||
#include <lwp.h>
|
||||
#endif
|
||||
|
||||
@@ -10,7 +10,7 @@ TAG="esp-develop-9.2.2-20260417"
|
||||
COMMIT="40edccac415693c5130f91c01d84176ae6008566"
|
||||
QEMU_WASM_COMMIT="0ef7b4e2814b231705d8371dd7997f5b72e70baf"
|
||||
IMAGE="xteink-qemu-wasm-build"
|
||||
JOBS="${JOBS:-2}"
|
||||
JOBS="${JOBS:-$(nproc 2>/dev/null || echo 2)}"
|
||||
|
||||
if [ -n "${CONTAINER_ENGINE:-}" ]; then
|
||||
ENGINE="$CONTAINER_ENGINE"
|
||||
@@ -30,19 +30,22 @@ if [ "$(git rev-parse HEAD)" != "$COMMIT" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f hw/display/xteink_x3_eink.c ] || [ ! -f tcg/wasm32.c ]; then
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "QEMU source has changes that conflict with the WASM patches" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Re-apply Patches On Drift - The source tree is a build artifact of the patches; reset and re-apply whenever a patch is newer than the last apply so `make web` never silently rebuilds stale sources.
|
||||
STAMP=".xteink-patch-stamp"
|
||||
if [ ! -f hw/display/xteink_x3_eink.c ] || [ ! -f tcg/wasm32.c ] \
|
||||
|| [ "$ROOT/qemu/patches/0001-xteink-machine.patch" -nt "$STAMP" ] \
|
||||
|| [ "$ROOT/qemu/patches/0002-qemu-wasm.patch" -nt "$STAMP" ]; then
|
||||
git reset --hard "$COMMIT"
|
||||
git clean -fdx -e build
|
||||
git apply "$ROOT/qemu/patches/0001-xteink-machine.patch"
|
||||
git apply --whitespace=nowarn "$ROOT/qemu/patches/0002-qemu-wasm.patch"
|
||||
touch "$STAMP"
|
||||
fi
|
||||
|
||||
printf 'Building Emscripten environment (qemu-wasm %s)\n' "$QEMU_WASM_COMMIT"
|
||||
"$ENGINE" build -t "$IMAGE" -f "$ROOT/qemu/wasm/Dockerfile" "$ROOT/qemu/wasm"
|
||||
|
||||
FLAGS="-O3 -Wno-error=unused-command-line-argument -matomics -mbulk-memory -DNDEBUG -DG_DISABLE_ASSERT -D_GNU_SOURCE -sASYNCIFY=1 -pthread -sPROXY_TO_PTHREAD=1 -sFORCE_FILESYSTEM -sALLOW_TABLE_GROWTH -sINITIAL_MEMORY=512MB -sWASM_BIGINT -sMALLOC=mimalloc -sEXPORT_ES6=1 -sASYNCIFY_IMPORTS=ffi_call_js"
|
||||
FLAGS="-O3 -Wno-error=unused-command-line-argument -matomics -mbulk-memory -DNDEBUG -DG_DISABLE_ASSERT -D_GNU_SOURCE -sASYNCIFY=1 -pthread -sPROXY_TO_PTHREAD=1 -sFORCE_FILESYSTEM -sALLOW_TABLE_GROWTH -sINITIAL_MEMORY=64MB -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1GB -sSTACK_SIZE=1048576 -sWASM_BIGINT -sMALLOC=mimalloc -sEXPORT_ES6=1 -sASYNCIFY_IMPORTS=ffi_call_js"
|
||||
|
||||
if [ ! -f build/build.ninja ]; then
|
||||
"$ENGINE" run --rm -v "$SRC:/qemu" "$IMAGE" bash -lc "
|
||||
@@ -62,7 +65,7 @@ if [ ! -f build/build.ninja ]; then
|
||||
--disable-tools \\
|
||||
--extra-cflags='$FLAGS' \\
|
||||
--extra-cxxflags='$FLAGS' \\
|
||||
--extra-ldflags='-sEXPORTED_RUNTIME_METHODS=getTempRet0,setTempRet0,addFunction,removeFunction,TTY,FS'
|
||||
--extra-ldflags='-sINITIAL_MEMORY=64MB -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=1GB -sEXPORTED_RUNTIME_METHODS=getTempRet0,setTempRet0,addFunction,removeFunction,TTY,FS'
|
||||
"
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { gunzipSync } from 'node:zlib';
|
||||
|
||||
const artifactRoot = process.argv[2];
|
||||
if (!artifactRoot) {
|
||||
@@ -9,12 +10,14 @@ if (!artifactRoot) {
|
||||
|
||||
const moduleUrl = pathToFileURL(`${artifactRoot}/qemu-system-riscv32.js`);
|
||||
const rom = new Uint8Array(await readFile(`${artifactRoot}/esp32c3-rom.bin`));
|
||||
const sdCard = gunzipSync(await readFile('web/assets/blank-sd.img.gz'));
|
||||
const { default: createQemu } = await import(moduleUrl);
|
||||
const options = {
|
||||
arguments: [
|
||||
'-S', '-machine', 'xteink,variant=x3', '-L', '/bios',
|
||||
'-S', '-machine', 'xteink,variant=x3', '-accel', 'tcg,tb-size=16', '-L', '/bios',
|
||||
'-display', 'none', '-serial', 'null', '-nic', 'none',
|
||||
'-drive', 'file=/flash.bin,if=mtd,format=raw',
|
||||
'-drive', 'file=/sd.img,if=sd,format=raw',
|
||||
],
|
||||
mainScriptUrlOrBlob: moduleUrl.href,
|
||||
};
|
||||
@@ -22,6 +25,7 @@ options.preRun = [() => {
|
||||
options.FS.mkdir('/bios');
|
||||
options.FS.writeFile('/bios/esp32c3-rom.bin', rom);
|
||||
options.FS.writeFile('/flash.bin', new Uint8Array(16 * 1024 * 1024));
|
||||
options.FS.writeFile('/sd.img', sdCard);
|
||||
}];
|
||||
|
||||
const module = await createQemu(options);
|
||||
|
||||
+11
-1
@@ -1,5 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { BUTTONS, setButton, mergeFirmware } from '../web/app.js';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { BUTTONS, setButton, mergeFirmware, loadEphemeralSdCard } from '../web/app.js';
|
||||
|
||||
// Merge - app image is placed at 0x10000 in a 0xFF-erased 16 MB flash with bootloader and partitions.
|
||||
const bootloader = new Uint8Array([1, 2, 3]);
|
||||
@@ -14,6 +15,15 @@ assert.equal(flash[0x10000], 0xe9);
|
||||
assert.equal(flash[0x20000], 0xff);
|
||||
assert.throws(() => mergeFirmware(new Uint8Array([0x00]), bootloader, partitions), /0xE9/);
|
||||
|
||||
const compressedSd = await readFile('web/assets/blank-sd.img.gz');
|
||||
const fetchSd = async () => new Response(compressedSd);
|
||||
const firstSd = await loadEphemeralSdCard(import.meta.url, fetchSd);
|
||||
const secondSd = await loadEphemeralSdCard(import.meta.url, fetchSd);
|
||||
assert.equal(firstSd.length, 64 * 1024 * 1024);
|
||||
assert.deepEqual(firstSd.slice(510, 512), new Uint8Array([0x55, 0xaa]));
|
||||
firstSd[0] ^= 0xff;
|
||||
assert.notEqual(firstSd[0], secondSd[0]);
|
||||
|
||||
const calls = [];
|
||||
const module = {
|
||||
_xteink_wasm_set_adc: (...args) => calls.push(['adc', ...args]),
|
||||
|
||||
+70
-19
@@ -38,6 +38,18 @@ async function fetchBin(url) {
|
||||
return new Uint8Array(await response.arrayBuffer());
|
||||
}
|
||||
|
||||
export async function loadEphemeralSdCard(assetRoot = import.meta.url, fetchFn = fetch) {
|
||||
const response = await fetchFn(new URL('assets/blank-sd.img.gz', assetRoot));
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not load blank SD card (${response.status}).`);
|
||||
}
|
||||
if (!response.body || typeof DecompressionStream === 'undefined') {
|
||||
throw new Error('This browser cannot decompress the blank SD card image.');
|
||||
}
|
||||
const stream = response.body.pipeThrough(new DecompressionStream('gzip'));
|
||||
return new Uint8Array(await new Response(stream).arrayBuffer());
|
||||
}
|
||||
|
||||
// A full 16 MB image is used as-is; anything else is treated as a CrossPoint app image and merged with the bundled bootloader and partition table.
|
||||
async function prepareFirmware(bytes, assetRoot) {
|
||||
if (bytes.length === FLASH_SIZE) {
|
||||
@@ -154,14 +166,19 @@ function enableControls(module) {
|
||||
}
|
||||
|
||||
let logEl = null;
|
||||
let persistLog = false;
|
||||
const logBuffer = [];
|
||||
const LOG_MAX_LINES = 1000;
|
||||
const DEBUG_LOG_KEY = 'xteink-debug-log';
|
||||
|
||||
function logLine(message) {
|
||||
logBuffer.push(message);
|
||||
if (logBuffer.length > LOG_MAX_LINES) {
|
||||
logBuffer.splice(0, logBuffer.length - LOG_MAX_LINES);
|
||||
}
|
||||
if (persistLog) {
|
||||
sessionStorage.setItem(DEBUG_LOG_KEY, logBuffer.join('\n'));
|
||||
}
|
||||
if (logEl) {
|
||||
const atBottom = logEl.scrollTop + logEl.clientHeight >= logEl.scrollHeight - 4;
|
||||
logEl.textContent = logBuffer.join('\n');
|
||||
@@ -171,32 +188,39 @@ function logLine(message) {
|
||||
}
|
||||
}
|
||||
|
||||
async function boot(file, variant, setStatus) {
|
||||
async function boot(file, variant, setStatus, sdCardProvider = loadEphemeralSdCard) {
|
||||
if (!crossOriginIsolated) {
|
||||
throw new Error('This emulator requires COOP/COEP headers. Start it with `make web`.');
|
||||
}
|
||||
setStatus('Loading firmware…');
|
||||
setStatus('Loading firmware and ephemeral SD card…');
|
||||
|
||||
const firmware = await prepareFirmware(new Uint8Array(await file.arrayBuffer()), import.meta.url);
|
||||
const artifactRoot = new URL('../dist/wasm/', import.meta.url);
|
||||
const qemuUrl = new URL('qemu-system-riscv32.js', artifactRoot);
|
||||
const rom = new Uint8Array(await fetch(new URL('esp32c3-rom.bin', artifactRoot)).then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not load ESP32-C3 ROM (${response.status}).`);
|
||||
}
|
||||
return response.arrayBuffer();
|
||||
}));
|
||||
const [firmware, rom, sdCard] = await Promise.all([
|
||||
prepareFirmware(new Uint8Array(await file.arrayBuffer()), import.meta.url),
|
||||
fetchBin(new URL('esp32c3-rom.bin', artifactRoot)),
|
||||
sdCardProvider(import.meta.url),
|
||||
]);
|
||||
const sdMessage = 'Using a blank ephemeral SD card; writes will be discarded when the emulator restarts.';
|
||||
console.log(sdMessage);
|
||||
logLine(sdMessage);
|
||||
const { default: createQemu } = await import(qemuUrl);
|
||||
|
||||
const qemuLogFlags = new URL(location.href).searchParams.has('trace')
|
||||
? 'guest_errors,unimp,in_asm'
|
||||
: 'guest_errors,unimp';
|
||||
const options = {
|
||||
arguments: [
|
||||
'-machine', `xteink,variant=${variant}`,
|
||||
'-accel', 'tcg,tb-size=16',
|
||||
'-icount', 'shift=auto,align=off,sleep=off',
|
||||
'-L', '/bios',
|
||||
'-display', 'none',
|
||||
'-serial', 'stdio',
|
||||
'-nic', 'none',
|
||||
'-d', 'guest_errors,unimp',
|
||||
'-d', qemuLogFlags,
|
||||
'-drive', 'file=/flash.bin,if=mtd,format=raw',
|
||||
'-drive', 'file=/sd.img,if=sd,format=raw',
|
||||
],
|
||||
locateFile: path => new URL(path, artifactRoot).href,
|
||||
mainScriptUrlOrBlob: qemuUrl.href,
|
||||
@@ -208,6 +232,7 @@ async function boot(file, variant, setStatus) {
|
||||
options.FS.mkdir('/bios');
|
||||
options.FS.writeFile('/bios/esp32c3-rom.bin', rom);
|
||||
options.FS.writeFile('/flash.bin', firmware);
|
||||
options.FS.writeFile('/sd.img', sdCard);
|
||||
}];
|
||||
|
||||
setStatus('Starting emulator…');
|
||||
@@ -222,8 +247,15 @@ function initialize() {
|
||||
const status = document.querySelector('#status');
|
||||
const canvas = document.querySelector('#screen');
|
||||
logEl = document.querySelector('#log');
|
||||
const params = new URL(location.href).searchParams;
|
||||
persistLog = params.has('firmware');
|
||||
if (persistLog) {
|
||||
logBuffer.push(...(sessionStorage.getItem(DEBUG_LOG_KEY) || '').split('\n').filter(Boolean));
|
||||
logEl.textContent = logBuffer.join('\n');
|
||||
}
|
||||
document.querySelector('#log-clear').addEventListener('click', () => {
|
||||
logBuffer.length = 0;
|
||||
sessionStorage.removeItem(DEBUG_LOG_KEY);
|
||||
logEl.textContent = '';
|
||||
});
|
||||
|
||||
@@ -232,19 +264,15 @@ function initialize() {
|
||||
status.classList.toggle('error', error);
|
||||
};
|
||||
|
||||
form.addEventListener('submit', async event => {
|
||||
event.preventDefault();
|
||||
const file = firmwareInput.files[0];
|
||||
if (!file) {
|
||||
setStatus('Choose a firmware image first.', true);
|
||||
return;
|
||||
}
|
||||
|
||||
const start = async (file, variant = variantInput.value) => {
|
||||
bootButton.disabled = true;
|
||||
firmwareInput.disabled = true;
|
||||
variantInput.disabled = true;
|
||||
try {
|
||||
const module = await boot(file, variantInput.value, setStatus);
|
||||
const module = await boot(file, variant, setStatus);
|
||||
if (persistLog) {
|
||||
window.__xteinkModule = module;
|
||||
}
|
||||
startDisplayLoop(module, canvas, setStatus, () => enableControls(module));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -253,7 +281,30 @@ function initialize() {
|
||||
firmwareInput.disabled = false;
|
||||
variantInput.disabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
form.addEventListener('submit', event => {
|
||||
event.preventDefault();
|
||||
const file = firmwareInput.files[0];
|
||||
if (!file) {
|
||||
setStatus('Choose a firmware image first.', true);
|
||||
return;
|
||||
}
|
||||
start(file);
|
||||
});
|
||||
|
||||
const firmwareUrl = params.get('firmware');
|
||||
if (firmwareUrl) {
|
||||
fetch(firmwareUrl)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not load debug firmware (${response.status}).`);
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then(blob => start(new File([blob], 'firmware.bin'), params.get('variant') || 'x3'))
|
||||
.catch(error => setStatus(error.message || String(error), true));
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
|
||||
+14
-13
@@ -31,21 +31,21 @@
|
||||
|
||||
<p id="status" class="status" role="status">Waiting for firmware.</p>
|
||||
|
||||
<div class="workspace">
|
||||
<section class="device" aria-label="Emulated xteink device">
|
||||
<div class="screen-shell">
|
||||
<canvas id="screen" width="528" height="792" aria-label="E-ink display"></canvas>
|
||||
</div>
|
||||
|
||||
<div id="controls" class="controls" aria-label="Device controls">
|
||||
<button type="button" data-button="back" disabled>Back</button>
|
||||
<div class="dpad">
|
||||
<button type="button" class="up" data-button="up" disabled aria-label="Up">▲</button>
|
||||
<button type="button" class="left" data-button="left" disabled aria-label="Left">◀</button>
|
||||
<button type="button" class="confirm" data-button="confirm" disabled>OK</button>
|
||||
<button type="button" class="right" data-button="right" disabled aria-label="Right">▶</button>
|
||||
<button type="button" class="down" data-button="down" disabled aria-label="Down">▼</button>
|
||||
<div class="device-frame" aria-label="Device controls">
|
||||
<button type="button" class="btn-power" data-button="power" disabled aria-label="Power"></button>
|
||||
<button type="button" class="btn-side btn-up" data-button="up" disabled aria-label="Up"></button>
|
||||
<div class="screen-shell">
|
||||
<canvas id="screen" width="528" height="792" aria-label="E-ink display"></canvas>
|
||||
</div>
|
||||
<button type="button" class="btn-side btn-down" data-button="down" disabled aria-label="Down"></button>
|
||||
<div class="btn-bottom">
|
||||
<button type="button" data-button="back" disabled aria-label="Back"></button>
|
||||
<button type="button" data-button="confirm" disabled aria-label="OK"></button>
|
||||
<button type="button" data-button="left" disabled aria-label="Left"></button>
|
||||
<button type="button" data-button="right" disabled aria-label="Right"></button>
|
||||
</div>
|
||||
<button type="button" class="power" data-button="power" disabled>Power</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
</div>
|
||||
<pre id="log" class="log" role="log" aria-live="polite"></pre>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script type="module" src="app.js"></script>
|
||||
|
||||
+19
-18
@@ -43,34 +43,35 @@ button.active { transform: translateY(2px); background: #d2752b; }
|
||||
.status { min-height: 1.5rem; margin: 1rem 0; }
|
||||
.status.error { color: #a52d20; font-weight: 700; }
|
||||
|
||||
.device {
|
||||
.workspace { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
|
||||
.device { display: flex; justify-content: center; }
|
||||
|
||||
.device-frame {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(18rem, 33rem) minmax(14rem, 20rem);
|
||||
gap: clamp(1.5rem, 5vw, 4rem);
|
||||
align-items: center;
|
||||
grid-template-columns: auto minmax(14rem, 30rem) auto;
|
||||
grid-template-rows: auto auto auto;
|
||||
gap: .6rem;
|
||||
align-items: stretch;
|
||||
justify-items: stretch;
|
||||
}
|
||||
|
||||
.screen-shell { padding: 1.1rem; border-radius: 1.2rem; background: #30312e; box-shadow: 0 1rem 2.5rem #5d584c40; }
|
||||
.screen-shell { grid-column: 2; grid-row: 2; padding: 1.1rem; border-radius: 1.2rem; background: #30312e; box-shadow: 0 1rem 2.5rem #5d584c40; }
|
||||
canvas { display: block; width: 100%; height: auto; background: #fff; image-rendering: pixelated; }
|
||||
|
||||
.controls { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: center; }
|
||||
.controls > button { min-height: 4rem; }
|
||||
.power { background: #8e3228; }
|
||||
.dpad { grid-column: 1 / -1; display: grid; grid-template: repeat(3, 4.25rem) / repeat(3, 4.25rem); justify-content: center; }
|
||||
.dpad button { border-radius: 50%; padding: .25rem; }
|
||||
.up { grid-area: 1 / 2; }
|
||||
.left { grid-area: 2 / 1; }
|
||||
.confirm { grid-area: 2 / 2; }
|
||||
.right { grid-area: 2 / 3; }
|
||||
.down { grid-area: 3 / 2; }
|
||||
.device-frame button { min-height: 0; border-radius: .6rem; }
|
||||
.btn-power { grid-column: 2; grid-row: 1; height: 1.6rem; }
|
||||
.btn-side { grid-row: 2; width: 1.6rem; }
|
||||
.btn-up { grid-column: 1; }
|
||||
.btn-down { grid-column: 3; }
|
||||
.btn-bottom { grid-column: 2; grid-row: 3; display: grid; grid-template-columns: repeat(4, 1fr); gap: .6rem; }
|
||||
.btn-bottom button { height: 1.6rem; }
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
main { padding-top: 1.5rem; }
|
||||
.device { grid-template-columns: 1fr; }
|
||||
.controls { max-width: 24rem; width: 100%; margin: 0 auto; }
|
||||
.workspace { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
.log-panel { margin-top: 2rem; }
|
||||
.log-panel { min-width: 0; }
|
||||
.log-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: .5rem; font-size: .85rem; color: #b9b3a4; }
|
||||
.log-header button { min-height: auto; padding: .25rem .75rem; font-size: .8rem; }
|
||||
.log { margin: 0; max-height: 20rem; overflow: auto; padding: .75rem 1rem; border-radius: .6rem; background: #1c1d1a; color: #d6d0c2; font: .78rem/1.4 ui-monospace, monospace; white-space: pre-wrap; word-break: break-word; }
|
||||
|
||||
Reference in New Issue
Block a user