mirror of
https://github.com/buserror/mii_emu.git
synced 2025-08-15 07:27:28 +00:00
mii: Pre release 1.95
Tons of stuff, mostly internal, mostly audio related, but well, there's been a lot. See Changelog.md for a bit more details Signed-off-by: Michel Pollet <buserror@gmail.com>
This commit is contained in:
19
CHANGELOG.md
19
CHANGELOG.md
@@ -3,6 +3,25 @@
|
||||
</p>
|
||||
|
||||
# MII Version Changelog
|
||||
## 1.95
|
||||
This is an intermediate release, mostly to fix a few bugs and add a few features that were requested.
|
||||
* Now have support for *writing* both DSK (PO and DO) as well as NIB files. As it
|
||||
turns out, this was a most requested feature -- I thought everyone would be
|
||||
happy with WOZ files, but no, people want to write DSK files. So now you can.
|
||||
* Quite a few tweaks and a couple bug fixes were provided by Frédérick @skippyfr for the core 65c02 emulation, thanks!
|
||||
* Quite a few code changes on the way I dealt with ROM files; removed the use
|
||||
of *incbin.h* as it was creating problems with the linker, and failed to work
|
||||
with webassembly.
|
||||
* Added a *non-functional* proto support for Apple //c -- it half boots at the
|
||||
moment. Still need work, mostly changing my Disk ][ driver to properly emulate
|
||||
an IWM, but that'll come.
|
||||
* Changed the SmartPort driver to continue booting if there are no Disks. This
|
||||
seems to solve a problem people had with booting from a floppy when there was
|
||||
no hard drive -- MII would stop at the basic prompt; not it continues to boot
|
||||
from the next slot down the line (with a message).
|
||||
* Audio processing was redone. There is now a notion of an audio 'sink' with an (now 32 bit float) FIFO. The speaker code (and soon, perhaps, the mockingboard) writes to it's own sink, and there an audio mixer that reads from all the sinks and mixes them together. This allows for a more flexible audio system, and also allows for a 'master volume' control.
|
||||
* Also now use 'sokol_audio' for the audio output, which gives me the possibility of a multi-platform audio output, if ever needed. I also added support for "miniaudio"; but it does a bit too much for my need at the minute, and slows down the compilation considerably, so it's not the default.
|
||||
|
||||
## 1.9
|
||||
#### Video
|
||||
* Video driver now only draw when the apple II video *really* changes. It keeps track of touched lines across the screen, and only updates when needed. This saves a considerable amount of CPU time.
|
||||
|
100
Makefile
100
Makefile
@@ -17,11 +17,14 @@ CPPFLAGS += -Icontrib
|
||||
CPPFLAGS += -Ilibmish/src
|
||||
CPPFLAGS += -Ilibmui/src
|
||||
|
||||
OPTIMIZE ?= -O2 -march=native -ffast-math
|
||||
OPTIMIZE ?= -O3 -march=native -ffast-math -ftree-vectorize
|
||||
#OPTIMIZE ?= -O0 -g -fno-omit-frame-pointer
|
||||
CFLAGS += --std=gnu99 -Wall -Wextra -g
|
||||
# This is useful for debugging, not so much for actual use
|
||||
#CFLAGS += -fno-omit-frame-pointer
|
||||
CFLAGS += $(OPTIMIZE)
|
||||
CFLAGS += -Wno-unused-parameter -Wno-unused-function
|
||||
CFLAGS += -Wno-unused-parameter -Wno-unused-function \
|
||||
-Wno-unused-result
|
||||
LDLIBS += -lX11 -lGL -lGLU
|
||||
LDLIBS += -lpthread -lutil -lm
|
||||
|
||||
@@ -90,8 +93,68 @@ $(LIB)/libmui.a : ${wildcard libmui/src/*} | $(LIB) $(OBJ) $(BIN)
|
||||
# Smartport firmware needs the assembler first
|
||||
test/asm/%.bin : test/asm/%.asm | $(BIN)/mii_asm
|
||||
$(BIN)/mii_asm -v -o $@ $<
|
||||
# And it also INCBIN the firmware driver
|
||||
$(OBJ)/mii_smarport.o : test/asm/mii_smartport_driver.bin
|
||||
|
||||
# ROMS: This bits convert the binary roms with the product number in the name
|
||||
# to a C header file that can be included in the project. I used to use
|
||||
# incbin.h for this, but there were issues with some of the linker used, for
|
||||
# example, webassembly, so we now use xxd to generate the .h file.
|
||||
# You'd NEED the xxd tool if you want to re-generate these files, but well,
|
||||
# they've been constant for over 40 years, my guess is that they ain't going
|
||||
# anywhere.
|
||||
#
|
||||
# The reason these roms are used like this here is that I always found that it
|
||||
# was a massive pain to have to deal with the roms in *any* apple II (or Mac)
|
||||
# emulator. You had to find the roms, put them in the right place, name them
|
||||
# correctly, and then, if you wanted to use a different version, you had to
|
||||
# rename them, and so on. Dreadful.
|
||||
# I think it prevents a lot of people learning about the Apple II, because it's
|
||||
# just too much hassle to get started. So, I've included the roms in the source
|
||||
# code, and they are compiled in the binary. For the user. Not for convenience,
|
||||
# Not for 'stealing' from apple, but for the user. For the user to have a
|
||||
# seamless experience. To be able to just run the emulator, and have it work.
|
||||
# And be amazed *at the brand*.
|
||||
#
|
||||
# Now, I understand that strictly speaking these are copyrighted material, but
|
||||
# they are so old, and so widely available, and are used here for educational
|
||||
# purposes, and with the upmost respect for all the original authors, and for
|
||||
# what 'the brand' represented for us at the time. With that in mind, I think
|
||||
# that there shouldn't be an issue. But if you, Mr&Mrs Apple Lawyer think
|
||||
# otherwise, please let me know, I'll remove them. Reluctantly. I'll cry&scream!
|
||||
.PHONY : roms
|
||||
define rom_to_h
|
||||
$(1) :
|
||||
if [ ! -f $$@ ]; then \
|
||||
echo "ROM file $$@ not found, relying on the exiting .h"; \
|
||||
touch $$@; \
|
||||
fi
|
||||
src/roms/$(2).h : $(1)
|
||||
if [ ! -s "$$<" ]; then \
|
||||
touch $$@; \
|
||||
else { echo "#pragma once"; \
|
||||
xxd -n $$(shell basename $$<|sed 's/_[0-9].*//') -i $$< |\
|
||||
sed 's/unsigned/static const unsigned/' ; \
|
||||
}>$$@ ; \
|
||||
fi
|
||||
|
||||
$(OBJ)/mii.o : src/roms/$(2).h
|
||||
roms: src/roms/$(2).h
|
||||
endef
|
||||
|
||||
# 38063e08c778503fc03ecebb979769e9 contrib/mii_rom_iiee_3420349b.bin
|
||||
$(eval $(call rom_to_h,contrib/mii_rom_iiee_3420349b.bin,mii_rom_iiee))
|
||||
# 9123fff3442c0e688cc6816be88dd4ab contrib/mii_rom_iiee_video_3420265a.bin
|
||||
$(eval $(call rom_to_h,contrib/mii_rom_iiee_video_3420265a.bin,mii_rom_iiee_video))
|
||||
# e0d67bb1aabe2030547b4cbdf3905b60 contrib/mii_rom_iic_3420033a.bin
|
||||
$(eval $(call rom_to_h,contrib/mii_rom_iic_3420033a.bin,mii_rom_iic))
|
||||
# 67c0d61ab0911183faf05270f881a97e contrib/mii_rom_ssc_3410065a.bin
|
||||
$(eval $(call rom_to_h,contrib/mii_rom_ssc_3410065a.bin,mii_rom_ssc))
|
||||
# 9123fff3442c0e688cc6816be88dd4ab contrib/mii_rom_iic_video_3410265a.bin
|
||||
$(eval $(call rom_to_h,contrib/mii_rom_iic_video_3410265a.bin,mii_rom_iic_video))
|
||||
|
||||
# This is the ROM file for the EEPROM card, with some games too...
|
||||
$(eval $(call rom_to_h,disks/GamesWithFirmware.po,mii_rom_epromcard))
|
||||
# And the smartport driver
|
||||
$(eval $(call rom_to_h,test/asm/mii_smartport_driver.bin,mii_rom_smartport))
|
||||
|
||||
clean :
|
||||
rm -rf $(O); make -C libmui clean; make -C libmish clean
|
||||
@@ -107,8 +170,12 @@ watch :
|
||||
|
||||
tests : $(BIN)/mii_test $(BIN)/mii_cpu_test $(BIN)/mii_asm
|
||||
|
||||
# Just the library for mii, not any of the UI stuff
|
||||
TEST_OBJ := ${patsubst %, ${OBJ}/%, ${notdir ${MII_SRC:.c=.o}}}
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q :=
|
||||
else
|
||||
Q := @
|
||||
endif
|
||||
|
||||
# Base test without the UI -- this re-include all C source in one big
|
||||
# executable, ignoring the existing .o files, just so we can have custom flags
|
||||
@@ -123,19 +190,18 @@ $(BIN)/mii_test :
|
||||
@echo " TEST" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} $@
|
||||
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LIB)/libmish.a
|
||||
|
||||
VPATH += test
|
||||
$(BIN)/mii_cpu_test : $(OBJ)/mii_cpu_test.o $(TEST_OBJ)
|
||||
$(OBJ)/mii_cpu_test.o : CFLAGS := -O0 -Og ${filter-out -O%, $(CFLAGS)}
|
||||
$(OBJ)/mii_cpu_test.o : CPPFLAGS += -DMII_TEST -DMII_65C02_DIRECT_ACCESS=0
|
||||
|
||||
# Assembler for the 6502
|
||||
$(BIN)/mii_asm : $(OBJ)/mii_asm.o $(TEST_OBJ)
|
||||
$(BIN)/mii_cpu_test : CFLAGS := -O0 -Og ${filter-out -O%, $(CFLAGS)}
|
||||
$(BIN)/mii_cpu_test : CPPFLAGS += -DMII_TEST -DMII_65C02_DIRECT_ACCESS=0
|
||||
$(BIN)/mii_cpu_test : test/mii_cpu_test.c src/mii_65c02*.c
|
||||
@echo " TEST" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} $@
|
||||
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
|
||||
|
||||
ifeq ($(V),1)
|
||||
Q :=
|
||||
else
|
||||
Q := @
|
||||
endif
|
||||
# Assembler for the 6502 -- it picks the .c it needs, no need for other objects
|
||||
|
||||
$(BIN)/mii_asm : test/mii_asm.c
|
||||
@echo " CC+LD" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} $@
|
||||
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
|
||||
|
||||
$(OBJ)/%.o : %.c | $(OBJ)
|
||||
@echo " CC" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} "$<"
|
||||
|
24
README.md
24
README.md
@@ -49,7 +49,7 @@ I wanted something:
|
||||
* RAMWorks III card, with 1MB of RAM
|
||||
* "Titan Accelerator //e" simulation, to turn on/off fast mode.
|
||||
* Terence's J Boldt [1MB ROM card](https://github.com/tjboldt/ProDOS-ROM-Drive), also because I own a couple!
|
||||
* Floppy Drive with WOZ 1/2 in read/write, NIB and DSK in read only.
|
||||
* Floppy Drive with **WOZ v1/v2**, **NIB** and **DSK** (all with read&write).
|
||||
* No dependencies (X11) OpenGL rendering
|
||||
* Built-in debugger (using telnet access)
|
||||
* Super cool looking UI!
|
||||
@@ -66,6 +66,7 @@ I wanted something:
|
||||
* libglu-dev
|
||||
* libx11-dev
|
||||
* libpixman-1-dev
|
||||
* pkg-config
|
||||
* Many of them will probably be installed already.
|
||||
* For more details on development, see [Compiling](docs/Compiling.md)
|
||||
* Then just type `make` and it should compile.
|
||||
@@ -74,7 +75,7 @@ I wanted something:
|
||||
|
||||
## Command line options
|
||||
If you run it with no options, and there are no config file, it will present
|
||||
you with a dialog to select the ROMs and the drives.
|
||||
you with a dialog to select the Cards and the disk images.
|
||||
|
||||
<center>
|
||||
<img src="docs/screen/screen_config.png" alt="Config dialog">
|
||||
@@ -91,6 +92,7 @@ You can also use the command line to specify them, and other options.
|
||||
-v, --verbose Verbose output
|
||||
-m, --mute Mute the speaker
|
||||
-vol, --volume <volume> Set speaker volume (0.0 to 10.0)
|
||||
--audio-off, --no-audio, --silent Disable audio output
|
||||
-speed, --speed <speed> Set the CPU speed in MHz
|
||||
-s, --slot <slot>:<driver> Specify a slot and driver
|
||||
Slot id is 1..7
|
||||
@@ -111,13 +113,14 @@ You can also use the command line to specify them, and other options.
|
||||
And the available drivers:
|
||||
|
||||
$ ./build-x86_64-linux-gnu/bin/mii_emu -L
|
||||
mii: available drivers:
|
||||
titan - Titan Accelerator IIe
|
||||
smartport - SmartPort card
|
||||
nsc - No Slot Clock
|
||||
mouse - Mouse card
|
||||
eecard - EEPROM 1MB card
|
||||
disk2 - Apple Disk ][
|
||||
mii: available drivers:
|
||||
titan - Titan Accelerator IIe
|
||||
ssc - Super Serial card
|
||||
smartport - SmartPort card
|
||||
nsc - No Slot Clock
|
||||
mouse - Mouse card
|
||||
eecard - EEPROM 1MB card
|
||||
disk2 - Apple Disk ][
|
||||
|
||||
## Key Bindings
|
||||
There are just a few keys that are mapped for anything useful. List is not exausive, but here are the main ones:
|
||||
@@ -158,7 +161,7 @@ There are just a few keys that are mapped for anything useful. List is not exaus
|
||||
|
||||
## What it cannot do
|
||||
* A2Desktop PT3 player doesn't see keypresses.
|
||||
* Thats' about it really, all the other things I tried work
|
||||
* That's about it really, all the other things I tried work
|
||||
* Joystick support is a bit limited, no 'mapping' I used a (USB) 8bitdo NES30 Pro, and it works, but it's not perfect. But, I can play choplifter with it, so it's good enough for now... *NOTE* Soon will have it's own config dialog to do mapping.
|
||||
|
||||
## What it could do with
|
||||
@@ -181,5 +184,6 @@ There are just a few keys that are mapped for anything useful. List is not exaus
|
||||
* Other bits were inspired by:
|
||||
* [bobbin](https://github.com/micahcowan/bobbin) which is newish as well, and is great for text mode, but I didn't like the fact its' all globals etc. I still borrowed the Floppy emulation from there, until I get around to do one.
|
||||
* [isapple2](https://github.com/ivanizag/izapple2/) for other bits and pieces.
|
||||
* [clemens_IIgs](https://github.com/samkusin/clemens_iigs) for the mockingboard code -- not working just yet.
|
||||
* And of course, countless books, articles and posts read over the last 40 years!
|
||||
|
||||
|
248
contrib/fifo_declare.h
Normal file
248
contrib/fifo_declare.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* fido_declare.h
|
||||
* Copyright (C) 2003-2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIFO implementation, aka circular buffers
|
||||
*
|
||||
* these macros define accessories for FIFOs of any name, type and
|
||||
* any (power of two) size.
|
||||
* This is entirely thread and MP safe, memory barriers are in place to
|
||||
* ensure that the read and write cursors and data are ordered correctly.
|
||||
*/
|
||||
|
||||
#ifndef __FIFO_DECLARE__
|
||||
#define __FIFO_DECLARE__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
doing a :
|
||||
DECLARE_FIFO(uint8_t, myfifo, 128);
|
||||
|
||||
will declare :
|
||||
enum : myfifo_overflow_f
|
||||
type : myfifo_t
|
||||
functions:
|
||||
// write a byte into the fifo, return 1 if there was room, 0 if there wasn't
|
||||
int myfifo_write(myfifo_t *c, uint8_t b);
|
||||
// reads a byte from the fifo, return 0 if empty. Use myfifo_isempty() to check beforehand
|
||||
uint8_t myfifo_read(myfifo_t *c);
|
||||
int myfifo_isfull(myfifo_t *c);
|
||||
int myfifo_isempty(myfifo_t *c);
|
||||
// returns number of items to read now
|
||||
uint16_t myfifo_get_read_size(myfifo_t *c);
|
||||
// read item at offset o from read cursor, no cursor advance
|
||||
uint8_t myfifo_read_at(myfifo_t *c, uint16_t o);
|
||||
// write b at offset o compared to current write cursor, no cursor advance
|
||||
void myfifo_write_at(myfifo_t *c, uint16_t o, uint8_t b);
|
||||
|
||||
In your .c you need to 'implement' the fifo:
|
||||
DEFINE_FIFO(uint8_t, myfifo)
|
||||
|
||||
To use the fifo, you must declare at least one :
|
||||
myfifo_t fifo = FIFO_NULL;
|
||||
|
||||
while (!myfifo_isfull(&fifo))
|
||||
myfifo_write(&fifo, 0xaa);
|
||||
....
|
||||
while (!myfifo_isempty(&fifo))
|
||||
b = myfifo_read(&fifo);
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h> // for memcpy
|
||||
#if __AVR__
|
||||
#define FIFO_CURSOR_TYPE uint8_t
|
||||
#define FIFO_BOOL_TYPE char
|
||||
#define FIFO_INLINE
|
||||
#define FIFO_SYNC
|
||||
#endif
|
||||
|
||||
#ifndef FIFO_CURSOR_TYPE
|
||||
#define FIFO_CURSOR_TYPE unsigned int
|
||||
#endif
|
||||
#ifndef FIFO_BOOL_TYPE
|
||||
#define FIFO_BOOL_TYPE int
|
||||
#endif
|
||||
#ifndef FIFO_INLINE
|
||||
#define FIFO_INLINE inline
|
||||
#endif
|
||||
|
||||
/* We should not need volatile */
|
||||
#ifndef FIFO_VOLATILE
|
||||
#define FIFO_VOLATILE
|
||||
#endif
|
||||
#ifndef FIFO_SYNC
|
||||
#define FIFO_SYNC __sync_synchronize()
|
||||
#endif
|
||||
|
||||
#ifndef FIFO_ZERO_INIT
|
||||
#define FIFO_ZERO_INIT {0}
|
||||
#endif
|
||||
#define FIFO_NULL {0}
|
||||
|
||||
/* New compilers don't like unused static functions. However,
|
||||
* we do like 'static inlines' for these small accessors,
|
||||
* so we mark them as 'unused'. It stops it complaining */
|
||||
#ifdef __GNUC__
|
||||
#define FIFO_DECL static __attribute__ ((unused))
|
||||
#else
|
||||
#define FIFO_DECL static
|
||||
#endif
|
||||
|
||||
#define DECLARE_FIFO(__type, __name, __size, __args...) \
|
||||
enum { __name##_fifo_size = (__size) }; \
|
||||
typedef struct __name##_t { \
|
||||
FIFO_VOLATILE FIFO_CURSOR_TYPE read; \
|
||||
FIFO_VOLATILE FIFO_CURSOR_TYPE write; \
|
||||
__args \
|
||||
__type buffer[__name##_fifo_size]; \
|
||||
} __name##_t
|
||||
|
||||
/*
|
||||
* This allow declaring a FIFO without the functions that use
|
||||
* pass-by-value arguments. The compiler isn't too happy using
|
||||
* some of the variable length array these days and outputs a warning.
|
||||
* Also, it is not efficient, so if your FIFO member is a big struct,
|
||||
* you should use the pass by pointer instead.
|
||||
*/
|
||||
#define DEFINE_PTR_FIFO(__type, __name) \
|
||||
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isfull(__name##_t *c)\
|
||||
{\
|
||||
FIFO_CURSOR_TYPE next = (c->write + 1) & (__name##_fifo_size-1);\
|
||||
return c->read == next;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_isempty(__name##_t * c)\
|
||||
{\
|
||||
return c->read == c->write;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_read_size(__name##_t *c)\
|
||||
{\
|
||||
return ((c->write + __name##_fifo_size) - c->read) & (__name##_fifo_size-1);\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE FIFO_CURSOR_TYPE __name##_get_write_size(__name##_t *c)\
|
||||
{\
|
||||
return (__name##_fifo_size-1) - __name##_get_read_size(c);\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE void __name##_read_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
|
||||
{\
|
||||
FIFO_SYNC; \
|
||||
c->read = (c->read + o) & (__name##_fifo_size-1);\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE void __name##_write_offset(__name##_t *c, FIFO_CURSOR_TYPE o)\
|
||||
{\
|
||||
FIFO_SYNC; \
|
||||
c->write = (c->write + o) & (__name##_fifo_size-1);\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE void __name##_reset(__name##_t *c)\
|
||||
{\
|
||||
FIFO_SYNC; \
|
||||
c->read = c->write = 0;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE __type * __name##_write_ptr(__name##_t * c) \
|
||||
{\
|
||||
return c->buffer + c->write;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE __type * __name##_read_ptr(__name##_t * c) \
|
||||
{\
|
||||
return c->buffer + c->read;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE unsigned int __name##_read_count(__name##_t * c, \
|
||||
unsigned int count, \
|
||||
__type array[count]) \
|
||||
{\
|
||||
FIFO_CURSOR_TYPE now = __name##_get_read_size(c);\
|
||||
if (count > now) \
|
||||
count = now;\
|
||||
if (!count) return 0;\
|
||||
if (c->read + count > __name##_fifo_size) { \
|
||||
unsigned int first = __name##_fifo_size - c->read; \
|
||||
memcpy(array, c->buffer + c->read, first * sizeof(__type)); \
|
||||
memcpy(array + first, c->buffer, (count - first) * sizeof(__type)); \
|
||||
} else { \
|
||||
memcpy(array, c->buffer + c->read, count * sizeof(__type)); \
|
||||
} \
|
||||
FIFO_SYNC; \
|
||||
c->read = (c->read + count) & (__name##_fifo_size-1); \
|
||||
return count;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE unsigned int __name##_write_count(__name##_t * c, \
|
||||
unsigned int count, \
|
||||
__type array[count]) \
|
||||
{\
|
||||
FIFO_CURSOR_TYPE now = __name##_get_write_size(c);\
|
||||
if (count > now) \
|
||||
count = now;\
|
||||
if (!count) return 0;\
|
||||
if (c->write + count > __name##_fifo_size) { \
|
||||
unsigned int first = __name##_fifo_size - c->write; \
|
||||
memcpy(c->buffer + c->write, array, first * sizeof(__type)); \
|
||||
memcpy(c->buffer, array + first, (count - first) * sizeof(__type)); \
|
||||
} else { \
|
||||
memcpy(c->buffer + c->write, array, count * sizeof(__type)); \
|
||||
} \
|
||||
FIFO_SYNC; \
|
||||
c->write = (c->write + count) & (__name##_fifo_size-1); \
|
||||
return count;\
|
||||
}\
|
||||
struct __name##_t
|
||||
|
||||
/*
|
||||
* And this declares the whole FIFO, including the pass-by-value functions
|
||||
*/
|
||||
#define DEFINE_FIFO(__type, __name) \
|
||||
DEFINE_PTR_FIFO(__type, __name); \
|
||||
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_write(__name##_t * c, __type b)\
|
||||
{\
|
||||
FIFO_CURSOR_TYPE now = c->write;\
|
||||
FIFO_CURSOR_TYPE next = (now + 1) & (__name##_fifo_size-1);\
|
||||
if (c->read != next) { \
|
||||
c->buffer[now] = b;\
|
||||
FIFO_SYNC; \
|
||||
c->write = next;\
|
||||
return 1;\
|
||||
}\
|
||||
return 0;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE __type __name##_read(__name##_t * c)\
|
||||
{\
|
||||
__type res = FIFO_ZERO_INIT; \
|
||||
FIFO_CURSOR_TYPE read = c->read;\
|
||||
if (read == c->write)\
|
||||
return res;\
|
||||
res = c->buffer[read];\
|
||||
FIFO_SYNC; \
|
||||
c->read = (read + 1) & (__name##_fifo_size-1);\
|
||||
return res;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE FIFO_BOOL_TYPE __name##_read_if_not_empty(__name##_t * c, __type *b)\
|
||||
{\
|
||||
FIFO_CURSOR_TYPE read = c->read;\
|
||||
if (read == c->write)\
|
||||
return 0;\
|
||||
*b = c->buffer[read];\
|
||||
FIFO_SYNC; \
|
||||
c->read = (read + 1) & (__name##_fifo_size-1);\
|
||||
return 1;\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE __type __name##_read_at(__name##_t *c, FIFO_CURSOR_TYPE o)\
|
||||
{\
|
||||
return c->buffer[(c->read + o) & (__name##_fifo_size-1)];\
|
||||
}\
|
||||
FIFO_DECL FIFO_INLINE void __name##_write_at(__name##_t *c, FIFO_CURSOR_TYPE o, __type b)\
|
||||
{\
|
||||
c->buffer[(c->write + o) & (__name##_fifo_size-1)] = b;\
|
||||
}\
|
||||
struct __name##_t
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
379
contrib/incbin.h
379
contrib/incbin.h
@@ -1,379 +0,0 @@
|
||||
/**
|
||||
* @file incbin.h
|
||||
* @author Dale Weiler
|
||||
* @brief Utility for including binary files
|
||||
*
|
||||
* Facilities for including binary files into the current translation unit and
|
||||
* making use from them externally in other translation units.
|
||||
*/
|
||||
#ifndef INCBIN_HDR
|
||||
#define INCBIN_HDR
|
||||
#include <limits.h>
|
||||
|
||||
// Michel addition:
|
||||
// Allow the included file to have an extra zero, to include text files
|
||||
// as plain zero terminated strings
|
||||
#ifdef INCBIN_TRAILING_ZERO
|
||||
#define INCBIN_TRAIL INCBIN_BYTE "0\n"
|
||||
#else
|
||||
#define INCBIN_TRAIL
|
||||
#endif
|
||||
|
||||
#if defined(__AVX512BW__) || \
|
||||
defined(__AVX512CD__) || \
|
||||
defined(__AVX512DQ__) || \
|
||||
defined(__AVX512ER__) || \
|
||||
defined(__AVX512PF__) || \
|
||||
defined(__AVX512VL__) || \
|
||||
defined(__AVX512F__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 6
|
||||
#elif defined(__AVX__) || \
|
||||
defined(__AVX2__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 5
|
||||
#elif defined(__SSE__) || \
|
||||
defined(__SSE2__) || \
|
||||
defined(__SSE3__) || \
|
||||
defined(__SSSE3__) || \
|
||||
defined(__SSE4_1__) || \
|
||||
defined(__SSE4_2__) || \
|
||||
defined(__neon__)
|
||||
# define INCBIN_ALIGNMENT_INDEX 4
|
||||
#elif ULONG_MAX != 0xffffffffu
|
||||
# define INCBIN_ALIGNMENT_INDEX 3
|
||||
# else
|
||||
# define INCBIN_ALIGNMENT_INDEX 2
|
||||
#endif
|
||||
|
||||
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
|
||||
#define INCBIN_ALIGN_SHIFT_0 1
|
||||
#define INCBIN_ALIGN_SHIFT_1 2
|
||||
#define INCBIN_ALIGN_SHIFT_2 4
|
||||
#define INCBIN_ALIGN_SHIFT_3 8
|
||||
#define INCBIN_ALIGN_SHIFT_4 16
|
||||
#define INCBIN_ALIGN_SHIFT_5 32
|
||||
#define INCBIN_ALIGN_SHIFT_6 64
|
||||
|
||||
/* Actual alignment value */
|
||||
#define INCBIN_ALIGNMENT \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
|
||||
INCBIN_ALIGNMENT_INDEX)
|
||||
|
||||
/* Stringize */
|
||||
#define INCBIN_STR(X) \
|
||||
#X
|
||||
#define INCBIN_STRINGIZE(X) \
|
||||
INCBIN_STR(X)
|
||||
/* Concatenate */
|
||||
#define INCBIN_CAT(X, Y) \
|
||||
X ## Y
|
||||
#define INCBIN_CONCATENATE(X, Y) \
|
||||
INCBIN_CAT(X, Y)
|
||||
/* Deferred macro expansion */
|
||||
#define INCBIN_EVAL(X) \
|
||||
X
|
||||
#define INCBIN_INVOKE(N, ...) \
|
||||
INCBIN_EVAL(N(__VA_ARGS__))
|
||||
|
||||
/* Green Hills uses a different directive for including binary data */
|
||||
#if defined(__ghs__)
|
||||
# if (__ghs_asm == 2)
|
||||
# define INCBIN_MACRO ".file"
|
||||
/* Or consider the ".myrawdata" entry in the ld file */
|
||||
# else
|
||||
# define INCBIN_MACRO "\tINCBIN"
|
||||
# endif
|
||||
#else
|
||||
# define INCBIN_MACRO ".incbin"
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define INCBIN_ALIGN \
|
||||
__attribute__((aligned(INCBIN_ALIGNMENT)))
|
||||
#else
|
||||
# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || /* GNU C and RealView */ \
|
||||
defined(__arm) || /* Diab */ \
|
||||
defined(_ARM) /* ImageCraft */
|
||||
# define INCBIN_ARM
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* Utilize .balign where supported */
|
||||
# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".balign 1\n"
|
||||
#elif defined(INCBIN_ARM)
|
||||
/*
|
||||
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
|
||||
* the shift count. This is the value passed to `.align'
|
||||
*/
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 0\n"
|
||||
#else
|
||||
/* We assume other inline assembler's treat `.align' as `.balign' */
|
||||
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
|
||||
# define INCBIN_ALIGN_BYTE ".align 1\n"
|
||||
#endif
|
||||
|
||||
/* INCBIN_CONST is used by incbin.c generated files */
|
||||
#if defined(__cplusplus)
|
||||
# define INCBIN_EXTERNAL extern "C"
|
||||
# define INCBIN_CONST extern const
|
||||
#else
|
||||
# define INCBIN_EXTERNAL extern
|
||||
# define INCBIN_CONST const
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Optionally override the linker section into which data is emitted.
|
||||
*
|
||||
* @warning If you use this facility, you'll have to deal with platform-specific linker output
|
||||
* section naming on your own
|
||||
*
|
||||
* Overriding the default linker output section, e.g for esp8266/Arduino:
|
||||
* @code
|
||||
* #define INCBIN_OUTPUT_SECTION ".irom.text"
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
* // Data is emitted into program memory that never gets copied to RAM
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_OUTPUT_SECTION)
|
||||
# if defined(__APPLE__)
|
||||
# define INCBIN_OUTPUT_SECTION ".const_data"
|
||||
# else
|
||||
# define INCBIN_OUTPUT_SECTION ".rodata"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
/* The directives are different for Apple branded compilers */
|
||||
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# define INCBIN_INT ".long "
|
||||
# define INCBIN_MANGLE "_"
|
||||
# define INCBIN_BYTE ".byte "
|
||||
# define INCBIN_TYPE(...)
|
||||
#else
|
||||
# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
|
||||
# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
|
||||
# if defined(__ghs__)
|
||||
# define INCBIN_INT ".word "
|
||||
# else
|
||||
# define INCBIN_INT ".int "
|
||||
# endif
|
||||
# if defined(__USER_LABEL_PREFIX__)
|
||||
# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
|
||||
# else
|
||||
# define INCBIN_MANGLE ""
|
||||
# endif
|
||||
# if defined(INCBIN_ARM)
|
||||
/* On arm assemblers, `@' is used as a line comment token */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
|
||||
# elif defined(__MINGW32__) || defined(__MINGW64__)
|
||||
/* Mingw doesn't support this directive either */
|
||||
# define INCBIN_TYPE(NAME)
|
||||
# else
|
||||
/* It's safe to use `@' on other architectures */
|
||||
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
|
||||
# endif
|
||||
# define INCBIN_BYTE ".byte "
|
||||
#endif
|
||||
|
||||
/* List of style types used for symbol names */
|
||||
#define INCBIN_STYLE_CAMEL 0
|
||||
#define INCBIN_STYLE_SNAKE 1
|
||||
|
||||
/**
|
||||
* @brief Specify the prefix to use for symbol names.
|
||||
*
|
||||
* By default this is `g', producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char gFooData[];
|
||||
* // const unsigned char *const gFooEnd;
|
||||
* // const unsigned int gFooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a prefix before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_PREFIX incbin
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols instead:
|
||||
* // const unsigned char incbinFooData[];
|
||||
* // const unsigned char *const incbinFooEnd;
|
||||
* // const unsigned int incbinFooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_PREFIX)
|
||||
# define INCBIN_PREFIX g
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Specify the style used for symbol names.
|
||||
*
|
||||
* Possible options are
|
||||
* - INCBIN_STYLE_CAMEL "CamelCase"
|
||||
* - INCBIN_STYLE_SNAKE "snake_case"
|
||||
*
|
||||
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
|
||||
* @code
|
||||
* #include "incbin.h"
|
||||
* INCBIN(Foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>FooData[];
|
||||
* // const unsigned char *const <prefix>FooEnd;
|
||||
* // const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*
|
||||
* If however you specify a style before including: e.g:
|
||||
* @code
|
||||
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
* #include "incbin.h"
|
||||
* INCBIN(foo, "foo.txt");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>foo_data[];
|
||||
* // const unsigned char *const <prefix>foo_end;
|
||||
* // const unsigned int <prefix>foo_size;
|
||||
* @endcode
|
||||
*/
|
||||
#if !defined(INCBIN_STYLE)
|
||||
# define INCBIN_STYLE INCBIN_STYLE_CAMEL
|
||||
#endif
|
||||
|
||||
/* Style lookup tables */
|
||||
#define INCBIN_STYLE_0_DATA Data
|
||||
#define INCBIN_STYLE_0_END End
|
||||
#define INCBIN_STYLE_0_SIZE Size
|
||||
#define INCBIN_STYLE_1_DATA _data
|
||||
#define INCBIN_STYLE_1_END _end
|
||||
#define INCBIN_STYLE_1_SIZE _size
|
||||
|
||||
/* Style lookup: returning identifier */
|
||||
#define INCBIN_STYLE_IDENT(TYPE) \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_STYLE_, \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_EVAL(INCBIN_STYLE), \
|
||||
INCBIN_CONCATENATE(_, TYPE)))
|
||||
|
||||
/* Style lookup: returning string literal */
|
||||
#define INCBIN_STYLE_STRING(TYPE) \
|
||||
INCBIN_STRINGIZE( \
|
||||
INCBIN_STYLE_IDENT(TYPE)) \
|
||||
|
||||
/* Generate the global labels by indirectly invoking the macro with our style
|
||||
* type and concatenating the name against them. */
|
||||
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_GLOBAL, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE))) \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_TYPE, \
|
||||
INCBIN_CONCATENATE( \
|
||||
NAME, \
|
||||
INCBIN_INVOKE( \
|
||||
INCBIN_STYLE_IDENT, \
|
||||
TYPE)))
|
||||
|
||||
/**
|
||||
* @brief Externally reference binary data included in another translation unit.
|
||||
*
|
||||
* Produces three external symbols that reference the binary data included in
|
||||
* another translation unit.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name given for the binary data
|
||||
*
|
||||
* @code
|
||||
* INCBIN_EXTERN(Foo);
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // extern const unsigned char <prefix>FooData[];
|
||||
* // extern const unsigned char *const <prefix>FooEnd;
|
||||
* // extern const unsigned int <prefix>FooSize;
|
||||
* @endcode
|
||||
*/
|
||||
#define INCBIN_EXTERN(NAME) \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(DATA))[]; \
|
||||
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(END)); \
|
||||
INCBIN_EXTERNAL const unsigned int \
|
||||
INCBIN_CONCATENATE( \
|
||||
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
|
||||
INCBIN_STYLE_IDENT(SIZE))
|
||||
|
||||
/**
|
||||
* @brief Include a binary file into the current translation unit.
|
||||
*
|
||||
* Includes a binary file into the current translation unit, producing three symbols
|
||||
* for objects that encode the data and size respectively.
|
||||
*
|
||||
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
|
||||
* "Data", as well as "End" and "Size" after. An example is provided below.
|
||||
*
|
||||
* @param NAME The name to associate with this binary data (as an identifier.)
|
||||
* @param FILENAME The file to include (as a string literal.)
|
||||
*
|
||||
* @code
|
||||
* INCBIN(Icon, "icon.png");
|
||||
*
|
||||
* // Now you have the following symbols:
|
||||
* // const unsigned char <prefix>IconData[];
|
||||
* // const unsigned char *const <prefix>IconEnd;
|
||||
* // const unsigned int <prefix>IconSize;
|
||||
* @endcode
|
||||
*
|
||||
* @warning This must be used in global scope
|
||||
* @warning The identifiers may be different if INCBIN_STYLE is not default
|
||||
*
|
||||
* To externally reference the data included by this in another translation unit
|
||||
* please @see INCBIN_EXTERN.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
INCBIN_EXTERN(NAME)
|
||||
#else
|
||||
#define INCBIN(NAME, FILENAME) \
|
||||
__asm__(INCBIN_SECTION \
|
||||
INCBIN_GLOBAL_LABELS(NAME, DATA) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
|
||||
INCBIN_MACRO " \"" FILENAME "\"\n" \
|
||||
INCBIN_TRAIL \
|
||||
INCBIN_GLOBAL_LABELS(NAME, END) \
|
||||
INCBIN_ALIGN_BYTE \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
|
||||
INCBIN_BYTE "1\n" \
|
||||
INCBIN_GLOBAL_LABELS(NAME, SIZE) \
|
||||
INCBIN_ALIGN_HOST \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
|
||||
INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
|
||||
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
|
||||
INCBIN_ALIGN_HOST \
|
||||
".text\n" \
|
||||
); \
|
||||
INCBIN_EXTERN(NAME)
|
||||
|
||||
#endif
|
||||
#endif
|
2609
contrib/sokol_audio.h
Normal file
2609
contrib/sokol_audio.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/Apple_logo_rainbow_version2_28x28.png
Normal file
BIN
docs/Apple_logo_rainbow_version2_28x28.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 633 B |
@@ -16,10 +16,26 @@ It evolved from the original mess, and I organized it into bits which would make
|
||||
* libgl-dev
|
||||
* libglu-dev
|
||||
* libx11-dev
|
||||
* pkg-config
|
||||
* Many of them will probably be installed already if you regulargly compile stuff.
|
||||
* Then just type `make` and it should compile.
|
||||
* To run it, just type `build-x86_64-linux-gnu/bin/mii_emu` and it should start.
|
||||
|
||||
### WSL
|
||||
MII Is reported to work on WSL, but I haven't tested it myself. You will need to install the X11 server for Windows, and set the DISPLAY variable to the correct value. I'm sure there are tutorials on the internet on how to do that.
|
||||
Also:
|
||||
* sudo apt install libasound2-plugins
|
||||
* nano ~/.asoundrc, and add these two lines:
|
||||
|
||||
```
|
||||
pcm.default pulse
|
||||
ctl.default pulse
|
||||
```
|
||||
|
||||
|
||||
* Restart WSL
|
||||
|
||||
|
||||
## Development
|
||||
* This is the first project I made using vscode as my main IDE; I used Eclipse for many years as the indexer was pretty much unbeatable (still is).
|
||||
* To support vscode I needed some tooling to create the '*compile_commands.json*' file, which is used by the C/C++ extension to provide intellisense. So to create that file, you use `make lsp` and it will create the file in the project directory.
|
||||
@@ -32,10 +48,11 @@ I have pretty consistent code style across my projects.
|
||||
* K&R style exclusively. None of that Allman/GNU horribleness.
|
||||
* GNU99 Dialect.
|
||||
* No Yoda, we are no longer in 1975. `if (0 == foo)` is just stupid.
|
||||
* I use !! to convert to 1 or zero from an expression. I call it the 'normalisation operator'. it is not commonly seen, apart from places like the Linux kernel.
|
||||
* I use "!!" to convert to 1 or zero from an expression. I call it the 'normalisation operator'. it is not commonly seen, apart from places like the Linux kernel.
|
||||
* I use *minipt.h* for protothreads. I like protothreads, and when you are aware of their limitations, they are a great tool. They are used for the video rendering, the no-slot-clock etc.
|
||||
|
||||
## What needs doing?
|
||||
* I'm sure there are bugs. I haven't tested it on a lot of hardware, and apart from quite a few games and a few known productivity app, it had had very little extensive testing. So testing!
|
||||
* In the code there are a few bits which needs fixing
|
||||
* The floppy driver still has issues writing, most notably it fails to 'bit copy' stuff when using Copy II plus. It's a timing issue, but I haven't found it yet.
|
||||
* The floppy driver still has issues writing, most notably it fails to 'bit copy' stuff when using Copy II plus. It's a timing issue, but I haven't found it yet.
|
||||
|
||||
|
@@ -16,15 +16,8 @@
|
||||
|
||||
#include "mii_rom_disk2.h"
|
||||
#include "mii_woz.h"
|
||||
#include "mii_floppy.h"
|
||||
#include "mii_disk2.h"
|
||||
|
||||
enum {
|
||||
// bits used to address the LSS ROM using lss_mode
|
||||
Q7_WRITE_BIT = 3,
|
||||
Q6_LOAD_BIT = 2,
|
||||
QA_BIT = 1,
|
||||
RP_BIT = 0,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIG_DR, // data register
|
||||
@@ -50,39 +43,13 @@ const char *sig_names[] = {
|
||||
"LSS_QA", "LSS_RP", "LSS_WB", "LSS_RANDOM", "WRITE_CYCLE",
|
||||
};
|
||||
|
||||
typedef struct mii_card_disk2_t {
|
||||
mii_t * mii;
|
||||
mii_dd_t drive[2];
|
||||
mii_floppy_t floppy[2];
|
||||
uint8_t selected;
|
||||
|
||||
uint8_t timer_off;
|
||||
uint8_t timer_lss;
|
||||
|
||||
uint8_t write_register;
|
||||
uint8_t head : 4; // bits are shifted in there
|
||||
uint16_t clock; // LSS clock cycles, read a bit when 0
|
||||
uint8_t lss_state : 4, // Sequence state
|
||||
lss_mode : 4; // WRITE/LOAD/SHIFT/QA/RP etc
|
||||
uint8_t lss_prev_state; // for write bit
|
||||
uint8_t lss_skip;
|
||||
uint8_t data_register;
|
||||
|
||||
uint64_t debug_last_write, debug_last_duration;
|
||||
mii_vcd_t *vcd;
|
||||
mii_signal_t *sig;
|
||||
} mii_card_disk2_t;
|
||||
|
||||
static void
|
||||
_mii_disk2_lss_tick(
|
||||
mii_card_disk2_t *c );
|
||||
static void
|
||||
_mii_disk2_vcd_debug(
|
||||
mii_card_disk2_t *c,
|
||||
int on);
|
||||
|
||||
// debug, used for mish, only supports one card tho (yet)
|
||||
static mii_card_disk2_t *_mish_d2 = NULL;
|
||||
mii_card_disk2_t *_mish_d2 = NULL;
|
||||
|
||||
/*
|
||||
* This timer is used to turn off the motor after a second
|
||||
@@ -228,23 +195,8 @@ _mii_disk2_access(
|
||||
mii_floppy_t * f = &c->floppy[c->selected];
|
||||
uint8_t ret = 0;
|
||||
|
||||
if (write) {
|
||||
// if (!(byte &0x80))
|
||||
// printf("WRITE PC:%04x %4.4x: %2.2x\n", mii->cpu.PC, addr, byte);
|
||||
c->write_register = byte;
|
||||
mii_raise_signal(c->sig + SIG_WR, byte);
|
||||
if (c->vcd) {
|
||||
uint8_t delta = c->vcd->cycle - c->debug_last_write;
|
||||
|
||||
mii_raise_signal_float(
|
||||
c->sig + SIG_WRITE_CYCLE, c->debug_last_duration != delta, 0);
|
||||
c->debug_last_duration = delta;
|
||||
c->debug_last_write = c->vcd->cycle;
|
||||
}
|
||||
}
|
||||
int psw = addr & 0x0F;
|
||||
int p = psw >> 1, on = psw & 1;
|
||||
int read = 0;
|
||||
switch (psw) {
|
||||
case 0x00 ... 0x07: {
|
||||
static const int8_t delta[4][4] = {
|
||||
@@ -288,7 +240,7 @@ _mii_disk2_access(
|
||||
unsigned int dstb = byte_index / MII_FLOPPY_HM_HIT_SIZE;
|
||||
f->heat->read.map[track_id][dstb] = 255;
|
||||
f->heat->read.seed++;
|
||||
read++;
|
||||
mii_raise_signal(c->sig + SIG_READ_DR, c->data_register);
|
||||
}
|
||||
break;
|
||||
case 0x0E:
|
||||
@@ -296,16 +248,53 @@ _mii_disk2_access(
|
||||
c->lss_mode = (c->lss_mode & ~(1 << Q7_WRITE_BIT)) | (!!on << Q7_WRITE_BIT);
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Here we run one LSS cycle ahead of 'schedule', just because it allows
|
||||
* the write protect bit to be loaded if needed, it *has* to run before
|
||||
* we return this value, so we marked a skip and run it here.
|
||||
*/
|
||||
// _mii_disk2_lss_tick(c);
|
||||
// c->lss_skip++;
|
||||
if (read)
|
||||
mii_raise_signal(c->sig + SIG_READ_DR, c->data_register);
|
||||
ret = on ? byte : c->data_register;
|
||||
switch (c->lss_mode & ((1 << Q6_LOAD_BIT) | (1 << Q7_WRITE_BIT))) {
|
||||
// off | off | Read data register
|
||||
case 0:
|
||||
ret = c->data_register;
|
||||
break;
|
||||
// on | off | Read status register
|
||||
case (1 << Q6_LOAD_BIT):
|
||||
if (write) {
|
||||
// printf("%s: IMW Write something register? %2x\n", __func__,byte);
|
||||
}
|
||||
ret = c->iwm_mode;
|
||||
break;
|
||||
// on | on | Write mode register (if drive is off)
|
||||
// data register (if drive is on)
|
||||
case (1 << Q6_LOAD_BIT) | (1 << Q7_WRITE_BIT):
|
||||
if (f->motor) {
|
||||
if (write) {
|
||||
// if (!(byte &0x80))
|
||||
// printf("WRITE PC:%04x %4.4x: %2.2x\n",
|
||||
// mii->cpu.PC, addr, byte);
|
||||
c->write_register = byte;
|
||||
if (c->vcd) {
|
||||
mii_raise_signal(c->sig + SIG_WR, byte);
|
||||
uint8_t delta = c->vcd->cycle - c->debug_last_write;
|
||||
|
||||
mii_raise_signal_float(
|
||||
c->sig + SIG_WRITE_CYCLE, c->debug_last_duration != delta, 0);
|
||||
c->debug_last_duration = delta;
|
||||
c->debug_last_write = c->vcd->cycle;
|
||||
}
|
||||
}
|
||||
ret = c->data_register;
|
||||
} else {
|
||||
if (write) {
|
||||
c->iwm_mode = byte;
|
||||
}
|
||||
ret = c->iwm_mode;
|
||||
}
|
||||
break;
|
||||
// off | on | Read handshake register
|
||||
case (1 << Q7_WRITE_BIT):
|
||||
// printf("%s read handshake register\n", __func__);
|
||||
// IWM ready and No Underrun
|
||||
ret = (1 << 7) | (1 << 6);
|
||||
break;
|
||||
}
|
||||
// ret = on ? byte : c->data_register;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -383,7 +372,7 @@ static mii_slot_drv_t _driver = {
|
||||
};
|
||||
MI_DRIVER_REGISTER(_driver);
|
||||
|
||||
static void
|
||||
void
|
||||
_mii_disk2_vcd_debug(
|
||||
mii_card_disk2_t *c,
|
||||
int on)
|
||||
@@ -520,7 +509,6 @@ static const uint8_t SEQUENCER_ROM_16[256] __attribute__((unused)) = {
|
||||
// 0 1 1 3 4 5 6 7 8 9 A B C D E F
|
||||
};
|
||||
|
||||
FILE *debug = NULL;
|
||||
|
||||
static void
|
||||
_mii_disk2_lss_tick(
|
||||
@@ -549,12 +537,20 @@ _mii_disk2_lss_tick(
|
||||
c->head = (c->head << 1) | bit;
|
||||
// see WOZ spec for how we do this here
|
||||
if ((c->head & 0xf) == 0) {
|
||||
bit = f->track_data[MII_FLOPPY_NOISE_TRACK][byte_index];
|
||||
rp = (bit >> bit_index) & 1;
|
||||
// printf("RANDOM TRACK %2d %2d %2d : %d\n",
|
||||
// track_id, byte_index, bit_index, bit);
|
||||
/* pick a random bit position for the random data */
|
||||
if (!f->random) {
|
||||
f->random = 1;
|
||||
f->random_position = random() % f->tracks[track_id].bit_count;
|
||||
}
|
||||
bit = f->track_data[MII_FLOPPY_NOISE_TRACK][f->random_position / 8];
|
||||
rp = (bit >> (f->random_position % 8)) & 1;
|
||||
f->random_position = (f->random_position + 1) % \
|
||||
f->tracks[track_id].bit_count;
|
||||
// printf("RANDOM TRACK %2d %2d %2d : %d\n",
|
||||
// track_id, byte_index, bit_index, rp);
|
||||
mii_raise_signal_float(c->sig + SIG_LSS_RANDOM, rp, 0);
|
||||
} else {
|
||||
f->random = 0;
|
||||
rp = (c->head >> 1) & 1;
|
||||
mii_raise_signal_float(c->sig + SIG_LSS_RANDOM, rp, 1);
|
||||
}
|
||||
@@ -596,7 +592,7 @@ _mii_disk2_lss_tick(
|
||||
c->data_register = c->write_register;
|
||||
mii_raise_signal(c->sig + SIG_DR, c->data_register);
|
||||
f->seed_dirty++;
|
||||
if (f->heat) {
|
||||
if (f->heat && track_id < MII_FLOPPY_TRACK_COUNT) {
|
||||
uint32_t byte_index = f->bit_position >> 3;
|
||||
unsigned int dstb = byte_index/MII_FLOPPY_HM_HIT_SIZE;
|
||||
f->heat->write.map[track_id][dstb] = 255;
|
||||
@@ -644,169 +640,3 @@ _mii_disk2_lss_tick(
|
||||
f->bit_position = (f->bit_position + 1) % f->tracks[track_id].bit_count;
|
||||
}
|
||||
}
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void
|
||||
_mii_mish_d2(
|
||||
void * param,
|
||||
int argc,
|
||||
const char * argv[])
|
||||
{
|
||||
// mii_t * mii = param;
|
||||
if (!_mish_d2) {
|
||||
printf("No Disk ][ card installed\n");
|
||||
return;
|
||||
}
|
||||
static int sel = 0;
|
||||
if (!argv[1] || !strcmp(argv[1], "list")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
mii_floppy_t *f = &c->floppy[i];
|
||||
printf("Drive %d %s\n", f->id, f->write_protected ? "WP" : "RW");
|
||||
printf("\tMotor: %3s qtrack:%3d Bit %6d/%6d\n",
|
||||
f->motor ? "ON" : "OFF", f->qtrack,
|
||||
f->bit_position, f->tracks[0].bit_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "sel")) {
|
||||
if (argv[2]) {
|
||||
sel = atoi(argv[2]);
|
||||
}
|
||||
printf("Selected drive: %d\n", sel);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "wp")) {
|
||||
if (argv[2]) {
|
||||
int wp = atoi(argv[2]);
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
f->write_protected = wp;
|
||||
}
|
||||
printf("Drive %d Write protected: %d\n", sel,
|
||||
_mish_d2->floppy[sel].write_protected);
|
||||
return;
|
||||
}
|
||||
// dump a track, specify track number and number of bytes
|
||||
if (!strcmp(argv[1], "track")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
if (argv[2]) {
|
||||
int track = atoi(argv[2]);
|
||||
if (track < 0 || track >= MII_FLOPPY_TRACK_COUNT) {
|
||||
printf("Invalid track %d\n", track);
|
||||
return;
|
||||
}
|
||||
int count = 256;
|
||||
if (argv[3]) {
|
||||
if (!strcmp(argv[3], "save")) {
|
||||
// save one binary file in tmp with just that track
|
||||
uint8_t *data = f->track_data[track];
|
||||
char *filename;
|
||||
asprintf(&filename, "/tmp/track_%02d.bin", track);
|
||||
int fd = open(filename, O_CREAT | O_WRONLY, 0666);
|
||||
write(fd, data, MII_FLOPPY_DEFAULT_TRACK_SIZE);
|
||||
close(fd);
|
||||
printf("Saved track %d to %s\n", track, filename);
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
count = atoi(argv[3]);
|
||||
}
|
||||
uint8_t *data = f->track_data[track];
|
||||
|
||||
for (int i = 0; i < count; i += 8) {
|
||||
uint8_t *line = data + i;
|
||||
#if 1
|
||||
for (int bi = 0; bi < 8; bi++) {
|
||||
uint8_t b = line[bi];
|
||||
for (int bbi = 0; bbi < 8; bbi++) {
|
||||
printf("%c", (b & 0x80) ? '1' : '0');
|
||||
b <<= 1;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
for (int bi = 0; bi < 8; bi++)
|
||||
printf("%8x", line[bi]);
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
printf("track <track 0-34> [count]\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "dirty")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
f->seed_dirty = f->seed_saved = rand();
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "resync")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
printf("Resyncing tracks\n");
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++)
|
||||
mii_floppy_resync_track(f, i, 0);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "map")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
|
||||
printf("Disk map:\n");
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
||||
mii_floppy_track_map_t map = {};
|
||||
int r = mii_floppy_map_track(f, i, &map, 0);
|
||||
printf("Track %2d: %7s\n",
|
||||
i, r == 0 ? "OK" : "Invalid");
|
||||
if (r != 0) {
|
||||
printf("Track %d has %5d bits\n", i, f->tracks[i].bit_count);
|
||||
for (int si = 0; si < 16; si++)
|
||||
printf("[%2d hs:%4d h:%5d ds:%3d d:%5d]%s",
|
||||
si, map.sector[si].hsync,
|
||||
map.sector[si].header,
|
||||
map.sector[si].dsync,
|
||||
map.sector[si].data,
|
||||
si & 1 ? "\n" : " ");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "trace")) {
|
||||
if (debug == NULL)
|
||||
debug = fopen("/tmp/disk2.log", "w");
|
||||
else {
|
||||
fclose(debug);
|
||||
debug = NULL;
|
||||
}
|
||||
printf("Debug trace %s\n", debug ? "ON" : "OFF");
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "vcd")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
_mii_disk2_vcd_debug(c, !c->vcd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#include "mish.h"
|
||||
|
||||
MISH_CMD_NAMES(d2, "d2");
|
||||
MISH_CMD_HELP(d2,
|
||||
"d2: disk ][ internals",
|
||||
" <default>: dump status",
|
||||
" list: list drives",
|
||||
" sel [0-1]: select drive",
|
||||
" wp [0-1]: write protect",
|
||||
" track <track 0-34> [count]: dump track",
|
||||
" track <track 0-34> save: save in /tmp/trackXX.bin",
|
||||
" dirty: mark track as dirty",
|
||||
" resync: resync all tracks",
|
||||
" map: show track map",
|
||||
" trace: toggle debug trace",
|
||||
" vcd: toggle VCD debug"
|
||||
);
|
||||
MII_MISH(d2, _mii_mish_d2);
|
||||
|
52
src/drivers/mii_disk2.h
Normal file
52
src/drivers/mii_disk2.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* mii_disk2.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "mii.h"
|
||||
#include "mii_floppy.h"
|
||||
|
||||
enum {
|
||||
// bits used to address the LSS ROM using lss_mode
|
||||
Q7_WRITE_BIT = 3,
|
||||
Q6_LOAD_BIT = 2,
|
||||
QA_BIT = 1,
|
||||
RP_BIT = 0,
|
||||
};
|
||||
|
||||
typedef struct mii_card_disk2_t {
|
||||
mii_t * mii;
|
||||
mii_dd_t drive[2];
|
||||
mii_floppy_t floppy[2];
|
||||
uint8_t selected;
|
||||
|
||||
uint8_t timer_off;
|
||||
uint8_t timer_lss;
|
||||
|
||||
uint8_t iwm_mode; // IWM mode register -- fake for now
|
||||
uint8_t write_register;
|
||||
uint8_t head : 4; // bits are shifted in there
|
||||
uint16_t clock; // LSS clock cycles, read a bit when 0
|
||||
uint8_t lss_state : 4, // Sequence state
|
||||
lss_mode : 4; // WRITE/LOAD/SHIFT/QA/RP etc
|
||||
uint8_t lss_prev_state; // for write bit
|
||||
uint8_t lss_skip;
|
||||
uint8_t data_register;
|
||||
|
||||
uint64_t debug_last_write, debug_last_duration;
|
||||
mii_vcd_t *vcd;
|
||||
mii_signal_t *sig;
|
||||
} mii_card_disk2_t;
|
||||
|
||||
|
||||
void
|
||||
_mii_disk2_vcd_debug(
|
||||
mii_card_disk2_t *c,
|
||||
int on);
|
245
src/drivers/mii_disk2_mish.c
Normal file
245
src/drivers/mii_disk2_mish.c
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* mii_disk2_mish.c
|
||||
*
|
||||
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE // for asprintf
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include "mii.h"
|
||||
#include "mii_bank.h"
|
||||
#include "mii_disk2.h"
|
||||
|
||||
|
||||
void
|
||||
mii_hexdump(
|
||||
const char * prompt,
|
||||
unsigned int display_offset,
|
||||
const void *buffer,
|
||||
unsigned int len);
|
||||
|
||||
extern mii_card_disk2_t *_mish_d2;
|
||||
|
||||
uint32_t
|
||||
mii_floppy_read_track_bits(
|
||||
mii_floppy_track_t * src,
|
||||
uint8_t * track_data,
|
||||
uint32_t pos,
|
||||
uint8_t count );
|
||||
|
||||
static void
|
||||
_mii_mish_d2(
|
||||
void * param,
|
||||
int argc,
|
||||
const char * argv[])
|
||||
{
|
||||
// mii_t * mii = param;
|
||||
if (!_mish_d2) {
|
||||
printf("No Disk ][ card installed\n");
|
||||
return;
|
||||
}
|
||||
static int sel = 0;
|
||||
if (!argv[1] || !strcmp(argv[1], "list")) {
|
||||
printf("LSS Status %02x Q6:%d Q7:%d\n", _mish_d2->lss_mode,
|
||||
!!(_mish_d2->lss_mode & (1 << Q6_LOAD_BIT)),
|
||||
!!(_mish_d2->lss_mode & (1 << Q7_WRITE_BIT)));
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
mii_floppy_t *f = &c->floppy[i];
|
||||
printf("Drive %d %s\n", f->id, f->write_protected ? "WP" : "RW");
|
||||
printf("\tMotor: %3s qtrack:%3d Bit %6d/%6d\n",
|
||||
f->motor ? "ON" : "OFF", f->qtrack,
|
||||
f->bit_position, f->tracks[0].bit_count);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "sel")) {
|
||||
if (argv[2]) {
|
||||
sel = atoi(argv[2]);
|
||||
}
|
||||
printf("Selected drive: %d\n", sel);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "wp")) {
|
||||
if (argv[2]) {
|
||||
int wp = atoi(argv[2]);
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
f->write_protected = wp;
|
||||
}
|
||||
printf("Drive %d Write protected: %d\n", sel,
|
||||
_mish_d2->floppy[sel].write_protected);
|
||||
return;
|
||||
}
|
||||
// dump a track, specify track number and number of bytes
|
||||
if (!strcmp(argv[1], "track")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
if (argv[2]) {
|
||||
int track = atoi(argv[2]);
|
||||
if (track < 0 || track >= MII_FLOPPY_TRACK_COUNT) {
|
||||
printf("Invalid track %d\n", track);
|
||||
return;
|
||||
}
|
||||
int count = 256;
|
||||
if (argv[3]) {
|
||||
if (!strcmp(argv[3], "save")) {
|
||||
// save one binary file in tmp with just that track
|
||||
uint8_t *data = f->track_data[track];
|
||||
char *filename;
|
||||
asprintf(&filename, "/tmp/track_%02d.bin", track);
|
||||
int fd = open(filename, O_CREAT | O_WRONLY, 0666);
|
||||
write(fd, data, MII_FLOPPY_MAX_TRACK_SIZE);
|
||||
close(fd);
|
||||
printf("Saved track %d to %s\n", track, filename);
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
count = atoi(argv[3]);
|
||||
}
|
||||
uint8_t *data = f->track_data[track];
|
||||
|
||||
for (int i = 0; i < count; i += 8) {
|
||||
uint8_t *line = data + i;
|
||||
#if 1
|
||||
for (int bi = 0; bi < 8; bi++) {
|
||||
uint8_t b = line[bi];
|
||||
for (int bbi = 0; bbi < 8; bbi++) {
|
||||
printf("%c", (b & 0x80) ? '1' : '0');
|
||||
b <<= 1;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
for (int bi = 0; bi < 8; bi++)
|
||||
printf("%8x", line[bi]);
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
printf("track <track 0-34> [count]\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "sector")) {
|
||||
// parameters are track then sector... dump the result as hex
|
||||
if (argv[2] && argv[3]) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
int track = atoi(argv[2]);
|
||||
int sector = atoi(argv[3]);
|
||||
if (track < 0 || track >= MII_FLOPPY_TRACK_COUNT) {
|
||||
printf("Invalid track %d\n", track);
|
||||
return;
|
||||
}
|
||||
if (sector < 0 || sector >= 16) {
|
||||
printf("Invalid sector %d\n", sector);
|
||||
return;
|
||||
}
|
||||
mii_floppy_track_map_t map = {};
|
||||
int r = mii_floppy_map_track(f, track, &map, 0);
|
||||
if (r != 0) {
|
||||
printf("Invalid track %d\n", track);
|
||||
return;
|
||||
}
|
||||
uint8_t sector_data[256];
|
||||
r = mii_floppy_read_sector(
|
||||
&f->tracks[track], f->track_data[track], &map,
|
||||
sector, sector_data);
|
||||
printf("Track %d Sector %d %s\n",
|
||||
track, sector, r == 0 ? "OK" : "Invalid");
|
||||
mii_hexdump("Sector", 0, sector_data, 256);
|
||||
} else {
|
||||
printf("sector <track 0-34> <sector 0-15>\n");
|
||||
}
|
||||
}
|
||||
if (!strcmp(argv[1], "dirty")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
f->seed_dirty = f->seed_saved = rand();
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "resync")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
printf("Resyncing tracks\n");
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++)
|
||||
mii_floppy_resync_track(f, i, 0);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "map")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
mii_floppy_t *f = &c->floppy[sel];
|
||||
|
||||
printf("Disk map:\n");
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
||||
mii_floppy_track_map_t map = {};
|
||||
int r = mii_floppy_map_track(f, i, &map, 0);
|
||||
printf("Track %2d: %7s\n",
|
||||
i, r == 0 ? "OK" : "Invalid");
|
||||
if (r != 0) {
|
||||
printf("Track %d has %5d bits\n", i, f->tracks[i].bit_count);
|
||||
for (int si = 0; si < 16; si++)
|
||||
printf("[%2d hs:%4d h:%5d ds:%3d d:%5d]%s",
|
||||
si, map.sector[si].hsync,
|
||||
map.sector[si].header,
|
||||
map.sector[si].dsync,
|
||||
map.sector[si].data,
|
||||
si & 1 ? "\n" : " ");
|
||||
}
|
||||
mii_floppy_track_map_t map2 = f->tracks[i].map;
|
||||
// do a deeper compare, not using memcmp
|
||||
for (int si = 0; si < 16; si++) {
|
||||
if (map.sector[si].header != map2.sector[si].header ||
|
||||
map.sector[si].data != map2.sector[si].data) {
|
||||
printf("Track %2d sector %2d mismatch\n", i, si);
|
||||
// display details
|
||||
printf(" %2d: h: %4d:%5d d: %3d:%5d %08x:%08x\n", si,
|
||||
map.sector[si].hsync, map.sector[si].header,
|
||||
map.sector[si].dsync, map.sector[si].data,
|
||||
mii_floppy_read_track_bits(&f->tracks[i], f->track_data[i],
|
||||
map.sector[si].header, 32),
|
||||
mii_floppy_read_track_bits(&f->tracks[i], f->track_data[i],
|
||||
map.sector[si].data, 32));
|
||||
printf(" %2d: h: %4d:%5d d: %3d:%5d %08x:%08x\n", si,
|
||||
map2.sector[si].hsync, map2.sector[si].header,
|
||||
map2.sector[si].dsync, map2.sector[si].data,
|
||||
mii_floppy_read_track_bits(&f->tracks[i], f->track_data[i],
|
||||
map2.sector[si].header, 32),
|
||||
mii_floppy_read_track_bits(&f->tracks[i], f->track_data[i],
|
||||
map2.sector[si].data, 32));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "vcd")) {
|
||||
mii_card_disk2_t *c = _mish_d2;
|
||||
_mii_disk2_vcd_debug(c, !c->vcd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#include "mish.h"
|
||||
|
||||
MISH_CMD_NAMES(d2, "d2");
|
||||
MISH_CMD_HELP(d2,
|
||||
"d2: disk ][ internals",
|
||||
" <default>: dump status",
|
||||
" list: list drives",
|
||||
" sel [0-1]: select drive",
|
||||
" wp [0-1]: write protect",
|
||||
" track <track 0-34> [count]: dump track",
|
||||
" track <track 0-34> save: save in /tmp/trackXX.bin",
|
||||
" dirty: mark track as dirty",
|
||||
" resync: resync all tracks",
|
||||
" map: show track map",
|
||||
" trace: toggle debug trace",
|
||||
" vcd: toggle VCD debug"
|
||||
);
|
||||
MII_MISH(d2, _mii_mish_d2);
|
@@ -21,12 +21,7 @@
|
||||
|
||||
#include "mii.h"
|
||||
#include "mii_bank.h"
|
||||
|
||||
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
#define INCBIN_PREFIX mii_
|
||||
#include "incbin.h"
|
||||
|
||||
INCBIN(1mb_rom, "disks/GamesWithFirmware.po");
|
||||
#include "mii_rom_epromcard.h"
|
||||
|
||||
typedef struct mii_card_ee_t {
|
||||
mii_dd_t drive[1];
|
||||
@@ -57,7 +52,7 @@ _mii_ee_init(
|
||||
mii_dd_register_drives(&mii->dd, c->drive, 1);
|
||||
|
||||
#if 1
|
||||
c->file = (uint8_t*)mii_1mb_rom_data;
|
||||
c->file = (uint8_t*)GamesWithFirmware_po;
|
||||
#else
|
||||
const char *fname = "disks/GamesWithFirmware.po";
|
||||
|
||||
@@ -124,7 +119,7 @@ _mii_ee_command(
|
||||
return -1;
|
||||
}
|
||||
mii_dd_drive_load(&c->drive[0], file);
|
||||
c->file = file ? file->map : (uint8_t*)mii_1mb_rom_data;
|
||||
c->file = file ? file->map : (uint8_t*)GamesWithFirmware_po;
|
||||
res = 0;
|
||||
} break;
|
||||
}
|
||||
|
@@ -5,13 +5,17 @@
|
||||
|
||||
#include "mii.h"
|
||||
#include "mockingboard.h"
|
||||
#include "mii_audio.h"
|
||||
|
||||
typedef struct mii_mb_t {
|
||||
mii_t * mii;
|
||||
uint8_t init; // init sequence bits
|
||||
bool init_done;
|
||||
uint8_t timer;
|
||||
struct mb_t *mb;
|
||||
mii_t * mii;
|
||||
uint8_t init; // init sequence bits
|
||||
bool init_done;
|
||||
uint8_t timer;
|
||||
struct mb_t * mb;
|
||||
mii_audio_source_t source;
|
||||
uint64_t flush_cycle_count;
|
||||
uint64_t last_flush_cycle;
|
||||
} mii_mb_t;
|
||||
|
||||
|
||||
@@ -22,17 +26,36 @@ _mii_mb_timer(
|
||||
{
|
||||
mii_mb_t * mb = param;
|
||||
mb_clock_t clock = {
|
||||
.ref_step = 1,
|
||||
.ts = mii->cpu.total_cycle,
|
||||
.ref_step = MB_CLOCKS_PHI0_CYCLE,
|
||||
.ts = mii->cpu.total_cycle * MB_CLOCKS_14MHZ_CYCLE,
|
||||
};
|
||||
// delta is ALWAYS negative or zero here
|
||||
int32_t delta = mii_timer_get(mii, mb->timer);
|
||||
uint64_t ret = -delta + 1;
|
||||
if (mb_io_sync(mb->mb, &clock)) {
|
||||
// printf("MB Sync IRQ\n");
|
||||
uint64_t res = 1 + -mii_timer_get(mii, mb->timer);
|
||||
|
||||
uint32_t irq = mb_io_sync(mb->mb, &clock);
|
||||
if (irq & MB_CARD_IRQ)
|
||||
mii->cpu_state.irq = 1;
|
||||
|
||||
if ((mii->cpu.total_cycle - mb->last_flush_cycle) >= mb->flush_cycle_count) {
|
||||
mb->last_flush_cycle = mii->cpu.total_cycle;
|
||||
|
||||
static float audio[1224] = {};
|
||||
memset(audio, 0, sizeof(audio));
|
||||
int r = mb_ay3_render(mb->mb, audio, 1024, 2, MII_AUDIO_FREQ);
|
||||
float min = 1e6, max = -1e6;
|
||||
mii_audio_frame_t *f = &mb->source.fifo;
|
||||
for (int i = 0; i < r * 2; i += 2) {
|
||||
mii_audio_sample_t s = (audio[i] + audio[i + 1]);
|
||||
// mii_audio_sample_t s = (audio[i]);
|
||||
if (s < min)
|
||||
min = s;
|
||||
if (s > max)
|
||||
max = s;
|
||||
mii_audio_frame_write(f, s);
|
||||
}
|
||||
printf("MB Audio cycle %ld r=%d min %.4f max %.4f\n",
|
||||
mb->flush_cycle_count, r, min, max);
|
||||
}
|
||||
return ret;
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -43,7 +66,10 @@ mii_mb_start(
|
||||
printf("MB Start\n");
|
||||
mb->init = 0;
|
||||
mb->init_done = true;
|
||||
mb->timer = mii_timer_register(mb->mii, _mii_mb_timer, mb, 1, __func__);
|
||||
mb->timer = mii_timer_register(mb->mii, _mii_mb_timer, mb, 1000, __func__);
|
||||
|
||||
mb->flush_cycle_count = 512 * mb->mii->audio.clk_per_sample;
|
||||
mii_audio_add_source(&mb->mii->audio, &mb->source);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -24,14 +24,8 @@
|
||||
#include "mii_bank.h"
|
||||
#include "mii_dd.h"
|
||||
#include "mii_slot.h"
|
||||
#include "mii_rom_smartport.h"
|
||||
|
||||
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
#define INCBIN_PREFIX mii_
|
||||
#include "incbin.h"
|
||||
|
||||
INCBIN(smartport_rom, "test/asm/mii_smartport_driver.bin");
|
||||
|
||||
extern const unsigned char mii_smartport_rom_data[];
|
||||
|
||||
#define MII_SM_DRIVE_COUNT 2
|
||||
|
||||
@@ -289,7 +283,7 @@ _mii_sm_init(
|
||||
uint16_t addr = 0xc100 + (slot->id * 0x100);
|
||||
mii_bank_write(
|
||||
&mii->bank[MII_BANK_CARD_ROM],
|
||||
addr, mii_smartport_rom_data, 256);
|
||||
addr, mii_smartport_driver_bin, 256);
|
||||
|
||||
uint8_t trap_hd = mii_register_trap(mii, _mii_hd_callback);
|
||||
uint8_t trap_sm = mii_register_trap(mii, _mii_sm_callback);
|
||||
|
@@ -43,11 +43,7 @@ make && A2_TTY=/dev/tntX ./surl-server
|
||||
#include "fifo_declare.h"
|
||||
#include "bsd_queue.h"
|
||||
|
||||
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
|
||||
#define INCBIN_PREFIX mii_
|
||||
#include "incbin.h"
|
||||
INCBIN(ssc_rom, "roms/mii_rom_scc_3410065a.bin");
|
||||
|
||||
#include "mii_rom_ssc.h"
|
||||
|
||||
#include <termios.h>
|
||||
#include <pty.h>
|
||||
@@ -363,7 +359,7 @@ _mii_ssc_select(
|
||||
_mii_ssc_thread_start(c);
|
||||
}
|
||||
}
|
||||
mii_bank_write(c->rom, 0xc800, mii_ssc_rom_data, 2048);
|
||||
mii_bank_write(c->rom, 0xc800, mii_rom_ssc, 2048);
|
||||
c->slot->aux_rom_selected = true;
|
||||
return false;
|
||||
}
|
||||
@@ -536,7 +532,7 @@ _mii_ssc_init(
|
||||
|
||||
uint16_t addr = 0xc100 + (slot->id * 0x100);
|
||||
c->rom = &mii->bank[MII_BANK_CARD_ROM];
|
||||
mii_bank_write(c->rom, addr, mii_ssc_rom_data + 7*256, 256);
|
||||
mii_bank_write(c->rom, addr, mii_rom_ssc + 7*256, 256);
|
||||
/*
|
||||
* install a callback that will be called for every access to the
|
||||
* ROM area, we need this to re-install the secondary part of the ROM
|
||||
|
@@ -577,6 +577,8 @@ _ay3_render( //
|
||||
float noise;
|
||||
uint envelope;
|
||||
|
||||
printf("ay3_render: duration=%d, channel=%d, out_limit=%d, samples_per_frame=%d, samples_per_second=%d\n", duration, channel, out_limit, samples_per_frame, samples_per_second);
|
||||
printf("render_window_secs=%f, sample_dt=%f, render_dt=%d\n", render_window_secs, sample_dt, render_dt);
|
||||
// TODO: we can just persist tone_period + half_tone_period instead of
|
||||
// frequency and trim back and forth calculations in _ay3_tone_setup
|
||||
for (render_t = 0.0f; render_t < render_window_secs && sample_count < out_limit;
|
||||
@@ -745,8 +747,8 @@ _ay3_update( //
|
||||
return;
|
||||
}
|
||||
|
||||
printf("AY3: reset_b=%c bdir=%c bc1=%c\n", reset_b ? '1' : '0',
|
||||
bdir ? '1' : '0', bc1 ? '1' : '0');
|
||||
// printf("AY3: reset_b=%c bdir=%c bc1=%c\n", reset_b ? '1' : '0',
|
||||
// bdir ? '1' : '0', bc1 ? '1' : '0');
|
||||
|
||||
switch (*bus_control & 0x3) {
|
||||
case 0x3:
|
||||
@@ -985,6 +987,7 @@ mb_io_sync( //
|
||||
|
||||
board->sync_time_budget += dt_clocks;
|
||||
|
||||
//printf("mb_io_sync: dt_clocks=%d, sync_time_budget=%d\n", dt_clocks, board->sync_time_budget);
|
||||
while (board->sync_time_budget > clock->ref_step) {
|
||||
_via_update_state(
|
||||
&board->via[0], &board->via_ay3_bus[0], &board->via_ay3_bus_control[0]);
|
||||
@@ -1003,10 +1006,10 @@ mb_io_sync( //
|
||||
}
|
||||
board->last_clocks = *clock;
|
||||
|
||||
return (_mmio_via_irq_active(&board->via[0]) ||
|
||||
_mmio_via_irq_active(&board->via[1])) ?
|
||||
MB_CARD_IRQ :
|
||||
0;
|
||||
uint32_t res = 0;
|
||||
res |= _mmio_via_irq_active(&board->via[1]) ? MB_CARD_IRQ : 0;
|
||||
res |= _mmio_via_irq_active(&board->via[0]) ? MB_CARD_IRQ : 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
|
@@ -82,6 +82,7 @@ struct mb_t;
|
||||
//#define MB_OP_IO_DEVSEL 0x80
|
||||
|
||||
#define MB_CARD_IRQ 0x80000000
|
||||
#define MB_CARD_NMI 0x40000000
|
||||
|
||||
//
|
||||
/** _clock_ is of type mb_clock_t
|
||||
|
252
src/format/mii_dsk.c
Normal file
252
src/format/mii_dsk.c
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* mii_dsk.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "mii_dsk.h"
|
||||
|
||||
#define NIBBLE_SECTOR_SIZE 416
|
||||
#define NIBBLE_TRACK_SIZE 6656
|
||||
#define DSK_SECTOR_SIZE 256
|
||||
//#define MAX_SECTORS 16
|
||||
#define VOLUME_NUMBER 254
|
||||
#define DSK_TRACK_SIZE (DSK_SECTOR_SIZE * MAX_SECTORS)
|
||||
|
||||
//static const size_t nib_disksz = 232960;
|
||||
//static const size_t dsk_disksz = 143360;
|
||||
|
||||
// DOS 3.3 Physical sector order (index is physical sector,
|
||||
// value is DOS sector)
|
||||
static const uint8_t DO[] = {
|
||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
|
||||
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
|
||||
};
|
||||
// ProDOS Physical sector order (index is physical sector,
|
||||
// value is ProDOS sector).
|
||||
static const uint8_t PO[] = {
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb,
|
||||
0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf
|
||||
};
|
||||
|
||||
const uint8_t TRANS62[] = {
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
};
|
||||
const int8_t DETRANS62[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x02, 0x03, 0x00, 0x04, 0x05, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08,
|
||||
0x00, 0x00, 0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
|
||||
0x00, 0x00, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
|
||||
0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E,
|
||||
0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x21,
|
||||
0x00, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x2A, 0x2B,
|
||||
0x00, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
|
||||
0x00, 0x00, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x00, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* take a normalized nibble sector, and decode it to a 256 byte sector.
|
||||
* return 0 if the checksum is correct, -1 if not.
|
||||
*/
|
||||
int
|
||||
mii_floppy_decode_sector(
|
||||
uint8_t data_sector[342 + 1],
|
||||
uint8_t data[256])
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t data2[0x56] = {};
|
||||
uint8_t last = 0;
|
||||
int val = 0;
|
||||
uint8_t didx = 0;
|
||||
uint8_t *src = data_sector;
|
||||
|
||||
for (int jdx = 0x55; jdx >= 0; jdx--) {
|
||||
didx = *src++ - 0x80;
|
||||
val = DETRANS62[didx] ^ last;
|
||||
data2[jdx] = val;
|
||||
last = val;
|
||||
}
|
||||
for (int jdx = 0; jdx < 0x100; jdx++) {
|
||||
didx = *src++ - 0x80;
|
||||
val = DETRANS62[didx] ^ last;
|
||||
data[jdx] = val;
|
||||
last = val;
|
||||
}
|
||||
didx = *src++ - 0x80;
|
||||
uint8_t checkSum = DETRANS62[didx] ^ last;
|
||||
if (checkSum)
|
||||
res = -1;
|
||||
for (int kdx = 0, jdx = 0x55; kdx < 0x100; kdx++) {
|
||||
data[kdx] <<= 1;
|
||||
if ((data2[jdx] & 0x01) != 0) {
|
||||
data[kdx] |= 0x01;
|
||||
}
|
||||
data2[jdx] >>= 1;
|
||||
data[kdx] <<= 1;
|
||||
if ((data2[jdx] & 0x01) != 0) {
|
||||
data[kdx] |= 0x01;
|
||||
}
|
||||
data2[jdx] >>= 1;
|
||||
if (--jdx < 0)
|
||||
jdx = 0x55;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// This function is derived from Scullin Steel Co.'s apple2js code
|
||||
// https://github.com/whscullin/apple2js/blob/e280c3d/js/formats/format_utils.ts#L140
|
||||
/* Further recycled for MII .DSK decoding, using 10 bits sync words etc. */
|
||||
static void
|
||||
mii_floppy_dsk_render_sector(
|
||||
uint8_t vol, uint8_t track, uint8_t sector,
|
||||
const uint8_t *data,
|
||||
mii_floppy_track_t *dst,
|
||||
uint8_t * track_data )
|
||||
{
|
||||
unsigned int gap;
|
||||
|
||||
// if (track == 0 )
|
||||
// printf("NIB: vol %d track %d sector %d pos %5d\n",
|
||||
// vol, track, sector, dst->bit_count);
|
||||
gap = sector == 0 ? 100 : track == 0 ? 20 : 20;
|
||||
uint32_t pos = dst->bit_count;
|
||||
for (uint8_t i = 0; i < gap; ++i)
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xFF << 2, 10);
|
||||
dst->map.sector[sector].hsync = dst->bit_count - pos;
|
||||
dst->map.sector[sector].header = dst->bit_count;
|
||||
// Address Field
|
||||
const uint8_t checksum = vol ^ track ^ sector;
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xd5aa96, 24);
|
||||
mii_floppy_write_track_bits(dst, track_data, (vol >> 1) | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, vol | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, (track >> 1) | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, track | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, (sector >> 1) | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, sector | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, (checksum >> 1) | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, checksum | 0xAA, 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xdeaaeb, 24);
|
||||
pos = dst->bit_count;
|
||||
// Gap 2 (5)
|
||||
for (int i = 0; i < 5; ++i)
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xFF << 2, 10);
|
||||
// total 48 bits sync, which helps keeping us byte aligned on the data
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xFF, 8);
|
||||
dst->map.sector[sector].dsync = dst->bit_count - pos;
|
||||
dst->map.sector[sector].data = dst->bit_count;
|
||||
// printf("Track %2d sector %2d pos %5d %s\n", track, sector, dst->bit_count,
|
||||
// dst->bit_count % 8 == 0 ? "" : "NOT BYTE ALIGNED");
|
||||
// Data Field
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xd5aaad, 24);
|
||||
uint8_t nibbles[0x156] = {};
|
||||
const unsigned ptr2 = 0;
|
||||
const unsigned ptr6 = 0x56;
|
||||
|
||||
int i2 = 0x55;
|
||||
for (int i6 = 0x101; i6 >= 0; --i6) {
|
||||
uint8_t val6 = data[i6 % 0x100];
|
||||
uint8_t val2 = nibbles[ptr2 + i2];
|
||||
val2 = (val2 << 1) | (val6 & 1); val6 >>= 1;
|
||||
val2 = (val2 << 1) | (val6 & 1); val6 >>= 1;
|
||||
nibbles[ptr6 + i6] = val6;
|
||||
nibbles[ptr2 + i2] = val2;
|
||||
if (--i2 < 0)
|
||||
i2 = 0x55;
|
||||
}
|
||||
uint8_t last = 0;
|
||||
// get a CRC for that sector before we write it
|
||||
dst->map.sector[sector].crc = mii_floppy_crc(-1, nibbles, 342);
|
||||
for (int i = 0; i < 342; ++i) {
|
||||
const uint8_t val = nibbles[i];
|
||||
mii_floppy_write_track_bits(dst, track_data, TRANS62[last ^ val], 8);
|
||||
last = val;
|
||||
}
|
||||
mii_floppy_write_track_bits(dst, track_data, TRANS62[last], 8);
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xdeaaeb, 24);
|
||||
// Gap 3
|
||||
mii_floppy_write_track_bits(dst, track_data, 0xFF << 2, 10);
|
||||
}
|
||||
|
||||
void
|
||||
_mii_floppy_dsk_write_sector(
|
||||
mii_dd_file_t *file,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t track_id,
|
||||
uint8_t sector,
|
||||
uint8_t data_sector[342 + 1] )
|
||||
{
|
||||
uint8_t data[256];
|
||||
|
||||
int errors = mii_floppy_decode_sector(data_sector, data);
|
||||
if (errors) {
|
||||
printf("%s: T %2d S %2d has errors -- not writing sector\n",
|
||||
__func__, track_id, sector);
|
||||
} else {
|
||||
printf("%s: T %2d S %2d has changed, writing sector\n",
|
||||
__func__, track_id, sector);
|
||||
memcpy(file->map + map->sector[sector].dsk_position, data, 256);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mii_floppy_dsk_load(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file )
|
||||
{
|
||||
const char *filename = basename(file->pathname);
|
||||
|
||||
const char *ext = rindex(filename, '.');
|
||||
ext = ext ? ext+1 : "";
|
||||
const uint8_t * secmap = DO;
|
||||
if (!strcasecmp(ext, "PO")) {
|
||||
printf("%s opening %s as PO.\n", __func__, filename);
|
||||
secmap = PO;
|
||||
} else {
|
||||
printf("%s opening %s as DO.\n", __func__, filename);
|
||||
}
|
||||
for (int i = 0; i < 35; ++i) {
|
||||
mii_floppy_track_t *dst = &f->tracks[i];
|
||||
uint8_t *track_data = f->track_data[i];
|
||||
dst->bit_count = 0;
|
||||
dst->virgin = 0;
|
||||
dst->has_map = 1; // being filled by nibblize_sector
|
||||
for (int phys_sector = 0; phys_sector < 16; phys_sector++) {
|
||||
const uint8_t dos_sector = secmap[phys_sector];
|
||||
uint32_t off = ((16 * i + dos_sector) * DSK_SECTOR_SIZE);
|
||||
uint8_t *src = file->map + off;
|
||||
mii_floppy_dsk_render_sector(VOLUME_NUMBER, i, phys_sector,
|
||||
src, dst, track_data);
|
||||
dst->map.sector[phys_sector].dsk_position = off;
|
||||
}
|
||||
if (i == 0)
|
||||
printf("%s: track %2d has %d bits %d bytes\n",
|
||||
__func__, i, dst->bit_count, dst->bit_count >> 3);
|
||||
}
|
||||
// DSK is read only
|
||||
// f->write_protected |= MII_FLOPPY_WP_RO_FORMAT;
|
||||
|
||||
return 0;
|
||||
}
|
29
src/format/mii_dsk.h
Normal file
29
src/format/mii_dsk.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* mii_dsk.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mii_floppy.h"
|
||||
|
||||
int
|
||||
mii_floppy_dsk_load(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file );
|
||||
void
|
||||
_mii_floppy_dsk_write_sector(
|
||||
mii_dd_file_t *file,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t track_id,
|
||||
uint8_t sector,
|
||||
uint8_t data_sector[342 + 1] );
|
||||
int
|
||||
mii_floppy_decode_sector(
|
||||
uint8_t data_sector[342 + 1],
|
||||
uint8_t data[256]);
|
@@ -13,6 +13,39 @@
|
||||
|
||||
#include "mii_floppy.h"
|
||||
#include "mii_woz.h"
|
||||
#include "mii_dsk.h"
|
||||
#include "mii_nib.h"
|
||||
|
||||
typedef void (*mii_floppy_write_sector_cb)(
|
||||
mii_dd_file_t *file,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t track_id,
|
||||
uint8_t sector,
|
||||
uint8_t data_sector[342 + 1] );
|
||||
|
||||
|
||||
uint16_t
|
||||
mii_floppy_crc( //
|
||||
uint16_t crc, // Initial value
|
||||
uint8_t *data,
|
||||
size_t length)
|
||||
{
|
||||
// uint16_t crc = 0xFFFF; // Initial value
|
||||
const uint16_t polynomial = 0x1021; // Polynomial used in CRC16-CCITT
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
crc ^= (data[i] << 8); // Move the byte into the CRC-16's high byte
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
if (crc & 0x8000) { // If the top bit is set, shift left
|
||||
crc = (crc << 1) ^ polynomial; // and XOR with polynomial
|
||||
} else {
|
||||
crc = crc << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void
|
||||
mii_floppy_init(
|
||||
@@ -50,7 +83,7 @@ mii_floppy_init(
|
||||
}
|
||||
// copy all that random stuff across the rest of the 'track'
|
||||
int rbi = 0;
|
||||
for (int bi = 256; bi < MII_FLOPPY_DEFAULT_TRACK_SIZE; bi++)
|
||||
for (int bi = 256; bi < MII_FLOPPY_MAX_TRACK_SIZE; bi++)
|
||||
random[bi] = random[rbi++ % 256];
|
||||
// important, the +1 means we initialize the random track too
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT + 1; i++) {
|
||||
@@ -64,21 +97,21 @@ mii_floppy_init(
|
||||
uint8_t *track = f->track_data[i];
|
||||
if (i != MII_FLOPPY_NOISE_TRACK) {
|
||||
#if 0
|
||||
memset(track, 0, MII_FLOPPY_DEFAULT_TRACK_SIZE);
|
||||
memset(track, 0, MII_FLOPPY_MAX_TRACK_SIZE);
|
||||
#else
|
||||
for (int bi = 0; bi < MII_FLOPPY_DEFAULT_TRACK_SIZE; bi++)
|
||||
for (int bi = 0; bi < MII_FLOPPY_MAX_TRACK_SIZE; bi++)
|
||||
track[bi] = random[rbi++ % 256];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mii_track_write_bits(
|
||||
mii_floppy_track_t * dst,
|
||||
uint8_t * track_data,
|
||||
uint32_t bits,
|
||||
uint8_t count )
|
||||
void
|
||||
mii_floppy_write_track_bits(
|
||||
mii_floppy_track_t * dst,
|
||||
uint8_t * track_data,
|
||||
uint32_t bits,
|
||||
uint8_t count )
|
||||
{
|
||||
while (count--) {
|
||||
uint32_t byte_index = dst->bit_count >> 3;
|
||||
@@ -89,15 +122,16 @@ mii_track_write_bits(
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
mii_track_read_bits(
|
||||
uint32_t
|
||||
mii_floppy_read_track_bits(
|
||||
mii_floppy_track_t * src,
|
||||
uint8_t * track_data,
|
||||
uint32_t pos,
|
||||
uint8_t count )
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
while (count--) {
|
||||
// align on 8 bits boudary
|
||||
while (count && (pos & 7) != 0) {
|
||||
pos = pos % src->bit_count;
|
||||
uint32_t byte_index = pos >> 3;
|
||||
uint8_t bit_index = 7 - (pos & 7);
|
||||
@@ -105,10 +139,124 @@ mii_track_read_bits(
|
||||
bits |= !!(track_data[byte_index] & (1 << bit_index));
|
||||
// we CAN have a wrap around here, but it's ok
|
||||
pos++;
|
||||
count--;
|
||||
}
|
||||
// now we can read 8 bits at a time
|
||||
while (count >= 8) {
|
||||
pos = pos % src->bit_count;
|
||||
uint32_t byte_index = pos >> 3;
|
||||
bits = (bits << 8) | track_data[byte_index];
|
||||
pos += 8;
|
||||
count -= 8;
|
||||
}
|
||||
// get any remaining bits
|
||||
while (count) {
|
||||
pos = pos % src->bit_count;
|
||||
uint32_t byte_index = pos >> 3;
|
||||
uint8_t bit_index = 7 - (pos & 7);
|
||||
bits <<= 1;
|
||||
bits |= !!(track_data[byte_index] & (1 << bit_index));
|
||||
// we CAN have a wrap around here, but it's ok
|
||||
pos++;
|
||||
count--;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
/*
|
||||
* At least PRODOS doesn't necessarily write the sector data in exactly
|
||||
* the same place as it was loaded from... so a valid sector map when loading
|
||||
* is not nessarily exact when saving.
|
||||
* This code check the data header, and if it is not 0xd5aaad, we look back
|
||||
* a few bit then forward until we find it again, and update the data position
|
||||
* in the sector map.
|
||||
*/
|
||||
static int
|
||||
mii_floppy_realign_sector_map(
|
||||
mii_floppy_track_t *track,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t sector )
|
||||
{
|
||||
// now it is entirely possible that the sector has moved a bit,
|
||||
// at least prodos does that, so we need to re-find it.
|
||||
uint32_t win = mii_floppy_read_track_bits(
|
||||
track, track_data, map->sector[sector].data, 24);
|
||||
if (win != 0xd5aaad) {
|
||||
// printf("%s: track %2d sector %2d has moved %08x\n",
|
||||
// __func__, track_id, i,
|
||||
// mii_floppy_read_track_bits(track, track_data, map->sector[sector]].data, 24));
|
||||
|
||||
uint32_t pos = map->sector[sector].data;
|
||||
win = mii_floppy_read_track_bits(
|
||||
track, track_data, pos - 24, 24);
|
||||
for (int j = 0; j < 100; j++) {
|
||||
win = (win << 1) | mii_floppy_read_track_bits(track, track_data, pos, 1);
|
||||
win &= 0xffffff;
|
||||
if (win == 0xd5aaad) {
|
||||
// printf("%s: track %2d sector %2d found at %d (was %d)\n",
|
||||
// __func__, track_id, i, pos, map->sector[sector].data);
|
||||
map->sector[sector].data = pos - 23;
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return win == 0xd5aaad ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This reads the sector nibbles. I realign all to 8 bits, and also skips
|
||||
* any spurious zero bits that might be in the data.
|
||||
* Return the number of spurious zero bits found. This is not necessarily
|
||||
* an error.
|
||||
*/
|
||||
int
|
||||
mii_floppy_read_sector_nibbles(
|
||||
mii_floppy_track_t *track,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t sector,
|
||||
uint8_t data_sector[342 + 1] )
|
||||
{
|
||||
// if the data is not byte aligned, we have to
|
||||
// read the whole lot, this will align it to 8 bits
|
||||
int j = 0, bit = map->sector[sector].data + (3 * 8), errors = 0;
|
||||
while (j < 342 + 1) {
|
||||
uint8_t b = mii_floppy_read_track_bits(track, track_data, bit, 8);
|
||||
while (!(b & 0x80)) {
|
||||
errors++;
|
||||
bit++;
|
||||
b = mii_floppy_read_track_bits(track, track_data, bit, 8);
|
||||
}
|
||||
bit += 8;
|
||||
data_sector[j++] = b;
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
int
|
||||
mii_floppy_read_sector(
|
||||
mii_floppy_track_t *track,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t sector,
|
||||
uint8_t data[256] )
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
uint8_t data_sector[342 + 1];
|
||||
|
||||
int errors = mii_floppy_read_sector_nibbles(
|
||||
track, track_data, map, sector, data_sector);
|
||||
if (errors)
|
||||
printf("%s warning sector %d has %d spurious zero bits\n",
|
||||
__func__, sector, errors);
|
||||
res = mii_floppy_decode_sector(data_sector, data);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* given a track a a starting position, look for a run of 0b1111111100,
|
||||
* return the number of sync bits found, and also update the position to
|
||||
@@ -131,29 +279,29 @@ mii_floppy_find_next_sync(
|
||||
do {
|
||||
do {
|
||||
window = (window << 1) |
|
||||
mii_track_read_bits(src, track_data, pos, 1);
|
||||
mii_floppy_read_track_bits(src, track_data, pos, 1);
|
||||
pos++;
|
||||
if ((window & 0x3ff) == 0b1111111100)
|
||||
break;
|
||||
} while (tries-- > 0 );
|
||||
wi = 10;
|
||||
if (mii_track_read_bits(src, track_data, pos, 1) == 0) {
|
||||
if (mii_floppy_read_track_bits(src, track_data, pos, 1) == 0) {
|
||||
pos++;
|
||||
wi++;
|
||||
}
|
||||
do {
|
||||
uint16_t w = mii_track_read_bits(src, track_data, pos + wi, 9);
|
||||
uint16_t w = mii_floppy_read_track_bits(src, track_data, pos + wi, 9);
|
||||
if (w == 0b111111110)
|
||||
wi += 9;
|
||||
else if ((w & 0b111111110) == 0b111111110) {
|
||||
wi += 8;
|
||||
break;
|
||||
}
|
||||
if (mii_track_read_bits(src, track_data, pos + wi, 1) == 0) {
|
||||
if (mii_floppy_read_track_bits(src, track_data, pos + wi, 1) == 0) {
|
||||
wi++;
|
||||
} else
|
||||
break;
|
||||
if (mii_track_read_bits(src, track_data, pos + wi, 1) == 0) {
|
||||
if (mii_floppy_read_track_bits(src, track_data, pos + wi, 1) == 0) {
|
||||
wi++;
|
||||
}
|
||||
} while (tries-- > 0 && wi < 2000);
|
||||
@@ -170,8 +318,6 @@ mii_floppy_find_next_sync(
|
||||
return wi;
|
||||
}
|
||||
|
||||
#define DE44(a, b) ((((a) & 0x55) << 1) | ((b) & 0x55))
|
||||
|
||||
|
||||
/*
|
||||
* this creates a sector+data map of a bitstream, and returns the positions
|
||||
@@ -197,7 +343,7 @@ mii_floppy_map_track(
|
||||
int pass = 0;
|
||||
do {
|
||||
wi = mii_floppy_find_next_sync(src, track_data, &pos);
|
||||
uint32_t header = mii_track_read_bits(src, track_data, pos, 24);
|
||||
uint32_t header = mii_floppy_read_track_bits(src, track_data, pos, 24);
|
||||
if (wi == 0) {
|
||||
printf("T%2d pos:%5d hmap:%04x dmap:%04x done?\n",
|
||||
track_id, pos, hmap, dmap);
|
||||
@@ -229,9 +375,9 @@ mii_floppy_map_track(
|
||||
track_id, wi, pos, src->bit_count, header);
|
||||
uint8_t hb[8];
|
||||
for (int hi = 0; hi < 8; hi++)
|
||||
hb[hi] = mii_track_read_bits(src, track_data,
|
||||
hb[hi] = mii_floppy_read_track_bits(src, track_data,
|
||||
pos + 24 + (hi * 8), 8);
|
||||
uint32_t tailer = mii_track_read_bits(src, track_data,
|
||||
uint32_t tailer = mii_floppy_read_track_bits(src, track_data,
|
||||
pos + 24 + (8 * 8), 20);
|
||||
uint8_t vol = DE44(hb[0], hb[1]);
|
||||
uint8_t track = DE44(hb[2], hb[3]);
|
||||
@@ -274,10 +420,10 @@ mii_floppy_map_track(
|
||||
// track, sector, vol, chk, want, tailer);
|
||||
pos += 24 + 8 * 8 + 24; // skip the whole header
|
||||
|
||||
if (track_id == 0 && sector == 0) {
|
||||
if (0 && track_id == 0 && sector == 0) {
|
||||
printf("pos %5d/%5d\n", pos, src->bit_count);
|
||||
for (int bi = 0; bi < 10; bi++) {
|
||||
uint32_t bits = mii_track_read_bits(src, track_data,
|
||||
uint32_t bits = mii_floppy_read_track_bits(src, track_data,
|
||||
pos + (bi * 10), 10);
|
||||
printf("%010b\n", bits);
|
||||
}
|
||||
@@ -304,9 +450,10 @@ get_new_sync:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* This reposition the sector 0 to the beginning of the track,
|
||||
hopefully also realign the nibbles to something readable.
|
||||
See Sather 9-28 for details
|
||||
/*
|
||||
* This reposition the sector 0 to the beginning of the track,
|
||||
* hopefully also realign the nibbles to something readable.
|
||||
* See Sather 9-28 for details
|
||||
*/
|
||||
void
|
||||
mii_floppy_resync_track(
|
||||
@@ -342,385 +489,67 @@ mii_floppy_resync_track(
|
||||
uint8_t *new_track = malloc(6656);
|
||||
while (new.bit_count < src->bit_count) {
|
||||
int cnt = src->bit_count - new.bit_count > 32 ? 32 : src->bit_count - new.bit_count;
|
||||
uint32_t bits = mii_track_read_bits(src, track_data, pos, cnt);
|
||||
mii_track_write_bits(&new, new_track, bits, cnt);
|
||||
uint32_t bits = mii_floppy_read_track_bits(src, track_data, pos, cnt);
|
||||
mii_floppy_write_track_bits(&new, new_track, bits, cnt);
|
||||
pos += cnt;
|
||||
}
|
||||
// printf("%s: Track %2d has been resynced!\n", __func__, track_id);
|
||||
memcpy(track_data, new_track, MII_FLOPPY_DEFAULT_TRACK_SIZE);
|
||||
memcpy(track_data, new_track, MII_FLOPPY_MAX_TRACK_SIZE);
|
||||
free(new_track);
|
||||
src->dirty = 1;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* NIB isn't ideal to use with our bitstream, as it's lacking the sync
|
||||
* bits.
|
||||
* Anyway, We can recreate the proper bitstream by finding sectors headers,
|
||||
* filling up a few 'correct' 10 bits sync uint8_ts, then plonk said sector
|
||||
* as is.
|
||||
* This iterates all the sectors, looking for changed ones using the CRC
|
||||
* and call the callback to write them back to the file.
|
||||
* Callback can be DSK or NIB specific.
|
||||
*/
|
||||
static void
|
||||
mii_nib_rebit_track(
|
||||
uint8_t *src_track,
|
||||
mii_floppy_track_t * dst,
|
||||
uint8_t * dst_track)
|
||||
mii_floppy_write_track(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file,
|
||||
uint8_t track_id,
|
||||
mii_floppy_write_sector_cb cb )
|
||||
{
|
||||
dst->bit_count = 0;
|
||||
dst->virgin = 0;
|
||||
uint32_t window = 0;
|
||||
int srci = 0;
|
||||
int seccount = 0;
|
||||
int state = 0; // look for address field
|
||||
int tid = 0, sid;
|
||||
uint16_t hmap = 0, dmap = 0;
|
||||
do {
|
||||
window = (window << 8) | src_track[srci++];
|
||||
switch (state) {
|
||||
case 0: {
|
||||
if (window != 0xffd5aa96)
|
||||
break;
|
||||
// uint32_t pos = dst->bit_count;
|
||||
for (int i = 0; i < (seccount == 0 ? 40 : 20); i++)
|
||||
mii_track_write_bits(dst, dst_track, 0xff << 2, 10);
|
||||
uint8_t * h = src_track + srci - 4; // incs first 0xff
|
||||
tid = DE44(h[6], h[7]);
|
||||
sid = DE44(h[8], h[9]);
|
||||
// printf("Track %2d sector %2d pos %5d\n", tid, sid, pos);
|
||||
hmap |= 1 << sid;
|
||||
memcpy(dst_track + (dst->bit_count >> 3), h, 15);
|
||||
dst->bit_count += 15 * 8;
|
||||
srci += 11;
|
||||
state = 1;
|
||||
} break;
|
||||
case 1: {
|
||||
if (window != 0xffd5aaad)
|
||||
break;
|
||||
for (int i = 0; i < 4; i++)
|
||||
mii_track_write_bits(dst, dst_track, 0xff << 2, 10);
|
||||
// printf("\tdata at %d\n", dst->bit_count);
|
||||
dmap |= 1 << sid;
|
||||
uint8_t *h = src_track + srci - 4;
|
||||
memcpy(dst_track + (dst->bit_count >> 3), h, 4 + 342 + 4);
|
||||
dst->bit_count += (4 + 342 + 4) * 8;
|
||||
srci += 4 + 342;
|
||||
seccount++;
|
||||
state = 0;
|
||||
} break;
|
||||
mii_floppy_track_t *track = &f->tracks[track_id];
|
||||
uint8_t *track_data = f->track_data[track_id];
|
||||
|
||||
if (!track->has_map) {
|
||||
printf("%s: track %d has no map\n", __func__, track_id);
|
||||
return;
|
||||
}
|
||||
// look for changed sectors, re-calculate crc, when a sector is found changed,
|
||||
// convert it back from 6:2 encoding, write it using the corresponding sector
|
||||
// map for this format, then update the crc.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mii_floppy_track_map_t *map = &track->map;
|
||||
|
||||
// now it is entirely possible that the sector has moved a bit,
|
||||
// at least prodos does that, so we need to re-find it.
|
||||
if (mii_floppy_realign_sector_map(track, track_data, map, i)) {
|
||||
printf("%s: track %2d sector %2d not found %08x\n",
|
||||
__func__, track_id, i,
|
||||
mii_floppy_read_track_bits(track, track_data, map->sector[i].data, 24));
|
||||
continue;
|
||||
}
|
||||
} while (srci < 6656);
|
||||
printf("%s %d sectors found hmap %04x dmap %04x - %5d bits\n",
|
||||
__func__, seccount, hmap, dmap, dst->bit_count);
|
||||
if (hmap != 0xffff || dmap != 0xffff)
|
||||
printf("%s: track %2d incomplete? (header 0x%04x data 0x%04x)\n",
|
||||
__func__, tid, ~hmap, ~dmap);
|
||||
}
|
||||
uint8_t data_sector[342 + 1];
|
||||
|
||||
static int
|
||||
mii_floppy_load_nib(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file )
|
||||
{
|
||||
const char *filename = basename(file->pathname);
|
||||
printf("%s: loading NIB %s\n", __func__, filename);
|
||||
for (int i = 0; i < 35; i++) {
|
||||
uint8_t *track = file->map + (i * 6656);
|
||||
mii_nib_rebit_track(track, &f->tracks[i], f->track_data[i]);
|
||||
if (f->tracks[i].bit_count < 100) {
|
||||
printf("%s: %s: Invalid track %d has zero bits!\n", __func__,
|
||||
filename, i);
|
||||
return -1;
|
||||
int errors = mii_floppy_read_sector_nibbles(
|
||||
track, track_data, map, i, data_sector);
|
||||
if (errors) {
|
||||
printf("%s: T %2d S %2d has %d spurious zeroes\n",
|
||||
__func__, track_id, i, errors);
|
||||
}
|
||||
// printf("Track %d converted to %d bits\n", i, f->tracks[i].bit_count);
|
||||
f->tracks[i].dirty = 0;
|
||||
uint8_t * src = data_sector;
|
||||
uint16_t crc = mii_floppy_crc(-1, src, 342);
|
||||
if (crc == map->sector[i].crc)
|
||||
continue;
|
||||
|
||||
cb(file, track_data, map, track_id, i, data_sector);
|
||||
// update the crc
|
||||
map->sector[i].crc = crc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mii_floppy_write_track_woz(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file,
|
||||
int track_id )
|
||||
{
|
||||
mii_woz_header_t *header = (mii_woz_header_t *)file->map;
|
||||
|
||||
int version = !strncmp((char*)header, "WOZ", 3);
|
||||
if (!version) {
|
||||
printf("%s: not a WOZ file %4.4s\n", __func__, (char*)&header->magic_le);
|
||||
return 0;
|
||||
}
|
||||
version += !strncmp((char*)header, "WOZ2", 4);
|
||||
|
||||
/* I don't really want to recalculate the CRC. Seems pointless in a file
|
||||
like this, and i'd have to walk 250KB+ of data each time I update
|
||||
anything.
|
||||
Mark is as cleared, perhapps I need a tool to 'fix' it later, or JUST
|
||||
at closing time ?*/
|
||||
header->crc_le = 0;
|
||||
|
||||
mii_woz_tmap_t *tmap = NULL;
|
||||
if (version == 1) {
|
||||
mii_woz1_info_t *info = (mii_woz1_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz1_trks_t *trks = (mii_woz1_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
|
||||
trks->track[track_id].bit_count_le = htole32(f->tracks[track_id].bit_count);
|
||||
uint32_t byte_count = (le32toh(trks->track[track_id].bit_count_le) + 7) >> 3;
|
||||
memcpy(trks->track[track_id].bits,
|
||||
f->track_data[track_id], byte_count);
|
||||
trks->track[track_id].byte_count_le = htole16(byte_count);
|
||||
} else {
|
||||
mii_woz2_info_t *info = (mii_woz2_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz2_trks_t *trks = (mii_woz2_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
|
||||
uint8_t *track = file->map +
|
||||
(le16toh(trks->track[track_id].start_block_le) << 9);
|
||||
|
||||
trks->track[track_id].bit_count_le = htole32(f->tracks[track_id].bit_count);
|
||||
uint32_t byte_count = (le32toh(trks->track[track_id].bit_count_le) + 7) >> 3;
|
||||
memcpy(track, f->track_data[track_id], byte_count);
|
||||
}
|
||||
f->tracks[track_id].dirty = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
mii_floppy_woz_load_tmap(
|
||||
mii_floppy_t *f,
|
||||
mii_woz_tmap_t *tmap )
|
||||
{
|
||||
uint64_t used_tracks = 0;
|
||||
int tmap_size = le32toh(tmap->chunk.size_le);
|
||||
for (int ti = 0; ti < (int)sizeof(f->track_id) && ti < tmap_size; ti++) {
|
||||
f->track_id[ti] = tmap->track_id[ti] == 0xff ?
|
||||
MII_FLOPPY_NOISE_TRACK : tmap->track_id[ti];
|
||||
if (tmap->track_id[ti] != 0xff)
|
||||
used_tracks |= 1L << f->track_id[ti];
|
||||
}
|
||||
return used_tracks;
|
||||
}
|
||||
|
||||
static int
|
||||
mii_floppy_load_woz(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file )
|
||||
{
|
||||
const char *filename = basename(file->pathname);
|
||||
printf("%s: loading WOZ %s\n", __func__, filename);
|
||||
mii_woz_header_t *header = (mii_woz_header_t *)file->map;
|
||||
|
||||
int version = !strncmp((char*)header, "WOZ", 3);
|
||||
if (!version) {
|
||||
printf("%s: not a WOZ file %4.4s\n", __func__, (char*)&header->magic_le);
|
||||
return 0;
|
||||
}
|
||||
version += !strncmp((char*)header, "WOZ2", 4);
|
||||
mii_woz_tmap_t *tmap = NULL;
|
||||
uint64_t used_tracks = 0;
|
||||
|
||||
if (version == 1) {
|
||||
mii_woz1_info_t *info = (mii_woz1_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz1_trks_t *trks = (mii_woz1_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
used_tracks = mii_floppy_woz_load_tmap(f, tmap);
|
||||
#if 1
|
||||
printf("WOZ: version %d, type %d\n",
|
||||
info->version, info->disk_type );
|
||||
printf("WOZ: creator '%s'\n", info->creator);
|
||||
printf("WOZ: track map %4.4s size %d\n",
|
||||
(char*)&tmap->chunk.id_le,
|
||||
le32toh(tmap->chunk.size_le));
|
||||
printf("WOZ: Track chunk %4.4s size %d\n",
|
||||
(char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le));
|
||||
#endif
|
||||
int max_track = le32toh(trks->chunk.size_le) / sizeof(trks->track[0]);
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT && i < max_track; i++) {
|
||||
uint8_t *track = trks->track[i].bits;
|
||||
if (!(used_tracks & (1L << i))) {
|
||||
// printf("WOZ: Track %d not used\n", i);
|
||||
continue;
|
||||
}
|
||||
f->tracks[i].virgin = 0;
|
||||
memcpy(f->track_data[i], track, le16toh(trks->track[i].byte_count_le));
|
||||
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
|
||||
}
|
||||
} else {
|
||||
mii_woz2_info_t *info = (mii_woz2_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz2_trks_t *trks = (mii_woz2_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
used_tracks = mii_floppy_woz_load_tmap(f, tmap);
|
||||
#if 1
|
||||
printf("WOZ: version %d, type %d, sides %d, largest track %d, optimal bit timing: %d\n",
|
||||
info->version, info->disk_type, info->sides,
|
||||
le16toh(info->largest_track_le) * 512,
|
||||
info->optimal_bit_timing);
|
||||
printf("WOZ: creator '%s'\n", info->creator);
|
||||
printf("WOZ: track map %4.4s size %d\n",
|
||||
(char*)&tmap->chunk.id_le,
|
||||
le32toh(tmap->chunk.size_le));
|
||||
printf("WOZ: Track chunk %4.4s size %d\n",
|
||||
(char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le));
|
||||
#endif
|
||||
/* TODO: this doesn't work yet... */
|
||||
//f->bit_timing = info->optimal_bit_timing;
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
||||
if (!(used_tracks & (1L << i))) {
|
||||
// printf("WOZ: Track %d not used\n", i);
|
||||
continue;
|
||||
}
|
||||
uint8_t *track = file->map +
|
||||
(le16toh(trks->track[i].start_block_le) << 9);
|
||||
uint32_t byte_count = (le32toh(trks->track[i].bit_count_le) + 7) >> 3;
|
||||
f->tracks[i].virgin = 0;
|
||||
memcpy(f->track_data[i], track, byte_count);
|
||||
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
#define NIBBLE_SECTOR_SIZE 416
|
||||
#define NIBBLE_TRACK_SIZE 6656
|
||||
#define DSK_SECTOR_SIZE 256
|
||||
#define MAX_SECTORS 16
|
||||
#define VOLUME_NUMBER 254
|
||||
#define DSK_TRACK_SIZE (DSK_SECTOR_SIZE * MAX_SECTORS)
|
||||
|
||||
//static const size_t nib_disksz = 232960;
|
||||
//static const size_t dsk_disksz = 143360;
|
||||
|
||||
// DOS 3.3 Physical sector order (index is physical sector,
|
||||
// value is DOS sector)
|
||||
static const uint8_t DO[] = {
|
||||
0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4,
|
||||
0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF
|
||||
};
|
||||
// ProDOS Physical sector order (index is physical sector,
|
||||
// value is ProDOS sector).
|
||||
static const uint8_t PO[] = {
|
||||
0x0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb,
|
||||
0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf
|
||||
};
|
||||
|
||||
static const uint8_t TRANS62[] = {
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
||||
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
||||
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
};
|
||||
|
||||
// This function is derived from Scullin Steel Co.'s apple2js code
|
||||
// https://github.com/whscullin/apple2js/blob/e280c3d/js/formats/format_utils.ts#L140
|
||||
/* Further recycled for MII .DSK decoding, using 10 bits sync words etc. */
|
||||
static void
|
||||
mii_floppy_nibblize_sector(
|
||||
uint8_t vol, uint8_t track, uint8_t sector,
|
||||
const uint8_t *data,
|
||||
mii_floppy_track_t *dst,
|
||||
uint8_t * track_data )
|
||||
{
|
||||
unsigned int gap;
|
||||
|
||||
// if (track == 0 )
|
||||
// printf("NIB: vol %d track %d sector %d pos %5d\n",
|
||||
// vol, track, sector, dst->bit_count);
|
||||
gap = sector == 0 ? 100 : track == 0 ? 20 : 20;
|
||||
for (uint8_t i = 0; i < gap; ++i)
|
||||
mii_track_write_bits(dst, track_data, 0xFF << 2, 10);
|
||||
// Address Field
|
||||
const uint8_t checksum = vol ^ track ^ sector;
|
||||
mii_track_write_bits(dst, track_data, 0xd5aa96, 24);
|
||||
mii_track_write_bits(dst, track_data, (vol >> 1) | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, vol | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, (track >> 1) | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, track | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, (sector >> 1) | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, sector | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, (checksum >> 1) | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, checksum | 0xAA, 8);
|
||||
mii_track_write_bits(dst, track_data, 0xdeaaeb, 24);
|
||||
// Gap 2 (5)
|
||||
for (int i = 0; i < 5; ++i)
|
||||
mii_track_write_bits(dst, track_data, 0xFF << 2, 10);
|
||||
// Data Field
|
||||
mii_track_write_bits(dst, track_data, 0xd5aaad, 24);
|
||||
uint8_t nibbles[0x156] = {};
|
||||
const unsigned ptr2 = 0;
|
||||
const unsigned ptr6 = 0x56;
|
||||
|
||||
int i2 = 0x55;
|
||||
for (int i6 = 0x101; i6 >= 0; --i6) {
|
||||
uint8_t val6 = data[i6 % 0x100];
|
||||
uint8_t val2 = nibbles[ptr2 + i2];
|
||||
val2 = (val2 << 1) | (val6 & 1); val6 >>= 1;
|
||||
val2 = (val2 << 1) | (val6 & 1); val6 >>= 1;
|
||||
nibbles[ptr6 + i6] = val6;
|
||||
nibbles[ptr2 + i2] = val2;
|
||||
if (--i2 < 0)
|
||||
i2 = 0x55;
|
||||
}
|
||||
uint8_t last = 0;
|
||||
for (int i = 0; i != 0x156; ++i) {
|
||||
const uint8_t val = nibbles[i];
|
||||
mii_track_write_bits(dst, track_data, TRANS62[last ^ val], 8);
|
||||
last = val;
|
||||
}
|
||||
mii_track_write_bits(dst, track_data, TRANS62[last], 8);
|
||||
mii_track_write_bits(dst, track_data, 0xdeaaeb, 24);
|
||||
// Gap 3
|
||||
mii_track_write_bits(dst, track_data, 0xFF << 2, 10);
|
||||
}
|
||||
|
||||
static int
|
||||
mii_floppy_load_dsk(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file )
|
||||
{
|
||||
const char *filename = basename(file->pathname);
|
||||
|
||||
const char *ext = rindex(filename, '.');
|
||||
ext = ext ? ext+1 : "";
|
||||
const uint8_t * secmap = DO;
|
||||
if (!strcasecmp(ext, "PO")) {
|
||||
printf("%s opening %s as PO.\n", __func__, filename);
|
||||
secmap = PO;
|
||||
} else {
|
||||
printf("%s opening %s as DO.\n", __func__, filename);
|
||||
}
|
||||
for (int i = 0; i < 35; ++i) {
|
||||
mii_floppy_track_t *dst = &f->tracks[i];
|
||||
uint8_t *track_data = f->track_data[i];
|
||||
dst->bit_count = 0;
|
||||
dst->virgin = 0;
|
||||
for (int phys_sector = 0; phys_sector < MAX_SECTORS; phys_sector++) {
|
||||
const uint8_t dos_sector = secmap[phys_sector];
|
||||
uint32_t off = ((MAX_SECTORS * i + dos_sector) * DSK_SECTOR_SIZE);
|
||||
uint8_t *src = file->map + off;
|
||||
mii_floppy_nibblize_sector(VOLUME_NUMBER, i, phys_sector,
|
||||
src, dst, track_data);
|
||||
}
|
||||
if (i == 0)
|
||||
printf("%s: track %2d has %d bits %d bytes\n",
|
||||
__func__, i, dst->bit_count, dst->bit_count >> 3);
|
||||
}
|
||||
// DSK is read only
|
||||
f->write_protected |= MII_FLOPPY_WP_RO_FORMAT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
@@ -737,14 +566,23 @@ mii_floppy_update_tracks(
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
||||
if (!f->tracks[i].dirty)
|
||||
continue;
|
||||
// printf("%s: track %d is dirty, saving\n", __func__, i);
|
||||
printf("%s: track %d is dirty, saving\n", __func__, i);
|
||||
switch (file->format) {
|
||||
case MII_DD_FILE_NIB:
|
||||
mii_floppy_write_track(f, file, i, _mii_floppy_nib_write_sector);
|
||||
break;
|
||||
case MII_DD_FILE_WOZ:
|
||||
mii_floppy_write_track_woz(f, file, i);
|
||||
mii_floppy_woz_write_track(f, file, i);
|
||||
// printf("%s: WOZ track %d updated\n", __func__, i);
|
||||
break;
|
||||
case MII_DD_FILE_DSK:
|
||||
case MII_DD_FILE_PO:
|
||||
case MII_DD_FILE_DO:
|
||||
mii_floppy_write_track(f, file, i, _mii_floppy_dsk_write_sector);
|
||||
// printf("%s: DSK track %d updated\n", __func__, i);
|
||||
break;
|
||||
default:
|
||||
printf("%s: unsupported format %d\n", __func__, file->format);
|
||||
}
|
||||
f->tracks[i].dirty = 0;
|
||||
}
|
||||
@@ -762,15 +600,15 @@ mii_floppy_load(
|
||||
int res = -1;
|
||||
switch (file->format) {
|
||||
case MII_DD_FILE_NIB:
|
||||
res = mii_floppy_load_nib(f, file);
|
||||
res = mii_floppy_nib_load(f, file);
|
||||
break;
|
||||
case MII_DD_FILE_WOZ:
|
||||
res = mii_floppy_load_woz(f, file);
|
||||
res = mii_floppy_woz_load(f, file);
|
||||
break;
|
||||
case MII_DD_FILE_DSK:
|
||||
case MII_DD_FILE_PO:
|
||||
case MII_DD_FILE_DO:
|
||||
res = mii_floppy_load_dsk(f, file);
|
||||
res = mii_floppy_dsk_load(f, file);
|
||||
break;
|
||||
default:
|
||||
printf("%s: unsupported format %d\n", __func__, file->format);
|
||||
@@ -781,5 +619,25 @@ mii_floppy_load(
|
||||
else
|
||||
f->write_protected &= ~MII_FLOPPY_WP_RO_FILE;
|
||||
f->seed_dirty = f->seed_saved = rand();
|
||||
#if 0
|
||||
// dump the floppy track maps to check on offsets etc
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
||||
mii_floppy_track_map_t *map = &f->tracks[i].map;
|
||||
if (!f->tracks[i].has_map)
|
||||
continue;
|
||||
printf("Track %2d\n", i);
|
||||
// also show the first 24 bits of data, to check for sync
|
||||
for (int j = 0; j < 16; j++) {
|
||||
printf("\tSector %2d header :%4d:%5d data %3d:%5d crc %04x sync %06x %06x\n",
|
||||
j, map->sector[j].hsync, map->sector[j].header,
|
||||
map->sector[j].dsync, map->sector[j].data, map->sector[j].crc,
|
||||
mii_floppy_read_track_bits(&f->tracks[i], f->track_data[i],
|
||||
map->sector[j].header, 24),
|
||||
mii_floppy_read_track_bits(&f->tracks[i], f->track_data[i],
|
||||
map->sector[j].data, 24));
|
||||
// printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
@@ -11,9 +11,12 @@
|
||||
#include <stdint.h>
|
||||
#include "mii_dd.h"
|
||||
|
||||
// for NIB and others. can be bigger on .WOZ
|
||||
#define MII_FLOPPY_DEFAULT_TRACK_SIZE 6656
|
||||
#define MII_FLOPPY_MAX_TRACK_SIZE 6656
|
||||
#define MII_FLOPPY_TRACK_COUNT 35
|
||||
|
||||
|
||||
#define DE44(a, b) ((((a) & 0x55) << 1) | ((b) & 0x55))
|
||||
|
||||
/*
|
||||
* Reasons for write protect. Ie checkbox in the UI, or file format
|
||||
* doesn't support writes, or the file has no write permissions.
|
||||
@@ -24,10 +27,22 @@ enum {
|
||||
MII_FLOPPY_WP_RO_FORMAT = (1 << 2), // File format doesn't do writes
|
||||
};
|
||||
|
||||
typedef struct mii_floppy_track_map_t {
|
||||
struct {
|
||||
int32_t hsync, dsync; // number of sync bits
|
||||
uint32_t header, data; // position of the header and data
|
||||
uint16_t crc; // crc of the data
|
||||
uint32_t dsk_position; // position in the DSK file
|
||||
uint32_t nib_position; // position in the nibble stream
|
||||
} sector[16];
|
||||
} mii_floppy_track_map_t;
|
||||
|
||||
typedef struct mii_floppy_track_t {
|
||||
uint8_t dirty : 1, // track has been written to
|
||||
virgin : 1; // track is not loaded/formatted
|
||||
uint32_t bit_count;
|
||||
uint8_t dirty : 1, // track has been written to
|
||||
has_map : 1, // track has a valid map
|
||||
virgin : 1; // track is not loaded/formatted
|
||||
uint32_t bit_count;
|
||||
mii_floppy_track_map_t map; // position of all the sectors
|
||||
} mii_floppy_track_t;
|
||||
|
||||
|
||||
@@ -36,39 +51,48 @@ typedef struct mii_floppy_track_t {
|
||||
// thats 208 bytes per track or about 7KB*2 for the whole disk for read+write
|
||||
// we align it on 16 bytes to make it easier to use in a shader
|
||||
#define MII_FLOPPY_HM_TRACK_SIZE \
|
||||
(((MII_FLOPPY_DEFAULT_TRACK_SIZE / MII_FLOPPY_HM_HIT_SIZE) + 15) & ~15)
|
||||
(((MII_FLOPPY_MAX_TRACK_SIZE / MII_FLOPPY_HM_HIT_SIZE) + 15) & ~15)
|
||||
|
||||
typedef struct mii_track_heatmap_t {
|
||||
// 32 bytes of track data corresponds to one byte of heatmap
|
||||
// this needs to be aligned, otherwise SSE code will die horribly
|
||||
uint8_t map[MII_FLOPPY_TRACK_COUNT][MII_FLOPPY_HM_TRACK_SIZE]
|
||||
uint8_t map[MII_FLOPPY_TRACK_COUNT][MII_FLOPPY_HM_TRACK_SIZE]
|
||||
__attribute__((aligned(32)));
|
||||
uint32_t seed, tex, cleared;
|
||||
uint32_t seed; // increased each time the heatmap is touched
|
||||
uint8_t cleared; // set to 1 when the heatmap is cleared
|
||||
uint32_t tex; // texture handle for the UI
|
||||
} mii_track_heatmap_t;
|
||||
|
||||
typedef struct mii_floppy_heatmap_t {
|
||||
mii_track_heatmap_t read, write;
|
||||
} mii_floppy_heatmap_t;
|
||||
|
||||
//
|
||||
|
||||
// the last track is used for noise
|
||||
#define MII_FLOPPY_NOISE_TRACK MII_FLOPPY_TRACK_COUNT
|
||||
|
||||
typedef struct mii_floppy_t {
|
||||
uint8_t write_protected : 3, id : 2;
|
||||
uint8_t bit_timing; // 0=32 (default)
|
||||
uint8_t motor; // motor is on
|
||||
uint8_t stepper; // last step we did...
|
||||
uint8_t qtrack; // quarter track we are on
|
||||
uint32_t bit_position;
|
||||
// write_protected is a bitfield of MII_FLOPPY_WP_*
|
||||
uint8_t write_protected : 3,
|
||||
id : 2;
|
||||
uint8_t bit_timing; // 0=32 (default)
|
||||
uint8_t motor; // motor is on
|
||||
uint8_t stepper; // last step we did...
|
||||
uint8_t qtrack; // quarter track we are on
|
||||
uint32_t bit_position;
|
||||
// this two relate to what we do when LSS needs to return random bits
|
||||
uint32_t random_position;// position in the random data
|
||||
uint8_t random; // random data is used
|
||||
// this is incremented each time a track is marked dirty
|
||||
uint32_t seed_dirty;
|
||||
uint32_t seed_saved; // last seed we saved at
|
||||
uint8_t track_id[MII_FLOPPY_TRACK_COUNT * 4];
|
||||
mii_floppy_track_t tracks[MII_FLOPPY_TRACK_COUNT + 1];
|
||||
// used when deciding wether to save to disk (or update texture)
|
||||
uint32_t seed_dirty;
|
||||
uint32_t seed_saved; // last seed we saved at
|
||||
uint8_t track_id[MII_FLOPPY_TRACK_COUNT * 4];
|
||||
mii_floppy_track_t tracks[MII_FLOPPY_TRACK_COUNT + 1];
|
||||
// keep all the data together, we'll use it to make a texture
|
||||
// the last track is used for noise
|
||||
uint8_t track_data[MII_FLOPPY_TRACK_COUNT + 1]
|
||||
[MII_FLOPPY_DEFAULT_TRACK_SIZE];
|
||||
uint8_t track_data[MII_FLOPPY_TRACK_COUNT + 1]
|
||||
[MII_FLOPPY_MAX_TRACK_SIZE];
|
||||
/* This is set by the UI to track the head movements,
|
||||
* no functional use */
|
||||
mii_floppy_heatmap_t * heat; // optional heatmap
|
||||
@@ -87,7 +111,7 @@ int
|
||||
mii_floppy_load(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file );
|
||||
|
||||
// save any dirty track (if floppy is writeable)
|
||||
int
|
||||
mii_floppy_update_tracks(
|
||||
mii_floppy_t *f,
|
||||
@@ -97,13 +121,13 @@ mii_floppy_resync_track(
|
||||
mii_floppy_t *f,
|
||||
uint8_t track_id,
|
||||
uint8_t flags );
|
||||
|
||||
typedef struct mii_floppy_track_map_t {
|
||||
struct {
|
||||
int32_t hsync, dsync; // number of sync bits
|
||||
uint32_t header, data; // position of the header and data
|
||||
} sector[16];
|
||||
} mii_floppy_track_map_t;
|
||||
int
|
||||
mii_floppy_read_sector(
|
||||
mii_floppy_track_t *track,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t sector,
|
||||
uint8_t data[256] );
|
||||
|
||||
/*
|
||||
* this creates a sector+data map of a bitstream, and returns the positions
|
||||
@@ -116,3 +140,14 @@ mii_floppy_map_track(
|
||||
uint8_t track_id,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t flags );
|
||||
void
|
||||
mii_floppy_write_track_bits(
|
||||
mii_floppy_track_t * dst,
|
||||
uint8_t * track_data,
|
||||
uint32_t bits,
|
||||
uint8_t count );
|
||||
uint16_t
|
||||
mii_floppy_crc( //
|
||||
uint16_t crc, // Initial value
|
||||
uint8_t *data,
|
||||
size_t length);
|
||||
|
133
src/format/mii_nib.c
Normal file
133
src/format/mii_nib.c
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* mii_nib.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "mii_dsk.h"
|
||||
|
||||
/*
|
||||
* NIB isn't ideal to use with our bitstream, as it's lacking the sync
|
||||
* bits.
|
||||
* Anyway, We can recreate the proper bitstream by finding sectors headers,
|
||||
* filling up a few 'correct' 10 bits sync uint8_ts, then plonk said sector
|
||||
* as is.
|
||||
*/
|
||||
static void
|
||||
mii_floppy_nib_render_track(
|
||||
uint8_t *src_track,
|
||||
mii_floppy_track_t * dst,
|
||||
uint8_t * dst_track)
|
||||
{
|
||||
dst->bit_count = 0;
|
||||
dst->virgin = 0;
|
||||
uint32_t window = 0;
|
||||
int srci = 0;
|
||||
int seccount = 0;
|
||||
int state = 0; // look for address field
|
||||
int tid = 0, sid;
|
||||
uint16_t hmap = 0, dmap = 0;
|
||||
dst->has_map = 0; // we are redoing it all
|
||||
do {
|
||||
window = (window << 8) | src_track[srci++];
|
||||
switch (state) {
|
||||
case 0: {
|
||||
if (window != 0xffd5aa96)
|
||||
break;
|
||||
uint32_t pos = dst->bit_count;
|
||||
for (int i = 0; i < (seccount == 0 ? 40 : 20); i++)
|
||||
mii_floppy_write_track_bits(dst, dst_track, 0xff << 2, 10);
|
||||
// mii_floppy_write_track_bits(dst, dst_track, 0xff, 8);
|
||||
// Points to the last sync 0xff of sync (which is 8 bits)
|
||||
uint8_t * h = src_track + srci - 4;
|
||||
tid = DE44(h[6], h[7]);
|
||||
sid = DE44(h[8], h[9]);
|
||||
// printf("Track %2d sector %2d pos %5d\n", tid, sid, pos);
|
||||
hmap |= 1 << sid;
|
||||
dst->map.sector[sid].hsync = dst->bit_count - pos;
|
||||
// points to the 0xd5
|
||||
dst->map.sector[sid].header = dst->bit_count + 8;
|
||||
memcpy(dst_track + (dst->bit_count >> 3), h, 15);
|
||||
dst->bit_count += 15 * 8;
|
||||
srci += 11;
|
||||
state = 1;
|
||||
} break;
|
||||
case 1: {
|
||||
if (window != 0xffd5aaad)
|
||||
break;
|
||||
uint32_t pos = dst->bit_count;
|
||||
for (int i = 0; i < 4; i++)
|
||||
mii_floppy_write_track_bits(dst, dst_track, 0xff << 2, 10);
|
||||
// printf("\tdata at %d\n", dst->bit_count);
|
||||
dmap |= 1 << sid;
|
||||
uint8_t *h = src_track + srci - 4;
|
||||
dst->map.sector[sid].dsync = dst->bit_count - pos;
|
||||
// keep the position in track to be able to save sectors back
|
||||
// this points to the first byte of data
|
||||
dst->map.sector[sid].nib_position = srci;
|
||||
// points to the 0xd5
|
||||
dst->map.sector[sid].data = dst->bit_count + 8;
|
||||
memcpy(dst_track + (dst->bit_count >> 3), h, 4 + 342 + 4);
|
||||
dst->map.sector[sid].crc = mii_floppy_crc(-1, src_track + srci, 342);
|
||||
dst->bit_count += (4 + 342 + 4) * 8;
|
||||
srci += 4 + 342;
|
||||
seccount++;
|
||||
state = 0;
|
||||
} break;
|
||||
}
|
||||
} while (srci < 6656);
|
||||
// printf("%s %d sectors found hmap %04x dmap %04x - %5d bits\n",
|
||||
// __func__, seccount, hmap, dmap, dst->bit_count);
|
||||
if (hmap != 0xffff || dmap != 0xffff)
|
||||
printf("%s: track %2d incomplete? (header 0x%04x data 0x%04x)\n",
|
||||
__func__, tid, ~hmap, ~dmap);
|
||||
else
|
||||
dst->has_map = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This one is easy, just copy the nibble back where they came from
|
||||
*/
|
||||
void
|
||||
_mii_floppy_nib_write_sector(
|
||||
mii_dd_file_t *file,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t track_id,
|
||||
uint8_t sector,
|
||||
uint8_t data_sector[342 + 1] )
|
||||
{
|
||||
printf("%s: T %2d S %2d has changed, writing sector\n",
|
||||
__func__, track_id, sector);
|
||||
uint8_t *dst = file->map + (track_id * 6656) +
|
||||
map->sector[sector].nib_position;
|
||||
memcpy(dst, data_sector, 342 + 1);
|
||||
}
|
||||
|
||||
int
|
||||
mii_floppy_nib_load(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file )
|
||||
{
|
||||
const char *filename = basename(file->pathname);
|
||||
printf("%s: loading NIB %s\n", __func__, filename);
|
||||
for (int i = 0; i < 35; i++) {
|
||||
uint8_t *track = file->map + (i * 6656);
|
||||
mii_floppy_nib_render_track(track, &f->tracks[i], f->track_data[i]);
|
||||
if (f->tracks[i].bit_count < 100) {
|
||||
printf("%s: %s: Invalid track %d has zero bits!\n", __func__,
|
||||
filename, i);
|
||||
return -1;
|
||||
}
|
||||
// printf("Track %d converted to %d bits\n", i, f->tracks[i].bit_count);
|
||||
f->tracks[i].dirty = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
24
src/format/mii_nib.h
Normal file
24
src/format/mii_nib.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* mii_nib.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mii_floppy.h"
|
||||
|
||||
int
|
||||
mii_floppy_nib_load(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file );
|
||||
void
|
||||
_mii_floppy_nib_write_sector(
|
||||
mii_dd_file_t *file,
|
||||
uint8_t *track_data,
|
||||
mii_floppy_track_map_t *map,
|
||||
uint8_t track_id,
|
||||
uint8_t sector,
|
||||
uint8_t data_sector[342 + 1] );
|
169
src/format/mii_woz.c
Normal file
169
src/format/mii_woz.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* mii_woz.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "mii_floppy.h"
|
||||
#include "mii_woz.h"
|
||||
|
||||
|
||||
int
|
||||
mii_floppy_woz_write_track(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file,
|
||||
int track_id )
|
||||
{
|
||||
mii_woz_header_t *header = (mii_woz_header_t *)file->map;
|
||||
|
||||
int version = !strncmp((char*)header, "WOZ", 3);
|
||||
if (!version) {
|
||||
printf("%s: not a WOZ file %4.4s\n", __func__, (char*)&header->magic_le);
|
||||
return 0;
|
||||
}
|
||||
version += !strncmp((char*)header, "WOZ2", 4);
|
||||
|
||||
/* I don't really want to recalculate the CRC. Seems pointless in a file
|
||||
like this, and i'd have to walk 250KB+ of data each time I update
|
||||
anything.
|
||||
Mark is as cleared, perhapps I need a tool to 'fix' it later, or JUST
|
||||
at closing time ?*/
|
||||
header->crc_le = 0;
|
||||
|
||||
mii_woz_tmap_t *tmap = NULL;
|
||||
if (version == 1) {
|
||||
mii_woz1_info_t *info = (mii_woz1_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz1_trks_t *trks = (mii_woz1_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
|
||||
trks->track[track_id].bit_count_le = htole32(f->tracks[track_id].bit_count);
|
||||
uint32_t byte_count = (le32toh(trks->track[track_id].bit_count_le) + 7) >> 3;
|
||||
memcpy(trks->track[track_id].bits,
|
||||
f->track_data[track_id], byte_count);
|
||||
trks->track[track_id].byte_count_le = htole16(byte_count);
|
||||
} else {
|
||||
mii_woz2_info_t *info = (mii_woz2_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz2_trks_t *trks = (mii_woz2_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
|
||||
uint8_t *track = file->map +
|
||||
(le16toh(trks->track[track_id].start_block_le) << 9);
|
||||
|
||||
trks->track[track_id].bit_count_le = htole32(f->tracks[track_id].bit_count);
|
||||
uint32_t byte_count = (le32toh(trks->track[track_id].bit_count_le) + 7) >> 3;
|
||||
memcpy(track, f->track_data[track_id], byte_count);
|
||||
}
|
||||
f->tracks[track_id].dirty = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
mii_floppy_woz_load_tmap(
|
||||
mii_floppy_t *f,
|
||||
mii_woz_tmap_t *tmap )
|
||||
{
|
||||
uint64_t used_tracks = 0;
|
||||
int tmap_size = le32toh(tmap->chunk.size_le);
|
||||
for (int ti = 0; ti < (int)sizeof(f->track_id) && ti < tmap_size; ti++) {
|
||||
f->track_id[ti] = tmap->track_id[ti] == 0xff ?
|
||||
MII_FLOPPY_NOISE_TRACK : tmap->track_id[ti];
|
||||
if (tmap->track_id[ti] != 0xff)
|
||||
used_tracks |= 1L << f->track_id[ti];
|
||||
}
|
||||
return used_tracks;
|
||||
}
|
||||
|
||||
int
|
||||
mii_floppy_woz_load(
|
||||
mii_floppy_t *f,
|
||||
mii_dd_file_t *file )
|
||||
{
|
||||
const char *filename = basename(file->pathname);
|
||||
printf("%s: loading WOZ %s\n", __func__, filename);
|
||||
mii_woz_header_t *header = (mii_woz_header_t *)file->map;
|
||||
|
||||
int version = !strncmp((char*)header, "WOZ", 3);
|
||||
if (!version) {
|
||||
printf("%s: not a WOZ file %4.4s\n", __func__, (char*)&header->magic_le);
|
||||
return 0;
|
||||
}
|
||||
version += !strncmp((char*)header, "WOZ2", 4);
|
||||
mii_woz_tmap_t *tmap = NULL;
|
||||
uint64_t used_tracks = 0;
|
||||
|
||||
if (version == 1) {
|
||||
mii_woz1_info_t *info = (mii_woz1_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz1_trks_t *trks = (mii_woz1_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
used_tracks = mii_floppy_woz_load_tmap(f, tmap);
|
||||
#if 1
|
||||
printf("WOZ: version %d, type %d\n",
|
||||
info->version, info->disk_type );
|
||||
printf("WOZ: creator '%s'\n", info->creator);
|
||||
printf("WOZ: track map %4.4s size %d\n",
|
||||
(char*)&tmap->chunk.id_le,
|
||||
le32toh(tmap->chunk.size_le));
|
||||
printf("WOZ: Track chunk %4.4s size %d\n",
|
||||
(char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le));
|
||||
#endif
|
||||
int max_track = le32toh(trks->chunk.size_le) / sizeof(trks->track[0]);
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT && i < max_track; i++) {
|
||||
uint8_t *track = trks->track[i].bits;
|
||||
if (!(used_tracks & (1L << i))) {
|
||||
// printf("WOZ: Track %d not used\n", i);
|
||||
continue;
|
||||
}
|
||||
f->tracks[i].virgin = 0;
|
||||
memcpy(f->track_data[i], track, le16toh(trks->track[i].byte_count_le));
|
||||
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
|
||||
}
|
||||
} else {
|
||||
mii_woz2_info_t *info = (mii_woz2_info_t *)(header + 1);
|
||||
tmap = (mii_woz_tmap_t *)((uint8_t *)info +
|
||||
le32toh(info->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
mii_woz2_trks_t *trks = (mii_woz2_trks_t *)((uint8_t *)tmap +
|
||||
le32toh(tmap->chunk.size_le) + sizeof(mii_woz_chunk_t));
|
||||
used_tracks = mii_floppy_woz_load_tmap(f, tmap);
|
||||
#if 1
|
||||
printf("WOZ: version %d, type %d, sides %d, largest track %d, optimal bit timing: %d\n",
|
||||
info->version, info->disk_type, info->sides,
|
||||
le16toh(info->largest_track_le) * 512,
|
||||
info->optimal_bit_timing);
|
||||
printf("WOZ: creator '%s'\n", info->creator);
|
||||
printf("WOZ: track map %4.4s size %d\n",
|
||||
(char*)&tmap->chunk.id_le,
|
||||
le32toh(tmap->chunk.size_le));
|
||||
printf("WOZ: Track chunk %4.4s size %d\n",
|
||||
(char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le));
|
||||
#endif
|
||||
/* TODO: this doesn't work yet... */
|
||||
//f->bit_timing = info->optimal_bit_timing;
|
||||
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
|
||||
if (!(used_tracks & (1L << i))) {
|
||||
// printf("WOZ: Track %d not used\n", i);
|
||||
continue;
|
||||
}
|
||||
uint8_t *track = file->map +
|
||||
(le16toh(trks->track[i].start_block_le) << 9);
|
||||
uint32_t byte_count = (le32toh(trks->track[i].bit_count_le) + 7) >> 3;
|
||||
f->tracks[i].virgin = 0;
|
||||
memcpy(f->track_data[i], track, byte_count);
|
||||
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
@@ -92,3 +92,15 @@ typedef struct mii_woz1_trks_t {
|
||||
uint16_t reserved;
|
||||
} track[35];
|
||||
} __attribute__((packed)) mii_woz1_trks_t;
|
||||
|
||||
struct mii_floppy_t;
|
||||
|
||||
int
|
||||
mii_floppy_woz_write_track(
|
||||
struct mii_floppy_t *f,
|
||||
mii_dd_file_t *file,
|
||||
int track_id );
|
||||
int
|
||||
mii_floppy_woz_load(
|
||||
struct mii_floppy_t *f,
|
||||
mii_dd_file_t *file );
|
||||
|
103
src/mii.c
103
src/mii.c
@@ -12,6 +12,7 @@
|
||||
#include <ctype.h>
|
||||
|
||||
#include "mii_rom_iiee.h"
|
||||
#include "mii_rom_iic.h"
|
||||
#include "mii.h"
|
||||
#include "mii_bank.h"
|
||||
#include "mii_video.h"
|
||||
@@ -221,12 +222,17 @@ mii_page_table_update(
|
||||
page2 ? MII_BANK_AUX : MII_BANK_MAIN, 0x20, 0x3f);
|
||||
}
|
||||
// c1-cf are at ROM state when we arrive here
|
||||
if (!intcxrom) {
|
||||
mii_page_set(mii, MII_BANK_CARD_ROM, MII_BANK_CARD_ROM, 0xc1, 0xcf);
|
||||
if (!slotc3rom)
|
||||
mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc3, 0xc3);
|
||||
if (intc8rom)
|
||||
mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc8, 0xcf);
|
||||
if (mii->emu == MII_EMU_IIC) {
|
||||
// mii_page_set(mii, MII_BANK_ROM, MII_BANK_ROM, 0xc1, 0xcf);
|
||||
// mii_page_set(mii, MII_BANK_ROM, MII_BANK_ROM, 0xd0, 0xff);
|
||||
} else {
|
||||
if (!intcxrom) {
|
||||
mii_page_set(mii, MII_BANK_CARD_ROM, MII_BANK_CARD_ROM, 0xc1, 0xcf);
|
||||
if (!slotc3rom)
|
||||
mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc3, 0xc3);
|
||||
if (intc8rom)
|
||||
mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc8, 0xcf);
|
||||
}
|
||||
}
|
||||
bool bsrread = SWW_GETSTATE(sw, BSRREAD);
|
||||
bool bsrwrite = SWW_GETSTATE(sw, BSRWRITE);
|
||||
@@ -368,7 +374,7 @@ mii_access_soft_switches(
|
||||
int slot = ((addr >> 4) & 7) - 1;
|
||||
#if 0
|
||||
printf("SLOT %d addr %04x write %d %02x drv %s\n",
|
||||
slot, addr, write, *byte,
|
||||
slot + 1, addr, write, *byte,
|
||||
mii->slot[slot].drv ? mii->slot[slot].drv->name : "none");
|
||||
#endif
|
||||
if (mii->slot[slot].drv) {
|
||||
@@ -443,6 +449,23 @@ mii_access_soft_switches(
|
||||
mii_page_table_update(mii);
|
||||
return res;
|
||||
}
|
||||
if (mii->emu == MII_EMU_IIC) {
|
||||
switch (addr) {
|
||||
case 0xc020 ... 0xc02f:
|
||||
res = true;
|
||||
if (mii->bank[MII_BANK_ROM].mem == mii_rom_iic) {
|
||||
printf("BANKING IIC SECOND ROM\n");
|
||||
mii->bank[MII_BANK_ROM].mem =
|
||||
(uint8_t*)&mii_rom_iic[16 * 1024];
|
||||
} else {
|
||||
printf("BANKING IIC FIRST ROM\n");
|
||||
mii->bank[MII_BANK_ROM].mem =
|
||||
(uint8_t*)&mii_rom_iic[0];
|
||||
}
|
||||
return res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (write) {
|
||||
switch (addr) {
|
||||
case SW80STOREOFF:
|
||||
@@ -472,6 +495,11 @@ mii_access_soft_switches(
|
||||
case SWINTCXROMOFF:
|
||||
case SWINTCXROMON:
|
||||
res = true;
|
||||
if (mii->emu == MII_EMU_IIC) {
|
||||
// IIc always has the internal rom on, obs
|
||||
SW_SETSTATE(mii, SWINTCXROM, 1);
|
||||
break;
|
||||
}
|
||||
SW_SETSTATE(mii, SWINTCXROM, addr & 1);
|
||||
mii_bank_poke(sw, SWINTCXROM, (addr & 1) << 7);
|
||||
break;
|
||||
@@ -490,11 +518,11 @@ mii_access_soft_switches(
|
||||
} else {
|
||||
switch (addr) {
|
||||
case SWBSRBANK2:
|
||||
*byte = SW_GETSTATE(mii, BSRPAGE2) << 7;
|
||||
SW_READ(*byte, mii, BSRPAGE2);
|
||||
res = true;
|
||||
break;
|
||||
case SWBSRREADRAM:
|
||||
*byte = SW_GETSTATE(mii, BSRREAD) << 7;
|
||||
SW_READ(*byte, mii, BSRREAD);
|
||||
res = true;
|
||||
break;
|
||||
case SWPAGE2OFF:
|
||||
@@ -506,17 +534,25 @@ mii_access_soft_switches(
|
||||
case SW80STORE:
|
||||
case SWINTCXROM:
|
||||
case SWALTPZ:
|
||||
res = true;
|
||||
*byte |= mii_bank_peek(sw, addr);
|
||||
break;
|
||||
case SWSLOTC3ROM:
|
||||
res = true;
|
||||
*byte = mii_bank_peek(sw, addr);
|
||||
if (mii->emu == MII_EMU_IIC) {
|
||||
break;
|
||||
}
|
||||
*byte |= mii_bank_peek(sw, addr);
|
||||
break;
|
||||
case 0xc068:
|
||||
res = true;
|
||||
// IIgs register, read by prodos tho
|
||||
break;
|
||||
case 0xc020: // toggle TAPE output ?!?!
|
||||
// res = true;
|
||||
// break;
|
||||
// toggle TAPE output ?!?!
|
||||
// IIc is switch ROM banking
|
||||
case 0xc020 ... 0xc02f:
|
||||
res = true;
|
||||
break;
|
||||
default:
|
||||
res = true;
|
||||
// if (addr != 0xc00b)
|
||||
@@ -541,6 +577,11 @@ mii_access_soft_switches(
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keyboard (and joystick buttons) related access. The soft switches
|
||||
* from 0xc000 to 0xc01f all return the ascii value in the 7 lower bits.
|
||||
* The 8th bit is set when the key is pressed
|
||||
*/
|
||||
static bool
|
||||
mii_access_keyboard(
|
||||
mii_t *mii,
|
||||
@@ -550,21 +591,22 @@ mii_access_keyboard(
|
||||
{
|
||||
bool res = false;
|
||||
mii_bank_t * sw = &mii->bank[MII_BANK_SW];
|
||||
if (!write && (addr & 0xff) <= 0x1f) {
|
||||
*byte = mii_bank_peek(sw, SWKBD);
|
||||
}
|
||||
switch (addr) {
|
||||
case SWKBD:
|
||||
if (!write) {
|
||||
res = true;
|
||||
*byte = mii_bank_peek(sw, SWKBD);
|
||||
*byte = mii_bank_peek(sw, SWAKD);
|
||||
}
|
||||
break;
|
||||
case SWAKD: {
|
||||
res = true;
|
||||
res = addr == SWAKD;
|
||||
uint8_t r = mii_bank_peek(sw, SWAKD);
|
||||
if (!write)
|
||||
*byte = r;
|
||||
r &= 0x7f;
|
||||
mii_bank_poke(sw, SWAKD, r);
|
||||
mii_bank_poke(sw, SWKBD, r);
|
||||
mii_bank_poke(sw, SWAKD, r & 0x7f);
|
||||
} break;
|
||||
case 0xc061 ... 0xc063: // Push Button 0, 1, 2 (Apple Keys)
|
||||
res = true;
|
||||
@@ -581,9 +623,8 @@ mii_keypress(
|
||||
uint8_t key)
|
||||
{
|
||||
mii_bank_t * sw = &mii->bank[MII_BANK_SW];
|
||||
key |= 0x80;
|
||||
mii_bank_poke(sw, SWAKD, key);
|
||||
mii_bank_poke(sw, SWKBD, key);
|
||||
mii_bank_poke(sw, SWAKD, key | 0x80);
|
||||
mii_bank_poke(sw, SWKBD, key & 0x7f);
|
||||
}
|
||||
|
||||
/* ramworks came populated in chunks, this duplicates these rows of chips */
|
||||
@@ -617,7 +658,7 @@ mii_init(
|
||||
|
||||
for (int i = 0; i < MII_BANK_COUNT; i++)
|
||||
mii->bank[i] = _mii_banks_init[i];
|
||||
mii->bank[MII_BANK_ROM].mem = (uint8_t*)&iie_enhanced_rom_bin[0];
|
||||
mii->bank[MII_BANK_ROM].mem = (uint8_t*)&mii_rom_iiee[0];
|
||||
for (int i = 0; i < MII_BANK_COUNT; i++)
|
||||
mii_bank_init(&mii->bank[i]);
|
||||
uint8_t *mem = realloc(mii->bank[MII_BANK_MAIN].mem, 0x10000);
|
||||
@@ -632,6 +673,7 @@ mii_init(
|
||||
mii_dd_system_init(mii, &mii->dd);
|
||||
mii_analog_init(mii, &mii->analog);
|
||||
mii_video_init(mii);
|
||||
mii_audio_init(mii, &mii->audio);
|
||||
mii_speaker_init(mii, &mii->speaker);
|
||||
|
||||
mii_reset(mii, true);
|
||||
@@ -642,10 +684,6 @@ mii_init(
|
||||
#endif
|
||||
for (int i = 0; i < 7; i++)
|
||||
mii->slot[i].id = i;
|
||||
// srandom(time(NULL));
|
||||
for (int i = 0; i < 256; i++)
|
||||
mii->random[i] = random();
|
||||
|
||||
mii_bank_install_access_cb(&mii->bank[MII_BANK_ROM],
|
||||
_mii_select_c3introm, mii, 0xc3, 0xc3);
|
||||
}
|
||||
@@ -655,6 +693,11 @@ mii_prepare(
|
||||
mii_t *mii,
|
||||
uint32_t flags )
|
||||
{
|
||||
if (mii->emu == MII_EMU_IIC) {
|
||||
printf("IIC Mode engaged\n");
|
||||
mii->bank[MII_BANK_ROM].mem = (uint8_t*)&mii_rom_iic[0];
|
||||
}
|
||||
|
||||
int banks = (flags >> MII_INIT_RAMWORKS_BIT) & 0xf;
|
||||
banks = 12;
|
||||
if (banks > 12)
|
||||
@@ -670,6 +713,7 @@ mii_prepare(
|
||||
}
|
||||
drv = drv->next;
|
||||
}
|
||||
mii_audio_start(&mii->audio);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -689,6 +733,7 @@ mii_dispose(
|
||||
}
|
||||
}
|
||||
mii_speaker_dispose(&mii->speaker);
|
||||
mii_audio_dispose(&mii->audio);
|
||||
mii_dd_system_dispose(&mii->dd);
|
||||
mii->state = MII_INIT;
|
||||
}
|
||||
@@ -699,6 +744,10 @@ mii_reset(
|
||||
bool cold)
|
||||
{
|
||||
// printf("%s cold %d\n", __func__, cold);
|
||||
if (mii->emu == MII_EMU_IIC) {
|
||||
printf("IIC Mode engaged\n");
|
||||
mii->bank[MII_BANK_ROM].mem = (uint8_t*)&mii_rom_iic[0];
|
||||
}
|
||||
mii->state = MII_RUNNING;
|
||||
mii->cpu_state.reset = 1;
|
||||
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
|
||||
@@ -922,7 +971,7 @@ mii_run(
|
||||
{
|
||||
/* this runs all cycles for one instruction */
|
||||
if (unlikely(mii->state != MII_RUNNING || mii->trace_cpu > 1)) {
|
||||
printf("tracing\n");
|
||||
// printf("tracing\n");
|
||||
mii->cpu.instruction_run = 0;
|
||||
} else
|
||||
mii->cpu.instruction_run = 100000;
|
||||
|
13
src/mii.h
13
src/mii.h
@@ -15,6 +15,7 @@
|
||||
#include "mii_bank.h"
|
||||
#include "mii_slot.h"
|
||||
#include "mii_video.h"
|
||||
#include "mii_audio.h"
|
||||
#include "mii_speaker.h"
|
||||
#include "mii_mouse.h"
|
||||
#include "mii_analog.h"
|
||||
@@ -23,6 +24,11 @@
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
enum {
|
||||
MII_EMU_IIEE = 0,
|
||||
MII_EMU_IIC,
|
||||
};
|
||||
|
||||
enum {
|
||||
MII_BANK_MAIN = 0, // main 48K address space
|
||||
MII_BANK_BSR, // 0xd000 - 0xffff bank switched RAM 16KB
|
||||
@@ -97,6 +103,7 @@ typedef uint64_t (*mii_timer_p)(
|
||||
* principal emulator state, for a faceless emulation
|
||||
*/
|
||||
typedef struct mii_t {
|
||||
uint emu; // MII_EMU_*
|
||||
mii_cpu_t cpu;
|
||||
mii_cpu_state_t cpu_state;
|
||||
/* this is the video frame/VBL rate vs 60hz, default to MII_SPEED_NTSC */
|
||||
@@ -166,9 +173,7 @@ typedef struct mii_t {
|
||||
mii_mouse_t mouse;
|
||||
mii_dd_system_t dd;
|
||||
mii_analog_t analog;
|
||||
|
||||
uint8_t random[256];
|
||||
uint8_t random_index;
|
||||
mii_audio_sink_t audio;
|
||||
} mii_t;
|
||||
|
||||
enum {
|
||||
@@ -341,7 +346,7 @@ extern mii_slot_drv_t * mii_slot_drv_list;
|
||||
mii_slot_drv_list = &_mii_driver; \
|
||||
}
|
||||
|
||||
#define MII_TRAP 0xdbfb
|
||||
#define MII_TRAP 0xebfb
|
||||
/*
|
||||
* Request a trap ID for the given callback. Calling code is responsible
|
||||
* for setting up the trap using the 2 magic NOPs in sequence.
|
||||
|
@@ -90,11 +90,15 @@ next_instruction:
|
||||
}
|
||||
if (unlikely(s.irq && cpu->P.I == 0)) {
|
||||
if (!cpu->IRQ)
|
||||
cpu->IRQ = 1;
|
||||
cpu->IRQ = MII_CPU_IRQ_IRQ;
|
||||
}
|
||||
if (unlikely(s.nmi && cpu->P.I == 0)) {
|
||||
if (!cpu->IRQ)
|
||||
cpu->IRQ = MII_CPU_IRQ_NMI;
|
||||
}
|
||||
if (unlikely(cpu->IRQ)) {
|
||||
s.irq = 0;
|
||||
cpu->P.B = cpu->IRQ == 2;
|
||||
cpu->P.B = cpu->IRQ == MII_CPU_IRQ_BRK;
|
||||
cpu->_D = cpu->PC;
|
||||
_STORE(0x0100 | cpu->S--, cpu->_D >> 8);
|
||||
_STORE(0x0100 | cpu->S--, cpu->_D & 0xff);
|
||||
@@ -102,11 +106,17 @@ next_instruction:
|
||||
MII_GET_P(cpu, p);
|
||||
_STORE(0x0100 | cpu->S--, p);
|
||||
cpu->P.I = 1;
|
||||
if (cpu->IRQ == 2)
|
||||
if (cpu->IRQ == MII_CPU_IRQ_BRK)
|
||||
cpu->P.D = 0;
|
||||
if (cpu->IRQ == MII_CPU_IRQ_NMI) {
|
||||
// printf("NMI!\n");
|
||||
_FETCH(0xfffa); cpu->_P = s.data;
|
||||
_FETCH(0xfffb); cpu->_P |= s.data << 8;
|
||||
} else {
|
||||
_FETCH(0xfffe); cpu->_P = s.data;
|
||||
_FETCH(0xffff); cpu->_P |= s.data << 8;
|
||||
}
|
||||
cpu->IRQ = 0;
|
||||
_FETCH(0xfffe); cpu->_P = s.data;
|
||||
_FETCH(0xffff); cpu->_P |= s.data << 8;
|
||||
cpu->PC = cpu->_P;
|
||||
}
|
||||
s.sync = 1;
|
||||
@@ -192,7 +202,9 @@ next_instruction:
|
||||
case IND_Z: { // ($xx)
|
||||
_FETCH(cpu->PC++); cpu->_D = s.data;
|
||||
_FETCH(cpu->_D); cpu->_P = s.data;
|
||||
_FETCH(cpu->_D + 1); cpu->_P |= s.data << 8;
|
||||
// _FETCH((cpu->_D + 1)); cpu->_P |= s.data << 8;
|
||||
// FD if $xx=0xFF then 0xFF+1 = 0x00 and not 0x100 bug fixed
|
||||
_FETCH((cpu->_D + 1) & 0xFF); cpu->_P |= s.data << 8;
|
||||
} break;
|
||||
case IND_AX: { // ($xxxx,X)
|
||||
_FETCH(cpu->PC++); cpu->_D = s.data;
|
||||
@@ -218,7 +230,8 @@ next_instruction:
|
||||
uint8_t lo = (cpu->A & 0x0f) + (D & 0x0f) + !!cpu->P.C;
|
||||
if (lo > 9) lo += 6;
|
||||
uint8_t hi = (cpu->A >> 4) + (D >> 4) + (lo > 0x0f);
|
||||
cpu->P.Z = ((uint8_t)(cpu->A + D + cpu->P.C)) == 0;
|
||||
// FD removed
|
||||
// cpu->P.Z = ((cpu->A + D + cpu->P.C) & 0xff) == 0;
|
||||
// that is 6502 behaviour
|
||||
// cpu->P.N = !!(hi & 0xf8);
|
||||
cpu->P.V = !!((!((cpu->A ^ D) & 0x80) &&
|
||||
@@ -230,6 +243,8 @@ next_instruction:
|
||||
cpu->A = (hi << 4) | (lo & 0x0f);
|
||||
// THAT is 65c02 behaviour
|
||||
cpu->P.N = !!(cpu->A & 0x80);
|
||||
// FD THAT is 65C02 behavior
|
||||
cpu->P.Z = cpu->A == 0;
|
||||
} else {
|
||||
uint16_t sum = cpu->A + cpu->_D + !!cpu->P.C;
|
||||
cpu->P.V = cpu->P.C = 0;
|
||||
@@ -247,6 +262,7 @@ next_instruction:
|
||||
} break;
|
||||
case 0x0A:
|
||||
{ // ASL
|
||||
_FETCH(cpu->PC); // cycle++
|
||||
cpu->P.C = !!(cpu->A & 0x80);
|
||||
cpu->A <<= 1;
|
||||
_NZ(cpu->A);
|
||||
@@ -308,7 +324,7 @@ next_instruction:
|
||||
// https://www.nesdev.org/the%20'B'%20flag%20&%20BRK%20instruction.txt#:~:text=A%20note%20on%20the%20BRK,opcode%2C%20and%20not%20just%201.
|
||||
_FETCH(cpu->PC++);
|
||||
s.irq = 1;
|
||||
cpu->IRQ = 2; // BRK sort of IRQ interrupt
|
||||
cpu->IRQ = MII_CPU_IRQ_BRK; // BRK sort of IRQ interrupt
|
||||
} break;
|
||||
case 0x18: case 0xD8: case 0x58: case 0xB8:
|
||||
{ // CLC, CLD, CLI, CLV
|
||||
@@ -361,7 +377,7 @@ next_instruction:
|
||||
_NZ(cpu->A);
|
||||
} break;
|
||||
case 0x1A:
|
||||
{ // INC
|
||||
{ // INC (accumulator)
|
||||
_FETCH(cpu->PC);
|
||||
_NZ(++cpu->A);
|
||||
} break;
|
||||
@@ -476,7 +492,8 @@ next_instruction:
|
||||
_NZ(cpu->Y);
|
||||
} break;
|
||||
case 0x2A:
|
||||
{ // ROL
|
||||
{ // ROL immediate
|
||||
_FETCH(cpu->PC); // cycle++
|
||||
uint8_t c = cpu->P.C;
|
||||
cpu->P.C = !!(cpu->A & 0x80);
|
||||
cpu->A <<= 1;
|
||||
@@ -493,6 +510,7 @@ next_instruction:
|
||||
} break;
|
||||
case 0x6A:
|
||||
{ // ROR
|
||||
_FETCH(cpu->PC); // cycle++
|
||||
uint8_t c = cpu->P.C;
|
||||
cpu->P.C = !!(cpu->A & 0x01);
|
||||
cpu->A >>= 1;
|
||||
@@ -511,8 +529,10 @@ next_instruction:
|
||||
{ // RTI
|
||||
_FETCH(cpu->PC); // dummy write
|
||||
cpu->S++; _FETCH(0x0100 | cpu->S);
|
||||
// FD : Modified to set Break bit to 0 in order to pass Harte's tests .
|
||||
for (int i = 0; i < 8; i++)
|
||||
MII_SET_P_BIT(cpu, i, i == B_B || (s.data & (1 << i)));
|
||||
// MII_SET_P_BIT(cpu, i, i == B_B || (s.data & (1 << i)));
|
||||
MII_SET_P_BIT(cpu, i, !(i == B_B) && (s.data & (1 << i)));
|
||||
cpu->P._R = 1;
|
||||
cpu->S++; _FETCH(0x0100 | cpu->S);
|
||||
cpu->_P = s.data;
|
||||
@@ -533,6 +553,7 @@ next_instruction:
|
||||
{ // SBC
|
||||
// Handle subbing in BCD with bit D
|
||||
if (unlikely(cpu->P.D)) {
|
||||
#if 1
|
||||
uint8_t D = 0x99 - cpu->_D;
|
||||
// verbatim ADC code here
|
||||
uint8_t lo = (cpu->A & 0x0f) + (D & 0x0f) + !!cpu->P.C;
|
||||
@@ -550,6 +571,33 @@ next_instruction:
|
||||
cpu->A = (hi << 4) | (lo & 0x0f);
|
||||
// THAT is 65c02 behaviour
|
||||
cpu->P.N = !!(cpu->A & 0x80);
|
||||
#else
|
||||
// Decimal mode
|
||||
// Perform decimal subtraction
|
||||
// fully based on http://www.6502.org/tutorials/decimal_mode.html
|
||||
unsigned int result; // the final 16bit result of the substration
|
||||
|
||||
int16_t A = cpu->A;
|
||||
uint8_t B = cpu->_D;
|
||||
uint8_t C = cpu->P.C; // Carry (borrow) bit : must be 1 if no borrow
|
||||
|
||||
result = A - B - (1 - C); // do the calculation in binary mode
|
||||
cpu->P.C = !(result & 0xFF00);
|
||||
cpu->P.V = !!((A ^ B) & (A ^ result) & 0x80); // complex but it works !!!
|
||||
|
||||
uint8_t AH = (A >> 4) & 0x0F; // get the accumulator high digit
|
||||
int8_t AL;
|
||||
uint8_t BH = (B >> 4) & 0x0F; // get the high digit of the substracted value
|
||||
uint8_t BL = B & 0x0F; // get the low digit of the substracted value
|
||||
|
||||
AL = (A & 0x0F) - (B & 0x0F) + C -1; // 3a et 4a, calculation is performed on the low digits, +C-1 is a trick
|
||||
// A = A - B + C -1; // 4b calculation is performed with the full 8bit original values. Already done with result
|
||||
A = result;
|
||||
if (A < 0) A = A - 0x60; // 4c if negative then substraction is performed to stay in the 00-99 range
|
||||
if (AL < 0) A = A - 0x06; // 4d if low digit is <0 than apply the same operation on it
|
||||
cpu->A = A & 0xFF; // 3e et 4e and voila !, we have the right value for the result
|
||||
_NZ(cpu->A); // set N and Z bits withe decimal result
|
||||
#endif
|
||||
} else {
|
||||
cpu->_D = (~cpu->_D) & 0xff;
|
||||
uint16_t sum = cpu->A + cpu->_D + !!cpu->P.C;
|
||||
@@ -637,8 +685,23 @@ next_instruction:
|
||||
case 0xF4:
|
||||
_FETCH(cpu->PC++); // consume that byte
|
||||
break;
|
||||
case 0xdb: case 0xfb:
|
||||
// trap NOPs
|
||||
case 0xdb:
|
||||
// trap NOPs / STP (WDC)
|
||||
_FETCH(cpu->PC++); // FD: Added to pass HARTE's test
|
||||
break;
|
||||
case 0xCB :
|
||||
// FD: Added to pass HARTE's test
|
||||
// WAI for WDC65C02 not in R65C02
|
||||
// FD: Added to properly pass HARTE's test
|
||||
case 0x0B: case 0x1B: case 0x2B: case 0x3B: case 0x4B: case 0x5B:
|
||||
case 0x6B: case 0x7B: case 0x8B: case 0x9B: case 0xAB: case 0xBB:
|
||||
// these two are SPECIAL, 0xebfb is used as the 'trap' that calls
|
||||
// back into the emulator. This is used by the smartport driver
|
||||
case 0xEB: case 0xFB:
|
||||
case 0x03: case 0X13: case 0X23: case 0X33: case 0x43: case 0x53:
|
||||
case 0x63: case 0x73: case 0x83: case 0x93: case 0xA3: case 0xB3:
|
||||
case 0xC3: case 0xD3: case 0xE3: case 0xF3:
|
||||
// NOPs
|
||||
break;
|
||||
default:
|
||||
printf("%04x %02x UNKNOWN INSTRUCTION\n", cpu->PC, cpu->IR);
|
||||
|
@@ -54,6 +54,13 @@ typedef union mii_cpu_state_t {
|
||||
uint32_t raw;
|
||||
} mii_cpu_state_t ;
|
||||
|
||||
enum {
|
||||
MII_CPU_IRQ_NONE = 0,
|
||||
MII_CPU_IRQ_IRQ,
|
||||
MII_CPU_IRQ_NMI,
|
||||
MII_CPU_IRQ_BRK,
|
||||
};
|
||||
|
||||
#ifndef MII_65C02_DIRECT_ACCESS
|
||||
#define MII_65C02_DIRECT_ACCESS 1
|
||||
#endif
|
||||
@@ -134,8 +141,8 @@ mii_cpu_run(
|
||||
|
||||
#ifdef MII_PACK_P
|
||||
#define MII_SET_P(_cpu, _byte) { \
|
||||
(_cpu)->P.P = _byte | 0x30; \
|
||||
}
|
||||
(_cpu)->P.P = (_byte & 0xEF) | 0x20; \
|
||||
} // FD: to pass HARTE's test : 0x20 instead of 0x30, unset Break Bit
|
||||
#define MII_GET_P(_cpu, _res) \
|
||||
(_res) = (_cpu)->P.P
|
||||
#define MII_SET_P_BIT(_cpu, _bit, _val) { \
|
||||
|
@@ -27,7 +27,7 @@ _mii_extract_token(
|
||||
position++;
|
||||
char *kw = strsep(&position, " \t");
|
||||
if (kw)
|
||||
strncpy(dst, kw, dst_len);
|
||||
strncpy(dst, kw, dst_len-1);
|
||||
return position;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,16 @@ _mii_extract_name(
|
||||
return end;
|
||||
}
|
||||
|
||||
static const char * apple2_charset =
|
||||
"@ABCDEFGHIJKLMNO"
|
||||
"PQRSTUVWXYZ[\\]^_"
|
||||
" !\"#$%&'()*+,-./"
|
||||
"0123456789:;<=>?"
|
||||
"................"
|
||||
"................"
|
||||
"`abcdefghijklmno"
|
||||
"pqrstuvwxyz{|}~";
|
||||
|
||||
static char *
|
||||
_mii_extract_value_or_name(
|
||||
mii_cpu_asm_line_t *l,
|
||||
@@ -69,6 +79,13 @@ _mii_extract_value_or_name(
|
||||
while (isdigit(*end))
|
||||
l->op_value = (l->op_value << 4) +
|
||||
(strchr(hex, tolower(*end++)) - hex);
|
||||
} else if (*end == '\'' || *end == '"') {
|
||||
end++;
|
||||
while (*end && *end != '\'' && *end != '"') {
|
||||
char *c = strchr(apple2_charset, *end++);
|
||||
if (c)
|
||||
l->op_value = (l->op_value << 8) + (c - apple2_charset);
|
||||
}
|
||||
} else {
|
||||
end = _mii_extract_name(end, l->op_name, sizeof(l->op_name));
|
||||
l->label_resolved = 0;
|
||||
@@ -166,6 +183,38 @@ mii_cpu_asm_load(
|
||||
}
|
||||
goto next_line;
|
||||
}
|
||||
if (!strncmp(l->mnemonic, ".asc", 4) || !strcmp(l->mnemonic, "text")) {
|
||||
/* remaining of the line is a string, that we convert to apple2 charset
|
||||
strings are delimited with "" and can also have numerical values, so
|
||||
a typical syntax is:
|
||||
.asc "Hello World", 0
|
||||
*/
|
||||
char *kw = position;
|
||||
do {
|
||||
while (*kw == ' ' || *kw == '\t') kw++;
|
||||
if (*kw == '"') {
|
||||
kw++;
|
||||
while (*kw && *kw != '"') {
|
||||
char *c = strchr(apple2_charset, *kw++);
|
||||
if (c)
|
||||
l->opcodes[l->opcode_count++] = 0x80 +
|
||||
(c - apple2_charset);
|
||||
}
|
||||
} else {
|
||||
kw = _mii_extract_value_or_name(l, kw);
|
||||
if (_mii_resolve_symbol(p, l, 0) == 0)
|
||||
printf("ERROR -- sorry code symbols don't work, just EQUs\n");
|
||||
else
|
||||
l->opcodes[l->opcode_count++] = l->op_value;
|
||||
}
|
||||
while (*kw == ' ' || *kw == '\t') kw++;
|
||||
if (*kw == ',')
|
||||
kw++;
|
||||
else
|
||||
break;
|
||||
} while (*kw);
|
||||
goto next_line;
|
||||
}
|
||||
position = _mii_extract_token(position, l->operand, sizeof(l->operand));
|
||||
if (l->operand[0] == '.' || l->operand[0] == '=') {
|
||||
// a directive that has been not aligned on the first line, re-adjust
|
||||
|
@@ -39,20 +39,20 @@ enum {
|
||||
};
|
||||
|
||||
enum {
|
||||
IMPLIED, // BRK
|
||||
IMM, // LDA #$01
|
||||
ZP_REL, // LDA $C0 or BCC $FF
|
||||
ZP_X, // LDA $C0,X
|
||||
ZP_Y, // LDX $C0,Y
|
||||
ABS, // LDA $1234
|
||||
ABS_X, // LDA $1234,X
|
||||
ABS_Y, // LDA $1234,Y
|
||||
IND_X, // LDA ($FF,X)
|
||||
IND_AX, // JMP ($1234,X)
|
||||
IND_Y, // LDA ($FF),Y
|
||||
IND, // JMP ($1234)
|
||||
IND_Z, // LDA ($C0)
|
||||
BRANCH, // BEQ/BNE etc
|
||||
IMPLIED, // BRK
|
||||
IMM, // LDA #$01
|
||||
ZP_REL, // LDA $C0 or BCC $FF
|
||||
ZP_X, // LDA $C0,X
|
||||
ZP_Y, // LDX $C0,Y
|
||||
ABS, // LDA $1234
|
||||
ABS_X, // LDA $1234,X
|
||||
ABS_Y, // LDA $1234,Y
|
||||
IND_X, // LDA ($FF,X)
|
||||
IND_AX, // JMP ($1234,X)
|
||||
IND_Y, // LDA ($FF),Y
|
||||
IND, // JMP ($1234)
|
||||
IND_Z, // LDA ($C0)
|
||||
BRANCH, // BEQ/BNE etc
|
||||
};
|
||||
|
||||
#define PCODE_(_name, _mode, _op, _pc, _r, _w, _br, _sb, _sv, _cpc) \
|
||||
|
@@ -123,6 +123,14 @@ mii_argv_parse(
|
||||
mii_slot_drv_register(mii, 4, "mouse");
|
||||
mii_slot_drv_register(mii, 6, "disk2");
|
||||
mii_slot_drv_register(mii, 7, "smartport");
|
||||
} else if (!strcmp(arg, "-2c") || !strcmp(arg, "--2c") ||
|
||||
!strcmp(arg, "--iic")) {
|
||||
mii->emu = MII_EMU_IIC;
|
||||
mii_slot_drv_register(mii, 1, "ssc");
|
||||
mii_slot_drv_register(mii, 2, "ssc");
|
||||
mii_slot_drv_register(mii, 4, "mouse");
|
||||
mii_slot_drv_register(mii, 5, "smartport");
|
||||
mii_slot_drv_register(mii, 6, "disk2");
|
||||
} else if (!strcmp(arg, "-L") || !strcmp(arg, "--list-drivers")) {
|
||||
mii_slot_drv_t * drv = mii_slot_drv_list;
|
||||
printf("mii: available drivers:\n");
|
||||
@@ -132,18 +140,18 @@ mii_argv_parse(
|
||||
}
|
||||
exit(0);
|
||||
} else if (!strcmp(arg, "-m") || !strcmp(arg, "--mute")) {
|
||||
mii->speaker.muted = true;
|
||||
mii->audio.muted = true;
|
||||
} else if (!strcmp(arg, "--audio-off") ||
|
||||
!strcmp(arg, "--no-audio") ||
|
||||
!strcmp(arg, "--silent")) {
|
||||
mii->speaker.speaker_off = true;
|
||||
mii->audio.drv = NULL;
|
||||
*ioFlags |= MII_INIT_SILENT;
|
||||
} else if (!strcmp(arg, "-vol") || !strcmp(arg, "--volume")) {
|
||||
if (i < argc-1) {
|
||||
float vol = atof(argv[++i]);
|
||||
if (vol < 0) vol = 0;
|
||||
else if (vol > 10) vol = 10;
|
||||
mii_speaker_volume(&mii->speaker, vol);
|
||||
mii_audio_volume(&mii->speaker.source, vol);
|
||||
} else {
|
||||
printf("mii: missing volume value\n");
|
||||
return 1;
|
||||
|
109
src/mii_audio.c
Normal file
109
src/mii_audio.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* mii_audio.c
|
||||
*
|
||||
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "mii.h"
|
||||
|
||||
void
|
||||
mii_audio_init(
|
||||
struct mii_t *mii,
|
||||
mii_audio_sink_t *sink)
|
||||
{
|
||||
sink->drv = NULL;
|
||||
sink->mii = mii;
|
||||
SLIST_INIT(&sink->source);
|
||||
mii_audio_run(sink);
|
||||
}
|
||||
|
||||
void
|
||||
mii_audio_dispose(
|
||||
mii_audio_sink_t *sink)
|
||||
{
|
||||
if (sink->drv && sink->drv->stop)
|
||||
sink->drv->stop(sink);
|
||||
while (!SLIST_EMPTY(&sink->source)) {
|
||||
mii_audio_source_t *source = SLIST_FIRST(&sink->source);
|
||||
source->sink = NULL;
|
||||
SLIST_REMOVE_HEAD(&sink->source, self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mii_audio_set_driver(
|
||||
mii_audio_sink_t *sink,
|
||||
const mii_audio_driver_t *drv)
|
||||
{
|
||||
printf("%s: %p\n", __func__, drv);
|
||||
sink->drv = (mii_audio_driver_t*)drv;
|
||||
}
|
||||
|
||||
void
|
||||
mii_audio_start(
|
||||
mii_audio_sink_t *sink )
|
||||
{
|
||||
if (sink->drv && sink->drv->start)
|
||||
sink->drv->start(sink);
|
||||
}
|
||||
|
||||
void
|
||||
mii_audio_run(
|
||||
mii_audio_sink_t *s )
|
||||
{
|
||||
if (!s || !s->mii) return;
|
||||
// if CPU speed has changed, recalculate the number of cycles per sample
|
||||
if (s->cpu_speed != s->mii->speed) {
|
||||
s->cpu_speed = s->mii->speed;
|
||||
s->clk_per_sample = ((1000000.0 * s->mii->speed) /
|
||||
(float)MII_AUDIO_FREQ) + 0.5f;
|
||||
printf("%s: %.2f cycles per sample\n", __func__, s->clk_per_sample);
|
||||
}
|
||||
}
|
||||
|
||||
// this is here so we dont' have to drag in libm math library.
|
||||
double fastPow(double a, double b) {
|
||||
union { double d; int32_t x[2]; } u = { .d = a };
|
||||
u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
|
||||
u.x[0] = 0;
|
||||
return u.d;
|
||||
}
|
||||
|
||||
// volume from 0 to 10, sets the audio sample multiplier.
|
||||
void
|
||||
mii_audio_volume(
|
||||
mii_audio_source_t *s,
|
||||
float volume)
|
||||
{
|
||||
if (volume < 0) volume = 0;
|
||||
else if (volume > 10) volume = 10;
|
||||
double mul = (fastPow(10.0, volume / 10.0) / 10.0) - 0.09;
|
||||
s->vol_multiplier = mul;
|
||||
s->volume = volume;
|
||||
}
|
||||
|
||||
void
|
||||
mii_audio_add_source(
|
||||
mii_audio_sink_t *sink,
|
||||
mii_audio_source_t *source)
|
||||
{
|
||||
source->sink = sink;
|
||||
mii_audio_volume(source, 5);
|
||||
SLIST_INSERT_HEAD(&sink->source, source, self);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
mii_audio_source_push(
|
||||
mii_audio_source_t *source,
|
||||
mii_audio_frame_t *frame)
|
||||
{
|
||||
if (source->sink->drv && source->sink->drv->write)
|
||||
source->sink->drv->write(source->sink, source);
|
||||
}
|
106
src/mii_audio.h
Normal file
106
src/mii_audio.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* mii_audio.h
|
||||
*
|
||||
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "fifo_declare.h"
|
||||
#include "bsd_queue.h"
|
||||
|
||||
#define MII_AUDIO_FREQ (44100)
|
||||
// circular buffer
|
||||
#define MII_AUDIO_FRAME_SIZE 4096
|
||||
|
||||
typedef float mii_audio_sample_t;
|
||||
|
||||
DECLARE_FIFO(mii_audio_sample_t, mii_audio_frame, MII_AUDIO_FRAME_SIZE);
|
||||
DEFINE_FIFO(mii_audio_sample_t, mii_audio_frame);
|
||||
|
||||
struct mii_audio_sink_t;
|
||||
|
||||
|
||||
enum mii_audio_source_state_e {
|
||||
MII_AUDIO_IDLE,
|
||||
MII_AUDIO_STARTING,
|
||||
MII_AUDIO_PLAYING,
|
||||
MII_AUDIO_STOPPING,
|
||||
};
|
||||
|
||||
/*
|
||||
* A source of samples. It has a FIFO that source can fill up, and
|
||||
* it is attached to a sink that will consume the samples.
|
||||
* The state field is filed by the source itself, the audio sink uses
|
||||
* it to know when playing starts/stops for padding reasons.
|
||||
*/
|
||||
typedef struct mii_audio_source_t {
|
||||
struct mii_audio_sink_t * sink;
|
||||
SLIST_ENTRY(mii_audio_source_t) self;
|
||||
uint state;
|
||||
// mute (without having to the the volume to zero)
|
||||
float volume; // volume, 0.0 to 10.0
|
||||
float vol_multiplier; // 0.0 to 1.0
|
||||
mii_audio_frame_t fifo;
|
||||
uint last_read;
|
||||
} mii_audio_source_t;
|
||||
|
||||
/*
|
||||
* Audio sink "pulls" samples from the sources, mix them, and send them to the
|
||||
* audio driver.
|
||||
*/
|
||||
typedef struct mii_audio_driver_t {
|
||||
void (*start)(
|
||||
struct mii_audio_sink_t *sink);
|
||||
void (*stop)(
|
||||
struct mii_audio_sink_t *sink);
|
||||
void (*write)(
|
||||
struct mii_audio_sink_t *sink,
|
||||
mii_audio_source_t *source);
|
||||
} mii_audio_driver_t;
|
||||
|
||||
|
||||
typedef struct mii_audio_sink_t {
|
||||
struct mii_t * mii;
|
||||
mii_audio_driver_t * drv;
|
||||
uint muted : 1, state;
|
||||
SLIST_HEAD(, mii_audio_source_t) source;
|
||||
// last CPU speed in MHz, to calculate clk_per_sample
|
||||
float cpu_speed;
|
||||
// number of cycles per sample (at current CPU speed)
|
||||
float clk_per_sample;
|
||||
} mii_audio_sink_t;
|
||||
|
||||
void
|
||||
mii_audio_init(
|
||||
struct mii_t *mii,
|
||||
mii_audio_sink_t *sink);
|
||||
|
||||
void
|
||||
mii_audio_dispose(
|
||||
mii_audio_sink_t *sink);
|
||||
|
||||
void
|
||||
mii_audio_set_driver(
|
||||
mii_audio_sink_t *sink,
|
||||
const mii_audio_driver_t *drv);
|
||||
|
||||
void
|
||||
mii_audio_add_source(
|
||||
mii_audio_sink_t *sink,
|
||||
mii_audio_source_t *source);
|
||||
|
||||
void
|
||||
mii_audio_start(
|
||||
mii_audio_sink_t *sink );
|
||||
void
|
||||
mii_audio_run(
|
||||
mii_audio_sink_t *sink );
|
||||
// volume from 0 to 10, sets the audio sample multiplier.
|
||||
void
|
||||
mii_audio_volume(
|
||||
mii_audio_source_t *source,
|
||||
float volume);
|
@@ -19,6 +19,46 @@
|
||||
#include "mii_65c02_ops.h"
|
||||
#include "mii_65c02_disasm.h"
|
||||
|
||||
void
|
||||
mii_hexdump(
|
||||
const char * prompt,
|
||||
unsigned int display_offset,
|
||||
const void *buffer,
|
||||
unsigned int len)
|
||||
{
|
||||
const uint8_t *buf = buffer;
|
||||
static const char *hex = "0123456789abcdef";
|
||||
char line[80];
|
||||
unsigned int o = 0;
|
||||
const int line_l = 16;
|
||||
if (prompt)
|
||||
printf("%s (%d/0x%x):\n", prompt, len, len);
|
||||
for (unsigned int i = 0; i < len; ) {
|
||||
memset(line, ' ', 80 - 1);
|
||||
char *dst = line;
|
||||
char *txt = dst + (line_l * 3);
|
||||
unsigned int so = o;
|
||||
for (int c = 0; c < line_l && i < len; c++, i++, o++) {
|
||||
uint8_t b = *buf++;
|
||||
*dst++ = hex[b >> 4];
|
||||
*dst++ = hex[b & 0xf];
|
||||
dst++; // space
|
||||
*txt++ = b < ' ' || b > 126 ? '.' : b;
|
||||
}
|
||||
*txt = 0;
|
||||
printf("%04x: %s\n", display_offset + so, line);
|
||||
}
|
||||
}
|
||||
|
||||
static const char * apple2_charset =
|
||||
"@ABCDEFGHIJKLMNO"
|
||||
"PQRSTUVWXYZ[\\]^_"
|
||||
" !\"#$%&'()*+,-./"
|
||||
"0123456789:;<=>?"
|
||||
"................"
|
||||
"................"
|
||||
"`abcdefghijklmno"
|
||||
"pqrstuvwxyz{|}~";
|
||||
|
||||
void
|
||||
_mii_mish_text(
|
||||
@@ -39,7 +79,7 @@ _mii_mish_text(
|
||||
printf("%04x: ", a);
|
||||
for (int ci = 0; ci < 40; ci++) {
|
||||
uint8_t c = (mii_read_one(mii, a++) & 0x3f);
|
||||
printf("%c", c >= 0x20 && c < 0x7f ? c : '.');
|
||||
printf("%c", apple2_charset[c]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
@@ -411,6 +451,7 @@ _mii_mish_step(
|
||||
}
|
||||
|
||||
#include <math.h>
|
||||
extern int mii_speaker_debug;
|
||||
|
||||
static void
|
||||
_mii_mish_audio(
|
||||
@@ -421,28 +462,33 @@ _mii_mish_audio(
|
||||
mii_t * mii = param;
|
||||
if (argc < 2) {
|
||||
printf("audio volume: %.3f multiplier:%.3f muted:%d\n",
|
||||
mii->speaker.volume, mii->speaker.vol_multiplier,
|
||||
mii->speaker.muted);
|
||||
mii->speaker.source.volume,
|
||||
mii->speaker.source.vol_multiplier,
|
||||
mii->audio.muted);
|
||||
return;
|
||||
}
|
||||
if (!strcmp(argv[1], "record")) {
|
||||
if (mii->speaker.debug_fd != -1) {
|
||||
close(mii->speaker.debug_fd);
|
||||
mii->speaker.debug_fd = -1;
|
||||
#if 1
|
||||
if (mii_speaker_debug) {
|
||||
// close(mii_speaker_debug_fd);
|
||||
// mii_speaker_debug_fd = -1;
|
||||
mii_speaker_debug = 0;
|
||||
printf("audio: stop recording\n");
|
||||
} else {
|
||||
mii->speaker.debug_fd = open("audio.raw",
|
||||
O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
mii_speaker_debug = 1;
|
||||
// mii_speaker_debug_fd = open("audio.raw",
|
||||
// O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
printf("audio: start recording\n");
|
||||
}
|
||||
#endif
|
||||
} else if (!strcmp(argv[1], "mute")) {
|
||||
if (argv[2] && !strcmp(argv[2], "off"))
|
||||
mii->speaker.muted = false;
|
||||
mii->audio.muted = false;
|
||||
else if (argv[2] && !strcmp(argv[2], "on"))
|
||||
mii->speaker.muted = true;
|
||||
mii->audio.muted = true;
|
||||
else if (!argv[2] || (argv[2] && !strcmp(argv[2], "toggle")))
|
||||
mii->speaker.muted = !mii->speaker.muted;
|
||||
printf("audio: %s\n", mii->speaker.muted ? "muted" : "unmuted");
|
||||
mii->audio.muted = !mii->audio.muted;
|
||||
printf("audio: %s\n", mii->audio.muted ? "muted" : "unmuted");
|
||||
} else if (!strcmp(argv[1], "volume")) {
|
||||
if (argc < 3) {
|
||||
printf("audio: missing volume\n");
|
||||
@@ -452,9 +498,9 @@ _mii_mish_audio(
|
||||
float vol = atof(argv[2]);
|
||||
if (vol < 0) vol = 0;
|
||||
else if (vol > 10) vol = 10;
|
||||
mii_speaker_volume(&mii->speaker, vol);
|
||||
mii_audio_volume(&mii->speaker.source, vol);
|
||||
printf("audio: volume %.3f (amp: %.4f)\n",
|
||||
vol, mii->speaker.vol_multiplier);
|
||||
vol, mii->speaker.source.vol_multiplier);
|
||||
} else {
|
||||
printf("audio: unknown command %s\n", argv[1]);
|
||||
}
|
||||
|
@@ -11,81 +11,133 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "mii.h"
|
||||
#include "mii_speaker.h"
|
||||
|
||||
// one frame of audio per frame of video?
|
||||
#define MII_SPEAKER_FRAME_SIZE (MII_SPEAKER_FREQ / 60)
|
||||
|
||||
// TODO Make some sort of driver for audio and move alsa code there
|
||||
#ifdef HAS_ALSA
|
||||
#include <alsa/asoundlib.h>
|
||||
#define MII_SPEAKER_BASE_SAMPLE 0.5f
|
||||
#define MII_SPEAKER_RAMP_ON 16
|
||||
#define MII_SPEAKER_RAMP_OFF 128
|
||||
|
||||
#define PCM_DEVICE "default"
|
||||
int mii_speaker_debug = 0;
|
||||
int mii_speaker_debug_fd = -1;
|
||||
|
||||
static int
|
||||
_alsa_init(
|
||||
mii_speaker_t *s)
|
||||
static inline void
|
||||
_mii_speaker_write(
|
||||
mii_audio_frame_t *f,
|
||||
mii_audio_sample_t sample,
|
||||
bool start, bool stop)
|
||||
{
|
||||
int pcm;
|
||||
unsigned int rate = 44100, channels = 1;
|
||||
snd_pcm_t *alsa;
|
||||
snd_pcm_hw_params_t *params;
|
||||
snd_pcm_uframes_t frames;
|
||||
|
||||
/* Open the PCM device in playback mode */
|
||||
if ((pcm = snd_pcm_open(&alsa, PCM_DEVICE,
|
||||
SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
||||
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
|
||||
PCM_DEVICE, snd_strerror(pcm));
|
||||
|
||||
/* Allocate parameters object and fill it with default values*/
|
||||
snd_pcm_hw_params_alloca(¶ms);
|
||||
snd_pcm_hw_params_any(alsa, params);
|
||||
|
||||
/* Set parameters */
|
||||
if ((pcm = snd_pcm_hw_params_set_access(alsa, params,
|
||||
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
printf("ERROR: Can't set interleaved mode. %s\n",
|
||||
snd_strerror(pcm));
|
||||
|
||||
if ((pcm = snd_pcm_hw_params_set_format(alsa, params,
|
||||
SND_PCM_FORMAT_S16_LE)) < 0)
|
||||
printf("ERROR: Can't set format. %s\n",
|
||||
snd_strerror(pcm));
|
||||
if ((pcm = snd_pcm_hw_params_set_channels(alsa, params, channels)) < 0)
|
||||
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
|
||||
|
||||
if ((pcm = snd_pcm_hw_params_set_rate_near(alsa, params, &rate, 0)) < 0)
|
||||
printf("ERROR: Can't set rate. %s\n",
|
||||
snd_strerror(pcm));
|
||||
frames = MII_SPEAKER_FRAME_SIZE;
|
||||
/* Write parameters */
|
||||
if ((pcm = snd_pcm_hw_params(alsa, params)) < 0)
|
||||
printf("ERROR: Can't set harware parameters. %s\n",
|
||||
snd_strerror(pcm));
|
||||
// printf("%s frames want %d got %ld\n",
|
||||
// __func__, MII_SPEAKER_FRAME_SIZE, frames);
|
||||
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
snd_pcm_sw_params_alloca (&sw_params);
|
||||
snd_pcm_sw_params_current (alsa, sw_params);
|
||||
snd_pcm_sw_params_set_start_threshold(alsa, sw_params, frames * 4);
|
||||
snd_pcm_sw_params_set_avail_min(alsa, sw_params, frames*4);
|
||||
snd_pcm_sw_params(alsa, sw_params);
|
||||
|
||||
s->fsize = frames;
|
||||
s->alsa_pcm = alsa;
|
||||
snd_pcm_prepare(s->alsa_pcm);
|
||||
return 0;
|
||||
mii_audio_frame_write(f, sample);
|
||||
static int mii_speaker_debug_fd = -1;
|
||||
if (!mii_speaker_debug) {
|
||||
if (mii_speaker_debug_fd != -1)
|
||||
close(mii_speaker_debug_fd);
|
||||
mii_speaker_debug_fd = -1;
|
||||
return;
|
||||
}
|
||||
if (start) {
|
||||
if (mii_speaker_debug_fd != -1)
|
||||
close(mii_speaker_debug_fd);
|
||||
mii_speaker_debug_fd = open("speaker.raw",
|
||||
O_CREAT|O_TRUNC|O_WRONLY, 0644);
|
||||
}
|
||||
if (mii_speaker_debug_fd != -1)
|
||||
write(mii_speaker_debug_fd, &sample, sizeof(sample));
|
||||
#if 0
|
||||
if (stop) {
|
||||
close(mii_speaker_debug_fd);
|
||||
mii_speaker_debug_fd = -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
_mii_speaker_pad(
|
||||
mii_speaker_t *s,
|
||||
bool click)
|
||||
{
|
||||
mii_audio_frame_t *f = &s->source.fifo;
|
||||
uint64_t now = s->mii->cpu.total_cycle;
|
||||
|
||||
// printf("pad: %d %d\n", s->play_state, click);
|
||||
do {
|
||||
switch(s->source.state) {
|
||||
case MII_AUDIO_IDLE:
|
||||
// nothing to do
|
||||
if (click) {
|
||||
s->source.state = MII_AUDIO_STARTING;
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
case MII_AUDIO_STARTING:
|
||||
// we are starting, so we need to pad the start of the frame
|
||||
// with a small attack to soften the beeps
|
||||
// printf("%s started avail W:%5d\n",
|
||||
// __func__, mii_audio_frame_get_write_size(f));
|
||||
mii_audio_sample_t attack = -s->sample;
|
||||
for (int i = MII_SPEAKER_RAMP_ON; i >= 1; i--)
|
||||
_mii_speaker_write(f, (attack / i),
|
||||
i == MII_SPEAKER_RAMP_ON, false);
|
||||
s->source.state = MII_AUDIO_PLAYING;
|
||||
s->last_fill_cycle = now;
|
||||
break;
|
||||
case MII_AUDIO_PLAYING: {
|
||||
uint64_t last_click = (now - s->last_click_cycle) /
|
||||
s->source.sink->clk_per_sample;
|
||||
if (last_click > (MII_AUDIO_FREQ / 64)) {
|
||||
// printf("%s stopping\n", __func__);
|
||||
s->source.state = MII_AUDIO_STOPPING;
|
||||
// continue;
|
||||
}
|
||||
// write padding
|
||||
uint64_t fill_amount = (now - s->last_fill_cycle) /
|
||||
s->source.sink->clk_per_sample;
|
||||
// printf("play %d pad %5ld/%5ld rds %5d\n", click,
|
||||
// fill_amount, last_click,
|
||||
// mii_audio_frame_get_read_size(f));
|
||||
while (fill_amount > 0 && !mii_audio_frame_isfull(f)) {
|
||||
_mii_speaker_write(f, s->sample, false, false);
|
||||
fill_amount--;
|
||||
}
|
||||
s->last_fill_cycle = now;
|
||||
// printf(" fifo state W:%4d R:%4d\n",
|
||||
// mii_audio_frame_get_write_size(f),
|
||||
// mii_audio_frame_get_read_size(f));
|
||||
} break;
|
||||
case MII_AUDIO_STOPPING: {
|
||||
// printf("%s stopped\n", __func__);
|
||||
// we are stopping, so we need to pad the end of the frame
|
||||
// with a small tailoff to soften the beeps
|
||||
mii_audio_sample_t tail = s->sample;
|
||||
for (int i = 1; i <= MII_SPEAKER_RAMP_OFF; i++)
|
||||
_mii_speaker_write(f, tail / i,
|
||||
false, i == MII_SPEAKER_RAMP_OFF);
|
||||
s->source.state = MII_AUDIO_IDLE;
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
} while(1);
|
||||
|
||||
if (click) {
|
||||
s->last_click_cycle = now;
|
||||
s->sample = -s->sample;
|
||||
_mii_speaker_write(f, s->sample, false, false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint64_t
|
||||
_mii_speaker_timer_cb(
|
||||
mii_t * mii,
|
||||
void * param );
|
||||
void * param )
|
||||
{
|
||||
mii_speaker_t *s = (mii_speaker_t*)param;
|
||||
_mii_speaker_pad(s, false);
|
||||
return s->source.state == MII_AUDIO_IDLE ? 0 :
|
||||
(MII_AUDIO_FRAME_SIZE / 2) * s->source.sink->clk_per_sample;
|
||||
}
|
||||
|
||||
// Initialize the speaker with the frame size in samples
|
||||
void
|
||||
@@ -94,182 +146,28 @@ mii_speaker_init(
|
||||
mii_speaker_t *s)
|
||||
{
|
||||
s->mii = mii;
|
||||
s->debug_fd = -1;
|
||||
s->fsize = MII_SPEAKER_FRAME_SIZE;
|
||||
s->sample = -MII_SPEAKER_BASE_SAMPLE;
|
||||
s->source.state = MII_AUDIO_IDLE;
|
||||
mii_audio_add_source(&mii->audio, &s->source);
|
||||
// disabled at start...
|
||||
s->timer_id = mii_timer_register(mii,
|
||||
_mii_speaker_timer_cb, s, 0, __func__);
|
||||
#ifdef HAS_ALSA
|
||||
printf("%s audio is %s\n", __func__, s->speaker_off ? "off" : "on");
|
||||
if (!s->speaker_off)
|
||||
_alsa_init(s); // this can/will change fsize
|
||||
#endif
|
||||
mii_speaker_volume(s, 1);
|
||||
s->sample = 0x8000;
|
||||
s->findex = 0;
|
||||
for (int i = 0; i < MII_SPEAKER_FRAME_COUNT; i++)
|
||||
s->frame[i].audio = calloc(sizeof(s->frame[i].audio[0]), s->fsize);
|
||||
}
|
||||
|
||||
void
|
||||
mii_speaker_dispose(
|
||||
mii_speaker_t *s)
|
||||
{
|
||||
s->fsize = 0;
|
||||
mii_timer_set(s->mii, s->timer_id, 0);
|
||||
#ifdef HAS_ALSA
|
||||
if (s->alsa_pcm)
|
||||
snd_pcm_close(s->alsa_pcm);
|
||||
#endif
|
||||
for (int i = 0; i < MII_SPEAKER_FRAME_COUNT; i++) {
|
||||
free(s->frame[i].audio);
|
||||
s->frame[i].audio = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if there's a new frame to send, send it
|
||||
// this timer is always running; it keeps checking for non-empty frames
|
||||
static uint64_t
|
||||
_mii_speaker_timer_cb(
|
||||
mii_t * mii,
|
||||
void * param )
|
||||
{
|
||||
mii_speaker_t *s = (mii_speaker_t *)param;
|
||||
|
||||
if (s->muted || s->speaker_off)
|
||||
goto done;
|
||||
mii_audio_frame_t *f = &s->frame[s->fplay];
|
||||
// if the frame is empty, we mark the fact we are in underrun,
|
||||
// so we can restart the audio later on.
|
||||
if (!f->fill) {
|
||||
if (s->under < 10)
|
||||
s->under++;
|
||||
goto done;
|
||||
}
|
||||
s->under = 0;
|
||||
// Here we got a frame to play, so we play it, and move on to the next
|
||||
// There's also the case were we stopped playing and the last frame
|
||||
// wasn't complete, in which case we pad it, and flush it as well
|
||||
// printf("%s: fplay %d findex %d fsize %d fill %d\n",
|
||||
// __func__, s->fplay, s->findex, s->fsize, f->fill);
|
||||
uint16_t sample = f->audio[f->fill - 1] ^ 0xffff;
|
||||
while (f->fill < s->fsize)
|
||||
f->audio[f->fill++] = sample;
|
||||
s->fplay = (s->fplay + 1) % MII_SPEAKER_FRAME_COUNT;
|
||||
s->frame[s->fplay].fill = 0;
|
||||
if (!s->muted) {
|
||||
if (s->debug_fd != -1)
|
||||
write(s->debug_fd, f->audio,
|
||||
f->fill * sizeof(s->frame[0].audio[0]));
|
||||
#ifdef HAS_ALSA
|
||||
if (s->alsa_pcm) {
|
||||
int pcm;
|
||||
if ((pcm = snd_pcm_writei(s->alsa_pcm,
|
||||
f->audio, f->fill)) == -EPIPE) {
|
||||
printf("%s Underrun.\n", __func__);
|
||||
snd_pcm_recover(s->alsa_pcm, pcm, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
done:
|
||||
return s->fsize * s->clk_per_sample;
|
||||
}
|
||||
|
||||
// Called when $c030 is touched, place a sample at the 'appropriate' time
|
||||
void
|
||||
mii_speaker_click(
|
||||
mii_speaker_t *s)
|
||||
{
|
||||
// if CPU speed has changed, recalculate the number of cycles per sample
|
||||
if (s->cpu_speed != s->mii->speed) {
|
||||
s->cpu_speed = s->mii->speed;
|
||||
s->clk_per_sample = ((1000000.0 /* / s->mii->speed */) /
|
||||
(float)MII_SPEAKER_FREQ) + 0.5f;
|
||||
printf("%s: %.2f cycles per sample\n", __func__, s->clk_per_sample);
|
||||
mii_timer_set(s->mii, s->timer_id, s->fsize * s->clk_per_sample);
|
||||
}
|
||||
int64_t remains = mii_timer_get(s->mii, s->timer_id);
|
||||
|
||||
mii_audio_frame_t *f = &s->frame[s->findex];
|
||||
// if we had stopped playing for 2 frames, restart
|
||||
if (s->under > 1) {
|
||||
s->under = 0;
|
||||
// printf("Restarting playback\n");
|
||||
#ifdef HAS_ALSA
|
||||
if (s->alsa_pcm)
|
||||
snd_pcm_prepare(s->alsa_pcm);
|
||||
#endif
|
||||
f->fill = 0;
|
||||
// add a small attack to the start of the frame to soften the beeps
|
||||
// we are going to flip the sample, so we need to preemptively
|
||||
// flip the attack as well
|
||||
mii_audio_sample_t attack = s->sample ^ 0xffff;
|
||||
for (int i = 8; i >= 1; i--)
|
||||
f->audio[f->fill++] = (attack / i) * s->vol_multiplier;
|
||||
s->fplay = s->findex; // restart here
|
||||
mii_timer_set(s->mii, s->timer_id, (s->fsize - 16) * s->clk_per_sample);
|
||||
remains = mii_timer_get(s->mii, s->timer_id);
|
||||
}
|
||||
// calculate the sample index we are going to fill -- this is relative
|
||||
// to the frame we are waiting to play
|
||||
long sample_index = // (s->fsize / 2) +
|
||||
(((s->fsize * s->clk_per_sample) - remains) /
|
||||
s->clk_per_sample);
|
||||
// fill from last sample to here with the current sample
|
||||
for (; f->fill < sample_index && f->fill < s->fsize; f->fill++)
|
||||
f->audio[f->fill] = s->sample * s->vol_multiplier;
|
||||
// printf("%s: findex %d fsize %d fill %d sample_index %ld\n",
|
||||
// __func__, s->findex, s->fsize, f->fill, sample_index);
|
||||
// if we've gone past the end of the frame, switch to the next one
|
||||
if (sample_index >= s->fsize || sample_index < f->fill) {
|
||||
sample_index = sample_index % s->fsize;
|
||||
s->findex = (s->findex + 1) % MII_SPEAKER_FRAME_COUNT;
|
||||
f = &s->frame[s->findex];
|
||||
f->fill = 0;
|
||||
// fill from start of this frame to newly calculated sample_index
|
||||
for (; f->fill < sample_index && f->fill < s->fsize; f->fill++)
|
||||
f->audio[f->fill] = s->sample * s->vol_multiplier;
|
||||
f->audio[sample_index] = 0;
|
||||
}
|
||||
s->sample ^= 0xffff;
|
||||
// if we are touching a new sample, make sure the next one is clear too.
|
||||
#if 1
|
||||
int32_t mix = s->sample * s->vol_multiplier;
|
||||
#else
|
||||
/* Mixing code; in case theres multiple clicks within a sample period.
|
||||
* This is not used, because it's not really needed, as 2 clicks
|
||||
* would cancel each others anyway. */
|
||||
if (!f->audio[sample_index] && sample_index < s->fsize)
|
||||
f->audio[sample_index + 1] = 0;
|
||||
int32_t mix = f->audio[sample_index] + (s->sample * s->vol_multiplier);
|
||||
if (mix > 0x7fff) mix = 0x7fff;
|
||||
else if (mix < -0x8000) mix = -0x8000;
|
||||
#endif
|
||||
f->audio[sample_index] = mix;
|
||||
_mii_speaker_pad(s, true);
|
||||
mii_timer_set(s->mii, s->timer_id,
|
||||
(MII_AUDIO_FRAME_SIZE / 2) * s->source.sink->clk_per_sample);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// this is here so we dont' have to drag in libm math library.
|
||||
double fastPow(double a, double b) {
|
||||
union { double d; int32_t x[2]; } u = { .d = a };
|
||||
u.x[1] = (int)(b * (u.x[1] - 1072632447) + 1072632447);
|
||||
u.x[0] = 0;
|
||||
return u.d;
|
||||
}
|
||||
|
||||
// take the volume from 0 to 10, save it, convert it to a multiplier
|
||||
void
|
||||
mii_speaker_volume(
|
||||
mii_speaker_t *s,
|
||||
float volume)
|
||||
{
|
||||
if (volume < 0) volume = 0;
|
||||
else if (volume > 10) volume = 10;
|
||||
double mul = (fastPow(10.0, volume / 10.0) / 10.0) - 0.09;
|
||||
s->vol_multiplier = mul;
|
||||
s->volume = volume;
|
||||
|
||||
// printf("audio: speaker volume set to %.3f (%.4f)\n", volume, mul);
|
||||
}
|
@@ -9,36 +9,17 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define MII_SPEAKER_FREQ (44100)
|
||||
#define MII_SPEAKER_FRAME_COUNT 4
|
||||
#include "mii_audio.h"
|
||||
|
||||
|
||||
struct mii_t;
|
||||
struct _snd_pcm;
|
||||
typedef int16_t mii_audio_sample_t;
|
||||
|
||||
typedef struct mii_audio_frame_t {
|
||||
uint16_t fill;
|
||||
mii_audio_sample_t *audio;
|
||||
} mii_audio_frame_t;
|
||||
|
||||
typedef struct mii_speaker_t {
|
||||
struct mii_t *mii;
|
||||
uint8_t timer_id; // timer id for the audio thread
|
||||
int debug_fd; // if > 0, dump audio to this fd
|
||||
struct _snd_pcm *alsa_pcm; // alsa pcm handle
|
||||
uint muted : 1; // if true, don't play anything
|
||||
uint speaker_off : 1; // if true, don't even initialize (alsa)
|
||||
float volume; // volume, 0.0 to 10.0
|
||||
float vol_multiplier; // sample multiplier, 0.0 to 1.0
|
||||
float cpu_speed; // CPU speed in MHz, to calculate clk_per_sample
|
||||
uint8_t under; // number of frames we've skipped
|
||||
uint8_t fplay; // frame we want to play
|
||||
uint16_t fsize; // size in samples of a frame
|
||||
uint8_t findex; // frame we are currently filling
|
||||
// number of cycles per sample (at current CPU speed)
|
||||
float clk_per_sample;
|
||||
mii_audio_sample_t sample; // current value for the speaker output
|
||||
mii_audio_frame_t frame[MII_SPEAKER_FRAME_COUNT];
|
||||
struct mii_t * mii;
|
||||
uint8_t timer_id;
|
||||
mii_audio_sample_t sample; // current value for the speaker output
|
||||
mii_audio_source_t source;
|
||||
uint64_t last_click_cycle, last_fill_cycle;
|
||||
} mii_speaker_t;
|
||||
|
||||
// Initialize the speaker with the frame size in samples
|
||||
@@ -53,12 +34,3 @@ mii_speaker_dispose(
|
||||
void
|
||||
mii_speaker_click(
|
||||
mii_speaker_t *speaker);
|
||||
// Check to see if there's a new frame to send, send it
|
||||
//void
|
||||
//mii_speaker_run(
|
||||
// mii_speaker_t *speaker);
|
||||
// volume from 0 to 10, sets the audio sample multiplier.
|
||||
void
|
||||
mii_speaker_volume(
|
||||
mii_speaker_t *s,
|
||||
float volume);
|
||||
|
@@ -140,3 +140,9 @@ static const char __unused__ *mii_sw_names[] = {
|
||||
SWW_SETSTATE((_mii)->sw_state, _sw, _state)
|
||||
#define SW_GETSTATE(_mii, _sw) \
|
||||
SWW_GETSTATE((_mii)->sw_state, _sw)
|
||||
|
||||
// set bit 8 of a byte to the state of a softswitch
|
||||
#define SWW_READ(_byte, _bits, _sw) \
|
||||
(_byte) = ((_byte) & 0x7f) | (SWW_GETSTATE(_bits, _sw) << 7)
|
||||
#define SW_READ(_byte, _mii, _sw) \
|
||||
SWW_READ(_byte, (_mii)->sw_state, _sw)
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "mii.h"
|
||||
#include "mii_bank.h"
|
||||
#include "mii_rom_iiee_video.h"
|
||||
#include "mii_rom_iic_video.h"
|
||||
#include "mii_sw.h"
|
||||
#include "minipt.h"
|
||||
|
||||
@@ -537,6 +538,8 @@ _mii_line_render_text(
|
||||
int i = mii->video.line >> 3;
|
||||
a += ((i & 0x07) << 7) | ((i >> 3) << 5) | ((i >> 3) << 3);
|
||||
mii->video.line_addr = a;
|
||||
const uint8_t *rom_base = mii->emu == MII_EMU_IIEE ?
|
||||
mii_rom_iiee_video : mii_rom_iic_video;
|
||||
|
||||
bool col80 = SW_GETSTATE(mii, SW80COL);
|
||||
bool altset = SW_GETSTATE(mii, SWALTCHARSET);
|
||||
@@ -555,7 +558,7 @@ _mii_line_render_text(
|
||||
if (c >= 0x40 && c <= 0x7f)
|
||||
c = (int)c + flash;
|
||||
}
|
||||
const uint8_t * rom = iie_enhanced_video + (c << 3);
|
||||
const uint8_t * rom = rom_base + (c << 3);
|
||||
uint8_t bits = rom[mii->video.line & 0x07];
|
||||
for (int pi = 0; pi < 7; pi++) {
|
||||
uint8_t pixel = (bits >> pi) & 1;
|
||||
@@ -902,8 +905,9 @@ mii_access_video(
|
||||
case SWALTCHARSET:
|
||||
case SWRDDHIRES:
|
||||
res = true;
|
||||
/* we OR the return flag, as the lower 7 bits are keyboard related */
|
||||
if (!write)
|
||||
*byte = mii_bank_peek(sw, addr);
|
||||
*byte |= mii_bank_peek(sw, addr);
|
||||
break;
|
||||
case SWHIRESOFF:
|
||||
case SWHIRESON:
|
||||
@@ -967,6 +971,8 @@ mii_access_video(
|
||||
SW_SETSTATE(mii, SWMIXED, addr & 1);
|
||||
mii_bank_poke(sw, SWMIXED, (addr & 1) << 7);
|
||||
_mii_video_mode_changed(mii);
|
||||
if (!write)
|
||||
*byte = mii_video_get_vapor(mii);
|
||||
break;
|
||||
}
|
||||
return res;
|
||||
|
87386
src/roms/mii_rom_epromcard.h
Normal file
87386
src/roms/mii_rom_epromcard.h
Normal file
File diff suppressed because it is too large
Load Diff
2735
src/roms/mii_rom_iic.h
Normal file
2735
src/roms/mii_rom_iic.h
Normal file
File diff suppressed because it is too large
Load Diff
346
src/roms/mii_rom_iic_video.h
Normal file
346
src/roms/mii_rom_iic_video.h
Normal file
@@ -0,0 +1,346 @@
|
||||
#pragma once
|
||||
static const unsigned char mii_rom_iic_video[] = {
|
||||
0x1c, 0x22, 0x2a, 0x3a, 0x1a, 0x02, 0x3c, 0x00, 0x08, 0x14, 0x22, 0x22,
|
||||
0x3e, 0x22, 0x22, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e, 0x00,
|
||||
0x1c, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1c, 0x00, 0x1e, 0x22, 0x22, 0x22,
|
||||
0x22, 0x22, 0x1e, 0x00, 0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x3e, 0x00,
|
||||
0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02, 0x00, 0x3c, 0x02, 0x02, 0x02,
|
||||
0x32, 0x22, 0x3c, 0x00, 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00,
|
||||
0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x22, 0x1c, 0x00, 0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22, 0x00,
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e, 0x00, 0x22, 0x36, 0x2a, 0x2a,
|
||||
0x22, 0x22, 0x22, 0x00, 0x22, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x22, 0x00,
|
||||
0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x1e, 0x22, 0x22, 0x1e,
|
||||
0x02, 0x02, 0x02, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x2a, 0x12, 0x2c, 0x00,
|
||||
0x1e, 0x22, 0x22, 0x1e, 0x0a, 0x12, 0x22, 0x00, 0x1c, 0x22, 0x02, 0x1c,
|
||||
0x20, 0x22, 0x1c, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x22, 0x22, 0x22, 0x22,
|
||||
0x22, 0x14, 0x08, 0x00, 0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22, 0x00,
|
||||
0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, 0x22, 0x22, 0x14, 0x08,
|
||||
0x08, 0x08, 0x08, 0x00, 0x3e, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x00,
|
||||
0x3e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3e, 0x00, 0x00, 0x02, 0x04, 0x08,
|
||||
0x10, 0x20, 0x00, 0x00, 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00,
|
||||
0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, 0x14, 0x14, 0x14, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x14, 0x14, 0x3e, 0x14, 0x3e, 0x14, 0x14, 0x00,
|
||||
0x08, 0x3c, 0x0a, 0x1c, 0x28, 0x1e, 0x08, 0x00, 0x06, 0x26, 0x10, 0x08,
|
||||
0x04, 0x32, 0x30, 0x00, 0x04, 0x0a, 0x0a, 0x04, 0x2a, 0x12, 0x2c, 0x00,
|
||||
0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x04, 0x02, 0x02,
|
||||
0x02, 0x04, 0x08, 0x00, 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00,
|
||||
0x08, 0x2a, 0x1c, 0x08, 0x1c, 0x2a, 0x08, 0x00, 0x00, 0x08, 0x08, 0x3e,
|
||||
0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x08, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00,
|
||||
0x1c, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x1c, 0x00, 0x08, 0x0c, 0x08, 0x08,
|
||||
0x08, 0x08, 0x1c, 0x00, 0x1c, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3e, 0x00,
|
||||
0x3e, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1c, 0x00, 0x10, 0x18, 0x14, 0x12,
|
||||
0x3e, 0x10, 0x10, 0x00, 0x3e, 0x02, 0x1e, 0x20, 0x20, 0x22, 0x1c, 0x00,
|
||||
0x38, 0x04, 0x02, 0x1e, 0x22, 0x22, 0x1c, 0x00, 0x3e, 0x20, 0x10, 0x08,
|
||||
0x04, 0x04, 0x04, 0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c, 0x00,
|
||||
0x1c, 0x22, 0x22, 0x3c, 0x20, 0x10, 0x0e, 0x00, 0x00, 0x00, 0x08, 0x00,
|
||||
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04, 0x00,
|
||||
0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x3e, 0x00,
|
||||
0x3e, 0x00, 0x00, 0x00, 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00,
|
||||
0x1c, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00, 0xef, 0xf7, 0xc9, 0x80,
|
||||
0xc0, 0xc0, 0x81, 0xc9, 0xef, 0xf7, 0xc9, 0xbe, 0xde, 0xde, 0xb5, 0xc9,
|
||||
0xff, 0xff, 0xfd, 0xf9, 0xf1, 0xe1, 0xc9, 0xbd, 0x80, 0xdd, 0xeb, 0xf7,
|
||||
0xf7, 0xeb, 0xd5, 0x80, 0xff, 0xbf, 0xdf, 0xee, 0xf5, 0xfb, 0xfb, 0xff,
|
||||
0x80, 0xc0, 0xa0, 0x93, 0x8a, 0x84, 0x84, 0x80, 0x8f, 0x9f, 0x81, 0xce,
|
||||
0x86, 0xcf, 0xc0, 0xfd, 0xff, 0xe7, 0xf8, 0xff, 0xf8, 0xf3, 0xf7, 0x8f,
|
||||
0xf7, 0xfb, 0xfd, 0x80, 0xfd, 0xfb, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xd5, 0xf7, 0xf7, 0xf7, 0xf7, 0xb6, 0xd5, 0xe3, 0xf7,
|
||||
0xf7, 0xe3, 0xd5, 0xb6, 0xf7, 0xf7, 0xf7, 0xf7, 0x80, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xbf, 0xbf, 0xbf, 0xbb, 0xb9, 0x80, 0xf9, 0xfb,
|
||||
0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xec, 0xe7, 0xe3, 0x81,
|
||||
0xe3, 0xe7, 0xef, 0x90, 0x9b, 0xf3, 0xe3, 0xc0, 0xe3, 0xf3, 0xfb, 0x84,
|
||||
0xbf, 0xb7, 0xf7, 0x80, 0xc1, 0xe3, 0xb7, 0xbf, 0xbf, 0xb7, 0xe3, 0xc1,
|
||||
0x80, 0xf7, 0xb7, 0xbf, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff,
|
||||
0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0x80, 0xf7, 0xef, 0xdf, 0x80,
|
||||
0xdf, 0xef, 0xf7, 0xff, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa,
|
||||
0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xaa, 0xd5, 0xff, 0xc1, 0xbe, 0xfe,
|
||||
0xfe, 0xfe, 0x80, 0xff, 0xff, 0xff, 0xc0, 0xbf, 0xbf, 0xbf, 0x80, 0xff,
|
||||
0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xf7, 0xe3, 0xc1, 0x80,
|
||||
0xc1, 0xe3, 0xf7, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
|
||||
0xeb, 0xeb, 0x88, 0xff, 0x88, 0xeb, 0xeb, 0xff, 0x80, 0xbf, 0xbf, 0xb3,
|
||||
0xb3, 0xbf, 0xbf, 0x80, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
|
||||
0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x20,
|
||||
0x3c, 0x22, 0x3c, 0x00, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x00,
|
||||
0x00, 0x00, 0x3c, 0x02, 0x02, 0x02, 0x3c, 0x00, 0x20, 0x20, 0x3c, 0x22,
|
||||
0x22, 0x22, 0x3c, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x3e, 0x02, 0x3c, 0x00,
|
||||
0x18, 0x24, 0x04, 0x1e, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x22,
|
||||
0x22, 0x3c, 0x20, 0x1c, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00,
|
||||
0x08, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x10, 0x00, 0x18, 0x10,
|
||||
0x10, 0x10, 0x12, 0x0c, 0x02, 0x02, 0x22, 0x12, 0x0e, 0x12, 0x22, 0x00,
|
||||
0x0c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00, 0x00, 0x00, 0x36, 0x2a,
|
||||
0x2a, 0x2a, 0x22, 0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00,
|
||||
0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00, 0x00, 0x00, 0x1e, 0x22,
|
||||
0x22, 0x1e, 0x02, 0x02, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x20,
|
||||
0x00, 0x00, 0x3a, 0x06, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x3c, 0x02,
|
||||
0x1c, 0x20, 0x1e, 0x00, 0x04, 0x04, 0x1e, 0x04, 0x04, 0x24, 0x18, 0x00,
|
||||
0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2c, 0x00, 0x00, 0x00, 0x22, 0x22,
|
||||
0x22, 0x14, 0x08, 0x00, 0x00, 0x00, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x00,
|
||||
0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, 0x22, 0x22,
|
||||
0x22, 0x3c, 0x20, 0x1c, 0x00, 0x00, 0x3e, 0x10, 0x08, 0x04, 0x3e, 0x00,
|
||||
0x38, 0x0c, 0x0c, 0x06, 0x0c, 0x0c, 0x38, 0x00, 0x08, 0x08, 0x08, 0x08,
|
||||
0x08, 0x08, 0x08, 0x08, 0x0e, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0e, 0x00,
|
||||
0x2c, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x14, 0x2a,
|
||||
0x14, 0x2a, 0x00, 0x00, 0xe3, 0xdd, 0xd5, 0xc5, 0xe5, 0xfd, 0xc3, 0xff,
|
||||
0xf7, 0xeb, 0xdd, 0xdd, 0xc1, 0xdd, 0xdd, 0xff, 0xe1, 0xdd, 0xdd, 0xe1,
|
||||
0xdd, 0xdd, 0xe1, 0xff, 0xe3, 0xdd, 0xfd, 0xfd, 0xfd, 0xdd, 0xe3, 0xff,
|
||||
0xe1, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xe1, 0xff, 0xc1, 0xfd, 0xfd, 0xe1,
|
||||
0xfd, 0xfd, 0xc1, 0xff, 0xc1, 0xfd, 0xfd, 0xe1, 0xfd, 0xfd, 0xfd, 0xff,
|
||||
0xc3, 0xfd, 0xfd, 0xfd, 0xcd, 0xdd, 0xc3, 0xff, 0xdd, 0xdd, 0xdd, 0xc1,
|
||||
0xdd, 0xdd, 0xdd, 0xff, 0xe3, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xe3, 0xff,
|
||||
0xdf, 0xdf, 0xdf, 0xdf, 0xdf, 0xdd, 0xe3, 0xff, 0xdd, 0xed, 0xf5, 0xf9,
|
||||
0xf5, 0xed, 0xdd, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xc1, 0xff,
|
||||
0xdd, 0xc9, 0xd5, 0xd5, 0xdd, 0xdd, 0xdd, 0xff, 0xdd, 0xdd, 0xd9, 0xd5,
|
||||
0xcd, 0xdd, 0xdd, 0xff, 0xe3, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xe3, 0xff,
|
||||
0xe1, 0xdd, 0xdd, 0xe1, 0xfd, 0xfd, 0xfd, 0xff, 0xe3, 0xdd, 0xdd, 0xdd,
|
||||
0xd5, 0xed, 0xd3, 0xff, 0xe1, 0xdd, 0xdd, 0xe1, 0xf5, 0xed, 0xdd, 0xff,
|
||||
0xe3, 0xdd, 0xfd, 0xe3, 0xdf, 0xdd, 0xe3, 0xff, 0xc1, 0xf7, 0xf7, 0xf7,
|
||||
0xf7, 0xf7, 0xf7, 0xff, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xe3, 0xff,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xeb, 0xf7, 0xff, 0xdd, 0xdd, 0xdd, 0xd5,
|
||||
0xd5, 0xc9, 0xdd, 0xff, 0xdd, 0xdd, 0xeb, 0xf7, 0xeb, 0xdd, 0xdd, 0xff,
|
||||
0xdd, 0xdd, 0xeb, 0xf7, 0xf7, 0xf7, 0xf7, 0xff, 0xc1, 0xdf, 0xef, 0xf7,
|
||||
0xfb, 0xfd, 0xc1, 0xff, 0xc1, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc1, 0xff,
|
||||
0xff, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xff, 0xff, 0xc1, 0xcf, 0xcf, 0xcf,
|
||||
0xcf, 0xcf, 0xc1, 0xff, 0xff, 0xff, 0xf7, 0xeb, 0xdd, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xff, 0xf7, 0xff,
|
||||
0xeb, 0xeb, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0xeb, 0xc1, 0xeb,
|
||||
0xc1, 0xeb, 0xeb, 0xff, 0xf7, 0xc3, 0xf5, 0xe3, 0xd7, 0xe1, 0xf7, 0xff,
|
||||
0xf9, 0xd9, 0xef, 0xf7, 0xfb, 0xcd, 0xcf, 0xff, 0xfb, 0xf5, 0xf5, 0xfb,
|
||||
0xd5, 0xed, 0xd3, 0xff, 0xf7, 0xf7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xf7, 0xfb, 0xfd, 0xfd, 0xfd, 0xfb, 0xf7, 0xff, 0xf7, 0xef, 0xdf, 0xdf,
|
||||
0xdf, 0xef, 0xf7, 0xff, 0xf7, 0xd5, 0xe3, 0xf7, 0xe3, 0xd5, 0xf7, 0xff,
|
||||
0xff, 0xf7, 0xf7, 0xc1, 0xf7, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xf7, 0xf7, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xdf, 0xef, 0xf7,
|
||||
0xfb, 0xfd, 0xff, 0xff, 0xe3, 0xdd, 0xcd, 0xd5, 0xd9, 0xdd, 0xe3, 0xff,
|
||||
0xf7, 0xf3, 0xf7, 0xf7, 0xf7, 0xf7, 0xe3, 0xff, 0xe3, 0xdd, 0xdf, 0xe7,
|
||||
0xfb, 0xfd, 0xc1, 0xff, 0xc1, 0xdf, 0xef, 0xe7, 0xdf, 0xdd, 0xe3, 0xff,
|
||||
0xef, 0xe7, 0xeb, 0xed, 0xc1, 0xef, 0xef, 0xff, 0xc1, 0xfd, 0xe1, 0xdf,
|
||||
0xdf, 0xdd, 0xe3, 0xff, 0xc7, 0xfb, 0xfd, 0xe1, 0xdd, 0xdd, 0xe3, 0xff,
|
||||
0xc1, 0xdf, 0xef, 0xf7, 0xfb, 0xfb, 0xfb, 0xff, 0xe3, 0xdd, 0xdd, 0xe3,
|
||||
0xdd, 0xdd, 0xe3, 0xff, 0xe3, 0xdd, 0xdd, 0xc3, 0xdf, 0xef, 0xf1, 0xff,
|
||||
0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff,
|
||||
0xf7, 0xf7, 0xfb, 0xff, 0xef, 0xf7, 0xfb, 0xfd, 0xfb, 0xf7, 0xef, 0xff,
|
||||
0xff, 0xff, 0xc1, 0xff, 0xc1, 0xff, 0xff, 0xff, 0xfb, 0xf7, 0xef, 0xdf,
|
||||
0xef, 0xf7, 0xfb, 0xff, 0xe3, 0xdd, 0xef, 0xf7, 0xf7, 0xff, 0xf7, 0xff,
|
||||
0xe3, 0xdd, 0xd5, 0xc5, 0xe5, 0xfd, 0xc3, 0xff, 0xf7, 0xeb, 0xdd, 0xdd,
|
||||
0xc1, 0xdd, 0xdd, 0xff, 0xe1, 0xdd, 0xdd, 0xe1, 0xdd, 0xdd, 0xe1, 0xff,
|
||||
0xe3, 0xdd, 0xfd, 0xfd, 0xfd, 0xdd, 0xe3, 0xff, 0xe1, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xdd, 0xe1, 0xff, 0xc1, 0xfd, 0xfd, 0xe1, 0xfd, 0xfd, 0xc1, 0xff,
|
||||
0xc1, 0xfd, 0xfd, 0xe1, 0xfd, 0xfd, 0xfd, 0xff, 0xc3, 0xfd, 0xfd, 0xfd,
|
||||
0xcd, 0xdd, 0xc3, 0xff, 0xdd, 0xdd, 0xdd, 0xc1, 0xdd, 0xdd, 0xdd, 0xff,
|
||||
0xe3, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xe3, 0xff, 0xdf, 0xdf, 0xdf, 0xdf,
|
||||
0xdf, 0xdd, 0xe3, 0xff, 0xdd, 0xed, 0xf5, 0xf9, 0xf5, 0xed, 0xdd, 0xff,
|
||||
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xc1, 0xff, 0xdd, 0xc9, 0xd5, 0xd5,
|
||||
0xdd, 0xdd, 0xdd, 0xff, 0xdd, 0xdd, 0xd9, 0xd5, 0xcd, 0xdd, 0xdd, 0xff,
|
||||
0xe3, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xe3, 0xff, 0xe1, 0xdd, 0xdd, 0xe1,
|
||||
0xfd, 0xfd, 0xfd, 0xff, 0xe3, 0xdd, 0xdd, 0xdd, 0xd5, 0xed, 0xd3, 0xff,
|
||||
0xe1, 0xdd, 0xdd, 0xe1, 0xf5, 0xed, 0xdd, 0xff, 0xe3, 0xdd, 0xfd, 0xe3,
|
||||
0xdf, 0xdd, 0xe3, 0xff, 0xc1, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xff,
|
||||
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xe3, 0xff, 0xdd, 0xdd, 0xdd, 0xdd,
|
||||
0xdd, 0xeb, 0xf7, 0xff, 0xdd, 0xdd, 0xdd, 0xd5, 0xd5, 0xc9, 0xdd, 0xff,
|
||||
0xdd, 0xdd, 0xeb, 0xf7, 0xeb, 0xdd, 0xdd, 0xff, 0xdd, 0xdd, 0xeb, 0xf7,
|
||||
0xf7, 0xf7, 0xf7, 0xff, 0xc1, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xc1, 0xff,
|
||||
0xc1, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xc1, 0xff, 0xff, 0xfd, 0xfb, 0xf7,
|
||||
0xef, 0xdf, 0xff, 0xff, 0xc1, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xc1, 0xff,
|
||||
0xff, 0xff, 0xf7, 0xeb, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0x80, 0xfb, 0xf7, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xe3, 0xdf, 0xc3, 0xdd, 0xc3, 0xff, 0xfd, 0xfd, 0xe1, 0xdd,
|
||||
0xdd, 0xdd, 0xe1, 0xff, 0xff, 0xff, 0xc3, 0xfd, 0xfd, 0xfd, 0xc3, 0xff,
|
||||
0xdf, 0xdf, 0xc3, 0xdd, 0xdd, 0xdd, 0xc3, 0xff, 0xff, 0xff, 0xe3, 0xdd,
|
||||
0xc1, 0xfd, 0xc3, 0xff, 0xe7, 0xdb, 0xfb, 0xe1, 0xfb, 0xfb, 0xfb, 0xff,
|
||||
0xff, 0xff, 0xe3, 0xdd, 0xdd, 0xc3, 0xdf, 0xe3, 0xfd, 0xfd, 0xe1, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xff, 0xf7, 0xff, 0xf3, 0xf7, 0xf7, 0xf7, 0xe3, 0xff,
|
||||
0xef, 0xff, 0xe7, 0xef, 0xef, 0xef, 0xed, 0xf3, 0xfd, 0xfd, 0xdd, 0xed,
|
||||
0xf1, 0xed, 0xdd, 0xff, 0xf3, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xe3, 0xff,
|
||||
0xff, 0xff, 0xc9, 0xd5, 0xd5, 0xd5, 0xdd, 0xff, 0xff, 0xff, 0xe1, 0xdd,
|
||||
0xdd, 0xdd, 0xdd, 0xff, 0xff, 0xff, 0xe3, 0xdd, 0xdd, 0xdd, 0xe3, 0xff,
|
||||
0xff, 0xff, 0xe1, 0xdd, 0xdd, 0xe1, 0xfd, 0xfd, 0xff, 0xff, 0xc3, 0xdd,
|
||||
0xdd, 0xc3, 0xdf, 0xdf, 0xff, 0xff, 0xc5, 0xf9, 0xfd, 0xfd, 0xfd, 0xff,
|
||||
0xff, 0xff, 0xc3, 0xfd, 0xe3, 0xdf, 0xe1, 0xff, 0xfb, 0xfb, 0xe1, 0xfb,
|
||||
0xfb, 0xdb, 0xe7, 0xff, 0xff, 0xff, 0xdd, 0xdd, 0xdd, 0xcd, 0xd3, 0xff,
|
||||
0xff, 0xff, 0xdd, 0xdd, 0xdd, 0xeb, 0xf7, 0xff, 0xff, 0xff, 0xdd, 0xdd,
|
||||
0xd5, 0xd5, 0xc9, 0xff, 0xff, 0xff, 0xdd, 0xeb, 0xf7, 0xeb, 0xdd, 0xff,
|
||||
0xff, 0xff, 0xdd, 0xdd, 0xdd, 0xc3, 0xdf, 0xe3, 0xff, 0xff, 0xc1, 0xef,
|
||||
0xf7, 0xfb, 0xc1, 0xff, 0xc7, 0xf3, 0xf3, 0xf9, 0xf3, 0xf3, 0xc7, 0xff,
|
||||
0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf1, 0xe7, 0xe7, 0xcf,
|
||||
0xe7, 0xe7, 0xf1, 0xff, 0xd3, 0xe5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xd5, 0xeb, 0xd5, 0xeb, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xee, 0xbb, 0xfe, 0xfe, 0xff, 0xff,
|
||||
0xfd, 0xfd, 0xdd, 0x77, 0xfd, 0xfd, 0xff, 0xff, 0xfc, 0xfc, 0xcc, 0x33,
|
||||
0xfc, 0xfc, 0xff, 0xff, 0xfb, 0xfb, 0xbb, 0xee, 0xfb, 0xfb, 0xff, 0xff,
|
||||
0xfa, 0xfa, 0xaa, 0xaa, 0xfa, 0xfa, 0xff, 0xff, 0xf9, 0xf9, 0x99, 0x66,
|
||||
0xf9, 0xf9, 0xff, 0xff, 0xf8, 0xf8, 0x88, 0x22, 0xf8, 0xf8, 0xff, 0xff,
|
||||
0xf7, 0xf7, 0x77, 0xdd, 0xf7, 0xf7, 0xff, 0xff, 0xf6, 0xf6, 0x66, 0x99,
|
||||
0xf6, 0xf6, 0xff, 0xff, 0xf5, 0xf5, 0x55, 0x55, 0xf5, 0xf5, 0xff, 0xff,
|
||||
0xf4, 0xf4, 0x44, 0x11, 0xf4, 0xf4, 0xff, 0xff, 0xf3, 0xf3, 0x33, 0xcc,
|
||||
0xf3, 0xf3, 0xff, 0xff, 0xf2, 0xf2, 0x22, 0x88, 0xf2, 0xf2, 0xff, 0xff,
|
||||
0xf1, 0xf1, 0x11, 0x44, 0xf1, 0xf1, 0xff, 0xff, 0xf0, 0xf0, 0x00, 0x00,
|
||||
0xf0, 0xf0, 0xff, 0xff, 0xef, 0xef, 0xff, 0xff, 0xef, 0xef, 0xee, 0xbb,
|
||||
0xee, 0xee, 0xee, 0xbb, 0xee, 0xee, 0xee, 0xbb, 0xed, 0xed, 0xdd, 0x77,
|
||||
0xed, 0xed, 0xee, 0xbb, 0xec, 0xec, 0xcc, 0x33, 0xec, 0xec, 0xee, 0xbb,
|
||||
0xeb, 0xeb, 0xbb, 0xee, 0xeb, 0xeb, 0xee, 0xbb, 0xea, 0xea, 0xaa, 0xaa,
|
||||
0xea, 0xea, 0xee, 0xbb, 0xe9, 0xe9, 0x99, 0x66, 0xe9, 0xe9, 0xee, 0xbb,
|
||||
0xe8, 0xe8, 0x88, 0x22, 0xe8, 0xe8, 0xee, 0xbb, 0xe7, 0xe7, 0x77, 0xdd,
|
||||
0xe7, 0xe7, 0xee, 0xbb, 0xe6, 0xe6, 0x66, 0x99, 0xe6, 0xe6, 0xee, 0xbb,
|
||||
0xe5, 0xe5, 0x55, 0x55, 0xe5, 0xe5, 0xee, 0xbb, 0xe4, 0xe4, 0x44, 0x11,
|
||||
0xe4, 0xe4, 0xee, 0xbb, 0xe3, 0xe3, 0x33, 0xcc, 0xe3, 0xe3, 0xee, 0xbb,
|
||||
0xe2, 0xe2, 0x22, 0x88, 0xe2, 0xe2, 0xee, 0xbb, 0xe1, 0xe1, 0x11, 0x44,
|
||||
0xe1, 0xe1, 0xee, 0xbb, 0xe0, 0xe0, 0x00, 0x00, 0xe0, 0xe0, 0xee, 0xbb,
|
||||
0xdf, 0xdf, 0xff, 0xff, 0xdf, 0xdf, 0xdd, 0x77, 0xde, 0xde, 0xee, 0xbb,
|
||||
0xde, 0xde, 0xdd, 0x77, 0xdd, 0xdd, 0xdd, 0x77, 0xdd, 0xdd, 0xdd, 0x77,
|
||||
0xdc, 0xdc, 0xcc, 0x33, 0xdc, 0xdc, 0xdd, 0x77, 0xdb, 0xdb, 0xbb, 0xee,
|
||||
0xdb, 0xdb, 0xdd, 0x77, 0xda, 0xda, 0xaa, 0xaa, 0xda, 0xda, 0xdd, 0x77,
|
||||
0xd9, 0xd9, 0x99, 0x66, 0xd9, 0xd9, 0xdd, 0x77, 0xd8, 0xd8, 0x88, 0x22,
|
||||
0xd8, 0xd8, 0xdd, 0x77, 0xd7, 0xd7, 0x77, 0xdd, 0xd7, 0xd7, 0xdd, 0x77,
|
||||
0xd6, 0xd6, 0x66, 0x99, 0xd6, 0xd6, 0xdd, 0x77, 0xd5, 0xd5, 0x55, 0x55,
|
||||
0xd5, 0xd5, 0xdd, 0x77, 0xd4, 0xd4, 0x44, 0x11, 0xd4, 0xd4, 0xdd, 0x77,
|
||||
0xd3, 0xd3, 0x33, 0xcc, 0xd3, 0xd3, 0xdd, 0x77, 0xd2, 0xd2, 0x22, 0x88,
|
||||
0xd2, 0xd2, 0xdd, 0x77, 0xd1, 0xd1, 0x11, 0x44, 0xd1, 0xd1, 0xdd, 0x77,
|
||||
0xd0, 0xd0, 0x00, 0x00, 0xd0, 0xd0, 0xdd, 0x77, 0xcf, 0xcf, 0xff, 0xff,
|
||||
0xcf, 0xcf, 0xcc, 0x33, 0xce, 0xce, 0xee, 0xbb, 0xce, 0xce, 0xcc, 0x33,
|
||||
0xcd, 0xcd, 0xdd, 0x77, 0xcd, 0xcd, 0xcc, 0x33, 0xcc, 0xcc, 0xcc, 0x33,
|
||||
0xcc, 0xcc, 0xcc, 0x33, 0xcb, 0xcb, 0xbb, 0xee, 0xcb, 0xcb, 0xcc, 0x33,
|
||||
0xca, 0xca, 0xaa, 0xaa, 0xca, 0xca, 0xcc, 0x33, 0xc9, 0xc9, 0x99, 0x66,
|
||||
0xc9, 0xc9, 0xcc, 0x33, 0xc8, 0xc8, 0x88, 0x22, 0xc8, 0xc8, 0xcc, 0x33,
|
||||
0xc7, 0xc7, 0x77, 0xdd, 0xc7, 0xc7, 0xcc, 0x33, 0xc6, 0xc6, 0x66, 0x99,
|
||||
0xc6, 0xc6, 0xcc, 0x33, 0xc5, 0xc5, 0x55, 0x55, 0xc5, 0xc5, 0xcc, 0x33,
|
||||
0xc4, 0xc4, 0x44, 0x11, 0xc4, 0xc4, 0xcc, 0x33, 0xc3, 0xc3, 0x33, 0xcc,
|
||||
0xc3, 0xc3, 0xcc, 0x33, 0xc2, 0xc2, 0x22, 0x88, 0xc2, 0xc2, 0xcc, 0x33,
|
||||
0xc1, 0xc1, 0x11, 0x44, 0xc1, 0xc1, 0xcc, 0x33, 0xc0, 0xc0, 0x00, 0x00,
|
||||
0xc0, 0xc0, 0xcc, 0x33, 0xbf, 0xbf, 0xff, 0xff, 0xbf, 0xbf, 0xbb, 0xee,
|
||||
0xbe, 0xbe, 0xee, 0xbb, 0xbe, 0xbe, 0xbb, 0xee, 0xbd, 0xbd, 0xdd, 0x77,
|
||||
0xbd, 0xbd, 0xbb, 0xee, 0xbc, 0xbc, 0xcc, 0x33, 0xbc, 0xbc, 0xbb, 0xee,
|
||||
0xbb, 0xbb, 0xbb, 0xee, 0xbb, 0xbb, 0xbb, 0xee, 0xba, 0xba, 0xaa, 0xaa,
|
||||
0xba, 0xba, 0xbb, 0xee, 0xb9, 0xb9, 0x99, 0x66, 0xb9, 0xb9, 0xbb, 0xee,
|
||||
0xb8, 0xb8, 0x88, 0x22, 0xb8, 0xb8, 0xbb, 0xee, 0xb7, 0xb7, 0x77, 0xdd,
|
||||
0xb7, 0xb7, 0xbb, 0xee, 0xb6, 0xb6, 0x66, 0x99, 0xb6, 0xb6, 0xbb, 0xee,
|
||||
0xb5, 0xb5, 0x55, 0x55, 0xb5, 0xb5, 0xbb, 0xee, 0xb4, 0xb4, 0x44, 0x11,
|
||||
0xb4, 0xb4, 0xbb, 0xee, 0xb3, 0xb3, 0x33, 0xcc, 0xb3, 0xb3, 0xbb, 0xee,
|
||||
0xb2, 0xb2, 0x22, 0x88, 0xb2, 0xb2, 0xbb, 0xee, 0xb1, 0xb1, 0x11, 0x44,
|
||||
0xb1, 0xb1, 0xbb, 0xee, 0xb0, 0xb0, 0x00, 0x00, 0xb0, 0xb0, 0xbb, 0xee,
|
||||
0xaf, 0xaf, 0xff, 0xff, 0xaf, 0xaf, 0xaa, 0xaa, 0xae, 0xae, 0xee, 0xbb,
|
||||
0xae, 0xae, 0xaa, 0xaa, 0xad, 0xad, 0xdd, 0x77, 0xad, 0xad, 0xaa, 0xaa,
|
||||
0xac, 0xac, 0xcc, 0x33, 0xac, 0xac, 0xaa, 0xaa, 0xab, 0xab, 0xbb, 0xee,
|
||||
0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xa9, 0xa9, 0x99, 0x66, 0xa9, 0xa9, 0xaa, 0xaa, 0xa8, 0xa8, 0x88, 0x22,
|
||||
0xa8, 0xa8, 0xaa, 0xaa, 0xa7, 0xa7, 0x77, 0xdd, 0xa7, 0xa7, 0xaa, 0xaa,
|
||||
0xa6, 0xa6, 0x66, 0x99, 0xa6, 0xa6, 0xaa, 0xaa, 0xa5, 0xa5, 0x55, 0x55,
|
||||
0xa5, 0xa5, 0xaa, 0xaa, 0xa4, 0xa4, 0x44, 0x11, 0xa4, 0xa4, 0xaa, 0xaa,
|
||||
0xa3, 0xa3, 0x33, 0xcc, 0xa3, 0xa3, 0xaa, 0xaa, 0xa2, 0xa2, 0x22, 0x88,
|
||||
0xa2, 0xa2, 0xaa, 0xaa, 0xa1, 0xa1, 0x11, 0x44, 0xa1, 0xa1, 0xaa, 0xaa,
|
||||
0xa0, 0xa0, 0x00, 0x00, 0xa0, 0xa0, 0xaa, 0xaa, 0x9f, 0x9f, 0xff, 0xff,
|
||||
0x9f, 0x9f, 0x99, 0x66, 0x9e, 0x9e, 0xee, 0xbb, 0x9e, 0x9e, 0x99, 0x66,
|
||||
0x9d, 0x9d, 0xdd, 0x77, 0x9d, 0x9d, 0x99, 0x66, 0x9c, 0x9c, 0xcc, 0x33,
|
||||
0x9c, 0x9c, 0x99, 0x66, 0x9b, 0x9b, 0xbb, 0xee, 0x9b, 0x9b, 0x99, 0x66,
|
||||
0x9a, 0x9a, 0xaa, 0xaa, 0x9a, 0x9a, 0x99, 0x66, 0x99, 0x99, 0x99, 0x66,
|
||||
0x99, 0x99, 0x99, 0x66, 0x98, 0x98, 0x88, 0x22, 0x98, 0x98, 0x99, 0x66,
|
||||
0x97, 0x97, 0x77, 0xdd, 0x97, 0x97, 0x99, 0x66, 0x96, 0x96, 0x66, 0x99,
|
||||
0x96, 0x96, 0x99, 0x66, 0x95, 0x95, 0x55, 0x55, 0x95, 0x95, 0x99, 0x66,
|
||||
0x94, 0x94, 0x44, 0x11, 0x94, 0x94, 0x99, 0x66, 0x93, 0x93, 0x33, 0xcc,
|
||||
0x93, 0x93, 0x99, 0x66, 0x92, 0x92, 0x22, 0x88, 0x92, 0x92, 0x99, 0x66,
|
||||
0x91, 0x91, 0x11, 0x44, 0x91, 0x91, 0x99, 0x66, 0x90, 0x90, 0x00, 0x00,
|
||||
0x90, 0x90, 0x99, 0x66, 0x8f, 0x8f, 0xff, 0xff, 0x8f, 0x8f, 0x88, 0x22,
|
||||
0x8e, 0x8e, 0xee, 0xbb, 0x8e, 0x8e, 0x88, 0x22, 0x8d, 0x8d, 0xdd, 0x77,
|
||||
0x8d, 0x8d, 0x88, 0x22, 0x8c, 0x8c, 0xcc, 0x33, 0x8c, 0x8c, 0x88, 0x22,
|
||||
0x8b, 0x8b, 0xbb, 0xee, 0x8b, 0x8b, 0x88, 0x22, 0x8a, 0x8a, 0xaa, 0xaa,
|
||||
0x8a, 0x8a, 0x88, 0x22, 0x89, 0x89, 0x99, 0x66, 0x89, 0x89, 0x88, 0x22,
|
||||
0x88, 0x88, 0x88, 0x22, 0x88, 0x88, 0x88, 0x22, 0x87, 0x87, 0x77, 0xdd,
|
||||
0x87, 0x87, 0x88, 0x22, 0x86, 0x86, 0x66, 0x99, 0x86, 0x86, 0x88, 0x22,
|
||||
0x85, 0x85, 0x55, 0x55, 0x85, 0x85, 0x88, 0x22, 0x84, 0x84, 0x44, 0x11,
|
||||
0x84, 0x84, 0x88, 0x22, 0x83, 0x83, 0x33, 0xcc, 0x83, 0x83, 0x88, 0x22,
|
||||
0x82, 0x82, 0x22, 0x88, 0x82, 0x82, 0x88, 0x22, 0x81, 0x81, 0x11, 0x44,
|
||||
0x81, 0x81, 0x88, 0x22, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x88, 0x22,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x77, 0xdd, 0xfe, 0xfe, 0xee, 0xbb,
|
||||
0xfe, 0xfe, 0x77, 0xdd, 0xfd, 0xfd, 0xdd, 0x77, 0xfd, 0xfd, 0x77, 0xdd,
|
||||
0xfc, 0xfc, 0xcc, 0x33, 0xfc, 0xfc, 0x77, 0xdd, 0xfb, 0xfb, 0xbb, 0xee,
|
||||
0xfb, 0xfb, 0x77, 0xdd, 0xfa, 0xfa, 0xaa, 0xaa, 0xfa, 0xfa, 0x77, 0xdd,
|
||||
0xf9, 0xf9, 0x99, 0x66, 0xf9, 0xf9, 0x77, 0xdd, 0xf8, 0xf8, 0x88, 0x22,
|
||||
0xf8, 0xf8, 0x77, 0xdd, 0xf7, 0xf7, 0x77, 0xdd, 0xf7, 0xf7, 0x77, 0xdd,
|
||||
0xf6, 0xf6, 0x66, 0x99, 0xf6, 0xf6, 0x77, 0xdd, 0xf5, 0xf5, 0x55, 0x55,
|
||||
0xf5, 0xf5, 0x77, 0xdd, 0xf4, 0xf4, 0x44, 0x11, 0xf4, 0xf4, 0x77, 0xdd,
|
||||
0xf3, 0xf3, 0x33, 0xcc, 0xf3, 0xf3, 0x77, 0xdd, 0xf2, 0xf2, 0x22, 0x88,
|
||||
0xf2, 0xf2, 0x77, 0xdd, 0xf1, 0xf1, 0x11, 0x44, 0xf1, 0xf1, 0x77, 0xdd,
|
||||
0xf0, 0xf0, 0x00, 0x00, 0xf0, 0xf0, 0x77, 0xdd, 0xef, 0xef, 0xff, 0xff,
|
||||
0xef, 0xef, 0x66, 0x99, 0xee, 0xee, 0xee, 0xbb, 0xee, 0xee, 0x66, 0x99,
|
||||
0xed, 0xed, 0xdd, 0x77, 0xed, 0xed, 0x66, 0x99, 0xec, 0xec, 0xcc, 0x33,
|
||||
0xec, 0xec, 0x66, 0x99, 0xeb, 0xeb, 0xbb, 0xee, 0xeb, 0xeb, 0x66, 0x99,
|
||||
0xea, 0xea, 0xaa, 0xaa, 0xea, 0xea, 0x66, 0x99, 0xe9, 0xe9, 0x99, 0x66,
|
||||
0xe9, 0xe9, 0x66, 0x99, 0xe8, 0xe8, 0x88, 0x22, 0xe8, 0xe8, 0x66, 0x99,
|
||||
0xe7, 0xe7, 0x77, 0xdd, 0xe7, 0xe7, 0x66, 0x99, 0xe6, 0xe6, 0x66, 0x99,
|
||||
0xe6, 0xe6, 0x66, 0x99, 0xe5, 0xe5, 0x55, 0x55, 0xe5, 0xe5, 0x66, 0x99,
|
||||
0xe4, 0xe4, 0x44, 0x11, 0xe4, 0xe4, 0x66, 0x99, 0xe3, 0xe3, 0x33, 0xcc,
|
||||
0xe3, 0xe3, 0x66, 0x99, 0xe2, 0xe2, 0x22, 0x88, 0xe2, 0xe2, 0x66, 0x99,
|
||||
0xe1, 0xe1, 0x11, 0x44, 0xe1, 0xe1, 0x66, 0x99, 0xe0, 0xe0, 0x00, 0x00,
|
||||
0xe0, 0xe0, 0x66, 0x99, 0xdf, 0xdf, 0xff, 0xff, 0xdf, 0xdf, 0x55, 0x55,
|
||||
0xde, 0xde, 0xee, 0xbb, 0xde, 0xde, 0x55, 0x55, 0xdd, 0xdd, 0xdd, 0x77,
|
||||
0xdd, 0xdd, 0x55, 0x55, 0xdc, 0xdc, 0xcc, 0x33, 0xdc, 0xdc, 0x55, 0x55,
|
||||
0xdb, 0xdb, 0xbb, 0xee, 0xdb, 0xdb, 0x55, 0x55, 0xda, 0xda, 0xaa, 0xaa,
|
||||
0xda, 0xda, 0x55, 0x55, 0xd9, 0xd9, 0x99, 0x66, 0xd9, 0xd9, 0x55, 0x55,
|
||||
0xd8, 0xd8, 0x88, 0x22, 0xd8, 0xd8, 0x55, 0x55, 0xd7, 0xd7, 0x77, 0xdd,
|
||||
0xd7, 0xd7, 0x55, 0x55, 0xd6, 0xd6, 0x66, 0x99, 0xd6, 0xd6, 0x55, 0x55,
|
||||
0xd5, 0xd5, 0x55, 0x55, 0xd5, 0xd5, 0x55, 0x55, 0xd4, 0xd4, 0x44, 0x11,
|
||||
0xd4, 0xd4, 0x55, 0x55, 0xd3, 0xd3, 0x33, 0xcc, 0xd3, 0xd3, 0x55, 0x55,
|
||||
0xd2, 0xd2, 0x22, 0x88, 0xd2, 0xd2, 0x55, 0x55, 0xd1, 0xd1, 0x11, 0x44,
|
||||
0xd1, 0xd1, 0x55, 0x55, 0xd0, 0xd0, 0x00, 0x00, 0xd0, 0xd0, 0x55, 0x55,
|
||||
0xcf, 0xcf, 0xff, 0xff, 0xcf, 0xcf, 0x44, 0x11, 0xce, 0xce, 0xee, 0xbb,
|
||||
0xce, 0xce, 0x44, 0x11, 0xcd, 0xcd, 0xdd, 0x77, 0xcd, 0xcd, 0x44, 0x11,
|
||||
0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, 0x44, 0x11, 0xcb, 0xcb, 0xbb, 0xee,
|
||||
0xcb, 0xcb, 0x44, 0x11, 0xca, 0xca, 0xaa, 0xaa, 0xca, 0xca, 0x44, 0x11,
|
||||
0xc9, 0xc9, 0x99, 0x66, 0xc9, 0xc9, 0x44, 0x11, 0xc8, 0xc8, 0x88, 0x22,
|
||||
0xc8, 0xc8, 0x44, 0x11, 0xc7, 0xc7, 0x77, 0xdd, 0xc7, 0xc7, 0x44, 0x11,
|
||||
0xc6, 0xc6, 0x66, 0x99, 0xc6, 0xc6, 0x44, 0x11, 0xc5, 0xc5, 0x55, 0x55,
|
||||
0xc5, 0xc5, 0x44, 0x11, 0xc4, 0xc4, 0x44, 0x11, 0xc4, 0xc4, 0x44, 0x11,
|
||||
0xc3, 0xc3, 0x33, 0xcc, 0xc3, 0xc3, 0x44, 0x11, 0xc2, 0xc2, 0x22, 0x88,
|
||||
0xc2, 0xc2, 0x44, 0x11, 0xc1, 0xc1, 0x11, 0x44, 0xc1, 0xc1, 0x44, 0x11,
|
||||
0xc0, 0xc0, 0x00, 0x00, 0xc0, 0xc0, 0x44, 0x11, 0xbf, 0xbf, 0xff, 0xff,
|
||||
0xbf, 0xbf, 0x33, 0xcc, 0xbe, 0xbe, 0xee, 0xbb, 0xbe, 0xbe, 0x33, 0xcc,
|
||||
0xbd, 0xbd, 0xdd, 0x77, 0xbd, 0xbd, 0x33, 0xcc, 0xbc, 0xbc, 0xcc, 0x33,
|
||||
0xbc, 0xbc, 0x33, 0xcc, 0xbb, 0xbb, 0xbb, 0xee, 0xbb, 0xbb, 0x33, 0xcc,
|
||||
0xba, 0xba, 0xaa, 0xaa, 0xba, 0xba, 0x33, 0xcc, 0xb9, 0xb9, 0x99, 0x66,
|
||||
0xb9, 0xb9, 0x33, 0xcc, 0xb8, 0xb8, 0x88, 0x22, 0xb8, 0xb8, 0x33, 0xcc,
|
||||
0xb7, 0xb7, 0x77, 0xdd, 0xb7, 0xb7, 0x33, 0xcc, 0xb6, 0xb6, 0x66, 0x99,
|
||||
0xb6, 0xb6, 0x33, 0xcc, 0xb5, 0xb5, 0x55, 0x55, 0xb5, 0xb5, 0x33, 0xcc,
|
||||
0xb4, 0xb4, 0x44, 0x11, 0xb4, 0xb4, 0x33, 0xcc, 0xb3, 0xb3, 0x33, 0xcc,
|
||||
0xb3, 0xb3, 0x33, 0xcc, 0xb2, 0xb2, 0x22, 0x88, 0xb2, 0xb2, 0x33, 0xcc,
|
||||
0xb1, 0xb1, 0x11, 0x44, 0xb1, 0xb1, 0x33, 0xcc, 0xb0, 0xb0, 0x00, 0x00,
|
||||
0xb0, 0xb0, 0x33, 0xcc, 0xaf, 0xaf, 0xff, 0xff, 0xaf, 0xaf, 0x22, 0x88,
|
||||
0xae, 0xae, 0xee, 0xbb, 0xae, 0xae, 0x22, 0x88, 0xad, 0xad, 0xdd, 0x77,
|
||||
0xad, 0xad, 0x22, 0x88, 0xac, 0xac, 0xcc, 0x33, 0xac, 0xac, 0x22, 0x88,
|
||||
0xab, 0xab, 0xbb, 0xee, 0xab, 0xab, 0x22, 0x88, 0xaa, 0xaa, 0xaa, 0xaa,
|
||||
0xaa, 0xaa, 0x22, 0x88, 0xa9, 0xa9, 0x99, 0x66, 0xa9, 0xa9, 0x22, 0x88,
|
||||
0xa8, 0xa8, 0x88, 0x22, 0xa8, 0xa8, 0x22, 0x88, 0xa7, 0xa7, 0x77, 0xdd,
|
||||
0xa7, 0xa7, 0x22, 0x88, 0xa6, 0xa6, 0x66, 0x99, 0xa6, 0xa6, 0x22, 0x88,
|
||||
0xa5, 0xa5, 0x55, 0x55, 0xa5, 0xa5, 0x22, 0x88, 0xa4, 0xa4, 0x44, 0x11,
|
||||
0xa4, 0xa4, 0x22, 0x88, 0xa3, 0xa3, 0x33, 0xcc, 0xa3, 0xa3, 0x22, 0x88,
|
||||
0xa2, 0xa2, 0x22, 0x88, 0xa2, 0xa2, 0x22, 0x88, 0xa1, 0xa1, 0x11, 0x44,
|
||||
0xa1, 0xa1, 0x22, 0x88, 0xa0, 0xa0, 0x00, 0x00, 0xa0, 0xa0, 0x22, 0x88,
|
||||
0x9f, 0x9f, 0xff, 0xff, 0x9f, 0x9f, 0x11, 0x44, 0x9e, 0x9e, 0xee, 0xbb,
|
||||
0x9e, 0x9e, 0x11, 0x44, 0x9d, 0x9d, 0xdd, 0x77, 0x9d, 0x9d, 0x11, 0x44,
|
||||
0x9c, 0x9c, 0xcc, 0x33, 0x9c, 0x9c, 0x11, 0x44, 0x9b, 0x9b, 0xbb, 0xee,
|
||||
0x9b, 0x9b, 0x11, 0x44, 0x9a, 0x9a, 0xaa, 0xaa, 0x9a, 0x9a, 0x11, 0x44,
|
||||
0x99, 0x99, 0x99, 0x66, 0x99, 0x99, 0x11, 0x44, 0x98, 0x98, 0x88, 0x22,
|
||||
0x98, 0x98, 0x11, 0x44, 0x97, 0x97, 0x77, 0xdd, 0x97, 0x97, 0x11, 0x44,
|
||||
0x96, 0x96, 0x66, 0x99, 0x96, 0x96, 0x11, 0x44, 0x95, 0x95, 0x55, 0x55,
|
||||
0x95, 0x95, 0x11, 0x44, 0x94, 0x94, 0x44, 0x11, 0x94, 0x94, 0x11, 0x44,
|
||||
0x93, 0x93, 0x33, 0xcc, 0x93, 0x93, 0x11, 0x44, 0x92, 0x92, 0x22, 0x88,
|
||||
0x92, 0x92, 0x11, 0x44, 0x91, 0x91, 0x11, 0x44, 0x91, 0x91, 0x11, 0x44,
|
||||
0x90, 0x90, 0x00, 0x00, 0x90, 0x90, 0x11, 0x44, 0x8f, 0x8f, 0xff, 0xff,
|
||||
0x8f, 0x8f, 0x00, 0x00, 0x8e, 0x8e, 0xee, 0xbb, 0x8e, 0x8e, 0x00, 0x00,
|
||||
0x8d, 0x8d, 0xdd, 0x77, 0x8d, 0x8d, 0x00, 0x00, 0x8c, 0x8c, 0xcc, 0x33,
|
||||
0x8c, 0x8c, 0x00, 0x00, 0x8b, 0x8b, 0xbb, 0xee, 0x8b, 0x8b, 0x00, 0x00,
|
||||
0x8a, 0x8a, 0xaa, 0xaa, 0x8a, 0x8a, 0x00, 0x00, 0x89, 0x89, 0x99, 0x66,
|
||||
0x89, 0x89, 0x00, 0x00, 0x88, 0x88, 0x88, 0x22, 0x88, 0x88, 0x00, 0x00,
|
||||
0x87, 0x87, 0x77, 0xdd, 0x87, 0x87, 0x00, 0x00, 0x86, 0x86, 0x66, 0x99,
|
||||
0x86, 0x86, 0x00, 0x00, 0x85, 0x85, 0x55, 0x55, 0x85, 0x85, 0x00, 0x00,
|
||||
0x84, 0x84, 0x44, 0x11, 0x84, 0x84, 0x00, 0x00, 0x83, 0x83, 0x33, 0xcc,
|
||||
0x83, 0x83, 0x00, 0x00, 0x82, 0x82, 0x22, 0x88, 0x82, 0x82, 0x00, 0x00,
|
||||
0x81, 0x81, 0x11, 0x44, 0x81, 0x81, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
|
||||
0x80, 0x80, 0x00, 0x00
|
||||
};
|
||||
static const unsigned int mii_rom_iic_video_len = 4096;
|
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
static const unsigned char iie_enhanced_rom_bin[] = {
|
||||
static const unsigned char mii_rom_iiee[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
@@ -1367,4 +1367,4 @@ static const unsigned char iie_enhanced_rom_bin[] = {
|
||||
0x83, 0x7f, 0x5d, 0xcc, 0xb5, 0xfc, 0x17, 0x17, 0xf5, 0x03, 0xfb, 0x03,
|
||||
0x62, 0xfa, 0xfa, 0xc3
|
||||
};
|
||||
static const unsigned int iie_enhanced_rom_bin_len = 16384;
|
||||
static const unsigned int mii_rom_iiee_len = 16384;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
static const unsigned char iie_enhanced_video[] = {
|
||||
static const unsigned char mii_rom_iiee_video[] = {
|
||||
0x1c, 0x22, 0x2a, 0x3a, 0x1a, 0x02, 0x3c, 0x00, 0x08, 0x14, 0x22, 0x22,
|
||||
0x3e, 0x22, 0x22, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e, 0x00,
|
||||
0x1c, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1c, 0x00, 0x1e, 0x22, 0x22, 0x22,
|
||||
@@ -343,4 +343,4 @@ static const unsigned char iie_enhanced_video[] = {
|
||||
0x81, 0x81, 0x11, 0x44, 0x81, 0x81, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
|
||||
0x80, 0x80, 0x00, 0x00
|
||||
};
|
||||
static const unsigned int iie_enhanced_video_len = 4096;
|
||||
static const unsigned int mii_rom_iiee_video_len = 4096;
|
||||
|
Binary file not shown.
26
src/roms/mii_rom_smartport.h
Normal file
26
src/roms/mii_rom_smartport.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
static const unsigned char mii_smartport_driver_bin[] = {
|
||||
0xa2, 0x20, 0xa9, 0x00, 0xa2, 0x03, 0xa9, 0x00, 0x2c, 0xff, 0xcf, 0xa0,
|
||||
0x00, 0x84, 0x44, 0x84, 0x46, 0x84, 0x47, 0xc8, 0x84, 0x42, 0xa9, 0x4c,
|
||||
0x8d, 0xfd, 0x07, 0xa9, 0xc0, 0x8d, 0xfe, 0x07, 0x20, 0x58, 0xff, 0xba,
|
||||
0xbd, 0x00, 0x01, 0x8d, 0xff, 0x07, 0x0a, 0x0a, 0x0a, 0x0a, 0x85, 0x43,
|
||||
0xa9, 0x08, 0x85, 0x45, 0x64, 0x44, 0x64, 0x46, 0x64, 0x47, 0x20, 0xfd,
|
||||
0x07, 0xb0, 0x1e, 0xa9, 0x0a, 0x85, 0x45, 0xa9, 0x01, 0x85, 0x46, 0x20,
|
||||
0xfd, 0x07, 0xb0, 0x11, 0xad, 0x01, 0x08, 0xf0, 0x0c, 0xa9, 0x01, 0xcd,
|
||||
0x00, 0x08, 0xd0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xad, 0xff, 0x07,
|
||||
0xc9, 0xc1, 0xf0, 0x08, 0xc5, 0x01, 0xd0, 0x04, 0xa5, 0x00, 0xf0, 0x03,
|
||||
0x4c, 0x00, 0xe0, 0xa9, 0x92, 0x85, 0x44, 0xad, 0xff, 0x07, 0x85, 0x45,
|
||||
0xa0, 0x00, 0xb1, 0x44, 0xf0, 0x06, 0x99, 0x55, 0x07, 0xc8, 0x80, 0xf6,
|
||||
0xad, 0xff, 0x07, 0x29, 0x0f, 0x3a, 0x09, 0xb0, 0x99, 0x55, 0x07, 0x4c,
|
||||
0xba, 0xfa, 0x8e, 0xef, 0xa0, 0x93, 0xed, 0xe1, 0xf2, 0xf4, 0x90, 0xef,
|
||||
0xf2, 0xf4, 0xa0, 0x84, 0xe9, 0xf3, 0xe3, 0xac, 0xa0, 0x82, 0xef, 0xef,
|
||||
0xf4, 0xe9, 0xee, 0xe7, 0xa0, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xea, 0x80, 0x0d, 0x80, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xeb, 0xfb, 0x00, 0x80, 0x1b, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xfb, 0x00, 0x80,
|
||||
0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xb0, 0x03, 0xa9, 0x00, 0x60, 0xa9, 0x27, 0x60, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x17, 0xc0
|
||||
};
|
||||
static const unsigned int mii_smartport_driver_bin_len = 256;
|
175
src/roms/mii_rom_ssc.h
Normal file
175
src/roms/mii_rom_ssc.h
Normal file
@@ -0,0 +1,175 @@
|
||||
#pragma once
|
||||
static const unsigned char mii_rom_ssc[] = {
|
||||
0x20, 0x9b, 0xc9, 0xa9, 0x16, 0x48, 0xa9, 0x00, 0x9d, 0xb8, 0x04, 0x9d,
|
||||
0xb8, 0x03, 0x9d, 0x38, 0x04, 0x9d, 0xb8, 0x05, 0x9d, 0x38, 0x06, 0x9d,
|
||||
0xb8, 0x06, 0xb9, 0x82, 0xc0, 0x85, 0x2b, 0x4a, 0x4a, 0x90, 0x04, 0x68,
|
||||
0x29, 0xfe, 0x48, 0xb8, 0xb9, 0x81, 0xc0, 0x4a, 0xb0, 0x07, 0x4a, 0xb0,
|
||||
0x0e, 0xa9, 0x01, 0xd0, 0x3d, 0x4a, 0xa9, 0x03, 0xb0, 0x02, 0xa9, 0x80,
|
||||
0x9d, 0xb8, 0x04, 0x2c, 0x58, 0xff, 0xa5, 0x2b, 0x29, 0x20, 0x49, 0x20,
|
||||
0x9d, 0xb8, 0x03, 0x70, 0x0a, 0x20, 0x9b, 0xc8, 0xae, 0xf8, 0x07, 0x9d,
|
||||
0xb8, 0x05, 0x60, 0xa5, 0x2b, 0x4a, 0x4a, 0x29, 0x03, 0xa8, 0xf0, 0x04,
|
||||
0x68, 0x29, 0x7f, 0x48, 0xb9, 0xa6, 0xc9, 0x9d, 0x38, 0x06, 0xa4, 0x26,
|
||||
0x68, 0x29, 0x95, 0x48, 0xa9, 0x09, 0x9d, 0x38, 0x05, 0x68, 0x9d, 0x38,
|
||||
0x07, 0xa5, 0x2b, 0x48, 0x29, 0xa0, 0x50, 0x02, 0x29, 0x80, 0x20, 0xa1,
|
||||
0xcd, 0x20, 0x81, 0xcd, 0x68, 0x29, 0x0c, 0x50, 0x02, 0xa9, 0x00, 0x0a,
|
||||
0x0a, 0x0a, 0x09, 0x0b, 0x99, 0x8a, 0xc0, 0xb9, 0x88, 0xc0, 0x60, 0x20,
|
||||
0x9b, 0xc9, 0x20, 0xaa, 0xc8, 0x29, 0x7f, 0xac, 0xf8, 0x07, 0xbe, 0xb8,
|
||||
0x05, 0x60, 0x20, 0xff, 0xca, 0xb0, 0x05, 0x20, 0x2c, 0xcc, 0x90, 0xf6,
|
||||
0x60, 0x20, 0x1e, 0xca, 0x68, 0xa8, 0x68, 0xaa, 0xa5, 0x27, 0x60, 0xf0,
|
||||
0x29, 0xbd, 0xb8, 0x06, 0x10, 0x05, 0x5e, 0xb8, 0x06, 0xd0, 0x24, 0x20,
|
||||
0x3e, 0xcc, 0x90, 0x1a, 0xbd, 0xb8, 0x03, 0x29, 0xc0, 0xf0, 0x0e, 0xa5,
|
||||
0x27, 0xc9, 0xe0, 0x90, 0x08, 0xbd, 0xb8, 0x04, 0x09, 0x40, 0x9d, 0xb8,
|
||||
0x04, 0x28, 0xf0, 0xd0, 0xd0, 0xcb, 0x20, 0xff, 0xca, 0x90, 0xdc, 0x20,
|
||||
0x11, 0xcc, 0x28, 0x08, 0xf0, 0xda, 0x20, 0xd1, 0xc9, 0x4c, 0xd0, 0xc8,
|
||||
0x20, 0x1a, 0xcb, 0xb0, 0xb7, 0xa5, 0x27, 0x48, 0xbd, 0x38, 0x07, 0x29,
|
||||
0xc0, 0xd0, 0x16, 0xa5, 0x24, 0xf0, 0x42, 0xc9, 0x08, 0xf0, 0x04, 0xc9,
|
||||
0x10, 0xd0, 0x0a, 0x09, 0xf0, 0x3d, 0xb8, 0x06, 0x18, 0x65, 0x24, 0x85,
|
||||
0x24, 0xbd, 0xb8, 0x06, 0xc5, 0x24, 0xf0, 0x29, 0xa9, 0xa0, 0x90, 0x08,
|
||||
0xbd, 0x38, 0x07, 0x0a, 0x10, 0x1f, 0xa9, 0x88, 0x85, 0x27, 0x2c, 0x58,
|
||||
0xff, 0x08, 0x70, 0x0c, 0xea, 0x2c, 0x58, 0xff, 0x50, 0xb8, 0xae, 0xf8,
|
||||
0x07, 0x4c, 0xef, 0xc9, 0x20, 0xb5, 0xc9, 0x20, 0x6b, 0xcb, 0x4c, 0x68,
|
||||
0xc9, 0x68, 0xb8, 0x08, 0x85, 0x27, 0x48, 0x20, 0x68, 0xcb, 0x20, 0xb5,
|
||||
0xc9, 0x68, 0x49, 0x8d, 0x0a, 0xd0, 0x05, 0x9d, 0xb8, 0x06, 0x85, 0x24,
|
||||
0xbd, 0xb8, 0x04, 0x10, 0x0d, 0xbd, 0x38, 0x06, 0xf0, 0x08, 0x18, 0xfd,
|
||||
0xb8, 0x06, 0xa9, 0x8d, 0x90, 0xda, 0x28, 0x70, 0xa4, 0xbd, 0x38, 0x07,
|
||||
0x30, 0x16, 0xbc, 0xb8, 0x06, 0x0a, 0x30, 0x0e, 0x98, 0xa0, 0x00, 0x38,
|
||||
0xfd, 0x38, 0x06, 0xc9, 0xf8, 0x90, 0x03, 0x69, 0x27, 0xa8, 0x84, 0x24,
|
||||
0x4c, 0xb8, 0xc8, 0x8e, 0xf8, 0x07, 0x84, 0x26, 0xa9, 0x00, 0x9d, 0xb8,
|
||||
0x05, 0x60, 0x29, 0x48, 0x50, 0x84, 0x85, 0x27, 0x20, 0x9b, 0xc9, 0x20,
|
||||
0x63, 0xcb, 0x4c, 0xa3, 0xc8, 0xa5, 0x27, 0x49, 0x08, 0x0a, 0xf0, 0x04,
|
||||
0x49, 0xee, 0xd0, 0x09, 0xde, 0xb8, 0x06, 0x10, 0x03, 0x9d, 0xb8, 0x06,
|
||||
0x60, 0xc9, 0xc0, 0xb0, 0xfb, 0xfe, 0xb8, 0x06, 0x60, 0xbd, 0x38, 0x07,
|
||||
0x29, 0x08, 0xf0, 0x16, 0xbd, 0xb8, 0x04, 0xa4, 0x27, 0xc0, 0x94, 0xd0,
|
||||
0x04, 0x09, 0x80, 0xd0, 0x06, 0xc0, 0x92, 0xd0, 0x05, 0x29, 0x7f, 0x9d,
|
||||
0xb8, 0x04, 0x60, 0x8a, 0x0a, 0x0a, 0x0a, 0x0a, 0x85, 0x26, 0xa9, 0x00,
|
||||
0x9d, 0xb8, 0x05, 0x70, 0x0f, 0xa0, 0x00, 0xb1, 0x3c, 0x85, 0x27, 0x20,
|
||||
0x02, 0xcc, 0x20, 0xba, 0xfc, 0x90, 0xf2, 0x60, 0x20, 0xd2, 0xca, 0x90,
|
||||
0xfb, 0xb9, 0x88, 0xc0, 0xa0, 0x00, 0x91, 0x3c, 0x20, 0xba, 0xfc, 0x90,
|
||||
0xef, 0x60, 0xbd, 0xb8, 0x04, 0x10, 0x31, 0xa9, 0x02, 0x48, 0xa9, 0x7f,
|
||||
0x20, 0xe2, 0xcd, 0xa4, 0x24, 0xb1, 0x28, 0x85, 0x27, 0xa9, 0x07, 0x25,
|
||||
0x4f, 0xd0, 0x10, 0xa4, 0x24, 0xa9, 0xdf, 0xd1, 0x28, 0xd0, 0x02, 0xa5,
|
||||
0x27, 0x91, 0x28, 0xe6, 0x4f, 0xe6, 0x4f, 0xbd, 0xb8, 0x04, 0x30, 0x09,
|
||||
0x20, 0x11, 0xcc, 0x68, 0xa9, 0x8d, 0x85, 0x27, 0x60, 0x20, 0xff, 0xca,
|
||||
0x90, 0x0c, 0x20, 0x11, 0xcc, 0x20, 0xd1, 0xc9, 0x20, 0xa3, 0xcc, 0x4c,
|
||||
0x2b, 0xca, 0x20, 0x3e, 0xcc, 0x90, 0xc6, 0x70, 0xbe, 0xbd, 0x38, 0x07,
|
||||
0x0a, 0x10, 0x22, 0x68, 0xa8, 0xa5, 0x27, 0xc0, 0x01, 0xf0, 0x20, 0xb0,
|
||||
0x34, 0xc9, 0x9b, 0xd0, 0x06, 0xc8, 0x98, 0x48, 0x4c, 0x2b, 0xca, 0xc9,
|
||||
0xc1, 0x90, 0x08, 0xc9, 0xdb, 0xb0, 0x04, 0x09, 0x20, 0x85, 0x27, 0x98,
|
||||
0x48, 0x20, 0x68, 0xcb, 0x4c, 0x2b, 0xca, 0xc9, 0x9b, 0xf0, 0xe2, 0xc9,
|
||||
0xb0, 0x90, 0x0a, 0xc9, 0xbb, 0xb0, 0x06, 0xa8, 0xb9, 0x09, 0xca, 0x85,
|
||||
0x27, 0xa0, 0x00, 0xf0, 0xe2, 0xc9, 0x9b, 0xd0, 0xde, 0xa0, 0x00, 0xf0,
|
||||
0xc9, 0x9b, 0x9c, 0x9f, 0xdb, 0xdc, 0xdf, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
||||
0xa2, 0xca, 0xca, 0xd0, 0xfd, 0x38, 0xe9, 0x01, 0xd0, 0xf6, 0xae, 0xf8,
|
||||
0x07, 0x60, 0xa4, 0x26, 0xb9, 0x89, 0xc0, 0x48, 0x29, 0x20, 0x4a, 0x4a,
|
||||
0x85, 0x35, 0x68, 0x29, 0x0f, 0xc9, 0x08, 0x90, 0x04, 0x29, 0x07, 0xb0,
|
||||
0x02, 0xa5, 0x35, 0x05, 0x35, 0xf0, 0x05, 0x09, 0x20, 0x9d, 0xb8, 0x05,
|
||||
0x60, 0xa4, 0x26, 0xb9, 0x89, 0xc0, 0x29, 0x70, 0xc9, 0x10, 0x60, 0x20,
|
||||
0xd2, 0xca, 0x90, 0x15, 0xb9, 0x88, 0xc0, 0x09, 0x80, 0xc9, 0x8a, 0xd0,
|
||||
0x09, 0xa8, 0xbd, 0x38, 0x07, 0x29, 0x20, 0xd0, 0x03, 0x98, 0x38, 0x60,
|
||||
0x18, 0x60, 0xa4, 0x26, 0xb9, 0x81, 0xc0, 0x4a, 0xb0, 0x36, 0xbd, 0xb8,
|
||||
0x04, 0x29, 0x07, 0xf0, 0x05, 0x20, 0xfc, 0xcd, 0x38, 0x60, 0xa5, 0x27,
|
||||
0x29, 0x7f, 0xdd, 0x38, 0x05, 0xd0, 0x05, 0xfe, 0xb8, 0x04, 0x38, 0x60,
|
||||
0xbd, 0x38, 0x07, 0x29, 0x08, 0xf0, 0x15, 0x20, 0xff, 0xca, 0x90, 0x10,
|
||||
0xc9, 0x93, 0xf0, 0x0e, 0x48, 0xbd, 0x38, 0x07, 0x4a, 0x4a, 0x68, 0x90,
|
||||
0x04, 0x9d, 0xb8, 0x06, 0x18, 0x60, 0x20, 0xaa, 0xc8, 0xc9, 0x91, 0xd0,
|
||||
0xf9, 0x18, 0x60, 0x20, 0x1a, 0xcb, 0xb0, 0xf1, 0x20, 0x9e, 0xcc, 0xa4,
|
||||
0x26, 0xb9, 0x81, 0xc0, 0x4a, 0x90, 0x4e, 0x4a, 0x90, 0x4b, 0xa5, 0x27,
|
||||
0x48, 0xbd, 0x38, 0x04, 0xc9, 0x67, 0x90, 0x10, 0xc9, 0x6c, 0xb0, 0x22,
|
||||
0xc9, 0x6b, 0x68, 0x48, 0x49, 0x9b, 0x29, 0x7f, 0xd0, 0x18, 0xb0, 0x19,
|
||||
0xbd, 0xb8, 0x04, 0x29, 0x1f, 0x09, 0x80, 0x85, 0x27, 0x20, 0x02, 0xcc,
|
||||
0x20, 0xaa, 0xc8, 0x49, 0x86, 0xd0, 0xed, 0x9d, 0x38, 0x04, 0xde, 0x38,
|
||||
0x04, 0x68, 0x85, 0x27, 0x49, 0x8d, 0x0a, 0xd0, 0x0a, 0xbd, 0xb8, 0x03,
|
||||
0x29, 0x30, 0xf0, 0x03, 0x9d, 0x38, 0x04, 0x20, 0x02, 0xcc, 0x4c, 0xea,
|
||||
0xcb, 0x20, 0x02, 0xcc, 0x0a, 0xa8, 0xbd, 0xb8, 0x03, 0xc0, 0x18, 0xf0,
|
||||
0x0c, 0x4a, 0x4a, 0xc0, 0x14, 0xf0, 0x06, 0x4a, 0x4a, 0xc0, 0x1a, 0xd0,
|
||||
0x25, 0x29, 0x03, 0xf0, 0x0d, 0xa8, 0xb9, 0xfe, 0xcb, 0xa8, 0xa9, 0x20,
|
||||
0x20, 0xc4, 0xca, 0x88, 0xd0, 0xf8, 0xa5, 0x27, 0x0a, 0xc9, 0x1a, 0xd0,
|
||||
0x0d, 0xbd, 0x38, 0x07, 0x6a, 0x90, 0x07, 0xa9, 0x8a, 0x85, 0x27, 0x4c,
|
||||
0x6b, 0xcb, 0x60, 0x01, 0x08, 0x40, 0x20, 0xf5, 0xca, 0xd0, 0xfb, 0x98,
|
||||
0x09, 0x89, 0xa8, 0xa5, 0x27, 0x99, 0xff, 0xbf, 0x60, 0x48, 0xa4, 0x24,
|
||||
0xa5, 0x27, 0x91, 0x28, 0x68, 0xc9, 0x95, 0xd0, 0x0c, 0xa5, 0x27, 0xc9,
|
||||
0x20, 0xb0, 0x06, 0x20, 0xdf, 0xcc, 0x59, 0xdb, 0xcc, 0x85, 0x27, 0x60,
|
||||
0x18, 0xbd, 0x38, 0x07, 0x29, 0x04, 0xf0, 0x09, 0xad, 0x00, 0xc0, 0x10,
|
||||
0x04, 0x8d, 0x10, 0xc0, 0x38, 0x60, 0xe6, 0x4e, 0xd0, 0x02, 0xe6, 0x4f,
|
||||
0x20, 0x2c, 0xcc, 0xb8, 0x90, 0xf3, 0x20, 0x11, 0xcc, 0x29, 0x7f, 0xdd,
|
||||
0x38, 0x05, 0xd0, 0x3d, 0xa4, 0x26, 0xb9, 0x81, 0xc0, 0x4a, 0xb0, 0x35,
|
||||
0xa0, 0x0a, 0xb9, 0x93, 0xcc, 0x85, 0x27, 0x98, 0x48, 0x20, 0xa3, 0xcc,
|
||||
0x68, 0xa8, 0x88, 0x10, 0xf1, 0xa9, 0x01, 0x20, 0x7b, 0xce, 0x20, 0x34,
|
||||
0xcc, 0x10, 0xfb, 0xc9, 0x88, 0xf0, 0xe1, 0x85, 0x27, 0x20, 0xa3, 0xcc,
|
||||
0x20, 0x1a, 0xcb, 0xbd, 0xb8, 0x04, 0x29, 0x07, 0xd0, 0xe8, 0xa9, 0x8d,
|
||||
0x85, 0x27, 0x2c, 0x58, 0xff, 0x38, 0x60, 0xba, 0xc3, 0xd3, 0xd3, 0xa0,
|
||||
0xc5, 0xcc, 0xd0, 0xd0, 0xc1, 0x8d, 0xbd, 0x38, 0x07, 0x10, 0x13, 0xbd,
|
||||
0x38, 0x07, 0x29, 0x02, 0xf0, 0x0d, 0xbd, 0xb8, 0x04, 0x29, 0x38, 0xf0,
|
||||
0x06, 0x8a, 0x48, 0xa9, 0xaf, 0x48, 0x60, 0x20, 0xdf, 0xcc, 0x09, 0x80,
|
||||
0xc9, 0xe0, 0x90, 0x06, 0x59, 0xd3, 0xcc, 0x4c, 0xf6, 0xfd, 0xc9, 0xc1,
|
||||
0x90, 0xf9, 0xc9, 0xdb, 0xb0, 0xf5, 0x59, 0xd7, 0xcc, 0x90, 0xf0, 0x20,
|
||||
0x00, 0xe0, 0x20, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0xe0, 0xc0, 0xbd,
|
||||
0xb8, 0x03, 0x2a, 0x2a, 0x2a, 0x29, 0x03, 0xa8, 0xa5, 0x27, 0x60, 0x42,
|
||||
0x67, 0xc0, 0x54, 0x47, 0xa6, 0x43, 0x87, 0xa6, 0x51, 0x47, 0xb8, 0x52,
|
||||
0xc7, 0xac, 0x5a, 0xe7, 0xf3, 0x49, 0x90, 0xd3, 0x4b, 0x90, 0xdf, 0x45,
|
||||
0x43, 0x80, 0x46, 0xe3, 0x04, 0x4c, 0xe3, 0x01, 0x58, 0xe3, 0x08, 0x54,
|
||||
0x83, 0x40, 0x53, 0x43, 0x40, 0x4d, 0xe3, 0x20, 0x00, 0x42, 0xf6, 0x7c,
|
||||
0x50, 0xf6, 0x9a, 0x44, 0xf6, 0x9b, 0x46, 0xf6, 0x46, 0x4c, 0xf6, 0x40,
|
||||
0x43, 0xf6, 0x3a, 0x54, 0xd6, 0x34, 0x4e, 0x90, 0xe8, 0x53, 0x56, 0x60,
|
||||
0x00, 0xa9, 0x3f, 0xa0, 0x07, 0xd0, 0x10, 0xa9, 0xcf, 0xa0, 0x05, 0xd0,
|
||||
0x0a, 0xa9, 0xf3, 0xa0, 0x03, 0xd0, 0x04, 0xa9, 0xfc, 0xa0, 0x01, 0x3d,
|
||||
0xb8, 0x03, 0x85, 0x2a, 0xbd, 0x38, 0x04, 0x29, 0x03, 0x18, 0x6a, 0x2a,
|
||||
0x88, 0xd0, 0xfc, 0x05, 0x2a, 0x9d, 0xb8, 0x03, 0x60, 0x29, 0x07, 0x0a,
|
||||
0x0a, 0x0a, 0x85, 0x2a, 0x0a, 0xc5, 0x26, 0xf0, 0x0f, 0xbd, 0xb8, 0x04,
|
||||
0x29, 0xc7, 0x05, 0x2a, 0x9d, 0xb8, 0x04, 0xa9, 0x00, 0x9d, 0x38, 0x06,
|
||||
0x60, 0x29, 0x0f, 0xd0, 0x07, 0xb9, 0x81, 0xc0, 0x4a, 0x4a, 0x4a, 0x4a,
|
||||
0x09, 0x10, 0x85, 0x2a, 0xa9, 0xe0, 0x85, 0x2b, 0xb9, 0x8b, 0xc0, 0x25,
|
||||
0x2b, 0x05, 0x2a, 0x99, 0x8b, 0xc0, 0x60, 0x88, 0x0a, 0x0a, 0x0a, 0x0a,
|
||||
0x0a, 0x85, 0x2a, 0xa9, 0x1f, 0xd0, 0xe7, 0x1e, 0xb8, 0x04, 0x38, 0xb0,
|
||||
0x10, 0x99, 0x89, 0xc0, 0x20, 0x93, 0xfe, 0x20, 0x89, 0xfe, 0xae, 0xf8,
|
||||
0x07, 0x1e, 0xb8, 0x04, 0x18, 0x7e, 0xb8, 0x04, 0x60, 0xb9, 0x8a, 0xc0,
|
||||
0x48, 0x09, 0x0c, 0x99, 0x8a, 0xc0, 0xa9, 0xe9, 0x20, 0xc4, 0xca, 0x68,
|
||||
0x99, 0x8a, 0xc0, 0x60, 0xa9, 0x28, 0x9d, 0x38, 0x06, 0xa9, 0x80, 0x1d,
|
||||
0x38, 0x07, 0xd0, 0x05, 0xa9, 0xfe, 0x3d, 0x38, 0x07, 0x9d, 0x38, 0x07,
|
||||
0x60, 0xc9, 0x28, 0x90, 0x0e, 0x9d, 0x38, 0x06, 0xa9, 0x3f, 0xd0, 0xee,
|
||||
0x1e, 0x38, 0x05, 0x38, 0x7e, 0x38, 0x05, 0x60, 0xa8, 0xa5, 0x27, 0x29,
|
||||
0x7f, 0xc9, 0x20, 0xd0, 0x09, 0xc0, 0x03, 0xf0, 0x01, 0x60, 0xa9, 0x04,
|
||||
0xd0, 0x6d, 0xc9, 0x0d, 0xd0, 0x12, 0x20, 0x79, 0xce, 0xc0, 0x07, 0xf0,
|
||||
0x01, 0x60, 0xa9, 0xcd, 0x48, 0xbd, 0x38, 0x04, 0x48, 0xa4, 0x26, 0x60,
|
||||
0x85, 0x35, 0xa9, 0xce, 0x48, 0xb9, 0x30, 0xce, 0x48, 0xa5, 0x35, 0x60,
|
||||
0xa7, 0x37, 0x61, 0x89, 0x8a, 0xa7, 0x89, 0x89, 0xdd, 0x38, 0x05, 0xd0,
|
||||
0x06, 0xde, 0xb8, 0x04, 0x4c, 0x02, 0xcc, 0xc9, 0x30, 0x90, 0x0d, 0xc9,
|
||||
0x3a, 0xb0, 0x09, 0x29, 0x0f, 0x9d, 0x38, 0x04, 0xa9, 0x02, 0xd0, 0x27,
|
||||
0xc9, 0x20, 0xb0, 0x06, 0x9d, 0x38, 0x05, 0x4c, 0x79, 0xce, 0xa0, 0x00,
|
||||
0xf0, 0x4d, 0x49, 0x30, 0xc9, 0x0a, 0xb0, 0x0d, 0xa0, 0x0a, 0x7d, 0x38,
|
||||
0x04, 0x88, 0xd0, 0xfa, 0x9d, 0x38, 0x04, 0xf0, 0x15, 0xa0, 0x2e, 0xd0,
|
||||
0x36, 0xa9, 0x00, 0x85, 0x2a, 0xae, 0xf8, 0x07, 0xbd, 0xb8, 0x04, 0x29,
|
||||
0xf8, 0x05, 0x2a, 0x9d, 0xb8, 0x04, 0x60, 0xa8, 0xbd, 0x38, 0x04, 0xc0,
|
||||
0x44, 0xf0, 0x09, 0xc0, 0x45, 0xd0, 0x11, 0x1d, 0x38, 0x07, 0xd0, 0x05,
|
||||
0x49, 0xff, 0x3d, 0x38, 0x07, 0x9d, 0x38, 0x07, 0xa9, 0x06, 0xd0, 0xd3,
|
||||
0xa9, 0x20, 0x9d, 0xb8, 0x05, 0xd0, 0xf5, 0xb9, 0xeb, 0xcc, 0xf0, 0xf4,
|
||||
0xc5, 0x35, 0xf0, 0x05, 0xc8, 0xc8, 0xc8, 0xd0, 0xf2, 0xc8, 0xb9, 0xeb,
|
||||
0xcc, 0x85, 0x2a, 0x29, 0x20, 0xd0, 0x07, 0xbd, 0x38, 0x07, 0x29, 0x10,
|
||||
0xd0, 0xeb, 0xbd, 0x38, 0x07, 0x4a, 0x4a, 0x24, 0x2a, 0xb0, 0x04, 0x10,
|
||||
0xe0, 0x30, 0x02, 0x50, 0xdc, 0xa5, 0x2a, 0x48, 0x29, 0x07, 0x20, 0x7b,
|
||||
0xce, 0xc8, 0x68, 0x29, 0x10, 0xd0, 0x07, 0xb9, 0xeb, 0xcc, 0x9d, 0x38,
|
||||
0x04, 0x60, 0xa9, 0xcd, 0x48, 0xb9, 0xeb, 0xcc, 0x48, 0xa4, 0x26, 0xbd,
|
||||
0x38, 0x04, 0x60, 0xc2, 0x2c, 0x58, 0xff, 0x70, 0x0c, 0x38, 0x90, 0x18,
|
||||
0xb8, 0x50, 0x06, 0x01, 0x31, 0x8e, 0x94, 0x97, 0x9a, 0x85, 0x27, 0x86,
|
||||
0x35, 0x8a, 0x48, 0x98, 0x48, 0x08, 0x78, 0x8d, 0xff, 0xcf, 0x20, 0x58,
|
||||
0xff, 0xba, 0xbd, 0x00, 0x01, 0x8d, 0xf8, 0x07, 0xaa, 0x0a, 0x0a, 0x0a,
|
||||
0x0a, 0x85, 0x26, 0xa8, 0x28, 0x50, 0x29, 0x1e, 0x38, 0x05, 0x5e, 0x38,
|
||||
0x05, 0xb9, 0x8a, 0xc0, 0x29, 0x1f, 0xd0, 0x05, 0xa9, 0xef, 0x20, 0x05,
|
||||
0xc8, 0xe4, 0x37, 0xd0, 0x0b, 0xa9, 0x07, 0xc5, 0x36, 0xf0, 0x05, 0x85,
|
||||
0x36, 0x18, 0x90, 0x08, 0xe4, 0x39, 0xd0, 0xf9, 0xa9, 0x05, 0x85, 0x38,
|
||||
0xbd, 0x38, 0x07, 0x29, 0x02, 0x08, 0x90, 0x03, 0x4c, 0xbf, 0xc8, 0xbd,
|
||||
0xb8, 0x04, 0x48, 0x0a, 0x10, 0x0e, 0xa6, 0x35, 0xa5, 0x27, 0x09, 0x20,
|
||||
0x9d, 0x00, 0x02, 0x85, 0x27, 0xae, 0xf8, 0x07, 0x68, 0x29, 0xbf, 0x9d,
|
||||
0xb8, 0x04, 0x28, 0xf0, 0x06, 0x20, 0x63, 0xcb, 0x4c, 0xb5, 0xc8, 0x4c,
|
||||
0xfc, 0xc8, 0x20, 0x00, 0xc8, 0xa2, 0x00, 0x60, 0x4c, 0x9b, 0xc8, 0x4c,
|
||||
0xaa, 0xc9, 0x4a, 0x20, 0x9b, 0xc9, 0xb0, 0x08, 0x20, 0xf5, 0xca, 0xf0,
|
||||
0x06, 0x18, 0x90, 0x03, 0x20, 0xd2, 0xca, 0xbd, 0xb8, 0x05, 0xaa, 0x60,
|
||||
0xa2, 0x03, 0xb5, 0x36, 0x48, 0xca, 0x10, 0xfa, 0xae, 0xf8, 0x07, 0xbd,
|
||||
0x38, 0x06, 0x85, 0x36, 0xbd, 0xb8, 0x04, 0x29, 0x38, 0x4a, 0x4a, 0x4a,
|
||||
0x09, 0xc0, 0x85, 0x37, 0x8a, 0x48, 0xa5, 0x27, 0x48, 0x09, 0x80, 0x20,
|
||||
0xed, 0xfd, 0x68, 0x85, 0x27, 0x68, 0x8d, 0xf8, 0x07, 0xaa, 0x0a, 0x0a,
|
||||
0x0a, 0x0a, 0x85, 0x26, 0x8d, 0xff, 0xcf, 0xa5, 0x36, 0x9d, 0x38, 0x06,
|
||||
0xa2, 0x00, 0x68, 0x95, 0x36, 0xe8, 0xe0, 0x04, 0x90, 0xf8, 0xae, 0xf8,
|
||||
0x07, 0x60, 0xc1, 0xd0, 0xd0, 0xcc, 0xc5, 0x08
|
||||
};
|
||||
static const unsigned int mii_rom_ssc_len = 2048;
|
@@ -16,6 +16,9 @@ ioerr = $27 ; I/O error code
|
||||
nodev = $28 ; no device connected
|
||||
wperr = $2B ; write protect error
|
||||
knownRts = $FF58
|
||||
; https://6502disassembly.com/a2-rom/AutoF8ROM.html
|
||||
sloop = $faba ; continue scanning next slot
|
||||
|
||||
.org $c000
|
||||
.verbose
|
||||
entryOffset = $c0 ; entry point offset
|
||||
@@ -64,22 +67,55 @@ start:
|
||||
bne error
|
||||
ldx unit ; ProDOS block 0 code wants ProDOS unit number in X
|
||||
jmp $801 ; Continue reading the disk
|
||||
error jmp $E000 ; Out to BASIC on error
|
||||
; display a message that we are continuing without a Smartport disk
|
||||
error:
|
||||
; $00 $01 will contain our slot rom address if we were called by the bootrom
|
||||
; otherwise it will have something else (in case of PR#x command)
|
||||
lda $07FF ; current slot base address
|
||||
cmp #$c1 ; make sure we aren't in slot 1
|
||||
beq basic ; if we were, basic prompt
|
||||
cmp $01 ; Bootrom also store it there, make sure it is.
|
||||
bne basic ; if we were, bail out
|
||||
lda $00
|
||||
beq disp ; seem we were called by the ROM
|
||||
; We were in slot 1!
|
||||
basic:
|
||||
jmp $E000 ; Out to BASIC on error
|
||||
|
||||
.org $c0c0
|
||||
disp lda #<msg
|
||||
sta buflo
|
||||
lda $07FF
|
||||
sta bufhi
|
||||
ldy #0
|
||||
_dsp lda (buflo),y
|
||||
beq dspdone
|
||||
sta $0755,y ; last-1 line of text
|
||||
iny
|
||||
bra _dsp
|
||||
dspdone:
|
||||
lda $07FF
|
||||
and #$0F
|
||||
dec
|
||||
ora #$b0 ; Make it an ascii number
|
||||
sta $0755,y ; last line of text
|
||||
; This bit of ROM is what will decrement the slot number, scan for another
|
||||
; disc controller, and JUMP to that ROM in turn.
|
||||
jmp sloop
|
||||
|
||||
; offset byte (to center the text) followed by a message
|
||||
msg .asc "No SmartPort Disc, Booting S", $0
|
||||
|
||||
.org $c0c0 ; do not change this, this is entryOffset
|
||||
; jump back to mii code
|
||||
entryHD nop ; Hard drive driver address
|
||||
bra magicHD
|
||||
bra magicSM
|
||||
|
||||
.org $c0d0
|
||||
magicHD .db $db, $fb, $0
|
||||
magicHD .db $eb, $fb, $0
|
||||
bra done
|
||||
|
||||
.org $c0e0
|
||||
magicSM .db $db, $fb, $0
|
||||
magicSM .db $eb, $fb, $0
|
||||
bra done
|
||||
|
||||
.org $c0f0
|
||||
DONE bcs ERR
|
||||
lda #$00
|
||||
|
Binary file not shown.
@@ -14,9 +14,9 @@
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "mii_65c02.h"
|
||||
#include "mii_65c02_asm.h"
|
||||
#include "mii_65c02_disasm.h"
|
||||
#include "mii_65c02.c"
|
||||
#include "mii_65c02_asm.c"
|
||||
#include "mii_65c02_disasm.c"
|
||||
#include "mii_65c02_ops.h"
|
||||
|
||||
int
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "mii_ssc.h"
|
||||
#include "mii_mui_gl.h"
|
||||
#include "miigl_counter.h"
|
||||
#include "mii_sokol_audio.h"
|
||||
#define MII_ICON64_DEFINE
|
||||
#include "mii_icon64.h"
|
||||
|
||||
@@ -533,21 +534,6 @@ mii_x11_terminate(
|
||||
XCloseDisplay(ui->dpy);
|
||||
}
|
||||
|
||||
// TODO factor this into a single table, this is dupped from mii_mui_settings.c!
|
||||
static const struct {
|
||||
const char * name;
|
||||
} mii_slot_driver[MII_SLOT_DRIVER_COUNT] = {
|
||||
[MII_SLOT_DRIVER_NONE] = { "none", },
|
||||
[MII_SLOT_DRIVER_SMARTPORT] = { "smartport", },
|
||||
[MII_SLOT_DRIVER_DISK2] = { "disk2", },
|
||||
[MII_SLOT_DRIVER_MOUSE] = { "mouse", },
|
||||
[MII_SLOT_DRIVER_SSC] = { "ssc", },
|
||||
[MII_SLOT_DRIVER_ROM1MB] = { "eecard", },
|
||||
[MII_SLOT_DRIVER_MOCKINGBOARD] = { "mockingboard", },
|
||||
#ifdef MII_DANII
|
||||
[MII_SLOT_DRIVER_DANII] = { "danii", },
|
||||
#endif
|
||||
};
|
||||
|
||||
void
|
||||
mii_ui_reconfigure_slot(
|
||||
@@ -618,7 +604,7 @@ _mii_ui_load_config(
|
||||
*ioFlags |= MII_INIT_NSC;
|
||||
if (config->titan_accelerator)
|
||||
*ioFlags |= MII_INIT_TITAN;
|
||||
mii->speaker.muted = config->audio_muted;
|
||||
mii->audio.muted = config->audio_muted;
|
||||
mii->video.color_mode = config->video_mode;
|
||||
mii_video_set_mode(mii, config->video_mode);
|
||||
for (int i = 0; i < 7; i++) {
|
||||
@@ -631,9 +617,9 @@ _mii_ui_load_config(
|
||||
}
|
||||
int slot = i + 1;
|
||||
if (mii_slot_drv_register(mii, slot,
|
||||
mii_slot_driver[config->slot[i].driver].name) < 0) {
|
||||
mii_slot_driver[config->slot[i].driver].driver) < 0) {
|
||||
printf("%s failed to register driver %s\n", __func__,
|
||||
mii_slot_driver[config->slot[i].driver].name);
|
||||
mii_slot_driver[config->slot[i].driver].driver);
|
||||
}
|
||||
mii_ui_reconfigure_slot(mii, config, slot);
|
||||
}
|
||||
@@ -659,9 +645,12 @@ mii_x11_reload_config(
|
||||
mii_mui_menu_slot_menu_update(&ui->video);
|
||||
printf("%s (re)loading config\n", __func__);
|
||||
// if we're silent from the command line, we are *always* silent.
|
||||
mii->speaker.speaker_off = !!(g_startup_flags & MII_INIT_SILENT);
|
||||
mii_init(mii);
|
||||
_mii_ui_load_config(mii, config, &flags);
|
||||
if (g_startup_flags & MII_INIT_SILENT)
|
||||
mii->audio.drv = NULL;
|
||||
else
|
||||
mii_sokol_audio_init(mii);
|
||||
mii_prepare(mii, flags);
|
||||
mii_reset(mii, true);
|
||||
mii_mui_gl_prepare_textures(&ui->video);
|
||||
@@ -713,6 +702,10 @@ main(
|
||||
printf("mii: Invalid argument %s, skipped\n", argv[idx]);
|
||||
} else if (r == -1)
|
||||
exit(1);
|
||||
if (!(flags & MII_INIT_SILENT))
|
||||
mii_sokol_audio_init(mii);
|
||||
else
|
||||
printf("Audio disabled\n");
|
||||
mii_prepare(mii, flags);
|
||||
g_startup_flags = flags;
|
||||
}
|
||||
|
@@ -311,8 +311,8 @@ mii_mui_load_1mbrom(
|
||||
c->uid_mask = uid_mask;
|
||||
c->value = config->use_default != 0;
|
||||
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c) == 0)
|
||||
continue;
|
||||
mui_control_set_action(c, _mii_1mb_action_cb, m);
|
||||
|
@@ -86,14 +86,14 @@ _mii_floppy_check_file(
|
||||
int iswoz = 0;
|
||||
if (!strcasecmp(suffix, ".nib")) {
|
||||
want_size = NIB_SIZE;
|
||||
out->file_ro_format = 1;
|
||||
out->file_ro_format = 0;
|
||||
} else if (!strcasecmp(suffix, ".dsk")) {
|
||||
want_size = DSK_SIZE;
|
||||
out->file_ro_format = 1;
|
||||
out->file_ro_format = 0;
|
||||
} else if (!strcasecmp(suffix, ".po") ||
|
||||
!strcasecmp(suffix, ".do")) {
|
||||
want_size = DSK_SIZE;
|
||||
out->file_ro_format = 1;
|
||||
out->file_ro_format = 0;
|
||||
} else if (!strcasecmp(suffix, ".woz") ||
|
||||
!strcasecmp(suffix, ".woz1") ||
|
||||
!strcasecmp(suffix, ".woz2")) {
|
||||
@@ -321,7 +321,7 @@ mii_mui_load_2dsk(
|
||||
return w;
|
||||
}
|
||||
c2_pt_t where = {};
|
||||
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 640, 340);
|
||||
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 640, 370);
|
||||
c2_rect_offset(&wpos,
|
||||
(ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
|
||||
(ui->screen_size.y * 0.45) - (c2_rect_height(&wpos) / 2));
|
||||
@@ -382,8 +382,8 @@ mii_mui_load_2dsk(
|
||||
cp.r = c2_rect_width(&w->frame) - margin * 4;
|
||||
c = mui_separator_new(w, cp);
|
||||
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c) == 0)
|
||||
continue;
|
||||
mui_control_set_action(c, _mii_2dsk_action_cb, m);
|
||||
|
@@ -14,8 +14,7 @@
|
||||
#include "mui.h"
|
||||
#include "mii_icon64.h"
|
||||
|
||||
extern const unsigned char mui_geneva_font_data[];
|
||||
extern const unsigned int mui_geneva_font_size;
|
||||
#include "fonts/mui_geneva_font.h"
|
||||
|
||||
enum {
|
||||
MII_ABOUT_WINDOW_ID = FCC('a','b','o','t'),
|
||||
@@ -192,7 +191,7 @@ mii_mui_about(
|
||||
mui_font_t *geneva = mui_font_find(ui, "geneva");
|
||||
if (!geneva) {
|
||||
geneva = mui_font_from_mem(ui, "geneva", 24,
|
||||
mui_geneva_font_data, mui_geneva_font_size);
|
||||
mui_geneva_font, mui_geneva_font_len);
|
||||
}
|
||||
mui_glyph_line_array_t lines_about = {};
|
||||
mui_font_measure(font, tbox, about, 0, &lines_about,
|
||||
|
@@ -196,8 +196,8 @@ mii_mui_gl_prepare_textures(
|
||||
// the init() call clears the structure, keep our id around
|
||||
unsigned int tex = dr->texture.id;
|
||||
mui_drawable_init(dr,
|
||||
C2_PT(MII_FLOPPY_DEFAULT_TRACK_SIZE, MII_FLOPPY_TRACK_COUNT),
|
||||
8, floppy[fi]->track_data, MII_FLOPPY_DEFAULT_TRACK_SIZE);
|
||||
C2_PT(MII_FLOPPY_MAX_TRACK_SIZE, MII_FLOPPY_TRACK_COUNT),
|
||||
8, floppy[fi]->track_data, MII_FLOPPY_MAX_TRACK_SIZE);
|
||||
dr->texture.id = tex;
|
||||
_prep_grayscale_texture(dr);
|
||||
if (!f->heat) {
|
||||
@@ -206,8 +206,9 @@ mii_mui_gl_prepare_textures(
|
||||
#elif defined(__SSE2__)
|
||||
posix_memalign((void**)&f->heat, 16, sizeof(*f->heat));
|
||||
#else
|
||||
f->heat = calloc(1, sizeof(*f->heat));
|
||||
f->heat = malloc(sizeof(*f->heat));
|
||||
#endif
|
||||
memset(f->heat, 0, sizeof(*f->heat));
|
||||
}
|
||||
dr = &ui->pixels.floppy[fi].hm_read;
|
||||
tex = dr->texture.id;
|
||||
@@ -402,7 +403,7 @@ mii_mui_gl_run(
|
||||
ui->floppy[fi].seed_load = f->seed_dirty;
|
||||
// printf("Floppy %d: Reloading texture\n", fi);
|
||||
int bc = (f->tracks[0].bit_count + 7) / 8;
|
||||
int max = MII_FLOPPY_DEFAULT_TRACK_SIZE;
|
||||
int max = MII_FLOPPY_MAX_TRACK_SIZE;
|
||||
ui->floppy[fi].max_width = (double)bc / (double)max;
|
||||
glBindTexture(GL_TEXTURE_2D, dr->texture.id);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
|
||||
|
@@ -220,8 +220,8 @@ mii_mui_load_binary(
|
||||
m->file.button->uid = MII_LBIN_SELECT;
|
||||
m->file.button->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, 's');
|
||||
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c) == 0)
|
||||
continue;
|
||||
mui_control_set_action(c, _mii_loadbin_action_cb, m);
|
||||
|
@@ -144,20 +144,20 @@ mii_menubar_action(
|
||||
items[i].mark[0] = 0;
|
||||
} break;
|
||||
case FCC('a','u','d','0'):
|
||||
if (mii->speaker.muted)
|
||||
if (mii->audio.muted)
|
||||
strcpy(items[i].mark, MUI_GLYPH_TICK);
|
||||
else
|
||||
items[i].mark[0] = 0;
|
||||
break;
|
||||
case FCC('a','u','d','+'):
|
||||
items[i].disabled = mii->speaker.volume >= 10;
|
||||
items[i].disabled = mii->speaker.source.volume >= 10;
|
||||
break;
|
||||
case FCC('a','u','d','-'):
|
||||
items[i].disabled = mii->speaker.volume <= 0.1;
|
||||
items[i].disabled = mii->speaker.source.volume <= 0.1;
|
||||
break;
|
||||
case FCC('s','a','u','d'):
|
||||
// are we in silent mode ?
|
||||
items[i].disabled = mii->speaker.speaker_off;
|
||||
items[i].disabled = mii->audio.drv == NULL;
|
||||
break;
|
||||
case FCC('m','h','z','1'):
|
||||
if (mii->speed <= 1.1 && mii->speed >= 0.9)
|
||||
@@ -275,14 +275,16 @@ mii_menubar_action(
|
||||
mii_th_fifo_write(mii_thread_get_fifo(&ui->mii), sig);
|
||||
} break;
|
||||
case FCC('a','u','d','0'):
|
||||
mii->speaker.muted = !mii->speaker.muted;
|
||||
ui->config.audio_muted = mii->speaker.muted;
|
||||
mii->audio.muted = !mii->audio.muted;
|
||||
ui->config.audio_muted = mii->audio.muted;
|
||||
break;
|
||||
case FCC('a','u','d','+'):
|
||||
mii_speaker_volume(&mii->speaker, mii->speaker.volume + 1);
|
||||
mii_audio_volume(&mii->speaker.source,
|
||||
mii->speaker.source.volume + 1);
|
||||
break;
|
||||
case FCC('a','u','d','-'):
|
||||
mii_speaker_volume(&mii->speaker, mii->speaker.volume - 1);
|
||||
mii_audio_volume(&mii->speaker.source,
|
||||
mii->speaker.source.volume - 1);
|
||||
break;
|
||||
case FCC('v','d','C','l'): {
|
||||
// printf("%s Cycle video\n", __func__);
|
||||
|
@@ -187,18 +187,24 @@ mii_settings_load(
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char * name;
|
||||
} mii_slot_driver[MII_SLOT_DRIVER_COUNT] = {
|
||||
[MII_SLOT_DRIVER_NONE] = { "none", },
|
||||
[MII_SLOT_DRIVER_SMARTPORT] = { "smartport", },
|
||||
[MII_SLOT_DRIVER_DISK2] = { "disk2", },
|
||||
[MII_SLOT_DRIVER_MOUSE] = { "mouse", },
|
||||
[MII_SLOT_DRIVER_SSC] = { "ssc", },
|
||||
[MII_SLOT_DRIVER_ROM1MB] = { "eecard", },
|
||||
[MII_SLOT_DRIVER_MOCKINGBOARD] = { "mockingboard", },
|
||||
const mii_slot_driver_t mii_slot_driver[MII_SLOT_DRIVER_COUNT] = {
|
||||
[MII_SLOT_DRIVER_NONE] = {
|
||||
.driver = "none", .label = "", .description = ""},
|
||||
[MII_SLOT_DRIVER_SMARTPORT] = {
|
||||
.driver = "smartport", .label = "", .description = ""},
|
||||
[MII_SLOT_DRIVER_DISK2] = {
|
||||
.driver = "disk2", .label = "", .description = ""},
|
||||
[MII_SLOT_DRIVER_MOUSE] = {
|
||||
.driver = "mouse", .label = "", .description = ""},
|
||||
[MII_SLOT_DRIVER_SSC] = {
|
||||
.driver = "ssc", .label = "", .description = ""},
|
||||
[MII_SLOT_DRIVER_ROM1MB] = {
|
||||
.driver = "eecard", .label = "", .description = ""},
|
||||
[MII_SLOT_DRIVER_MOCKINGBOARD] = {
|
||||
.driver = "mockingboard", .label = "", .description = ""},
|
||||
#ifdef MII_DANII
|
||||
[MII_SLOT_DRIVER_DANII] = { "danii" },
|
||||
[MII_SLOT_DRIVER_DANII] = {
|
||||
.driver = "danii", .label = "", .description = ""},
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -302,7 +308,7 @@ mii_emu_save(
|
||||
break;
|
||||
}
|
||||
mii_config_set(cf, section, "driver",
|
||||
mii_slot_driver[config->slot[i].driver].name);
|
||||
mii_slot_driver[config->slot[i].driver].driver);
|
||||
}
|
||||
mii_settings_save(cf);
|
||||
return 0;
|
||||
@@ -379,7 +385,7 @@ mii_emu_load(
|
||||
if (!cl)
|
||||
continue;
|
||||
for (int j = 0; j < MII_SLOT_DRIVER_COUNT; j++) {
|
||||
if (!strcmp(mii_slot_driver[j].name, cl->value)) {
|
||||
if (!strcmp(mii_slot_driver[j].driver, cl->value)) {
|
||||
config->slot[i].driver = j;
|
||||
break;
|
||||
}
|
||||
|
@@ -99,6 +99,14 @@ enum mii_mui_driver_e {
|
||||
MII_SLOT_DRIVER_COUNT
|
||||
};
|
||||
|
||||
typedef struct mii_slot_driver_t {
|
||||
const char * driver;
|
||||
const char * label;
|
||||
const char * description;
|
||||
} mii_slot_driver_t;
|
||||
|
||||
extern const mii_slot_driver_t mii_slot_driver[MII_SLOT_DRIVER_COUNT];
|
||||
|
||||
// This is obviously not made to be terribly compact.
|
||||
typedef struct mii_machine_config_t {
|
||||
uint32_t reboot_on_load : 1,
|
||||
|
@@ -58,6 +58,50 @@ static const mii_machine_config_t _default_config = {
|
||||
},
|
||||
};
|
||||
|
||||
static const mii_machine_config_t _default_config_2c
|
||||
__attribute__((unused)) = {
|
||||
.no_slot_clock = 1,
|
||||
.titan_accelerator = 0,
|
||||
.slot = {
|
||||
[0] = {
|
||||
.driver = MII_SLOT_DRIVER_SSC,
|
||||
.conf.ssc = {
|
||||
.kind = 0,
|
||||
.socket_port = 1969,
|
||||
.device = "/dev/ttyS0",
|
||||
.baud = 9600,
|
||||
.bits = 8,
|
||||
.parity = 0,
|
||||
.stop = 0,
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.driver = MII_SLOT_DRIVER_SSC,
|
||||
.conf.ssc = {
|
||||
.kind = 0,
|
||||
.socket_port = 1970,
|
||||
.device = "/dev/ttyS1",
|
||||
.baud = 9600,
|
||||
.bits = 8,
|
||||
.parity = 0,
|
||||
.stop = 0,
|
||||
}
|
||||
},
|
||||
[2] = { .driver = 0, },
|
||||
[3] = {
|
||||
.driver = MII_SLOT_DRIVER_MOUSE,
|
||||
},
|
||||
[4] = {
|
||||
.driver = MII_SLOT_DRIVER_SMARTPORT,
|
||||
},
|
||||
[5] = {
|
||||
.driver = MII_SLOT_DRIVER_DISK2,
|
||||
},
|
||||
[6] = {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
enum {
|
||||
MII_SLOT_WINDOW_ID = FCC('s','l','o','t'),
|
||||
MII_SLOT_SAVE = FCC('s','a','v','e'),
|
||||
@@ -81,7 +125,7 @@ static const struct {
|
||||
[MII_SLOT_DRIVER_ROM1MB] = { "ROM 1MB", 1 },
|
||||
// [MII_SLOT_DRIVER_MOCKINGBOARD] = { "Mockingboard", },
|
||||
#ifdef MII_DANII
|
||||
[MII_SLOT_DRIVER_DANII] = { "DAN ][", 0 },
|
||||
// [MII_SLOT_DRIVER_DANII] = { "DAN ][", 0 },
|
||||
#endif
|
||||
{ NULL, 0 },
|
||||
};
|
||||
@@ -430,8 +474,8 @@ mii_mui_configure_slots(
|
||||
"Config…", MII_SLOT_DRIVER_CONF + i);
|
||||
c2_rect_offset(&slot_line_rect, 0, 38);
|
||||
}
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c) == 0)
|
||||
continue;
|
||||
mui_control_set_action(c, _mii_config_slot_action_cb, m);
|
||||
|
@@ -393,8 +393,8 @@ mii_mui_configure_ssc(
|
||||
"Hardware Handshake",
|
||||
MII_SSC_HANDSHAKE);
|
||||
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, 'h');
|
||||
c = NULL;
|
||||
TAILQ_FOREACH(c, &w->controls, self) {
|
||||
c = mui_controls_first(&w->controls, MUI_CONTROLS_ALL);
|
||||
for (; c; c = mui_controls_next(c, MUI_CONTROLS_ALL)) {
|
||||
if (mui_control_get_uid(c) == 0)
|
||||
continue;
|
||||
mui_control_set_action(c, _mii_ssc_action_cb, m);
|
||||
|
@@ -19,34 +19,38 @@ mii_mui_fileselect_widget( //
|
||||
mui_control_t * c = NULL;
|
||||
c2_rect_t cf;
|
||||
c2_rect_t cp = *where;
|
||||
cp.b = cp.t + base_size * 2.3;
|
||||
cp.b = cp.t + base_size * 3;
|
||||
where->b = cp.b;
|
||||
const int but_width = 100;
|
||||
cp.r = where->r - margin * 2 - but_width;
|
||||
|
||||
const int label_size = base_size * 1.85;
|
||||
c2_rect_set(&cf, margin, (margin / 2),
|
||||
c2_rect_width(&w->frame) - margin - but_width,
|
||||
(margin/2) + base_size);
|
||||
(margin/2) + label_size);
|
||||
out->box = c = mui_groupbox_new(w, cp,
|
||||
enclosing_box_title, MUI_CONTROL_TEXTBOX_FRAME);
|
||||
|
||||
c2_rect_bottom_of(&cf, cp.t, base_size);
|
||||
c2_rect_bottom_of(&cf, cp.t,
|
||||
(c2_rect_height(&cf) / 2));
|
||||
c2_rect_right_of(&cf, cp.l, margin * 0.5);
|
||||
cf.b = cf.t + icons_size;
|
||||
//cf.b = cf.t + icons_size;
|
||||
cf.r = cf.l + icons_size;
|
||||
out->icon = c = mui_textbox_new(w, cf,
|
||||
MUI_ICON_FILE, "icon_small",
|
||||
MUI_TEXT_ALIGN_MIDDLE | MUI_TEXT_ALIGN_CENTER | 0);
|
||||
c->state = MUI_CONTROL_STATE_DISABLED;
|
||||
cf.l = cf.r;
|
||||
cf.l = cf.r + 5;
|
||||
cf.r = cp.r - margin * 0.5;
|
||||
cf.b = cf.t + label_size;
|
||||
out->fname = c = mui_textbox_new(w, cf,
|
||||
"Click \"Select\" to pick a file", NULL,
|
||||
MUI_TEXT_ALIGN_MIDDLE);
|
||||
MUI_TEXT_ALIGN_MIDDLE|0);
|
||||
c->state = MUI_CONTROL_STATE_DISABLED;
|
||||
|
||||
c2_rect_right_of(&cf, cp.r, margin);
|
||||
cf.r = where->r - margin * 0.5;
|
||||
cf.b = cf.t + base_size * 1.1;
|
||||
c2_rect_offset(&cf, 0, (c2_rect_height(&cf) / 2.5));
|
||||
c2_rect_inset(&cf, -4,-4);
|
||||
out->button = c = mui_button_new(w,
|
||||
cf, MUI_BUTTON_STYLE_NORMAL,
|
||||
|
211
ui_gl/mii_sokol_audio.c
Normal file
211
ui_gl/mii_sokol_audio.c
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* mui_sokol_audio.c
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "mii.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
#define SOKOL
|
||||
|
||||
#ifdef SOKOL
|
||||
#define SOKOL_IMPL
|
||||
#include "sokol_audio.h"
|
||||
#endif
|
||||
#ifdef MINIAUDIO
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#define MA_NO_WAV
|
||||
#define MA_NO_MP3
|
||||
#define MA_NO_FLAC
|
||||
#define MA_ENABLE_ONLY_SPECIFIC_BACKENDS
|
||||
//#define MA_ENABLE_PULSEAUDIO
|
||||
#define MA_ENABLE_ALSA
|
||||
#include "miniaudio.h"
|
||||
#endif
|
||||
|
||||
static void
|
||||
mii_sokol_audio_stop(
|
||||
mii_audio_sink_t *sink)
|
||||
{
|
||||
#ifdef SOKOL
|
||||
saudio_shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
mii_sokol_audio_write(
|
||||
mii_audio_sink_t *sink,
|
||||
mii_audio_source_t *source)
|
||||
{
|
||||
// mii_audio_buffer_t *fifo = &source->fifo;
|
||||
// mii_audio_frame_t *frame = mii_audio_buffer_write_ptr(fifo);
|
||||
}
|
||||
|
||||
/* This is very pedestrian, but it helps with the compiler explorer to
|
||||
* see what gets vectorized in here -- as it turns out, -O3 seems to do
|
||||
* a slightly better job at it than trying to use the generic gcc vectoring
|
||||
* extensions. So I keep the scalar code instead */
|
||||
unsigned int
|
||||
_mix_sample_buffer(
|
||||
const float volume,
|
||||
unsigned int num_frames,
|
||||
const float src[num_frames],
|
||||
float dst[num_frames])
|
||||
{
|
||||
#define fmin -1.0f
|
||||
#define fmax 1.0f
|
||||
while (num_frames) {
|
||||
float s = *src;
|
||||
float d = *dst;
|
||||
float m = s * volume;
|
||||
float a = d + m;
|
||||
d = a > fmax ? fmax : a < fmin ? fmin : a;
|
||||
*dst = d;
|
||||
dst++;
|
||||
src++;
|
||||
num_frames--;
|
||||
}
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
static void __attribute__((unused))
|
||||
_sokol_stream_cb(
|
||||
float * buffer,
|
||||
int num_frames,
|
||||
int num_channels,
|
||||
void *user_data)
|
||||
{
|
||||
mii_audio_sink_t *sink = user_data;
|
||||
|
||||
mii_audio_run(sink);
|
||||
|
||||
mii_audio_source_t *s;
|
||||
// audio buffer is not zeroed.
|
||||
const uint num_samples = num_frames * num_channels;
|
||||
memset(buffer, 0, num_samples * sizeof(float));
|
||||
uint count = 0;
|
||||
float inter[num_samples];
|
||||
SLIST_FOREACH(s, &sink->source, self) {
|
||||
mii_audio_frame_t *f = &s->fifo;
|
||||
uint avail = mii_audio_frame_get_read_size(f);
|
||||
if (avail > num_samples)
|
||||
avail = num_samples;
|
||||
uint dst;
|
||||
if (sink->muted) { // just advance read pointer
|
||||
mii_audio_frame_read_offset(f, avail);
|
||||
} else {
|
||||
/*
|
||||
* Wait for a full buffer of audio available before we start
|
||||
* taking it, otherwise we'd be padding the end of the frame
|
||||
* with zeroes, creating horrible click.
|
||||
* Once the engine is started, we're OK to take whatever is
|
||||
* available, as we'll be padding the end of the frame with
|
||||
* the last sample we read, which is a lot less noticeable.
|
||||
*/
|
||||
if ((s->last_read == 0 && avail >= num_samples) ||
|
||||
((s->last_read > 0 && avail > 0))) {
|
||||
dst = mii_audio_frame_read_count(f, avail, inter);
|
||||
count += dst;
|
||||
_mix_sample_buffer(s->vol_multiplier, dst, inter, buffer);
|
||||
s->last_read = dst;
|
||||
} else
|
||||
s->last_read = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef MINIAUDIO
|
||||
static void
|
||||
_ma_stream_cb(
|
||||
ma_device* pDevice,
|
||||
void* pOutput,
|
||||
const void* pInput,
|
||||
ma_uint32 frameCount)
|
||||
{
|
||||
_sokol_stream_cb(pOutput, frameCount, 1, pDevice->pUserData);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* these make sure the audio buffer is aligned to 32 bytes
|
||||
*/
|
||||
static void *
|
||||
_audio_aligned_alloc(
|
||||
size_t size,
|
||||
void* user_data)
|
||||
{
|
||||
void *ptr = NULL;
|
||||
posix_memalign(&ptr, 32, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void
|
||||
_audio_free(
|
||||
void *ptr,
|
||||
void *user_data)
|
||||
{
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void
|
||||
mii_sokol_audio_start(
|
||||
mii_audio_sink_t *sink)
|
||||
{
|
||||
printf("mii_sokol_audio_start\n");
|
||||
#ifdef SOKOL
|
||||
saudio_setup(&(saudio_desc){
|
||||
.stream_userdata_cb = _sokol_stream_cb,
|
||||
.user_data = sink,
|
||||
.num_channels = 1,
|
||||
.sample_rate = 44100,
|
||||
.buffer_frames = 1024,
|
||||
// .logger.func = slog_func,
|
||||
.allocator.alloc_fn = _audio_aligned_alloc,
|
||||
.allocator.free_fn = _audio_free,
|
||||
});
|
||||
#endif
|
||||
#ifdef MINIAUDIO
|
||||
ma_device_config config = ma_device_config_init(ma_device_type_playback);
|
||||
config.playback.format = ma_format_f32;
|
||||
config.playback.channels = 1;
|
||||
config.sampleRate = 44100;
|
||||
config.dataCallback = _ma_stream_cb;
|
||||
config.pUserData = sink;
|
||||
ma_device device;
|
||||
if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
|
||||
printf("%s: miniaudio failed to open playback device.\n", __func__);
|
||||
return;
|
||||
}
|
||||
if (ma_device_start(&device) != MA_SUCCESS) {
|
||||
printf("%s: miniaudio failed to start playback device.\n", __func__);
|
||||
ma_device_uninit(&device);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static const struct mii_audio_driver_t mii_audio_driver = {
|
||||
.start = mii_sokol_audio_start,
|
||||
.stop = mii_sokol_audio_stop,
|
||||
.write = mii_sokol_audio_write,
|
||||
};
|
||||
|
||||
void
|
||||
mii_sokol_audio_init(
|
||||
mii_t *mii)
|
||||
{
|
||||
mii_audio_sink_t *sink = &mii->audio;
|
||||
mii_audio_set_driver(sink, &mii_audio_driver);
|
||||
}
|
14
ui_gl/mii_sokol_audio.h
Normal file
14
ui_gl/mii_sokol_audio.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* mui_sokol_audio.h
|
||||
*
|
||||
* Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "mii.h"
|
||||
|
||||
void
|
||||
mii_sokol_audio_init(
|
||||
mii_t *mii);
|
@@ -31,7 +31,7 @@ END {
|
||||
{
|
||||
acc = acc $0
|
||||
acc = gensub(/([^\\])"/, "\\1\\\\\"", "g", acc);
|
||||
acc = gensub(/ +/, " ", "g", acc);
|
||||
acc = gensub(/[ \t]+/, " ", "g", acc);
|
||||
cnt = split(acc, e, / *\|\| *| *&& */);
|
||||
acc = "";
|
||||
for (cmd in e) {
|
||||
|
Reference in New Issue
Block a user