1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-12-05 19:50:33 +00:00
Commit Graph

244 Commits

Author SHA1 Message Date
Andy McFadden
3d4250cbc4 Auto-save, part 1
Added configuration option to app settings.  It's in the "code view"
tab, which isn't quite right, but none of the others fit better.

Also, force a Save As when a new project is first created.
2024-08-02 13:47:39 -07:00
Andy McFadden
ef13101923 Clarify some app settings
The pop-up that lets you choose which assembler to set configuration
options for looked like it let you choose an assembler generally.
Changed some wording and visuals for better clarity.
2024-08-01 15:08:43 -07:00
Andy McFadden
c78f1c945e Disable "seek nearby targets" for new projects
It's really handy for certain types of programs, but it's a
distraction in many others.  I find myself turning it off for most
things.

Some changes will be needed in the tutorial, which makes use of
the feature.  (The "odds & ends" section discusses it in detail.)
I'm putting that off until other changes are in.
2024-08-01 09:08:18 -07:00
Andy McFadden
5c6d1b5016 Project/platform syms for address table formatting
The address table formatter tries to map the calculated addresses
to offsets within the file.  When that fails, it currently just
gives up.

We now try to resolve external addresses to project/platform symbols.

(issue #160)
2024-07-08 10:09:38 -07:00
Andy McFadden
5a560aa9eb Fancy comments, part 5 (of 5)
Added a "format help" button to the long comment edit window.  This
brings up a quick summary of the format tags in a modal dialog.

Updated documentation and tutorial.
2024-07-07 18:12:29 -07:00
Andy McFadden
1c84357bbf Fancy comments, part 2
Started implementing the fancy formatter.  It currently doesn't do
anything fancy, just word-wrapping.

Moved the static DebugShowRuler field into Formatter, so that the
cached data is discarded when the setting changes.

Also, updated SourceNotes with instructions for publishing a release.
2024-07-06 15:01:24 -07:00
Andy McFadden
83da0d952c Fancy comments, part 1
Added a "fancy" flag to MultiLineComment.  If set, we will use
formatting commands embedded in the text itself.  The max width pop-up
and "boxed" flag will be ignored in favor of width and boxing
directives.  (See issue #111.)

The current "fancy" formatter is just a placeholder that folds lines
every 10 characters.

Removed FormattedMlcCache (added two changes back).  Caching the
rendered string list in the MLC itself is simpler and more efficient
than having a separate cache, and it works for Notes as well.

Added anchors for more comments in the 20090 test.
2024-07-04 17:25:21 -07:00
Andy McFadden
9784ad043e Use '*' for full-line Merlin comments
While it's okay to use ';', the classic Merlin editor will treat it
as an end-of-line comment and shove the entire thing off to the right
side of the screen.

This adds a configuration item to the app settings, with a default
value of ';'.
2024-06-26 10:54:46 -07:00
Andy McFadden
18674e6627 Rework Formatter.FormatConfig
It was defined as a struct with exposed fields.  Now it's a class
with simple properties.  Default values are set explicitly, and
the contents are copied with a copy constructor instead of using
the struct member assignment.

The only functional change should be that the DelimiterSet members
are now properly cloned.  The bulk of the changes are just refactoring
renames for the property names.

This also adds comments for all the properties.
2024-06-24 16:36:27 -07:00
Andy McFadden
4e5c34f457 Binary includes
This adds a new data format option, "binary include", that takes a
filename operand.  When assembly sources are generated, the section
of file is replaced with an appropriate pseudo-op, and binary files
are generated that hold the file contents.  This is a convenient way
to remove large binary blobs, such as music or sound samples, that
aren't useful to have in text form in the sources.

Partial pathnames are allowed, so you can output a sound blob to
"sounds/blather.bin".  For safety reasons, we don't allow the files
to be created above the project directory, and existing files will
only be overwritten if they have a matching length (so you don't
accidentally stomp on your project file).

The files are not currently shown in the GenAsm dialog, which lets
you see a preview of the generated sources.  The hex dump tool
can do this for the (presumably rare) situations where it's useful.

A new regression test, 20300-binary-include, has been added.  The
pseudo-op name can be overridden on-screen in the settings.

We don't currently do anything new for text/HTML exports.  It might
be useful to generate an optional appendix with a hex dump of the
excised sections.

(issue #144)
2024-05-31 14:22:39 -07:00
Andy McFadden
ea4ddc1071 Address region isolation, part 1
This adds a pair of flags to address regions that control how
address resolution is performed.

Generally, when we're trying to convert an address to a file offset,
we do a depth-first search through the hierarchy of address regions
to find a match.  For certain situations, such as multi-bank ROMs or
when code is executed in a separate subsystem, we don't want the
address resolver to specify an offset for something that's in a
different address space.

The search for a matching address starts from the region where the
address is referenced.  The flags will prevent the search from
progressing "outward" (to parent or sibling regions) or "inward"
(from parent or sibling regions).  Setting both flags effectively
makes the region an island.

Descending farther into the tree is not restricted by the "outward"
flag, i.e. addresses will still be found in child regions (assuming
they don't have the "disallow inward" flag set).

(issue #139)
2024-05-20 14:50:18 -07:00
Andy McFadden
d6cfc83368 Disable "prefer 32 bit" build flag
The build flag causes the application to run as a 32-bit app on 64-bit
systems, which we don't want.  We're still using "Any CPU" as the
platform target, so it will continue to work on 32-bit x86 systems;
we just no longer force it to always be 32-bit.

This only affects the main .NET Framework application executable.
The setting is not relevant for the libraries.

(Note to future self: the way things work changed with .NET Core,
which requires different executables for different targets, specified
with the RID.  The older .NET Framework is Windows-only, which makes
it easier to have a single multi-target EXE.)
2024-05-09 10:03:08 -07:00
Andy McFadden
4322a0c231 Add option to put labels on separate lines
We currently have two options for assembly code output, selected by
a checkbox in the application settings: always put labels on the same
lines as the instruction or data operand, or split the labels onto
their own line if they were wider than the label text field.

This change adds a third option, which puts labels on their own line
whenever possible.  Assemblers don't generally allow this for variable
assignment pseudo-ops like "foo = $1000", but it's accepted for most
other situations.  This is a cosmetic change to the output, and will
not affect the generated code.

The old true/false app setting will be disregarded.  "Split if too
long" will be used by default.

Added test 20280-label-placement to exercise the "split whenever
allowed" behavior.

The "export" function has a similar option that has not been updated
(for no particular reason other than laziness).

Also, simplified the app settings GetEnum / SetEnum calls, which
can infer the enumerated type from the arguments.  This should not
impact behavior.
2024-04-21 16:26:42 -07:00
Andy McFadden
e425237783 First cut at label file generation
Some debuggers allow you to import a list of labels for addresses.
This adds a command that generates a file full of labels in a
specific format.  Currently the VICE monitor format is supported.

(issue #151)
2024-04-20 16:37:08 -07:00
Andy McFadden
da91d9fc0e Allow arbitrary code list font sizes
The point size must be between 3 and 64, inclusive, just to avoid
doing anything too weird.  Also, added 14 to the drop-down.
2024-04-19 14:46:17 -07:00
Andy McFadden
8532cfb433 Rename menu item
Changed "File > Assemble" to "File > Generate Assembly".

Also, correctly mark a read-only text field as such.
2024-04-19 14:16:30 -07:00
Andy McFadden
110a9ae818 Change the way app settings Apply button works
We were passing a reference to MainController in, and calling a
method on it, which is a little convoluted.  Now the main controller
subscribes to a "settings applied" event, and handles the update with
an event handler.

No change in behavior.
2024-03-02 14:48:06 -08:00
Andy McFadden
c637d6549c Minor updates 2024-03-02 11:17:52 -08:00
Andy McFadden
ca50730611 Update comments 2023-04-14 13:46:40 -07:00
Andy McFadden
0878854759 Fix crash in font picker
It's possible to have multiple fonts with the same font family.
Don't use the list add method that throws an exception when it
finds a duplicate key.
2022-04-29 09:00:01 -07:00
Andy McFadden
42fe037b55 Update copyright date in "about" 2022-03-02 09:37:18 -08:00
Andy McFadden
13dca8b78c More tweaks to def sym editing
If you edit an existing symbol, the "is the label unique" test will
always false-positive match itself, so we have to explicitly handle
that case.  Dialogs like Edit Instruction Operand make things a bit
more complicated because they don't flush results to the symbol
table immediately, which means the symbol we pass into the Edit Def
Symbol dialog to edit isn't necessarily the one we need to exclude
from the label uniqueness test.

The dialog was using the initial value as both "original" and
"initial", which caused some issues.  We now pass both values in.

Also, removed some dead code identified by VS.
2022-03-02 08:13:46 -08:00
Andy McFadden
ca3b6a9adb Fix crash when editing project symbol
The recent tweaks to improve operand editing broke a common case
when creating a project symbol.

Also, tweaked the operand edit case a little more, for the case where
the operand's symbol was entered manually.
2022-02-28 15:03:09 -08:00
Andy McFadden
55c80fb642 Improve double-click handling in symbols window
Double-clicking on an entry in the symbols window is supposed to take
you to the place where that symbol is defined.  This worked for code
labels but not for project/platform symbols.  We now jump to the
appropriate EQU statement, if one exists.
2022-01-12 15:28:59 -08:00
Andy McFadden
60b024d24e Improve instruction operand edit edge case
The instruction operand editor has a shortcut button for editing a
project symbol.  Attempting to change the comment field twice
without closing the operand editor in between would result in a
complaint about duplicate symbol names.
2022-01-12 14:17:28 -08:00
Andy McFadden
50a954cf4d Change default for "comma-separated bulk data"
The setting determines whether bulk data is displayed as an unbroken
string of hex digits, or as "$xx,$xx,...".  The latter is easier to
read and should be the default.

One place in the code did consider it to be the default, so if the
config file didn't have a value for the setting, the settings UI would
incorrectly show it as enabled.
2022-01-12 13:33:45 -08:00
Andy McFadden
75ccffe393 Update visualization bitmap API
The existing API was better suited to direct color than indexed
color.  The NES visualizer was using a slightly silly hack to avoid
duplicate colors; this has been removed.
2022-01-12 11:48:20 -08:00
Andy McFadden
92f304a030 Minor fixes
Escape '_' in filenames for the recents menu.

Fix status flag update for ROL.
2021-12-23 11:29:29 -08:00
Andy McFadden
5a400ab738 Change highlight region
The ListView style was using "Stretch" for TextBoxes in the code list,
which caused the background of the entire address / label / operand
field to be drawn in the highlight color, rather than just the area
covered by the text.  This is fine for address and label, but it just
felt weird for the operand field because that tends to be very wide
(to accommodate strings, bulk hex data, etc).

There doesn't seem to be a way to specify HorizontalContentAlignment
per-column in WPF.  (Note this is different from HorizontalAlignment,
which *is* is a per-column property.)

This changes the style to use HorizontalContentAlignment=Left, so
the highlight just covers the text.  The only time this causes a
functional change is when you highlight an operand for a line that
doesn't have a label, because instead of highlighting an empty
rectangle you now see nothing at all.  (The address field is still
highlighted though.)
2021-11-17 11:40:36 -08:00
Andy McFadden
33aa0ff004 Add operand highlighting
When a code or data line is selected in the code list, if the operand
is an address inside the file, we highlight the address and label.
It's also useful to highlight the other way: when a code or data line
is selected, find all lines whose operands reference it, and highlight
the operand field.

This is a little trickier because there can be multiple references,
but all of the information we need is in the cross-reference table.
2021-11-17 11:18:23 -08:00
Andy McFadden
3ea07f3eb7 Tweak comment entry UI
End-of-line comments have a couple of guidelines: keep it short, and
don't use non-ASCII characters.  Violating these isn't an error, so
we should be making the text blue rather than red.
2021-11-03 20:23:27 -07:00
Andy McFadden
a04557762d Fix keyboard nav after selection jump
If you change the selection by double-clicking in one of the side
windows (References, Notes, Symbols, Messages) and then attempt
to navigate with the arrow keys, the program will appear to hang
briefly, then jump to the start or end of the project and shift the
window focus to something else (like the Help menu in the menu bar).

For reasons I don't fully understand, the behavior is fixed by
removing unnecessary calls to codeListView.Focus().  The calls were
intended to shift input focus to the main ListView, but they're no
longer necessary, and appear to upset WPF.

(issue #113)
2021-10-25 15:45:42 -07:00
Andy McFadden
cd937709fa Tweak address region edit dialog
Altered the address region edit UI a little to improve clarity.

Also, close the hex dump viewer window when Escape is hit.  (The
tool windows don't have "cancel" buttons, so the key has to be
handled explicitly.)
2021-10-20 09:06:53 -07:00
Andy McFadden
0fb0b4cca8 Fix some address region issues
Fixed a crash when the listing is refreshed while a .adrend line is
the only thing selected.

Fixed .adrend lines being spammed if the last thing before it is a
multi-byte item, and you edit a comment / note / label on that line.
Harmless, but weird.

Also, added keyboard shortcuts in DefSymbol editor, so you can change
from address to constant with Alt+C.
2021-10-17 18:02:29 -07:00
Andy McFadden
d7dbd8d012 Minor tweak to instruction operand editor
If you put a value in the "symbol" field of an instruction with an
address operand, you're establishing a symbolic reference to a label
that may be at a different address than the numeric value of the
operand.  But if you then hit the "edit label" button, you'll edit
the label at the *numeric* reference address, which can be confusing.
We now disable the create/edit label button when the format has been
set to "symbol".

The button is just a short-cut, so disabling it doesn't prevent the
user from doing anything.
2021-10-16 14:46:13 -07:00
Andy McFadden
adf5726f62 Add daily tips to start screen
The initial screen is largely blank, with just the four large buttons
for new/open/recent1/recent2.  It now also has a "tip of the day" box,
with text and an optional image.

The tips and images are kept in the RuntimeData directory.  They're
small enough that they could have been baked into the binary, but
there's enough other stuff going on there that it didn't seem
necessary.  Also, if the tips annoy you, removing the tips file will
hide the tip UI.

The index of the tip shown is based on the day of the year, modulo
the number of defined tips.  So it will be different every day (with
a bit of hand-waving at the end of the year).
2021-10-16 09:15:28 -07:00
Andy McFadden
2008558870 Add "quick set" menu to delimiter settings tab
Added a pop-up menu with three options: default (curly quotes),
straight, and Merlin.  Removed the "reset to defaults" buttons.

Also, slightly rearranged the Display Format tab so that the quick
set pop-up is on the left, near the items it affects.  Moved the
"use comma-separated format for bulk data" checkbox over as well,
since it's part of the set.
2021-10-15 10:01:14 -07:00
Andy McFadden
cb114be0f6 Add "uninitialized data" format type
This allows regions that hold variable storage to be marked as data
that is initialized by the program before it is used.  Previously
the choices were to treat it as bulk data (initialized) or junk
(totally unused), neither of which are correct.

This is functionally equivalent to "junk" as far as source code
generation is concerned (though it doesn't have to be).

For the code/data/junk counter, uninitialized data is counted as
junk, because it technically does not need to be part of the binary.
2021-10-13 15:05:07 -07:00
Andy McFadden
09eba228dd Add "remove formatting" action
This action removes operand formatting from all code and data in the
selected range.  In most cases this is equivalent to simply editing
the various items and clicking the "default" format radio button,
but the feature can be used to remove data formats that end up inside
multi-byte instructions.  Instructions with such formats cause warnings
and were tricky to fix.

Labels embedded in multi-byte items are also tricky to remove, so this
clears those as well.  It does not remove visible labels.  This is
done in a single pass, which means that labels that would become visible
after the formatting is cleared will still be removed.

Also, fix inclusion of address range end lines when restoring the
selection.  Their peculiar nature -- being associated with the offset
of the last byte of multi-byte items -- was interfering with the
selection save code.  This does not add them to the selection when
an address region deletion is undone, since technically they weren't
part of the selection.

Also, moved Edit Note higher in the Actions menu.
2021-10-12 13:04:34 -07:00
Andy McFadden
6df29e562f Various tweaks
Changed the code that generates cross-references for pre-labels to
ignore labels in regions with non-addressable parents.  Also, changed
the code that complains about references to labels in non-addressable
areas to ignore pre-labels, because it was complaining about references
to pre-labels on region starts that were followed by a non-addressable
region start.

In the address region edit dialog, split up the descriptive text for
the "resize" option to make it easier to see the new end offset and
length.  It doesn't look quite right because it's not using the mono
font like the text near the top, but it'll do.

When multiple lines are selected, the Info window now shows the first
line/offset, last line/offset, and bytes spanned by the selection.
This is helpful if you're trying to figure out how big something is.
2021-10-11 14:44:44 -07:00
Andy McFadden
0ac0686c7a ORG rework, part 9
Modified "jump to" code to understand address range start/end lines.
If there are multiple starts or ends at the same offset, we jump to
the first one in the set, which is suboptimal but simpler to do.
Simplified the API, embedding GoToMode in the Location object (which
is where it really needs to be, to make fwd/back work right).

Updated HTML export to grey out addresses in NON_ADDR sections.

Changed default pseudo-op strings for address regions to ".addrs" and
".adrend", after trying a bunch of things that were worse.  Added
definitions for region-end pseudo-ops to Merlin32 and cc65 for display
on screen.

Added regression test 20260 for address region pre-labels.

Fixed handling of leading underscores in platform/project symbols.
These need to be escaped in 64tass output.  Updated regression test
20170-external-symbols to check it.
2021-10-07 12:39:30 -07:00
Andy McFadden
d2326c389f ORG rework, part 8
Implemented address region pre-labels.  These are useful if the code is
relocating a block from address A to address B, because the code that
does the copying refers to both the "before" address and the "after"
address.  Previously you'd give the block the "after" address and the
"before" would just appears as hex, because it's effectively an
external address.

Pre-labels are shown on screen with their address, but no other fields.
Showing the address makes it easy to see the label's value, which isn't
always obvious right before a .arstart.  The labels are suppressed if the
address value evaluates to non-addressable.

This defines a new type of symbol, which is external and always global
in scope.  Pre-labels affect label localization and must go through
the usual remapping to handle clashes with opcode mnemonics and the
use of leading underscores.  Cross-references are computed, but are
associated with the file offset rather than the label line itself.

Added a new filter to the Symbols window ("PreL").

Implemented label input and checking in the address editor.  Generally
added highlighting of relevant error labels.
2021-10-04 20:41:19 -07:00
Andy McFadden
e8608770b9 ORG rework, part 7
Implemented "is relative" flag.  This only affects source code
generation, replacing ".arstart <addr>" with ".arstart *+<value>".
Only output by 64tass and ACME generators.

Added a bold-text summary to radio buttons in address region edit
dialog.  This makes it much easier to see what you're doing.  Added
a warning to the label edit dialog when a label is being placed in
a non-addressable region.

Modified double-click behavior for .arstart/.arend to jump to the
other end when the opcode is clicked on.  This matches the behavior
of instructions with address operands.

Reordered Actions menu, putting "edit operand" at the top.

Fixed AddressMap entry collision testing.
Fixed PRG issue with multiple address regions at offset +000002.

Added regression tests.  Most of the complicated stuff with regions
is tested by unit tests inside AddressMap, but we still need to
exercise nested region code generation.
2021-10-02 15:43:41 -07:00
Andy McFadden
e6c5c7f8df ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM.  Regions are specified
with the "NA" address value.  The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).

Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence.  The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data.  So we keep the label and add a
warning to the Messages list when we see a reference.

Moved NON_ADDR constants to Address class.  AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.

Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.

Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map.  It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.

Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-09-30 21:11:26 -07:00
Andy McFadden
2fed19ac47 ORG rework, part 5
Updated project file format to save the new map entries.

Tweaked appearance of .arend directives to show the .arstart address
in the operand field.  This makes it easier to match them up on screen.
Also, add a synthetic comment on auto-generated .arstart entries.

Added .arstart/.arend to the things that respond to Jump to Operand
(Ctrl+J).  Selecting one jumps to the other end.  (Well, it jumps
to the code nearest the other, which will do for now.)

Added a menu item to display a text rendering of the address map.
Helpful when things get complicated.

Modified the linear map iterator to return .arend items with the offset
of the last byte in the region, rather than the first byte of the
following region.  While the "exclusive end" approach is pretty
common, it caused problems when updating the line list, because it
meant that the .arend directives were outside the range of offsets
being updated (and, for directives at the end of the file, outside
the file itself).  This was painful to deal with for partial updates.
Changing this required some relatively subtle changes and annoyed some
of the debug assertions, such as the one where all Line items have
offsets that match the start of a line, but it's the cleaner approach.
2021-09-27 18:13:06 -07:00
Andy McFadden
3a2c4fa6d2 ORG rework, part 4
Reimplemented "set address" dialog as the Address Region Editor.  The
new dialog configures itself differently depending on whether the user
appears to be trying to create, edit, or resize a region.  Each mode
has two options, to allow the user to choose between floating and fixed
end points.

The old dialog would allow you to delete an address override by erasing
the address field.  Now there's an explicit "delete region" button.

Changed the SetAddress undoable change function to use AddressMapEntry
objects.

We now show detailed information on .arstart/.arend in the Info window
when the lines are selected.

PRG files are now created without specifying a region for the first
two bytes, so the load address exists in a NON_ADDR hole.  Fixed a
couple of issues to make that look right.
2021-09-26 17:17:54 -07:00
Andy McFadden
5f472b60cf ORG rework, part 3
Split ".org" into ".arstart" and ".arend" (address range start/end).
Address range ends are now shown in the code list view, and the
pseudo-op can be edited in app settings.  Address range starts are
now shown after notes and long comments, rather than before, which
brings the on-screen display in sync with generated code.

Reworked the address range editor UI to include the new features.
The implementation is fully broken.

More changes to the AddressMap API, putting the resolved region length
into a separate ActualLength field.  Added FindRegion().  Renamed
some things.

Code generation changed slightly: the blank line before a region-end
line now comes after it, and ACME's "} ;!pseudopc" is now just "}".
This required minor updates to some of the regression test results.
2021-09-22 15:28:11 -07:00
Andy McFadden
39b7b20144 ORG rework, part 1
This is the first step toward changing the address region map from a
linear list to a hierarchy.  See issue #107 for the plan.

The AddressMap class has been rewritten to support the new approach.
The rest of the project has been updated to conform to the new API,
but feature-wise is unchanged.  While the map class supports
nested regions with explicit lengths, the rest of the application
still assumes a series of non-overlapping regions with "floating"
lengths.

The Set Address dialog is currently non-functional.

All of the output for cc65 changed because generation of segment
comments has been removed.  Some of the output for ACME changed as
well, because we no longer follow "* = addr" with a redundant
pseudopc statement.  ACME and 65tass have similar approaches to
placing things in memory, and so now have similar implementations.
2021-09-16 17:02:19 -07:00
Andy McFadden
0dfa2326dd Fix L1/L2 ASCII string editing
The data operand editor determines low vs. high ASCII formatting by
examining the first byte of string data.  Unfortunately the test was
broken, and for strings with a 1- or 2-byte length, was testing the
length byte instead of the character data.  This is now fixed.

This also changes the way empty strings are handled.  Before, they
were allowed but not counted, so you couldn't create an empty string
by itself, but could do it if it were part of a larger group.  This
was unnecessarily restrictive.  Empty L1/L2/null-term strings are now
allowed.

This means that a buffer full of $00 can be formatted as a big pile
of empty strings, which seems a bit ridiculous but there's no good
reason to obstruct it.

(issue #110)
2021-09-12 09:46:55 -07:00
Andy McFadden
74fa310718 Fix multi-select with highlighted target
If you select a line that refers to another line, the target line's
address is highlighted.  If you select multiple lines, the highlight
is removed.  The code that removes the highlight was inadvertently
resetting the selection, making it impossible to shift-click a
range that ended with a highlighted line.
2021-09-11 10:48:38 -07:00