Compare commits

...

63 Commits

Author SHA1 Message Date
Peter Rutenbar
a9b2af09c0
Update README.md
Added a note of appreciation for issues/PRs
2023-03-29 10:49:13 -07:00
Peter Rutenbar
2b4768daee Update README.md 2017-04-17 11:52:13 -07:00
Peter Rutenbar
3fd03263fa Merge pull request #12 from mietek/master
Fix linking with Xcode 7
2016-09-04 02:02:17 -07:00
Miëtek Bak
578d274ab0 Fix linking with Xcode 7
Closes #11.
2015-11-28 22:03:13 +00:00
Miëtek Bak
bbb89edf8c Update .gitignore 2015-11-28 22:03:12 +00:00
Peter Rutenbar
70cf3734fb Update README.md 2015-09-13 20:16:46 -07:00
Peter Rutenbar
d26cbef0c1 Closing out some final simple changes 2015-09-13 19:11:18 -07:00
Peter Rutenbar
0322e558b4 Preliminary support for ethernet/multiple screens in cocoa client 2015-09-13 00:39:58 -07:00
Peter Rutenbar
1ab8a6a988 Fixed bugs in addx, subx, and cmpm
(cmpm was setting the CC codes based on a garbage source operand, so fixing that might fix a lot of other problems)
2015-09-10 16:27:00 -04:00
Peter Rutenbar
7f8b393fcc Fixed that dumb bsun bug 2015-09-04 12:01:01 -04:00
Peter Rutenbar
c0439a2f57 Fixed some integer division bugs, and other misc bugs 2015-09-01 16:04:30 -04:00
Peter Rutenbar
2ffbc963d2 Implemented chk2/cmp2, sbcd, and nbcd 2015-08-31 17:33:52 -04:00
Peter Rutenbar
36fdd1a6d6 Fixed/implemented roxx_mem, rox_mem, abcd, pack, unpk, cas2
Trying to finish up the last few unimplemented 68020 insts
2015-08-31 01:57:37 -04:00
Peter Rutenbar
3bc6aed00a Some preliminary work on ASC (Apple Sound Chip) emulation 2015-08-24 22:41:52 -04:00
Peter Rutenbar
b936ace3eb Update README.md 2015-04-09 22:22:51 -04:00
Peter Rutenbar
a66ad6d9b4 A/UX 3 should boot reliably now
- Added a hack to bypass SetUpTimeK, which works on 3.0.0,
  not sure about other versions.
2015-01-29 23:20:23 -05:00
Peter Rutenbar
476a8bb570 Major speed improvements (25-50%)
There may be bugs lurking in it. On my Core i7 macbook pro,
shoebill now runs so fast that SetUpTimeK() on A/UX 3 hangs.
(SetUpTimeK tries to time a dbra loop, and refuses to accept any
speed faster than a certain threshold, which shoebill is now
surpassing. If you see A/UX hanging early in boot, it's probably
that.)

- Added a new specialized cache for instruction stream reads
-- This also lets us distinguish between data and instruction
   reads, which the 68020 does. Instruction reads are now done
   with the correct function code (2 or 6), although that
   doesn't currently fix or improve anything currently
- Added an obvious condition code optimization, dunno how I missed
  it earlier
- Other little changes
2015-01-29 00:19:57 -05:00
Peter Rutenbar
76f2b35170 Unbroke shoebill, speed improvements
- Fixed inst_ext, which was completely broken since
  last commit, whoops!
- Added a bunch of __builtin_expect()s in all the
  critical paths, and now Marathon runs quite noticeably
  faster.
- Fixed (I think) all the SDL makefiles
2015-01-26 22:26:30 -05:00
Peter Rutenbar
9c3640cf48 fcmp and various other changes
- Read the 68881 docs for fcmp more carefully, and tweaked it to more
  accurately implement the (documented) behavior
- Implemented de-un-normalizing for all x80 softfloat ops
- Some other changes I don't remember - it's my emulator, I can be vague
  if I want XD
2014-12-10 22:22:45 -05:00
Peter Rutenbar
aea23c6dfc Implemented ftentox and fixed fscale again
- (Badly) worked around a clang bug to implement ftentox
- (Very badly) fixed another edge case in fscale
- Found another bug in softfloat (floatx80_to_int32)
-- Not so sure anymore that my floatx80/subnormal math fix works right.
   Just figured out that x80's exp is relative to the second mantissa
   bit. float128{exp=0x3fff, man=0} == floatx80{exp=0x3fff, man=0x800...}
   even though x80 has an explicit bit, the exponents are the same...
2014-11-24 01:31:53 -05:00
Peter Rutenbar
9e91b8067a Fixed a million SoftFloat bugs, and an edge case in fscale
SoftFloat's support for subnormal numbers is completely broken
on floatx80. Particularly when the result of an op on a subnormal
number is normal, and vice versa. I'm not completely sure that
my fixes are correct, or that they didn't break anything. I need
to do more testing.

Also fixed an edge case in fscale that gets weird when the
input goes from normal to subnormal... oh, and I still need to
fix the opposite case.
2014-11-23 19:41:06 -05:00
Peter Rutenbar
8142098fa1 Implemented fgetman,fscale,fsincos, and partial-fixed a softfloat bug
- fgetman, fscale, and fsincos are implemented but not tested at all

- fixed part of a softbug float when converting subnormal numbers between
  floatx80 and float128

- also, I checked in the wrong core/Makefile with the last commit.
  Re-committing the correct Makefile
2014-11-22 16:47:49 -05:00
Peter Rutenbar
f04e039dca Promoting newfpu to be the current fpu (aka fpu.c)
Just merged the newfpu branch to master, almost everything
is implemented, but there are clearly lots of problems still.

(According to xcalc, sin(3.1415927) == 0.054803665, which is
pretty far beyond the acceptable error margin.)
2014-11-10 20:40:54 -05:00
Peter Rutenbar
c8e7ba594c Implemented fatanh,cosh,etox,etoxm1,lognp1,tanh, and twotox
but not ftentox, because this hits a clang bug. Clang 3.5
(and earlier?) wants to optimize pow(10.0, x) to
__exp10(x), but __exp10() doesn't exist in the 10.8 SDK,
so the linker fails. There's probably some simple workaround...
2014-11-09 15:31:48 -05:00
Peter Rutenbar
7fa1e15257 Implemented flog2, flog10, and flogn, fixed fneg
xeyes works now
2014-11-09 13:56:16 -05:00
Peter Rutenbar
c7a28b8520 Implemented fsin,fcos,ftan,fatan,fasin,facos
Not sure if they work exactly right yet - xeyes is messed up,
but xclock seems to work.
2014-11-09 13:16:34 -05:00
Peter Rutenbar
0910915301 Finished the native-half of the transcendental impls
... for x86. Remains to be seen whether this will *really*
work on PowerPC or other architectures.
2014-11-09 12:25:39 -05:00
Peter Rutenbar
937a28a8bb Implemented frem, fmod, fsglmul/div, and fixed fabs
- And also fixed an assert
- New frem impl is slower, but easier to get the quotient bits.
2014-11-09 10:56:58 -05:00
Peter Rutenbar
db111528e8 Marathon works!
Fixed a bunch of dumb bugs, and now newfpu is working almost as well as old-fpu.
Presumably it's also running much more slowly, but I'm not noticing a speed difference.
2014-11-07 19:55:13 -05:00
Peter Rutenbar
df66c085da fmovecr now actually works
a cursory boot of 3.0.0 didn't reveal any asserts, except for x11. And I'm sure Marathon will die, and Mathematica, etc. Calculator works though. IE 2 hangs, and finder shutdown crashes.

... getting there
2014-11-06 01:12:28 -05:00
Peter Rutenbar
18c5a2b261 Newfpu works enough to "boot" A/UX
... but that's not saying much
2014-11-06 00:13:31 -05:00
Peter Rutenbar
2d04c6c104 Implemented fmovem_control()
I'm on a roll
2014-11-05 21:40:07 -05:00
Peter Rutenbar
2790b03327 Added a bunch of disassemblers for newfpu 2014-11-05 18:31:33 -05:00
Peter Rutenbar
688199cf6d Dumping a lot of new FPU-related changes in a new branch
A/UX doesn't even come close to booting yet with the new FPU,
but we're getting there. I don't want to trash master with
a code base that doesn't work. Also, I turned off ethernet
in the cocoa GUI, since the TAP driver isn't working right
on 10.10 yet.
2014-11-05 13:38:51 -05:00
pruten
a155eb7734 Merge branch 'master' of https://github.com/pruten/Shoebill 2014-10-16 15:55:19 -04:00
pruten
7c96e7c101 Began implementing a few more newfpu instructions
Though Motorola's documentation is surprisingly buggy
and imprecise.
2014-10-16 15:53:32 -04:00
Peter Rutenbar
16f1af72f8 Update README.md 2014-10-15 18:11:54 -04:00
pruten
78fbd8235f Refactored a big chunk of newfpu
- the framework for throwing exceptions should work correctly now
- NaNs and signaling NaNs should work
- condition codes are set now
- write-back and rounding (including throwing INEX2) are now implemented

(Note: you can't actually use any of this code yet, and it doesn't actually
       fully implement any fmath_* instructions yet)
2014-10-13 23:21:06 -04:00
pruten
c9adc49a82 More minor tweaks to newfpu 2014-10-07 22:37:22 -04:00
pruten
adc2e16ffd Implemented CAS
It compiles, but I haven't even tested it once. *fingers crossed*
2014-09-28 19:40:16 -04:00
pruten
bb6d1e719f More half-baked newfpu goodness
Once the framework for handling exceptions, accrued exceptions,
condition codes, and rounding is done, I can start implementing
actual "fmath" instructions. Everything not handled by softfloat
will be imprecisely and hackily implemented by using the best
available native float math (e.g. cosf(), cos(), or cosl())
2014-09-28 16:51:08 -04:00
pruten
bb2ec0a27d Working on a total FPU rewrite based on softfloat
5 instructions down, a million more to go
2014-09-27 13:46:57 -04:00
pruten
a0810f55b9 Added a hook for shutdown
To which the Macintosh II usually responds by powering off
2014-09-22 17:02:17 -04:00
pruten
b29c69453e Mostly-working ethernet and various changes
This may break the linux/windows builds - I haven't tried to compile
on those platforms yet.

- Ethernet is more or less working with a hardcoded /dev/tun0 network interface
and hardcoded MAC address, though there's no automatic ifconfig/route
configuration yet.

- Instructions TAS and ILLEGAL are implemented now

- Fixed some bugs in MOVEP

- Implemented some other instruction disassemblers

- Other little changes
2014-09-11 03:40:20 -04:00
pruten
4af4262993 Implemented movep 2014-08-30 05:10:54 -04:00
name
fd31d642b0 Lots of new ethernet-related code (doesn't work yet)
The board is basically implemented. It's just a dummy slot ROM
with no driver or primary/secondary init, that looks enough
like the Apple EtherTalk card. The DP8390 controller chip
has its address space implemented, and some of its registers
work. Lots more work to do on that

With slog() tracing enabled, you can see A/UX try to send a
multicast ethernet frame, then give up waiting for some kind
of response from the chip, then decide that the ethernet
controller is dead and print an error to console
2014-08-27 19:37:26 -04:00
Peter Rutenbar
20fedf386b Improved SCSI a bit.
Apple HD SC Setup still doesn't work, but it won't crash anymore.
Non-existent SCSI devices can no longer be selected
2014-07-28 01:09:16 -04:00
Peter Rutenbar
ea23ef3ac6 Update README.md 2014-07-03 22:10:32 -04:00
Peter Rutenbar
df614785c9 Update README.md 2014-07-03 22:09:05 -04:00
Peter Rutenbar
4ac0c6c4b2 Update README.md 2014-06-30 19:06:33 -04:00
Peter Rutenbar
6773dd6020 Tweaks to SDL GUI, video driver, and FPU
- Fewer FPU-related crashes
- Fewer video driver-related crashes
- Cleaned up the SDL GUI
- Bumped up cocoa GUI to v0.0.4
2014-06-30 16:50:38 -04:00
Peter Rutenbar
305d84faad Very dumb bug fixed, 3.0.1 boots now!
For READ(6) and WRITE(6), SCSI spec says,
"A TRANSFER LENGTH field set to zero specifies that 256 logical blocks shall be written"
Oops
2014-06-23 10:55:41 -04:00
Peter Rutenbar
f051d42597 Resuscitated the toby frame buffer
- Integrated it into the SDL GUI (via the "toby" cli parameter)
- It does not include the card ROM, so it will not run on A/UX 1, 2, or 3
2014-06-15 16:41:06 -04:00
Peter Rutenbar
42fd2a0b4e Fixed some dumbness in that last commit
- #ifdef __LITTLE_ENDIAN__ is not how you check for little endianness
- Also fixed the last pointer-to-int warnings on GCC

Also, shout out to  Neozeed for figuring out how to compile this on windows!
2014-06-14 01:25:37 -04:00
Peter Rutenbar
1bee24316c Now more Windows-friendly
- cpu_thread now stops and waits on a pthread condition variable, rather
  than sleep(1)ing and waiting to be pthread_kill()'d. Signals don't
  work well on Windows, apparently.
- fopen() now open binary files with the "b" mode
- the keymap red-black tree no longer casts pointers to ints, because
  mingw/gcc complains about it
- added a dumb batch script to compile the sdl gui on windows
- {n,h}to{h,n}{s,l,ll} is now handled better on windows
2014-06-14 00:19:08 -04:00
Peter Rutenbar
952fe7ae89 Linux SDL GUI + bug fixes + printf->slog
- sdl-gui/sdl.c is a tiny, basically working SDL2-based GUI that can compile and run on linux.
  It still needs PRAM integration and a config file.

- Fixed a dumb bug in shoebill_initialize that was causing sporadic crashes

- Replaced a million printf() calls with slog(), to make the SDL client run more quietly on the CLI

- Added more corruption checking to alloc_pool
2014-06-09 16:21:12 -04:00
Peter Rutenbar
5c1fdf6b73 VIA Timers + 16/32-bit video support
- VIA timers should probably work now, although it turns out A/UX rarely uses them
  (only during startup to time a dbra loop and other stuff)

- Updated video.c and the fake nubus video card driver to support "thousands"
  and "millions" of colors when 32-bit QuickDraw is available
2014-06-02 00:59:08 -04:00
Peter Rutenbar
a16a6700e4 Update README.md 2014-05-24 16:51:01 -04:00
Peter Rutenbar
d5dd7385b1 Minor twiddling before releasing 0.0.3 2014-05-24 16:35:36 -04:00
Peter Rutenbar
f4f546deb5 Restart + PRAM integrated into GUI + misc
Restart/shutdown now work (most of the time)
PRAM is now integrated into the GUI
The real time clock sorta works, but is a bit wonky
Full-screen support
Lots of other little bug fixes
2014-05-24 13:39:39 -04:00
Peter Rutenbar
d19c17812c New debugger + inital PRAM/real-time clock + fixes
- Refactored VIA rega/b implementation
  - Timers don't "work" yet, but they work a little better
  - Split rega/b into input and output versions
  - Fixed a bug that would mistake VIA1 accesses for VIA2

- Added basic support for PRAM and the RTC, although they're not
  wired into the GUI yet

- Replaced every *alloc() call with an alloc_pool call, which is
  a start toward supporting clean restarts

- Replaced ea_addr, ea_read, ea_write, and ea_read_commit with
  jump tables

- Got rid of ~inst() macro in cpu.c

- added a GLUT-based debugger
2014-05-10 19:25:31 -04:00
Peter Rutenbar
2800cc3c7e Twiddling README.md some more 2014-05-10 19:01:13 -04:00
Peter Rutenbar
1ffea71ed3 Twiddling the README.md 2014-05-10 18:55:12 -04:00
80 changed files with 20511 additions and 5755 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@
*.xcworkspace
xcuserdata
/gui/build
/debugger/debugger
/debugger/debugger.dSYM

View File

@ -1,11 +1,17 @@
CC = clang
CFLAGS = -O3 -flto -Wno-deprecated-declarations
all: shoebill
shoebill: make_gui
shoebill: make_gui debugger
make_gui: make_core
xcodebuild -project gui/Shoebill.xcodeproj SYMROOT=build
debugger: make_core
$(MAKE) -C debugger
make_core:
$(MAKE) -C core -j 4

View File

@ -1,39 +1,46 @@
Shoebill
========
<h1><img align=right src="../../../pruten.github.io/raw/master/web/stork_tiny_head3.jpg"/>Shoebill</h1>
A Macintosh II emulator that runs A/UX (and A/UX only).
Shoebill is an all-new, BSD-licensed Macintosh II emulator designed from the ground up with the singular goal of running A/UX.
Shoebill requires a OS X, a Macintosh II, IIx or IIcx ROM, and a disk image with A/UX installed.
Shoebill requires a Macintosh II, IIx or IIcx ROM, and a disk image with A/UX installed.
[Download the latest release], and then see the [getting started] wiki.
Also check out [screenshots].
__Update (April 26, 2014): Shoebill 0.0.2 is available, and it supports A/UX 3.0.0! And you no longer need to supply your own kernel.__
__Update (March 29, 2023): About issues/pull requests__
__I just wanted to say that I appreciate some folks are still using Shoebill and submitting issues and pull requests. I wish I could continue working on this project, but there's a likely conflict of interest, and so I've mostly avoided pushing changes. I apologize for being unable to address the many, many bugs in this repo. (Also for anyone unaware, [Qemu is now able] now to run A/UX 3.x on its emulated Quadra 800.)__
__Update (Sept 13, 2015): [Shoebill 0.0.5 is available]__
__This will probably be the last release. I won't be able to work on Shoebill going forward (by contractual obligation), so I wanted to race out one last release. Only an OS X binary is available, sorry, and it's very unpolished. But the SDL GUI should still build on linux/windows.__
####Supports
* A/UX 1.1.1 through 3.0.0 (but not 3.0.1 or higher, yet)
#### Supports
* A/UX 1.1.1 through 3.1 (and 3.1.1 a little)
####Currently Implements
#### Currently Implements
* 68020 CPU (mostly)
* 68881 FPU (a little)
* 68881 FPU (mostly)
* 68851 PMMU (just enough to boot A/UX)
* SCSI
* ADB
* A NuBus video card with 8-bit resolution
* PRAM
* Ethernet (via emulated Apple EtherTalk/DP8390 card)
* A NuBus video card with 24-bit depth.
#### Does not implement (yet)
* Sound
* Floppy
* Serial ports
* Ethernet
* PRAM / VIA timers
* Support for multiple screens, or 16/24-bit resolutions
[Download the latest release]:https://github.com/pruten/Shoebill/releases
[getting started]:https://github.com/pruten/Shoebill/wiki/Getting-Started
[screenshots]:https://github.com/pruten/Shoebill/wiki/Screenshots
[Shoebill 0.0.5 is available]:https://github.com/pruten/Shoebill/releases
[The thread on emaculation.com]:http://www.emaculation.com/forum/viewtopic.php?f=7&t=8288
[Qemu is now able]:https://virtuallyfun.com/2021/09/02/qemus-macintosh-quadra-in-alpha-usability-runs-a-ux/

View File

@ -1,55 +0,0 @@
Shoebill - a Macintosh II emulator that runs A/UX
See the wiki on https://github.com/pruten/shoebill for better
documentation on building and running Shoebill.
*** KEEP IN MIND ***
* Shoebill v.0.0.2
* ONLY RUNS A/UX
* Shoebill has broken, ultra-minimalist support for
* 68020 (CPU) + 68851 (MMU) + 68881 (FPU)
* Some instructions for 020 and most for the MMU and FPU are unimplemented
* SCSI, ADB, and VIA
* Shoebill does not support
* The floppy controller, IWM/SWIM
* Serial
* Ethernet
* PRAM
* Most other things
*** RUNNING ***
You will need
* OS X 10.8 or 10.9
* A Macintosh II, IIx, or IIcx ROM
* A disk image with A/UX 1.x.x or 2.x.x, or 3.0.0 installed
* Note: 3.0.1 and 3.1.x do not work!
* If you happen to have an installation CD image for A/UX, that will work
To boot A/UX
* Backup your disk images!!
When Shoebill inevitably crashes, your A/UX boot image
will very likely be corrupted - sometimes so severely
that A/UX cant even boot enough to run fsck.
* Open Shoebill.app and select Preferences menu item
* Set the paths for your ROM and disk image(s).
* Do use SCSI ID #0 for your A/UX boot image.
* Press “Apply and Run”
* Note: As of 0.0.2, you no longer need to provide your own kernel file
*** BUILDING ***
1) cd to shoebill/
2) make
3) The resulting app will be in gui/build
*** ETC. ***
Props to Jared Falter for technical and emotional support!

View File

@ -1,13 +1,13 @@
CC = clang
CFLAGS = -O3 -flto -ggdb -Wno-deprecated-declarations
CFLAGS = -O3 -ggdb -flto -Wno-deprecated-declarations
# CFLAGS = -O0 -ggdb -Wno-deprecated-declarations
DEPS = core_api.h coff.h mc68851.h redblack.h shoebill.h Makefile macro.pl
DEPS = mc68851.h shoebill.h Makefile macro.pl
NEED_DECODER = cpu dis
NEED_PREPROCESSING = adb fpu mc68851 mem via
NEED_NOTHING = atrap_tab coff exception floppy macii_symbols redblack scsi toby_frame_buffer video core_api filesystem debug_server alloc_pool
NEED_PREPROCESSING = adb mc68851 mem via floppy core_api fpu
NEED_NOTHING = atrap_tab coff exception macii_symbols redblack scsi video filesystem alloc_pool toby_frame_buffer sound ethernet SoftFloat/softfloat
# Object files that can be compiled directly from the source
OBJ_NEED_NOTHING = $(patsubst %,$(TEMP)/%.o,$(NEED_NOTHING))
@ -73,11 +73,12 @@ $(TEMP)/dis_decoder_guts.c: $(TEMP)/decoder_gen $(DEPS)
# Compile the decoder generator
$(TEMP)/decoder_gen: decoder_gen.c $(DEPS)
$(CC) $(CFLAGS) decoder_gen.c -o $(TEMP)/decoder_gen
$(CC) decoder_gen.c -o $(TEMP)/decoder_gen
$(TEMP):
mkdir -p $(TEMP)
mkdir -p $(TEMP)/SoftFloat
clean:
rm -rf $(TEMP)

68
core/SoftFloat/386-GCC.h Normal file
View File

@ -0,0 +1,68 @@
/*----------------------------------------------------------------------------
| One of the macros `BIGENDIAN' or `LITTLEENDIAN' must be defined.
*----------------------------------------------------------------------------*/
#define LITTLEENDIAN
/*----------------------------------------------------------------------------
| The macro `BITS64' can be defined to indicate that 64-bit integer types are
| supported by the compiler.
*----------------------------------------------------------------------------*/
#define BITS64
/*----------------------------------------------------------------------------
| Each of the following `typedef's defines the most convenient type that holds
| integers of at least as many bits as specified. For example, `uint8' should
| be the most convenient type that can hold unsigned integers of as many as
| 8 bits. The `flag' type must be able to hold either a 0 or 1. For most
| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
| to the same as `int'.
*----------------------------------------------------------------------------*/
typedef char flag;
typedef unsigned char uint8;
typedef signed char int8;
typedef int uint16;
typedef int int16;
typedef unsigned int uint32;
typedef signed int int32;
#ifdef BITS64
typedef unsigned long long int uint64;
typedef signed long long int int64;
#endif
/*----------------------------------------------------------------------------
| Each of the following `typedef's defines a type that holds integers
| of _exactly_ the number of bits specified. For instance, for most
| implementation of C, `bits16' and `sbits16' should be `typedef'ed to
| `unsigned short int' and `signed short int' (or `short int'), respectively.
*----------------------------------------------------------------------------*/
typedef unsigned char bits8;
typedef signed char sbits8;
typedef unsigned short int bits16;
typedef signed short int sbits16;
typedef unsigned int bits32;
typedef signed int sbits32;
#ifdef BITS64
typedef unsigned long long int bits64;
typedef signed long long int sbits64;
#endif
#ifdef BITS64
/*----------------------------------------------------------------------------
| The `LIT64' macro takes as its argument a textual integer literal and
| if necessary ``marks'' the literal as having a 64-bit integer type.
| For example, the GNU C Compiler (`gcc') requires that 64-bit literals be
| appended with the letters `LL' standing for `long long', which is `gcc's
| name for the 64-bit integer type. Some compilers may allow `LIT64' to be
| defined as the identity macro: `#define LIT64( a ) a'.
*----------------------------------------------------------------------------*/
#define LIT64( a ) a##LL
#endif
/*----------------------------------------------------------------------------
| The macro `INLINE' can be used before functions that should be inlined. If
| a compiler does not support explicit inlining, this macro should be defined
| to be `static'.
*----------------------------------------------------------------------------*/
#define INLINE static

View File

@ -0,0 +1,728 @@
/*
* SoftFloat with lots of fixes and modified for use by Shoebill.
*
* Based on SoftFloat 2b by John R. Hauser.
* Modifications by Peter Rutenbar. (pruten@gmail.com)
*/
/*============================================================================
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2b.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
arithmetic/SoftFloat.html'.
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) the source code for the derivative work includes prominent notice that
the work is derivative, and (2) the source code includes prominent notice with
these four paragraphs for those parts of this code that are retained.
=============================================================================*/
/*----------------------------------------------------------------------------
| Shifts `a' right by the number of bits given in `count'. If any nonzero
| bits are shifted off, they are ``jammed'' into the least significant bit of
| the result by setting the least significant bit to 1. The value of `count'
| can be arbitrarily large; in particular, if `count' is greater than 32, the
| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
| The result is stored in the location pointed to by `zPtr'.
*----------------------------------------------------------------------------*/
INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
{
bits32 z;
if ( count == 0 ) {
z = a;
}
else if ( count < 32 ) {
z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
}
else {
z = ( a != 0 );
}
*zPtr = z;
}
/*----------------------------------------------------------------------------
| Shifts `a' right by the number of bits given in `count'. If any nonzero
| bits are shifted off, they are ``jammed'' into the least significant bit of
| the result by setting the least significant bit to 1. The value of `count'
| can be arbitrarily large; in particular, if `count' is greater than 64, the
| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
| The result is stored in the location pointed to by `zPtr'.
*----------------------------------------------------------------------------*/
INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
{
bits64 z;
if ( count == 0 ) {
z = a;
}
else if ( count < 64 ) {
z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
}
else {
z = ( a != 0 );
}
*zPtr = z;
}
/*----------------------------------------------------------------------------
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
| _plus_ the number of bits given in `count'. The shifted result is at most
| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'. The
| bits shifted off form a second 64-bit result as follows: The _last_ bit
| shifted off is the most-significant bit of the extra result, and the other
| 63 bits of the extra result are all zero if and only if _all_but_the_last_
| bits shifted off were all zero. This extra result is stored in the location
| pointed to by `z1Ptr'. The value of `count' can be arbitrarily large.
| (This routine makes more sense if `a0' and `a1' are considered to form
| a fixed-point value with binary point between `a0' and `a1'. This fixed-
| point value is shifted right by the number of bits given in `count', and
| the integer part of the result is returned at the location pointed to by
| `z0Ptr'. The fractional part of the result may be slightly corrupted as
| described above, and is returned at the location pointed to by `z1Ptr'.)
*----------------------------------------------------------------------------*/
INLINE void
shift64ExtraRightJamming(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z0, z1;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z1 = a1;
z0 = a0;
}
else if ( count < 64 ) {
z1 = ( a0<<negCount ) | ( a1 != 0 );
z0 = a0>>count;
}
else {
if ( count == 64 ) {
z1 = a0 | ( a1 != 0 );
}
else {
z1 = ( ( a0 | a1 ) != 0 );
}
z0 = 0;
}
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
| number of bits given in `count'. Any bits shifted off are lost. The value
| of `count' can be arbitrarily large; in particular, if `count' is greater
| than 128, the result will be 0. The result is broken into two 64-bit pieces
| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
shift128Right(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z0, z1;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z1 = a1;
z0 = a0;
}
else if ( count < 64 ) {
z1 = ( a0<<negCount ) | ( a1>>count );
z0 = a0>>count;
}
else {
// [shoebill] This is a bug, right? ( count < 64 ) can never be true
// z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
z1 = ( count < 128 ) ? ( a0>>( count & 63 ) ) : 0;
z0 = 0;
}
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
| number of bits given in `count'. If any nonzero bits are shifted off, they
| are ``jammed'' into the least significant bit of the result by setting the
| least significant bit to 1. The value of `count' can be arbitrarily large;
| in particular, if `count' is greater than 128, the result will be either
| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or
| nonzero. The result is broken into two 64-bit pieces which are stored at
| the locations pointed to by `z0Ptr' and `z1Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
shift128RightJamming(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z0, z1;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z1 = a1;
z0 = a0;
}
else if ( count < 64 ) {
z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
z0 = a0>>count;
}
else {
if ( count == 64 ) {
z1 = a0 | ( a1 != 0 );
}
else if ( count < 128 ) {
z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
}
else {
z1 = ( ( a0 | a1 ) != 0 );
}
z0 = 0;
}
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
| by 64 _plus_ the number of bits given in `count'. The shifted result is
| at most 128 nonzero bits; these are broken into two 64-bit pieces which are
| stored at the locations pointed to by `z0Ptr' and `z1Ptr'. The bits shifted
| off form a third 64-bit result as follows: The _last_ bit shifted off is
| the most-significant bit of the extra result, and the other 63 bits of the
| extra result are all zero if and only if _all_but_the_last_ bits shifted off
| were all zero. This extra result is stored in the location pointed to by
| `z2Ptr'. The value of `count' can be arbitrarily large.
| (This routine makes more sense if `a0', `a1', and `a2' are considered
| to form a fixed-point value with binary point between `a1' and `a2'. This
| fixed-point value is shifted right by the number of bits given in `count',
| and the integer part of the result is returned at the locations pointed to
| by `z0Ptr' and `z1Ptr'. The fractional part of the result may be slightly
| corrupted as described above, and is returned at the location pointed to by
| `z2Ptr'.)
*----------------------------------------------------------------------------*/
INLINE void
shift128ExtraRightJamming(
bits64 a0,
bits64 a1,
bits64 a2,
int16 count,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 negCount = ( - count ) & 63;
if ( count == 0 ) {
z2 = a2;
z1 = a1;
z0 = a0;
}
else {
if ( count < 64 ) {
z2 = a1<<negCount;
z1 = ( a0<<negCount ) | ( a1>>count );
z0 = a0>>count;
}
else {
if ( count == 64 ) {
z2 = a1;
z1 = a0;
}
else {
a2 |= a1;
if ( count < 128 ) {
z2 = a0<<negCount;
z1 = a0>>( count & 63 );
}
else {
z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
z1 = 0;
}
}
z0 = 0;
}
z2 |= ( a2 != 0 );
}
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
| number of bits given in `count'. Any bits shifted off are lost. The value
| of `count' must be less than 64. The result is broken into two 64-bit
| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
shortShift128Left(
bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
{
*z1Ptr = a1<<count;
*z0Ptr =
( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
}
/*----------------------------------------------------------------------------
| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
| by the number of bits given in `count'. Any bits shifted off are lost.
| The value of `count' must be less than 64. The result is broken into three
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
| `z1Ptr', and `z2Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
shortShift192Left(
bits64 a0,
bits64 a1,
bits64 a2,
int16 count,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 negCount;
z2 = a2<<count;
z1 = a1<<count;
z0 = a0<<count;
if ( 0 < count ) {
negCount = ( ( - count ) & 63 );
z1 |= a2>>negCount;
z0 |= a1>>negCount;
}
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
| value formed by concatenating `b0' and `b1'. Addition is modulo 2^128, so
| any carry out is lost. The result is broken into two 64-bit pieces which
| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
add128(
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits64 z1;
z1 = a1 + b1;
*z1Ptr = z1;
*z0Ptr = a0 + b0 + ( z1 < a1 );
}
/*----------------------------------------------------------------------------
| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
| 192-bit value formed by concatenating `b0', `b1', and `b2'. Addition is
| modulo 2^192, so any carry out is lost. The result is broken into three
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
| `z1Ptr', and `z2Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
add192(
bits64 a0,
bits64 a1,
bits64 a2,
bits64 b0,
bits64 b1,
bits64 b2,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 carry0, carry1;
z2 = a2 + b2;
carry1 = ( z2 < a2 );
z1 = a1 + b1;
carry0 = ( z1 < a1 );
z0 = a0 + b0;
z1 += carry1;
z0 += ( z1 < carry1 );
z0 += carry0;
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
| 128-bit value formed by concatenating `a0' and `a1'. Subtraction is modulo
| 2^128, so any borrow out (carry out) is lost. The result is broken into two
| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
| `z1Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
sub128(
bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
{
*z1Ptr = a1 - b1;
*z0Ptr = a0 - b0 - ( a1 < b1 );
}
/*----------------------------------------------------------------------------
| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
| from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
| Subtraction is modulo 2^192, so any borrow out (carry out) is lost. The
| result is broken into three 64-bit pieces which are stored at the locations
| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
sub192(
bits64 a0,
bits64 a1,
bits64 a2,
bits64 b0,
bits64 b1,
bits64 b2,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2;
int8 borrow0, borrow1;
z2 = a2 - b2;
borrow1 = ( a2 < b2 );
z1 = a1 - b1;
borrow0 = ( a1 < b1 );
z0 = a0 - b0;
z0 -= ( z1 < borrow1 );
z1 -= borrow1;
z0 -= borrow0;
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Multiplies `a' by `b' to obtain a 128-bit product. The product is broken
| into two 64-bit pieces which are stored at the locations pointed to by
| `z0Ptr' and `z1Ptr'.
*----------------------------------------------------------------------------*/
INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
{
bits32 aHigh, aLow, bHigh, bLow;
bits64 z0, zMiddleA, zMiddleB, z1;
aLow = a;
aHigh = a>>32;
bLow = b;
bHigh = b>>32;
z1 = ( (bits64) aLow ) * bLow;
zMiddleA = ( (bits64) aLow ) * bHigh;
zMiddleB = ( (bits64) aHigh ) * bLow;
z0 = ( (bits64) aHigh ) * bHigh;
zMiddleA += zMiddleB;
z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
zMiddleA <<= 32;
z1 += zMiddleA;
z0 += ( z1 < zMiddleA );
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by
| `b' to obtain a 192-bit product. The product is broken into three 64-bit
| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
| `z2Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
mul128By64To192(
bits64 a0,
bits64 a1,
bits64 b,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr
)
{
bits64 z0, z1, z2, more1;
mul64To128( a1, b, &z1, &z2 );
mul64To128( a0, b, &z0, &more1 );
add128( z0, more1, 0, z1, &z0, &z1 );
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
| product. The product is broken into four 64-bit pieces which are stored at
| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
*----------------------------------------------------------------------------*/
INLINE void
mul128To256(
bits64 a0,
bits64 a1,
bits64 b0,
bits64 b1,
bits64 *z0Ptr,
bits64 *z1Ptr,
bits64 *z2Ptr,
bits64 *z3Ptr
)
{
bits64 z0, z1, z2, z3;
bits64 more1, more2;
mul64To128( a1, b1, &z2, &z3 );
mul64To128( a1, b0, &z1, &more2 );
add128( z1, more2, 0, z2, &z1, &z2 );
mul64To128( a0, b0, &z0, &more1 );
add128( z0, more1, 0, z1, &z0, &z1 );
mul64To128( a0, b1, &more1, &more2 );
add128( more1, more2, 0, z2, &more1, &z2 );
add128( z0, z1, 0, more1, &z0, &z1 );
*z3Ptr = z3;
*z2Ptr = z2;
*z1Ptr = z1;
*z0Ptr = z0;
}
/*----------------------------------------------------------------------------
| Returns an approximation to the 64-bit integer quotient obtained by dividing
| `b' into the 128-bit value formed by concatenating `a0' and `a1'. The
| divisor `b' must be at least 2^63. If q is the exact quotient truncated
| toward zero, the approximation returned lies between q and q + 2 inclusive.
| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
| unsigned integer is returned.
*----------------------------------------------------------------------------*/
static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
{
bits64 b0, b1;
bits64 rem0, rem1, term0, term1;
bits64 z;
if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
b0 = b>>32;
z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
mul64To128( b, z, &term0, &term1 );
sub128( a0, a1, term0, term1, &rem0, &rem1 );
while ( ( (sbits64) rem0 ) < 0 ) {
z -= LIT64( 0x100000000 );
b1 = b<<32;
add128( rem0, rem1, b0, b1, &rem0, &rem1 );
}
rem0 = ( rem0<<32 ) | ( rem1>>32 );
z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
return z;
}
/*----------------------------------------------------------------------------
| Returns an approximation to the square root of the 32-bit significand given
| by `a'. Considered as an integer, `a' must be at least 2^31. If bit 0 of
| `aExp' (the least significant bit) is 1, the integer returned approximates
| 2^31*sqrt(`a'/2^31), where `a' is considered an integer. If bit 0 of `aExp'
| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30). In either
| case, the approximation returned lies strictly within +/-2 of the exact
| value.
*----------------------------------------------------------------------------*/
static bits32 estimateSqrt32( int16 aExp, bits32 a )
{
static const bits16 sqrtOddAdjustments[] = {
0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
};
static const bits16 sqrtEvenAdjustments[] = {
0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
};
int8 index;
bits32 z;
index = ( a>>27 ) & 15;
if ( aExp & 1 ) {
z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
z = ( ( a / z )<<14 ) + ( z<<15 );
a >>= 1;
}
else {
z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
z = a / z + z;
z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
}
return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
}
/*----------------------------------------------------------------------------
| Returns the number of leading 0 bits before the most-significant 1 bit of
| `a'. If `a' is zero, 32 is returned.
*----------------------------------------------------------------------------*/
static int8 countLeadingZeros32( bits32 a )
{
static const int8 countLeadingZerosHigh[] = {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int8 shiftCount;
shiftCount = 0;
if ( a < 0x10000 ) {
shiftCount += 16;
a <<= 16;
}
if ( a < 0x1000000 ) {
shiftCount += 8;
a <<= 8;
}
shiftCount += countLeadingZerosHigh[ a>>24 ];
return shiftCount;
}
/*----------------------------------------------------------------------------
| Returns the number of leading 0 bits before the most-significant 1 bit of
| `a'. If `a' is zero, 64 is returned.
*----------------------------------------------------------------------------*/
static int8 countLeadingZeros64( bits64 a )
{
int8 shiftCount;
shiftCount = 0;
if ( a < ( (bits64) 1 )<<32 ) {
shiftCount += 32;
}
else {
a >>= 32;
}
shiftCount += countLeadingZeros32( a );
return shiftCount;
}
/*----------------------------------------------------------------------------
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
| is equal to the 128-bit value formed by concatenating `b0' and `b1'.
| Otherwise, returns 0.
*----------------------------------------------------------------------------*/
INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 == b0 ) && ( a1 == b1 );
}
/*----------------------------------------------------------------------------
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
| than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
| Otherwise, returns 0.
*----------------------------------------------------------------------------*/
INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
}
/*----------------------------------------------------------------------------
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
| than the 128-bit value formed by concatenating `b0' and `b1'. Otherwise,
| returns 0.
*----------------------------------------------------------------------------*/
INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
}
/*----------------------------------------------------------------------------
| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
| not equal to the 128-bit value formed by concatenating `b0' and `b1'.
| Otherwise, returns 0.
*----------------------------------------------------------------------------*/
INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
{
return ( a0 != b0 ) || ( a1 != b1 );
}

View File

@ -0,0 +1,464 @@
/*============================================================================
This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2b.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
arithmetic/SoftFloat.html'.
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) the source code for the derivative work includes prominent notice that
the work is derivative, and (2) the source code includes prominent notice with
these four paragraphs for those parts of this code that are retained.
=============================================================================*/
/*----------------------------------------------------------------------------
| Underflow tininess-detection mode, statically initialized to default value.
| (The declaration in `softfloat.h' must match the `int8' type here.)
*----------------------------------------------------------------------------*/
int8 float_detect_tininess = float_tininess_after_rounding;
/*----------------------------------------------------------------------------
| Raises the exceptions specified by `flags'. Floating-point traps can be
| defined here if desired. It is currently not possible for such a trap
| to substitute a result value. If traps are not implemented, this routine
| should be simply `float_exception_flags |= flags;'.
*----------------------------------------------------------------------------*/
void float_raise( int8 flags )
{
float_exception_flags |= flags;
}
/*----------------------------------------------------------------------------
| Internal canonical NaN format.
*----------------------------------------------------------------------------*/
typedef struct {
flag sign;
bits64 high, low;
} commonNaNT;
/*----------------------------------------------------------------------------
| The pattern for a default generated single-precision NaN.
*----------------------------------------------------------------------------*/
#define float32_default_nan 0xFFC00000
/*----------------------------------------------------------------------------
| Returns 1 if the single-precision floating-point value `a' is a NaN;
| otherwise returns 0.
*----------------------------------------------------------------------------*/
flag float32_is_nan( float32 a )
{
return ( 0xFF000000 < (bits32) ( a<<1 ) );
}
/*----------------------------------------------------------------------------
| Returns 1 if the single-precision floating-point value `a' is a signaling
| NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
flag float32_is_signaling_nan( float32 a )
{
return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
}
/*----------------------------------------------------------------------------
| Returns the result of converting the single-precision floating-point NaN
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
| exception is raised.
*----------------------------------------------------------------------------*/
static commonNaNT float32ToCommonNaN( float32 a )
{
commonNaNT z;
if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a>>31;
z.low = 0;
z.high = ( (bits64) a )<<41;
return z;
}
/*----------------------------------------------------------------------------
| Returns the result of converting the canonical NaN `a' to the single-
| precision floating-point format.
*----------------------------------------------------------------------------*/
static float32 commonNaNToFloat32( commonNaNT a )
{
return ( ( (bits32) a.sign )<<31 ) | 0x7FC00000 | ( a.high>>41 );
}
/*----------------------------------------------------------------------------
| Takes two single-precision floating-point values `a' and `b', one of which
| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
| signaling NaN, the invalid exception is raised.
*----------------------------------------------------------------------------*/
static float32 propagateFloat32NaN( float32 a, float32 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = float32_is_nan( a );
aIsSignalingNaN = float32_is_signaling_nan( a );
bIsNaN = float32_is_nan( b );
bIsSignalingNaN = float32_is_signaling_nan( b );
a |= 0x00400000;
b |= 0x00400000;
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsSignalingNaN ) {
if ( bIsSignalingNaN ) goto returnLargerSignificand;
return bIsNaN ? b : a;
}
else if ( aIsNaN ) {
if ( bIsSignalingNaN | ! bIsNaN ) return a;
returnLargerSignificand:
if ( (bits32) ( a<<1 ) < (bits32) ( b<<1 ) ) return b;
if ( (bits32) ( b<<1 ) < (bits32) ( a<<1 ) ) return a;
return ( a < b ) ? a : b;
}
else {
return b;
}
}
/*----------------------------------------------------------------------------
| The pattern for a default generated double-precision NaN.
*----------------------------------------------------------------------------*/
#define float64_default_nan LIT64( 0xFFF8000000000000 )
/*----------------------------------------------------------------------------
| Returns 1 if the double-precision floating-point value `a' is a NaN;
| otherwise returns 0.
*----------------------------------------------------------------------------*/
flag float64_is_nan( float64 a )
{
return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
}
/*----------------------------------------------------------------------------
| Returns 1 if the double-precision floating-point value `a' is a signaling
| NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
flag float64_is_signaling_nan( float64 a )
{
return
( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
&& ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
}
/*----------------------------------------------------------------------------
| Returns the result of converting the double-precision floating-point NaN
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
| exception is raised.
*----------------------------------------------------------------------------*/
static commonNaNT float64ToCommonNaN( float64 a )
{
commonNaNT z;
if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a>>63;
z.low = 0;
z.high = a<<12;
return z;
}
/*----------------------------------------------------------------------------
| Returns the result of converting the canonical NaN `a' to the double-
| precision floating-point format.
*----------------------------------------------------------------------------*/
static float64 commonNaNToFloat64( commonNaNT a )
{
return
( ( (bits64) a.sign )<<63 )
| LIT64( 0x7FF8000000000000 )
| ( a.high>>12 );
}
/*----------------------------------------------------------------------------
| Takes two double-precision floating-point values `a' and `b', one of which
| is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a
| signaling NaN, the invalid exception is raised.
*----------------------------------------------------------------------------*/
static float64 propagateFloat64NaN( float64 a, float64 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = float64_is_nan( a );
aIsSignalingNaN = float64_is_signaling_nan( a );
bIsNaN = float64_is_nan( b );
bIsSignalingNaN = float64_is_signaling_nan( b );
a |= LIT64( 0x0008000000000000 );
b |= LIT64( 0x0008000000000000 );
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsSignalingNaN ) {
if ( bIsSignalingNaN ) goto returnLargerSignificand;
return bIsNaN ? b : a;
}
else if ( aIsNaN ) {
if ( bIsSignalingNaN | ! bIsNaN ) return a;
returnLargerSignificand:
if ( (bits64) ( a<<1 ) < (bits64) ( b<<1 ) ) return b;
if ( (bits64) ( b<<1 ) < (bits64) ( a<<1 ) ) return a;
return ( a < b ) ? a : b;
}
else {
return b;
}
}
#ifdef FLOATX80
/*----------------------------------------------------------------------------
| The pattern for a default generated extended double-precision NaN. The
| `high' and `low' values hold the most- and least-significant bits,
| respectively.
*----------------------------------------------------------------------------*/
#define floatx80_default_nan_high 0xFFFF
#define floatx80_default_nan_low LIT64( 0xC000000000000000 )
/*----------------------------------------------------------------------------
| Returns 1 if the extended double-precision floating-point value `a' is a
| NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
flag floatx80_is_nan( floatx80 a )
{
return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
}
/*----------------------------------------------------------------------------
| Returns 1 if the extended double-precision floating-point value `a' is a
| signaling NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
flag floatx80_is_signaling_nan( floatx80 a )
{
bits64 aLow;
aLow = a.low & ~ LIT64( 0x4000000000000000 );
return
( ( a.high & 0x7FFF ) == 0x7FFF )
&& (bits64) ( aLow<<1 )
&& ( a.low == aLow );
}
/*----------------------------------------------------------------------------
| Returns the result of converting the extended double-precision floating-
| point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the
| invalid exception is raised.
*----------------------------------------------------------------------------*/
static commonNaNT floatx80ToCommonNaN( floatx80 a )
{
commonNaNT z;
if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a.high>>15;
z.low = 0;
z.high = a.low<<1;
return z;
}
/*----------------------------------------------------------------------------
| Returns the result of converting the canonical NaN `a' to the extended
| double-precision floating-point format.
*----------------------------------------------------------------------------*/
static floatx80 commonNaNToFloatx80( commonNaNT a )
{
floatx80 z;
z.low = LIT64( 0xC000000000000000 ) | ( a.high>>1 );
z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
return z;
}
/*----------------------------------------------------------------------------
| Takes two extended double-precision floating-point values `a' and `b', one
| of which is a NaN, and returns the appropriate NaN result. If either `a' or
| `b' is a signaling NaN, the invalid exception is raised.
*----------------------------------------------------------------------------*/
static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = floatx80_is_nan( a );
aIsSignalingNaN = floatx80_is_signaling_nan( a );
bIsNaN = floatx80_is_nan( b );
bIsSignalingNaN = floatx80_is_signaling_nan( b );
a.low |= LIT64( 0xC000000000000000 );
b.low |= LIT64( 0xC000000000000000 );
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsSignalingNaN ) {
if ( bIsSignalingNaN ) goto returnLargerSignificand;
return bIsNaN ? b : a;
}
else if ( aIsNaN ) {
if ( bIsSignalingNaN | ! bIsNaN ) return a;
returnLargerSignificand:
if ( a.low < b.low ) return b;
if ( b.low < a.low ) return a;
return ( a.high < b.high ) ? a : b;
}
else {
return b;
}
}
#endif
#ifdef FLOAT128
/*----------------------------------------------------------------------------
| The pattern for a default generated quadruple-precision NaN. The `high' and
| `low' values hold the most- and least-significant bits, respectively.
*----------------------------------------------------------------------------*/
#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
#define float128_default_nan_low LIT64( 0x0000000000000000 )
/*----------------------------------------------------------------------------
| Returns 1 if the quadruple-precision floating-point value `a' is a NaN;
| otherwise returns 0.
*----------------------------------------------------------------------------*/
flag float128_is_nan( float128 a )
{
return
( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
&& ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
}
/*----------------------------------------------------------------------------
| Returns 1 if the quadruple-precision floating-point value `a' is a
| signaling NaN; otherwise returns 0.
*----------------------------------------------------------------------------*/
flag float128_is_signaling_nan( float128 a )
{
return
( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
&& ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
}
/*----------------------------------------------------------------------------
| Returns the result of converting the quadruple-precision floating-point NaN
| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid
| exception is raised.
*----------------------------------------------------------------------------*/
static commonNaNT float128ToCommonNaN( float128 a )
{
commonNaNT z;
if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid );
z.sign = a.high>>63;
shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
return z;
}
/*----------------------------------------------------------------------------
| Returns the result of converting the canonical NaN `a' to the quadruple-
| precision floating-point format.
*----------------------------------------------------------------------------*/
static float128 commonNaNToFloat128( commonNaNT a )
{
float128 z;
shift128Right( a.high, a.low, 16, &z.high, &z.low );
z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF800000000000 );
return z;
}
/*----------------------------------------------------------------------------
| Takes two quadruple-precision floating-point values `a' and `b', one of
| which is a NaN, and returns the appropriate NaN result. If either `a' or
| `b' is a signaling NaN, the invalid exception is raised.
*----------------------------------------------------------------------------*/
static float128 propagateFloat128NaN( float128 a, float128 b )
{
flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
aIsNaN = float128_is_nan( a );
aIsSignalingNaN = float128_is_signaling_nan( a );
bIsNaN = float128_is_nan( b );
bIsSignalingNaN = float128_is_signaling_nan( b );
a.high |= LIT64( 0x0000800000000000 );
b.high |= LIT64( 0x0000800000000000 );
if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid );
if ( aIsSignalingNaN ) {
if ( bIsSignalingNaN ) goto returnLargerSignificand;
return bIsNaN ? b : a;
}
else if ( aIsNaN ) {
if ( bIsSignalingNaN | ! bIsNaN ) return a;
returnLargerSignificand:
if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b;
if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a;
return ( a.high < b.high ) ? a : b;
}
else {
return b;
}
}
#endif

5400
core/SoftFloat/softfloat.c Normal file

File diff suppressed because it is too large Load Diff

259
core/SoftFloat/softfloat.h Normal file
View File

@ -0,0 +1,259 @@
/*============================================================================
This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
Package, Release 2b.
Written by John R. Hauser. This work was made possible in part by the
International Computer Science Institute, located at Suite 600, 1947 Center
Street, Berkeley, California 94704. Funding was partially provided by the
National Science Foundation under grant MIP-9311980. The original version
of this code was written as part of a project to build a fixed-point vector
processor in collaboration with the University of California at Berkeley,
overseen by Profs. Nelson Morgan and John Wawrzynek. More information
is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
arithmetic/SoftFloat.html'.
THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE. Although reasonable effort has
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
RESULT IN INCORRECT BEHAVIOR. USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
Derivative works are acceptable, even for commercial purposes, so long as
(1) the source code for the derivative work includes prominent notice that
the work is derivative, and (2) the source code includes prominent notice with
these four paragraphs for those parts of this code that are retained.
=============================================================================*/
/*----------------------------------------------------------------------------
| The macro `FLOATX80' must be defined to enable the extended double-precision
| floating-point format `floatx80'. If this macro is not defined, the
| `floatx80' type will not be defined, and none of the functions that either
| input or output the `floatx80' type will be defined. The same applies to
| the `FLOAT128' macro and the quadruple-precision format `float128'.
*----------------------------------------------------------------------------*/
#define FLOATX80
#define FLOAT128
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point types.
*----------------------------------------------------------------------------*/
typedef unsigned int float32;
typedef unsigned long long float64;
#ifdef FLOATX80
typedef struct {
unsigned long long low;
unsigned short high;
} floatx80;
#endif
#ifdef FLOAT128
typedef struct {
unsigned long long low, high;
} float128;
#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point underflow tininess-detection mode.
*----------------------------------------------------------------------------*/
extern signed char float_detect_tininess;
enum {
float_tininess_after_rounding = 0,
float_tininess_before_rounding = 1
};
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point rounding mode.
*----------------------------------------------------------------------------*/
extern signed char float_rounding_mode;
enum {
float_round_nearest_even = 0,
float_round_down = 1,
float_round_up = 2,
float_round_to_zero = 3
};
/*----------------------------------------------------------------------------
| Software IEC/IEEE floating-point exception flags.
*----------------------------------------------------------------------------*/
extern signed char float_exception_flags;
enum {
float_flag_invalid = 1,
float_flag_divbyzero = 4,
float_flag_overflow = 8,
float_flag_underflow = 16,
float_flag_inexact = 32
};
/*----------------------------------------------------------------------------
| Routine to raise any or all of the software IEC/IEEE floating-point
| exception flags.
*----------------------------------------------------------------------------*/
void float_raise( signed char );
/*----------------------------------------------------------------------------
| Software IEC/IEEE integer-to-floating-point conversion routines.
*----------------------------------------------------------------------------*/
float32 int32_to_float32( int );
float64 int32_to_float64( int );
#ifdef FLOATX80
floatx80 int32_to_floatx80( int );
#endif
#ifdef FLOAT128
float128 int32_to_float128( int );
#endif
float32 int64_to_float32( long long );
float64 int64_to_float64( long long );
#ifdef FLOATX80
floatx80 int64_to_floatx80( long long );
#endif
#ifdef FLOAT128
float128 int64_to_float128( long long );
#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE single-precision conversion routines.
*----------------------------------------------------------------------------*/
int float32_to_int32( float32 );
int float32_to_int32_round_to_zero( float32 );
long long float32_to_int64( float32 );
long long float32_to_int64_round_to_zero( float32 );
float64 float32_to_float64( float32 );
#ifdef FLOATX80
floatx80 float32_to_floatx80( float32 );
#endif
#ifdef FLOAT128
float128 float32_to_float128( float32 );
#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE single-precision operations.
*----------------------------------------------------------------------------*/
float32 float32_round_to_int( float32 );
float32 float32_add( float32, float32 );
float32 float32_sub( float32, float32 );
float32 float32_mul( float32, float32 );
float32 float32_div( float32, float32 );
float32 float32_rem( float32, float32 );
float32 float32_sqrt( float32 );
char float32_eq( float32, float32 );
char float32_le( float32, float32 );
char float32_lt( float32, float32 );
char float32_eq_signaling( float32, float32 );
char float32_le_quiet( float32, float32 );
char float32_lt_quiet( float32, float32 );
char float32_is_signaling_nan( float32 );
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision conversion routines.
*----------------------------------------------------------------------------*/
int float64_to_int32( float64 );
int float64_to_int32_round_to_zero( float64 );
long long float64_to_int64( float64 );
long long float64_to_int64_round_to_zero( float64 );
float32 float64_to_float32( float64 );
#ifdef FLOATX80
floatx80 float64_to_floatx80( float64 );
#endif
#ifdef FLOAT128
float128 float64_to_float128( float64 );
#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE double-precision operations.
*----------------------------------------------------------------------------*/
float64 float64_round_to_int( float64 );
float64 float64_add( float64, float64 );
float64 float64_sub( float64, float64 );
float64 float64_mul( float64, float64 );
float64 float64_div( float64, float64 );
float64 float64_rem( float64, float64 );
float64 float64_sqrt( float64 );
char float64_eq( float64, float64 );
char float64_le( float64, float64 );
char float64_lt( float64, float64 );
char float64_eq_signaling( float64, float64 );
char float64_le_quiet( float64, float64 );
char float64_lt_quiet( float64, float64 );
char float64_is_signaling_nan( float64 );
#ifdef FLOATX80
/*----------------------------------------------------------------------------
| Software IEC/IEEE extended double-precision conversion routines.
*----------------------------------------------------------------------------*/
int floatx80_to_int32( floatx80 );
int floatx80_to_int32_round_to_zero( floatx80 );
long long floatx80_to_int64( floatx80 );
long long floatx80_to_int64_round_to_zero( floatx80 );
float32 floatx80_to_float32( floatx80 );
float64 floatx80_to_float64( floatx80 );
#ifdef FLOAT128
float128 floatx80_to_float128( floatx80 );
#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE extended double-precision rounding precision. Valid
| values are 32, 64, and 80.
*----------------------------------------------------------------------------*/
extern signed char floatx80_rounding_precision;
/*----------------------------------------------------------------------------
| Software IEC/IEEE extended double-precision operations.
*----------------------------------------------------------------------------*/
floatx80 floatx80_round_to_int( floatx80 );
floatx80 floatx80_add( floatx80, floatx80 );
floatx80 floatx80_sub( floatx80, floatx80 );
floatx80 floatx80_mul( floatx80, floatx80 );
floatx80 floatx80_div( floatx80, floatx80 );
floatx80 floatx80_rem( floatx80, floatx80 );
floatx80 floatx80_sqrt( floatx80 );
char floatx80_eq( floatx80, floatx80 );
char floatx80_le( floatx80, floatx80 );
char floatx80_lt( floatx80, floatx80 );
char floatx80_eq_signaling( floatx80, floatx80 );
char floatx80_le_quiet( floatx80, floatx80 );
char floatx80_lt_quiet( floatx80, floatx80 );
char floatx80_is_signaling_nan( floatx80 );
#endif
#ifdef FLOAT128
/*----------------------------------------------------------------------------
| Software IEC/IEEE quadruple-precision conversion routines.
*----------------------------------------------------------------------------*/
int float128_to_int32( float128 );
int float128_to_int32_round_to_zero( float128 );
long long float128_to_int64( float128 );
long long float128_to_int64_round_to_zero( float128 );
float32 float128_to_float32( float128 );
float64 float128_to_float64( float128 );
#ifdef FLOATX80
floatx80 float128_to_floatx80( float128 );
#endif
/*----------------------------------------------------------------------------
| Software IEC/IEEE quadruple-precision operations.
*----------------------------------------------------------------------------*/
float128 float128_round_to_int( float128 );
float128 float128_add( float128, float128 );
float128 float128_sub( float128, float128 );
float128 float128_mul( float128, float128 );
float128 float128_div( float128, float128 );
float128 float128_rem( float128, float128 );
float128 float128_sqrt( float128 );
char float128_eq( float128, float128 );
char float128_le( float128, float128 );
char float128_lt( float128, float128 );
char float128_eq_signaling( float128, float128 );
char float128_le_quiet( float128, float128 );
char float128_lt_quiet( float128, float128 );
char float128_is_signaling_nan( float128 );
#endif

View File

@ -79,9 +79,35 @@
*/
void reset_adb_state()
{
pthread_mutex_t lock = shoe.adb.lock;
memset(&shoe.adb, 0, sizeof(adb_state_t));
memset(&shoe.key, 0, sizeof(keyboard_state_t));
memset(&shoe.mouse, 0, sizeof(mouse_state_t));
// Put the adb chip in state 3 (idle)
shoe.adb.state = 3;
shoe.adb.lock = lock;
}
void init_adb_state()
{
memset(&shoe.adb, 0, sizeof(adb_state_t));
memset(&shoe.key, 0, sizeof(keyboard_state_t));
memset(&shoe.mouse, 0, sizeof(mouse_state_t));
// Put the adb chip in state 3 (idle)
shoe.adb.state = 3;
pthread_mutex_init(&shoe.adb.lock, NULL);
}
void adb_start_service_request()
{
printf("adb_start_service_request: pending_requests = 0x%02x\n", shoe.adb.pending_service_requests);
//slog("adb_start_service_request: pending_requests = 0x%02x\n", shoe.adb.pending_service_requests);
if (shoe.adb.pending_service_requests) {
shoe.adb.service_request = 1;
@ -118,32 +144,33 @@ static void keyboard_talk(uint8_t reg)
else
shoe.adb.timeout = 1;
return ;
break ;
case 2:
// All the modifier keys are up
shoe.adb.data[0] = 0b01111111;
shoe.adb.data[1] = 0b11100111;
shoe.adb.data[0] = ~b(01111111);
shoe.adb.data[1] = ~b(11100111);
shoe.adb.data_len = 2;
return ;
break ;
case 1:
shoe.adb.timeout = 1;
return ;
break ;
case 3:
shoe.adb.data[0] = 0;
shoe.adb.data[1] = 0;
shoe.adb.data[0] = 0x02; // device address == 2 -> keyboard
shoe.adb.data[1] = 0x02; // device handler ID == 0x03 -> Apple Extended Keyboard
shoe.adb.data_len = 2;
return ;
break ;
}
slog("keyboard_talk: reg=%u timeout=%u data=0x%02x%02x datalen=%u\n", reg, shoe.adb.timeout, shoe.adb.data[0], shoe.adb.data[1], shoe.adb.data_len);
}
static void mouse_talk(uint8_t reg)
{
shoe.adb.timeout = 0;
printf("mouse_talk: reg=%u\n", reg);
slog("mouse_talk: reg=%u\n", reg);
switch (reg) {
case 0:
@ -154,7 +181,7 @@ static void mouse_talk(uint8_t reg)
int32_t x = shoe.mouse.delta_x;
int32_t y = shoe.mouse.delta_y;
//printf("mouse_talk: x=%d, y=%d button=%u\n", shoe.mouse.delta_x, shoe.mouse.delta_y, shoe.mouse.button_down);
//slog("mouse_talk: x=%d, y=%d button=%u\n", shoe.mouse.delta_x, shoe.mouse.delta_y, shoe.mouse.button_down);
if (x > hi_delta_limit) x = hi_delta_limit;
@ -168,7 +195,7 @@ static void mouse_talk(uint8_t reg)
//shoe.adb.data[1] |= 0x80;
shoe.adb.data[0] |= 0x80;
}
// printf("mouse_talk: ")
// slog("mouse_talk: ")
shoe.adb.data_len = 2;
@ -217,7 +244,7 @@ static void adb_handle_state_zero(uint8_t command_byte, uint8_t is_poll) // "Com
else
assert(!"What is this adb state-0 command? xxxx 01xx");
printf("adb_handle_state_zero: command_byte=0x%02x, id=%u, reg=%u\n", command_byte, id, reg);
slog("adb_handle_state_zero: command_byte=0x%02x, id=%u, reg=%u\n", command_byte, id, reg);
shoe.adb.command_device_id = id;
shoe.adb.command_reg = reg;
@ -245,7 +272,7 @@ static void adb_handle_state_zero(uint8_t command_byte, uint8_t is_poll) // "Com
shoe.adb.poll = 0;
via->regb |= VIA_REGB_ADB_STATUS;
via->regb_input |= VIA_REGB_ADB_STATUS;
via_raise_interrupt(1, IFR_SHIFT_REG);
}
@ -253,7 +280,7 @@ static void adb_handle_state_one (void) // "Even" state
{
via_state_t *via = &shoe.via[0];
printf("adb_handle_state_one: ");
slog("adb_handle_state_one: ");
if (shoe.adb.poll) {
// Upon receiving a service request, the adb controller sends a TALK/reg=0 to the last accessed device
adb_handle_state_zero((shoe.adb.command_device_id << 4) | 0x0c, 1);
@ -266,12 +293,12 @@ static void adb_handle_state_one (void) // "Even" state
break;
case adb_talk:
printf("adb_talk: ");
slog("adb_talk: ");
if (shoe.adb.timeout) {
shoe.adb.timeout = 0;
via->regb &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == timeout
via->regb_input &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == timeout
via_raise_interrupt(1, IFR_SHIFT_REG);
printf("timeout\n");
slog("timeout\n");
return ;
}
@ -280,17 +307,17 @@ static void adb_handle_state_one (void) // "Even" state
else
via->sr = 0;
printf("set sr = 0x%02x\n", via->sr);
slog("set sr = 0x%02x\n", via->sr);
break;
case adb_listen:
printf("adb_listen: ");
slog("adb_listen: ");
if (shoe.adb.timeout) {
shoe.adb.timeout = 0;
via->regb &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == timeout
via->regb_input &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == timeout
via_raise_interrupt(1, IFR_SHIFT_REG);
printf("timeout\n");
slog("timeout\n");
return ;
}
@ -299,12 +326,12 @@ static void adb_handle_state_one (void) // "Even" state
else
assert(!"OS made us listen to > 8 bytes");
printf("loaded sr = 0x%02x\n", via->sr);
slog("loaded sr = 0x%02x\n", via->sr);
break;
}
via->regb |= VIA_REGB_ADB_STATUS; // adb_status_line set == didn't-timeout
via->regb_input |= VIA_REGB_ADB_STATUS; // adb_status_line set == didn't-timeout
via_raise_interrupt(1, IFR_SHIFT_REG);
}
@ -312,39 +339,39 @@ static void adb_handle_state_two (void) // "Odd" state
{
via_state_t *via = &shoe.via[0];
printf("adb_handle_state_two: ");
slog("adb_handle_state_two: ");
// If this transaction was part of a service request, clear the service_request flag now
if (shoe.adb.service_request) {
shoe.adb.service_request = 0;
via->regb &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == service request
printf("(service request) ");
via->regb_input &= ~~VIA_REGB_ADB_STATUS; // adb_status_line cleared == service request
slog("(service request) ");
}
else
via->regb |= VIA_REGB_ADB_STATUS; // adb_status_line set == no-service request
via->regb_input |= VIA_REGB_ADB_STATUS; // adb_status_line set == no-service request
switch (shoe.adb.command_type) {
case adb_flush:
case adb_reset:
printf("adb_flush/reset\n");
slog("adb_flush/reset\n");
break;
case adb_talk:
printf("adb_talk: ");
slog("adb_talk: ");
if (shoe.adb.data_i < shoe.adb.data_len)
via->sr = shoe.adb.data[shoe.adb.data_i++];
else
via->sr = 0;
printf("set sr = 0x%02x\n", via->sr);
slog("set sr = 0x%02x\n", via->sr);
break;
case adb_listen:
printf("adb_listen: ");
slog("adb_listen: ");
if (shoe.adb.data_i < 8)
shoe.adb.data[shoe.adb.data_i++] = via->sr;
else
assert(!"OS made us listen to > 8 bytes");
printf("read sr = 0x%02x\n", via->sr);
slog("read sr = 0x%02x\n", via->sr);
break;
}
@ -353,7 +380,7 @@ static void adb_handle_state_two (void) // "Odd" state
static void adb_handle_state_three (void) // "idle" state
{
printf("adb_handle_state_three: completed for id %u\n", shoe.adb.command_device_id);
slog("adb_handle_state_three: completed for id %u\n", shoe.adb.command_device_id);
switch (shoe.adb.command_type) {
case adb_reset:
@ -362,7 +389,7 @@ static void adb_handle_state_three (void) // "idle" state
break;
case adb_listen:
printf("adb_handle_state_three: listen completed for id %u, reg %u, data_len = %u {%02x %02x}\n",
slog("adb_handle_state_three: listen completed for id %u, reg %u, data_len = %u {%02x %02x}\n",
shoe.adb.command_device_id, shoe.adb.command_reg, shoe.adb.data_i, shoe.adb.data[0], shoe.adb.data[1]);
break;
}
@ -374,7 +401,7 @@ void adb_handle_state_change(uint8_t old_state, uint8_t new_state)
{
via_state_t *via = &shoe.via[0];
printf("%s: lock\n", __func__); fflush(stdout);
slog("%s: lock\n", __func__); fflush(stdout);
assert(pthread_mutex_lock(&shoe.adb.lock) == 0);
shoe.adb.state = new_state;
@ -398,7 +425,7 @@ void adb_handle_state_change(uint8_t old_state, uint8_t new_state)
break ;
}
printf("%s: unlock\n", __func__); fflush(stdout);
slog("%s: unlock\n", __func__); fflush(stdout);
pthread_mutex_unlock(&shoe.adb.lock);
}

View File

@ -26,18 +26,54 @@
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "../core/shoebill.h"
/*typedef struct _alloc_pool_t {
/*
#define POOL_ALLOC_TYPE 0
#define POOL_CHILD_LINK 1
#define POOL_HEAD 2
typedef struct _alloc_pool_t {
struct _alloc_pool_t *prev, *next;
uint32_t size, magic;
} alloc_pool_t;*/
uint8_t type;
union {
struct {
uint32_t size;
} alloc;
struct {
struct _alloc_pool_t *child; // pointer to the child's HEAD
} child_link;
struct {
struct _alloc_pool_t *parent_link; // pointer to the parent's CHILD_LINK
} head;
} t;
uint32_t magic;
} alloc_pool_t;
*/
static void _check_pool(alloc_pool_t *pool)
{
}
static alloc_pool_t* _ptr_to_header(void *ptr)
{
alloc_pool_t *apt = (alloc_pool_t*)ptr;
return &apt[-1];
}
void* p_alloc(alloc_pool_t *pool, uint64_t size)
{
alloc_pool_t *buf = calloc(sizeof(alloc_pool_t) + size, 1);
buf->size = size;
buf->magic = 'moof';
buf->type = POOL_ALLOC_TYPE;
buf->t.alloc.size = size;
buf->start_magic = POOL_START_MAGIC;
buf->end_magic = POOL_END_MAGIC;
buf->next = pool->next;
buf->prev = pool;
@ -51,19 +87,36 @@ void* p_alloc(alloc_pool_t *pool, uint64_t size)
void* p_realloc(void *ptr, uint64_t size)
{
alloc_pool_t *header = &((alloc_pool_t*)ptr)[-1];
alloc_pool_t *header = _ptr_to_header(ptr);
assert(header->start_magic == POOL_START_MAGIC);
assert(header->end_magic == POOL_END_MAGIC);
assert(header->type == POOL_ALLOC_TYPE);
alloc_pool_t *new_header = realloc(header, size + sizeof(alloc_pool_t));
if (new_header)
if (new_header) {
new_header->t.alloc.size = size;
if (new_header->next)
new_header->next->prev = new_header;
if (new_header->prev)
new_header->prev->next = new_header;
return &new_header[1];
}
return NULL;
}
void p_free(void *ptr)
/*
* Free *any* kind of alloc_pool_t header
*/
static void _p_free_any(alloc_pool_t *header)
{
alloc_pool_t *header = &((alloc_pool_t*)ptr)[-1];
assert(header->magic == 'moof');
assert(header->start_magic == POOL_START_MAGIC);
assert(header->end_magic == POOL_END_MAGIC);
if (header->next)
header->next->prev = header->prev;
@ -74,6 +127,17 @@ void p_free(void *ptr)
free(header);
}
/*
* Free an alloc_pool allocation (but not HEAD or CHILD_LINK)
*/
void p_free(void *ptr)
{
alloc_pool_t *header = _ptr_to_header(ptr);
assert(header->type == POOL_ALLOC_TYPE);
memset(ptr, 0xaa, header->t.alloc.size);
_p_free_any(header);
}
void p_free_pool(alloc_pool_t *pool)
{
while (pool->prev)
@ -82,14 +146,50 @@ void p_free_pool(alloc_pool_t *pool)
while (pool) {
alloc_pool_t *cur = pool;
pool = cur->next;
assert(cur->magic == 'moof');
free(cur);
assert(cur->start_magic == POOL_START_MAGIC);
assert(cur->end_magic == POOL_END_MAGIC);
switch (cur->type) {
case POOL_ALLOC_TYPE:
_p_free_any(cur);
break;
case POOL_CHILD_LINK: {
// p_free_pool will free and unlink cur
// (its parent's CHILD_LINK)
p_free_pool(cur->t.child_link.child);
break;
}
case POOL_HEAD: {
if (cur->t.head.parent_link) {
assert(cur->t.head.parent_link->type == POOL_CHILD_LINK);
_p_free_any(cur->t.head.parent_link);
}
_p_free_any(cur);
break;
}
default:
assert(!"unknown POOL_ type");
}
}
}
alloc_pool_t* p_new_pool(void)
alloc_pool_t* p_new_pool(alloc_pool_t *parent_pool)
{
alloc_pool_t *pool = calloc(sizeof(alloc_pool_t), 1);
pool->magic = 'moof';
pool->start_magic = POOL_START_MAGIC;
pool->end_magic = POOL_END_MAGIC;
pool->type = POOL_HEAD;
if (parent_pool) {
alloc_pool_t *link = _ptr_to_header(p_alloc(parent_pool, 0));
link->type = POOL_CHILD_LINK;
link->t.child_link.child = pool; // child_link.child points to the child's HEAD
pool->t.head.parent_link = link; // head.parent_link points to the parent's CHILD_LINK
}
else
pool->t.head.parent_link = NULL;
return pool;
}

View File

@ -30,14 +30,13 @@
#include <stdint.h>
#include <assert.h>
#include "shoebill.h"
#include "coff.h"
void symb_inorder(rb_node *cur) {
const coff_symbol *sym = (coff_symbol*)cur->value;
const coff_symbol *sym = *(coff_symbol**)&cur[1];
if (!sym)
return ;
symb_inorder(cur->left);
printf("0x%x %s\n", cur->key, sym->name);
slog("0x%x %s\n", cur->key, sym->name);
symb_inorder(cur->right);
}
@ -59,25 +58,31 @@ void symb_inorder(rb_node *cur) {
_result; \
})
void coff_free(coff_file *coff)
{
p_free_pool(coff->pool);
}
// Given a path to a COFF binary, create a coff_file structure and return a pointer.
// God help you if you want to free it.
coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
coff_file* coff_parse(uint8_t *buf, uint32_t buflen, alloc_pool_t *parent_pool)
{
uint8_t rawhead[20], *ptr;
uint32_t i;
coff_file *cf = NULL;
uint32_t bufptr = 0;
alloc_pool_t *pool = p_new_pool(parent_pool);
// Pull out 20 bytes (the file header)
if (!_coff_buf_read(rawhead, 20)) {
printf("coff_parse: error: this binary is missing its file header\n");
slog("coff_parse: error: this binary is missing its file header\n");
goto fail;
}
// Allocate a coff_file and copy in the header
cf = (coff_file*)calloc(1, sizeof(coff_file));
cf = (coff_file*)p_alloc(pool, sizeof(coff_file));
cf->pool = pool;
ptr = rawhead;
cf->magic = be2native(&ptr, 2);
cf->num_sections = be2native(&ptr, 2);
cf->timestamp = be2native(&ptr, 4);
@ -88,32 +93,32 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
// A little sanity checking...
if (cf->magic != 0x150) {
printf("coff_parse: I don't recognize this magic number: 0x%04x\n", cf->magic);
slog("coff_parse: I don't recognize this magic number: 0x%04x\n", cf->magic);
goto fail;
}
else if (cf->num_sections != 3) {
//printf("coff_parse: warning: there are %u sections in this file (not 3, like I expect)\n", cf->num_sections);
//slog("coff_parse: warning: there are %u sections in this file (not 3, like I expect)\n", cf->num_sections);
// FIXME: investigate all the other possible section types
}
// pull out cf->opt_header bytes (a.out-format header, I guess?)
if (cf->opt_header_len > 0) {
uint8_t *opt = malloc(cf->opt_header_len);
uint8_t *opt = p_alloc(cf->pool, cf->opt_header_len);
if (!_coff_buf_read(opt, cf->opt_header_len)) {
printf("coff_parse: I ran out of data pulling the optional header (%u bytes)\n", cf->opt_header_len);
free(opt);
slog("coff_parse: I ran out of data pulling the optional header (%u bytes)\n", cf->opt_header_len);
p_free(opt);
goto fail;
}
cf->opt_header = opt;
}
// start pulling out sections
cf->sections = calloc(cf->num_sections, sizeof(coff_section));
cf->sections = p_alloc(cf->pool, cf->num_sections * sizeof(coff_section));
for (i=0; i<cf->num_sections; i++) {
// read the header
uint8_t rawsec[40];
if (!_coff_buf_read(rawsec, 40)) {
printf("coff_parse: I ran out of data pulling section #%u\n", i+1);
slog("coff_parse: I ran out of data pulling section #%u\n", i+1);
goto fail;
}
// and copy it into cf->sections[i]
@ -131,7 +136,7 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
// a little bit of sanity checking:
if (cf->sections[i].v_addr != cf->sections[i].p_addr) {
//printf("coff_parse: warning: section %u's virtual_addr and physical_addr don't match: p=%x v=%x\n",
//slog("coff_parse: warning: section %u's virtual_addr and physical_addr don't match: p=%x v=%x\n",
// i+1, cf->sections[i].p_addr, cf->sections[i].v_addr);
// This is okay for the unix kernel
}
@ -153,15 +158,15 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
// seek to the position in the binary that holds this section's raw data
if (!_coff_buf_seek(cf->sections[i].data_ptr)) {
printf("coff_parse: I couldn't seek to 0x%x in section %u\n", cf->sections[i].data_ptr, i+1);
slog("coff_parse: I couldn't seek to 0x%x in section %u\n", cf->sections[i].data_ptr, i+1);
goto fail;
}
// load the data and attach it to the section struct
data = malloc(cf->sections[i].sz); // FIXME: sz might not be a sane value
data = p_alloc(cf->pool, cf->sections[i].sz); // FIXME: sz might not be a sane value
if (!_coff_buf_read(data, cf->sections[i].sz)) {
printf("coff_parse: I couldn't fread section %u (%s)'s data (%u bytes)\n", i+1, cf->sections[i].name, cf->sections[i].sz);
free(data);
slog("coff_parse: I couldn't fread section %u (%s)'s data (%u bytes)\n", i+1, cf->sections[i].name, cf->sections[i].sz);
p_free(data);
goto fail;
}
cf->sections[i].data = data;
@ -172,20 +177,20 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
if (cf->num_symbols == 0) // if num_symbols==0, symtab_offset may be bogus
return cf; // just return
cf->func_tree = rb_new();
//printf("func_tree = %llx, *func_tree = %llx\n", cf->func_tree, *cf->func_tree);
cf->symbols = (coff_symbol*)calloc(sizeof(coff_symbol), cf->num_symbols);
cf->func_tree = rb_new(cf->pool, sizeof(coff_symbol*));
//slog("func_tree = %llx, *func_tree = %llx\n", cf->func_tree, *cf->func_tree);
cf->symbols = (coff_symbol*)p_alloc(cf->pool, sizeof(coff_symbol) *cf->num_symbols);
// Seek to the symbol table
if (!_coff_buf_seek(cf->symtab_offset)) {
printf("coff_parse: I couldn't seek to symtab_offset, 0x%x\n", cf->symtab_offset);
slog("coff_parse: I couldn't seek to symtab_offset, 0x%x\n", cf->symtab_offset);
goto fail;
}
for (i=0; i < cf->num_symbols; i++) {
uint8_t raw_symb[18];
if (!_coff_buf_read(raw_symb, 18)) {
printf("coff_parse: I ran out of data pulling symbol #%u\n", i+1);
slog("coff_parse: I ran out of data pulling symbol #%u\n", i+1);
goto fail;
}
@ -196,19 +201,19 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
for (j=4, offset=0; j<8; j++) offset = (offset<<8) | raw_symb[j];
idx += offset;
// printf("Loading from external: base idx=0x%x, offset=%u, addr=0x%x\n", idx-offset, offset, idx);
// slog("Loading from external: base idx=0x%x, offset=%u, addr=0x%x\n", idx-offset, offset, idx);
if (!_coff_buf_seek(idx)) {
printf("coff_parse: I ran out of data pulling symbol %u's name (idx=0x%x)\n", i+1, idx);
slog("coff_parse: I ran out of data pulling symbol %u's name (idx=0x%x)\n", i+1, idx);
goto fail;
}
for (j=0; (_coff_buf_read(&tmp_name[j], 1)) && tmp_name[j]; j++) {
if (j >= 255) {
// printf("coff_parse: this symbol's name is too long: %u\n", i+1);
// slog("coff_parse: this symbol's name is too long: %u\n", i+1);
goto fail;
}
}
cf->symbols[i].name = malloc(j+1);
cf->symbols[i].name = p_alloc(cf->pool, j+1);
memcpy(cf->symbols[i].name, tmp_name, j);
cf->symbols[i].name[j] = 0;
_coff_buf_seek(cf->symtab_offset + (i+1)*18);
@ -230,17 +235,18 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
// FIXME: I need to handle numaux > 0.
//if (cf->symbols[i].numaux > 0) {
printf("%s\n", cf->symbols[i].name);
printf("value=0x%08x scnum=0x%04x type=0x%04x sclass=0x%02x numaux=%u\n\n",
slog("%s\n", cf->symbols[i].name);
slog("value=0x%08x scnum=0x%04x type=0x%04x sclass=0x%02x numaux=%u\n\n",
cf->symbols[i].value, cf->symbols[i].scnum, cf->symbols[i].type, cf->symbols[i].sclass, cf->symbols[i].numaux);
//}
if (cf->symbols[i].sclass == 2 || cf->symbols[i].sclass == 3) {
rb_insert(cf->func_tree, cf->symbols[i].value, &cf->symbols[i], NULL);
//printf("%s addr=0x%x\n", cf->symbols[i].name, cf->symbols[i].value);
void *ptr = &cf->symbols[i];
rb_insert(cf->func_tree, cf->symbols[i].value, &ptr, NULL);
//slog("%s addr=0x%x\n", cf->symbols[i].name, cf->symbols[i].value);
}
// printf("%u: %s (class=%u)\n", i+1, cf->symbols[i].name, cf->symbols[i].sclass);
// slog("%u: %s (class=%u)\n", i+1, cf->symbols[i].name, cf->symbols[i].sclass);
}
@ -252,24 +258,24 @@ coff_file* coff_parse(uint8_t *buf, uint32_t buflen)
fail:
if (cf) {
if (cf->opt_header) {
free(cf->opt_header);
p_free(cf->opt_header);
}
if (cf->sections) {
for (i=0; i<cf->num_sections; i++) {
if (cf->sections[i].data) {
free(cf->sections[i].data);
p_free(cf->sections[i].data);
}
}
free(cf->sections);
p_free(cf->sections);
}
free(cf);
p_free(cf);
}
return NULL;
}
coff_file* coff_parse_from_path(const char *path)
coff_file* coff_parse_from_path(const char *path, alloc_pool_t *parent_pool)
{
FILE *f = fopen(path, "r");
FILE *f = fopen(path, "rb");
uint8_t *buf = malloc(1);
uint32_t i=0, tmp;
coff_file *coff;
@ -284,7 +290,7 @@ coff_file* coff_parse_from_path(const char *path)
} while ((tmp > 0) && (i < 64*1024*1024));
coff = coff_parse(buf, i);
coff = coff_parse(buf, i, parent_pool);
free(buf);
return coff;
}
@ -296,19 +302,19 @@ void print_coff_info(coff_file *coff)
time_t timestamp = coff->timestamp;
uint32_t i;
printf("Magic = 0x%04x\n", coff->magic);
printf("Linked on %s", ctime_r(&timestamp, timebuf));
printf("Num sections = %u\n", coff->num_sections);
slog("Magic = 0x%04x\n", coff->magic);
slog("Linked on %s", ctime_r(&timestamp, timebuf));
slog("Num sections = %u\n", coff->num_sections);
printf("debug: num_symbols=%u, symtab_offset=0x%x\n", coff->num_symbols, coff->symtab_offset);
slog("debug: num_symbols=%u, symtab_offset=0x%x\n", coff->num_symbols, coff->symtab_offset);
for (i=0; i<coff->num_sections; i++) {
coff_section *s = &coff->sections[i];
char name[9];
memcpy(name, s->name, 8);
name[8] = 0;
printf("Section #%u: %s\n", i+1, name);
printf("\taddr=0x%08x, len=0x%x, (debug: paddr=0x%08x, flags=0x%08x)\n", s->v_addr, s->sz, s->p_addr, s->flags);
slog("Section #%u: %s\n", i+1, name);
slog("\taddr=0x%08x, len=0x%x, (debug: paddr=0x%08x, flags=0x%08x)\n", s->v_addr, s->sz, s->p_addr, s->flags);
}
}
@ -328,21 +334,21 @@ coff_symbol* coff_find_func(coff_file *coff, uint32_t addr)
rb_node *cur;
coff_symbol *last = NULL;
// printf("coff->num_symbols = %u\n", coff->num_symbols);
// slog("coff->num_symbols = %u\n", coff->num_symbols);
if (coff->num_symbols == 0)
return NULL;
cur = *coff->func_tree;
cur = coff->func_tree->root;
while (cur) {
// printf("... iterating\n");
// slog("... iterating\n");
if (addr < cur->key)
cur = cur->left;
else if (addr > cur->key) {
last = cur->value;
last = *(coff_symbol**)&cur[1];
cur = cur->right;
}
else
return cur->value;
return *(coff_symbol**)&cur[1];
}
return last;

View File

@ -1,85 +0,0 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _COFF_H
#define _COFF_H
#include <stdint.h>
#include "redblack.h"
typedef struct {
char *name;
uint32_t value;
uint16_t scnum, type;
uint8_t sclass, numaux;
} coff_symbol;
// informed by http://www.delorie.com/djgpp/doc/coff/scnhdr.html
typedef struct {
char name[8];
uint32_t p_addr;
uint32_t v_addr;
uint32_t sz;
uint32_t data_ptr;
uint32_t reloc_ptr;
uint32_t line_ptr;
uint16_t num_relocs;
uint16_t num_lines;
uint32_t flags;
uint8_t *data;
} coff_section;
// data for this segment appears in the file, but shouldn't be copied into memory
#define coff_copy 0x0010
#define coff_text 0x0020
#define coff_data 0x0040
#define coff_bss 0x0080
typedef struct {
uint16_t magic;
uint16_t num_sections;
uint32_t timestamp;
uint32_t symtab_offset;
uint32_t num_symbols;
uint16_t opt_header_len;
uint16_t flags;
uint8_t *opt_header;
coff_section *sections;
rb_tree *func_tree;
coff_symbol *symbols;
} coff_file;
coff_symbol* coff_find_func(coff_file *coff, uint32_t addr);
coff_symbol* coff_find_symbol(coff_file *coff, const char *name);
coff_file* coff_parse(uint8_t *buf, uint32_t buflen);
coff_file* coff_parse_from_path(const char *path);
uint32_t be2native (uint8_t **dat, uint32_t bytes);
void print_coff_info(coff_file *coff);
#endif // _COFF_H

File diff suppressed because it is too large Load Diff

View File

@ -1,132 +0,0 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _CORE_API_H
#define _CORE_API_H
#include <stdint.h>
typedef enum {
card_none = 0, // Empty slot
card_toby_frame_buffer, // Original Macintosh II video card
card_shoebill_video, // Fancy 21st-century Shoebill video card
card_shoebill_ethernet // FIXME: doesn't exist yet
} card_names_t;
typedef struct {
uint8_t *frame_buffer;
} shoebill_card_tfb_t;
typedef struct {
uint8_t r, g, b, a;
} video_ctx_color_t;
typedef struct {
video_ctx_color_t *direct_buf, *clut;
uint8_t *indexed_buf, *rom;
uint8_t *cur_buf;
uint32_t pixels;
uint16_t width, height, scanline_width;
uint16_t depth, clut_idx;
double refresh_rate;
} shoebill_card_video_t;
typedef struct {
// Doesn't exist yet
} shoebill_card_ethernet_t;
typedef struct {
/*
* Fill in this part of the struct
* before you call shoebill_initialize()
*/
uint32_t ram_size;
const char *rom_path;
const char *aux_kernel_path;
uint8_t aux_verbose : 1; // Whether to boot A/UX in verbose mode
uint8_t aux_autoconfig : 1; // Whether to run A/UX autoconfig
uint16_t root_ctrl, swap_ctrl;
uint8_t root_drive, swap_drive;
uint8_t root_partition, swap_partition;
uint8_t root_cluster;
/* Devices at the 7 possible target SCSI ids */
struct {
const char *path;
uint64_t size; // will be filled in later
} scsi_devices[7]; // scsi device #7 is the initiator (can't be a target)
/* ^^^ You can stop now ^^^ */
/* Cards installed in the 16 (really, just 0x9 to 0xe) nubus slots */
struct {
card_names_t card_type;
union {
shoebill_card_tfb_t tfb;
shoebill_card_video_t video;
shoebill_card_ethernet_t ethernet;
} card;
} slots[16];
pthread_t cpu_thread_pid, via_thread_pid;
char error_msg[8192];
} shoebill_control_t;
uint32_t shoebill_initialize(shoebill_control_t *params);
uint32_t shoebill_install_video_card(shoebill_control_t *control, uint8_t slotnum,
uint16_t width, uint16_t height,
double refresh_rate);
/*
* These keyboard modifier constants match the ones used
* in NSEvent shifted right by 16 bits.
*/
enum {
modCapsLock = 1 << 0,
modShift = 1 << 1,
modControl = 1 << 2,
modOption = 1 << 3,
modCommand = 1 << 4
};
void shoebill_key(uint8_t down, uint8_t key);
void shoebill_key_modifier(uint8_t modifier_mask);
void shoebill_mouse_move(int32_t x, int32_t y);
void shoebill_mouse_move_delta (int32_t x, int32_t y);
void shoebill_mouse_click(uint8_t down);
void shoebill_start();
uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, char *error_str, uint32_t *len);
#endif

1751
core/cpu.c

File diff suppressed because it is too large Load Diff

View File

@ -1,126 +0,0 @@
/*
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "../core/shoebill.h"
#include "../core/coff.h"
#define DEBUG_MODE_STOPPED 0
#define DEBUG_MODE_RUNNING 1
#define DEBUG_MODE_STEP 2
#define SHOEBILL_DEBUG_PORT 0xfded
static int _start_debug_server(void)
{
struct sockaddr_in addr;
int sock = socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(struct sockaddr_in);
addr.sin_family = AF_INET;
addr.sin_port = htons(SHOEBILL_DEBUG_PORT);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (sock == -1) {
assert(!"can't socket");
}
else if (bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr)) != 0) {
assert(!"can't bind");
// return -1;
}
else if (listen(sock, 1) != 0) {
assert(!"can't listen");
}
return sock;
}
void *debug_server_thread (void *arg)
{
struct sockaddr_in addr;
socklen_t sin_size = sizeof(struct sockaddr_in);
uint8_t *inbuf = calloc(0x10000, 1);
uint8_t *outbuf = calloc(0x10000, 1);
int sock = _start_debug_server();
int clientfd = accept(sock, (struct sockaddr*)&addr, &sin_size);
shoe.dbg.connected = 1;
shoe.dbg.mode = DEBUG_MODE_RUNNING;
return NULL;
}
void *debug_cpu_thread (void *arg)
{
memset(&shoe.dbg, 0, sizeof(shoe.dbg));
shoe.dbg.mode = DEBUG_MODE_STOPPED;
pthread_t server_thread_pid;
pthread_create(&server_thread_pid,
NULL,
debug_server_thread,
NULL);
/*
* The CPU only runs once the debugger is connected, and the
* emulator has started
*/
pthread_mutex_lock(&shoe.cpu_thread_lock);
while (!shoe.dbg.connected)
usleep(1000);
while (1) {
if (shoe.dbg.mode == DEBUG_MODE_RUNNING) {
if (!shoe.dbg.ignore_interrupts &&
(shoe.cpu_thread_notifications & 0xff)) {
process_pending_interrupt();
}
if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) {
continue;
}
cpu_step();
}
else if (shoe.dbg.mode == DEBUG_MODE_STOPPED)
pthread_yield_np();
else if (shoe.dbg.mode == DEBUG_MODE_STEP) {
cpu_step();
shoe.dbg.mode = DEBUG_MODE_STOPPED;
}
}
}

View File

@ -1493,14 +1493,8 @@ void begin_definitions()
/* --- FPU (68881) instructions --- */
{
inst_t *inst = new_inst("fpu_decode", "2", 1);
add_range(inst, "1111 001 xxx xxxxxx");
no_ea(inst);
}
/* { // all other fpu ops
inst_t *inst = new_inst("fpu_decode", "2", 1);
{ // all other fpu ops
inst_t *inst = new_inst("fpu_other", "2", 1);
add_range(inst, "1111 001 000 MMMMMM");
ea_all(inst);
}
@ -1511,9 +1505,30 @@ void begin_definitions()
ea_data_alterable(inst);
}
{ // FDBcc
inst_t *inst = new_inst("fdbcc", "2", 1);
add_range(inst, "1111 001 001 001xxx");
no_ea(inst);
}
{ // FTRAPcc
inst_t *inst = new_inst("ftrapcc", "2", 1);
add_range(inst, "1111 001 001 111 010");
add_range(inst, "1111 001 001 111 011");
add_range(inst, "1111 001 001 111 100");
no_ea(inst);
}
{ // FBcc
inst_t *inst = new_inst("fbcc", "2", 1);
add_range(inst, "1111 001 01x xxxxxx");
sub_range(inst, "1111 001 010 000000"); // fnop
no_ea(inst);
}
{ // fnop
inst_t *inst = new_inst("fnop", "2", 1);
add_range(inst, "1111 001 010 000000");
no_ea(inst);
}
@ -1529,7 +1544,7 @@ void begin_definitions()
add_range(inst, "1111 001 101 MMMMMM");
ea_control(inst);
ea_add_mode(inst, EA_011);
}*/
}
}

View File

@ -47,6 +47,12 @@ uint16_t dis_next_word (void)
return next;
}
uint32_t dis_next_long (void)
{
uint32_t next = dis_next_word();
return (next << 16) | dis_next_word();
}
//
// EA decoder routines
//
@ -67,7 +73,7 @@ void disass_ea_extended (char *buf, uint8_t mr)
// z == base displacement size
// I == index/indirect selection
// printf("index_reg_type=%u, index_reg=%u, index_sz=%u, scale=%u, brief/ful=%u, supress_base=%u, suppress_index=%u, base_disp_sz=%u, I=%x\n",
// slog("index_reg_type=%u, index_reg=%u, index_sz=%u, scale=%u, brief/ful=%u, supress_base=%u, suppress_index=%u, base_disp_sz=%u, I=%x\n",
// d, r, w, s, F, b, i, z, I);
if (F == 0) { // If this is the brief extension word
@ -269,8 +275,13 @@ char* decode_ea_rw (uint8_t mr, uint8_t sz)
} else if (sz == 2) {
sprintf(str, "0x%04x", ext);
} else {
const uint16_t ext2 = dis_next_word();
sprintf(str, "0x%04x%04x", ext, ext2);
uint32_t i;
assert((sz & 1) == 0);
sprintf(str, "0x%04x", ext);
for (i=2; i<sz; i+=2) {
const uint16_t ext2 = dis_next_word();
sprintf(str + strlen(str), "%04x", ext2);
}
}
return str;
}
@ -290,7 +301,8 @@ char* decode_ea_addr (uint8_t mr)
char *str = dis.ea_str_internal + dis.ea_last_pos_internal;
dis.ea_last_pos_internal = (dis.ea_last_pos_internal+256) % 1024;
switch (mode) {
case 0 ... 1: { // Data/address register direct mode
case 0:
case 1: { // Data/address register direct mode
sprintf(str, "???");
return str;
}
@ -299,11 +311,11 @@ char* decode_ea_addr (uint8_t mr)
return str;
}
case 3: { // address register indirect with postincrement mode
sprintf(str, "???");
sprintf(str, "(a%u)+", reg);
return str;
}
case 4: { // address register indirect with predecrement mode
sprintf(str, "???");
sprintf(str, "-(a%u)", reg);
return str;
}
case 5: { // address register indirect with displacement mode
@ -537,7 +549,18 @@ void dis_eori_to_sr() {
}
void dis_movep() {
sprintf(dis.str, "movep???");
const int16_t disp = dis_next_word();
~decompose(dis_op, 0000 ddd 1 s r 001 aaa);
if (r) { // reg -> mem
sprintf(dis.str, "movep.%c d%u,%s0x%x(a%u)", "wl"[s], d,
(disp >= 0) ? "" : "-", abs(disp), a);
}
else { // mem -> reg
sprintf(dis.str, "movep.%c %s0x%x(a%u),d%u", "wl"[s],
(disp >= 0) ? "" : "-", abs(disp), a, d);
}
}
void dis_bfextu() {
@ -754,7 +777,7 @@ void dis_long_div () {
sprintf(dest, "d%u", R);
if (Q != R)
sprintf(dest+2, ":d%u", Q);
sprintf(dis.str, "div%c%s.l %s,%s", "us"[u], s?"":"l", decode_ea_rw(M, 4), dest);
sprintf(dis.str, "div%c%s.l %s,%s", "us"[u], s?"l":"", decode_ea_rw(M, 4), dest);
}
void dis_cmpm () {
@ -794,24 +817,40 @@ void dis_movea () {
}
void dis_move () {
~decompose(dis_op, 00 ss RRR MMM oooooo)
~decompose(dis_op, 00 ss RRR MMM oooooo);
// (oooooo = source EA), (MMMRRR = dest EA)
const uint8_t sizes[3] = {1, 4, 2};
const char *sourceStr = decode_ea_rw(o, sizes[s-1]);
const char *destStr = decode_ea_rw((M<<3)|R, sizes[s-1]);
sprintf(dis.str, "move.%c %s,%s", "blw"[s-1], sourceStr, destStr);
const uint8_t sizes[4] = {0, 1, 4, 2};
const char *sourceStr = decode_ea_rw(o, sizes[s]);
const char *destStr = decode_ea_rw((M<<3)|R, sizes[s]);
sprintf(dis.str, "move.%c %s,%s", "?blw"[s], sourceStr, destStr);
}
void dis_move_d_to_d () {
sprintf(dis.str, "move_d_to_d ???");
~decompose(dis_op, 00 ss DDD 000 000 SSS);
sprintf(dis.str, "move.%c d%u,d%u", "?blw"[s], S, D);
}
void dis_move_to_d () {
sprintf(dis.str, "move_to_d ???");
~decompose(dis_op, 00 ss rrr 000 MMMMMM);
const uint8_t sizes[4] = {0, 1, 4, 2};
sprintf(dis.str,
"move.%c %s,d%u",
"?blw"[s],
decode_ea_rw(M, sizes[s]),
r);
}
void dis_move_from_d () {
sprintf(dis.str, "move_from_d ???");
~decompose(dis_op, 00 ss RRR MMM 000 rrr);
const uint8_t sizes[4] = {0, 1, 4, 2};
sprintf(dis.str,
"move.%c d%u,%s",
"?blw"[s],
r,
decode_ea_rw((M<<3) | R, sizes[s]));
}
void dis_scc () {
@ -1077,11 +1116,16 @@ void dis_pea() {
}
void dis_nbcd() {
sprintf(dis.str, "nbcd???");
~decompose(dis_op, 0100 1000 00 MMMMMM);
sprintf(dis.str, "nbcd %s", decode_ea_rw(M, 1));
}
void dis_sbcd() {
sprintf(dis.str, "sbcd???");
~decompose(dis_op, 1000 yyy 10000 r xxx);
if (r)
sprintf(dis.str, "sbcd d%u,d%u", x, y);
else
sprintf(dis.str, "sbcd -(a%u),-(a%u)", x, y);
}
void dis_pack() {
@ -1103,7 +1147,8 @@ void dis_divs() {
}
void dis_bkpt() {
sprintf(dis.str, "bkpt???");
~decompose(dis_op, 0100 1000 0100 1 vvv);
sprintf(dis.str, "bkpt %u", v);
}
void dis_swap() {
@ -1384,23 +1429,43 @@ void dis_move16 () {
}
void dis_rtm () {
sprintf(dis.str, "rtm???");
~decompose(dis_op, 0000 0110 1100 d rrr);
sprintf(dis.str, "rtm %c%u", "da"[d], r);
}
void dis_tas () {
sprintf(dis.str, "tas???");
~decompose(dis_op, 1000 rrr 011 MMMMMM);
sprintf(dis.str, "tas.b %s", decode_ea_rw(M, 1));
}
void dis_trapcc() {
sprintf(dis.str, "trapcc???");
~decompose(dis_op, 0101 cccc 11111 ooo);
const char *condition_names[16] = {
"t", "ra", "hi", "ls", "cc", "cs", "ne", "eq",
"vc", "vs", "pl", "mi", "ge", "lt", "gt", "le"
};
uint32_t data;
switch (o) {
case 2:
data = dis_next_word();
sprintf(dis.str, "trap%s.w 0x%04x", condition_names[c], data);
break;
case 3:
data = dis_next_long();
sprintf(dis.str, "trap%s.l 0x%08x", condition_names[c], data);
break;
case 4:
sprintf(dis.str, "trap%s", condition_names[c]);
break;
}
}
void dis_trapv() {
sprintf(dis.str, "trapv???");
sprintf(dis.str, "trapv");
}
void dis_mc68851_decode() {
~decompose(dis_op, 1111 000 a b c MMMMMM);
@ -1468,6 +1533,203 @@ void dis_mc68851_decode() {
assert(!"never get here");
}
const char *_fcc_names[32] = {
"f", "eq", "ogt", "oge", "olt", "ole", "ogl", "or",
"un", "ueq", "ugt", "uge", "ult", "ule", "ne", "t",
"sf", "seq", "gt", "ge", "lt", "le", "gl", "gle",
"ngle", "ngl", "nle", "nlt", "nge", "ngt", "sne", "st"
};
static void dis_fmove_to_mem(uint16_t ext)
{
const uint8_t _format_sizes[8] = {4, 4, 12, 12, 2, 8, 1, 12};
~decompose(dis_op, 1111 001 000 MMMMMM);
~decompose(ext, 011 fff sss kkkkkkk);
sprintf(dis.str, "fmove.%c", "lsxpwdbp"[f]);
if (f == 3)
sprintf(dis.str + strlen(dis.str), "{#%u}", k);
else
sprintf(dis.str + strlen(dis.str), "{d%u}", k >> 4);
sprintf(dis.str + strlen(dis.str), " fp%u,%s", s,
decode_ea_rw(M, _format_sizes[f]));
}
static void dis_fmovem_control(uint16_t ext)
{
~decompose(dis_op, 1111 001 000 MMMMMM);
~decompose(ext, 10 d CSI 0000 000000);
sprintf(dis.str, "fmovem.l ");
const uint16_t count = C + S + I;
if (count == 0)
sprintf(dis.str + strlen(dis.str), "0,");
if (C)
sprintf(dis.str + strlen(dis.str), "fpcr%s", (count > 1)?"&":",");
if (S)
sprintf(dis.str + strlen(dis.str), "fpsr%s", ((S+I) > 1)?"&":",");
if (I)
sprintf(dis.str + strlen(dis.str), "fpiar,");
sprintf(dis.str + strlen(dis.str), "%s", decode_ea_rw(M, count * 4));
}
static void dis_fmovem(uint16_t ext)
{
~decompose(dis_op, 1111 001 000 mmmrrr);
~decompose(dis_op, 1111 001 000 MMMMMM);
~decompose(ext, 11 d ps 000 LLLLLLLL); // Static register mask
~decompose(ext, 11 0 00 000 0yyy0000); // Register for dynamic mode
if (s) { // if dynamic mode
if (d) // mem -> reg
sprintf(dis.str, "fmovem.x %s,a%u", decode_ea_rw(M, 4), y);
else
sprintf(dis.str, "fmovem.x a%u,%s", y, decode_ea_rw(M, 4));
return;
}
uint32_t i, count=0;
char list[64] = "";
uint8_t oldmask = L, mask = L;
// for predecrement mode, the mask is reversed
if (m == 4) {
for (i=0; i<8; i++) {
mask <<= 1;
mask |= (oldmask & 1);
oldmask >>= 1;
count++;
}
}
for (i=0; i<8; i++) {
if (mask & 0x80)
sprintf(list + strlen(list), "fp%u.", i);
mask <<= 1;
}
if (d) // mem -> reg
sprintf(dis.str, "fmovem.x %s,%s", decode_ea_rw(M, 4), list);
else // reg -> mem
sprintf(dis.str, "fmovem.x %s,%s", list, decode_ea_rw(M, 4));
}
void dis_fscc () {
~decompose(dis_op, 1111 001 001 MMMMMM);
const uint16_t ext = dis_next_word();
~decompose(ext, 0000 0000 00 0ccccc);
sprintf(dis.str, "fs%s.b %s", _fcc_names[c], decode_ea_rw(M, 1));
}
void dis_fbcc () {
~decompose(dis_op, 1111 001 01s 0ccccc);
uint32_t new_pc = dis.orig_pc + 2;
if (s == 0) {
const int16_t tmp = dis_next_word();
new_pc += tmp;
}
else
new_pc += dis_next_long();
sprintf(dis.str, "fb%s.%c *0x%08x", _fcc_names[c], "wl"[s], new_pc);
}
void dis_fsave () {
~decompose(dis_op, 1111 001 100 MMMMMM);
sprintf(dis.str, "fsave %s", decode_ea_addr(M));
}
void dis_frestore () {
~decompose(dis_op, 1111 001 101 MMMMMM);
sprintf(dis.str, "frestore %s", decode_ea_addr(M));
}
void dis_fpu_other () {
~decompose(dis_op, 1111 001 000 MMMMMM);
const uint16_t ext = dis_next_word();
~decompose(ext, ccc xxx yyy eeeeeee);
switch (c) {
case 0: // Reg to reg
dis_fmath(dis_op, ext, dis.str);
return;
case 1: // unused
sprintf(dis.str, "f???");
return;
case 2: // Memory->reg & movec
dis_fmath(dis_op, ext, dis.str);
return;
case 3: // reg->mem
dis_fmove_to_mem(ext);
return;
case 4: // mem -> sys ctl registers
case 5: // sys ctl registers -> mem
dis_fmovem_control(ext);
return;
case 6: // movem to fp registers
case 7: // movem to memory
dis_fmovem(ext);
return;
}
}
void dis_fdbcc () {
~decompose(dis_op, 1111 001 001 001 rrr);
const uint16_t ext = dis_next_word();
~decompose(ext, 0000 0000 00 0ccccc);
const int16_t disp = dis_next_word();
// FIXME: 68kprm is helpfully unclear about which address
// to add the displacement. Based on cpDBcc, dbcc, bcc, and fbcc,
// I'm guessing it starts at the address *of* the displacement
const uint32_t newpc = dis.orig_pc + 4 + disp;
sprintf(dis.str, "fdb%s d%u,0x%08x", _fcc_names[c], r, newpc);
}
void dis_ftrapcc () {
~decompose(dis_op, 1111 001 001 111 ooo);
const uint16_t ext = dis_next_word();
~decompose(ext, 0000 0000 00 0ccccc);
uint32_t data;
switch (o) {
case 2:
data = dis_next_word();
sprintf(dis.str, "ftrap%s.w 0x%04x", _fcc_names[c], data);
break;
case 3:
data = dis_next_long();
sprintf(dis.str, "ftrap%s.l 0x%08x", _fcc_names[c], data);
break;
case 4:
sprintf(dis.str, "ftrap%s", _fcc_names[c]);
break;
default:
sprintf(dis.str, "ftrap????");
break;
}
}
void dis_fnop () {
const uint16_t ext = dis_next_word();
sprintf(dis.str, "fnop");
}
#include "dis_decoder_guts.c"
/*

862
core/ethernet.c Normal file
View File

@ -0,0 +1,862 @@
/*
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "shoebill.h"
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/select.h>
#include "ethernet_rom/rom.c"
static uint32_t compute_nubus_crc(uint8_t *rom, uint32_t len)
{
uint32_t i, sum = 0;
for (i=0; i<len; i++) {
uint8_t byte = rom[i];
if (i==(len-9) || i==(len-10) || i==(len-11) || i==(len-12))
byte = 0;
sum = (sum << 1) + (sum >> 31) + byte;
}
rom[len-9] = sum & 0xff;
rom[len-10] = (sum >> 8) & 0xff;
rom[len-11] = (sum >> 16) & 0xff;
rom[len-12] = (sum >> 24) & 0xff;
return sum;
}
#define ETHPAGE() (ctx->cr >> 6)
const char *eth_r0_reg_names[16] = {
"cr", "clda0", "clda1", "bnry",
"tsr", "ncr", "fifo", "isr",
"crda0", "crda1", "reserved1", "reserved2",
"rsr", "cntr0", "cntr1", "cntr2"
};
const char *eth_1_reg_names[16] = {
"cr", "par0", "par1", "par2",
"par3", "par4", "par5", "curr",
"mar0", "mar1", "mar2", "mar3",
"mar4", "mar5", "mar6", "mar7"
};
const char *eth_w0_reg_names[16] = {
"cr", "pstart", "pstop", "bnry",
"tpsr", "tbcr0", "tbcr1", "isr",
"rsar0", "rsar1", "rbcr0", "rbcr1",
"rcr", "tcr", "dcr", "imr"
};
// command register bit masks
enum ether_cr_masks {
cr_stp = 1<<0, // stop
cr_sta = 1<<1, // start
cr_txp = 1<<2, // transmit packet
cr_rd0 = 1<<3, // remote dma command (0)
cr_rd1 = 1<<4, // remote dma command (1)
cr_rd2 = 1<<5, // remote dma command (2)
cr_ps0 = 1<<6, // page select (0)
cr_ps1 = 1<<7, // page select (1)
};
// interrupt service register bit masks
enum ether_isr_masks {
isr_prx = 1<<0, // packet received
isr_ptx = 1<<1, // packet transmitted
isr_rxe = 1<<2, // receive error
isr_txe = 1<<3, // transmit error
isr_ovw = 1<<4, // overwrite warning
isr_cnt = 1<<5, // counter overflow
isr_rdc = 1<<6, // remote dma complete
isr_rst = 1<<7, // reset status (not actually an interrupt)
};
// interrupt mask register bit masks
enum ether_imr_masks {
imr_pxre = 1<<0, // packet received interrupt enable
imr_ptxe = 1<<1, // packet transmitted interrupt enable
imr_rxee = 1<<2, // receive error interrupt enable
imr_txee = 1<<3, // transmit error interrupt enable
imr_ovwe = 1<<4, // overwrite warning interrupt enable
imr_cnte = 1<<5, // counter overflow interrupt enable
imr_rdce = 1<<6, // dma complete
};
// receive configuration register bit masks
enum ether_rcr_masks {
rcr_sep = 1<<0, // save error packets
rcr_ar = 1<<1, // accept runt packets
rcr_ab = 1<<2, // accept broadcast
rcr_am = 1<<3, // accept multicast
rcr_pro = 1<<4, // promiscuous physical
rcr_mon = 1<<5, // monitor mode
};
// transmit configuration register bit masks
enum ether_tcr_masks {
tcr_crc = 1<<0, // inhibit crc
tcr_lb0 = 1<<1, // encoded loopback control (0)
tcr_lb1 = 1<<2, // encoded loopback control (1)
tcr_atd = 1<<3, // auto transmit disable
tcr_ofst = 1<<4, // collision offset enable
};
// data configuration register bit masks
enum ether_dcr_masks {
dcr_wts = 1<<0, // word transfer select
dcr_bos = 1<<1, // byte order select
dcr_las = 1<<2, // long address select
dcr_ls = 1<<3, // loopback select
dcr_arm = 1<<4, // auto-initialize remote
dcr_ft0 = 1<<5, // fifo threshhold select (0)
dcr_ft1 = 1<<6, // fifo threshhold select (1)
};
// receive status register
enum ether_rsr_masks {
rsr_prx = 1<<0, // packet received intact
rsr_crc = 1<<1, // crc error
rsr_fae = 1<<2, // frame alignment error
rsr_fo = 1<<3, // fifo overrun
rsr_mpa = 1<<4, // missed packet
rsr_phy = 1<<5, // physical/multicast address (0->phys, 1->multi)
rsr_dis = 1<<6, // received disabled
rsr_dfr = 1<<7, // deferring
};
static void _nubus_interrupt(uint8_t slotnum)
{
shoe.via[1].rega_input &= 0x3f & ~(1 << (slotnum - 9));
via_raise_interrupt(2, IFR_CA1);
}
static void _clear_nubus_interrupt(uint8_t slotnum)
{
shoe.via[1].rega_input |= (1 << (slotnum - 9));
}
/*
* How many recv buffers (256-byte buffers) does the
* given number of bytes require?
*/
#define eth_recv_required_bufs(a) ({ \
const uint32_t sz = (a); \
(sz >> 8) + ((sz & 0xff) != 0); \
})
/*
* The number of 256-byte buffers available for writing
* in the receive buffer (between ctx->curr and ctx->bnry)
*/
#define eth_recv_free_bufs() ({ \
const uint8_t boundary = (ctx->bnry >= ctx->pstop) ? ctx->pstart : ctx->bnry; \
const uint8_t curr = (ctx->curr >= ctx->pstop) ? ctx->pstart : ctx->curr; \
const uint8_t total_bufs = ctx->pstop - ctx->pstart; \
uint8_t f; \
if (curr == boundary) \
f = 0; /* This shouldn't happen */ \
else if (curr > boundary) \
f = (ctx->pstop - curr) + (boundary- ctx->pstart) - 1; \
else \
f = boundary - curr - 1; \
f; \
})
void *_ethernet_receiver_thread(void *arg)
{
const uint8_t multicast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)arg;
uint8_t *buf = malloc(4096);
assert(buf);
// While nubus_ethernet_destroy() hasn't been called
while (!ctx->teardown) {
struct timeval tv;
fd_set fdset;
int ret;
uint32_t i;
FD_ZERO(&fdset);
FD_SET(ctx->tap_fd, &fdset);
tv.tv_sec = 0;
tv.tv_usec = 100000;
ret = select(ctx->tap_fd + 1, &fdset, NULL, NULL, &tv);
assert(ret != -1);
if (FD_ISSET(ctx->tap_fd, &fdset)) {
FD_CLR(ctx->tap_fd, &fdset);
/*
* Read in the next packet, leaving space for the 4 byte
* header
*/
int actual_packet_length = read(ctx->tap_fd, buf + 4, 4092);
slog("ethernet: received packet bnry=%x curr=%x pstart=%x pstop=%x cr=%x ret=%d frame=0x%02x%02x\n",
ctx->bnry, ctx->curr, ctx->pstart, ctx->pstop, ctx->cr, actual_packet_length,
buf[0x10], buf[0x11]);
/*
* If it's a bogus packet length, reject it
* (what's the actual minimum allowable packet length?)
*/
if (actual_packet_length <= 12) {
slog("ethernet: too small len\n");
continue;
}
/* I'm sure A/UX can't handle > 2kb packets */
if (actual_packet_length > 2048) {
slog("ethernet: too high len\n");
continue;
}
/* If it's neither multicast nor addressed to us, reject it */
if ((memcmp(buf + 4, ctx->ethernet_addr, 6) != 0) &&
(memcmp(buf + 4, multicast_addr, 6) != 0)) {
slog("ethernet: bad address\n");
continue;
}
/* A/UX seems to expect a minimum packet length (60 bytes??) */
if (actual_packet_length < 60)
actual_packet_length = 60;
/* The number of bytes to write + the 4 byte header */
const uint32_t received_bytes = actual_packet_length + 4;
pthread_mutex_lock(&ctx->lock);
/*
* If the card isn't initialized yet, just drop the packet
*/
if (ctx->cr & cr_stp) {
slog("ethernet: uninit\n");
pthread_mutex_unlock(&ctx->lock);
continue;
}
/*
* If the receive-register state is bogus, just drop the
* packet
*/
if ((ctx->pstop <= ctx->pstart) ||
(ctx->curr < ctx->pstart) ||
(ctx->bnry < ctx->pstart) ||
(ctx->pstop > 0x40) ||
(ctx->pstart == 0)) {
// This shouldn't happen if the card is initialized
assert(!"ethernet: receive register state is bogus");
pthread_mutex_unlock(&ctx->lock);
continue;
}
slog("ethernet: success, req=%u free=%u\n", eth_recv_required_bufs(received_bytes), eth_recv_free_bufs());
/*
* If there isn't enough buffer space to store the packet,
* block until ctx->bnry is modified.
*/
const uint8_t required_bufs = eth_recv_required_bufs(received_bytes);
while (eth_recv_free_bufs() < required_bufs) {
pthread_mutex_unlock(&ctx->lock);
if (ctx->teardown)
goto bail;
printf("ethernet: sleeping\n");
usleep(50); // FIXME: use a cond variable here
pthread_mutex_lock(&ctx->lock);
}
/* Roll around ctx->curr if necessary */
if (ctx->curr >= ctx->pstop)
ctx->curr = ctx->pstart;
const uint8_t orig_curr = ctx->curr;
/* Copy the packet to card RAM */
for (i = 0; i < required_bufs; i++) {
assert(ctx->curr != ctx->bnry); // this can't happen if we did our math right earlier
uint8_t *ptr = &ctx->ram[ctx->curr * 256];
memcpy(ptr, &buf[i * 256], 256);
ctx->curr++;
if (ctx->curr >= ctx->pstop)
ctx->curr = ctx->pstart;
}
assert(ctx->curr != ctx->bnry); // this can't happen if we did our math right earlier
/* The packet was received intact */
ctx->rsr = rsr_prx;
/* Fill in the 4 byte packet header */
ctx->ram[orig_curr * 256 + 0] = ctx->rsr;
ctx->ram[orig_curr * 256 + 1] = ctx->curr;
ctx->ram[orig_curr * 256 + 2] = received_bytes & 0xff; // low byte
ctx->ram[orig_curr * 256 + 3] = (received_bytes >> 8) & 0xff; // high byte
/* If the prx interrupt is enabled, interrupt */
if (ctx->imr & imr_pxre) {
ctx->isr |= isr_prx;
_nubus_interrupt(ctx->slotnum);
}
slog("ethernet: received packet (len=%d)\n", ret);
pthread_mutex_unlock(&ctx->lock);
}
}
bail:
free(buf);
return NULL;
}
/*
_Bool sent_arp_response = 0;
const uint8_t arp_response[60] = {
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // router's MAC address
0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd, // card MAC address
0x08, 0x06, // ARP frame
0x00, 0x01, // Ethernet
0x08, 0x00, // IP
0x06, // MAC size
0x04, // IP size
0x00, 0x02, // reply
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // router's MAC address
192, 168, 2, 1, // router IP address
0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd, // card MAC address
192, 168, 2, 100, // card IP address
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // padding
};
void _test_write_packet(shoebill_card_ethernet_t *ctx)
{
slog("ethernet: writing packet to curr=0x%02x\n", ctx->curr);
uint8_t *ptr = &ctx->ram[ctx->curr * 256];
// The packet was received intact
ctx->rsr = rsr_prx;
// The next packet address is the next 256 byte chunk
ctx->curr += 1;
ptr[0] = ctx->rsr;
ptr[1] = ctx->curr; // next packet ptr (8 bit)
ptr[2] = 60; // low byte of the packet size
ptr[3] = 0; // high byte of the packet size
memcpy(ptr + 4, arp_response, 60); // the packet
if (ctx->imr & imr_pxre)
ctx->isr |= isr_prx;
_nubus_interrupt(ctx->slotnum);
}
*/
void *_ethernet_sender_thread(void *arg)
{
shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)arg;
slog("ethernet: ethernet_sender_thread starts...\n");
// While nubus_ethernet_destroy() hasn't been called
while (!ctx->teardown) {
struct timeval now;
struct timespec later;
int ret;
// Wait on the condition variable, with a timeout of 100ms
// slog("ethernet: locking cond mutex...\n");
pthread_mutex_lock(&ctx->sender_cond_mutex);
// slog("ethernet: locked cond mutex...\n");
gettimeofday(&now, NULL);
later.tv_sec = now.tv_sec;
later.tv_nsec = (now.tv_usec * 1000) + (1000000000 / 10);
if (later.tv_nsec >= 1000000000) {
later.tv_nsec -= 1000000000;
later.tv_sec++;
}
// slog("ethernet: waiting on cond...\n");
pthread_cond_timedwait(&ctx->sender_cond,
&ctx->sender_cond_mutex,
&later);
assert(pthread_mutex_unlock(&ctx->sender_cond_mutex) == 0);
// Only proceed if there's a packet ready to send
if (!ctx->send_ready)
continue;
slog("ethernet: sender thread wakes up...\n");
ctx->send_ready = 0;
// --- Send the packet here ---
assert(ctx->tbcr <= 2048); // sanity check the packet len
assert(ctx->tbcr >= 42);
ret = write(ctx->tap_fd, ctx->ram, ctx->tbcr);
if (ret != ctx->tbcr) {
slog("ethernet: write() returned %d, not %d errno=%d\n", ret, ctx->tbcr, errno);
}
// Lock the ethernet context (we're going to manipulate the ethernet registers)
pthread_mutex_lock(&ctx->lock);
// indicate that the packet has been sent
ctx->cr &= ~cr_txp; // clear the command register txp bit
ctx->isr |= isr_ptx; // interrupt status: packet transmitted with no errors
// the "packet transmitted" interrupt really should be enabled
if (ctx->imr & imr_ptxe) {
_nubus_interrupt(ctx->slotnum);
slog("ethernet: sender: sending interrupt to slot %u\n", ctx->slotnum);
}
assert(pthread_mutex_unlock(&ctx->lock) == 0);
}
return NULL;
}
void nubus_ethernet_init(void *_ctx, uint8_t slotnum, uint8_t ethernet_addr[6], int tap_fd)
{
shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)_ctx;
memset(ctx, 0, sizeof(shoebill_card_ethernet_t));
memcpy(ctx->rom, _ethernet_rom, 4096);
memcpy(ctx->ethernet_addr, ethernet_addr, 6);
memcpy(ctx->rom, ethernet_addr, 6);
ctx->rom[6] = 0x00;
ctx->rom[7] = 0x00;
ctx->slotnum = slotnum; // so the threads know which slot this is
pthread_mutex_init(&ctx->lock, NULL);
pthread_cond_init(&ctx->sender_cond, NULL);
pthread_mutex_init(&ctx->sender_cond_mutex, NULL);
pthread_create(&ctx->sender_pid, NULL, _ethernet_sender_thread, ctx);
pthread_create(&ctx->receiver_pid, NULL, _ethernet_receiver_thread, ctx);
/*
* The first 8 bytes contain the MAC address
* and aren't part of the CRC
*/
compute_nubus_crc(&ctx->rom[8], 4096 - 8);
ctx->cr |= cr_stp; // "STP powers up high"
ctx->isr |= isr_rst; // I presume ISR's RST powers up high too
/* Platform-specific tap code */
ctx->tap_fd = tap_fd;
}
void nubus_ethernet_destroy_func(uint8_t slotnum)
{
shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)shoe.slots[slotnum].ctx;
ctx->teardown = 1;
pthread_join(ctx->sender_pid, NULL);
pthread_join(ctx->receiver_pid, NULL);
pthread_mutex_destroy(&ctx->lock);
pthread_mutex_destroy(&ctx->sender_cond_mutex);
pthread_cond_destroy(&ctx->sender_cond);
}
uint32_t nubus_ethernet_read_func(const uint32_t rawaddr,
const uint32_t size,
const uint8_t slotnum)
{
shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)shoe.slots[slotnum].ctx;
uint32_t result = 0;
pthread_mutex_lock(&ctx->lock);
switch ((rawaddr >> 16) & 0xf) {
case 0xd: { // ram
const uint16_t addr = rawaddr & 0x3fff;
uint8_t *ram = ctx->ram;
if (size == 1)
result = ram[addr];
else if (size == 2) {
result = ram[addr] << 8;
result |= ram[(addr+1) & 0x3fff];
}
else
assert(!"read: bogus size");
// slog("ethernet: reading from ram addr 0x%x sz=%u ", addr, size);
goto done;
}
case 0xe: { // registers
// For some reason, the register address bits are all inverted
const uint8_t reg = 15 ^ ((rawaddr >> 2) & 15);
assert(size == 1);
{
const char *name = "???";
if (ETHPAGE() == 0) name = eth_r0_reg_names[reg];
else if (ETHPAGE() == 1) name = eth_1_reg_names[reg];
slog("ethernet: reading from register %u (%s) (raw=0x%x) pc=0x%x ", reg, name, rawaddr, shoe.pc);
}
if (reg == 0) { // command register (exists in all pages)
result = ctx->cr;
goto done;
} else if (ETHPAGE() == 0) { // page 0
switch (reg) {
default:
assert(!"never get here");
goto done;
case 1: // clda0 (current local dma address 0)
goto done;
case 2: // clda1 (current local dma address 1)
goto done;
case 3: // bnry (boundary pointer)
result = ctx->bnry;
goto done;
case 4: // tsr (transmit status)
goto done;
case 5: // ncr (number of collisions)
goto done;
case 6: // fifo
goto done;
case 7: // isr (interrupt status register)
result = ctx->isr;
// test test test
// if we're reading isr_ptx for the first time,
// send a test packet (but never again)
/*if ((result & isr_ptx) && (!sent_arp_response)) {
sent_arp_response = 1;
_test_write_packet(ctx);
}*/
goto done;
case 8: // crda0 (current remote DMA address 0)
goto done;
case 9: // crda1 (current remote DMA address 1)
goto done;
case 10: // reserved 1
assert(!"read to reserved 1");
goto done;
case 11: // reserved 2
assert(!"read to reserved 2");
goto done;
case 12: // rsr (receive status register)
result = ctx->rsr;
goto done;
case 13: // cntr0 (tally counter 0 (frame alignment errors))
goto done;
case 14: // cntr1 (tally counter 1 (crc errors))
goto done;
case 15: // cntr2 (tally counter 2 (missed packet errors))
goto done;
}
} else if (ETHPAGE() == 1) { // page 1
switch (reg) {
default:
assert(!"never get here");
goto done;
case 1: // par (physical address)
case 2:
case 3:
case 4:
case 5:
case 6:
result = ctx->par[reg - 1];
goto done;
case 7: // curr (current page register)
result = ctx->curr;
goto done;
case 8: // mar (multicast address)
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
result = ctx->mar[reg - 8];
goto done;
}
} else
assert(!"read: Somebody accessed page 2 or 3!");
assert(!"never get here");
goto done;
}
case 0xf: { // rom
// Byte lanes = 0101 (respond to shorts)
// respond to (addr & 3 == 0) and (addr & 3 == 2)
// xxxx00 xxxx10
if ((rawaddr & 1) == 0)
result = ctx->rom[(rawaddr >> 1) % 4096];
slog("ethernet: reading from rom addr=%x ", rawaddr);
goto done;
}
default: // Not sure what happens when you access a different addr
assert(!"read: unknown ethernet register");
}
done:
pthread_mutex_unlock(&ctx->lock);
slog("result = 0x%x\n", result);
// slog("ethernet: reading 0x%x sz=%u from addr 0x%x\n", result, size, rawaddr);
return result;
}
void nubus_ethernet_write_func(const uint32_t rawaddr,
const uint32_t size,
const uint32_t data,
const uint8_t slotnum)
{
shoebill_card_ethernet_t *ctx = (shoebill_card_ethernet_t*)shoe.slots[slotnum].ctx;
uint32_t i;
pthread_mutex_lock(&ctx->lock);
switch ((rawaddr >> 16) & 0xf) {
case 0xd: { // ram
const uint16_t addr = rawaddr & 0x3fff;
uint8_t *ram = ctx->ram;
if (size == 1)
ram[addr] = data;
else if (size == 2) {
ram[addr] = data >> 8;
ram[(addr+1) & 0x3fff] = data & 0xff;
}
else
assert(!"write: bogus size");
// slog("ethernet: writing 0x%x sz=%u to ram addr 0x%x\n", data, size, addr);
goto done;
}
case 0xe: { // registers
// For some reason, the register address bits are all inverted
const uint8_t reg = 15 ^ ((rawaddr >> 2) & 15);
assert(size == 1);
{
const char *name = "???";
if (ETHPAGE() == 0) name = eth_w0_reg_names[reg];
else if (ETHPAGE() == 1) name = eth_1_reg_names[reg];
slog("ethernet: writing 0x%02x to register %u (%s) (rawaddr=0x%x) pc=0x%x\n", data, reg, name, rawaddr, shoe.pc);
}
if (reg == 0) { // command register (exists in all pages)
// If we're setting TXP, wake up the sender thread
if (((ctx->cr & cr_txp) == 0) &&
((data & cr_txp) != 0)) {
ctx->send_ready = 1;
assert(pthread_mutex_lock(&ctx->sender_cond_mutex) == 0);
assert(pthread_cond_signal(&ctx->sender_cond) == 0);
assert(pthread_mutex_unlock(&ctx->sender_cond_mutex) == 0);
}
// if we're setting STA, clear isr_rst
if (data & cr_sta)
ctx->isr &= ~isr_rst;
// FIXME: if we're setting STP, then we probably need to set isr_rst
ctx->cr = data;
goto done;
} else if (ETHPAGE() == 0) { // page 0
switch (reg) {
default:
assert(!"never get here");
goto done;
case 1: // pstart (page start)
ctx->pstart = data;
goto done;
case 2: // pstop (page stop)
ctx->pstop = data;
goto done;
case 3: // bnry (boundary pointer)
ctx->bnry = data;
goto done;
case 4: // tpsr (transmit page start address)
ctx->tpsr = data;
goto done;
case 5: // tbcr0 (transmit byte count 0)
ctx->tbcr = (ctx->tbcr & 0xff00) | data;
goto done;
case 6: // tbcr1 (transmit byte count 1)
ctx->tbcr = (ctx->tbcr & 0x00ff) | (data<<8);
goto done;
case 7: { // isr (interrupt status)
// writing 1's clears the bits in the ISR
uint8_t mask = data & 0x7f; // but not the RST bit
ctx->isr &= ~mask;
/*
* If there are packets yet to be processed,
* then continue to assert the isr_prx bit
*/
uint8_t inc_boundary = ctx->bnry + 1;
if (inc_boundary >= ctx->pstop)
inc_boundary = ctx->pstart;
if (ctx->curr != inc_boundary)
ctx->isr |= isr_prx;
/*
* If prx and ptx are no longer asserted,
* then we may clear the nubus interrupt.
*/
if (((ctx->isr & (isr_prx | isr_ptx)) == 0) &&
((ctx->cr & cr_stp) == 0))
_clear_nubus_interrupt(slotnum);
goto done;
}
case 8: // rsar0 (remote start address 0)
goto done;
case 9: // rsar1 (remote start address 1)
goto done;
case 10: // rbcr0 (remote byte count 0)
goto done;
case 11: // rbcr1 (remote byte count 1)
goto done;
case 12: // rcr (receive configuration)
ctx->rcr = data;
goto done;
case 13: // tcr (transmit configuration)
ctx->tcr = data;
goto done;
case 14: // dcr (data configuration)
ctx->dcr = data;
goto done;
case 15: // imr (interrupt mask)
ctx->imr = data & 0x7f;
goto done;
}
} else if (ETHPAGE() == 1) { // page 1
switch (reg) {
default:
assert(!"never get here");
goto done;
case 1: // par (physical address)
case 2:
case 3:
case 4:
case 5:
case 6:
ctx->par[reg - 1] = data;
goto done;
case 7: // curr (current page register)
ctx->curr = data;
goto done;
case 8: // mar (multicast address)
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
ctx->mar[reg - 8] = data;
goto done;
}
} else
assert(!"write: Somebody accessed page 2 or 3!");
assert(!"never get here");
goto done;
}
default:
assert(!"write: unknown ethernet register");
}
done:
pthread_mutex_unlock(&ctx->lock);
return;
}

BIN
core/ethernet_rom/rom.bin Normal file

Binary file not shown.

514
core/ethernet_rom/rom.c Normal file
View File

@ -0,0 +1,514 @@
uint8_t _ethernet_rom[4096] = {
0x77, 0x6f, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00,
0x77, 0x6f, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x00, 0x64,
0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x14,
0x02, 0x00, 0x00, 0x18, 0x20, 0x00, 0x00, 0x08,
0x24, 0x00, 0x00, 0x28, 0xff, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x53, 0x68, 0x6f, 0x65, 0x62, 0x69, 0x6c, 0x6c,
0x20, 0x50, 0x68, 0x6f, 0x6e, 0x79, 0x20, 0x45,
0x74, 0x68, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x00,
0x01, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x14,
0x04, 0x00, 0x00, 0x18, 0x53, 0x68, 0x6f, 0x65,
0x62, 0x69, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
0x52, 0x65, 0x76, 0x2d, 0x31, 0x00, 0x00, 0x00,
0x42, 0x6f, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0x18,
0x0a, 0x00, 0x00, 0x30, 0x80, 0xff, 0xff, 0x7c,
0xff, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x4e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x5f, 0x45, 0x74, 0x68, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x5f, 0x53, 0x68, 0x6f,
0x65, 0x62, 0x69, 0x6c, 0x6c, 0x00, 0x00, 0x00,
0x00, 0x0d, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf0, 0x24,
0x00, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x5a, 0x93, 0x2b, 0xc7, 0x00, 0xa5
};

View File

@ -0,0 +1 @@
# File: shoebill_ether.make # Target: shoebill_ether # Sources: shoebill_ether_rom.a OBJECTS = shoebill_ether_rom.a.o shoebill_ether ÄÄ shoebill_ether.make {OBJECTS} Link ¶ {OBJECTS} ¶ -o shoebill_ether shoebill_ether_rom.a.o Ä shoebill_ether.make shoebill_ether_rom.a Asm shoebill_ether_rom.a

View File

@ -0,0 +1 @@
MACHINE MC68020 STRING C PRINT OFF ; What does this do? INCLUDE 'SysErr.a' INCLUDE 'SysEqu.a' INCLUDE 'ROMEqu.a' INCLUDE 'SlotEqu.a' INCLUDE 'TimeEqu.a' INCLUDE 'Traps.a' INCLUDE 'VideoEqu.a' INCLUDE 'QuickEqu.a' PRINT ON VideoDeclROM MAIN WITH VDPageInfo,SlotIntQElement ; EtherAddr is placed here, before sResourceDir and out of the range of the CRC ; check, because I guess it was too difficult to recompute the CRC when they were ; burning EPROMs back in 1986. ; So there's some ugly offset-accounting to make the rom seem (4096-8) bytes long, ; but still access this MAC address. myEtherAddr dc.l 'woof' dc.l 'woof' FormatBlockSize EQU 20 ROMSize EQU 4096 MyBoardID EQU $0008 ; Apple EtherTalk board ID ; ---- sResource directory ---- CategoryBoard EQU 1 CategoryEther EQU 128 sResourceDir OSLstEntry CategoryBoard, sResourceBoard OSLstEntry CategoryEther, sResourceEther DatLstEntry endOfList,0 ; ---- Board sResource ---- sResourceBoard OSLstEntry sRsrcType, boardType OSLstEntry sRsrcName, boardName DatLstEntry boardID, MyBoardID OSLstEntry vendorInfo, myVendorInfo ; No primary or secondary init needed (phew) DatLstEntry endOfList, 0 boardType DC.W CatBoard ; category DC.W TypBoard ; type DC.W 0 ; driver sw ? DC.W 0 ; driver hw ? STRING C boardName DC.L 'Shoebill Phony Ethernet' myVendorInfo OSLstEntry VendorId, myVendorID OSLstEntry RevLevel, myRevLevel OSLstEntry PartNum, myPartNum myVendorID DC.L 'Shoebill' myRevLevel DC.L 'Rev-1' myPartNum DC.L 'Bort' ; ---- Ethernet sResource ---- sResourceEther OSLstEntry sRsrcType, myEtherType OSLstEntry sRsrcName, myEtherName OSLstEntry MinorBaseOS, myMinorBaseOS dc.w $80ff ; 80 -> MAC address, $FFFFxx relative address of the MAC address dc.w -*+2 DatLstEntry endOfList, 0 myEtherType dc.w CatNetwork dc.w TypEtherNet dc.w 0 ; drvrSw, doesn't matter dc.w 1 ; drvrHw, this is DrHw3Com on Apple EtherTalk myEtherName dc.l 'Network_Ethernet_Shoebill' myMinorBaseOS DC.L $D0000 STRING C ; ---- Format block ---- ; Pad to align this structure with the end of the rom ORG ROMSize-FormatBlockSize ; Offset to sResource directory ;OSLstEntry 0,sResourceDir DC.L (sResourceDir-*)**$00FFFFFF DC.L ROMSize-8 DC.L 0 ; CRC goes here DC.B 1 ; Rom revision level DC.B AppleFormat DC.L TestPattern DC.B 0 ; Reserved DC.B $A5 ; Byte lanes 1010 0101 (LSB from CPU's perspective) ENDP END

View File

@ -30,8 +30,12 @@
#define SSW_IS_READ (1<<6)
#define SSW_DF (1<<8)
// throw the long-format (format 0xb) buss error. Mac II ROM expects to see this format for nubus bus errors.
// FIXME: nearly all the fields here are bogus
/*
* Throw the long-format (format 0xb) bus error. Mac II ROM expects to see
* this format for nubus bus errors.
*
* FIXME: nearly all the fields here are bogus
*/
void throw_long_bus_error(uint32_t addr, uint8_t is_write)
{
if (shoe.suppress_exceptions) {
@ -41,7 +45,7 @@ void throw_long_bus_error(uint32_t addr, uint8_t is_write)
// Save this value now, because lset() calls will reset it
const uint8_t fc = shoe.logical_fc;
//printf("throw_long_bus_error(): I'm throwing a LONG bus error (at pc = 0x%08x)!\n", shoe.orig_pc);
//slog("throw_long_bus_error(): I'm throwing a LONG bus error (at pc = 0x%08x)!\n", shoe.orig_pc);
// set supervisor bit
set_sr_s(1);
@ -56,12 +60,12 @@ void throw_long_bus_error(uint32_t addr, uint8_t is_write)
// fetch vector handler address
const uint32_t vector_addr = lget(shoe.vbr + vector_offset, 4);
//printf("throw_long_bus_error(): shoe.vbr=0x%08x, vector_addr=0x%08x, offending addr=0x%08x, shoe.op=0x%04x\n", shoe.vbr, vector_addr, addr, shoe.op);
//slog("throw_long_bus_error(): shoe.vbr=0x%08x, vector_addr=0x%08x, offending addr=0x%08x, shoe.op=0x%04x\n", shoe.vbr, vector_addr, addr, shoe.op);
assert(!shoe.abort); // FIXME: I can't handle another exception here
const uint16_t ssw = SSW_DF | (is_write ? 0 : SSW_IS_READ) | fc;
// Note: We're pushing frame format 0xA
// Note: We're pushing frame format 0xB
push_a7(0, 4); // internal registers, 18 words
push_a7(0, 4);
@ -93,7 +97,7 @@ void throw_long_bus_error(uint32_t addr, uint8_t is_write)
push_a7(0, 4); // instruction pipe stage B and C
push_a7(ssw, 2); // special status word
push_a7(0, 2); // internal register 1
push_a7(0xB000 | vector_offset, 2); // format word
push_a7(0xB000 | vector_offset, 2); // format word (frame format B)
push_a7(shoe.orig_pc, 4); // PC for the current instruction
push_a7(shoe.orig_sr, 2); // original status register
@ -116,7 +120,7 @@ void throw_bus_error(uint32_t addr, uint8_t is_write)
//dbg_state.running = 0;
//printf("throw_bus_error(): I'm throwing a bus error (at pc = 0x%08x)!\n", shoe.orig_pc);
//slog("throw_bus_error(): I'm throwing a bus error (at pc = 0x%08x)!\n", shoe.orig_pc);
// set supervisor bit
set_sr_s(1);
@ -131,7 +135,7 @@ void throw_bus_error(uint32_t addr, uint8_t is_write)
// fetch vector handler address
const uint32_t vector_addr = lget(shoe.vbr + vector_offset, 4);
//printf("throw_bus_error(): shoe.vbr=0x%08x, vector_addr=0x%08x, offending addr=0x%08x, shoe.op=0x%04x, a7=0x%08x", shoe.vbr, vector_addr, addr, shoe.op, shoe.a[7]);
//slog("throw_bus_error(): shoe.vbr=0x%08x, vector_addr=0x%08x, offending addr=0x%08x, shoe.op=0x%04x, a7=0x%08x", shoe.vbr, vector_addr, addr, shoe.op, shoe.a[7]);
assert(!shoe.abort); // FIXME: I can't handle another exception here
const uint16_t ssw =
@ -139,7 +143,7 @@ void throw_bus_error(uint32_t addr, uint8_t is_write)
(is_write ? 0 : SSW_IS_READ) | // read or write
fc; // a/ux 3.0.1 cares about this - the address space
//printf(" fc=%u\n", shoe.logical_fc);
//slog(" fc=%u\n", shoe.logical_fc);
// Note: We're pushing frame format 0xA
push_a7(0, 4); // internal registers 5 and 4
@ -163,12 +167,12 @@ void throw_bus_error(uint32_t addr, uint8_t is_write)
void throw_address_error()
{
printf("throw_address_error(): I'm throwing an address error!\n");
slog("throw_address_error(): I'm throwing an address error!\n");
assert(!"address error");
shoe.abort = 1;
}
static void throw_frame_zero(uint16_t sr, uint32_t pc, uint16_t vector_num)
void throw_frame_zero(uint16_t sr, uint32_t pc, uint16_t vector_num)
{
// set supervisor bit
set_sr_s(1);
@ -213,7 +217,7 @@ void throw_frame_two (uint16_t sr, uint32_t next_pc, uint32_t vector_num, uint32
void throw_illegal_instruction()
{
//printf("throw_illegal_instruction(): I'm throwing an illegal instruction exception! (shoe.pc = 0x%08x, op=0x%04x, a7=0x%08x)\n", shoe.orig_pc, shoe.op, shoe.a[7]);
//slog("throw_illegal_instruction(): I'm throwing an illegal instruction exception! (shoe.pc = 0x%08x, op=0x%04x, a7=0x%08x)\n", shoe.orig_pc, shoe.op, shoe.a[7]);
/*if ((shoe.op != 0xf010) && ((shoe.op >> 12) != 0xa))
//assert(!"illegal");
@ -227,7 +231,7 @@ void throw_illegal_instruction()
throw_frame_zero(shoe.orig_sr, shoe.orig_pc, vector_num);
/*if ((shoe.op >> 12) == 0xa) {
printf("Atrap: %s\n", atrap_names[shoe.op & 0xfff]?atrap_names[shoe.op & 0xfff]:"???");
slog("Atrap: %s\n", atrap_names[shoe.op & 0xfff]?atrap_names[shoe.op & 0xfff]:"???");
}
else
dbg_state.running = 0;*/
@ -236,7 +240,7 @@ void throw_illegal_instruction()
void throw_privilege_violation()
{
//printf("throw_privilege_violation(): I'm throwing a privilege violation exception! (shoe.orig_pc = 0x%08x op=0x%04x\n", shoe.orig_pc, shoe.op);
//slog("throw_privilege_violation(): I'm throwing a privilege violation exception! (shoe.orig_pc = 0x%08x op=0x%04x\n", shoe.orig_pc, shoe.op);
throw_frame_zero(shoe.orig_sr, shoe.orig_pc, 8);
// shoe.abort = 1;

View File

@ -30,16 +30,6 @@
#include <string.h>
#include "../core/shoebill.h"
#define fix_endian(x) do { \
if (ntohs(1) == 1) \
break; \
switch (sizeof(x)) { \
case 1: break; \
case 2: (x) = ntohs(x); break; \
case 4: (x) = ntohl(x); break; \
default: assert(!"bogus size"); \
}} while (0)
/* --- Disk/partition management stuff --- */
#pragma mark Disk/partition management stuff
@ -197,7 +187,7 @@ static disk_t* open_disk (const char *disk_path, char *error_str)
uint8_t block[512];
apple_partition_map_t apm;
uint32_t i;
alloc_pool_t *pool = p_new_pool();
alloc_pool_t *pool = p_new_pool(NULL);
FILE *f;
disk = p_alloc(pool, sizeof(disk_t));
@ -207,7 +197,7 @@ static disk_t* open_disk (const char *disk_path, char *error_str)
disk->error_str = error_str;
disk->path = disk_path;
f = fopen(disk_path, "r");
f = fopen(disk_path, "rb");
if (f == NULL) {
sprintf(error_str, "Can't open that path");
goto fail;
@ -240,7 +230,7 @@ static disk_t* open_disk (const char *disk_path, char *error_str)
}
}
// printf("sizeof(apple_part_map_t) = %lu\n", sizeof(apple_partition_map_t));
// slog("sizeof(apple_part_map_t) = %lu\n", sizeof(apple_partition_map_t));
// Load the partition maps
@ -269,8 +259,8 @@ static disk_t* open_disk (const char *disk_path, char *error_str)
memcpy(disk->partitions[i].name, disk->partition_maps[i].pmPartName, 32);
memcpy(disk->partitions[i].type, disk->partition_maps[i].pmPartType, 32);
printf("%u type:%s name:%s\n", i, disk->partitions[i].type, disk->partitions[i].name);
printf("bz_magic=0x%08x slice=%u\n", disk->partition_maps[i].bz.magic, disk->partition_maps[i].bz.slice);
slog("%u type:%s name:%s\n", i, disk->partitions[i].type, disk->partitions[i].name);
slog("bz_magic=0x%08x slice=%u\n", disk->partition_maps[i].bz.magic, disk->partition_maps[i].bz.slice);
}
return disk;
@ -332,7 +322,7 @@ static int32_t find_root_partition_number(disk_t *disk, uint8_t clus_num)
partition_t *part = &disk->partitions[i];
apple_partition_map_t *apm = &disk->partition_maps[i];
// printf("%u magic=0x%08x root=%u type=%s\n", i, apm->bz.magic, apm->bz.root, part->type);
// slog("%u magic=0x%08x root=%u type=%s\n", i, apm->bz.magic, apm->bz.root, part->type);
if (apm->bz.magic != 0xabadbabe)
continue;
@ -417,7 +407,7 @@ static uint8_t svfs_read_block(svfs_t *mount, uint8_t *block, uint32_t blockno)
const uint32_t start_sector = blockno * sectors_per_block;
uint32_t i;
// printf("sectors_per_block = %u, start_sector=%u\n", sectors_per_block, start_sector);
// slog("sectors_per_block = %u, start_sector=%u\n", sectors_per_block, start_sector);
for (i=0; i<sectors_per_block; i++) {
part_get_block(mount->part, &block[i * 512], start_sector+i);
@ -637,9 +627,9 @@ svfs_inode_t* svfs_traverse_path(svfs_t *mount, const char *_path)
for (elem = strtok_r(path, "/", &last);
elem;
elem = strtok_r(NULL, "/", &last)) {
//printf("elem = [%s]\n", elem);
//slog("elem = [%s]\n", elem);
const uint32_t num_entries = inode->size / 16;
//printf("inode size = %u\n", inode->size);
//slog("inode size = %u\n", inode->size);
svfs_dir_entry_t *dir = (svfs_dir_entry_t*)svfs_read_inode_data(mount, inode);
if (!dir)
goto fail;
@ -660,7 +650,7 @@ svfs_inode_t* svfs_traverse_path(svfs_t *mount, const char *_path)
goto fail;
}
}
//printf("final inode size = %u\n", inode->size);
//slog("final inode size = %u\n", inode->size);
p_free(path);
return inode;
@ -900,8 +890,8 @@ static uint8_t ufs_load_inode(ufs_t *mount, ufs_inode_t *inode, uint32_t inum)
uint32_t i;
uint8_t *buf = p_alloc(mount->pool, mount->frag_size);
// printf("group_num = %u, ino_offset=%u, addr = 0x%08x, offset = 0x%08x\n", group_num, group_ino_offset, frag_addr, frag_offset);
// printf("mount->superblock.iblkno = 0x%08x\n", mount->superblock.iblkno);
// slog("group_num = %u, ino_offset=%u, addr = 0x%08x, offset = 0x%08x\n", group_num, group_ino_offset, frag_addr, frag_offset);
// slog("mount->superblock.iblkno = 0x%08x\n", mount->superblock.iblkno);
if (!ufs_read_frag(mount, buf, frag_addr))
goto fail;
@ -953,7 +943,7 @@ static uint8_t ufs_read_level(ufs_t *mount,
goto fail;
// for (i=0; i<num_pointers; i++)
// printf("%u 0x%08x\n", i, ntohl(table[i]));
// slog("%u 0x%08x\n", i, ntohl(table[i]));
if (level == 1)
block = p_alloc(mount->pool, mount->block_size);
@ -970,11 +960,11 @@ static uint8_t ufs_read_level(ufs_t *mount,
const uint32_t block_addr = (blockno / mount->frag_per_block) * mount->frag_per_block;
const uint32_t block_offset = (blockno - block_addr) * mount->frag_size;
printf("L%u: raw_blkno=0x%08x len=0x%08x blockno:0x%08x chunk_size=0x%08x\n", level-1, blockno, (uint32_t)*len, block_addr, (uint32_t)chunk_size);
slog("L%u: raw_blkno=0x%08x len=0x%08x blockno:0x%08x chunk_size=0x%08x\n", level-1, blockno, (uint32_t)*len, block_addr, (uint32_t)chunk_size);
// If the chunk_size is a whole block, then we better be reading in a whole block
if (chunk_size == mount->block_size) {
// printf("block_offset = 0x%x\n", block_offset);
// slog("block_offset = 0x%x\n", block_offset);
assert(block_offset == 0);
}
@ -1020,7 +1010,7 @@ static uint8_t* ufs_read_inode_data(ufs_t *mount, ufs_inode_t *inode)
const uint32_t block_addr = (inode->direct[i] / mount->frag_per_block) * mount->frag_per_block;
const uint32_t block_offset = (inode->direct[i] - block_addr) * mount->frag_size;
// printf("block_addr=0x%08x, block_offset")
// slog("block_addr=0x%08x, block_offset")
// If the chunk_size is a whole block, then we better be reading in a whole block
if (chunk_size == mount->block_size)
@ -1032,7 +1022,7 @@ static uint8_t* ufs_read_inode_data(ufs_t *mount, ufs_inode_t *inode)
memcpy(buf + len, block + block_offset, chunk_size);
len += chunk_size;
// printf("direct block %u = 0x%08x\n", i, inode->direct[i]);
// slog("direct block %u = 0x%08x\n", i, inode->direct[i]);
}
@ -1252,7 +1242,7 @@ uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path,
sprintf(error_str, "Couldn't find root partition");
goto done;
}
printf("apm_part_num = %u\n", apm_part_num);
slog("apm_part_num = %u\n", apm_part_num);
svfs_mount_obj = svfs_mount(&disk->partitions[apm_part_num]);
if (svfs_mount_obj) {
@ -1290,7 +1280,7 @@ uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path,
done:
if (strlen(error_str))
printf("error: [%s]\n", error_str);
slog("error: [%s]\n", error_str);
if (disk)
close_disk(disk);
return kernel_data;
@ -1306,7 +1296,7 @@ done:
if (!buf)
return 0;
FILE *f = fopen("result", "w");
FILE *f = fopen("result", "wb");
fwrite(buf, size, 1, f);
fclose(f);

View File

@ -26,7 +26,8 @@
#include <stdio.h>
#include <assert.h>
#include "shoebill.h"
#include <string.h>
#include "../core/shoebill.h"
const char *latch_names[8] = {
"phase0", "phase1", "phase2", "phase3",
@ -47,13 +48,13 @@ uint8_t iwm_dma_read()
if (latch_val)
shoe.iwm.latch |= (1 << latch_addr);
else
shoe.iwm.latch &= (~(1 << latch_addr));
shoe.iwm.latch &= (~~(1 << latch_addr));
// reg = {q7, q6, motor}
const uint8_t reg = ((shoe.iwm.latch >> 5) & 6) |
((shoe.iwm.latch >> 4) & 1);
printf("iwm_dma_read: %s %s (reg = %u%u%u '%s' ",
slog("iwm_dma_read: %s %s (reg = %u%u%u '%s' ",
latch_val ? "setting" : "clearing",
latch_names[latch_addr],
(reg>>2), (reg>>1)&1, reg&1, reg_names[reg]);
@ -75,12 +76,12 @@ uint8_t iwm_dma_read()
case 2:
case 3: // Read status register
// High 3 bits are mode register, low 5 are status
result = (shoe.iwm.status & 0b10100000);
result |= (shoe.iwm.mode & 0b11111);
result = (shoe.iwm.status & ~b(10100000));
result |= (shoe.iwm.mode & ~b(11111));
break;
case 4:
case 5: // Read "write-handshake" register
result = (shoe.iwm.handshake | 0b00111111); // low 6 bits all 1's
result = (shoe.iwm.handshake | ~b(00111111)); // low 6 bits all 1's
break;
default:
result = 0;
@ -88,7 +89,7 @@ uint8_t iwm_dma_read()
}
done:
printf("result=0x%02x)\n", result);
slog("result=0x%02x)\n", result);
return result;
}
@ -102,17 +103,17 @@ void iwm_dma_write()
if (latch_val)
shoe.iwm.latch |= (1 << latch_addr);
else
shoe.iwm.latch &= (~(1 << latch_addr));
shoe.iwm.latch &= (~~(1 << latch_addr));
// reg = {q7, q6, motor}
const uint8_t reg = ((shoe.iwm.latch >> 5) & 6) |
((shoe.iwm.latch >> 4) & 1);
printf("iwm_dma_write: %s %s (reg = %u%u%u '%s' val 0x%02x)\n",
latch_val ? "setting" : "clearing",
latch_names[latch_addr],
(reg>>2), (reg>>1)&1, reg&1, reg_names[reg],
data);
slog("iwm_dma_write: %s %s (reg = %u%u%u '%s' val 0x%02x)\n",
latch_val ? "setting" : "clearing",
latch_names[latch_addr],
(reg>>2), (reg>>1)&1, reg&1, reg_names[reg],
data);
// Allegedly, register writes can only occur when latch_val==1
if (!latch_val) {
@ -121,10 +122,20 @@ void iwm_dma_write()
switch (reg) {
case 6: // Write mode
shoe.iwm.mode = data & 0b01111111;
shoe.iwm.mode = data & ~b(01111111);
break;
case 7: // Write data
shoe.iwm.data = data;
break;
}
}
void init_iwm_state ()
{
memset(&shoe.iwm, 0, sizeof(iwm_state_t));
}
void reset_iwm_state ()
{
memset(&shoe.iwm, 0, sizeof(iwm_state_t));
}

4275
core/fpu.c

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,6 @@ use strict;
use Carp;
use Storable qw(dclone);
my $glob_printLineNumbers = 1; # Whether to prepend generated lines with their corresponding input file's line numbers
my $tab = " "; # one tab => four spaces
main();
@ -140,9 +139,6 @@ sub parse {
if ($newline) { # a newline just began
$newline = 0;
$ctx->{indent} = $text->{indents}->[$line_no]; # keep track of how many indents are on this line
if ($glob_printLineNumbers and ($ctx->{depth}==0)) {
# $out .= sprintf('/*%04u*/ ', $line_no+1);
}
$out .= spaces($ctx->{indent}); # and begin the line with the appropriate indentation
}
@ -155,9 +151,6 @@ sub parse {
my $macro = resolve_macro($ctx);
$i = $ctx->{cur_pos};
if ($glob_printLineNumbers and ($ctx->{depth}==0)) {
# $line_str = sprintf('/*%04u*/ ', $line_no+1);
}
$out .= join("\n$line_str".spaces($ctx->{indent}), split(/\n/, $macro->{str}));
}
@ -328,19 +321,6 @@ sub count_args {
# Macros go here:
# ------------------------------------------------------------------
sub macro_repeat {
my $args = shift;
my $num = $args->[0];
my $text = $args->[1];
my $str = "";
for (my $i=0; $i < $num; $i++) {
$str .= $text;
}
return "{$str}";
}
# ~decompose(op, "0101 ab0cd mmmrrr")
sub macro_decompose {
my ($args, $ctx) = @_;
@ -416,23 +396,6 @@ sub macro_b {
return sprintf("0x%x", $val);
}
# ~perl({my $str="hello"; print $str; return $str;}) // causes macrophile.pl to print hello and insert "hello" into the generated file
sub macro_perl {
my ($args, $ctx) = @_;
if (scalar(@$args) != 1) {
croak(sprintf("line %u: ~perl: ~perl() expects 1 argument (preferably a block), got %u", $ctx->{current_line}, scalar(@$args)));
}
my $code = $args->[0];
my $_perl_return_val;
eval 'sub ___macro_perl_sub {'.$code.'} $_perl_return_val=___macro_perl_sub($args,$ctx);';
if ($@) {
croak(sprintf("line %u: ~perl: code fragment croaked, err={%s}", $ctx->{current_line}, $@));
}
return $_perl_return_val;
}
# if (~bmatch(op, 1000 xxxx 01 xxx xxx)) {...}
sub macro_bmatch {
my ($args, $ctx) = @_;
@ -497,29 +460,6 @@ sub macro_newmacro {
return "";
}
# ~ignore( This is a comment, the only character you can't use is the closing paren. )
sub macro_ignore {
# ignore arguments, return empty string
return "";
}
# qw(word, foobar) -> {"word", "foobar"}
sub macro_qw {
my ($args, $ctx) = @_;
my $str = "{";
if (scalar(@$args)==0) {
return "{}";
}
foreach my $word (@$args) {
$str .= '"'.$word.'", ';
}
$str = substr($str, 0, -2);
$str .= "}";
return $str;
}
sub macro_bytes {
my ($args, $ctx) = @_;
count_args("bytes", $args, $ctx, 1);

View File

@ -36,61 +36,67 @@ extern struct dis_t dis;
extern uint16_t dis_op;
void inst_mc68851_prestore() {
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_psave(){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_pbcc(){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_pdbcc(uint16_t cond){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_ptrapcc(uint16_t cond){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_pscc(uint16_t cond){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_pload(uint16_t ext){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_pvalid(uint16_t ext){
printf("%s: Error, not implemented!\n", __func__);
slog("%s: Error, not implemented!\n", __func__);
assert(!"blowup");
}
void inst_mc68851_pflushr(uint16_t ext){
verify_supervisor();
printf("pflushr!");
slog("pflushr!");
// Just nuke the entire cache
bzero(shoe.pmmu_cache[0].valid_map, PMMU_CACHE_SIZE/8);
bzero(shoe.pmmu_cache[1].valid_map, PMMU_CACHE_SIZE/8);
memset(shoe.pmmu_cache[0].valid_map, 0, PMMU_CACHE_SIZE/8);
memset(shoe.pmmu_cache[1].valid_map, 0, PMMU_CACHE_SIZE/8);
/* Invalidate the pc cache */
invalidate_pccache();
}
void inst_mc68851_pflush(uint16_t ext){
verify_supervisor();
printf("pflush!");
bzero(shoe.pmmu_cache[0].valid_map, PMMU_CACHE_SIZE/8);
bzero(shoe.pmmu_cache[1].valid_map, PMMU_CACHE_SIZE/8);
// printf("%s: Error, not implemented!\n", __func__);
slog("pflush!");
memset(shoe.pmmu_cache[0].valid_map, 0, PMMU_CACHE_SIZE/8);
memset(shoe.pmmu_cache[1].valid_map, 0, PMMU_CACHE_SIZE/8);
// slog("%s: Error, not implemented!\n", __func__);
/* Invalidate the pc cache */
invalidate_pccache();
}
void inst_mc68851_pmove(uint16_t ext){
@ -100,6 +106,12 @@ void inst_mc68851_pmove(uint16_t ext){
~decompose(shoe.op, 1111 000 000 MMMMMM);
~decompose(ext, fff ppp w 0000 nnn 00);
/*
* For simplicity, just blow away pccache whenever
* the PMMU state changes at all
*/
if (!w)
invalidate_pccache();
// instruction format #1
@ -119,7 +131,16 @@ void inst_mc68851_pmove(uint16_t ext){
switch (p) {
case 0: // tc
if (!w) shoe.tc = shoe.dat & 0x83FFFFFF;
if (!w) {
shoe.tc = shoe.dat & 0x83FFFFFF;
shoe.tc_is = (shoe.tc >> 16) & 0xf;
shoe.tc_ps = (shoe.tc >> 20) & 0xf;
shoe.tc_pagesize = 1 << shoe.tc_ps;
shoe.tc_pagemask = shoe.tc_pagesize - 1;
shoe.tc_is_plus_ps = shoe.tc_is + shoe.tc_ps;
shoe.tc_enable = (shoe.tc >> 31) & 1;
shoe.tc_sre = (shoe.tc >> 25) & 1;
}
else {
shoe.dat = shoe.tc;
//if (!tc_fcl()) assert(!"pmove->tc: function codes not supported\n");
@ -193,7 +214,7 @@ static int64_t ptest_search(const uint32_t _logical_addr, const uint64_t rootp)
uint8_t i;
uint64_t desc = rootp; // Initial descriptor is the root pointer descriptor
uint8_t desc_size = 1; // And the root pointer descriptor is always 8 bytes (1==8 bytes, 0==4 bytes)
uint8_t used_bits = tc_is(); // Keep track of how many bits will be the effective "page size"
uint8_t used_bits = shoe.tc_is; // Keep track of how many bits will be the effective "page size"
// (If the table search terminates early (before used_bits == ts_ps()),
// then this will be the effective page size. That is, the number of bits
// we or into the physical addr from the virtual addr)
@ -232,7 +253,7 @@ static int64_t ptest_search(const uint32_t _logical_addr, const uint64_t rootp)
// load the child descriptor
if (_logical_addr == 0)
printf("Loading descriptor s=%llu from addr=0x%08x\n", desc_dt(desc, desc_size) & 1, (uint32_t)desc_table_addr(desc));
slog("Loading descriptor s=%llu from addr=0x%08x\n", desc_dt(desc, desc_size) & 1, (uint32_t)desc_table_addr(desc));
const uint32_t table_base_addr = desc_table_addr(desc);
const uint8_t s = desc_dt(desc, desc_size) & 1;
@ -244,7 +265,7 @@ static int64_t ptest_search(const uint32_t _logical_addr, const uint64_t rootp)
const uint8_t dt = desc_dt(desc, desc_size);
if (_logical_addr == 0)
printf("i=%u desc = 0x%llx dt=%u\n", i, desc, dt);
slog("i=%u desc = 0x%llx dt=%u\n", i, desc, dt);
// If this descriptor is invalid, throw a bus error
if (dt == 0) {
@ -315,7 +336,7 @@ void inst_mc68851_ptest(uint16_t ext){
~decompose(shoe.op, 1111 0000 00 MMMMMM);
~decompose(ext, 100 LLL R AAAA FFFFF); // Erata in 68kPRM - F is 6 bits, and A is 3
assert(tc_enable()); // XXX: Throws some exception if tc_enable isn't set
assert(shoe.tc_enable); // XXX: Throws some exception if tc_enable isn't set
assert(tc_fcl() == 0); // XXX: I can't handle function code lookups, and I don't want to
assert(L == 7); // XXX: Not currently handling searching to a particular level
@ -339,7 +360,7 @@ void inst_mc68851_ptest(uint16_t ext){
else if (fc == 5)
rootp = shoe.srp;
else {
printf("ptest: I can't handle this FC: %u pc=0x%08x\n", fc, shoe.orig_pc);
slog("ptest: I can't handle this FC: %u pc=0x%08x\n", fc, shoe.orig_pc);
assert(!"ptest: I can't handle this FC");
}
@ -353,7 +374,7 @@ void inst_mc68851_ptest(uint16_t ext){
// printf("%s: Error, not implemented!\n", __func__);
// slog("%s: Error, not implemented!\n", __func__);
}
void dis_mc68851_prestore() {

1169
core/mem.c

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1539
core/oldfpu.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -26,26 +26,33 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "shoebill.h"
#include "redblack.h"
// Create a new red-black tree
// (just return an empty black leaf pointer)
rb_tree* rb_new()
// Create a new red-black tree
rb_tree* rb_new(alloc_pool_t *parent_pool, uint32_t sz)
{
return calloc(sizeof(rb_tree), 1);
alloc_pool_t *pool = p_new_pool(parent_pool);
rb_tree *tree = (rb_tree*)p_alloc(pool, sizeof(rb_tree));
tree->root = NULL;
tree->pool = pool;
tree->sz = sz;
return tree;
}
// Insert a new key/value into the tree
// Insert a new key/value into the tree
// (and return the old value if *old_value is non-null.)
// Returns true if the key already existed.
uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old_value)
{
uint8_t rb_insert(rb_tree *tree, rb_key_t key, void *value, void *old_value)
{
const uint32_t alloc_size = sizeof(rb_node) + tree->sz;
rb_node **root = &tree->root;
// Special edge case: insert the root node if tree's empty
if (*root == NULL) {
*root = calloc(sizeof(rb_node), 1);
*root = p_alloc(tree->pool, alloc_size);
(*root)->key = key;
(*root)->value = value;
memcpy(&(*root)[1], value, tree->sz);
return 0;
}
@ -58,19 +65,19 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old
else if (key > (*cur)->key) // right
cur = &((*cur)->right);
else { // the key already exists
if (old_value)
*old_value = (*cur)->value;
(*cur)->value = value;
if (old_value)
memcpy(old_value, &(*cur)[1], tree->sz);
memcpy(&(*cur)[1], value, tree->sz);
return 1; // 1 => the key already existed
}
}
// insert
*cur = calloc(sizeof(rb_node), 1);
*cur = p_alloc(tree->pool, alloc_size);
(*cur)->parent = parent;
(*cur)->key = key;
(*cur)->value = value;
(*cur)->is_red = 1;
memcpy(&(*cur)[1], value, tree->sz);
// resolve
@ -113,7 +120,7 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old
uint8_t mycase = ((gparent->left==parent)?0:1);
mycase = (mycase << 1) | ((parent->left==red)?0:1);
switch (mycase) {
case 0: {// LLb
rb_node *Br = parent->right;
*gparent_ptr = parent;
@ -125,11 +132,11 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old
parent->parent = gparent->parent;
gparent->parent = parent;
if (Br) Br->parent = gparent;
red = gparent->right; // gparent became red, gparent->left is black, check gparent->right
break ;
}
case 1: {// LRb
case 1: {// LRb
rb_node *Cl = red->left;
rb_node *Cr = red->right;
*gparent_ptr = red;
@ -165,7 +172,7 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old
parent->parent = red;
if (Cr) Cr->parent = parent;
if (Cl) Cl->parent = gparent;
red = gparent->left;
break;
}
@ -186,24 +193,24 @@ uint8_t rb_insert(rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old
}
}
}
(*root)->is_red = 0; // make double-sure root is red
return 0;
}
// Find a value given a key
uint8_t rb_find (rb_tree *tree, rb_key_t key, rb_value_t *value)
uint8_t rb_find (rb_tree *tree, rb_key_t key, void *value)
{
rb_node *cur = *tree;
rb_node *cur = tree->root;
while (cur) {
if (key < cur->key)
if (key < cur->key)
cur = cur->left;
else if (key > cur->key)
cur = cur->right;
else {
if (value)
*value = cur->value;
memcpy(value, &cur[1], tree->sz);
return 1;
}
}
@ -212,56 +219,51 @@ uint8_t rb_find (rb_tree *tree, rb_key_t key, rb_value_t *value)
uint8_t _rb_index (rb_node *cur, uint32_t *index, rb_node **result)
{
if (!cur)
if (!cur)
return 0;
else if (_rb_index(cur->left, index, result) == 1)
else if (_rb_index(cur->left, index, result) == 1)
return 1;
else if (0 == *index) {
*result = cur;
return 1;
}
--*index;
return _rb_index(cur->right, index, result);
}
// Do an in-order traversal, and retrieve the (index)th sorted key/value
uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, rb_value_t *value)
uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, void *value)
{
rb_node *cur = *tree, *result;
rb_node *cur = tree->root, *result;
if (_rb_index(cur, &index, &result)) {
if (key) *key = result->key;
if (value) *value = result->value;
if (key)
*key = result->key;
if (value)
memcpy(value, &result[1], tree->sz);
return 1;
}
return 0;
}
// Count the number of nodes in the tree
uint32_t rb_count (rb_tree *tree)
static uint32_t _rb_count (rb_node *node)
{
rb_node *node = *tree;
if (!node)
if (!node)
return 0;
return 1 + rb_count(&node->left) + rb_count(&node->right);
return 1 + _rb_count(node->left) + _rb_count(node->right);
}
void _rb_free (rb_node *node)
uint32_t rb_count (rb_tree *tree)
{
if (!node) return ;
_rb_free(node->right);
if (node->right) free(node->right);
_rb_free(node->left);
if (node->left) free(node->left);
return _rb_count(tree->root);
}
// Free all the nodes (and the rb_tree ptr itself)
void rb_free (rb_tree *tree)
{
_rb_free(*tree);
free(*tree);
free(tree);
p_free_pool(tree->pool);
}

View File

@ -102,162 +102,105 @@ const char *scsi_write_reg_str[8] = {
"start_dma_initiator_receive"
};
enum scsi_bus_phase {
BUS_FREE = 0,
ARBITRATION,
SELECTION,
RESELECTION,
COMMAND,
DATA_OUT,
DATA_IN,
STATUS,
MESSAGE_IN,
MESSAGE_OUT
};
static void scsi_raise_irq() {via_raise_interrupt(2, IFR_CB2);}
static void scsi_raise_drq() {via_raise_interrupt(2, IFR_CA2);}
typedef struct {
// Phase
enum scsi_bus_phase phase;
// Scsi bus signals
uint8_t init_bsy:1; // BSY, driven by initiator
uint8_t target_bsy:1; // BSY, driven by target
uint8_t sel:1; // SEL, driven by both target and initiator
uint8_t rst:1; // RST, driven by both target and initiator
uint8_t cd:1; // C/D (control or data), driven by target
uint8_t io:1; // I/O, driven by target
uint8_t ack:1; // ACK, driven by initiator
uint8_t msg:1; // MSG, driven by target
uint8_t atn:1; // ATN, driven by initiator
uint8_t req:1; // REQ, driven by target
uint8_t data; // DB0-7, data lines, driven by both target and initiator
// NCR 5380 registers
uint8_t initiator_command;
uint8_t mode;
uint8_t target_command;
uint8_t select_enable; // probably not implementing this...
// Arbitration state
uint8_t init_id; // initiator ID (as a bit mask) (usually 0x80)
// Selection state
uint8_t target_id; // target ID (as an int [0, 7])
// transfer buffers
uint8_t buf[512 * 256];
uint32_t bufi;
uint32_t in_len, in_i;
uint32_t out_len, out_i;
uint32_t write_offset;
uint8_t status_byte;
uint8_t message_byte; // only one-byte messages supported for now
// hack
uint8_t dma_send_written; // Gets set whenever register 5 (start_dma_send) is written to, and cleared randomly.
// This is because aux 1.1.1 sends an extra byte after sending the write command, and that's not
// part of the write data. start_dma_send will be written when the data is actually starting.
uint8_t sent_status_byte_via_reg0; // Gets set when the status byte is red via register 0.
// This lets us know it's safe to switch to the MESSAGE_IN phase
} scsi_bus_state_t;
scsi_bus_state_t scsi;
static _Bool phase_match (void)
{
uint8_t phase_tmp = shoe.scsi.msg;
phase_tmp = (phase_tmp << 1) | shoe.scsi.cd;
phase_tmp = (phase_tmp << 1) | shoe.scsi.io;
return (phase_tmp == (shoe.scsi.target_command & 7));
}
static void switch_status_phase (uint8_t status_byte)
{
slog("scsi_reg_something: switching to STATUS phase\n");
printf("scsi_reg_something: switching to STATUS phase\n");
shoe.scsi.phase = STATUS;
shoe.scsi.status_byte = status_byte;
shoe.scsi.msg = 0;
shoe.scsi.cd = 1;
shoe.scsi.io = 1;
scsi.phase = STATUS;
scsi.status_byte = status_byte;
scsi.msg = 0;
scsi.cd = 1;
scsi.io = 1;
scsi.bufi = 0;
shoe.scsi.bufi = 0;
// Phase mismatch (I think)
via_raise_interrupt(2, 0);
scsi_raise_drq();
}
static void switch_command_phase (void)
{
printf("scsi_reg_something: switching to COMMAND phase\n");
scsi.phase = COMMAND;
slog("scsi_reg_something: switching to COMMAND phase\n");
shoe.scsi.phase = COMMAND;
scsi.msg = 0;
scsi.cd = 1;
scsi.io = 0;
shoe.scsi.msg = 0;
shoe.scsi.cd = 1;
shoe.scsi.io = 0;
scsi.bufi = 0;
shoe.scsi.bufi = 0;
// Phase mismatch, probably
via_raise_interrupt(2, 0);
scsi_raise_drq();
}
static void switch_message_in_phase (uint8_t message_byte)
{
printf("scsi_reg_something: switching to MESSAGE_IN phase\n");
slog("scsi_reg_something: switching to MESSAGE_IN phase\n");
scsi.phase = MESSAGE_IN;
scsi.msg = 1;
scsi.cd = 1;
scsi.io = 1;
shoe.scsi.phase = MESSAGE_IN;
shoe.scsi.msg = 1;
shoe.scsi.cd = 1;
shoe.scsi.io = 1;
scsi.message_byte = message_byte; // only one-byte messages supported for now
shoe.scsi.message_byte = message_byte; // only one-byte messages supported for now
// Phase mismatch, probably
via_raise_interrupt(2, 0);
scsi_raise_drq();
}
static void switch_bus_free_phase (void)
{
printf("scsi_reg_something: switching to BUS_FREE phase\n");
slog("scsi_reg_something: switching to BUS_FREE phase\n");
scsi.phase = BUS_FREE;
shoe.scsi.phase = BUS_FREE;
scsi.msg = 0;
scsi.cd = 0;
scsi.io = 0;
shoe.scsi.msg = 0;
shoe.scsi.cd = 0;
shoe.scsi.io = 0;
scsi.target_bsy = 0;
scsi.req = 0;
shoe.scsi.target_bsy = 0;
shoe.scsi.req = 0;
scsi.bufi = 0;
shoe.scsi.bufi = 0;
// Phase mismatch not possible here.
}
static void switch_data_in_phase (void)
{
printf("scsi_reg_something: switching to DATA_IN phase\n");
slog("scsi_reg_something: switching to DATA_IN phase\n");
scsi.phase = DATA_IN;
shoe.scsi.phase = DATA_IN;
scsi.msg = 0;
scsi.cd = 0;
scsi.io = 1;
shoe.scsi.msg = 0;
shoe.scsi.cd = 0;
shoe.scsi.io = 1;
// Phase mismatch, probably
via_raise_interrupt(2, 0);
scsi_raise_drq();
}
static void switch_data_out_phase (void)
{
printf("scsi_reg_something: switching to DATA_OUT phase\n");
slog("scsi_reg_something: switching to DATA_OUT phase\n");
scsi.phase = DATA_OUT;
shoe.scsi.phase = DATA_OUT;
scsi.msg = 0;
scsi.cd = 0;
scsi.io = 0;
shoe.scsi.msg = 0;
shoe.scsi.cd = 0;
shoe.scsi.io = 0;
via_raise_interrupt(2, 0);
scsi_raise_drq();
}
struct inquiry_response_t {
@ -334,83 +277,59 @@ static void scsi_handle_inquiry_command(const uint8_t alloc_len)
// XXX: added this because A/UX 3.0.1 requsts 6 bytes of the the inquiry response (sometimes?) I think it's polling for all attached scsi devices.
// Fixme: figure out how to respond "not attached"
if (alloc_len > sizeof(resp))
scsi.in_len = sizeof(resp);
shoe.scsi.in_len = sizeof(resp);
else
scsi.in_len = alloc_len;
memcpy(scsi.buf, &resp, scsi.in_len);
scsi.in_i = 0;
shoe.scsi.in_len = alloc_len;
memcpy(shoe.scsi.buf, &resp, shoe.scsi.in_len);
shoe.scsi.in_i = 0;
switch_data_in_phase();
}
static void scsi_buf_set (uint8_t byte)
{
assert(scsi.bufi <= sizeof(scsi.buf));
scsi.buf[scsi.bufi++] = byte;
assert(shoe.scsi.bufi <= sizeof(shoe.scsi.buf));
shoe.scsi.buf[shoe.scsi.bufi++] = byte;
if (scsi.phase == COMMAND) {
const uint32_t cmd_len = (scsi.buf[0] >= 0x20) ? 10 : 6; // 10 or 6 byte command?
if (shoe.scsi.phase == COMMAND) {
const uint32_t cmd_len = (shoe.scsi.buf[0] >= 0x20) ? 10 : 6; // 10 or 6 byte command?
assert(scsi.target_id < 8);
scsi_device_t *dev = &shoe.scsi_devices[scsi.target_id];
assert(shoe.scsi.target_id < 8);
scsi_device_t *dev = &shoe.scsi_devices[shoe.scsi.target_id];
// If we need more data for this command, keep driving REQ
if (scsi.bufi < cmd_len) {
// scsi.req = 1;
if (shoe.scsi.bufi < cmd_len) {
// shoe.scsi.req = 1;
// FIXME: keep driving DMA_REQUEST too
return ;
}
switch (scsi.buf[0]) {
switch (shoe.scsi.buf[0]) {
case 0: // test unit ready (6)
printf("scsi_buf_set: responding to test-unit-ready\n");
slog("scsi_buf_set: responding to test-unit-ready\n");
switch_status_phase(0); // switch to the status phase, with a status byte of 0
break;
case 0x15: // mode select (6)
printf("scsi_buf_set: responding to mode-select\n");
switch_status_phase(0);
break;
case 0x3: { // request sense (6)
const uint8_t alloc_len = shoe.scsi.buf[4];
const uint8_t control = shoe.scsi.buf[5];
const _Bool desc = shoe.scsi.buf[1] & 1;
case 0x25: // read capacity (10)
printf("scsi_buf_set: responding to read-capacity\n");
// bytes [0,3] -> BE number of blocks
scsi.buf[0] = (dev->num_blocks >> 24) & 0xff;
scsi.buf[1] = (dev->num_blocks >> 16) & 0xff;
scsi.buf[2] = (dev->num_blocks >> 8) & 0xff;
scsi.buf[3] = (dev->num_blocks) & 0xff;
switch_status_phase(2); // CHECK_CONDITION
// bytes [4,7] -> BE block size (needs to be 512)
scsi.buf[4] = (dev->block_size >> 24) & 0xff;
scsi.buf[5] = (dev->block_size >> 16) & 0xff;
scsi.buf[6] = (dev->block_size >> 8) & 0xff;
scsi.buf[7] = (dev->block_size) & 0xff;
scsi.in_i = 0;
scsi.in_len = 8;
switch_data_in_phase();
break;
case 0x12: { // inquiry command (6)
printf("scsi_buf_set: responding to inquiry\n");
const uint8_t alloc_len = scsi.buf[4];
scsi_handle_inquiry_command(alloc_len);
break;
}
case 0x8: { // read (6)
const uint32_t offset =
(scsi.buf[1] << 16) |
(scsi.buf[2] << 8 ) |
(scsi.buf[3]);
const uint8_t len = scsi.buf[4];
(shoe.scsi.buf[1] << 16) |
(shoe.scsi.buf[2] << 8 ) |
(shoe.scsi.buf[3]);
const uint16_t len = (shoe.scsi.buf[4]==0) ? 0x100 : shoe.scsi.buf[4]; // len==0 -> 256 sectors
assert(dev->f);
printf("scsi_buf_set: Responding to read at off=%u len=%u\n", offset, len);
slog("scsi_buf_set: Responding to read at off=%u len=%u\n", offset, len);
//assert(len <= 64);
@ -422,68 +341,136 @@ static void scsi_buf_set (uint8_t byte)
}
assert(0 == fseeko(dev->f, 512 * offset, SEEK_SET));
assert(fread(scsi.buf, len * 512, 1, dev->f) == 1);
assert(fread(shoe.scsi.buf, len * 512, 1, dev->f) == 1);
scsi.in_len = len * 512;
scsi.in_i = 0;
shoe.scsi.in_len = len * 512;
shoe.scsi.in_i = 0;
switch_data_in_phase();
break;
}
case 0xa: { // write (6)
const uint32_t offset =
(scsi.buf[1] << 16) |
(scsi.buf[2] << 8 ) |
(scsi.buf[3]);
const uint8_t len = scsi.buf[4];
(shoe.scsi.buf[1] << 16) |
(shoe.scsi.buf[2] << 8 ) |
(shoe.scsi.buf[3]);
const uint16_t len = (shoe.scsi.buf[4]==0) ? 0x100 : shoe.scsi.buf[4]; // len==0 -> 256 sectors
printf("scsi_buf_set: Responding to write at off=%u len=%u\n", offset, len);
slog("scsi_buf_set: Responding to write at off=%u len=%u\n", offset, len);
//assert(len <= 64);
scsi.write_offset = offset;
scsi.out_len = len * 512;
scsi.out_i = 0;
shoe.scsi.write_offset = offset;
shoe.scsi.out_len = len * 512;
shoe.scsi.out_i = 0;
scsi.dma_send_written = 0; // reset here. The real data will come in after start_dma_send is written to.
shoe.scsi.dma_send_written = 0; // reset here. The real data will come in after start_dma_send is written to.
switch_data_out_phase();
break;
}
case 0x12: { // inquiry command (6)
slog("scsi_buf_set: responding to inquiry\n");
const uint8_t alloc_len = shoe.scsi.buf[4];
scsi_handle_inquiry_command(alloc_len);
break;
}
case 0x15: // mode select (6)
slog("scsi_buf_set: responding to mode-select\n");
switch_status_phase(0);
break;
case 0x1a: { // mode sense (6)
const _Bool dbd = (shoe.scsi.buf[1] >> 3) & 1;
const uint8_t pc = (shoe.scsi.buf[2] >> 6) & 3;
const uint8_t page_code = shoe.scsi.buf[2] & 0x3f;
const uint8_t subpage_code = shoe.scsi.buf[3];
const uint8_t alloc_len = shoe.scsi.buf[4];
const uint8_t control = shoe.scsi.buf[6];
slog("scsi_bug_set: responding to mode-sense\n");
slog("dbd=%u pc=%u page_code=%u subpage_code=%u alloc_len=%u control=%u\n",
dbd, pc, page_code, subpage_code, alloc_len, control);
// FIXME: set sense code!
switch_status_phase(2); // CHECK_CONDITION
}
case 0x25: // read capacity (10)
slog("scsi_buf_set: responding to read-capacity\n");
// bytes [0,3] -> BE number of blocks
shoe.scsi.buf[0] = (dev->num_blocks >> 24) & 0xff;
shoe.scsi.buf[1] = (dev->num_blocks >> 16) & 0xff;
shoe.scsi.buf[2] = (dev->num_blocks >> 8) & 0xff;
shoe.scsi.buf[3] = (dev->num_blocks) & 0xff;
// bytes [4,7] -> BE block size (needs to be 512)
shoe.scsi.buf[4] = (dev->block_size >> 24) & 0xff;
shoe.scsi.buf[5] = (dev->block_size >> 16) & 0xff;
shoe.scsi.buf[6] = (dev->block_size >> 8) & 0xff;
shoe.scsi.buf[7] = (dev->block_size) & 0xff;
shoe.scsi.in_i = 0;
shoe.scsi.in_len = 8;
switch_data_in_phase();
break;
case 0x28: { // read (10)
// FIXME: set sense code!
switch_status_phase(2); // CHECK_CONDITION
break;
}
default:
assert(!"unknown commmand!");
printf("unknown scsi command (0x%02x)\n", shoe.scsi.buf[0]);
// FIXME: set sense code
switch_status_phase(2); // CHECK_CONDITION
break;
}
scsi.bufi = 0;
shoe.scsi.bufi = 0;
}
}
void init_scsi_bus_state ()
{
memset(&scsi, 0, sizeof(scsi_bus_state_t));
memset(&shoe.scsi, 0, sizeof(scsi_bus_state_t));
scsi.phase = BUS_FREE;
shoe.scsi.phase = BUS_FREE;
}
void reset_scsi_bus_state ()
{
memset(&shoe.scsi, 0, sizeof(scsi_bus_state_t));
shoe.scsi.phase = BUS_FREE;
}
void scsi_reg_read ()
{
const uint32_t reg = ((shoe.physical_addr & 0xffff) >> 4) & 0xf;
//printf("\nscsi_reg_read: reading from register %s(%u) ", scsi_read_reg_str[reg], reg);
//slog("\nscsi_reg_read: reading from register %s(%u) ", scsi_read_reg_str[reg], reg);
switch (reg) {
case 0: // Current scsi data bus register
if (scsi.phase == ARBITRATION)
if (shoe.scsi.phase == ARBITRATION)
shoe.physical_dat = 0; // I don't know why A/UX expects 0 here. It should be the initiator's ID, I think
else if (scsi.phase == MESSAGE_IN) {
shoe.physical_dat = scsi.message_byte; // one-byte messages supported for now
else if (shoe.scsi.phase == MESSAGE_IN) {
shoe.physical_dat = shoe.scsi.message_byte; // one-byte messages supported for now
}
else if (scsi.phase == STATUS) {
shoe.physical_dat = scsi.status_byte;
scsi.sent_status_byte_via_reg0 = 1;
else if (shoe.scsi.phase == STATUS) {
shoe.physical_dat = shoe.scsi.status_byte;
shoe.scsi.sent_status_byte_via_reg0 = 1;
}
else
assert(!"scsi_reg_read: reading data reg (0) from unknown phase\n");
@ -492,38 +479,38 @@ void scsi_reg_read ()
case 1: // Initiator command register
if (scsi.phase == ARBITRATION &&
(scsi.initiator_command & INIT_COMM_ARBITRATION_IN_PROGRESS)) {
if (shoe.scsi.phase == ARBITRATION &&
(shoe.scsi.initiator_command & INIT_COMM_ARBITRATION_IN_PROGRESS)) {
shoe.physical_dat = scsi.initiator_command;
shoe.physical_dat = shoe.scsi.initiator_command;
// the INIT_COMM_ARBITRATION_IN_PROGRESS bit is transient. Just clear
// it after the first access (it needs to go hi, then later low)
scsi.initiator_command &= ~INIT_COMM_ARBITRATION_IN_PROGRESS;
shoe.scsi.initiator_command &= ~INIT_COMM_ARBITRATION_IN_PROGRESS;
}
else
shoe.physical_dat = scsi.initiator_command;
shoe.physical_dat = shoe.scsi.initiator_command;
break;
case 2: // Mode register
shoe.physical_dat = scsi.mode;
shoe.physical_dat = shoe.scsi.mode;
break;
case 3: // Target command register
shoe.physical_dat = scsi.target_command & 0xf; // only the low 4 bits are significant
shoe.physical_dat = shoe.scsi.target_command & 0xf; // only the low 4 bits are significant
break;
case 4: { // Current SCSI control register
uint8_t tmp = 0;
tmp |= (scsi.sel * CURR_SCSI_CONTROL_SEL);
tmp |= (scsi.io * CURR_SCSI_CONTROL_IO);
tmp |= (scsi.cd * CURR_SCSI_CONTROL_CD);
tmp |= (scsi.msg * CURR_SCSI_CONTROL_MSG);
tmp |= (scsi.req * CURR_SCSI_CONTROL_REQ);
tmp |= ((scsi.target_bsy || scsi.init_bsy) ? CURR_SCSI_CONTROL_BSY : 0);
tmp |= (scsi.rst * CURR_SCSI_CONTROL_RST);
tmp |= (shoe.scsi.sel * CURR_SCSI_CONTROL_SEL);
tmp |= (shoe.scsi.io * CURR_SCSI_CONTROL_IO);
tmp |= (shoe.scsi.cd * CURR_SCSI_CONTROL_CD);
tmp |= (shoe.scsi.msg * CURR_SCSI_CONTROL_MSG);
tmp |= (shoe.scsi.req * CURR_SCSI_CONTROL_REQ);
tmp |= ((shoe.scsi.target_bsy || shoe.scsi.init_bsy) ? CURR_SCSI_CONTROL_BSY : 0);
tmp |= (shoe.scsi.rst * CURR_SCSI_CONTROL_RST);
shoe.physical_dat = tmp;
break;
}
@ -532,16 +519,10 @@ void scsi_reg_read ()
uint8_t tmp = 0;
// Compute phase match (IO, CD, MSG match the assertions in target_command register)
uint8_t phase_tmp = 0;
{
phase_tmp = (phase_tmp << 1) | scsi.msg;
phase_tmp = (phase_tmp << 1) | scsi.cd;
phase_tmp = (phase_tmp << 1) | scsi.io;
phase_tmp = (phase_tmp == (scsi.target_command & 7));
}
uint8_t phase_tmp = phase_match();
tmp |= (scsi.ack * BUS_STATUS_ACK);
tmp |= (scsi.atn * BUS_STATUS_ATN);
tmp |= (shoe.scsi.ack * BUS_STATUS_ACK);
tmp |= (shoe.scsi.atn * BUS_STATUS_ATN);
tmp |= (phase_tmp * BUS_STATUS_PHASE_MATCH);
// let's just say BUS_ERROR is always false (fixme: wrong)
@ -561,7 +542,7 @@ void scsi_reg_read ()
break;
}
//printf("(set to 0x%02x)\n\n", (uint32_t)shoe.physical_dat);
//slog("(set to 0x%02x)\n\n", (uint32_t)shoe.physical_dat);
}
void scsi_reg_write ()
@ -571,67 +552,73 @@ void scsi_reg_write ()
switch (reg) {
case 0: // Output data register
scsi.data = dat;
shoe.scsi.data = dat;
break;
case 1: { // Initiator command register
scsi.initiator_command = dat;
shoe.scsi.initiator_command = dat;
scsi.ack = ((scsi.initiator_command & INIT_COMM_ASSERT_ACK) != 0);
scsi.rst = ((scsi.initiator_command & INIT_COMM_ASSERT_RST) != 0);
scsi.init_bsy = ((scsi.initiator_command & INIT_COMM_ASSERT_BSY) != 0);
scsi.sel = ((scsi.initiator_command & INIT_COMM_ASSERT_SEL) != 0);
scsi.atn = ((scsi.initiator_command & INIT_COMM_ASSERT_ATN) != 0);
shoe.scsi.ack = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_ACK) != 0);
shoe.scsi.rst = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_RST) != 0);
shoe.scsi.init_bsy = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_BSY) != 0);
shoe.scsi.sel = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_SEL) != 0);
shoe.scsi.atn = ((shoe.scsi.initiator_command & INIT_COMM_ASSERT_ATN) != 0);
/*
// --- Arbitration ---
// Check whether to switch from ARBITRATION to SELECTION
if (scsi.sel && scsi.phase == ARBITRATION) {
if (shoe.scsi.sel && shoe.scsi.phase == ARBITRATION) {
// Asserting SEL in arbitration phase means we switch to selection phase :)
scsi.phase = SELECTION;
scsi.target_id = INVALID_ID; // invalid ID
printf("scsi_reg_write: selection phase\n");
shoe.scsi.phase = SELECTION;
shoe.scsi.target_id = INVALID_ID; // invalid ID
slog("scsi_reg_write: selection phase\n");
break;
}
*/
// --- Selection ---
// If we're in SELECTION, receive the target_id from scsi.data
if (scsi.sel && (scsi.initiator_command & INIT_COMM_ASSERT_DATA_BUS) &&
((scsi.phase == ARBITRATION) || (scsi.phase == BUS_FREE)))
// If we're in SELECTION, receive the target_id from shoe.scsi.data
if (shoe.scsi.sel && (shoe.scsi.initiator_command & INIT_COMM_ASSERT_DATA_BUS) &&
((shoe.scsi.phase == ARBITRATION) || (shoe.scsi.phase == BUS_FREE)))
{
uint8_t id;
for (id=0; (id < 8) && !(scsi.data & (1 << id)); id++) ;
for (id=0; (id < 8) && !(shoe.scsi.data & (1 << id)); id++) ;
assert(id != 8);
scsi.target_id = id;
printf("scsi_reg_write: selected target id %u\n", id);
scsi.target_bsy = 1; // target asserts BSY to acknowledge being selected
scsi.phase = SELECTION;
shoe.scsi.target_id = id;
slog("scsi_reg_write: selected target id %u\n", id);
if (shoe.scsi_devices[shoe.scsi.target_id].f == NULL) {
shoe.scsi.phase = BUS_FREE;
}
else {
shoe.scsi.target_bsy = 1; // target asserts BSY to acknowledge being selected
shoe.scsi.phase = SELECTION;
}
break;
}
// SELECTION ends when SEL gets unset
if (!scsi.sel && scsi.phase == SELECTION) {
printf("scsi_reg_write: switch to COMMAND phase\n"); // what's next?
if (!shoe.scsi.sel && shoe.scsi.phase == SELECTION) {
slog("scsi_reg_write: switch to COMMAND phase\n"); // what's next?
scsi.req = 1; // target asserts REQ after initiator deasserts SEL
shoe.scsi.req = 1; // target asserts REQ after initiator deasserts SEL
// Switch to COMMAND phase
scsi.cd = 1;
scsi.io = 0;
scsi.msg = 0;
scsi.phase = COMMAND;
shoe.scsi.cd = 1;
shoe.scsi.io = 0;
shoe.scsi.msg = 0;
shoe.scsi.phase = COMMAND;
break;
}
// --- Information transfer ---
// If initiator asserts ACK, then target needs to deassert REQ
// (I think this only makes sense for non-arbitration/selection/busfree situations
if ((scsi.phase != BUS_FREE) && (scsi.phase != ARBITRATION) && (scsi.phase != SELECTION)) {
if ((shoe.scsi.phase != BUS_FREE) && (shoe.scsi.phase != ARBITRATION) && (shoe.scsi.phase != SELECTION)) {
// If this is the message_in phase, use the unsetting-ACK portion of the REQ/ACK handshake
// to go to BUS_FREE.
// Don't bother asserting REQ here. Also, switch_bus_free_phase() will deassert target_BSY.
if (scsi.phase == MESSAGE_IN && !scsi.ack && !scsi.req) {
if (shoe.scsi.phase == MESSAGE_IN && !shoe.scsi.ack && !shoe.scsi.req) {
switch_bus_free_phase();
break ;
}
@ -640,12 +627,12 @@ void scsi_reg_write ()
// Do this when the OS deasserts ACK. We know that ACK was previously asserted if !REQ.
// (This is kinda hacky - maybe I can detect if ACK is deasserted by looking at the
// previous value of reg1)
else if (scsi.phase == STATUS && !scsi.ack && !scsi.req && scsi.sent_status_byte_via_reg0) {
scsi.req = 1;
else if (shoe.scsi.phase == STATUS && !shoe.scsi.ack && !shoe.scsi.req && shoe.scsi.sent_status_byte_via_reg0) {
shoe.scsi.req = 1;
switch_message_in_phase(0);
}
else {
scsi.req = !scsi.ack;
shoe.scsi.req = !shoe.scsi.ack;
}
}
@ -653,12 +640,12 @@ void scsi_reg_write ()
}
case 2: { // Mode register
scsi.mode = dat;
shoe.scsi.mode = dat;
if (scsi.mode & MODE_ARBITRATE) {
printf("scsi_reg_write: arbitration phase\n");
scsi.phase = ARBITRATION;
scsi.initiator_command |= INIT_COMM_ARBITRATION_IN_PROGRESS;
if (shoe.scsi.mode & MODE_ARBITRATE) {
slog("scsi_reg_write: arbitration phase\n");
shoe.scsi.phase = ARBITRATION;
shoe.scsi.initiator_command |= INIT_COMM_ARBITRATION_IN_PROGRESS;
}
else {
@ -667,27 +654,27 @@ void scsi_reg_write ()
break;
}
case 3: // Target command register
scsi.target_command = dat & 0xf; // only the bottom 4 bits are writable
shoe.scsi.target_command = dat & 0xf; // only the bottom 4 bits are writable
break;
case 4: // ID select register
scsi.select_enable = dat;
shoe.scsi.select_enable = dat;
break;
case 5: // Start DMA send
scsi.dma_send_written = 1;
via_raise_interrupt(2, 0);
shoe.scsi.dma_send_written = 1;
scsi_raise_drq();
break;
case 6: // Start DMA target receive
break;
case 7: // Start DMA initiator receive
via_raise_interrupt(2, 0);
scsi_raise_drq();
break;
}
printf("\nscsi_reg_write: writing to register %s(%u) (0x%x)\n\n", scsi_write_reg_str[reg], reg, dat);
slog("\nscsi_reg_write: writing to register %s(%u) (0x%x)\n\n", scsi_write_reg_str[reg], reg, dat);
}
void scsi_dma_write_long(const uint32_t dat)
@ -700,34 +687,34 @@ void scsi_dma_write_long(const uint32_t dat)
void scsi_dma_write (const uint8_t byte)
{
if (scsi.phase == COMMAND) {
printf("scsi_reg_dma_write: writing COMMAND byte 0x%02x\n", byte);
if (shoe.scsi.phase == COMMAND) {
slog("scsi_reg_dma_write: writing COMMAND byte 0x%02x\n", byte);
scsi_buf_set(byte);
}
else if (scsi.phase == DATA_OUT && scsi.dma_send_written) {
scsi.buf[scsi.out_i++] = byte;
else if (shoe.scsi.phase == DATA_OUT && shoe.scsi.dma_send_written) {
shoe.scsi.buf[shoe.scsi.out_i++] = byte;
//printf("scsi_reg_dma_write: writing DATA_OUT byte 0x%02x (%c)\n", byte, isprint(byte)?byte:'.');
//slog("scsi_reg_dma_write: writing DATA_OUT byte 0x%02x (%c)\n", byte, isprint(byte)?byte:'.');
if (scsi.out_i >= scsi.out_len) {
assert(scsi.target_id < 8);
scsi_device_t *dev = &shoe.scsi_devices[scsi.target_id];
if (shoe.scsi.out_i >= shoe.scsi.out_len) {
assert(shoe.scsi.target_id < 8);
scsi_device_t *dev = &shoe.scsi_devices[shoe.scsi.target_id];
assert(dev->f);
assert(0 == fseeko(dev->f, 512 * scsi.write_offset, SEEK_SET));
assert(fwrite(scsi.buf, scsi.out_len, 1, dev->f) == 1);
assert(0 == fseeko(dev->f, 512 * shoe.scsi.write_offset, SEEK_SET));
assert(fwrite(shoe.scsi.buf, shoe.scsi.out_len, 1, dev->f) == 1);
fflush(dev->f);
scsi.out_i = 0;
scsi.out_len = 0;
shoe.scsi.out_i = 0;
shoe.scsi.out_len = 0;
switch_status_phase(0);
}
}
else if (scsi.phase == DATA_OUT) {
printf("scsi_reg_dma_write: writing DATA_OUT byte (without scsi.dma_send_written) 0x%02x\n", byte);
else if (shoe.scsi.phase == DATA_OUT) {
slog("scsi_reg_dma_write: writing DATA_OUT byte (without shoe.scsi.dma_send_written) 0x%02x\n", byte);
}
else {
printf("scsi_reg_dma_write: writing 0x%02x in UNKNOWN PHASE!\n", byte);
slog("scsi_reg_dma_write: writing 0x%02x in UNKNOWN PHASE!\n", byte);
}
}
@ -747,23 +734,23 @@ uint8_t scsi_dma_read ()
{
uint8_t result = 0;
if (scsi.phase == STATUS) {
if (shoe.scsi.phase == STATUS) {
// If in the STATUS phase, return the status byte and switch back to COMMAND phase
result = scsi.status_byte;
result = shoe.scsi.status_byte;
switch_message_in_phase(0);
}
else if (scsi.phase == DATA_IN) {
assert(scsi.in_len > 0);
result = scsi.buf[scsi.in_i++];
if (scsi.in_i >= scsi.in_len) {
scsi.in_i = 0;
scsi.in_len = 0;
else if (shoe.scsi.phase == DATA_IN) {
assert(shoe.scsi.in_len > 0);
result = shoe.scsi.buf[shoe.scsi.in_i++];
if (shoe.scsi.in_i >= shoe.scsi.in_len) {
shoe.scsi.in_i = 0;
shoe.scsi.in_len = 0;
switch_status_phase(0);
}
}
//printf("scsi_reg_dma_read: called, returning 0x%02x\n", (uint8_t)result);
//slog("scsi_reg_dma_read: called, returning 0x%02x\n", (uint8_t)result);
return result;
}

View File

@ -31,12 +31,156 @@
#include <stdint.h>
#include <sys/time.h>
#include <pthread.h>
//#include <histedit.h>
// void ring_print(const char *str);
// extern char *ring_tmp;
#include "coff.h"
#if (defined WIN32) || (defined _WIN64)
#ifndef ntohl
/* Assumes that all windows platforms are little endian */
#define ntohs(_v) ({const uint16_t v = (_v); (v>>8) | (v<<8);})
#define ntohl(_v) ({const uint32_t v = (_v); (v>>24) | (((v>>16)&0xff)<<8) | (((v>>8)&0xff)<<16) | ((v&0xff)<<24);})
#define ntohll(_x) ({uint64_t x = (_x); (((uint64_t)ntohl((uint32_t)x))<<32) | ntohl(x>>32);})
#define htons(_v) ntohs(_v)
#define htonl(_v) ntohl(_v)
#endif
#else /* #if (defined WIN32) || (defined _WIN64) */
#include <arpa/inet.h>
#if (defined __APPLE__)
#include <machine/endian.h>
#include <libkern/OSByteOrder.h>
#ifndef ntohll
#define ntohll(x) OSSwapBigToHostInt64(x)
#endif
#else /* #if (defined __APPLE__) */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ntohll(_x) ({uint64_t x = (_x); (((uint64_t)ntohl((uint32_t)x))<<32) | ntohl(x>>32);})
#else
#define ntohll(_x) (_x)
#endif
#endif /* #if (defined __APPLE__) */
#endif /* #if (defined WIN32) || (defined _WIN64) */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define fix_endian(x) do { \
switch (sizeof(x)) { \
case 1: break; \
case 2: (x) = ntohs(x); break; \
case 4: (x) = ntohl(x); break; \
case 8: (x) = ntohll(x); break; \
default: assert(!"bogus size"); \
}} while (0)
#else
#define fix_endian(x)
#endif
#define slikely(e) (__builtin_expect(!!(e), 1))
#define sunlikely(e) (__builtin_expect(!!(e), 0))
/*
* core_api.c stuff
*/
typedef void (*shoebill_pram_callback_t) (void *param, const uint8_t addr, const uint8_t byte);
typedef struct {
uint32_t ram_size;
const char *rom_path;
const char *aux_kernel_path; // almost always "/unix"
_Bool aux_verbose : 1; // Whether to boot A/UX in verbose mode
_Bool aux_autoconfig : 1; // Whether to run A/UX autoconfig
_Bool debug_mode : 1; // Whether to enable hacks that debugger depends on
uint16_t root_ctrl, swap_ctrl;
uint8_t root_drive, swap_drive;
uint8_t root_partition, swap_partition;
uint8_t root_cluster;
/* Devices at the 7 possible target SCSI ids */
struct {
const char *path;
} scsi_devices[7]; // scsi id #7 is the initiator (can't be a target)
/* Initialize pram[] with initial PRAM data */
uint8_t pram[256];
/*
* This callback is called whenever a PRAM byte is changed.
* It blocks the CPU, so try to return immediately.
*/
shoebill_pram_callback_t pram_callback;
void *pram_callback_param;
char error_msg[8192];
} shoebill_config_t;
typedef struct {
const uint8_t *buf;
uint16_t width, height, scan_width, depth;
} shoebill_video_frame_info_t;
/* Take a shoebill_config_t structure and configure the global emulator context */
uint32_t shoebill_initialize(shoebill_config_t *params);
void shoebill_restart (void);
/* Call this after shoebill_initialize() to configure a video card */
uint32_t shoebill_install_video_card(shoebill_config_t *config, uint8_t slotnum,
uint16_t width, uint16_t height);
uint32_t shoebill_install_tfb_card(shoebill_config_t *config, uint8_t slotnum);
/* Call this after shoebill_initialize() to add an ethernet card */
uint32_t shoebill_install_ethernet_card(shoebill_config_t *config, uint8_t slotnum, uint8_t ethernet_addr[6], int tap_fd);
/* Get a video frame from a particular video card */
shoebill_video_frame_info_t shoebill_get_video_frame(uint8_t slotnum, _Bool just_params);
/* Call this after rendering a video frame to send a VBL interrupt */
void shoebill_send_vbl_interrupt(uint8_t slotnum);
/* Call to validate input pram and zap if invalid */
void shoebill_validate_or_zap_pram(uint8_t *pram, _Bool forcezap);
/*
* These keyboard modifier constants match the ones used
* in NSEvent shifted right by 16 bits.
*/
enum {
modCapsLock = 1 << 0,
modShift = 1 << 1,
modControl = 1 << 2,
modOption = 1 << 3,
modCommand = 1 << 4
};
void shoebill_key(uint8_t down, uint8_t key);
void shoebill_key_modifier(uint8_t modifier_mask);
void shoebill_mouse_move(int32_t x, int32_t y);
void shoebill_mouse_move_delta (int32_t x, int32_t y);
void shoebill_mouse_click(uint8_t down);
void shoebill_start();
void shoebill_stop();
void slog(const char *fmt, ...);
uint8_t* shoebill_extract_kernel(const char *disk_path, const char *kernel_path, char *error_str, uint32_t *len);
/*
* Internal shoebill stuff
*/
// -- Global constants --
@ -100,7 +244,7 @@
// set the status register, swapping a7 if necessary
#define set_sr(newsr) { \
make_stack_pointers_valid(); \
shoe.sr = newsr & 0xf71f; \
shoe.sr = (newsr) & 0xf71f; \
load_stack_pointer(); \
}
@ -117,11 +261,11 @@
#define set_sr_t1(b) {shoe.sr &= (~(1<<15)); shoe.sr |= (((b)!=0)<<15);}
// MMU
#define tc_enable() (shoe.tc >> 31)
#define tc_sre() ((shoe.tc >> 25) & 1)
#define _tc_enable() (shoe.tc >> 31) // _tc_enable,sre,ps,is are all extracted in shoe.tc_*
#define _tc_sre() ((shoe.tc >> 25) & 1)
#define tc_fcl() ((shoe.tc >> 24) & 1)
#define tc_ps() ((shoe.tc >> 20) & 0xf)
#define tc_is() ((shoe.tc >> 16) & 0xf)
#define _tc_ps() ((shoe.tc >> 20) & 0xf)
#define _tc_is() ((shoe.tc >> 16) & 0xf)
#define tc_tia() ((shoe.tc >> 12) & 0xf)
#define tc_tib() ((shoe.tc >> 8) & 0xf)
#define tc_tic() ((shoe.tc >> 4) & 0xf)
@ -138,18 +282,124 @@
#define ea_n(s) ((shoe.dat>>((s)*8-1))&1)
#define ea_z(s) (chop(shoe.dat, (s))==0)
// alloc_pool.c
/*
* alloc_pool.c
*/
#define POOL_START_MAGIC 0x231eb4af
#define POOL_END_MAGIC 0xb09f39f1
#define POOL_ALLOC_TYPE 0
#define POOL_CHILD_LINK 1
#define POOL_HEAD 2
typedef struct _alloc_pool_t {
uint32_t start_magic;
struct _alloc_pool_t *prev, *next;
uint32_t size, magic;
union {
struct {
uint64_t size;
} alloc;
struct {
struct _alloc_pool_t *child; // pointer to the child's HEAD
} child_link;
struct {
struct _alloc_pool_t *parent_link; // pointer to the parent's CHILD_LINK
} head;
} t;
uint32_t type;
uint32_t end_magic;
} alloc_pool_t;
void* p_alloc(alloc_pool_t *pool, uint64_t size);
void* p_realloc(void *ptr, uint64_t size);
void p_free(void *ptr);
void p_free_pool(alloc_pool_t *pool);
alloc_pool_t* p_new_pool(void);
alloc_pool_t* p_new_pool(alloc_pool_t *parent_pool);
/*
* redblack.c
*/
typedef uint32_t rb_key_t;
typedef struct _rb_node {
struct _rb_node *left, *right, *parent;
rb_key_t key;
uint8_t is_red : 1;
} rb_node;
typedef struct {
rb_node *root;
alloc_pool_t *pool;
uint32_t sz;
} rb_tree;
rb_tree* rb_new(alloc_pool_t *pool, uint32_t sz);
void rb_free (rb_tree *tree);
uint8_t rb_insert (rb_tree *root, rb_key_t key, void *value, void *old_value);
uint8_t rb_find (rb_tree *tree, rb_key_t key, void *value);
uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, void *value);
uint32_t rb_count (rb_tree *tree);
/*
* coff.c
*/
typedef struct {
char *name;
uint32_t value;
uint16_t scnum, type;
uint8_t sclass, numaux;
} coff_symbol;
// informed by http://www.delorie.com/djgpp/doc/coff/scnhdr.html
typedef struct {
char name[8];
uint32_t p_addr;
uint32_t v_addr;
uint32_t sz;
uint32_t data_ptr;
uint32_t reloc_ptr;
uint32_t line_ptr;
uint16_t num_relocs;
uint16_t num_lines;
uint32_t flags;
uint8_t *data;
} coff_section;
// data for this segment appears in the file, but shouldn't be copied into memory
#define coff_copy 0x0010
#define coff_text 0x0020
#define coff_data 0x0040
#define coff_bss 0x0080
typedef struct {
uint16_t magic;
uint16_t num_sections;
uint32_t timestamp;
uint32_t symtab_offset;
uint32_t num_symbols;
uint16_t opt_header_len;
uint16_t flags;
uint8_t *opt_header;
coff_section *sections;
rb_tree *func_tree;
coff_symbol *symbols;
alloc_pool_t *pool;
} coff_file;
coff_symbol* coff_find_func(coff_file *coff, uint32_t addr);
coff_symbol* coff_find_symbol(coff_file *coff, const char *name);
coff_file* coff_parse(uint8_t *buf, uint32_t buflen, alloc_pool_t *parent_pool);
coff_file* coff_parse_from_path(const char *path, alloc_pool_t *parent_pool);
void coff_free(coff_file *coff);
uint32_t be2native (uint8_t **dat, uint32_t bytes);
void print_coff_info(coff_file *coff);
typedef struct dbg_breakpoint_t {
@ -167,6 +417,27 @@ typedef struct {
dbg_breakpoint_t *breakpoints;
} debugger_state_t;
// Sound (Apple Sound Chip)
void sound_dma_write_raw(uint16_t addr, uint8_t sz, uint32_t data);
uint32_t sound_dma_read_raw(uint16_t addr, uint8_t sz);
void init_asc_state(void);
typedef struct {
uint8_t buf[0x800];
uint8_t version; // read-only
uint8_t asc_mode;
uint8_t channel_ctrl;
uint8_t fifo_ctrl;
uint8_t fifo_intr; // read-only
uint8_t unknown1;
uint8_t volume_ctrl;
uint8_t clock_ctrl;
uint16_t left_ptr, right_ptr;
} apple_sound_chip_registers_t;
typedef enum {
adb_talk,
adb_listen,
@ -196,9 +467,40 @@ typedef struct {
} adb_state_t;
typedef struct {
uint8_t ifr, ier, rega, regb, ddrb, ddra, sr;
uint8_t ifr, ier, ddrb, ddra, sr, acr, pcr;
uint8_t rega_input, regb_input;
uint8_t rega_output, regb_output;
uint16_t t1c, t2c, t1l;
long double t1_last_set, t2_last_set;
_Bool /*t1_interrupt_enabled,*/ t2_interrupt_enabled; // whether the "one-shot" interrupt can fire
} via_state_t;
#define PRAM_READ 1
#define PRAM_WRITE 2
typedef struct {
uint8_t data[256];
uint8_t last_bits;
// FSM
uint8_t command[8];
uint8_t byte, mode, command_i, bit_i;
shoebill_pram_callback_t callback;
void *callback_param;
} pram_state_t;
void init_via_state (uint8_t pram_data[256], shoebill_pram_callback_t callback, void *callback_param);
void init_adb_state();
void init_scsi_bus_state();
void init_iwm_state();
void reset_via_state();
void reset_adb_state();
void reset_scsi_bus_state();
void reset_iwm_state();
typedef struct {
uint8_t scsi_id;
uint32_t num_blocks, block_size;
@ -224,22 +526,6 @@ typedef struct {
uint8_t changed;
} mouse_state_t;
typedef struct {
uint8_t *buf_base;
uint32_t buf_size;
uint32_t h_offset; // offset in bytes for each horizontal line
uint8_t vsync;
// unit8_t via_interrupt_flag;
uint8_t depth;
uint8_t clut[256 * 3];
uint32_t clut_idx;
} video_state_t;
typedef struct {
// lsb==phase0, msb==L7
uint8_t latch;
@ -248,14 +534,163 @@ typedef struct {
uint8_t data, status, mode, handshake;
} iwm_state_t;
enum scsi_bus_phase {
BUS_FREE = 0,
ARBITRATION,
SELECTION,
RESELECTION,
COMMAND,
DATA_OUT,
DATA_IN,
STATUS,
MESSAGE_IN,
MESSAGE_OUT
};
typedef struct {
// Phase
enum scsi_bus_phase phase;
// Scsi bus signals
uint8_t init_bsy:1; // BSY, driven by initiator
uint8_t target_bsy:1; // BSY, driven by target
uint8_t sel:1; // SEL, driven by both target and initiator
uint8_t rst:1; // RST, driven by both target and initiator
uint8_t cd:1; // C/D (control or data), driven by target
uint8_t io:1; // I/O, driven by target
uint8_t ack:1; // ACK, driven by initiator
uint8_t msg:1; // MSG, driven by target
uint8_t atn:1; // ATN, driven by initiator
uint8_t req:1; // REQ, driven by target
uint8_t data; // DB0-7, data lines, driven by both target and initiator
// NCR 5380 registers
uint8_t initiator_command;
uint8_t mode;
uint8_t target_command;
uint8_t select_enable; // probably not implementing this...
// Arbitration state
uint8_t init_id; // initiator ID (as a bit mask) (usually 0x80)
// Selection state
uint8_t target_id; // target ID (as an int [0, 7])
// transfer buffers
uint8_t buf[512 * 256];
uint32_t bufi;
uint32_t in_len, in_i;
uint32_t out_len, out_i;
uint32_t write_offset;
uint8_t status_byte;
uint8_t message_byte; // only one-byte messages supported for now
// hack
uint8_t dma_send_written; // Gets set whenever register 5 (start_dma_send) is written to, and cleared randomly.
// This is because aux 1.1.1 sends an extra byte after sending the write command, and that's not
// part of the write data. start_dma_send will be written when the data is actually starting.
uint8_t sent_status_byte_via_reg0; // Gets set when the status byte is red via register 0.
// This lets us know it's safe to switch to the MESSAGE_IN phase
} scsi_bus_state_t;
typedef struct {
uint8_t r, g, b, a;
} video_ctx_color_t;
typedef struct {
video_ctx_color_t *temp_buf, *clut;
uint8_t *rom, *direct_buf;
uint32_t pixels;
uint16_t width, height, scanline_width, line_offset;
uint16_t depth, clut_idx;
} shoebill_card_video_t;
typedef struct {
uint8_t *direct_buf, *temp_buf, *clut, *rom;
uint16_t depth, clut_idx, line_offset;
uint8_t vsync;
} shoebill_card_tfb_t;
typedef struct {
// Card ROM (4kb)
uint8_t rom[0x1000];
// Card RAM (16kb buffer, apparently)
uint8_t ram[0x4000];
// Card MAC address
uint8_t ethernet_addr[6];
// Card slot number
uint8_t slotnum;
// -- thread state --
uint8_t recv_buf[4096], send_buf[4096];
uint16_t recv_len, send_len;
_Bool teardown, send_ready;
pthread_t sender_pid, receiver_pid;
pthread_mutex_t lock, sender_cond_mutex;
pthread_cond_t sender_cond;
// -- registers --
uint8_t cr; // command register, all pages, read/write
// Page 0 registers
uint8_t isr; // interrupt status register, read/write
uint8_t imr; // interrupt mask register, write
uint8_t dcr; // data configuration register (write)
uint8_t tcr; // transmit configuration register (write)
uint8_t rcr; // receive configuration register (write)
uint8_t pstart; // receive buffer start pointer (write)
uint8_t pstop; // receive buffer boundary (write)
uint8_t bnry; // a different kind of receive buffer boundary (read/write)
uint8_t tpsr; // transmit page start pointer (write)
uint16_t tbcr; // transmit buffer count register (write)
uint8_t rsr; // receive status register (read)
// Page 1 registers (read/write)
uint8_t mar[8]; // multicast address
uint8_t par[6]; // physical address
uint8_t curr; // current page
int tap_fd;
} shoebill_card_ethernet_t;
typedef enum {
card_none = 0, // Empty slot
card_toby_frame_buffer, // Original Macintosh II video card
card_shoebill_video, // Fancy 21st-century Shoebill video card
card_shoebill_ethernet // "Register-compatible" Apple EtherTalk card
} card_names_t;
typedef struct {
uint32_t (*read_func)(uint32_t, uint32_t, uint8_t);
void (*write_func)(uint32_t, uint32_t, uint32_t, uint8_t);
void (*destroy_func)(uint8_t);
uint8_t slotnum;
_Bool connected;
_Bool interrupts_enabled;
void *ctx;
uint8_t slotnum, connected, interrupts_enabled;
int32_t glut_window_id;
long double interrupt_rate, last_fired;
card_names_t card_type;
} nubus_card_t;
typedef struct {
@ -274,25 +709,31 @@ typedef struct {
} via_clock_t;
#define unstop_cpu_thread() do {\
assert(pthread_mutex_lock(&shoe.cpu_stop_mutex) == 0); \
assert(pthread_cond_signal(&shoe.cpu_stop_cond) == 0); \
assert(pthread_mutex_unlock(&shoe.cpu_stop_mutex) == 0); \
} while (0)
typedef struct {
#define SHOEBILL_STATE_STOPPED (1<<9)
_Bool running;
#define SHOEBILL_STATE_STOPPED (1 << 8)
#define SHOEBILL_STATE_RETURN (1 << 9)
// bits 0-6 are CPU interrupt priorities
// bit 8 indicates that STOP was called
volatile uint32_t cpu_thread_notifications;
volatile uint32_t via_thread_notifications;
pthread_mutex_t cpu_thread_lock;
pthread_mutex_t via_clock_thread_lock;
pthread_mutex_t cpu_freeze_lock;
pthread_mutex_t via_clock_thread_lock; // synchronizes shoebill_start() and the starting of via_clock_thread()
pthread_mutex_t via_cpu_lock; // synchronizes reads/writes of VIA registers and via_clock_thread()
// -- PMMU caching structures ---
#define PMMU_CACHE_KEY_BITS 10
#define PMMU_CACHE_SIZE (1<<PMMU_CACHE_KEY_BITS)
struct {
pmmu_cache_entry_t entry[PMMU_CACHE_SIZE];
uint8_t valid_map[PMMU_CACHE_SIZE / 8];
} pmmu_cache[2];
// The pthread condition/mutex pair for yielding CPU on STOP, and waking up upon receiving an interrupt
pthread_mutex_t cpu_stop_mutex;
pthread_cond_t cpu_stop_cond;
// -- Assorted CPU state variables --
uint16_t op; // the first word of the instruction we're currently running
@ -318,18 +759,26 @@ typedef struct {
_Bool logical_is_write; // <- boolean: true iff the operation is logical_set()
uint8_t logical_fc; // logical function code
// -- Interrupts/VIA chips --
#define invalidate_pccache() do {shoe.pccache_use_srp = 2;} while (0)
uint32_t pccache_use_srp; // 1 -> use srp, 0 -> use crp, other -> pccache is invalid
uint32_t pccache_logical_page;
uint8_t *pccache_ptr;
// uint8_t stopped; // whether STOP was called
// uint8_t pending_interrupt; // (0 -> no pending interrupt, >0 -> interrupt priority (autovector))
via_state_t via[2];
adb_state_t adb;
keyboard_state_t key;
mouse_state_t mouse;
iwm_state_t iwm;
// -- PMMU caching structures ---
#define PMMU_CACHE_KEY_BITS 10
#define PMMU_CACHE_SIZE (1<<PMMU_CACHE_KEY_BITS)
struct {
pmmu_cache_entry_t entry[PMMU_CACHE_SIZE];
uint8_t valid_map[PMMU_CACHE_SIZE / 8];
} pmmu_cache[2];
nubus_card_t slots[16];
// video_state_t video;
// -- EA state --
uint32_t uncommitted_ea_read_pc; // set by ea_read(). It's the PC that ea_read_commit will set.
uint64_t dat; // the raw input/output for the transaction
uint32_t extended_addr; // EA returned by ea_decode_extended()
uint32_t extended_len; // number of instruction bytes used by ea_decode_extended()
uint8_t sz; // the size of the EA transaction
uint8_t mr; // a 6-bit mode/reg pair
// -- Registers --
uint32_t d[8];
@ -350,6 +799,10 @@ typedef struct {
// 68851 registers
uint64_t crp, srp, drp; // user/supervisor/DMA root pointers
uint32_t tc; // translation control
uint32_t tc_pagesize, tc_pagemask; // page size and page mask
uint8_t tc_ps, tc_is, tc_is_plus_ps, tc_enable, tc_sre; // commonly read bits in shoe.tc
uint16_t pcsr; // PMMU cache status
uint16_t ac; // access control
uint16_t bad[8]; // breakpoint acknowledge data registers
@ -374,93 +827,63 @@ typedef struct {
} bits;
} psr;
// FPU registers
uint32_t fpiar; // FPU iaddr
union { // fpcr, fpu control register
struct {
// Mode control byte
uint16_t mc_zero : 4; // zero/dummy
uint16_t mc_rnd : 2; // rounding mode
uint16_t mc_prec : 2; // rounding precision
// Exception enable byte
uint16_t ee_inex1 : 1; // inexact decimal input
uint16_t ee_inex2 : 1; // inxact operation
uint16_t ee_dz : 1; // divide by zero
uint16_t ee_unfl : 1; // underflow
uint16_t ee_ovfl : 1; // overflow
uint16_t ee_operr : 1; // operand error
uint16_t ee_snan : 1; // signalling not a number
uint16_t ee_bsun : 1; // branch/set on unordered
} b;
uint16_t raw;
} fpcr;
union { // fpsr, fpu status register
struct {
// Accrued exception byte
uint32_t dummy1 : 3; // dummy/zero
uint32_t ae_inex : 1; // inexact
uint32_t ae_dz : 1; // divide by zero
uint32_t ae_unfl : 1; // underflow
uint32_t ae_ovfl : 1; // overflow
uint32_t ae_iop : 1; // invalid operation
// Exception status byte
uint32_t es_inex1 : 1; // inexact decimal input
uint32_t es_inex2 : 1; // inxact operation
uint32_t es_dz : 1; // divide by zero
uint32_t es_unfl : 1; // underflow
uint32_t es_ovfl : 1; // overflow
uint32_t es_operr : 1; // operand error
uint32_t es_snan : 1; // signalling not a number
uint32_t es_bsun : 1; // branch/set on unordered
// Quotient byte
uint32_t qu_quotient : 7;
uint32_t qu_s : 1;
// Condition code byte
uint32_t cc_nan : 1; // not a number
uint32_t cc_i : 1; // infinity
uint32_t cc_z : 1; // zero
uint32_t cc_n : 1; // negative
uint32_t dummy2 : 4; // dummy/zero
} b;
uint32_t raw;
} fpsr;
long double fp[8]; // 80 bit floating point general registers
// fpu_state_t pointer
// (declared here as a void*, to prevent other files
// from needing to include SoftFloat/softfloat.h)
void *fpu_state;
// -- EA state --
uint32_t uncommitted_ea_read_pc; // set by ea_read(). It's the PC that ea_read_commit will set.
uint64_t dat; // the raw input/output for the transaction
uint32_t extended_addr; // EA returned by ea_decode_extended()
uint32_t extended_len; // number of instruction bytes used by ea_decode_extended()
uint8_t sz; // the size of the EA transaction
uint8_t mr; // a 6-bit mode/reg pair
// -- Interrupts/VIA chips --
via_state_t via[2];
via_clock_t via_clocks;
adb_state_t adb;
pram_state_t pram;
keyboard_state_t key;
mouse_state_t mouse;
apple_sound_chip_registers_t asc;
iwm_state_t iwm;
struct timeval start_time; // when the emulator started (for computing timer interrupts)
uint64_t total_ticks; // how many 60hz ticks have been generated
scsi_bus_state_t scsi;
scsi_device_t scsi_devices[8]; // SCSI devices
nubus_card_t slots[16];
coff_file *coff; // Data/symbols from the unix kernel
coff_file *launch; // FIXME: delete me: coff symbols from aux 1.1.1 launch binary
scsi_device_t scsi_devices[8]; // SCSI devices
pthread_t cpu_thread_pid, via_thread_pid;
debugger_state_t dbg;
alloc_pool_t *pool;
shoebill_config_t config_copy; // copy of the config structure passed to shoebill_initialize()
} global_shoebill_context_t;
extern global_shoebill_context_t shoe; // declared in cpu.c
// fpu.c functions
void inst_fpu_decode(void);
void dis_fpu_decode(void);
void fpu_setup_jump_table();
void inst_fscc();
void inst_fbcc();
void inst_fsave();
void inst_frestore();
void inst_ftrapcc();
void inst_fdbcc();
void inst_fnop();
void inst_fpu_other();
void dis_fscc();
void dis_fbcc();
void dis_fsave();
void dis_frestore();
void dis_ftrapcc();
void dis_fdbcc();
void dis_fnop();
void dis_fpu_other();
void dis_fmath (uint16_t op, uint16_t ext, char *output);
void fpu_initialize();
void fpu_reset();
// cpu.c fuctions
void cpu_step (void);
@ -475,10 +898,14 @@ void throw_illegal_instruction();
void throw_privilege_violation();
void throw_divide_by_zero();
void throw_frame_two (uint16_t sr, uint32_t next_pc, uint32_t vector_num, uint32_t orig_pc);
void throw_frame_zero(uint16_t sr, uint32_t pc, uint16_t vector_num);
// mem.c functions
uint16_t pccache_nextword(uint32_t pc);
uint32_t pccache_nextlong(uint32_t pc);
//void physical_get (void);
typedef void (*physical_get_ptr) (void);
typedef void (*physical_set_ptr) (void);
@ -486,7 +913,12 @@ extern const physical_get_ptr physical_get_jump_table[16];
extern const physical_set_ptr physical_set_jump_table[16];
#define physical_set() physical_set_jump_table[shoe.physical_addr >> 28]()
#define pset(addr, s, val) {shoe.physical_addr=(addr); shoe.physical_size=(s); shoe.physical_dat=(val); physical_set();}
#define pset(addr, s, val) do { \
shoe.physical_addr=(addr); \
shoe.physical_size=(s); \
shoe.physical_dat=(val); \
physical_set(); \
} while (0)
#define physical_get() physical_get_jump_table[shoe.physical_addr >> 28]()
#define pget(addr, s) ({shoe.physical_addr=(addr); shoe.physical_size=(s); physical_get(); shoe.physical_dat;})
@ -499,37 +931,34 @@ void logical_get (void);
logical_get(); \
shoe.logical_dat; \
})
#define lget(addr, s) ({ \
shoe.logical_addr=(addr); \
shoe.logical_size=(s); \
shoe.logical_fc = (sr_s() ? 5 : 1); \
logical_get(); \
shoe.logical_dat; \
})
#define lget(addr, s) lget_fc((addr), (s), (sr_s() ? 5 : 1))
void logical_set (void);
#define lset_fc(addr, s, val, fc) {\
#define lset_fc(addr, s, val, fc) do { \
shoe.logical_addr=(addr); \
shoe.logical_size=(s); \
shoe.logical_dat=(val); \
shoe.logical_fc = (fc); \
logical_set();\
}
#define lset(addr, s, val) { \
lset_fc((addr), (s), (val), sr_s() ? 5 : 1) \
}
} while (0)
#define lset(addr, s, val) lset_fc((addr), (s), (val), sr_s() ? 5 : 1)
typedef void (*_ea_func) (void);
extern const _ea_func ea_read_jump_table[64];
extern const _ea_func ea_read_commit_jump_table[64];
extern const _ea_func ea_write_jump_table[64];
extern const _ea_func ea_addr_jump_table[64];
#define ea_read() ea_read_jump_table[shoe.mr]()
#define ea_read_commit() ea_read_commit_jump_table[shoe.mr]()
#define ea_write() ea_write_jump_table[shoe.mr]()
#define ea_addr() ea_addr_jump_table[shoe.mr]()
void ea_read();
void ea_read_commit();
void ea_write();
void ea_addr();
#define call_ea_read(M, s) {shoe.mr=(M);shoe.sz=(s);ea_read();if (shoe.abort) return;}
#define call_ea_write(M, s) {shoe.mr=(M);shoe.sz=(s);ea_write();if (shoe.abort) return;}
#define call_ea_read_commit(M, s) {shoe.mr=(M);shoe.sz=(s);ea_read_commit();if (shoe.abort) return;}
#define call_ea_addr(M) {shoe.mr=(M);ea_addr();if (shoe.abort) return;}
#define call_ea_read(M, s) {shoe.mr=(M);shoe.sz=(s);ea_read();if sunlikely(shoe.abort) return;}
#define call_ea_write(M, s) {shoe.mr=(M);shoe.sz=(s);ea_write();if sunlikely(shoe.abort) return;}
#define call_ea_read_commit(M, s) {shoe.mr=(M);shoe.sz=(s);ea_read_commit();if sunlikely(shoe.abort) return;}
#define call_ea_addr(M) {shoe.mr=(M);ea_addr();if sunlikely(shoe.abort) return;}
#define push_a7(_dat, _sz) {shoe.a[7]-=(_sz);lset(shoe.a[7], (_sz), (_dat));}
@ -549,9 +978,6 @@ void ea_addr();
desc_addr = (_addr); \
desc_level++; \
}
// if (shoe.dbg) \
// printf("desc_addr *0x%08x = 0x%llx\n", (uint32_t)(_addr), desc); \
// }
// dis.c functions
@ -589,11 +1015,10 @@ void scsi_dma_write(uint8_t byte);
void scsi_dma_write_long(uint32_t dat);
// via1 & via2 (+ CPU interrupts)
void check_time();
void via_raise_interrupt(uint8_t vianum, uint8_t ifr_bit);
void process_pending_interrupt();
void via_reg_read();
void via_reg_write();
void via_read_raw();
void via_write_raw();
void *via_clock_thread(void *arg);
// VIA registers
@ -620,8 +1045,8 @@ void *via_clock_thread(void *arg);
#define IFR_SHIFT_REG 2
#define IFR_CB2 3
#define IFR_CB1 4
#define IFR_TIMER1 5
#define IFR_TIMER2 6
#define IFR_TIMER2 5
#define IFR_TIMER1 6
#define IFR_IRQ 7
// adb / keyboard / mouse stuff
@ -638,23 +1063,27 @@ struct macii_rom_symbols_t {
extern const struct macii_rom_symbols_t macii_rom_symbols[];
// Emulated Toby Frame Buffer nubus card
void nubus_tfb_init(uint8_t slotnum);
void nubus_tfb_init(void *_ctx, uint8_t slotnum);
uint32_t nubus_tfb_read_func(uint32_t, uint32_t, uint8_t);
void nubus_tfb_write_func(uint32_t, uint32_t, uint32_t, uint8_t);
shoebill_video_frame_info_t nubus_tfb_get_frame(shoebill_card_tfb_t *ctx,
_Bool just_params);
// Shoebill Virtual Video Card
void nubus_video_init(void *_ctx, uint8_t slotnum,
uint16_t width, uint16_t height, uint16_t scanline_width,
double refresh_rate);
uint16_t width, uint16_t height, uint16_t scanline_width);
uint32_t nubus_video_read_func(const uint32_t rawaddr, const uint32_t size,
const uint8_t slotnum);
void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size,
const uint32_t data, const uint8_t slotnum);
shoebill_video_frame_info_t nubus_video_get_frame(shoebill_card_video_t *ctx,
_Bool just_params);
// debug_server.c
void *debug_cpu_thread (void *arg);
// Apple EtherTalk
void nubus_ethernet_init(void *_ctx, uint8_t slotnum, uint8_t ethernet_addr[6], int tap_fd);
uint32_t nubus_ethernet_read_func(uint32_t, uint32_t, uint8_t);
void nubus_ethernet_write_func(uint32_t, uint32_t, uint32_t, uint8_t);
void nubus_ethernet_destroy_func(uint8_t);
#endif // _SHOEBILL_H

279
core/sound.c Normal file
View File

@ -0,0 +1,279 @@
/*
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Props to MESS for their EASC register map: http://www.mess.org/mess/driver_info/easc
* EASC's PCM mode is backwards compatible with ASC, so their registers are very similar.
*/
#include <assert.h>
#include <string.h>
#include "../core/shoebill.h"
#define r_ptr (0x400 + (asc->right_ptr & 0x3ff)) /* right buf ptr */
#define l_ptr (asc->left_ptr & 0x3ff) /* left buf ptr */
#define m_ptr (asc->left_ptr & 0x7ff) /* mono buf ptr */
void init_asc_state(void)
{
memset(&shoe.asc, 0, sizeof(shoe.asc));
shoe.asc.version = 0x00; // My Mac II's ASC reports 0x00 as its version.
// From the ROM, it looks like other Mac II ASC masks have different version numbers
}
static uint8_t sound_dma_read_byte(uint16_t addr)
{
apple_sound_chip_registers_t *asc = &shoe.asc;
if (addr < 0x800) {
if (asc->asc_mode == 1) {
// PCM mode (FIFO is append-only)
if (asc->channel_ctrl & 2) {
// stereo mode - return the byte referenced by the right pointer
return asc->buf[r_ptr];
}
else {
// mono mode - return the byte referenced by the left pointer
return asc->buf[m_ptr];
}
}
else {
// Off or wavetable mode (FIFO is random access)
return asc->buf[addr];
}
}
switch (addr) {
case 0x800: // Version
return asc->version;
case 0x801: // ASC-mode
return asc->asc_mode;
case 0x802: // Channel control
return asc->channel_ctrl;
case 0x803: // FIFO control
return asc->fifo_ctrl;
case 0x804: // FIFO interrupt
//return asc->fifo_intr;
return 0xff;
case 0x805: // Unknown (??)
return asc->unknown1;
case 0x806: // Volume control
return asc->volume_ctrl;
case 0x807: // Clock control
return asc->clock_ctrl;
}
return 0;
}
static void sound_dma_write_byte(uint16_t addr, uint8_t data)
{
apple_sound_chip_registers_t *asc = &shoe.asc;
if (addr < 0x800) { // buf_a/b
if (asc->asc_mode == 1) {
// PCM mode (FIFO is append-only)
if (asc->channel_ctrl & 2) {
// stereo mode - each channel has its own buffer pointer
if (addr < 0x400) {
// left buffer
asc->buf[l_ptr] = data;
asc->left_ptr++;
}
else {
// right buffer
asc->buf[r_ptr] = data;
asc->right_ptr++;
}
return ;
}
else {
// Mono mode
asc->buf[m_ptr] = data;
asc->left_ptr++;
return ;
}
}
else {
// Off or wavetable mode (FIFO is random access)
asc->buf[addr] = data;
return ;
}
}
else {
switch (addr) {
case 0x800: // Version
// Version is read-only
break;
case 0x801: // ASC-mode
// ASC version 0x00 preserves the low 2 bits
asc->asc_mode = data & 0x03;
assert(asc->asc_mode != 3); // mode-3 produces noise, but isn't deterministic
break;
case 0x802: // Channel control
// ASC version 0x00 preserves the high bit and low 2 bits
asc->channel_ctrl = data & 0x83;
break;
case 0x803: // FIFO control
// ASC version 0x00 preserves the low 2 bits
asc->fifo_ctrl = data & 0x83;
break;
case 0x804: // FIFO interrupt
// FIFO interrupt is read-only
break;
case 0x805: // unknown (???)
// ASC version 0x00 preserves the high bit and low 4 bits
// (But what does it do?)
asc->unknown1 = data & 0x8f;
break;
case 0x806: // Volume control
// ASC version 0x00 preserves the high 6 bits
asc->volume_ctrl = data & 0xfc;
break;
case 0x807: // Clock control
// ASC version 0x00 preserves the low 2 bits
asc->clock_ctrl = data & 0x03;
break;
}
}
}
void sound_dma_write_raw(uint16_t addr, uint8_t sz, uint32_t data)
{
int32_t i;
slog("sound_dma_write: addr=0x%04x sz=%u dat=0x%x\n", addr, sz, data);
for (i = (sz-1) * 8; i >= 0; i -= 8)
sound_dma_write_byte(addr, (data >> i) & 0xff);
}
uint32_t sound_dma_read_raw(uint16_t addr, uint8_t sz)
{
uint32_t i, result = 0;
slog("sound_dma_read: addr=0x%04x sz=%u\n", addr, sz);
for (i=0; i<sz; i++) {
result <<= 8;
result |= sound_dma_read_byte(addr + i);
}
return result;
}
/*
ASC notes:
buf_a (0x000-0x3ff) -> left
buf_b (0x400-0x7ff) -> right
writing to any address in buf_a or buf_b appends to the end pointer in the ring buffer (ignoring the address)
reading from any address returns... the currently-being-read byte? Or the about-to-be-overwritten byte?
(def one or the other)
- Update: MODE=0: You can do regular random read/write access to buf_a/b when MODE=0.
MODE=1: Writing anywhere within the buffer automatically appends to the end of the ring buffer
For stereo mode, writing between 0-0x3ff appends to buf_a, 0x400-0x7ff to buf_b
Reading anywhere in the buffer is strange - it seems non-deterministic.
Update 2: no wait, it's returning the byte at buf_b's ring pointer, does it always do that?
Update 3: yes, it looks like reading anywhere returns the byte at buf_b's pointer in stereo mode,
and buf_a's pointer in mono mode
MODE=2: Same as MODE=0 (?)
- ASC maintains two distinct ring buffer pointers for stereo mode
0x800 - Version (?) MacII only ever seems to TST.B it
(READ ONLY)
$00 -> My Mac II's ASC's version
$?? -> Mac II's ROM is aware of other non-$00 ASC masks
$b0 -> EASC
0x801 - Mode (?) 0 == no output, 1 == PCM, 2 == wavetable, 3 == hissing static(??)
- Preserves low 2 bits, ignores high 6 bits
0x802 - Channel selector
Preserves mask 0x83 (high bit, low 2 bits)
- High bit is set if engine overflow (according to MESS)
- Low bits, 0x?2 -> stereo output (buf_a -> left, buf_b -> right)
0x?0 -> Mono output (from buf_a)
- Switching to stereo from mono produces a blip of sound in the right ear, unless the fifo has been cleared (buf_b), (or unless there was nothing in buf_b to start with)
0x803 - Fifo control (cycle 0x80 to reset the internal FIFO pointer (pointers?))
Preserves mask 0x83 (high bit, low 2 bits)
0x804 - Fifo interrupt status
(READ ONLY, doesnt preserve any written bits (doesnt acknowledge writes at all?))
0x805 - ???
- Preserves bits at 0x8F, (high bit, low 4 bits), doesn't seem to do anything
0x806 - Volume control (high 3 bits)
Preserves top 6 bits, ignores bottom 2 bits. (Only top 3 bits control volume)
0x807 - Clock rate select (0, 2, or 3, I think)
0x00 -> 11127hz
0x01 -> Illegal???
0x02 -> 11025hz
0x03 -> 22050hz
Writes to fifo_a/b(?) seem to block for clock rates 0, 2, and 3, but not 1. The speaker clicks like the sound chip is turning off when you set clock to 0x01.
ASC has more registers past 0x807 for wave table control
*/

View File

@ -26,7 +26,6 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <GLUT/glut.h>
#include "shoebill.h"
#define ROM_SIZE 4096
@ -41,45 +40,11 @@ uint8_t macii_video_rom[ROM_SIZE] = {
*/
};
typedef struct {
uint8_t *buf_base;
uint32_t buf_size;
uint32_t clut_idx;
uint32_t h_offset; // offset in bytes for each horizontal line
uint8_t clut[256 * 3];
uint8_t depth;
uint8_t vsync;
uint8_t *gldat;
} tfb_ctx_t;
void nubus_tfb_display_func (void)
static void nubus_tfb_clut_translate(shoebill_card_tfb_t *ctx)
{
uint32_t myw = glutGet(GLUT_WINDOW_WIDTH);
uint32_t myh = glutGet(GLUT_WINDOW_HEIGHT);
uint32_t slotnum, i;
int32_t my_window_id = glutGetWindow();
for (slotnum = 0; slotnum < 16; slotnum++)
if (shoe.slots[slotnum].glut_window_id == my_window_id)
break;
tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
uint32_t i, gli = 0;
const uint8_t depth = ctx->depth;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, myw, 0, myh, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.1, 0.1, 0.8);
uint32_t gli = 0;
for (i=0; i < (1024 * 480);) {
uint8_t code;
@ -91,87 +56,60 @@ void nubus_tfb_display_func (void)
assert(gli < (640 * 480));
if (depth <= 1) {
code = (ctx->buf_base[ctx->h_offset + i/8] & (0x80 >> (i % 8))) != 0;
code = (ctx->direct_buf[ctx->line_offset + i/8] & (0x80 >> (i % 8))) != 0;
code = (code << 7) | 0x7f;
}
else if (depth == 2) {
const uint8_t byte = ctx->buf_base[ctx->h_offset + i/4];
const uint8_t byte = ctx->direct_buf[ctx->line_offset + i/4];
const uint8_t mod = i % 4;
code = (byte << (mod * 2)) & 0xc0;
code |= 0x3f;
}
else if (depth == 4) {
const uint8_t byte = ctx->buf_base[ctx->h_offset + i/2];
const uint8_t byte = ctx->direct_buf[ctx->line_offset + i/2];
const uint8_t mod = i % 2;
code = (byte << (mod * 4)) & 0xf0;
code |= 0x0f;
}
else if (depth == 8) {
code = ctx->buf_base[ctx->h_offset + i];
code = ctx->direct_buf[ctx->line_offset + i];
}
ctx->gldat[gli * 3 + 0] = ctx->clut[code * 3 + 2];
ctx->gldat[gli * 3 + 1] = ctx->clut[code * 3 + 1];
ctx->gldat[gli * 3 + 2] = ctx->clut[code * 3 + 0];
ctx->temp_buf[gli * 4 + 0] = ctx->clut[code * 3 + 2];
ctx->temp_buf[gli * 4 + 1] = ctx->clut[code * 3 + 1];
ctx->temp_buf[gli * 4 + 2] = ctx->clut[code * 3 + 0];
gli++;
i++;
}
glViewport(0, 0, myw, myh);
glRasterPos2i(0, myh);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelZoom(1.0, -1.0);
glDrawPixels(myw, myh, GL_RGB, GL_UNSIGNED_BYTE, ctx->gldat);
glFlush();
}
void global_mouse_func (int button, int state, int x, int y);
void global_motion_func (int x, int y);
void global_passive_motion_func (int x, int y);
void global_keyboard_up_func (unsigned char c, int x, int y);
void global_keyboard_down_func (unsigned char c, int x, int y);
void global_special_up_func (int special, int x, int y);
void global_special_down_func (int special, int x, int y);
void nubus_tfb_init(uint8_t slotnum)
void nubus_tfb_init(void *_ctx, uint8_t slotnum)
{
tfb_ctx_t *ctx = (tfb_ctx_t*)calloc(sizeof(tfb_ctx_t), 1);
shoebill_card_tfb_t *ctx = (shoebill_card_tfb_t*)_ctx;
ctx->direct_buf = p_alloc(shoe.pool, 512 * 1024 + 4);
ctx->temp_buf = p_alloc(shoe.pool, 640 * 480 * 4);
ctx->rom = p_alloc(shoe.pool, 4096);
ctx->clut = p_alloc(shoe.pool, 256 * 3);
ctx->buf_size = 512 * 1024;
ctx->buf_base = (uint8_t*)calloc(ctx->buf_size, 1);
ctx->depth = 1;
ctx->clut_idx = 786;
ctx->gldat = valloc(480 * 640 * 3);
ctx->line_offset = 0;
memset(ctx->clut, 0x0, 384);
memset(ctx->clut+384, 0xff, 384);
memset(ctx->buf_base, 0xff, ctx->buf_size);
// memcpy(ctx->rom, macii_video_rom, 4096);
// Offset to the TFB sResource
ctx->rom[4096 - 20] = 0;
ctx->rom[4096 - 19] = 0xff;
ctx->rom[4096 - 18] = 0xf0;
ctx->rom[4096 - 17] = 0x14;
// Init CLUT to 1-bit depth
ctx->depth = 1;
memset(ctx->clut, 0x0, 3*128);
memset(ctx->clut + (3*128), 0xff, 3*128);
shoe.slots[slotnum].ctx = ctx;
glutInitWindowSize(640, 480);
shoe.slots[slotnum].glut_window_id = glutCreateWindow("");
glutDisplayFunc(nubus_tfb_display_func);
glutIgnoreKeyRepeat(1);
glutKeyboardFunc(global_keyboard_down_func);
glutKeyboardUpFunc(global_keyboard_up_func);
glutSpecialFunc(global_special_down_func);
glutSpecialUpFunc(global_special_up_func);
glutMouseFunc(global_mouse_func);
glutMotionFunc(global_motion_func);
glutPassiveMotionFunc(global_passive_motion_func);
glShadeModel(GL_FLAT);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glClearColor(0.1, 1.0, 0.1, 1.0);
}
uint32_t nubus_tfb_read_func(const uint32_t rawaddr, const uint32_t size, const uint8_t slotnum)
@ -190,7 +128,8 @@ uint32_t nubus_tfb_read_func(const uint32_t rawaddr, const uint32_t size, const
// 1101
// 1110
// 1111 = ROM (repeating 4kb images in S bits)
tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
//tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
shoebill_card_tfb_t *ctx = (shoebill_card_tfb_t*)shoe.slots[slotnum].ctx;
const uint32_t addr = rawaddr & 0x000fffff;
uint32_t result = 0;
@ -199,15 +138,15 @@ uint32_t nubus_tfb_read_func(const uint32_t rawaddr, const uint32_t size, const
case 0x0 ... 0x7: { // frame buffer
uint32_t i;
for (i=0; i<size; i++)
result = (result << 8) | ctx->buf_base[addr + i];
result = (result << 8) | ctx->direct_buf[addr + i];
break;
}
case 0xf: { // rom
if ((addr & 3) == 0) {
const uint32_t rom_addr = (addr >> 2) % 4096;
result = macii_video_rom[rom_addr];
printf("nubus_tfb_read_func: returning %x for addr %x (rom_addr=%x)\n", (uint32_t)result, shoe.physical_addr, rom_addr);
result = ctx->rom[rom_addr];
slog("nubus_tfb_read_func: returning %x for addr %x (rom_addr=%x)\n", (uint32_t)result, rawaddr, rom_addr);
}
else
result = 0;
@ -220,21 +159,21 @@ uint32_t nubus_tfb_read_func(const uint32_t rawaddr, const uint32_t size, const
if (addr == 0xd0000) {
ctx->vsync++;
result = (ctx->vsync >> 7)&1;
printf("nubus_tfb_read_func: reading vsync bit shoe.pc=0x%08x\n", shoe.orig_pc);
slog("nubus_tfb_read_func: reading vsync bit shoe.pc=0x%08x\n", shoe.orig_pc);
// We just need to oscillate the vsync bit, to pretend we're going in and out of the blanking interval.
// The card driver waits until the bit goes low, then waits until it goes high
// FIXME: clean this up
break;
}
else {
printf("nubus_tfb_read_func: unknown read of *0x%08x\n", shoe.physical_addr);
slog("nubus_tfb_read_func: unknown read of *0x%08x\n", rawaddr);
result = 0;
break;
}
}
default: {
printf("nubus_tfb_read_func: unknown read of *0x%08x\n", shoe.physical_addr);
slog("nubus_tfb_read_func: unknown read of *0x%08x\n", rawaddr);
result = 0;
break;
}
@ -246,7 +185,7 @@ uint32_t nubus_tfb_read_func(const uint32_t rawaddr, const uint32_t size, const
void nubus_tfb_write_func(const uint32_t rawaddr, const uint32_t size,
const uint32_t data, const uint8_t slotnum)
{
tfb_ctx_t *ctx = (tfb_ctx_t*)shoe.slots[slotnum].ctx;
shoebill_card_tfb_t *ctx = (shoebill_card_tfb_t*)shoe.slots[slotnum].ctx;
const uint32_t addr = rawaddr & 0x000fffff;
switch (addr >> 16) {
@ -254,7 +193,7 @@ void nubus_tfb_write_func(const uint32_t rawaddr, const uint32_t size,
case 0x0 ... 0x7: { // frame buffer
uint32_t i;
for (i=0; i<size; i++) {
ctx->buf_base[addr + size - (i+1)] = (data >> (8*i)) & 0xFF;
ctx->direct_buf[addr + size - (i+1)] = (data >> (8*i)) & 0xFF;
}
return ;
}
@ -277,19 +216,20 @@ void nubus_tfb_write_func(const uint32_t rawaddr, const uint32_t size,
}
if (addr == 0x8000c) { // horizontal offset
ctx->h_offset = 4 * ((~data) & 0xff);
ctx->line_offset = 4 * ((~data) & 0xff);
return ;
}
else {
printf("video_nubus_set: unknown write to *0x%08x (0x%x)\n", shoe.physical_addr, data);
slog("video_nubus_set: unknown write to *0x%08x (0x%x)\n", rawaddr, data);
return ;
}
}
case 0x9: { // CLUT registers?
if (addr == 0x90018) { // CLUT
printf("clut[0x%03x (0x%02x+%u)] = 0x%02x\n", ctx->clut_idx, ctx->clut_idx/3, ctx->clut_idx%3, (uint8_t)(data & 0xff));
ctx->clut[ctx->clut_idx] = 255 - (data & 0xff);
uint8_t *clut = (uint8_t*)ctx->clut;
slog("clut[0x%03x (0x%02x+%u)] = 0x%02x\n", ctx->clut_idx, ctx->clut_idx/3, ctx->clut_idx%3, (uint8_t)(data & 0xff));
clut[ctx->clut_idx] = 255 - (data & 0xff);
ctx->clut_idx = (ctx->clut_idx == 0) ? 767 : ctx->clut_idx-1;
@ -302,20 +242,41 @@ void nubus_tfb_write_func(const uint32_t rawaddr, const uint32_t size,
ctx->clut_idx = clut_dat * 3 + 2;
}
printf("video_nubus_set: unknown write to *0x%08x (0x%x)\n", shoe.physical_addr, data);
slog("video_nubus_set: unknown write to *0x%08x (0x%x)\n", rawaddr, data);
return ;
}
case 0xa: { // clear interrupt for slot 0xa(?) in via2.rega (by setting the appropriate bit)
assert((data & 0xff) == 0);
// shoe.via[1].rega |= 0b00000010;
shoe.via[1].rega |= (1 << (slotnum - 9));
shoe.via[1].rega_input |= (1 << (slotnum - 9));
return ;
}
default: {
printf("video_nubus_set: unknown write to *0x%08x (0x%x)\n", shoe.physical_addr, data);
slog("video_nubus_set: unknown write to *0x%08x (0x%x)\n", rawaddr, data);
return ;
}
}
}
shoebill_video_frame_info_t nubus_tfb_get_frame(shoebill_card_tfb_t *ctx,
_Bool just_params)
{
shoebill_video_frame_info_t result;
result.width = 640;
result.height = 480;
result.scan_width = 640;
result.depth = ctx->depth;
// If caller just wants video parameters...
if (just_params)
return result;
nubus_tfb_clut_translate(ctx);
result.buf = ctx->temp_buf;
return result;
}

View File

@ -29,11 +29,14 @@
#include <pthread.h>
#include <math.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include "../core/shoebill.h"
char *via_reg_str[16] = {
"orb",
"ora",
"regb",
"rega",
"ddrb",
"ddra",
"t1c-l",
@ -66,8 +69,11 @@ void via_raise_interrupt(uint8_t vianum, uint8_t ifr_bit)
// Only if the bit is enabled in IER do we raise a cpu interrupt
if (via->ier & (1 << ifr_bit))
set_pending_interrupt(vianum);
else
printf("didn't set pending interrupt\n");
// if the CPU was stopped, wake it up
if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) {
unstop_cpu_thread();
}
}
@ -96,12 +102,11 @@ void process_pending_interrupt ()
return ;
}
// If the CPU was stopped, unstop it
shoe.cpu_thread_notifications &= ~~SHOEBILL_STATE_STOPPED;
const uint16_t vector_offset = (priority + 24) * 4;
printf("Interrupt pri %u! mask=%u vector_offset=0x%08x\n", priority, sr_mask(), vector_offset);
slog("Interrupt pri %u! mask=%u vector_offset=0x%08x\n", priority, sr_mask(), vector_offset);
// Save the old SR, and switch to supervisor mode
const uint16_t old_sr = shoe.sr;
@ -109,15 +114,15 @@ void process_pending_interrupt ()
// Write a "format 0" exception frame to ISP or MSP
push_a7(0x0000 | vector_offset, 2);
printf("interrupt: pushed format 0x%04x to 0x%08x\n", 0x0000 | vector_offset, shoe.a[7]);
slog("interrupt: pushed format 0x%04x to 0x%08x\n", 0x0000 | vector_offset, shoe.a[7]);
assert(!shoe.abort);
push_a7(shoe.pc, 4);
printf("interrupt: pushed pc 0x%08x to 0x%08x\n", shoe.pc, shoe.a[7]);
slog("interrupt: pushed pc 0x%08x to 0x%08x\n", shoe.pc, shoe.a[7]);
assert(!shoe.abort);
push_a7(old_sr, 2);
printf("interrupt: pushed sr 0x%04x to 0x%08x\n", old_sr, shoe.a[7]);
slog("interrupt: pushed sr 0x%04x to 0x%08x\n", old_sr, shoe.a[7]);
assert(!shoe.abort);
if (sr_m()) {
@ -147,7 +152,7 @@ void process_pending_interrupt ()
// Fetch the autovector handler address
const uint32_t newpc = lget(shoe.vbr + vector_offset, 4);
printf("autovector handler = *0x%08x = 0x%08x\n", shoe.vbr + vector_offset, newpc);
slog("autovector handler = *0x%08x = 0x%08x\n", shoe.vbr + vector_offset, newpc);
assert(!shoe.abort);
shoe.pc = newpc;
@ -156,33 +161,6 @@ void process_pending_interrupt ()
shoe.cpu_thread_notifications &= ~~(1 << priority);
}
/*
Reset:
Host sends command, switch to state 0
Device sends byte 1
Host switches to state 2
Device sends byte 2
Host switches to state 3
Talk:
Host sends command, switch to state 0
Device sends byte 0 (even)
Hosts switches to state 1 (even = "just got even byte")
Device sends byte 1 (odd)
Host switches to state 2 (odd = "just got odd byte")
Device sends byte 2 (even)
*/
// via1 ORB bits abcd efgh
// cd -> adb FSM state
// e -> adb timeout occurred / service request (?)
// f/g/h nvram stuff
#define VIA_REGB_DONE 8
// VIA registers
#define VIA_ORB 0
@ -202,78 +180,465 @@ void process_pending_interrupt ()
#define VIA_IER 14
#define VIA_ORA_AUX 15
uint16_t counter;
// Interrupt flag register bits
#define VIA_IFR_CA2 (1<<0)
#define VIA_IFR_CA1 (1<<1)
#define VIA_IFR_SHIFT_REG (1<<2)
#define VIA_IFR_CB2 (1<<3)
#define VIA_IFR_CB1 (1<<4)
#define VIA_IFR_T2 (1<<5)
#define VIA_IFR_T1 (1<<6)
#define VIA_IFR_IRQ (1<<7)
void via_reg_read ()
static long double _now (void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
const long double secs = tv.tv_sec;
const long double usecs = tv.tv_usec;
const long double result = secs + (usecs / 1000000.0);
return result;
}
static void handle_pram_write_byte (void)
{
pram_state_t *pram = &shoe.pram;
slog("PRAMPRAM: wrote_byte 0x%02x\n", pram->byte);
pram->mode = PRAM_READ;
pram->byte = 0;
}
static void handle_pram_read_byte (void)
{
pram_state_t *pram = &shoe.pram;
assert(pram->command_i < 8);
pram->command[pram->command_i++] = pram->byte;
slog("PRAMPRAM: read_byte: 0x%02x\n", pram->byte);
// If this is a pram-read/write...
if ((pram->command[0] & 0x78) == 0x38) {
const _Bool isget = pram->command[0] >> 7;
const uint8_t addr = (pram->command[0] << 5) | ((pram->command[1] >> 2) & 0x1f);
if ((pram->command_i == 3) && !isget) { // complete set command
pram->mode = PRAM_READ; // stay in read-mode
pram->data[addr] = pram->command[2];
if (pram->callback)
pram->callback(pram->callback_param, addr, pram->command[2]);
slog("PRAMPRAM: setting pram addr 0x%02x = 0x%02x\n", addr, pram->command[2]);
pram->byte = 0;
pram->command_i = 0;
return ;
}
else if ((pram->command_i == 2) && isget) { // complete get command
pram->mode = PRAM_WRITE; // switch to write-mode
pram->byte = pram->data[addr];
pram->command_i = 0;
slog("PRAMPRAM: fetching pram addr 0x%02x (= 0x%02x)\n", addr, pram->byte);
return ;
}
else { // incomplete command, keep reading
assert(pram->command_i < 4);
pram->mode = PRAM_READ; // keep reading
return ;
}
}
// if this is clock-read/write
else if (~bmatch(pram->command[0], x 00x xx 01)) {
const _Bool isget = pram->command[0] >> 7;
const uint8_t addr = (pram->command[0] >> 2) & 3;
const _Bool mysterybit = (pram->command[0] >> 4) & 1; // FIXME: What does this do?
if ((pram->command_i == 2) && !isget) { // complete set command
pram->mode = PRAM_READ; // stay in read-mode
slog("PRAMPRAM: setting time byte %u to 0x%02x (mysterybit=%u)\n", addr, pram->command[1], mysterybit);
pram->byte = 0;
pram->command_i = 0;
return ;
}
else if ((pram->command_i == 1) && isget) { // complete get command
const uint32_t now = time(NULL) + 0x7c25b080;
//uint32_t now = 0xafd56d80; // Tue, 24 Jun 1997 12:26:40 GMT
const uint8_t now_byte = now >> (8*addr);
pram->mode = PRAM_WRITE;
pram->byte = now_byte;
pram->command_i = 0;
slog("PRAMPRAM: fetching time byte %u of 0x%08x (mysterybit=%u)\n", addr, now, mysterybit);
return ;
}
else { // incomplete command, keep reading
assert(pram->command_i < 3);
pram->mode = PRAM_READ;
return ;
}
}
// This is mystery command # 2
else if (pram->command[0] == 0x35) {
// Arrives in pairs of two bytes
if (pram->command_i == 2) {
slog("PRAMPRAM: mystery command 2 0x%02x 0x%02x (?))\n", pram->command[0], pram->command[1]);
pram->mode = PRAM_READ;
pram->command_i = 0;
return ;
}
else { // keep reading
assert(pram->command_i < 3);
pram->mode = PRAM_READ;
return ;
}
}
slog("PRAMPRAM: don't understand this command\n");
pram->command_i = 0;
pram->mode = PRAM_READ;
}
static void handle_pram_state_change (void)
{
pram_state_t *pram = &shoe.pram;
// If rtcClock or rtcEnable changed, then the state machine needs updating
if (pram->last_bits == (shoe.via[0].regb_output & shoe.via[0].ddrb & 6))
return ;
slog("PRAMPRAM: pram->last_bits = %u, (shoe.via[0].regb & 6) = %u\n", pram->last_bits, (shoe.via[0].regb_output & shoe.via[0].ddrb & 6));
// it doesn't matter what the last rtcData value was
const _Bool last_rtcClock = (pram->last_bits >> 1) & 1;
const _Bool last_rtcEnable = (pram->last_bits >> 2) & 1;
const _Bool rtcData = shoe.via[0].regb_output & 1;
const _Bool rtcClock = (shoe.via[0].regb_output >> 1) & 1;
const _Bool rtcEnable = (shoe.via[0].regb_output >> 2) & 1;
slog("PRAMPRAM: bits changed %u%ux -> %u%u%u\n", last_rtcEnable, last_rtcClock, rtcEnable, rtcClock, rtcData);
if (rtcEnable) {
// rtcEnable==true => the RTC chip is enabled and we are talking to it
// Not sure what happens when you toggle data/clock bits while rtcEnable is asserted...
if (last_rtcEnable)
slog("PRAMPRAM: toggled bits while rtcEnable was asserted!\n");
goto done;
}
if (!rtcEnable && last_rtcEnable) {
// if rtcEnable went from hi to low, then reset all the state stuff
pram->mode = PRAM_READ;
pram->command_i = 0; // the current command byte we're working on
pram->bit_i = 0; // the current bit num we're reading/writing
pram->byte = 0; // the current byte we're reading/writing
memset(pram->command, 0, 8);
goto done;
}
switch (pram->mode) {
case PRAM_READ: {
// if rtcClock goes from low to hi, then rtcData represents a new bit
if (rtcClock && !last_rtcClock) {
pram->byte <<= 1;
pram->byte |= rtcData;
pram->bit_i++;
}
if ((shoe.via[0].ddrb & 1) == 0) {
// This is input-mode -- should be output-mode
slog("PRAMPRAM: BOGUS MODE ddrb&1 == 0\n");
}
if (pram->bit_i >= 8) {
pram->bit_i = 0;
handle_pram_read_byte();
}
goto done;
}
case PRAM_WRITE: {
// if rtcClock goes from hi to low, load in the new rtcData bit
if (!rtcClock && last_rtcClock) {
const uint8_t newData = (pram->byte >> (7 - pram->bit_i)) & 1;
shoe.via[0].regb_input &= 0xfe;
shoe.via[0].regb_input |= newData;
}
// if B goes from low to hi, skip to the next bit
if (rtcClock && !last_rtcClock)
pram->bit_i++;
assert((shoe.via[0].ddrb & 1) == 0);
if (pram->bit_i >= 8) {
pram->bit_i = 0;
handle_pram_write_byte();
}
goto done;
}
default:
assert(!"can't get here");
}
done:
// Remember the last state of the bits
pram->last_bits = (shoe.via[0].regb_output & shoe.via[0].ddrb & 6);
}
void reset_via_state (void)
{
uint8_t pram_data[256];
shoebill_pram_callback_t callback = shoe.pram.callback;
void *callback_param = shoe.pram.callback_param;
memcpy(pram_data, shoe.pram.data, 256);
init_via_state(pram_data, callback, callback_param);
}
void init_via_state (uint8_t pram_data[256], shoebill_pram_callback_t callback, void *callback_param)
{
/* -- Zero everything -- */
memset(&shoe.pram, 0, sizeof(pram_state_t));
memset(&shoe.via, 0, 2 * sizeof(via_state_t));
// Jeez, keep this straight!
// DDR 0 -> input (from pins to OS)
// 1 -> output (from OS to pins)
/* -- Initialize VIA1 -- */
/* VIA 1 reg A
* Bit 7 - input - vSCCWrReq
* Bit 6 - input - CPU.ID1
* Bit 5 - output - vHeadSel
* Bit 4 - output - vOverlay
* Bit 3 - output - vSync
* Bit 2-0 unused
*/
shoe.via[0].ddra = ~b(00111000);
/* VIA 1 reg B
* Bit 7 - output - vSndEnb
* Bit 6 - unused
* Bit 5 - output - vFDesk2
* Bit 4 - output - vFDesk1
* Bit 3 - input - vFDBInt
* Bit 2 - output - rTCEnb
* Bit 1 - output - rtcClk
* Bit 0 - in/out - rtcData (initialize to output)
*/
shoe.via[0].ddrb = ~b(10110111); // A/UX apparently neglects to initialize ddra/b
/* -- Initialize VIA2 -- */
/* VIA 2 reg A
* Bit 7 - unused
* Bit 6 - unused
* Bit 5 - Interrupt for slot 15
* ...
* Bit 0 - Interrupt for slot 9
*/
shoe.via[1].ddra = 0x00; // via2/rega consists of input pins for nubus interrupts
shoe.via[1].rega_input = ~b(00111111); // no nubus interrupts currently asserted
/* VIA 2 reg B
* Bit 7 - output - v2VBL
* Bit 6 - input - v2SNDEXT
* Bit 5 - input - v2TM0A (nubus transfer what??)
* Bit 4 - input - v2TM1A
* Bit 3 - output - AMU/PMMU control
* Bit 2 - output - v2PowerOff (but leave this in input mode)
* Bit 1 - output - v2BusLk
* Bit 0 - output - v2cdis
*/
shoe.via[1].ddrb = 0x00;
shoe.via[1].ddrb = ~b(10001011);
// FIXME: apparently via2/regb bit 7 is tied to VIA1, and driven by timer T1, to
// generate 60.15hz (really 60.0hz) interrupts on VIA1
// emulate this more accurately!
// The power unit is wired to bit 2, waiting for it to be set 0.
// I guess we're supposed to leave it as an input, because the shutdown
// routine first sets the bit to 0, *then* switches the direction to output.
// FIXME: verify that this is correct
/* -- Initialize PRAM -- */
pram_state_t *pram = &shoe.pram;
pram->mode = PRAM_READ;
memcpy(pram->data, pram_data, 256);
pram->callback = callback;
pram->callback_param = callback_param;
/* -- Init clock stuff -- */
const long double now = _now();
shoe.via[0].t1_last_set = now;
shoe.via[0].t2_last_set = now;
shoe.via[1].t1_last_set = now;
shoe.via[1].t2_last_set = now;
}
#define E_CLOCK 783360
#define V2POWEROFF_MASK 0x04
#define _via_get_delta_counter(last_set) ({ \
const long double delta_t = now - (last_set); \
const long double delta_ticks = fmodl((delta_t * (long double)E_CLOCK), 0x80000000); \
/* The VIA timers decrement by 2 for every E_CLOCK tick */ \
const uint32_t delta_counter = ((uint32_t)delta_ticks) << 1; \
delta_counter; \
})
// from the pins' perspective
#define VIA_REGA_PINS(n) ((shoe.via[(n)-1].rega_output & shoe.via[(n)-1].ddra) | \
(shoe.via[(n)-1].rega_input & (~~shoe.via[(n)-1].ddra)))
#define VIA_REGB_PINS(n) ((shoe.via[(n)-1].regb_output & shoe.via[(n)-1].ddrb) | \
(shoe.via[(n)-1].regb_input & (~~shoe.via[(n)-1].ddrb)))
static void _via_poweroff(void)
{
slog("Poweroff!\n");
// exit(0);
}
static uint8_t via_read_reg(const uint8_t vianum, const uint8_t reg, const long double now)
{
const uint8_t vianum = (shoe.physical_addr >= 0x50002000) ? 2 : 1;
const uint8_t reg = (shoe.physical_addr >> 9) & 15;
via_state_t *via = &shoe.via[vianum - 1];
printf("via_reg_read: reading from via%u reg %s (%u)\n", vianum, via_reg_str[reg], reg);
slog("via_reg_read: reading from via%u reg %s (%u) (shoe.pc = 0x%08x)\n", vianum, via_reg_str[reg], reg, shoe.pc);
switch (reg) {
case VIA_ACR:
return via->acr;
case VIA_PCR:
return via->pcr;
case VIA_IER:
// According to the eratta, bit 7 is always set during a read
shoe.physical_dat = via->ier | 0x80;
break ;
return via->ier | 0x80;
case VIA_IFR: {
// Figure out whether any enabled interrupts are set, and set IRQ accordingly
const uint8_t irq = (via->ifr & via->ier & 0x7f) ? 0x80 : 0x0;
shoe.physical_dat = (via->ifr & 0x7f) | irq;
break ;
return (via->ifr & 0x7f) | irq;
}
case VIA_SR:
shoe.physical_dat = via->sr;
break;
case VIA_ORB:
shoe.physical_dat = via->regb;
break;
return via->sr;
case VIA_ORB: {
/*
* FIXME: this is not exactly correct.
* if input latching is enabled, then the value of input pins
* is held in escrow until a CB1 transition occurs. I'm not doing that.
*/
slog("via_reg_read: FYI: regb_output=0x%02x regb_input=0x%02x ddrb=0x%02x combined=0x%02x\n",
shoe.via[vianum-1].regb_output, shoe.via[vianum-1].regb_input, via->ddrb, VIA_REGB_PINS(vianum));
return VIA_REGB_PINS(vianum);
}
case VIA_ORA_AUX:
case VIA_ORA:
//if ((vianum==2) && !(via->ifr & (1<<IFR_CA1)))
//via->rega = 0x3f;
shoe.physical_dat = via->rega;
break;
case VIA_ORA: {
/*
* FIXME: This is not exactly correct either, and it behaves differently from regb
* Reading regA never returns the contents of the output register directly,
* it returns the the value of the pins - unless input latching is enabled,
* then it holds the pin values in escrow until a CA1 transition occurs.
* I'm just returning the value of the "pins"
*/
return VIA_REGA_PINS(vianum);
}
case VIA_DDRB:
shoe.physical_dat = via->ddrb;
break;
return via->ddrb;
case VIA_DDRA:
shoe.physical_dat = via->ddra;
break;
return via->ddra;
case VIA_T2C_HI: {
uint16_t counter = via->t2c - (uint16_t)_via_get_delta_counter(via->t2_last_set);
case VIA_T2C_LO:
// XXX: A/UX 3.0.1 tries to precisely time a huge dbra loop
// using via timer2. It won't accept any result shorter than
// 0x492, and hypothetically, this emulator could execute the
// loop faster than that (although not currently). So this is
// a super dumb hack that always returns a delta-t of 0x492
// between sequential reads from t2c.
// (oh, also, a/ux 3.0.1 cleverly reads from both t2c_lo and _hi
// simultaneously by doing a word-size read at VIA+0x11ff)
counter -= 0x492;
shoe.physical_dat = 0xffff & ((counter >> 8) | (counter << 8));
break;
/*
* This is a hack to allow A/UX 3.x.x to boot on fast hosts.
* The A/UX 3 kernel calls a function, SetUpTimeK, during boot
* to run a giant drba-to-self loop and time it via T2C.
* If the loop completes too quickly, (quicker than 0x492 E_CLOCK
* ticks on 3.0.0), it rejects it and tries again.
*
* SetUpTimeK reads T2C twice and compares them to determine the
* time elapsed. I don't want to count on getting the order correct,
* however. Therefore, we will fake it out by randomly adding 0x500
* to the the value of the clock whenever it's read by SetUpTimeK.
* By the Monte Carlo method, we'll eventually get a case where
* two sequential reads differ by at least 0x500.
*
* FIXME: optimize this better, stop using coff_find_func()
*/
if (sr_s()) {
coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc);
if (symb && strcmp(symb->name, "SetUpTimeK") == 0) {
if (random() & 1)
counter += 0x500;
}
}
default:
printf("via_reg_read: (unhandled!)\n");
break;
return counter >> 8;
}
case VIA_T2C_LO: {
const uint16_t counter = via->t2c - (uint16_t)_via_get_delta_counter(via->t2_last_set);
via->ifr &= ~~VIA_IFR_T2; // Read from T2C_LOW clears TIMER 2 interrupt
return (uint8_t)counter;
}
case VIA_T1C_LO:
via->ifr &= ~~VIA_IFR_T1; // Read from T1C_LOW clears TIMER 1 interrupt
return 0; // FIXME
case VIA_T1C_HI:
return 0; // FIXME
case VIA_T1L_LO:
return 0; // FIXME
case VIA_T1L_HI:
return 0; // FIXME
}
assert(!"never get here");
}
void via_reg_write()
static void via_write_reg(const uint8_t vianum, const uint8_t reg, const uint8_t data, const long double now)
{
const uint8_t vianum = (shoe.physical_addr >= 0x50002000) ? 2 : 1;
const uint8_t reg = (shoe.physical_addr >> 9) & 15;
const uint8_t data = (uint8_t)shoe.physical_dat;
via_state_t *via = &shoe.via[vianum - 1];
printf("via_reg_write: writing 0x%02x to via%u reg %s (%u)\n", (uint8_t)shoe.physical_dat, vianum, via_reg_str[reg], reg);
slog("via_reg_write: writing 0x%02x to via%u reg %s (%u) (pc=0x%08x)\n", data, vianum, via_reg_str[reg], reg, shoe.pc);
switch (reg) {
case VIA_IER: {
@ -300,104 +665,175 @@ void via_reg_write()
break;
case VIA_ORB: {
via->regb = data;
via->regb_output = data;
if (vianum == 1) {
const uint8_t adb_state = (data >> 4) & 3;
const uint8_t adb_state = (data >> 4) & 3; // just assume that the corresponding ddrb bits are marked "output"
if (shoe.adb.state != adb_state) {
const uint8_t old_state = shoe.adb.state;
shoe.adb.state = adb_state;
adb_handle_state_change(old_state, adb_state);
}
handle_pram_state_change();
}
if ((vianum == 2) &&
(via->ddrb & V2POWEROFF_MASK) &&
!(via->regb_output & V2POWEROFF_MASK))
_via_poweroff();
break;
}
case VIA_ORA_AUX:
case VIA_ORA:
via->rega = data;
break;
case VIA_ORA: {
via->rega_output = data;
case VIA_DDRB:
via->ddrb = data;
break;
}
case VIA_DDRB: {
via->ddrb = data;
if ((vianum == 2) &&
(via->ddrb & V2POWEROFF_MASK) &&
!(via->regb_output & V2POWEROFF_MASK))
_via_poweroff();
break;
}
case VIA_DDRA:
via->ddra = data;
break;
default:
printf("via_reg_read: (unhandled!)\n");
case VIA_ACR:
via->acr = data;
break;
case VIA_PCR:
via->pcr = data;
break;
case VIA_T2C_LO:
break;
case VIA_T2C_HI:
via->ifr &= ~~VIA_IFR_T2; // Write to T2C_HI clears TIMER 2 interrupt
via->t2_last_set = now;
via->t2_interrupt_enabled = 1;
break;
case VIA_T1C_LO:
break;
case VIA_T1C_HI:
via->ifr &= ~~VIA_IFR_T1; // Write to T1C_HI clears TIMER 1 interrupt
break;
case VIA_T1L_LO:
break;
case VIA_T1L_HI:
break;
}
}
// FIXME: check_time() is bad and needs rewritten
void check_time()
void via_write_raw (void)
{
struct timeval now, delta_tv;
const uint32_t hz = 10;
const uint8_t vianum = ((shoe.physical_addr >> 13) & 1) + 1;
const uint8_t reg = (shoe.physical_addr >> 9) & 15;
// return ;
pthread_mutex_lock(&shoe.via_cpu_lock);
gettimeofday(&now, NULL);
delta_tv.tv_sec = now.tv_sec - shoe.start_time.tv_sec;
if (now.tv_usec < shoe.start_time.tv_usec) {
delta_tv.tv_sec--;
delta_tv.tv_usec = (now.tv_usec + 1000000) - shoe.start_time.tv_usec;
if (shoe.physical_size == 1) {
const long double now = ((reg >= VIA_T1C_LO) && (reg <= VIA_T2C_HI)) ? _now() : 0.0;
// Common case: writing to only one register
via_write_reg(vianum, reg, (uint8_t)shoe.physical_dat, now);
}
else if ((shoe.physical_size == 2) && ((shoe.physical_addr & 0x1ff) == 0x1ff)) {
const long double now = ((reg >= VIA_T1C_LO) && ((reg+1) <= VIA_T2C_HI)) ? _now() : 0.0;
// Uncommon case: writing to two registers simultaneously
slog("via_write_raw: writing to two registers simultaneously %u and %u (0x%x)\n", reg, reg+1 , (uint32_t)shoe.physical_dat);
assert(reg != 15); // If A/UX is trying to write to two VIA chips simultanously, that's not cool
via_write_reg(vianum, reg, (uint8_t)(shoe.physical_dat >> 8), now);
via_write_reg(vianum, reg+1, (uint8_t)shoe.physical_dat, now);
}
else
delta_tv.tv_usec = now.tv_usec - shoe.start_time.tv_usec;
uint64_t delta = delta_tv.tv_sec * 1000;
delta += delta_tv.tv_usec / 1000;
uint64_t ticks = delta / hz;
if (ticks <= shoe.total_ticks)
return ;
shoe.total_ticks = ticks;
//printf("ticks = %llu\n", ticks);
via_raise_interrupt(1, IFR_CA1);
//
shoe.via[1].rega = 0b00111101;
via_raise_interrupt(2, IFR_CA1);
assert(!"Writing multiple bytes to the same VIA register!");
pthread_mutex_unlock(&shoe.via_cpu_lock);
}
static long double _now (void)
void via_read_raw (void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
const uint8_t vianum = ((shoe.physical_addr >> 13) & 1) + 1;
const uint8_t reg = (shoe.physical_addr >> 9) & 15;
long double secs = tv.tv_sec;
long double usecs = tv.tv_usec;
pthread_mutex_lock(&shoe.via_cpu_lock);
return secs + (usecs / 1000000.0);
if (shoe.physical_size == 1) {
const long double now = ((reg >= VIA_T1C_LO) && (reg <= VIA_T2C_HI)) ? _now() : 0.0;
// Common case: reading only one register
shoe.physical_dat = via_read_reg(vianum, reg, now);
}
else if ((shoe.physical_size == 2) && ((shoe.physical_addr & 0x1ff) == 0x1ff)) {
const long double now = ((reg >= VIA_T1C_LO) && ((reg+1) <= VIA_T2C_HI)) ? _now() : 0.0;
// Uncommon case: reading from two registers simultaneously
slog("via_read_raw: reading from two registers simultaneously %u and %u\n", reg, reg+1);
assert(reg != 15); // If A/UX is trying to read from two VIA chips simultaneously, that's not cool
uint16_t result = via_read_reg(vianum, reg, now);
result = (result << 8) | via_read_reg(vianum, reg+1, now);
shoe.physical_dat = result;
}
else
assert(!"Reading multiple bytes from the same VIA register!");
pthread_mutex_unlock(&shoe.via_cpu_lock);
}
#define fire(s) ({assert((s) >= 0); if (earliest_next_timer > (s)) earliest_next_timer = (s);})
void *via_clock_thread(void *arg)
{
pthread_mutex_lock(&shoe.via_clock_thread_lock);
const long double start_time = _now();
// const long double multiplier = 1.0 / 60.0;
const long double multiplier = 1.0;
const long double start_time = multiplier * _now();
uint64_t ca1_ticks = 0, ca2_ticks = 0;
uint32_t i;
while (1) {
const long double now = _now();
pthread_mutex_lock(&shoe.via_cpu_lock);
const long double now = multiplier * _now();
long double earliest_next_timer = 1.0;
const uint32_t via1_t2_delta = _via_get_delta_counter(shoe.via[0].t2_last_set);
// Check whether the 60.15hz timer should fire (via1 CA1)
const uint64_t expected_ca1_ticks = ((now - start_time) * 60.15L);
/*
* Check whether the 60hz timer should fire (via1 CA1)
*
* Note! Inside Macintosh claims this should be 60.15hz,
* but every version of A/UX configures the timer to be
* exactly 60.0hz
*/
const uint64_t expected_ca1_ticks = ((now - start_time) * 60.0L);
if (expected_ca1_ticks > ca1_ticks) {
// Figure out when the timer should fire next
const long double next_firing = (1.0L/60.15L) - fmodl(now - start_time, 1.0L/60.15L);
const long double next_firing = (1.0L/60.0L) - fmodl(now - start_time, 1.0L/60.0L);
fire(next_firing);
ca1_ticks = expected_ca1_ticks;
@ -422,30 +858,25 @@ void *via_clock_thread(void *arg)
via_raise_interrupt(2, IFR_TIMER2);*/
}
// Check if any nubus cards have interrupt timers
shoe.via[1].rega = 0b00111111;
for (i=9; i<15; i++) {
if (!shoe.slots[i].connected)
continue;
if (now >= (shoe.slots[i].last_fired + (1.0L/shoe.slots[i].interrupt_rate))) {
shoe.slots[i].last_fired = now;
fire(1.0L/shoe.slots[i].interrupt_rate);
if (shoe.slots[i].interrupts_enabled) {
// shoe.via[1].rega = 0b00111111 & ~~(1<<(i-9));
shoe.via[1].rega &= 0b00111111 & ~~(1<<(i-9));
via_raise_interrupt(2, IFR_CA1);
printf("Fired nubus interrupt %u\n", i);
}
// I'm only checking VIA1 T2, since the time manager only seems to use/care about that timer
if (shoe.via[0].t2_interrupt_enabled) {
if (via1_t2_delta >= shoe.via[0].t2c) {
shoe.via[0].t2_interrupt_enabled = 0;
via_raise_interrupt(1, IFR_TIMER2);
}
else {
fire((long double)(shoe.via[0].t2c - via1_t2_delta) / (E_CLOCK / 2.0));
}
}
pthread_mutex_unlock(&shoe.via_cpu_lock);
usleep((useconds_t)(earliest_next_timer * 1000000.0L));
if (shoe.via_thread_notifications & SHOEBILL_STATE_RETURN) {
pthread_mutex_unlock(&shoe.via_clock_thread_lock);
return NULL;
}
}
}

View File

@ -27,7 +27,6 @@
#include <stdlib.h>
#include <string.h>
#include "shoebill.h"
#include "core_api.h"
#include "video_rom/rom.c"
@ -55,7 +54,7 @@ typedef struct __attribute__ ((__packed__)) {
uint32_t plane_bytes;
} video_params_t;
uint32_t compute_nubus_crc(uint8_t *rom, uint32_t len)
static uint32_t compute_nubus_crc(uint8_t *rom, uint32_t len)
{
uint32_t i, sum = 0;
@ -79,30 +78,27 @@ uint32_t compute_nubus_crc(uint8_t *rom, uint32_t len)
static void _switch_depth(shoebill_card_video_t *ctx, uint32_t depth)
{
ctx->depth = depth;
if (depth > 8)
ctx->cur_buf = (uint8_t*)ctx->direct_buf;
else
ctx->cur_buf = ctx->indexed_buf;
}
void nubus_video_init(void *_ctx, uint8_t slotnum,
uint16_t width, uint16_t height, uint16_t scanline_width,
double refresh_rate)
uint16_t width, uint16_t height, uint16_t scanline_width)
{
shoebill_card_video_t *ctx = (shoebill_card_video_t*)_ctx;
ctx->width = width;
ctx->height = height;
ctx->scanline_width = scanline_width;
ctx->pixels = scanline_width * height;
ctx->line_offset = 0;
ctx->direct_buf = valloc(ctx->pixels * sizeof(video_ctx_color_t));
ctx->indexed_buf = valloc(ctx->pixels);
ctx->clut = malloc(256 * sizeof(video_ctx_color_t));
ctx->rom = malloc(4096);
ctx->direct_buf = p_alloc(shoe.pool, (ctx->pixels+4) * sizeof(video_ctx_color_t));
ctx->temp_buf = p_alloc(shoe.pool, (ctx->pixels+4) * sizeof(video_ctx_color_t));
ctx->clut = p_alloc(shoe.pool, 256 * sizeof(video_ctx_color_t));
ctx->rom = p_alloc(shoe.pool, 4096);
// Set the depth and clut for B&W
_switch_depth(ctx, 1);
bzero(ctx->clut, 256 * 4);
memset(ctx->clut, 0, 256 * 4);
ctx->clut[0].r = 0xff;
ctx->clut[0].g = 0xff;
ctx->clut[0].b = 0xff;
@ -128,13 +124,13 @@ void nubus_video_init(void *_ctx, uint8_t slotnum,
params[3].right = htons(width);
params[3].bottom = htons(height);
/*params[4].line_width = htons(scanline_width * 2);
params[4].line_width = htons(scanline_width * 2);
params[4].right = htons(width);
params[4].bottom = htons(height);
params[5].line_width = htons(scanline_width * 4);
params[5].right = htons(width);
params[5].bottom = htons(height);*/
params[5].bottom = htons(height);
// Recompute the rom crc
compute_nubus_crc(ctx->rom, 4096);
@ -149,9 +145,9 @@ uint32_t nubus_video_read_func(const uint32_t rawaddr, const uint32_t size,
const uint32_t addr = rawaddr & 0x00ffffff;
// ROM and control registers
if ((addr >> 20) == 0xf) {
if sunlikely((addr >> 20) == 0xf) {
printf("nubus_video_read_func: got a read to 0x%08x sz=%u\n", rawaddr, size);
slog("nubus_video_read_func: got a read to 0x%08x sz=%u\n", rawaddr, size);
// ROM (0xFsFFxxxx)
if ((addr >> 16) == 0xff) {
@ -175,27 +171,72 @@ uint32_t nubus_video_read_func(const uint32_t rawaddr, const uint32_t size,
// Else, this is video ram
uint32_t i, myaddr = addr % ctx->pixels, result = 0;
for (i=0; i<size; i++) {
result <<= 8;
result |= ctx->indexed_buf[(myaddr + i) % ctx->pixels];
uint32_t i, result = 0;
if slikely(addr < (ctx->pixels * 4)) {
for (i=0; i<size; i++)
result = (result << 8) | ((uint8_t*)ctx->direct_buf)[addr + i];
}
return result;
}
void _gray_page(shoebill_card_video_t *ctx)
{
uint32_t h, w, i;
if (ctx->depth <= 8) {
uint16_t pat;
uint8_t *ptr = ctx->direct_buf;
uint32_t width_bytes = (ctx->depth * ctx->scanline_width) / 8;
switch (ctx->depth) {
case 1:
pat = 0xaaaa;
break;
case 2:
pat = 0xcccc;
break;
case 4:
pat = 0xf0f0;
break;
case 8:
pat = 0x00ff;
break;
}
for (h=0; h<ctx->height; h++) {
for (w=0; w < width_bytes; w++)
ptr[w] = pat >> ((w&1)*8);
ptr += width_bytes;
pat ^= 0xffff;
}
}
else if (ctx->depth == 16) {
const uint16_t fill_word = htons(0x4210);
uint16_t *ptr = (uint16_t*)ctx->direct_buf;
for (i=0; i < (ctx->width * ctx->height); i++)
ptr[i] = fill_word;
}
else if (ctx->depth == 32) {
const uint32_t fill_long = htonl(0x00808080);
uint32_t *ptr = (uint32_t*)ctx->direct_buf;
for (i=0; i < (ctx->width * ctx->height); i++)
ptr[i] = fill_long;
}
else
assert(!"unknown depth");
}
void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size,
const uint32_t data, const uint8_t slotnum)
{
shoebill_card_video_t *ctx = (shoebill_card_video_t*)shoe.slots[slotnum].ctx;
const uint32_t addr = rawaddr & 0x00ffffff;
uint32_t i;
// ROM and control registers
if ((addr >> 20) == 0xf) {
if sunlikely((addr >> 20) == 0xf) {
printf("nubus_video_write_func: got a write to 0x%08x sz=%u data=0x%x\n", rawaddr, size, data);
slog("nubus_video_write_func: got a write to 0x%08x sz=%u data=0x%x\n", rawaddr, size, data);
// ROM (0xFsFFxxxx)
if ((addr >> 16) == 0xff)
@ -207,7 +248,7 @@ void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size,
if ((addr >> 16) == 0xf0) {
switch ((addr & 0x0000ffff) >> 2) {
case 0: {// Clear interrupt flag
shoe.via[1].rega |= (1 << (slotnum - 9));
shoe.via[1].rega_input |= (1 << (slotnum - 9));
break;
}
case 1: { // Set depth
@ -235,35 +276,62 @@ void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size,
assert(!"driver tried to set bogus depth");
}
_switch_depth(ctx, newdepth);
printf("nubus_magic: set depth = %u\n", ctx->depth);
slog("nubus_magic: set depth = %u\n", ctx->depth);
break;
}
case 2: { // Gray out screen
// FIXME: implement me
printf("nubus_magic: grey screen\n");
case 2: { // Gray out screen buffer
_gray_page(ctx);
slog("nubus_magic: grey screen\n");
break;
}
case 3: { // Set clut index
ctx->clut_idx = data & 0xff;
// assert(ctx->clut_idx < 256);
printf("nubus_magic: set clut_idx = %u\n", ctx->clut_idx);
slog("nubus_magic: set clut_idx = %u\n", ctx->clut_idx);
break;
}
case 4: { // Set red component of clut
ctx->clut[ctx->clut_idx].r = (data >> 8) & 0xff;
printf("nubus_magic: set %u.red = 0x%04x\n", ctx->clut_idx, data);
slog("nubus_magic: set %u.red = 0x%04x\n", ctx->clut_idx, data);
break;
}
case 5: { // Set green component of clut
ctx->clut[ctx->clut_idx].g = (data >> 8) & 0xff;
printf("nubus_magic: set %u.green = 0x%04x\n", ctx->clut_idx, data);
slog("nubus_magic: set %u.green = 0x%04x\n", ctx->clut_idx, data);
break;
}
case 6: { // Set blue component of clut
ctx->clut[ctx->clut_idx].b = (data >> 8) & 0xff;
printf("nubus_magic: set %u.blue = 0x%04x\n", ctx->clut_idx, data);
slog("nubus_magic: set %u.blue = 0x%04x\n", ctx->clut_idx, data);
break;
}
case 7: { // Set interrupts
shoe.slots[slotnum].interrupts_enabled = (data != 0);
slog("nubus_magic: interrupts_enabled = %u\n",
shoe.slots[slotnum].interrupts_enabled);
break;
}
case 8: { // Debug
slog("video_debug: 0x%08x\n", data);
break;
}
case 9: { // Gray out CLUT
if (!data) break;
for (i=0; i<256; i++) {
ctx->clut[i].r = 0x80;
ctx->clut[i].g = 0x80;
ctx->clut[i].b = 0x80;
}
break;
}
case 10: { // Use luminance (a.k.a. setGray)
slog("nubus_magic: use_luminance = %u\n", data);
// FIXME: not implemented
break;
}
}
}
@ -272,20 +340,112 @@ void nubus_video_write_func(const uint32_t rawaddr, const uint32_t size,
// Else, this is video ram
uint32_t myaddr, mydata;
for (myaddr = addr + size, mydata = data; addr < myaddr; ) {
// assert(myaddr < ctx->pixels)
ctx->indexed_buf[(--myaddr) % ctx->pixels] = mydata & 0xff;
mydata >>= 8;
if slikely(addr < (ctx->pixels * 4)) {
uint32_t mydata, myaddr;
for (myaddr = addr + size, mydata = data; addr < myaddr; ) {
((uint8_t*)ctx->direct_buf)[--myaddr] = mydata & 0xff;
mydata >>= 8;
}
}
}
static void _do_clut_translation(shoebill_card_video_t *ctx)
{
uint32_t i;
switch (ctx->depth) {
case 1: {
for (i=0; i < ctx->pixels/8; i++) {
const uint8_t byte = ctx->direct_buf[i];
ctx->temp_buf[i * 8 + 0] = ctx->clut[(byte >> 7) & 1];
ctx->temp_buf[i * 8 + 1] = ctx->clut[(byte >> 6) & 1];
ctx->temp_buf[i * 8 + 2] = ctx->clut[(byte >> 5) & 1];
ctx->temp_buf[i * 8 + 3] = ctx->clut[(byte >> 4) & 1];
ctx->temp_buf[i * 8 + 4] = ctx->clut[(byte >> 3) & 1];
ctx->temp_buf[i * 8 + 5] = ctx->clut[(byte >> 2) & 1];
ctx->temp_buf[i * 8 + 6] = ctx->clut[(byte >> 1) & 1];
ctx->temp_buf[i * 8 + 7] = ctx->clut[(byte >> 0) & 1];
}
break;
}
case 2: {
for (i=0; i < ctx->pixels/4; i++) {
const uint8_t byte = ctx->direct_buf[i];
ctx->temp_buf[i * 4 + 0] = ctx->clut[(byte >> 6) & 3];
ctx->temp_buf[i * 4 + 1] = ctx->clut[(byte >> 4) & 3];
ctx->temp_buf[i * 4 + 2] = ctx->clut[(byte >> 2) & 3];
ctx->temp_buf[i * 4 + 3] = ctx->clut[(byte >> 0) & 3];
}
break;
}
case 4: {
for (i=0; i < ctx->pixels/2; i++) {
const uint8_t byte = ctx->direct_buf[i];
ctx->temp_buf[i * 2 + 0] = ctx->clut[(byte >> 4) & 0xf];
ctx->temp_buf[i * 2 + 1] = ctx->clut[(byte >> 0) & 0xf];
}
break;
}
case 8: {
for (i=0; i < ctx->pixels; i++)
ctx->temp_buf[i] = ctx->clut[ctx->direct_buf[i]];
break;
}
case 16: {
uint16_t *direct = (uint16_t*)ctx->direct_buf;
for (i=0; i < ctx->pixels; i++) {
const uint16_t p = ntohs(direct[i]);
video_ctx_color_t tmp;
tmp.r = ((p >> 10) & 31);
tmp.g = (p >> 5) & 31;
tmp.b = (p >> 0) & 31;
ctx->temp_buf[i].r = (tmp.r << 3) | (tmp.r >> 2);
ctx->temp_buf[i].g = (tmp.g << 3) | (tmp.g >> 2);
ctx->temp_buf[i].b = (tmp.b << 3) | (tmp.b >> 2);
}
break;
}
case 32: {
uint32_t *direct = (uint32_t*)ctx->direct_buf, *tmp = (uint32_t*)ctx->temp_buf;
for (i=0; i < ctx->pixels; i++)
tmp[i] = direct[i] >> 8;
// OpenGL wants RGBA
// Apple must be ARGB (which is BGRA, when dereferenced)
break;
}
default:
assert(!"unsupported depth");
}
}
shoebill_video_frame_info_t nubus_video_get_frame(shoebill_card_video_t *ctx,
_Bool just_params)
{
shoebill_video_frame_info_t result;
result.width = ctx->width;
result.height = ctx->height;
result.scan_width = ctx->scanline_width;
result.depth = ctx->depth;
// If caller just wants video parameters...
if (just_params)
return result;
_do_clut_translation(ctx);
result.buf = (uint8_t*)ctx->temp_buf;
return result;
}

Binary file not shown.

View File

@ -22,92 +22,156 @@ uint8_t _video_rom[4096] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x80, 0x00,
0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08,
0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x0c, 0x80, 0x00, 0x00, 0x7e,
0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x18,
0x02, 0x00, 0x00, 0x1c, 0x20, 0x00, 0x00, 0x50,
0x22, 0x00, 0x00, 0x2c, 0x24, 0x00, 0x00, 0x3e,
0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6f, 0x65,
0x62, 0x69, 0x6c, 0x6c, 0x20, 0x50, 0x68, 0x6f,
0x6e, 0x79, 0x20, 0x56, 0x69, 0x64, 0x65, 0x6f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16,
0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00,
0x0b, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03, 0x84,
0x05, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x04,
0x80, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x03,
0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x16, 0x80,
0x00, 0x00, 0x00, 0x00, 0x03, 0x84, 0x05, 0xa0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x80, 0x00, 0x00, 0x04, 0x80, 0x00,
0x00, 0x10, 0x00, 0x20, 0x00, 0x03, 0x00, 0x08,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10,
0x80, 0x00, 0x01, 0x66, 0x81, 0x00, 0x01, 0x2e,
0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1c,
0x02, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x50,
0x22, 0x00, 0x00, 0x30, 0x24, 0x00, 0x00, 0xee,
0x26, 0x00, 0x00, 0x3c, 0xff, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x53, 0x68, 0x6f, 0x65, 0x62, 0x69, 0x6c, 0x6c,
0x20, 0x50, 0x68, 0x6f, 0x6e, 0x79, 0x20, 0x56,
0x69, 0x64, 0x65, 0x6f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x14, 0x02, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x31, 0x7c, 0x00, 0x01,
0x00, 0x02, 0x4e, 0x75, 0x00, 0x00, 0x00, 0xae,
0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x31, 0x7c, 0x00, 0x01, 0x00, 0x02, 0x2a, 0x48,
0x31, 0x7c, 0x00, 0x01, 0x00, 0x02, 0x10, 0x10,
0x12, 0x00, 0x00, 0x01, 0x00, 0xf0, 0xe0, 0x99,
0x22, 0x41, 0x9e, 0xfc, 0x00, 0x38, 0x20, 0x4f,
0x11, 0x40, 0x00, 0x31, 0x42, 0x28, 0x00, 0x33,
0x42, 0xa8, 0x00, 0x04, 0x20, 0x3c, 0x00, 0x00,
0xa8, 0x9f, 0xa7, 0x46, 0x22, 0x08, 0x20, 0x3c,
0x00, 0x00, 0xab, 0x03, 0xa7, 0x46, 0xb1, 0xc1,
0x67, 0x62, 0x20, 0x4f, 0x42, 0x28, 0x00, 0x32,
0x42, 0x28, 0x00, 0x30, 0x31, 0x7c, 0x00, 0x03,
0x00, 0x28, 0x31, 0x7c, 0x00, 0x01, 0x00, 0x2a,
0x31, 0x7c, 0x00, 0x01, 0x00, 0x2c, 0x31, 0x7c,
0x54, 0x03, 0x00, 0x2e, 0x70, 0x15, 0xa0, 0x6e,
0x3a, 0x28, 0x00, 0x26, 0x0c, 0x28, 0x00, 0x81,
0x00, 0x32, 0x67, 0x00, 0x00, 0x30, 0x70, 0x31,
0xa0, 0x6e, 0x11, 0x78, 0x00, 0x81, 0x00, 0x32,
0x42, 0x68, 0x00, 0x26, 0x42, 0xa8, 0x00, 0x18,
0x42, 0xa8, 0x00, 0x04, 0x70, 0x0a, 0xa0, 0x6e,
0x59, 0x4f, 0xaa, 0x29, 0x20, 0x5f, 0x20, 0x50,
0xba, 0x50, 0x66, 0x08, 0x20, 0x68, 0x00, 0x16,
0x20, 0x50, 0x20, 0x89, 0x4f, 0xef, 0x00, 0x38,
0x4e, 0x75, 0x01, 0x00, 0x00, 0x0c, 0x03, 0x00,
0x00, 0x14, 0x04, 0x00, 0x00, 0x18, 0x53, 0x68,
0x6f, 0x65, 0x62, 0x69, 0x6c, 0x6c, 0x00, 0x00,
0x00, 0x00, 0x52, 0x65, 0x76, 0x2d, 0x31, 0x00,
0x00, 0x00, 0x4d, 0x6f, 0x6f, 0x66, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x9c, 0x02, 0x00,
0x00, 0x78, 0x04, 0x00, 0x00, 0x9c, 0x08, 0x00,
0x00, 0x01, 0x0c, 0x00, 0x00, 0x64, 0x0d, 0x00,
0x00, 0x64, 0x80, 0x00, 0x00, 0x14, 0x81, 0x00,
0x00, 0x20, 0x82, 0x00, 0x00, 0x2c, 0x83, 0x00,
0x00, 0x38, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff,
0xfe, 0x9a, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff,
0xfe, 0xb8, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff,
0xfe, 0xd6, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 0xff,
0xfe, 0xf4, 0x03, 0x00, 0x00, 0x01, 0x04, 0x00,
0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x44, 0x69,
0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x56, 0x69,
0x64, 0x65, 0x6f, 0x5f, 0x41, 0x70, 0x70, 0x6c,
0x65, 0x5f, 0x53, 0x68, 0x6f, 0x65, 0x62, 0x69,
0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x00, 0x01, 0x00, 0x01, 0x54, 0x03, 0x02, 0x00,
0x00, 0x08, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xa6, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x62,
0x01, 0x62, 0x01, 0x70, 0x1d, 0x2e, 0x44, 0x69,
0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x56, 0x69,
0x64, 0x65, 0x6f, 0x5f, 0x41, 0x70, 0x70, 0x6c,
0x65, 0x5f, 0x53, 0x68, 0x6f, 0x65, 0x62, 0x69,
0x6c, 0x6c, 0x00, 0x00, 0x24, 0x48, 0x26, 0x49,
0x70, 0x10, 0xa7, 0x1e, 0x66, 0x00, 0x01, 0x64,
0x49, 0xfa, 0x01, 0x3e, 0x31, 0x7c, 0x00, 0x06,
0x00, 0x04, 0x21, 0x4c, 0x00, 0x08, 0x21, 0x6b,
0x00, 0x2a, 0x00, 0x0c, 0x70, 0x00, 0x10, 0x2b,
0x00, 0x28, 0xa0, 0x75, 0x66, 0x00, 0x01, 0x44,
0x70, 0x00, 0x4e, 0x75, 0x48, 0xe7, 0x7f, 0xfe,
0x72, 0x00, 0x32, 0x28, 0x00, 0x1a, 0x24, 0x69,
0x00, 0x00, 0x01, 0x00, 0x00, 0xf0, 0x02, 0x00,
0x00, 0xcc, 0x04, 0x00, 0x00, 0xf0, 0x08, 0x00,
0x00, 0x01, 0x0c, 0x00, 0x00, 0xb8, 0x0d, 0x00,
0x00, 0xb8, 0x80, 0x00, 0x00, 0x48, 0x81, 0x00,
0x00, 0x54, 0x82, 0x00, 0x00, 0x60, 0x83, 0x00,
0x00, 0x6c, 0x84, 0x00, 0x00, 0x78, 0x85, 0x00,
0x00, 0x84, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0xbc, 0x02, 0x00, 0x00, 0x98, 0x04, 0x00,
0x00, 0xbc, 0x08, 0x00, 0x00, 0x01, 0x0c, 0x00,
0x00, 0x84, 0x0d, 0x00, 0x00, 0x84, 0x80, 0x00,
0x00, 0x14, 0x81, 0x00, 0x00, 0x20, 0x82, 0x00,
0x00, 0x2c, 0x83, 0x00, 0x00, 0x38, 0xff, 0x00,
0x00, 0x00, 0x01, 0xff, 0xfd, 0x56, 0x03, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x01, 0xff, 0xfd, 0x74, 0x03, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x01, 0xff, 0xfd, 0x92, 0x03, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x01, 0xff, 0xfd, 0xb0, 0x03, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0xff, 0x00,
0x00, 0x00, 0x01, 0xff, 0xfd, 0xce, 0x03, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0xff, 0x00,
0x00, 0x00, 0x01, 0xff, 0xfd, 0xec, 0x03, 0x00,
0x00, 0x01, 0x04, 0x00, 0x00, 0x02, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
0x00, 0x00, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61,
0x79, 0x5f, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x5f,
0x41, 0x70, 0x70, 0x6c, 0x65, 0x5f, 0x53, 0x68,
0x6f, 0x65, 0x62, 0x69, 0x6c, 0x6c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01,
0x54, 0x03, 0x02, 0x00, 0x00, 0x08, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x42, 0x4c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32,
0x00, 0x00, 0x00, 0x62, 0x01, 0xd4, 0x01, 0xf8,
0x1d, 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61,
0x79, 0x5f, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x5f,
0x41, 0x70, 0x70, 0x6c, 0x65, 0x5f, 0x53, 0x68,
0x6f, 0x65, 0x62, 0x69, 0x6c, 0x6c, 0x00, 0x00,
0x24, 0x48, 0x26, 0x49, 0x70, 0x10, 0xa7, 0x1e,
0x66, 0x00, 0x02, 0x00, 0x49, 0xfa, 0x01, 0xda,
0x31, 0x7c, 0x00, 0x06, 0x00, 0x04, 0x21, 0x4c,
0x00, 0x08, 0x21, 0x6b, 0x00, 0x2a, 0x00, 0x0c,
0x70, 0x00, 0x10, 0x2b, 0x00, 0x28, 0xa0, 0x75,
0x66, 0x00, 0x01, 0xe0, 0x70, 0x00, 0x4e, 0x75,
0x48, 0xe7, 0x7f, 0xfe, 0x72, 0x00, 0x32, 0x28,
0x00, 0x1a, 0x24, 0x69, 0x00, 0x2a, 0xd5, 0xfc,
0x00, 0xf0, 0x00, 0x00, 0x26, 0x68, 0x00, 0x1c,
0x0c, 0x41, 0x00, 0x00, 0x66, 0x00, 0x00, 0x16,
0x25, 0x78, 0x00, 0x80, 0x00, 0x04, 0x25, 0x7c,
0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x70, 0x00,
0x60, 0x00, 0x01, 0x3a, 0x0c, 0x41, 0x00, 0x01,
0x66, 0x00, 0x00, 0x08, 0x70, 0x00, 0x60, 0x00,
0x01, 0x2c, 0x0c, 0x41, 0x00, 0x02, 0x66, 0x00,
0x00, 0x12, 0x15, 0x7c, 0x00, 0x01, 0x00, 0x24,
0x35, 0x53, 0x00, 0x04, 0x70, 0x00, 0x60, 0x00,
0x01, 0x14, 0x0c, 0x41, 0x00, 0x03, 0x66, 0x00,
0x00, 0x4a, 0x28, 0x53, 0x74, 0x00, 0x34, 0x2b,
0x00, 0x04, 0x76, 0x00, 0x36, 0x2b, 0x00, 0x06,
0x0c, 0x42, 0xff, 0xff, 0x66, 0x00, 0x00, 0x04,
0x4e, 0x70, 0x28, 0x02, 0xe7, 0x8c, 0xd9, 0xc4,
0x52, 0x83, 0x78, 0x00, 0x25, 0x42, 0x00, 0x0c,
0x35, 0x6c, 0x00, 0x02, 0x00, 0x10, 0x35, 0x6c,
0x00, 0x04, 0x00, 0x14, 0x35, 0x6c, 0x00, 0x06,
0x00, 0x18, 0x52, 0x82, 0x50, 0x8c, 0x52, 0x84,
0xb6, 0x44, 0x66, 0xe0, 0x70, 0x00, 0x60, 0x00,
0x00, 0xc4, 0x0c, 0x41, 0x00, 0x04, 0x66, 0x00,
0x00, 0x12, 0x28, 0x3c, 0xb0, 0x04, 0x00, 0x00,
0x25, 0x44, 0x00, 0x20, 0x70, 0xef, 0x60, 0x00,
0x00, 0xac, 0x0c, 0x41, 0x00, 0x05, 0x66, 0x00,
0x00, 0x16, 0x28, 0x3c, 0xb0, 0x05, 0x00, 0x00,
0x25, 0x44, 0x00, 0x20, 0x42, 0xaa, 0x00, 0x08,
0x70, 0x00, 0x60, 0x00, 0x00, 0x90, 0x0c, 0x41,
0x00, 0x06, 0x66, 0x00, 0x00, 0x16, 0x28, 0x3c,
0xb0, 0x06, 0x00, 0x00, 0x25, 0x44, 0x00, 0x20,
0x15, 0x53, 0x00, 0x28, 0x70, 0x00, 0x60, 0x00,
0x00, 0x74, 0x0c, 0x41, 0x00, 0x07, 0x66, 0x00,
0x00, 0x1a, 0x28, 0x3c, 0xb0, 0x07, 0x00, 0x00,
0x18, 0x13, 0x25, 0x44, 0x00, 0x20, 0x12, 0x13,
0x15, 0x41, 0x00, 0x1c, 0x70, 0x00, 0x60, 0x00,
0x00, 0x54, 0x0c, 0x41, 0x00, 0x08, 0x66, 0x00,
0x00, 0x08, 0x70, 0xef, 0x60, 0x00, 0x00, 0x46,
0x0c, 0x41, 0x00, 0x09, 0x66, 0x00, 0x00, 0x3c,
0x28, 0x3c, 0xb0, 0x09, 0x00, 0x00, 0x18, 0x13,
0x25, 0x44, 0x00, 0x20, 0x9e, 0xfc, 0x00, 0x38,
0x20, 0x4f, 0x11, 0x69, 0x00, 0x28, 0x00, 0x31,
0x42, 0x28, 0x00, 0x33, 0x51, 0x4f, 0x20, 0x8f,
0x70, 0x11, 0xa0, 0x6e, 0x1f, 0x53, 0x00, 0x03,
0x21, 0x4f, 0x00, 0x04, 0x70, 0x12, 0xa0, 0x6e,
0x4f, 0xef, 0x00, 0x40, 0x70, 0x00, 0x60, 0x00,
0x00, 0x04, 0x70, 0xef, 0x4c, 0xdf, 0x7f, 0xfe,
0x4e, 0x75, 0x48, 0xe7, 0x7f, 0xfe, 0x24, 0x69,
0x00, 0x2a, 0xd5, 0xfc, 0x00, 0xf0, 0x00, 0x00,
0x26, 0x68, 0x00, 0x1c, 0x0c, 0x41, 0x00, 0x00,
0x66, 0x00, 0x00, 0x16, 0x25, 0x78, 0x00, 0x80,
0x00, 0x04, 0x25, 0x7c, 0x00, 0x00, 0x00, 0x01,
0x00, 0x08, 0x70, 0x00, 0x60, 0x00, 0x00, 0xc8,
0x0c, 0x41, 0x00, 0x01, 0x66, 0x00, 0x00, 0x08,
0x70, 0x00, 0x60, 0x00, 0x00, 0xba, 0x0c, 0x41,
0x00, 0x02, 0x66, 0x00, 0x00, 0x0c, 0x35, 0x53,
0x00, 0x04, 0x70, 0x00, 0x60, 0x00, 0x00, 0xa8,
0x0c, 0x41, 0x00, 0x03, 0x66, 0x00, 0x00, 0x4a,
0x28, 0x53, 0x74, 0x00, 0x34, 0x2b, 0x00, 0x04,
0x76, 0x00, 0x36, 0x2b, 0x00, 0x06, 0x0c, 0x42,
0xff, 0xff, 0x66, 0x00, 0x00, 0x04, 0x4e, 0x70,
0x28, 0x02, 0xe7, 0x8c, 0xd9, 0xc4, 0x52, 0x83,
0x78, 0x00, 0x25, 0x42, 0x00, 0x0c, 0x35, 0x6c,
0x00, 0x02, 0x00, 0x10, 0x35, 0x6c, 0x00, 0x04,
0x00, 0x14, 0x35, 0x6c, 0x00, 0x06, 0x00, 0x18,
0x52, 0x82, 0x50, 0x8c, 0x52, 0x84, 0xb6, 0x44,
0x66, 0xe0, 0x70, 0xef, 0x60, 0x00, 0x00, 0x58,
0x0c, 0x41, 0x00, 0x04, 0x66, 0x00, 0x00, 0x08,
0x70, 0xef, 0x60, 0x00, 0x00, 0x4a, 0x0c, 0x41,
0x00, 0x05, 0x66, 0x00, 0x00, 0x08, 0x70, 0xef,
0x60, 0x00, 0x00, 0x3c, 0x0c, 0x41, 0x00, 0x06,
0x66, 0x00, 0x00, 0x08, 0x70, 0xef, 0x60, 0x00,
0x00, 0x2e, 0x0c, 0x41, 0x00, 0x07, 0x66, 0x00,
0x00, 0x08, 0x70, 0xef, 0x60, 0x00, 0x00, 0x20,
0x0c, 0x41, 0x00, 0x08, 0x66, 0x00, 0x00, 0x08,
0x70, 0xef, 0x60, 0x00, 0x00, 0x12, 0x0c, 0x41,
0x00, 0x09, 0x66, 0x00, 0x00, 0x08, 0x70, 0xef,
0x60, 0x00, 0x00, 0x04, 0x70, 0xef, 0x4c, 0xdf,
0x7f, 0xfe, 0x4e, 0x75, 0x26, 0x7c, 0xfa, 0x00,
0xbe, 0xef, 0x26, 0xbc, 0xde, 0xad, 0xbe, 0xef,
0x4e, 0x70, 0x26, 0x7c, 0xfa, 0x00, 0xbe, 0xef,
0x26, 0xbc, 0xde, 0xad, 0xbe, 0xef, 0x4e, 0x70,
0x25, 0x7c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x20,
0x70, 0xee, 0x60, 0x00, 0x00, 0x04, 0x70, 0xee,
0x4c, 0xdf, 0x7f, 0xfe, 0x4e, 0x75, 0x48, 0xe7,
0x7f, 0xfe, 0x24, 0x69, 0x00, 0x2a, 0xd5, 0xfc,
0x00, 0xf0, 0x00, 0x00, 0x42, 0xaa, 0x00, 0x1c,
0x25, 0x7c, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x20,
0x4c, 0xdf, 0x7f, 0xfe, 0x70, 0x00, 0x4e, 0x75,
0x20, 0x09, 0xe1, 0x98, 0x02, 0x80, 0x00, 0x00,
0x00, 0x0f, 0x20, 0x49, 0xd1, 0xfc, 0x00, 0xf0,
0x00, 0x00, 0x20, 0xbc, 0x00, 0x00, 0x00, 0x01,
@ -444,70 +508,7 @@ uint8_t _video_rom[4096] = {
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 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,
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, 0x00, 0xff, 0xf0, 0xcc,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf1, 0x28,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x5a, 0x93, 0x2b, 0xc7, 0x00, 0xe1};
0x01, 0x01, 0x5a, 0x93, 0x2b, 0xc7, 0x00, 0xe1
};

View File

@ -10,16 +10,16 @@ int main (int argc, char **argv)
uint32_t i;
if (argc != 3) {
printf("usage ./rom_to_c video_rom.bin video_rom.c\n");
printf("usage ./rom_to_c video|ethernet rom.bin rom.c\n");
return 0;
}
assert(in = fopen(argv[1], "r"));
assert(out = fopen(argv[2], "w"));
assert(in = fopen(argv[2], "rb"));
assert(out = fopen(argv[3], "w"));
assert(fread(rom, 4096, 1, in) == 1);
fprintf(out, "uint8_t _video_rom[4096] = {\n\t");
fprintf(out, "static uint8_t _%s_rom[4096] = {\n\t", argv[1]);
for (i=0; i<4095; i++) {
fprintf(out, "0x%02x, ", rom[i]);
if ((i % 8) == 7)

0
core/video_rom/shoebill_video.make Normal file → Executable file
View File

2
core/video_rom/shoebill_video_driver.a Normal file → Executable file

File diff suppressed because one or more lines are too long

2
core/video_rom/shoebill_video_primary_init.a Normal file → Executable file
View File

@ -1 +1 @@
BLANKS ON STRING ASIS MACHINE MC68020 WITH seBlock DC.B 2 ; Code revision DC.B 2 ; 68020 DC.W 0 ; reserved DC.L PrimaryInitStart-* PrimaryInitStart move.w #1, seStatus(a0) ; seStatus > 0 -> success move.l a0, a5 rts PrimaryInitEnd
BLANKS ON STRING ASIS MACHINE MC68020 WITH seBlock DC.B 2 ; Code revision DC.B 2 ; 68020 DC.W 0 ; reserved DC.L PrimaryInitStart-* PrimaryInitStart ; Return "success" move.w #1, seStatus(a0) ; seStatus > 0 -> success ; FIXME: Designing Cards and Drivers for the Macintosh Family says I should disable VBLs here... ; FIXME: Also gray out the screen ; Usually, we would check if 32bit QD exists ; If it doesn't, then we'd try to invalidate sResourceVideo_qd32 ; unless the Mac II slot manager is loaded, in which case, we can't actually ; invalidate anything. ; Since Shoebill very likely won't support non-Mac II ROMs for a while, ; we have nothing to do rts PrimaryInitEnd

2
core/video_rom/shoebill_video_rom.a Normal file → Executable file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
BLANKS ON STRING ASIS MACHINE MC68020 WITH seBlock,spBlock DC.B 2 ; Code revision DC.B 2 ; 68020 DC.W 0 ; reserved DC.L SecondaryInitStart-* SecondaryInitStart ; Return "success" move.w #1, seStatus(a0) ; seStatus > 0 -> success ; Figure out the slot base address (non-superslot) move.b seSlot(a0), d0 ; -> d0.b = slotnum move.b d0, d1 or.b #$F0, d1 ror.l #8, d1 move.l d1, a1 ; -> a1 = slot base addr ; Set up a slot parameter block suba #spBlockSize, sp move.l sp, a0 move.b d0, spSlot(a0) clr.b spExtDev(a0) clr.l spsPointer(a0) ; Verify that 32bit QD is loaded move.l #$a89f, d0 _GetTrapAddress ,NewTool move.l a0, d1 move.l #$ab03, d0 _GetTrapAddress ,NewTool cmpa.l d1, a0 beq.s sec_done ; Find the current video sResource move.l sp, a0 clr.b spID(a0) ; start at id=0 clr.b spTBMask(a0) ; match exactly move.w #CatDisplay, spCategory(a0) move.w #TypVideo, spCType(a0) move.w #DrSwApple, spDrvrSW(a0) move.w #DrHwShoe, spDrvrHW(a0) _sNextTypesRsrc move.w spRefNum(a0), d5 ; If, somehow, the 32 bit video sResource is the only active one, then just return cmp.b #CategoryVideo_qd32, spID(a0) beq.w sec_done ; Otherwise, it's the non-32bit sResource - so nuke it _sDeleteSRTRec ; Load our PRAM bytes, which contain the default depth ; suba.l #sizesPRAMRec, sp ; _sReadPRAMRec ; move.b VendorUse2(sp), spID(a0) ; adda.l #sizesPRAMRec, sp ; And activate the 32bit sResource move.b CategoryVideo_qd32, spID(a0) clr.w spRefNum(a0) ; ? clr.l spParamData(a0) ; clear for activation clr.l spsPointer(a0) ; add back a sRsrc in directory _InsertSRTRec ; If this is the boot screen, then update its gDevice subq #4, sp ; make room for function return _GetDeviceList ; get the boot gDevice move.l (sp)+, a0 ; get the gdHandle move.l (a0), a0 ; get pointer to gDevice cmp.w gdRefNum(a0), d5 ; was this the boot device? bne.s sec_done ; No? then return move.l gdPMap(a0), a0 ; get pixMap handle move.l (a0), a0 ; getpixMap ptr move.l a1, pmBaseAddr(a0) ; save new base address ; FIXME: Wait, that wasn't necessary. All video modes have the same pmBaseAddr, I think sec_done adda #spBlockSize, sp rts SecondaryInitEnd

12
debugger/Makefile Normal file
View File

@ -0,0 +1,12 @@
CC = clang
CFLAGS = -O3 -ggdb -flto -Wno-deprecated-declarations
LFLAGS = -L ../intermediates -lshoebill_core -framework GLUT -framework OpenGL -ledit
all: debugger
debugger: Makefile debugger.c ../intermediates/libshoebill_core.a
$(CC) $(CFLAGS) $(LFLAGS) debugger.c -o debugger
clean:
rm -rf debugger

903
debugger/debugger.c Normal file
View File

@ -0,0 +1,903 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <GLUT/glut.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <histedit.h>
#include "../core/shoebill.h"
rb_tree *keymap;
struct dbg_state_t {
EditLine *el;
uint8_t running;
uint64_t breakpoint_counter;
dbg_breakpoint_t *breakpoints;
_Bool trace;
uint32_t slow_factor;
char *ring;
uint32_t ring_i, ring_len;
uint64_t op_count[0x10000];
};
struct dbg_state_t dbg_state;
void print_mmu_rp(uint64_t rp)
{
printf("lu=%u limit=0x%x sg=%u dt=%u addr=0x%08x\n", rp_lu(rp), rp_limit(rp), rp_sg(rp), rp_dt(rp), rp_addr(rp));
}
void printregs()
{
printf("[d0]%08x [d1]%08x [d2]%08x [d3]%08x\n", shoe.d[0], shoe.d[1], shoe.d[2], shoe.d[3]);
printf("[d4]%08x [d5]%08x [d6]%08x [d7]%08x\n", shoe.d[4], shoe.d[5], shoe.d[6], shoe.d[7]);
printf("[a0]%08x [a1]%08x [a2]%08x [a3]%08x\n", shoe.a[0], shoe.a[1], shoe.a[2], shoe.a[3]);
printf("[a4]%08x [a5]%08x [a6]%08x [a7]%08x\n", shoe.a[4], shoe.a[5], shoe.a[6], shoe.a[7]);
printf("[pc]%08x [sr]%c%c%c%c%c%c%c [tc]%08x\n", shoe.pc,
sr_s()?'S':'s',
sr_m()?'M':'m',
sr_x()?'X':'x',
sr_n()?'N':'n',
sr_z()?'Z':'z',
sr_v()?'V':'v',
sr_c()?'C':'c',
shoe.tc
);
printf("[vbr]%08x\n", shoe.vbr);
printf("srp: ");
print_mmu_rp(shoe.srp);
printf("crp: ");
print_mmu_rp(shoe.crp);
printf("tc: e=%u sre=%u fcl=%u ps=%u is=%u (tia=%u tib=%u tic=%u tid=%u)\n",
_tc_enable(), _tc_sre(), tc_fcl(), _tc_ps(), _tc_is(), tc_tia(), tc_tib(), tc_tic(), tc_tid());
printf("\n");
}
void print_pc()
{
char str[1024];
uint8_t binary[32];
uint32_t i;
uint32_t len;
const char *name = NULL;
if ((shoe.pc >= 0x40000000) && (shoe.pc < 0x50000000)) {
uint32_t i, addr = shoe.pc % (shoe.physical_rom_size);
for (i=0; macii_rom_symbols[i].name; i++) {
if (macii_rom_symbols[i].addr > addr) {
break;
}
name = macii_rom_symbols[i].name;
}
}
else if (sr_s()) { // these symbols are only meaningful in supervisor mode
coff_symbol *symb = coff_find_func(shoe.coff, shoe.pc);
if (symb && strlen(symb->name))
name = symb->name;
}
else {
if ((shoe.pc >= 0x10000000) && (shoe.pc < 0x20000000)) {
uint32_t i, addr = shoe.pc % (shoe.physical_rom_size);
for (i=0; macii_rom_symbols[i].name; i++) {
if (macii_rom_symbols[i].addr > addr) {
break;
}
name = macii_rom_symbols[i].name;
}
}
else {
name = "";
}
}
const uint16_t old_abort = shoe.abort;
shoe.suppress_exceptions = 1;
for (i=0; i<32; i++) {
binary[i] = (uint8_t) lget(shoe.pc+i, 1);
}
disassemble_inst(binary, shoe.pc, str, &len);
printf("*0x%08x %s [ ", shoe.pc, name ? name : "");
for (i=0; i<len; i+=2) {
printf("%02x%02x ", binary[i], binary[i+1]);
}
printf("] %s\n", str);
shoe.abort = old_abort;
shoe.suppress_exceptions = 0;
}
static void dump_proc(uint32_t procnum)
{
uint32_t u_proc_p;
uint16_t pid;
uint8_t do_print = 0, cpuflag;
// Only dump this process state if we're in user mode
if (sr_s())
return ;
shoe.suppress_exceptions = 1;
cpuflag = lget(0x0000012f, 1);
set_sr_s(1); // set supervisor mode so we can access the proc structure
u_proc_p = lget(0x1ff01000, 4);
if (shoe.abort)
goto done;
pid = lget(u_proc_p + 0x26, 2);
if (shoe.abort)
goto done;
do_print = 1;
done:
set_sr_s(0);
shoe.abort = 0;
shoe.suppress_exceptions = 0;
if (do_print) {
printf("pid = %u, cpuflag=0x%02x\n", pid, cpuflag);
// print_pc();
// printregs();
}
}
void verb_backtrace_handler (const char *line)
{
const uint32_t old_abort = shoe.abort;
shoe.suppress_exceptions = 1;
shoe.abort = 0;
// link
// push a6 to a7
// set a6 = a7
// set a7 = a7 - (some stack space)
// jsr
// push return pointer to a7
// call
// set a7 = a7 - (some stack space)
// push arguments to a7
// push return pointer to a7
// (jump to function)
// push
// bt algorithm
// set a7 = a6
// pop a7 -> a6
// pop a7 -> return pointer
uint32_t i, j, a7, a6 = shoe.a[6];
coff_symbol *symb;
if (sr_s()) {
symb = coff_find_func(shoe.coff, shoe.pc);
printf("%u: *0x%08x %s+%u\n", 0, shoe.pc, (symb && strlen(symb->name))?symb->name:"?", shoe.pc - symb->value);
}
else
printf("%u: *0x%08x\n", 0, shoe.pc);
for (i=1; 1; i++) {
a7 = a6;
const uint32_t last_a6 = lget(a7, 4);
const uint32_t last_pc = lget(a7+4, 4);
if ((last_a6 - a6) <= 1000) {
printf(" {");
for (j = a6+8; j < last_a6; j+=4) {
uint32_t data = lget(j, 4);
printf("%x, ", data);
}
printf("}\n");
}
if (sr_s()) {
symb = coff_find_func(shoe.coff, last_pc);
printf("%u: *0x%08x %s+%u\n", i, last_pc, (symb && strlen(symb->name))?symb->name:"?", last_pc - symb->value);
}
else
printf("%u: *0x%08x\n", i, last_pc);
if ((last_a6 - a6) > 1000) {
break;
}
a6 = last_a6;
}
shoe.suppress_exceptions = 0;
shoe.abort = old_abort;
}
void verb_break_handler (const char *line)
{
errno = 0;
const uint32_t addr = (uint32_t) strtoul(line, NULL, 0);
if (errno) {
printf("errno: %d\n", errno);
return ;
}
dbg_breakpoint_t *brk = calloc(sizeof(dbg_breakpoint_t), 1);
brk->next = NULL;
brk->addr = addr;
brk->num = dbg_state.breakpoint_counter++;
dbg_breakpoint_t **cur = &dbg_state.breakpoints;
while (*cur)
cur = &(*cur)->next;
*cur = brk;
printf("Set breakpoint %llu = *0x%08x\n", brk->num, brk->addr);
}
void verb_delete_handler (const char *line)
{
errno = 0;
uint64_t num = strtoull(line, NULL, 0);
if (errno) {
printf("errno: %d\n", errno);
return ;
}
dbg_breakpoint_t **cur = &dbg_state.breakpoints;
while (*cur) {
if ((*cur)->num == num) {
dbg_breakpoint_t *victim = *cur;
*cur = (*cur)->next;
free(victim);
return ;
}
cur = &(*cur)->next;
}
printf("No such breakpoint (#%llu)\n", num);
}
void verb_help_handler (const char *line)
{
printf("Help help help\n");
}
void verb_stepi_handler (const char *line)
{
dbg_state.running = 1;
cpu_step();
dbg_state.running = 0;
print_pc();
}
void verb_registers_handler (const char *line)
{
printregs();
}
void verb_trace_toggle_handler (const char *line)
{
dbg_state.trace = !dbg_state.trace;
}
void verb_examine_handler (const char *line)
{
uint32_t addr = (uint32_t)strtoul(line, NULL, 0);
uint32_t old_suppress = shoe.suppress_exceptions;
shoe.suppress_exceptions = 1;
printf("(uint32_t)*0x%08x = 0x%08x\n", addr, (uint32_t)lget(addr, 4));
shoe.suppress_exceptions = old_suppress;
}
void verb_lookup_handler (const char *line)
{
char *sym_name = malloc(strlen(line)+1);
sscanf(line, "%s", sym_name);
coff_symbol *symb = coff_find_symbol(shoe.coff, sym_name);
free(sym_name);
if (symb == NULL) {
printf("Couldn't find \"%s\"\n", sym_name);
return ;
}
printf("%s = *0x%08x\n", symb->name, symb->value);
}
void stepper()
{
dbg_breakpoint_t *cur;
if (shoe.cpu_thread_notifications) {
// If there's an interrupt pending
if (shoe.cpu_thread_notifications & 0xff) {
// process_pending_interrupt() may clear SHOEBILL_STATE_STOPPED
process_pending_interrupt();
}
if (shoe.cpu_thread_notifications & SHOEBILL_STATE_STOPPED) {
// I think it's safe to ignore STOP instructions...
}
}
cpu_step();
if (dbg_state.trace) {
print_pc();
printregs();
}
for (cur = dbg_state.breakpoints; cur != NULL; cur = cur->next) {
if (shoe.pc == cur->addr) {
printf("Hit breakpoint %llu *0x%08x\n", cur->num, shoe.pc);
dbg_state.running = 0;
return ;
}
}
}
void verb_continue_handler (const char *line)
{
dbg_state.running = 1;
while (dbg_state.running) {
if (dbg_state.slow_factor)
usleep(dbg_state.slow_factor);
stepper();
}
print_pc();
}
void verb_quit_handler (const char *line)
{
printf("Quitting\n");
fflush(stdout);
exit(0);
}
void verb_reset_handler (const char *line)
{
p_free_pool(shoe.pool);
shoe.pool = NULL;
}
void verb_slow_handler (const char *line)
{
const uint64_t usecs = strtoul(line, NULL, 0);
printf("Slow factor %u -> %u\n", dbg_state.slow_factor, (uint32_t)usecs);
dbg_state.slow_factor = usecs;
}
struct verb_handler_table_t {
const char *name;
void (*func)(const char *);
} verb_handler_table[] =
{
{"quit", verb_quit_handler},
{"continue", verb_continue_handler},
{"help", verb_help_handler},
{"registers", verb_registers_handler},
{"stepi", verb_stepi_handler},
{"backtrace", verb_backtrace_handler},
{"bt", verb_backtrace_handler},
{"break", verb_break_handler},
{"delete", verb_delete_handler},
{"lookup", verb_lookup_handler},
{"trace", verb_trace_toggle_handler},
{"x", verb_examine_handler},
{"reset", verb_reset_handler},
{"slow", verb_slow_handler},
};
void execute_verb (const char *line)
{
char verb[128];
uint32_t max_len=0, max_i=0;
const char *remainder;
uint32_t i, matches = 0, match_i;
if (sscanf(line, "%127s", verb) != 1)
return ;
// Skip past the verb
for (remainder = line; *remainder && !isspace(*remainder); remainder++)
;
// Skip past the space between the verb and the arguments
for (; *remainder && isspace(*remainder); remainder++)
;
const uint32_t verb_len = strlen(verb);
for (i=0; i < (sizeof(verb_handler_table) / sizeof(struct verb_handler_table_t)); i++) {
const uint32_t i_len = strlen(verb_handler_table[i].name);
// If it's a perfect match,
if (strcasecmp(verb, verb_handler_table[i].name)==0) {
verb_handler_table[i].func(remainder);
return ;
}
// Otherwise, see if it's a partial match
if ((i_len >= verb_len) && strncasecmp(verb, verb_handler_table[i].name, verb_len)==0) {
matches++;
match_i = i;
}
}
// Only execute the verb if it's an unambiguous match (matches == 1)
if (matches == 1) {
verb_handler_table[match_i].func(remainder);
return ;
}
printf(" %s?\n", verb);
}
char *cli_prompt_callback(EditLine *el)
{
return "~ ";
}
// Hack to clear line after ^C. el_reset() screws up tty when called from the signal handler.
void ch_reset(EditLine *el, int mclear);
void signal_callback(int sig)
{
EditLine *el = dbg_state.el;
(void) signal(SIGINT, signal_callback);
(void) signal(SIGWINCH, signal_callback);
switch (sig) {
case SIGWINCH:
el_resize(el);
break ;
case SIGINT:
if (dbg_state.running) {
dbg_state.running = 0;
}
else {
printf("\n");
ch_reset(el, 0);
el_set(el, EL_REFRESH);
}
break ;
}
return ;
}
void *ui_thread (void *arg)
{
EditLine *el;
History *hist;
HistEvent histev;
const char *buf;
int num;
hist = history_init();
history(hist, &histev, H_SETSIZE, 10000); // Remember 10000 previous user inputs
el = el_init("Shoebill", stdin, stdout, stderr);
dbg_state.el = el;
el_set(el, EL_SIGNAL, 0);
el_set(el, EL_PROMPT, cli_prompt_callback);
el_set(el, EL_EDITOR, "emacs");
el_set(el, EL_HIST, history, hist);
(void) signal(SIGINT, signal_callback);
(void) signal(SIGWINCH, signal_callback);
while ((buf = el_gets(el, &num)) != NULL) {
if (strcmp(buf, "\n")!=0) {
execute_verb(buf);
history(hist, &histev, H_ENTER, buf);
}
}
el_end(el);
history_end(hist);
return NULL;
}
static uint8_t lookup_special(int special)
{
switch (special) {
case GLUT_KEY_UP: return 0x3e;
case GLUT_KEY_DOWN: return 0x3d;
case GLUT_KEY_LEFT: return 0x3b;
case GLUT_KEY_RIGHT: return 0x3c;
default: return 0xff;
}
}
static uint32_t _get_modifiers (void)
{
int glut_modifiers = glutGetModifiers();
uint32_t result = 0;
result |= (glut_modifiers & GLUT_ACTIVE_SHIFT) ? (1 << 17) : 0;
result |= (glut_modifiers & GLUT_ACTIVE_CTRL) ? (1 << 18) : 0;
result |= (glut_modifiers & GLUT_ACTIVE_ALT) ? (1 << 19) : 0;
return result;
}
void global_mouse_func (int button, int state, int x, int y)
{
shoebill_mouse_click(state == GLUT_DOWN);
shoebill_mouse_move(x, y);
}
void global_motion_func (int x, int y)
{
shoebill_mouse_click(1);
shoebill_mouse_move(x, y);
}
void global_passive_motion_func (int x, int y)
{
shoebill_mouse_click(0);
shoebill_mouse_move(x, y);
}
void global_keyboard_up_func (unsigned char c, int x, int y)
{
uint16_t value;
if (rb_find(keymap, c, &value)) {
shoebill_key_modifier((value >> 8) | (_get_modifiers() >> 16));
shoebill_key(0, value & 0xff);
}
}
void global_keyboard_down_func (unsigned char c, int x, int y)
{
uint16_t value;
if (rb_find(keymap, c, &value)) {
shoebill_key_modifier((value >> 8) | (_get_modifiers() >> 16));
shoebill_key(1, value & 0xff);
}
}
void global_special_up_func (int special, int x, int y)
{
const uint8_t code = lookup_special(special);
if (code != 0xff) {
shoebill_key_modifier(_get_modifiers() >> 16);
shoebill_key(0, code);
}
}
void global_special_down_func (int special, int x, int y)
{
const uint8_t code = lookup_special(special);
if (code != 0xff) {
shoebill_key_modifier(_get_modifiers() >> 16);
shoebill_key(1, code);
}
}
void timer_func (int arg)
{
glutTimerFunc(15, timer_func, 0); // 15ms = 66.67hz
glutPostRedisplay();
}
void _display_func (void)
{
shoebill_video_frame_info_t frame = shoebill_get_video_frame(9, 0);
shoebill_send_vbl_interrupt(9);
glDrawBuffer(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0.0, 0.0, 0.0, 0.0);
glViewport(0, 0, frame.width, frame.height);
glRasterPos2i(0, frame.height);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelZoom(1.0, -1.0);
glDrawPixels(frame.width,
frame.height,
GL_RGBA,
GL_UNSIGNED_BYTE,
frame.buf);
glutSwapBuffers();
}
static void _init_keyboard_map (void)
{
#define mapkeymod(u, a, m) do { \
assert((a >> 7) == 0); \
uint16_t value = ((m) << 8)| (a); \
rb_insert(keymap, u, &value, NULL); \
} while (0)
#define mapkey(_u, a) mapkeymod(_u, a, 0)
keymap = rb_new(p_new_pool(NULL), sizeof(uint16_t));
// Letters
mapkey('a', 0x00);
mapkey('b', 0x0b);
mapkey('c', 0x08);
mapkey('d', 0x02);
mapkey('e', 0x0e);
mapkey('f', 0x03);
mapkey('g', 0x05);
mapkey('h', 0x04);
mapkey('i', 0x22);
mapkey('j', 0x26);
mapkey('k', 0x28);
mapkey('l', 0x25);
mapkey('m', 0x2e);
mapkey('n', 0x2d);
mapkey('o', 0x1f);
mapkey('p', 0x23);
mapkey('q', 0x0c);
mapkey('r', 0x0f);
mapkey('s', 0x01);
mapkey('t', 0x11);
mapkey('u', 0x20);
mapkey('v', 0x09);
mapkey('w', 0x0d);
mapkey('x', 0x07);
mapkey('y', 0x10);
mapkey('z', 0x06);
// Numbers
mapkey('0', 0x1d);
mapkey('1', 0x12);
mapkey('2', 0x13);
mapkey('3', 0x14);
mapkey('4', 0x15);
mapkey('5', 0x17);
mapkey('6', 0x16);
mapkey('7', 0x1a);
mapkey('8', 0x1c);
mapkey('9', 0x19);
// Top row symbols
mapkeymod(')', 0x1d, modShift);
mapkeymod('!', 0x12, modShift);
mapkeymod('@', 0x13, modShift);
mapkeymod('#', 0x14, modShift);
mapkeymod('$', 0x15, modShift);
mapkeymod('%', 0x17, modShift);
mapkeymod('^', 0x16, modShift);
mapkeymod('&', 0x1a, modShift);
mapkeymod('*', 0x1c, modShift);
mapkeymod('(', 0x19, modShift);
// Other symbols (no shift)
mapkeymod('`', 0x32, 0);
mapkeymod('-', 0x1b, 0);
mapkeymod('=', 0x18, 0);
mapkeymod('[', 0x21, 0);
mapkeymod(']', 0x1e, 0);
mapkeymod('\\', 0x2a, 0);
mapkeymod(';', 0x29, 0);
mapkeymod('\'', 0x27, 0);
mapkeymod(',', 0x2b, 0);
mapkeymod('.', 0x2f, 0);
mapkeymod('/', 0x2c, 0);
// Other symbols (with shift)
mapkeymod('~', 0x32, modShift);
mapkeymod('_', 0x1b, modShift);
mapkeymod('+', 0x18, modShift);
mapkeymod('{', 0x21, modShift);
mapkeymod('}', 0x1e, modShift);
mapkeymod('|', 0x2a, modShift);
mapkeymod(':', 0x29, modShift);
mapkeymod('"', 0x27, modShift);
mapkeymod('<', 0x2b, modShift);
mapkeymod('>', 0x2f, modShift);
mapkeymod('?', 0x2c, modShift);
// Function keys
/*mapkey(NSF1FunctionKey, 0x7a);
mapkey(NSF2FunctionKey, 0x78);
mapkey(NSF3FunctionKey, 0x63);
mapkey(NSF4FunctionKey, 0x76);
mapkey(NSF5FunctionKey, 0x60);
mapkey(NSF6FunctionKey, 0x61);
mapkey(NSF7FunctionKey, 0x62);
mapkey(NSF8FunctionKey, 0x64);
mapkey(NSF9FunctionKey, 0x65);
mapkey(NSF10FunctionKey, 0x6d);
mapkey(NSF11FunctionKey, 0x67);
mapkey(NSF12FunctionKey, 0x6f);
mapkey(NSF13FunctionKey, 0x69);
mapkey(NSF14FunctionKey, 0x6b);
mapkey(NSF15FunctionKey, 0x71);*/
// Arrows
/*mapkey(NSUpArrowFunctionKey, 0x3e);
mapkey(NSDownArrowFunctionKey, 0x3d);
mapkey(NSRightArrowFunctionKey, 0x3c);
mapkey(NSLeftArrowFunctionKey, 0x3b);*/
// Delete
//mapkey(NSDeleteFunctionKey, 0x75);
mapkey(0x08, 0x33);
mapkey(0x7f, 0x33);
// Enter, NL, CR
mapkey('\r', 0x24);
mapkey('\n', 0x24);
mapkey(0x03, 0x24);
// Other keys
mapkey(0x1b, 0x35); // escape
mapkey(' ', 0x31); // space
mapkey('\t', 0x30); // tab
}
static void _init_glut_video (void)
{
shoebill_video_frame_info_t frame = shoebill_get_video_frame(9, 1);
glutInitWindowSize(frame.width, frame.height);
glutCreateWindow("Shoebill");
glutDisplayFunc(_display_func);
glutIgnoreKeyRepeat(1);
glutKeyboardFunc(global_keyboard_down_func);
glutKeyboardUpFunc(global_keyboard_up_func);
glutSpecialFunc(global_special_down_func);
glutSpecialUpFunc(global_special_up_func);
glutMouseFunc(global_mouse_func);
glutMotionFunc(global_motion_func);
glutPassiveMotionFunc(global_passive_motion_func);
glutInitDisplayMode (GLUT_DOUBLE);
glShadeModel(GL_FLAT);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glClearColor(0.1, 1.0, 0.1, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, frame.width, 0, frame.height, -1.0, 1.0);
glViewport(0, 0, frame.width, frame.height);
}
int main (int argc, char **argv)
{
shoebill_config_t config;
pthread_t pid;
bzero(&config, sizeof(shoebill_config_t));
/*
* A variety of hacky things happen in debug mode.
* shoebill_start() will not create a new thread to run
* the CPU loop. We'll create a CPU thread here, bypass
* core_api, and directly manipulate the emulator guts.
*
* This is not a great example of how to write a GUI
* for shoebill...
*/
config.debug_mode = 1;
config.aux_verbose = 0;
config.ram_size = 16 * 1024 * 1024;
config.aux_kernel_path = "/unix";
config.rom_path = "../../../shoebill_priv/macii.rom";
config.scsi_devices[0].path = "../../../shoebill_priv/root3.img";
//config.scsi_devices[1].path = "../priv/marathon.img";
/*dbg_state.ring_len = 256 * 1024 * 1024;
dbg_state.ring = malloc(dbg_state.ring_len);
dbg_state.ring_i = 0;*/
shoebill_validate_or_zap_pram(config.pram, 1);
if (!shoebill_initialize(&config)) {
printf("%s\n", config.error_msg);
return 0;
}
_init_keyboard_map();
shoebill_install_video_card(&config,
9, // slotnum
640, // 1024,
480); // 768,
// uint8_t ethernet_addr[6] = {0x22, 0x33, 0x55, 0x77, 0xbb, 0xdd};
// shoebill_install_ethernet_card(&config, 13, ethernet_addr);
// Start the VIA timer thread
shoebill_start();
// Create a new thread to drive the CPU & debugger UI
pthread_create(&pid, NULL, ui_thread, NULL);
int dummyargc = 1;
glutInit(&dummyargc, argv);
// Create/configure the screen
_init_glut_video();
// Set a GLUT timer to update the screen
glutTimerFunc(15, timer_func, 0);
glutMainLoop();
return 0;
}

View File

@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
870A2F9E192D2A6D00ABBC14 /* shoeScreenWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */; };
87495445189980E200E80F5B /* shoeScreenView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 87495444189980E200E80F5B /* shoeScreenView.xib */; };
8749544718999F5300E80F5B /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8749544618999F5300E80F5B /* OpenGL.framework */; };
874954491899A22D00E80F5B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 874954481899A22D00E80F5B /* QuartzCore.framework */; };
@ -26,12 +27,14 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
870A2F9C192D2A6D00ABBC14 /* shoeScreenWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shoeScreenWindowController.h; sourceTree = "<group>"; };
870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = shoeScreenWindowController.m; sourceTree = "<group>"; };
87495444189980E200E80F5B /* shoeScreenView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = shoeScreenView.xib; sourceTree = "<group>"; };
8749544618999F5300E80F5B /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
874954481899A22D00E80F5B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = /System/Library/Frameworks/QuartzCore.framework; sourceTree = "<absolute>"; };
8781D24918A19C340016F604 /* shoePreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shoePreferencesWindowController.h; sourceTree = "<group>"; };
8781D24A18A19C340016F604 /* shoePreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = shoePreferencesWindowController.m; sourceTree = "<group>"; };
8781D24B18A19C340016F604 /* shoePreferencesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = shoePreferencesWindowController.xib; sourceTree = "<group>"; };
8781D24918A19C340016F604 /* shoePreferencesWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shoePreferencesWindowController.h; path = Shoebill/shoePreferencesWindowController.h; sourceTree = "<group>"; };
8781D24A18A19C340016F604 /* shoePreferencesWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = shoePreferencesWindowController.m; path = Shoebill/shoePreferencesWindowController.m; sourceTree = "<group>"; };
8781D24B18A19C340016F604 /* shoePreferencesWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = shoePreferencesWindowController.xib; path = Shoebill/shoePreferencesWindowController.xib; sourceTree = "<group>"; };
8782FCE2189AFEFB0081E19E /* shoeApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shoeApplication.h; sourceTree = "<group>"; };
8782FCE3189AFEFB0081E19E /* shoeApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = shoeApplication.m; sourceTree = "<group>"; };
87F9772818987700000D589E /* Shoebill.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shoebill.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -70,6 +73,20 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
870A2F9F192D2ADA00ABBC14 /* Screen */ = {
isa = PBXGroup;
children = (
870A2F9C192D2A6D00ABBC14 /* shoeScreenWindowController.h */,
870A2F9D192D2A6D00ABBC14 /* shoeScreenWindowController.m */,
87495444189980E200E80F5B /* shoeScreenView.xib */,
AE75B30718A8210D00E66DB6 /* shoeScreenWindow.h */,
AE75B30818A8210D00E66DB6 /* shoeScreenWindow.m */,
87F9775F18987767000D589E /* shoeScreenView.h */,
87F9776018987767000D589E /* shoeScreenView.m */,
);
name = Screen;
sourceTree = "<group>";
};
87F9771F18987700000D589E = {
isa = PBXGroup;
children = (
@ -117,14 +134,8 @@
8782FCE3189AFEFB0081E19E /* shoeApplication.m */,
87F9773D18987700000D589E /* shoeAppDelegate.h */,
87F9773E18987700000D589E /* shoeAppDelegate.m */,
87495444189980E200E80F5B /* shoeScreenView.xib */,
AE75B30718A8210D00E66DB6 /* shoeScreenWindow.h */,
AE75B30818A8210D00E66DB6 /* shoeScreenWindow.m */,
87F9775F18987767000D589E /* shoeScreenView.h */,
87F9776018987767000D589E /* shoeScreenView.m */,
8781D24B18A19C340016F604 /* shoePreferencesWindowController.xib */,
8781D24918A19C340016F604 /* shoePreferencesWindowController.h */,
8781D24A18A19C340016F604 /* shoePreferencesWindowController.m */,
870A2F9F192D2ADA00ABBC14 /* Screen */,
AE0D1AAB193132E700EBBED0 /* Preferences */,
87F9773218987700000D589E /* Supporting Files */,
);
path = Shoebill;
@ -143,6 +154,17 @@
name = "Supporting Files";
sourceTree = "<group>";
};
AE0D1AAB193132E700EBBED0 /* Preferences */ = {
isa = PBXGroup;
children = (
8781D24B18A19C340016F604 /* shoePreferencesWindowController.xib */,
8781D24918A19C340016F604 /* shoePreferencesWindowController.h */,
8781D24A18A19C340016F604 /* shoePreferencesWindowController.m */,
);
name = Preferences;
path = ..;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -219,6 +241,7 @@
8781D24C18A19C340016F604 /* shoePreferencesWindowController.m in Sources */,
AE75B30918A8210D00E66DB6 /* shoeScreenWindow.m in Sources */,
8782FCE4189AFEFB0081E19E /* shoeApplication.m in Sources */,
870A2F9E192D2A6D00ABBC14 /* shoeScreenWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -260,6 +283,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_LINK_OBJC_RUNTIME = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
@ -299,6 +323,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_LINK_OBJC_RUNTIME = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13C1021" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="shoeApplication">
<connections>
<outlet property="delegate" destination="494" id="495"/>
<outlet property="run_stop_menu_item" destination="82" id="kWm-r3-myc"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
@ -185,4 +186,4 @@
<customObject id="420" customClass="NSFontManager"/>
<customObject id="VMD-IW-vcf" userLabel="Preferences" customClass="shoePreferencesWindowController"/>
</objects>
</document>
</document>

View File

@ -2,12 +2,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2013-2015 Peter Rutenbar</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>org.shoebill.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.2</string>
<string>0.0.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>0.0.2</string>
<string>0.0.5</string>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSMainNibFile</key>

View File

@ -1,13 +1,12 @@
{\rtf1\ansi\ansicpg1252\cocoartf1265
{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf200
{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\vieww9600\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc
\f0\b\fs24 \cf0 Shoebill
\b0 \
A Macintosh II emulator that runs A/UX\
\
{\field{\*\fldinst{HYPERLINK "https://github.com/pruten/shoebill"}}{\fldrslt https://github.com/pruten/shoebill}}\
\
Peter Rutenbar (pruten@gmail.com)}
}

View File

@ -24,7 +24,8 @@
*/
#import "shoeAppDelegate.h"
#import "shoeApplication.h"
#import "shoePreferencesWindowController.h"
@implementation shoeAppDelegate
@ -33,13 +34,14 @@
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
uint32_t i;
[((shoeApplication*)NSApp) zapPram:defaults ptr:nil];
[defaults setObject:@"/unix" forKey:@"rootKernelPath"];
[defaults setObject:@"" forKey:@"romPath"];
[defaults setInteger:NSOnState forKey:@"verboseState"];
[defaults setInteger:16 forKey:@"memorySize"];
[defaults setInteger:640 forKey:@"screenWidth"];
[defaults setInteger:480 forKey:@"screenHeight"];
// [defaults setInteger:640 forKey:@"screenWidth"];
// [defaults setInteger:480 forKey:@"screenHeight"];
for (i=0; i<7; i++)
[defaults setObject:@"" forKey:[NSString stringWithFormat:@"scsiPath%u", i]];
@ -49,6 +51,7 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
uint32_t i;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL isInitialized = [defaults boolForKey:@"defaultsInitialized"];
@ -56,9 +59,33 @@
if (!isInitialized)
[self createFirstTimeUserDefaults];
// Going from 0.0.1 to 0.0.2 leaves rootKernelPath uninitialized
// < 0.0.2 leaves rootKernelPath uninitialized
if ([defaults objectForKey:@"rootKernelPath"] == nil)
[defaults setObject:@"/unix" forKey:@"rootKernelPath"];
// < 0.0.3 leaves pramData uninitialized
if ([defaults objectForKey:@"pramData"] == nil)
[((shoeApplication*)NSApp) zapPram:defaults ptr:nil];
// < 0.0.5 leaves ethernet settings uninitialized
if ([defaults objectForKey:@"tapPathE"] == nil) {
uint8_t mac[6];
generateMACAddr(mac);
[defaults setObject:[NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]
forKey:@"macAddressE"];
[defaults setObject:@"/dev/tap0" forKey:@"tapPathE"];
[defaults setInteger:0 forKey:@"ethernetEnabledE"];
for (i=0; i<4; i++) {
[defaults setInteger:640 forKey:[NSString stringWithFormat:@"screenWidth%u", i]];
[defaults setInteger:480 forKey:[NSString stringWithFormat:@"screenHeight%u", i]];
[defaults setInteger:0 forKey:[NSString stringWithFormat:@"screenEnabled%u", i]];
}
[defaults setInteger:1 forKey:@"screenEnabled0"];
}
[defaults synchronize];
}

View File

@ -24,19 +24,39 @@
*/
#import <Cocoa/Cocoa.h>
#include "../../core/core_api.h"
#include "../../core/redblack.h"
#import "shoeScreenWindowController.h"
#include "../../core/shoebill.h"
struct shoe_app_pram_data_t
{
uint8_t pram[256];
volatile _Bool updated;
};
@interface shoeApplication : NSApplication {
rb_tree *keymap;
NSWindowController *windowController[16];
shoeScreenWindowController *windowController[16];
IBOutlet __weak NSMenuItem *run_stop_menu_item;
NSTimer *pram_flush_timer;
struct shoe_app_pram_data_t *pram;
@public
BOOL doCaptureMouse, doCaptureKeys;
BOOL isRunning;
shoebill_control_t control;
shoebill_config_t config;
char *tapPath;
uint8_t mac[6];
int tap_fd;
BOOL ethEnabled, tap_fd_valid;
struct {
uint16_t height, width, enabled;
} screens[4];
}
- (void) startEmulator;
- (void) startEmulator;
- (void) zapPram:(NSUserDefaults*)defaults ptr:(uint8_t*)ptr;
@end

View File

@ -25,20 +25,23 @@
#import "shoeApplication.h"
#import "shoeScreenWindow.h"
#import "shoeScreenWindowController.h"
#import "shoePreferencesWindowController.h"
#include <ctype.h>
@implementation shoeApplication
#define mapkeymod(u, a, m) do { \
assert((a >> 7) == 0); \
void *value = (void*)(((m) << 8)| (a)); \
rb_insert(keymap, u, value, NULL); \
uint16_t value = ((m) << 8)| (a); \
rb_insert(keymap, u, &value, NULL); \
} while (0) \
#define mapkey(_u, a) mapkeymod(_u, a, 0)
- (void)initKeyboardMap
{
keymap = rb_new();
keymap = rb_new(p_new_pool(NULL), sizeof(uint16_t));
// Letters
mapkey('a', 0x00);
@ -176,13 +179,12 @@
NSString *chars = [[event charactersIgnoringModifiers] lowercaseString];
NSUInteger modifierFlags = [event modifierFlags];
unichar c = [chars characterAtIndex:0];
void *_value;
uint16_t value;
if (keymap == NULL)
[self initKeyboardMap];
if (rb_find(keymap, c, &_value)) {
uint16_t value = (uint16_t)_value;
if (rb_find(keymap, c, &value)) {
shoebill_key_modifier((value >> 8) | (modifierFlags >> 16));
shoebill_key((type == NSKeyDown), value & 0xff);
@ -214,7 +216,7 @@
}
- (BOOL) fetchUserDefaults:(uint16_t*)height width:(uint16_t*)width
- (BOOL) fetchUserDefaults
{
uint32_t i;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
@ -223,6 +225,7 @@
NSString *romPathStr = [defaults objectForKey:@"romPath"];
NSInteger verboseState = [defaults integerForKey:@"verboseState"];
NSInteger memsize = [defaults integerForKey:@"memorySize"];
NSData *pramData = [defaults objectForKey:@"pramData"];
if (rootKernelPathStr == Nil || [rootKernelPathStr length]==0) {
[self complain:@"Kernel path invalid!"];
@ -237,37 +240,69 @@
if ((memsize < 1) || (memsize > 1024))
memsize = 8;
NSInteger screenHeightValue = [defaults integerForKey:@"screenHeight"];
NSInteger screenWidthValue = [defaults integerForKey:@"screenWidth"];
for (i=0; i<4; i++) {
NSInteger height = [defaults integerForKey:[NSString stringWithFormat:@"screenHeight%u", i]];
NSInteger width = [defaults integerForKey:[NSString stringWithFormat:@"screenWidth%u", i]];
NSInteger enabled = [defaults integerForKey:[NSString stringWithFormat:@"screenEnabled%u", i]];
if ((screenHeightValue < 342) || (screenHeightValue > 0xffff))
screenHeightValue = 480;
if ((screenWidthValue < 512) || (screenWidthValue > 0xffff))
screenWidthValue = 640;
if ((height < 342) || (height > 0xffff))
height = 480;
if ((width < 512) || (width > 0xffff))
width = 640;
screens[i].width = (uint16_t)width;
screens[i].height = (uint16_t)height;
screens[i].enabled = (uint16_t)enabled;
}
for (i=0; i<7; i++) {
NSString *str = [defaults objectForKey:[NSString stringWithFormat:@"scsiPath%u", i]];
if (str == nil || [str length] == 0)
control.scsi_devices[i].path = NULL;
config.scsi_devices[i].path = NULL;
else
control.scsi_devices[i].path = strdup([str UTF8String]);
config.scsi_devices[i].path = strdup([str UTF8String]);
}
char *rootKernelPathCString = strdup([rootKernelPathStr UTF8String]);
char *romPathCString = strdup([romPathStr UTF8String]);
// FIXME: I'm leaking these strings. Stop leaking stuff when the UI is more finalized
config.aux_verbose = (verboseState == NSOnState);
config.ram_size = (uint32_t)memsize * 1024 * 1024;
config.aux_kernel_path = rootKernelPathCString;
config.rom_path = romPathCString;
config.debug_mode = 0;
control.aux_verbose = (verboseState == NSOnState);
control.ram_size = (uint32_t)memsize * 1024 * 1024;
control.aux_kernel_path = rootKernelPathCString;
control.rom_path = romPathCString;
[pramData getBytes:config.pram length:256];
*width = screenWidthValue;
*height = screenHeightValue;
/*
* If the pram is corrupt, zap it.
* A/UX will apparently never zap corrupted pram,
* probably because it expects the bootloader/MacOS to do it.
*/
if (memcmp(config.pram+0xc, "NuMc", 4) != 0)
[self zapPram:defaults ptr:config.pram];
NSString *defaultTapPath = [defaults objectForKey:@"tapPathE"];
NSString *defaultMacAddr = [defaults objectForKey:@"macAddressE"];
ethEnabled = [defaults integerForKey:@"ethernetEnabledE"];
if (ethEnabled) {
if (tap_fd_valid && strcmp(tapPath, [defaultTapPath UTF8String]) != 0) {
close(tap_fd);
tap_fd_valid = 0;
}
if (tapPath)
free(tapPath);
tapPath = strdup([defaultTapPath UTF8String]);
if (!(parseMACAddr([defaultMacAddr UTF8String], mac))) {
[self complain:@"Bad MAC addr"];
ethEnabled = 0;
return NO;
}
}
return YES;
}
@ -275,17 +310,59 @@
- (void) createScreenWindow:(uint8_t)slotnum
height:(uint16_t)height
width:(uint16_t)width
refresh_freq:(double)refresh_freq
{
shoebill_install_video_card(&control,
shoebill_install_video_card(&config,
slotnum,
width,
height,
refresh_freq);
height);
windowController[slotnum] = [[NSWindowController alloc] initWithWindowNibName:@"shoeScreenView"];
shoeScreenWindow *win = (shoeScreenWindow*)windowController[slotnum].window;
[win configure:slotnum];
windowController[slotnum] = [[shoeScreenWindowController alloc]
initWithWindowNibName:@"shoeScreenView"
slotnum:slotnum];
}
- (void) zapPram:(NSUserDefaults*)defaults ptr:(uint8_t*)ptr
{
uint8_t init[256];
shoebill_validate_or_zap_pram(init, 1);
if (ptr)
memcpy(ptr, init, 256);
NSData *data = [NSData dataWithBytes:init length:256];
[defaults setObject:data forKey:@"pramData"];
[defaults synchronize];
assert("zapPram" && (memcmp(init+0xc, "NuMc", 4) == 0));
}
void pram_callback (void *param, const uint8_t addr, const uint8_t byte)
{
struct shoe_app_pram_data_t *pram = (struct shoe_app_pram_data_t*)param;
pram->pram[addr] = byte;
pram->updated = 1;
//printf("pram_callback: set pram[0x%x] = 0x%02x (%c)\n", addr, byte, isprint(byte)?byte:'.');
}
- (void) flushPram
{
uint8_t copy[256];
if (pram->updated) {
pram->updated = 0;
memcpy(copy, pram->pram, 256);
NSData* data = [NSData dataWithBytes:copy length:256];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:data forKey:@"pramData"];
[defaults synchronize];
}
}
- (void) pramFlushTimerFired:(NSTimer *)timer
{
[self flushPram];
}
- (void) startEmulator
@ -293,41 +370,110 @@
if (isRunning)
return;
uint16_t width, height;
uint32_t i;
bzero(&control, sizeof(shoebill_control_t));
bzero(&config, sizeof(shoebill_config_t));
[self fetchUserDefaults:&height width:&width];
if (![self fetchUserDefaults])
return ;
uint32_t result = shoebill_initialize(&control);
self->pram = calloc(1, sizeof(struct shoe_app_pram_data_t));
memcpy(self->pram, config.pram, 256);
pram_flush_timer = [NSTimer
scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(pramFlushTimerFired:)
userInfo:nil
repeats:YES];
config.pram_callback = pram_callback;
config.pram_callback_param = (void*)self->pram;
uint32_t result = shoebill_initialize(&config);
if (!result) {
[self complain:[NSString stringWithFormat:@"%s", control.error_msg]];
[self complain:[NSString stringWithFormat:@"%s", config.error_msg]];
return ;
}
[self createScreenWindow:9 height:height width:width refresh_freq:200.0/3.0];
/*[self createScreenWindow:10 height:height width:width refresh_freq:200.0/3.0];
[self createScreenWindow:11 height:height width:width refresh_freq:200.0/3.0];
[self createScreenWindow:12 height:height width:width refresh_freq:200.0/3.0];
[self createScreenWindow:13 height:height width:width refresh_freq:200.0/3.0];
[self createScreenWindow:14 height:height width:width refresh_freq:200.0/3.0];*/
for (i=0; i<4; i++) {
if (screens[i].enabled)
[self createScreenWindow:(9+i) height:screens[i].height width:screens[i].width];
}
if (ethEnabled) {
if (!tap_fd_valid) {
tap_fd = open(tapPath, O_RDWR | O_NOFOLLOW);
if (tap_fd == -1) {
NSAlert *theAlert = [NSAlert
alertWithMessageText:nil
defaultButton:nil
alternateButton:nil
otherButton:nil
informativeTextWithFormat:@"Couldn't open tap device (errno = %d)", errno
];
[theAlert runModal];
return ;
}
tap_fd_valid = 1;
}
if (!shoebill_install_ethernet_card(&config, 13, mac, tap_fd)) {
[self complain:[NSString stringWithFormat:@"%s", config.error_msg]];
return ;
}
}
shoebill_start();
isRunning = true;
for (i=0; i<16; i++)
for (i=0; i<16; i++) {
if (windowController[i]) {
shoeScreenWindow *win = (shoeScreenWindow*)[windowController[i] window];
[win reevaluateKeyWindowness];
}
}
[run_stop_menu_item setTitle: @"Stop"];
[run_stop_menu_item setKeyEquivalent:@""];
}
- (void) stopEmulator
{
uint32_t i;
for (i=0; i<16; i++) {
if (windowController[i]) {
[windowController[i] close];
windowController[i] = NULL;
}
}
doCaptureKeys = false;
doCaptureMouse = false;
isRunning = false;
shoebill_stop();
[pram_flush_timer invalidate];
pram_flush_timer = nil;
[self flushPram];
free(self->pram);
if (config.aux_kernel_path)
free((void*)config.aux_kernel_path);
if (config.rom_path)
free((void*)config.rom_path);
[run_stop_menu_item setTitle: @"Run"];
[run_stop_menu_item setKeyEquivalent:@"r"];
}
- (IBAction)runMenuItem:(id)sender
{
[self startEmulator];
if (isRunning)
[self stopEmulator];
else
[self startEmulator];
}

View File

@ -27,10 +27,17 @@
@interface shoePreferencesWindowController : NSWindowController {
IBOutlet __weak NSButton *apply, *cancel, *applyAndRun, *verbose;
IBOutlet __weak NSButton *apply, *cancel, *applyAndRun, *verbose, *ethernetEnabled;
IBOutlet __weak NSTextField *kernelPath, *romPath, *memorySize;
IBOutlet __weak NSTextField *scsiPath0, *scsiPath1, *scsiPath2, *scsiPath3, *scsiPath4, *scsiPath5, *scsiPath6;
IBOutlet __weak NSTextField *screenHeight, *screenWidth;
IBOutlet __weak NSTextField *macAddress, *tapPath;
IBOutlet __weak NSTextField *screenHeight1, *screenWidth1;
IBOutlet __weak NSTextField *screenHeight2, *screenWidth2;
IBOutlet __weak NSTextField *screenHeight3, *screenWidth3;
IBOutlet __weak NSTextField *screenHeight4, *screenWidth4;
IBOutlet __weak NSButton *enableScreen1, *enableScreen2, *enableScreen3, *enableScreen4;
}
@ -40,3 +47,6 @@
- (IBAction)browsePressed:(id)sender;
@end
void generateMACAddr (uint8_t *mac);
_Bool parseMACAddr (const char *str, uint8_t *mac);

View File

@ -25,6 +25,7 @@
#import "shoePreferencesWindowController.h"
#import "shoeApplication.h"
#include <ctype.h>
@implementation shoePreferencesWindowController
@ -35,6 +36,14 @@
- (void)windowDidLoad
{
uint32_t i;
NSTextField *screenWidths[4] = {
screenWidth1, screenWidth2, screenWidth3, screenWidth4};
NSTextField *screenHeights[4] = {
screenHeight1, screenHeight2, screenHeight3, screenHeight4};
NSButton *screenEnableds[4] = {
enableScreen1, enableScreen2, enableScreen3, enableScreen4};
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *rootKernelPathStr = [defaults objectForKey:@"rootKernelPath"];
@ -71,21 +80,28 @@
if (scsiPath5Str) [scsiPath5 setStringValue:scsiPath5Str];
if (scsiPath6Str) [scsiPath6 setStringValue:scsiPath6Str];
NSInteger screenHeightValue = [defaults integerForKey:@"screenHeight"];
NSInteger screenWidthValue = [defaults integerForKey:@"screenWidth"];
if ((screenHeightValue < 342) || (screenHeightValue > 0xffff)) {
screenHeightValue = 480;
[defaults setInteger:screenHeightValue forKey:@"screenHeight"];
for (i=0; i<4; i++) {
NSInteger height = [defaults integerForKey:[NSString stringWithFormat:@"screenHeight%u", i]];
NSInteger width = [defaults integerForKey:[NSString stringWithFormat:@"screenWidth%u", i]];
NSInteger enabled = [defaults integerForKey:[NSString stringWithFormat:@"screenEnabled%u", i]];
if ((height < 342) || (height > 0xffff))
height = 480;
if ((width < 342) || (width > 0xffff))
width = 640;
[screenHeights[i] setStringValue:[NSString stringWithFormat:@"%u", (uint32_t)height]];
[screenWidths[i] setStringValue:[NSString stringWithFormat:@"%u", (uint32_t)width]];
[screenEnableds[i] setState:enabled];
}
NSString *tapPathStr = [defaults objectForKey:@"tapPathE"];
NSString *macAddressStr = [defaults objectForKey:@"macAddressE"];
NSInteger ethernetEnabledState = [defaults integerForKey:@"ethernetEnabledE"];
if ((screenWidthValue < 512) || (screenWidthValue > 0xffff)) {
screenWidthValue = 640;
[defaults setInteger:screenWidthValue forKey:@"screenWidth"];
}
[screenWidth setStringValue:[NSString stringWithFormat:@"%u", (uint32_t)screenWidthValue]];
[screenHeight setStringValue:[NSString stringWithFormat:@"%u", (uint32_t)screenHeightValue]];
[tapPath setStringValue:tapPathStr];
[macAddress setStringValue:macAddressStr];
[ethernetEnabled setIntegerValue:ethernetEnabledState];
[defaults synchronize];
}
@ -134,8 +150,27 @@
[field setStringValue: [url path]];
}
- (void) complain:(NSString*)str
{
NSAlert *theAlert = [NSAlert
alertWithMessageText:nil
defaultButton:nil
alternateButton:nil
otherButton:nil
informativeTextWithFormat:@"%@", str
];
[theAlert runModal];
}
- (IBAction)applyPressed:(id)sender
{
uint32_t i;
NSTextField *screenWidths[4] = {
screenWidth1, screenWidth2, screenWidth3, screenWidth4};
NSTextField *screenHeights[4] = {
screenHeight1, screenHeight2, screenHeight3, screenHeight4};
NSButton *screenEnableds[4] = {
enableScreen1, enableScreen2, enableScreen3, enableScreen4};
NSString *rootKernelPathStr = [kernelPath stringValue];
NSString *romPathStr = [romPath stringValue];
@ -150,11 +185,21 @@
NSString *scsiPath5Str = [scsiPath5 stringValue];
NSString *scsiPath6Str = [scsiPath6 stringValue];
NSInteger screenHeightValue = [screenHeight integerValue];
NSInteger screenWidthValue = [screenWidth integerValue];
NSString *macAddressStr = [macAddress stringValue];
NSString *tapPathStr = [tapPath stringValue];
NSInteger ethernetEnabledState = [ethernetEnabled state];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
uint8_t mac[6];
if (!parseMACAddr ([macAddressStr UTF8String], mac)) {
[self complain:@"Bad MAC address"];
}
else {
[macAddress setStringValue:[NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]];
}
[defaults setObject:rootKernelPathStr forKey:@"rootKernelPath"];
[defaults setObject:romPathStr forKey:@"romPath"];
[defaults setInteger:verboseState forKey:@"verboseState"];
@ -168,8 +213,24 @@
[defaults setObject:scsiPath5Str forKey:@"scsiPath5"];
[defaults setObject:scsiPath6Str forKey:@"scsiPath6"];
[defaults setInteger:screenHeightValue forKey:@"screenHeight"];
[defaults setInteger:screenWidthValue forKey:@"screenWidth"];
for (i=0; i<4; i++) {
NSInteger height = [screenHeights[i] integerValue];
NSInteger width = [screenWidths[i] integerValue];
NSInteger enabled = [screenEnableds[i] state];
if ((height < 342) || (height > 0xffff))
height = 480;
if ((width < 342) || (width > 0xffff))
width = 640;
[defaults setInteger:height forKey:[NSString stringWithFormat:@"screenHeight%u", i]];
[defaults setInteger:width forKey:[NSString stringWithFormat:@"screenWidth%u", i]];
[defaults setInteger:enabled forKey:[NSString stringWithFormat:@"screenEnabled%u", i]];
}
[defaults setObject:macAddressStr forKey:@"macAddressE"];
[defaults setObject:tapPathStr forKey:@"tapPathE"];
[defaults setInteger:ethernetEnabledState forKey:@"ethernetEnabledE"];
[defaults synchronize];
}
@ -186,4 +247,69 @@
[[self window] close];
}
-(IBAction)zapPramPressed:(id)sender
{
shoeApplication *shoeApp = (shoeApplication*) NSApp;
[shoeApp zapPram:[NSUserDefaults standardUserDefaults] ptr:nil];
}
void generateMACAddr (uint8_t *mac)
{
srandom((unsigned)(random() ^ time(NULL)));
/* Generate a MAC address in the range of the original EtherTalk card */
mac[0] = 0x02;
mac[1] = 0x60;
mac[2] = 0x8c;
mac[3] = random() & 0x07;
mac[4] = random() & 0xff;
mac[5] = random() & 0xff;
}
_Bool parseMACAddr (const char *str, uint8_t *mac)
{
uint32_t i, nibbles = 0;
uint8_t allowed[256];
memset(allowed, 30, 256);
for (i=0; i<256; i++)
if (isspace(i))
allowed[i] = 20;
allowed[':'] = 20;
allowed['-'] = 20;
for (i=0; i<10; i++)
allowed['0' + i] = i;
for (i=0; i<6; i++) {
allowed['a' + i] = 10 + i;
allowed['A' + i] = 10 + i;
}
for (i=0; str[i]; i++) {
const uint8_t v = allowed[str[i]];
if (v == 30)
return 0;
else if (v == 20)
continue;
if (nibbles >= 12)
return 0;
mac[nibbles/2] <<= 4;
mac[nibbles/2] |= v;
nibbles++;
}
return (nibbles == 12);
}
-(IBAction)newMacAddrPressed:(id)sender
{
uint8_t mac[6];
generateMACAddr(mac);
[macAddress setStringValue:[NSString stringWithFormat:@"%02X:%02X:%02X:%02X:%02X:%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]]];
}
@end

View File

@ -1,17 +1,29 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14F27" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5053"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="shoePreferencesWindowController">
<connections>
<outlet property="enableScreen1" destination="JSa-Bw-xDM" id="3Vr-kl-5To"/>
<outlet property="enableScreen2" destination="QR4-Cc-AkE" id="An0-V4-7Gg"/>
<outlet property="enableScreen3" destination="C6X-Zw-psD" id="Ipx-9O-5jD"/>
<outlet property="enableScreen4" destination="isQ-0Q-gQ4" id="4rO-YL-FaT"/>
<outlet property="ethernetEnabled" destination="GCJ-G0-drE" id="urw-x8-B7H"/>
<outlet property="kernelPath" destination="zU0-5O-afD" id="Q3V-Ly-Y6B"/>
<outlet property="macAddress" destination="UAs-x7-p28" id="PTo-JO-bHU"/>
<outlet property="memorySize" destination="uvm-gd-pCd" id="zzI-jI-ZUW"/>
<outlet property="romPath" destination="LoN-Nd-9cy" id="R3k-vY-TPo"/>
<outlet property="screenHeight" destination="Wyt-jg-xmk" id="Sq6-IJ-xbQ"/>
<outlet property="screenWidth" destination="EMf-gC-m9T" id="xxW-Ji-t7R"/>
<outlet property="screenHeight1" destination="Wyt-jg-xmk" id="dCS-jW-MMh"/>
<outlet property="screenHeight2" destination="IPj-pY-K2b" id="cnW-fh-y05"/>
<outlet property="screenHeight3" destination="tqh-iM-5GM" id="sF2-HA-acz"/>
<outlet property="screenHeight4" destination="3aP-6m-As4" id="fsH-cL-DLg"/>
<outlet property="screenWidth1" destination="EMf-gC-m9T" id="GSA-cX-c70"/>
<outlet property="screenWidth2" destination="B2c-pG-rIH" id="1BQ-0g-PMw"/>
<outlet property="screenWidth3" destination="cQO-ri-LxM" id="fBQ-VU-X4L"/>
<outlet property="screenWidth4" destination="VvE-c8-mBx" id="acc-3d-WMa"/>
<outlet property="scsiPath0" destination="nhQ-gw-2di" id="Ayi-Wk-Nhf"/>
<outlet property="scsiPath1" destination="8th-va-hXP" id="xTk-Kd-9v2"/>
<outlet property="scsiPath2" destination="RTT-NZ-Tte" id="w7D-05-cvN"/>
@ -19,24 +31,24 @@
<outlet property="scsiPath4" destination="Biz-iI-IiP" id="gbh-Jl-dxV"/>
<outlet property="scsiPath5" destination="GE2-3P-G1I" id="ohD-Fj-EX9"/>
<outlet property="scsiPath6" destination="cy8-jg-woV" id="QoH-Z0-e8d"/>
<outlet property="tapPath" destination="Wvc-cC-QWO" id="kuK-DA-ui3"/>
<outlet property="verbose" destination="fkL-RE-iRz" id="uba-U2-Zkh"/>
<outlet property="window" destination="rKy-wc-8AE" id="sYz-fH-ohd"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="rKy-wc-8AE">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="498" height="433"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="4Fm-p8-6h9">
<rect key="frame" x="0.0" y="0.0" width="498" height="433"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tabView fixedFrame="YES" drawsBackground="NO" initialItem="Ffn-1Z-2rp" translatesAutoresizingMaskIntoConstraints="NO" id="i29-bb-k7Y">
<rect key="frame" x="13" y="33" width="472" height="394"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<font key="font" metaFont="system"/>
<tabViewItems>
<tabViewItem label="General" identifier="1" id="Ffn-1Z-2rp">
@ -46,7 +58,6 @@
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oVj-nA-5GP">
<rect key="frame" x="11" y="314" width="81" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="Rom Path:" usesSingleLineMode="YES" id="7qD-i1-FXI">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -55,7 +66,6 @@
</textField>
<button identifier="romPathBrowse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="fjl-Jg-nIF">
<rect key="frame" x="358" y="303" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ITb-nX-hmP">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -66,7 +76,6 @@
</button>
<textField identifier="romPath" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LoN-Nd-9cy">
<rect key="frame" x="98" y="292" width="258" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="macii.rom" drawsBackground="YES" id="IvC-yQ-qdn">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -75,7 +84,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RDI-wU-bdg">
<rect key="frame" x="2" y="267" width="90" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="Memory (MB):" usesSingleLineMode="YES" id="8mu-le-9YC">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -84,25 +92,73 @@
</textField>
<textField identifier="memorySize" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uvm-gd-pCd">
<rect key="frame" x="98" y="262" width="42" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="8" placeholderString="8" drawsBackground="YES" id="I5v-kb-eCo">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fNZ-zo-4uj">
<rect key="frame" x="7" y="10" width="103" height="32"/>
<buttonCell key="cell" type="push" title="Zap PRAM" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="EGV-fk-y4d">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="zapPramPressed:" target="-2" id="9N7-dY-Fl6"/>
</connections>
</button>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0nJ-ho-P1P">
<rect key="frame" x="11" y="235" width="81" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="Kernel Path:" usesSingleLineMode="YES" id="Wdx-2C-yGA">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="kernelPath" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zU0-5O-afD">
<rect key="frame" x="98" y="232" width="325" height="22"/>
<string key="toolTip">The path to the kernel on the root image. If you don't know what this is, just use "/unix" (Note: the root image needs to be at SCSI ID 0)</string>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="/unix" drawsBackground="YES" id="0MQ-1P-9gJ">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button identifier="verbose" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fkL-RE-iRz">
<rect key="frame" x="283" y="18" width="142" height="18"/>
<string key="toolTip">Verbose mode causes A/UX to print diagnostic info during boot, and it will also run fsck to repair the root filesystem if necessary.</string>
<buttonCell key="cell" type="check" title="A/UX Verbose Boot" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="htQ-zH-VRv">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Screens" identifier="" id="3o8-Ma-LAk">
<view key="view" id="Zdy-ik-svz">
<rect key="frame" x="10" y="33" width="452" height="348"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tgt-1x-QT0">
<rect key="frame" x="2" y="210" width="90" height="42"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="186" y="312" width="125" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="right" title="Screen Resolution:" id="9bl-WA-bhb">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ld1-xq-pdG">
<rect key="frame" x="186" y="287" width="47" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="right" title="Width" id="QBW-rk-c7E">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JNe-0k-g4e">
<rect key="frame" x="146" y="224" width="14" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="241" y="259" width="14" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="center" title="x" id="9cf-Xq-tuu">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -110,32 +166,165 @@
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="aSg-xG-o1t">
<rect key="frame" x="223" y="223" width="59" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="317" y="259" width="59" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="left" title="pixels" id="D5V-Zm-Grd">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="memorySize" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EMf-gC-m9T">
<rect key="frame" x="98" y="220" width="43" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textField identifier="width1" toolTip="width" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="EMf-gC-m9T">
<rect key="frame" x="192" y="256" width="43" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="width" placeholderString="width" drawsBackground="YES" id="sbq-WW-C05">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="memorySize" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wyt-jg-xmk">
<rect key="frame" x="168" y="220" width="49" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textField identifier="height1" toolTip="height" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wyt-jg-xmk">
<rect key="frame" x="262" y="256" width="49" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="height" placeholderString="height" drawsBackground="YES" id="qvE-Bk-NDl">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button identifier="verbose" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="JSa-Bw-xDM">
<rect key="frame" x="57" y="258" width="129" height="18"/>
<string key="toolTip">Verbose mode causes A/UX to print diagnostic info during boot, and it will also run fsck to repair the root filesystem if necessary.</string>
<buttonCell key="cell" type="check" title="Enable Screen 1" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="azN-j2-txA">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button identifier="verbose" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QR4-Cc-AkE">
<rect key="frame" x="57" y="228" width="129" height="18"/>
<string key="toolTip">Verbose mode causes A/UX to print diagnostic info during boot, and it will also run fsck to repair the root filesystem if necessary.</string>
<buttonCell key="cell" type="check" title="Enable Screen 2" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Zqf-mb-br4">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button identifier="verbose" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C6X-Zw-psD">
<rect key="frame" x="57" y="198" width="129" height="18"/>
<string key="toolTip">Verbose mode causes A/UX to print diagnostic info during boot, and it will also run fsck to repair the root filesystem if necessary.</string>
<buttonCell key="cell" type="check" title="Enable Screen 3" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Cfv-ZG-aBO">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="G8E-Jw-aPd">
<rect key="frame" x="241" y="229" width="14" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="center" title="x" id="1RP-Cp-YFY">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8hd-at-UXU">
<rect key="frame" x="317" y="229" width="59" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="left" title="pixels" id="2DF-kS-r5B">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="width2" toolTip="width" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="B2c-pG-rIH">
<rect key="frame" x="192" y="226" width="43" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="width" placeholderString="width" drawsBackground="YES" id="DMe-fk-aJx">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="height2" toolTip="height" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IPj-pY-K2b">
<rect key="frame" x="262" y="226" width="49" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="height" placeholderString="height" drawsBackground="YES" id="3F9-jB-hfl">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uAk-QS-Kpu">
<rect key="frame" x="241" y="199" width="14" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="center" title="x" id="XrI-eJ-Xmq">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="a0D-DN-Abg">
<rect key="frame" x="317" y="199" width="59" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="left" title="pixels" id="V3B-61-meh">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="width3" toolTip="width" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cQO-ri-LxM">
<rect key="frame" x="192" y="196" width="43" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="width" placeholderString="width" drawsBackground="YES" id="5Rx-30-P1a">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="height3" toolTip="height" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tqh-iM-5GM">
<rect key="frame" x="262" y="196" width="49" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="height" placeholderString="height" drawsBackground="YES" id="PdC-tw-Mou">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WMg-rd-urO">
<rect key="frame" x="241" y="169" width="14" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="center" title="x" id="r21-SX-0ht">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NDL-uY-GIk">
<rect key="frame" x="317" y="169" width="59" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="left" title="pixels" id="gKj-k6-3XK">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="width4" toolTip="width" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VvE-c8-mBx">
<rect key="frame" x="192" y="166" width="43" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="width" placeholderString="width" drawsBackground="YES" id="rfI-7T-Vc2">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="height4" toolTip="height" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3aP-6m-As4">
<rect key="frame" x="262" y="166" width="49" height="22"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="height" placeholderString="height" drawsBackground="YES" id="56I-cY-cml">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bgh-Ds-kMn">
<rect key="frame" x="260" y="287" width="47" height="17"/>
<textFieldCell key="cell" allowsUndo="NO" alignment="right" title="Height" id="Ato-zm-vpN">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<button identifier="verbose" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="isQ-0Q-gQ4">
<rect key="frame" x="57" y="168" width="129" height="18"/>
<string key="toolTip">Verbose mode causes A/UX to print diagnostic info during boot, and it will also run fsck to repair the root filesystem if necessary.</string>
<buttonCell key="cell" type="check" title="Enable Screen 4" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="dNj-R0-mIT">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</subviews>
</view>
</tabViewItem>
@ -146,7 +335,6 @@
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dA0-AE-SsA">
<rect key="frame" x="1" y="321" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 0" usesSingleLineMode="YES" id="YYd-8o-kqe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -155,7 +343,6 @@
</textField>
<button identifier="scsiPath0Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="BUV-3F-sRD">
<rect key="frame" x="353" y="310" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Ekq-OL-WH9">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -166,7 +353,6 @@
</button>
<textField identifier="scsiPath0" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nhQ-gw-2di">
<rect key="frame" x="79" y="299" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="4NZ-tu-sNu">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -175,7 +361,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Gjz-Nn-CkC">
<rect key="frame" x="1" y="274" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 1" usesSingleLineMode="YES" id="oPB-iw-Fuy">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -184,7 +369,6 @@
</textField>
<button identifier="scsiPath1Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="0OE-Ny-sRU">
<rect key="frame" x="353" y="263" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="iRT-nY-bQx">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -195,7 +379,6 @@
</button>
<textField identifier="scsiPath1" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8th-va-hXP">
<rect key="frame" x="79" y="252" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="wM4-Hu-6dD">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -204,7 +387,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="17i-H8-woP">
<rect key="frame" x="1" y="227" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 2" usesSingleLineMode="YES" id="rWu-qs-tIH">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -213,7 +395,6 @@
</textField>
<button identifier="scsiPath2Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="OS6-vm-18P">
<rect key="frame" x="353" y="216" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ojq-kT-7Hz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -224,7 +405,6 @@
</button>
<textField identifier="scsiPath2" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="RTT-NZ-Tte">
<rect key="frame" x="79" y="205" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="bx9-L2-52p">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -233,7 +413,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zeV-Os-yjZ">
<rect key="frame" x="1" y="180" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 3" usesSingleLineMode="YES" id="4Ir-Qy-6TG">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -242,7 +421,6 @@
</textField>
<button identifier="scsiPath3Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="hyG-x9-NBA">
<rect key="frame" x="353" y="169" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Toc-PB-8FO">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -253,7 +431,6 @@
</button>
<textField identifier="scsiPath3" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pKG-3S-zID">
<rect key="frame" x="79" y="158" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="Ksz-dn-t4g">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -262,7 +439,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Rpv-lh-1BC">
<rect key="frame" x="1" y="133" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 4" usesSingleLineMode="YES" id="RSB-J8-WHv">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -271,7 +447,6 @@
</textField>
<button identifier="scsiPath4Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="FUw-LT-FRg">
<rect key="frame" x="353" y="122" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="CDZ-hc-2Kz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -282,7 +457,6 @@
</button>
<textField identifier="scsiPath4" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Biz-iI-IiP">
<rect key="frame" x="79" y="111" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="j2E-vU-3LZ">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -291,7 +465,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eGO-wX-v7I">
<rect key="frame" x="1" y="86" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 5" usesSingleLineMode="YES" id="lVw-XZ-DkQ">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -300,7 +473,6 @@
</textField>
<button identifier="scsiPath5Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="Ur1-2a-9nV">
<rect key="frame" x="353" y="75" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="QW3-hO-eDz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -311,7 +483,6 @@
</button>
<textField identifier="scsiPath5" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GE2-3P-G1I">
<rect key="frame" x="79" y="64" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="BrK-XM-gcx">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -320,7 +491,6 @@
</textField>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="c4H-Kt-ANK">
<rect key="frame" x="1" y="39" width="72" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="SCSI ID 6" usesSingleLineMode="YES" id="NS8-2m-U2B">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -329,7 +499,6 @@
</textField>
<button identifier="scsiPath6Browse" verticalHuggingPriority="750" fixedFrame="YES" tag="9" translatesAutoresizingMaskIntoConstraints="NO" id="Twn-Yd-cyf">
<rect key="frame" x="353" y="28" width="97" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Browse..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="JNl-qq-v8p">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -340,7 +509,6 @@
</button>
<textField identifier="scsiPath6" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cy8-jg-woV">
<rect key="frame" x="79" y="17" width="272" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" truncatesLastVisibleLine="YES" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="None" drawsBackground="YES" id="gZa-0y-cOt">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@ -350,35 +518,56 @@
</subviews>
</view>
</tabViewItem>
<tabViewItem label="Booting" identifier="" id="3o8-Ma-LAk">
<view key="view" ambiguous="YES" id="Zdy-ik-svz">
<tabViewItem label="Ethernet" identifier="" id="npc-ti-4lo">
<view key="view" id="2Hm-kn-wCU">
<rect key="frame" x="10" y="33" width="452" height="348"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0nJ-ho-P1P">
<rect key="frame" x="6" y="315" width="81" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="Kernel Path:" usesSingleLineMode="YES" id="Wdx-2C-yGA">
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oLm-yd-Ca2">
<rect key="frame" x="15" y="272" width="93" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="MAC Address:" usesSingleLineMode="YES" id="vve-T4-Rce">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="kernelPath" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zU0-5O-afD">
<rect key="frame" x="93" y="310" width="342" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<string key="toolTip">The path to the kernel on the root image. If you don't know what this is, just use "/unix" (Note: the root image needs to be at SCSI ID 0)</string>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="/unix" drawsBackground="YES" id="0MQ-1P-9gJ">
<textField identifier="macAddress" toolTip="The MAC address of the emulated ethernet card" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UAs-x7-p28">
<rect key="frame" x="114" y="269" width="160" height="22"/>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="AA:AA:AA:AA:AA:AA" placeholderString="01:02:03:04:05:06" drawsBackground="YES" id="2is-7Y-0d2">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button identifier="verbose" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fkL-RE-iRz">
<rect key="frame" x="6" y="271" width="142" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<string key="toolTip">Verbose mode causes A/UX to print diagnostic info during boot, and it will also run fsck to repair the root filesystem if necessary.</string>
<buttonCell key="cell" type="check" title="A/UX Verbose Boot" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="htQ-zH-VRv">
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vp5-0F-Hl5">
<rect key="frame" x="30" y="302" width="78" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" alignment="right" title="TAP Device:" usesSingleLineMode="YES" id="QWi-Fs-xtO">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</textFieldCell>
</textField>
<textField identifier="tapPath" toolTip="The path to the TAP device (/dev/tap0 should be fine)" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wvc-cC-QWO">
<rect key="frame" x="114" y="299" width="313" height="22"/>
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" title="/dev/tap0" placeholderString="/dev/tap0" drawsBackground="YES" id="Sb8-nl-Q5r">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button toolTip="Generate a new MAC address" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gZ8-0N-f6V">
<rect key="frame" x="276" y="263" width="157" height="32"/>
<buttonCell key="cell" type="push" title="New MAC Address" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="04X-Ao-SYd">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newMacAddrPressed:" target="-2" id="Lgm-OH-3ff"/>
</connections>
</button>
<button identifier="enableEthernet" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GCJ-G0-drE">
<rect key="frame" x="15" y="218" width="142" height="18"/>
<buttonCell key="cell" type="check" title="Enable Ethernet" bezelStyle="regularSquare" imagePosition="left" inset="2" id="03F-wY-3oH">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
@ -390,7 +579,6 @@
</tabView>
<button identifier="applyAndRun" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r8k-B7-iXM">
<rect key="frame" x="352" y="5" width="132" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Apply and Run" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="oR1-x8-VgY">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -401,7 +589,6 @@
</button>
<button identifier="cancel" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T6y-kh-jgH">
<rect key="frame" x="188" y="5" width="82" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Sk4-hr-Ot6">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -415,7 +602,6 @@ Gw
</button>
<button identifier="apply" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ZDd-0H-a3B">
<rect key="frame" x="270" y="5" width="82" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Apply" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bC6-3u-5Op">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>

View File

@ -31,7 +31,6 @@
@interface shoeScreenView : NSOpenGLView {
CGColorSpaceRef colorspace;
shoebill_control_t *control;
NSTimer *timer;
NSRecursiveLock *lock;
CIContext *ciContext;

View File

@ -35,7 +35,6 @@
- (void)initCommon
{
shoeApp = (shoeApplication*) NSApp;
control = &shoeApp->control;
}
@ -73,7 +72,7 @@
colorspace = CGColorSpaceCreateDeviceRGB();
timer = [NSTimer
scheduledTimerWithTimeInterval:0.001
scheduledTimerWithTimeInterval:(1.0/60.0)
target:self
selector:@selector(timerFireMethod:)
userInfo:nil
@ -83,16 +82,7 @@
[[NSRunLoop currentRunLoop] addTimer:timer
forMode:NSEventTrackingRunLoopMode];
shoebill_card_video_t *video = &control->slots[10].card.video;
NSSize size = {
.height=video->height,
.width=video->width
};
[[self window] setContentSize:size];
[[self window] setTitle:[NSString stringWithFormat:@"Shoebill - Screen 1"]];
[[self window] setTitle:[NSString stringWithFormat:@"Shoebill"]];
[[self window] makeKeyAndOrderFront:nil];
}
@ -103,83 +93,35 @@
- (void)prepareOpenGL
{
NSRect frame = [self frame];
NSRect bounds = [self bounds];
GLfloat minX, minY, maxX, maxY;
minX = NSMinX(bounds);
minY = NSMinY(bounds);
maxX = NSMaxX(bounds);
maxY = NSMaxY(bounds);
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
if(NSIsEmptyRect([self visibleRect]))
{
glViewport(0, 0, 1, 1);
} else {
glViewport(0, 0, frame.size.width ,frame.size.height);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(minX, maxX, minY, maxY, -1.0, 1.0);
}
static void _do_clut_translation(shoebill_card_video_t *ctx)
{
uint32_t i;
switch (ctx->depth) {
case 1: {
for (i=0; i < ctx->pixels/8; i++) {
const uint8_t byte = ctx->indexed_buf[i];
ctx->direct_buf[i * 8 + 0] = ctx->clut[(byte >> 7) & 1];
ctx->direct_buf[i * 8 + 1] = ctx->clut[(byte >> 6) & 1];
ctx->direct_buf[i * 8 + 2] = ctx->clut[(byte >> 5) & 1];
ctx->direct_buf[i * 8 + 3] = ctx->clut[(byte >> 4) & 1];
ctx->direct_buf[i * 8 + 4] = ctx->clut[(byte >> 3) & 1];
ctx->direct_buf[i * 8 + 5] = ctx->clut[(byte >> 2) & 1];
ctx->direct_buf[i * 8 + 6] = ctx->clut[(byte >> 1) & 1];
ctx->direct_buf[i * 8 + 7] = ctx->clut[(byte >> 0) & 1];
}
break;
}
case 2: {
for (i=0; i < ctx->pixels/4; i++) {
const uint8_t byte = ctx->indexed_buf[i];
ctx->direct_buf[i * 4 + 0] = ctx->clut[(byte >> 6) & 3];
ctx->direct_buf[i * 4 + 1] = ctx->clut[(byte >> 4) & 3];
ctx->direct_buf[i * 4 + 2] = ctx->clut[(byte >> 2) & 3];
ctx->direct_buf[i * 4 + 3] = ctx->clut[(byte >> 0) & 3];
}
break;
}
case 4: {
for (i=0; i < ctx->pixels/2; i++) {
const uint8_t byte = ctx->indexed_buf[i];
ctx->direct_buf[i * 2 + 0] = ctx->clut[(byte >> 4) & 0xf];
ctx->direct_buf[i * 2 + 1] = ctx->clut[(byte >> 0) & 0xf];
}
break;
}
case 8:
for (i=0; i < ctx->pixels; i++)
ctx->direct_buf[i] = ctx->clut[ctx->indexed_buf[i]];
break;
default:
assert(!"unknown depth");
}
}
- (void)drawRect:(NSRect)rect
{
const uint8_t slotnum = ((shoeScreenWindowController*)[[self window] windowController])->slotnum;
[[self openGLContext] makeCurrentContext];
NSRect frame = [self frame];
NSRect bounds = [self bounds];
GLfloat minX = NSMinX(bounds);
GLfloat minY = NSMinY(bounds);
GLfloat maxX = NSMaxX(bounds);
GLfloat maxY = NSMaxY(bounds);
if(NSIsEmptyRect([self visibleRect]))
glViewport(0, 0, 1, 1);
else
glViewport(0, 0, frame.size.width ,frame.size.height);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(minX, maxX, minY, maxY, -1.0, 1.0);
glDrawBuffer(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT);
@ -187,32 +129,29 @@ static void _do_clut_translation(shoebill_card_video_t *ctx)
glClearColor(0.0, 0.0, 0.0, 0.0);
if (shoeApp->isRunning) {
uint8_t slotnum = ((shoeScreenWindow*)[self window])->slotnum;
shoebill_card_video_t *video = &control->slots[slotnum].card.video;
shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 0);
/*NSSize size = {
.height=video->height,
.width=video->width
};
[self setFrameSize:size];
[[self window] setContentSize:size];*/
_do_clut_translation(video);
glViewport(0, 0, video->width, video->height);
glRasterPos2i(0, video->height);
glViewport(0, 0, frame.width, frame.height);
glRasterPos2i(0, frame.height);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelZoom(1.0, -1.0);
glDrawPixels(video->width,
video->height,
glDrawPixels(frame.width,
frame.height,
GL_RGBA,
GL_UNSIGNED_BYTE,
video->direct_buf);
frame.buf);
[[self openGLContext] flushBuffer];
shoebill_send_vbl_interrupt(slotnum);
}
else {
[[self openGLContext] flushBuffer];
}
[[self openGLContext] flushBuffer];
}
- (void)viewDidMoveToWindow
@ -247,18 +186,6 @@ static void _do_clut_translation(shoebill_card_video_t *ctx)
[self mouseMoved:theEvent];
}
/*- (void) say:(NSString*)str
{
NSAlert *theAlert = [NSAlert
alertWithMessageText:nil
defaultButton:nil
alternateButton:nil
otherButton:nil
informativeTextWithFormat:@"%@", str
];
[theAlert runModal];
}*/
- (void)mouseDown:(NSEvent *)theEvent
{

View File

@ -1,19 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment defaultVersion="1080" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSWindowController">
<customObject id="-2" userLabel="File's Owner" customClass="shoeScreenWindowController">
<connections>
<outlet property="window" destination="1" id="aqm-Nb-PaF"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="shoeApplication"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" wantsToBeColor="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" customClass="shoeScreenWindow">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="1" customClass="shoeScreenWindow">
<windowStyleMask key="styleMask" titled="YES" miniaturizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenPrimary="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="480" height="270"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
@ -23,4 +24,4 @@
</view>
</window>
</objects>
</document>
</document>

View File

@ -28,10 +28,8 @@
@interface shoeScreenWindow : NSWindow {
@public
uint8_t slotnum;
}
- (void) configure:(uint8_t) _slotnum;
- (void) reevaluateKeyWindowness;
- (void) warpToCenter;
- (void) captureMouse;

View File

@ -29,20 +29,6 @@
@implementation shoeScreenWindow
- (void)configure:(uint8_t) _slotnum
{
slotnum = _slotnum;
shoeApplication *shoeApp = (shoeApplication*) NSApp;
shoebill_control_t *control = &shoeApp->control;
shoebill_card_video_t *video = &control->slots[slotnum].card.video;
NSSize size = {
.height=video->height,
.width=video->width
};
[self setContentSize:size];
}
// Called after all the shoeScreenWindows are created and configured,
// because one of them was already made key while isRunning==NO,
@ -50,7 +36,7 @@
- (void)reevaluateKeyWindowness
{
shoeApplication *shoeApp = (shoeApplication*)NSApp;
assert(shoeApp->isRunning);
if ([self isKeyWindow]) {
@ -62,6 +48,19 @@
}
}
- (void)toggleFullScreen:(id)sender
{
[super toggleFullScreen:sender];
const uint8_t slotnum = ((shoeScreenWindowController*)[self windowController])->slotnum;
shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 1);
NSSize size = {
.height=frame.height,
.width=frame.width,
};
[self setContentSize:size];
}
- (void)becomeKeyWindow
{
shoeApplication *shoeApp = (shoeApplication*)NSApp;
@ -105,7 +104,7 @@
shoeApplication *shoeApp = (shoeApplication*)NSApp;
shoeApp->doCaptureMouse = NO;
CGDisplayShowCursor(0);
[self setTitle:@"Shoebill - Screen 1"];
[self setTitle:@"Shoebill"];
}
- (void) captureMouse
@ -114,7 +113,7 @@
shoeApp->doCaptureMouse = YES;
CGDisplayHideCursor(0);
[self warpToCenter];
[self setTitle:@"Shoebill - Screen 1 (Ctrl-click to escape)"];
[self setTitle:@"Shoebill (Ctrl-click to escape)"];
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Rutenbar <pruten@gmail.com>
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -23,28 +23,13 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _REDBLACK_H
#define _REDBLACK_H
#import <Cocoa/Cocoa.h>
typedef uint32_t rb_key_t;
typedef void* rb_value_t;
@interface shoeScreenWindowController : NSWindowController {
@public
uint8_t slotnum;
}
typedef struct _rb_node {
struct _rb_node *left, *right, *parent;
rb_key_t key;
rb_value_t value;
uint8_t is_red : 1;
} rb_node;
- (id)initWithWindowNibName:(NSString *)windowNibName slotnum:(uint8_t)_slotnum;
typedef rb_node* rb_tree;
rb_tree* rb_new();
void rb_free (rb_tree *tree);
uint8_t rb_insert (rb_tree *root, rb_key_t key, rb_value_t value, rb_value_t *old_value);
uint8_t rb_find (rb_tree *tree, rb_key_t key, rb_value_t *value);
uint8_t rb_index (rb_tree *tree, uint32_t index, rb_key_t *key, rb_value_t *value);
uint32_t rb_count (rb_tree *tree);
#endif // _REDBLACK_H
@end

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "shoeScreenWindowController.h"
#import "shoeApplication.h"
@interface shoeScreenWindowController ()
@end
@implementation shoeScreenWindowController
- (id)initWithWindowNibName:(NSString *)windowNibName
slotnum:(uint8_t)_slotnum
{
shoeScreenWindowController *result = [super initWithWindowNibName:windowNibName];
result->slotnum = _slotnum;
return result;
}
- (void)windowDidLoad
{
[super windowDidLoad];
shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 1);
NSSize size = {
.height=frame.height,
.width=frame.width,
};
[[self window] setContentSize:size];
}
- (NSApplicationPresentationOptions)window:(NSWindow *)window
willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
{
return (NSApplicationPresentationFullScreen | // support full screen for this window (required)
NSApplicationPresentationHideDock | // completely hide the dock
NSApplicationPresentationAutoHideMenuBar); // yes we want the menu bar to show/hide
}
- (NSSize)window:(NSWindow *)window willUseFullScreenContentSize:(NSSize)proposedSize
{
shoebill_video_frame_info_t frame = shoebill_get_video_frame(slotnum, 1);
NSSize size = {
.height=frame.height,
.width=frame.width,
};
return size;
}
@end

22
sdl-gui/lin_build.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
CC=gcc
files=""
for i in adb fpu mc68851 mem via floppy core_api cpu dis; do
perl ../core/macro.pl ../core/$i.c $i.post.c
files="$files $i.post.c"
done
for i in SoftFloat/softfloat atrap_tab coff exception macii_symbols redblack scsi video filesystem alloc_pool toby_frame_buffer ethernet sound; do
files="$files ../core/$i.c"
done
$CC -O1 ../core/decoder_gen.c -o decoder_gen
./decoder_gen inst .
./decoder_gen dis .
cmd="$CC -O3 -ggdb -flto $files sdl.c -lpthread -lm -lSDL2 -lGL -o shoebill"
echo $cmd
$cmd

22
sdl-gui/osx_build.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
CC=gcc
files=""
for i in adb fpu mc68851 mem via floppy core_api cpu dis; do
perl ../core/macro.pl ../core/$i.c $i.post.c
files="$files $i.post.c"
done
for i in SoftFloat/softfloat atrap_tab coff exception macii_symbols redblack scsi video filesystem alloc_pool toby_frame_buffer ethernet sound; do
files="$files ../core/$i.c"
done
$CC -O1 ../core/decoder_gen.c -o decoder_gen
./decoder_gen inst .
./decoder_gen dis .
cmd="$CC -F/Library/Frameworks -O3 -ggdb -flto $files sdl.c -framework OpenGL -framework SDL2 -o shoebill"
echo $cmd
$cmd

635
sdl-gui/sdl.c Normal file
View File

@ -0,0 +1,635 @@
/*
* Copyright (c) 2014, Peter Rutenbar <pruten@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include "../core/shoebill.h"
static void _print_vers(void)
{
printf("Shoebill v0.0.4 - http://github.com/pruten/shoebill - Peter Rutenbar (c) 2014\n\n");
}
rb_tree *keymap;
static void _init_keyboard_map (void)
{
#define mapkeymod(u, a, m) do { \
assert((a >> 7) == 0); \
uint16_t value = ((m) << 8)| (a); \
rb_insert(keymap, u, &value, NULL); \
} while (0)
#define mapkey(_u, a) mapkeymod(_u, a, 0)
keymap = rb_new(p_new_pool(NULL), sizeof(uint16_t));
// Letters
mapkey('a', 0x00);
mapkey('b', 0x0b);
mapkey('c', 0x08);
mapkey('d', 0x02);
mapkey('e', 0x0e);
mapkey('f', 0x03);
mapkey('g', 0x05);
mapkey('h', 0x04);
mapkey('i', 0x22);
mapkey('j', 0x26);
mapkey('k', 0x28);
mapkey('l', 0x25);
mapkey('m', 0x2e);
mapkey('n', 0x2d);
mapkey('o', 0x1f);
mapkey('p', 0x23);
mapkey('q', 0x0c);
mapkey('r', 0x0f);
mapkey('s', 0x01);
mapkey('t', 0x11);
mapkey('u', 0x20);
mapkey('v', 0x09);
mapkey('w', 0x0d);
mapkey('x', 0x07);
mapkey('y', 0x10);
mapkey('z', 0x06);
// Numbers
mapkey('0', 0x1d);
mapkey('1', 0x12);
mapkey('2', 0x13);
mapkey('3', 0x14);
mapkey('4', 0x15);
mapkey('5', 0x17);
mapkey('6', 0x16);
mapkey('7', 0x1a);
mapkey('8', 0x1c);
mapkey('9', 0x19);
// Top row symbols
mapkeymod(')', 0x1d, modShift);
mapkeymod('!', 0x12, modShift);
mapkeymod('@', 0x13, modShift);
mapkeymod('#', 0x14, modShift);
mapkeymod('$', 0x15, modShift);
mapkeymod('%', 0x17, modShift);
mapkeymod('^', 0x16, modShift);
mapkeymod('&', 0x1a, modShift);
mapkeymod('*', 0x1c, modShift);
mapkeymod('(', 0x19, modShift);
// Other symbols (no shift)
mapkeymod('`', 0x32, 0);
mapkeymod('-', 0x1b, 0);
mapkeymod('=', 0x18, 0);
mapkeymod('[', 0x21, 0);
mapkeymod(']', 0x1e, 0);
mapkeymod('\\', 0x2a, 0);
mapkeymod(';', 0x29, 0);
mapkeymod('\'', 0x27, 0);
mapkeymod(',', 0x2b, 0);
mapkeymod('.', 0x2f, 0);
mapkeymod('/', 0x2c, 0);
// Other symbols (with shift)
mapkeymod('~', 0x32, modShift);
mapkeymod('_', 0x1b, modShift);
mapkeymod('+', 0x18, modShift);
mapkeymod('{', 0x21, modShift);
mapkeymod('}', 0x1e, modShift);
mapkeymod('|', 0x2a, modShift);
mapkeymod(':', 0x29, modShift);
mapkeymod('"', 0x27, modShift);
mapkeymod('<', 0x2b, modShift);
mapkeymod('>', 0x2f, modShift);
mapkeymod('?', 0x2c, modShift);
// Function keys
mapkey(SDLK_F1, 0x7a);
mapkey(SDLK_F2, 0x78);
mapkey(SDLK_F3, 0x63);
mapkey(SDLK_F4, 0x76);
mapkey(SDLK_F5, 0x60);
mapkey(SDLK_F6, 0x61);
mapkey(SDLK_F7, 0x62);
mapkey(SDLK_F8, 0x64);
mapkey(SDLK_F9, 0x65);
mapkey(SDLK_F10, 0x6d);
mapkey(SDLK_F11, 0x67);
mapkey(SDLK_F12, 0x6f);
mapkey(SDLK_F13, 0x69);
mapkey(SDLK_F14, 0x6b);
mapkey(SDLK_F15, 0x71);
// Arrows
mapkey(SDLK_UP, 0x3e);
mapkey(SDLK_DOWN, 0x3d);
mapkey(SDLK_RIGHT, 0x3c);
mapkey(SDLK_LEFT, 0x3b);
// Delete
mapkey(SDLK_DELETE, 0x75);
mapkey(SDLK_BACKSPACE, 0x33);
mapkey(SDLK_BACKSPACE, 0x33);
// Enter, NL, CR
mapkey(SDLK_RETURN2, 0x24);
mapkey(SDLK_RETURN, 0x24);
// mapkey(0x03, 0x24);
// Other keys
mapkey(SDLK_ESCAPE, 0x35); // escape
mapkey(SDLK_SPACE, 0x31); // space
mapkey(SDLK_TAB, 0x30); // tab
}
static void _display_frame (SDL_Window *win)
{
shoebill_video_frame_info_t frame = shoebill_get_video_frame(9, 0);
shoebill_send_vbl_interrupt(9);
glDrawBuffer(GL_BACK);
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(0, 0, 0, 1.0);
glViewport(0, 0, frame.width, frame.height);
glRasterPos2i(0, frame.height);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_TRUE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelZoom(1.0, -1.0);
glDrawPixels(frame.width,
frame.height,
GL_RGBA,
GL_UNSIGNED_BYTE,
frame.buf);
SDL_GL_SwapWindow(win);
}
struct shoe_app_pram_data_t {
uint8_t pram[256];
FILE *f;
pthread_t threadid;
volatile _Bool updated, tear_down_thread;
};
struct {
const char *scsi_path[8];
const char *rom_path;
const char *relative_unix_path;
const char *pram_path;
uint32_t height, width;
uint32_t ram_megabytes;
_Bool verbose, use_tfb;
struct shoe_app_pram_data_t pram_data;
} user_params;
#define equals_arg(name) keylen = strlen(name); value = argv[i]+keylen; if (strncmp((name), argv[i], keylen) == 0)
#if !((defined WIN32) || (defined _WIN64))
#include <sys/types.h>
#include <pwd.h>
#include <uuid/uuid.h>
#endif
static char* _get_home_dir (const char *terminal_element)
{
char *result = NULL;
#if (defined WIN32) || (defined _WIN64)
if (getenv("USERPROFILE") != NULL) {
result = malloc(strlen(getenv("USERPROFILE")) + strlen(terminal_element) + 32);
sprintf(result, "%s\\%s", getenv("USERPROFILE"), terminal_element);
goto done;
}
if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
result = malloc(strlen(getenv("HOMEDRIVE")) + strlen(getenv("HOMEPATH")) + strlen(terminal_element) + 32);
sprintf(result, "%s\\%s\\%s", getenv("HOMEDRIVE"), getenv("HOMEPATH"), terminal_element);
goto done;
}
#else
if (getenv("HOME") != NULL) {
result = malloc(strlen(getenv("HOME")) + strlen(terminal_element) + 32);
sprintf(result, "%s/%s", getenv("HOME"), terminal_element);
goto done;
}
struct passwd *pwd = getpwuid(getuid());
if (pwd) {
result = malloc(strlen(pwd->pw_dir) + strlen(terminal_element) + 32);
sprintf(result, "%s/%s", pwd->pw_dir, terminal_element);
goto done;
}
#endif
done:
// printf("_get_home_dir: debug: %s\n", result);
return result;
}
static void _print_help (void)
{
printf("Arguments have the form name=value.\n");
printf("\n");
printf("rom=<path to Mac II ROM>\n");
printf("Specifies the path to a Macintosh II ROM.\n");
printf("E.g. rom=/home/foo/macii.rom\n");
printf("\n");
printf("disk0..disk6=<path to disk image>\n");
printf("Specifies the path to a disk image for the given SCSI ID. Shoebill will always boot from disk0, so make sure disk0 points to a bootable A/UX image.\n");
printf("E.g. disk0=/home/foo/aux3.img disk1=/blah.img\n");
printf("\n");
printf("ram=<megabytes of memory>\n");
printf("E.g. ram=16\n");
printf("\n");
printf("height=<num pixels>\n");
printf("Specifies the height of the screen in pixels.\n");
printf("\n");
printf("width=<num pixels>\n");
printf("Specifies the width of the screen in pixels.\n");
printf("\n");
printf("pram-path=<path to PRAM file>\n");
printf("Defaults to ~/.shoebill_pram\n");
printf("\n");
printf("verbose=<1 or 0>\n");
printf("Whether to boot A/UX in verbose mode. Best to leave it at default (1).\n");
printf("\n");
printf("unix-path=<path to kernel on disk0>\n");
printf("Path to the kernel file on the root disk image. Best to leave it at default (/unix).\n");
printf("\n");
printf("\n");
printf("Examples:\n");
printf("\n");
printf("shoebill.exe disk0=C:\\aux3.img rom=C:\\macii.rom width=1024 height=768 ram=64\n");
printf("\n");
printf("./shoebill disk0=/aux3.img rom=/macii.rom width=1024 height=768 ram=64\n");
printf("\n");
}
static void _init_user_params (int argc, char **argv)
{
char *key;
uint32_t i;
for (i=0; i<8; i++)
user_params.scsi_path[i] = NULL;
user_params.rom_path = "macii.rom";
user_params.relative_unix_path = "/unix";
user_params.height = 640;
user_params.width = 800;
user_params.ram_megabytes = 16;
user_params.verbose = 1;
user_params.use_tfb = 0;
user_params.pram_path = _get_home_dir(".shoebill_pram");
if (argc < 2) {
_print_help();
exit(0);
}
for (i=1; i<argc; i++) {
key = "-h";
if (strncmp(key, argv[i], strlen(key)) == 0) {
_print_help();
exit(0);
}
key = "help";
if (strncmp(key, argv[i], strlen(key)) == 0) {
_print_help();
exit(0);
}
key = "toby"; // Whether to use the "toby frame buffer" card, instead of the regular shoebill video card
if(strncmp(key, argv[i], strlen(key)) == 0) {
user_params.use_tfb = 1;
continue;
}
key = "ram=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.ram_megabytes = strtoul(argv[i]+strlen(key), NULL, 10);
continue;
}
key = "height=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.height = strtoul(argv[i]+strlen(key), NULL, 10);
continue;
}
key = "width=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.width = strtoul(argv[i]+strlen(key), NULL, 10);
continue;
}
key = "verbose=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.verbose = strtoul(argv[i]+strlen(key), NULL, 10);
continue;
}
key = "rom=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.rom_path = argv[i] + strlen(key);
continue;
}
key = "unix-path=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.relative_unix_path = argv[i] + strlen(key);
continue;
}
key = "pram-path=";
if (strncmp(key, argv[i], strlen(key)) == 0) {
user_params.pram_path = argv[i] + strlen(key);
continue;
}
if ((strncmp("disk", argv[i], 4) == 0) && (isdigit(argv[i][4])) && (argv[i][5] == '=')) {
uint8_t scsi_num = argv[i][4] - '0';
if (scsi_num < 7) {
user_params.scsi_path[scsi_num] = &argv[i][6];
continue;
}
}
}
}
void _pram_callback (void *param, const uint8_t addr, const uint8_t byte)
{
struct shoe_app_pram_data_t *pram_data = (struct shoe_app_pram_data_t*)param;
pram_data->pram[addr] = byte;
pram_data->updated = 1;
}
void* _pram_writer_thread (void *param)
{
struct shoe_app_pram_data_t *pram_data = (struct shoe_app_pram_data_t*)param;
while (!pram_data->tear_down_thread) {
if (pram_data->updated) {
pram_data->updated = 0;
rewind(pram_data->f);
assert(fwrite(pram_data->pram, 256, 1, pram_data->f) == 1);
fflush(stdout);
pram_data->tear_down_thread = 0;
}
sleep(1);
}
return NULL;
}
static _Bool _setup_shoebill (void)
{
uint32_t i;
shoebill_config_t config;
memset(&config, 0, sizeof(shoebill_config_t));
config.aux_verbose = user_params.verbose;
config.ram_size = user_params.ram_megabytes * 1024 * 1024;
config.aux_kernel_path = user_params.relative_unix_path;
config.rom_path = user_params.rom_path;
config.pram_callback = _pram_callback;
config.pram_callback_param = (void*)&user_params.pram_data;
memcpy(config.pram, user_params.pram_data.pram, 256);
for (i=0; i<7; i++)
config.scsi_devices[i].path = user_params.scsi_path[i];
if (!shoebill_initialize(&config)) {
printf("%s\n", config.error_msg);
return 0;
}
if (user_params.use_tfb) {
shoebill_install_tfb_card(&config, 9);
}
else {
shoebill_install_video_card(&config,
9, // slotnum
user_params.width,
user_params.height);
}
shoebill_start();
return 1;
}
static void _handle_key_event (SDL_Event *event)
{
const SDL_Keycode sym = event->key.keysym.sym;
const _Bool key_down = (event->type == SDL_KEYDOWN);
const SDL_Keymod sdl_mod = SDL_GetModState();
uint16_t adb_mod = 0;
uint16_t value;
if (sdl_mod & KMOD_SHIFT) adb_mod |= modShift;
if (sdl_mod & KMOD_CTRL) adb_mod |= modControl;
if (sdl_mod & KMOD_ALT) adb_mod |= modOption;
if (sdl_mod & KMOD_GUI) adb_mod |= modCommand;
if (sdl_mod & KMOD_CAPS) adb_mod |= modCapsLock;
if (rb_find(keymap, sym, &value)) {
shoebill_key_modifier((value >> 8) | adb_mod);
shoebill_key(key_down, value & 0xff);
}
}
static _Bool _init_pram (void)
{
FILE *f = fopen(user_params.pram_path, "r+b");
memset(&user_params.pram_data, 0, sizeof(struct shoe_app_pram_data_t));
if ((f == NULL) || (fread(user_params.pram_data.pram, 256, 1, f) != 1)) {
if (f == NULL)
f = fopen(user_params.pram_path, "w+b");
if (f == NULL) {
printf("Can't open pram_path! [%s] [errno=%s]\n",
user_params.pram_path,
sys_errlist[errno]);
return 0;
}
rewind(f);
shoebill_validate_or_zap_pram(user_params.pram_data.pram, 1);
assert(fwrite(user_params.pram_data.pram, 256, 1, f) == 1);
fflush(f);
}
user_params.pram_data.f = f;
shoebill_validate_or_zap_pram(user_params.pram_data.pram, 0);
pthread_create(&user_params.pram_data.threadid,
NULL,
_pram_writer_thread,
&user_params.pram_data);
return 1;
}
int main (int argc, char **argv)
{
const uint32_t frame_ticks = 1000 / 60;
uint32_t last_frame_ticks;
_Bool capture_cursor;
_print_vers();
_init_keyboard_map();
_init_user_params(argc, argv);
if (!_init_pram())
return 0;
else if (!_setup_shoebill())
return 0;
shoebill_video_frame_info_t frame = shoebill_get_video_frame(9, 1);
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *win = SDL_CreateWindow("Shoebill",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
frame.width, frame.height,
SDL_WINDOW_OPENGL);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GLContext glctx = SDL_GL_CreateContext(win);
glShadeModel(GL_FLAT);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glClearColor(0.5, 0.5, 0.5, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, frame.width, 0, frame.height, -1.0, 1.0);
capture_cursor = 1;
SDL_ShowCursor(0);
SDL_SetRelativeMouseMode(1);
SDL_GL_SetSwapInterval(1);
last_frame_ticks = SDL_GetTicks();
while (1) {
const uint32_t now = SDL_GetTicks();
uint32_t ticks_to_next_frame;
SDL_Event event;
if ((now - last_frame_ticks) >= frame_ticks) {
_display_frame(win);
last_frame_ticks = now;
ticks_to_next_frame = frame_ticks;
}
else
ticks_to_next_frame = frame_ticks - (now - last_frame_ticks);
event.type = SDL_USEREVENT;
SDL_WaitEventTimeout(&event, ticks_to_next_frame);
switch (event.type) {
case SDL_QUIT:
goto quit;
case SDL_MOUSEBUTTONDOWN: {
if ((event.button.button == SDL_BUTTON_LEFT) && capture_cursor)
shoebill_mouse_click(1);
break;
}
case SDL_MOUSEBUTTONUP: {
// If the cursor isn't captured, then any click will capture it
if (!capture_cursor)
capture_cursor = 1;
// If the cursor is captured, then left clicks get sent to the OS
else if (event.button.button == SDL_BUTTON_LEFT)
shoebill_mouse_click(0);
// If the cursor is captured, then right clicks will uncapture it
else if ((event.button.button == SDL_BUTTON_RIGHT) && capture_cursor)
capture_cursor = 0;
break;
}
case SDL_MOUSEMOTION: {
if (capture_cursor) {
_Bool down = event.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT);
shoebill_mouse_click(down);
shoebill_mouse_move_delta(event.motion.xrel, event.motion.yrel);
}
break;
}
case SDL_KEYDOWN:
case SDL_KEYUP:
if (!event.key.repeat)
_handle_key_event(&event);
break;
}
if (capture_cursor) {
SDL_ShowCursor(0);
SDL_SetRelativeMouseMode(1);
}
else {
SDL_ShowCursor(1);
SDL_SetRelativeMouseMode(0);
}
}
quit:
// FIXME: tear down the pram thread and flush pram
return 0;
}

9
sdl-gui/win_build.bat Executable file
View File

@ -0,0 +1,9 @@
for %%i in (adb fpu mc68851 mem via floppy core_api cpu dis) do (
perl ..\core\macro.pl ..\core\%%i.c %%i.post.c
)
gcc -O1 ..\core\decoder_gen.c -o decoder_gen
decoder_gen inst .
decoder_gen dis .
gcc -O3 -flto -mno-ms-bitfields sdl.c adb.post.c fpu.post.c mc68851.post.c mem.post.c via.post.c floppy.post.c core_api.post.c cpu.post.c dis.post.c ..\core\atrap_tab.c ..\core\coff.c ..\core\exception.c ..\core\macii_symbols.c ..\core\redblack.c ..\core\scsi.c ..\core\video.c ..\core\filesystem.c ..\core\alloc_pool.c ..\core\toby_frame_buffer.c ..\core\ethernet.c ..\core\sound.c ..\core\SoftFloat\softfloat.c -lmingw32 -lopengl32 -lsdl2main -lsdl2 -o shoebill

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB