If you had a Commodore 64 in the 1980s, odds are the one peripheral you really wished you could have was a Lieutenant Kernal Hard Drive from Fiscal Information/Xetec.  Bringing 20 megabytes of blindingly-fast storage to the system, the Lieutenant Kernal (LTK for short) was the storage system of choice for C64 BBS operators everywhere.  The LTK also brought the power of the minicomputers of the day to the desktop with its integrated DOS and database technology.  100 times faster than the standard storage option of the day, the LTK also allowed attaching multiple devices to the bus for an aggregate storage capacity of 330 megabytes.  Compare this to the 170 kilobyte capacity of the 1541, and it shouldn’t be hard to see why the LTK was so successful.

In spring of 2017, a friend was clearing out his basement, and gave me a bin of odd hardware he was throwing away.  In the box were two Seagate ST296 SCSI drives, a Xetec Lieutenant Kernal host adapter, a Xetec drive enclosure, and a handful of cabling.   Immediately, it became clear that the drives were damaged.   One wouldn’t spin up at all, and belched forth an impressive amount of smoke as soon as it was connected to power.  The other would spin up, but the system would immediately lock up due to the mismatching serial numbers of the drive and host adapter.  The drive enclosure had a bad power supply.  After some online research, and discussions with friends, however, I was able to use the host adapter with a newer drive and PC power supply.

This left the two Seagate ST296 drives and the drive enclosure.

The enclosure was a simple thing to fix, there are much more efficient power supplies now and the voltage ratings are standard for hard drives, so the only real issue was footprint. I found the MeanWell RD-65A to be a suitable replacement, and after some cleanup of the original wiring, this was the result:

LTK Enclosure PSU Replacement

LTK Enclosure PSU Replacement

The drives were another issue.  I knew one had been used as the storage system for a local BBS (The Basement BBS in Cincinnati), so I couldn’t just toss it out without trying to recover some of that history.  (more on this later)

At this point, some basic project goals started to form.

So what is the LTK, really – a (very) simple SCSI interface and drive.  A host adapter plugs into the expansion port of the C64 and handles all of the communication betweeen the C64 and SCSI drive.  There are no special electronics in the drive unit itself.  It is worth mentioning that the 25-pin bus is not compatible with other external SCSI drives, but this is simply due to Xetec re-arranging the pins to prevent the use of cheaper SCSI enclosures.

Up next, digging into the ROM…

Introduction

One of the most common requests we receive on e-commerce projects where Magento 1.9 is being used as a store is to add a percentage discount that works on the entire cart WITH a cap on the overall discount amount. This article will explain how to add this to your Magento installation. The same technique could be used to create any discount type with a cap.

1. Add a column for discount_max to salesrule:

On your Magento database, run the following SQL. This will create the field for maximum discount amount.

ALTER TABLE SalesRule ADD COLUMN discount_max DECIMAL(12,4);

2. Add a new simple_action to Mage_SalesRule_Model_Rule_Model_Abstract (/app/code/core/Mage/SalesRule/Model/Rule.php)

This will allow the system to distinguish your per-cart-percentage rule from other types. Add:

const CART_PERCENT_ACTION = 'cart_percent';

after:

const CART_FIXED_ACTION = 'cart_fixed';

3. Add a case to the switch in Mage_Sales_Model_Quote_Item_Abstract->process() (/app/code/core/Mage/SalesRule/Model/Validator.php)

This is the code that actually processes this rule type for each item.

case Mage_SalesRule_Model_Rule::CART_PERCENT_ACTION:
    if (empty($this->_rulesItemTotals[$rule->getId()])) {
        Mage::throwException(Mage::helper('salesrule')->__('Item totals are not set for rule.'));
    }
    //Get the running cart total discount
    $cartDiscountAmount = $this->_rulesItemTotals[$rule->getId()]['cart_total_discount'];
    //percentage multiplier for maths
    $_rulePct = $rulePercent/100;
    // prevent applying whole cart discount for every shipping order, but only for first order
    if ($quote->getIsMultiShipping()) {
        $usedForAddressId = $this->getCartFixedRuleUsedForAddress($rule->getId());
        if ($usedForAddressId && $usedForAddressId != $address->getId()) {
            break;
        } else {
            $this->setCartFixedRuleUsedForAddress($rule->getId(), $address->getId());
        }
    }
    $cartRules = $address->getCartFixedRules();
    if (!isset($cartRules[$rule->getId()])) {
        $cartRules[$rule->getId()] = $rule->getDiscountAmount();
    }

    //Have we exceeded the per-cart limit?
    if ($cartDiscountAmount < $rule->getDiscountMax()) {
        //If not, what's our maximum remaining
        $maxAllowed = $rule->getDiscountMax()-$cartDiscountAmount;
        //This item's discount is the smaller of the two
        $discountAmount = $quote->getStore()->convertPrice(min($maxAllowed, (($baseItemPrice * $qty)*$_rulePct)));
        $quoteAmount = $quote->getStore()->convertPrice($discountAmount);
        $this->_rulesItemTotals[$rule->getId()]['cart_total_discount']+=$discountAmount;
        $cartRules[$rule->getId()] = $discountAmount;
    }
    $address->setCartFixedRules($cartRules);
    break;

4. Update Mage_Sales_Model_Quote_Address->initTotals for the new simple_action (/app/code/core/Mage/SalesRule/Model/Validator.php)

We need to make sure that running totals are created for this new rule type, so change:

if (Mage_SalesRule_Model_Rule::CART_FIXED_ACTION == $rule->getSimpleAction()

to:

if ((Mage_SalesRule_Model_Rule::CART_FIXED_ACTION == $rule->getSimpleAction()||Mage_SalesRule_Model_Rule::CART_PERCENT_ACTION == $rule->getSimpleAction())

We’ll need to initialize our running total when the rule begins processing, so after:

$validItemsCount = 0;

add:

$cartDiscountTotalForThisRule = 0;

and after:

'items_price' => $ruleTotalItemsPrice,

add

'cart_total_discount' => $cartDiscountTotalForThisRule,

Next, we move on to updates for the management interface.(You did want to be able to edit this, right?)

1. In Mage_Adminhtml_Block_Promo_Quote_Edit_Tab_Actions (/app/code/core/Mage/Adminhtml/Block/Promo/Quote/Edit/Tab/Actions.php)

First, we’ll need to add an option to the Discount Type dropdown on the SalesRule editor, so add:

Mage_SalesRule_Model_Rule::CART_PERCENT_ACTION => Mage::helper('salesrule')->__('Fixed percentage discount for whole cart'),

after:

Mage_SalesRule_Model_Rule::CART_FIXED_ACTION => Mage::helper('salesrule')->__('Fixed amount discount for whole cart'),

2. Add new simple_action to Mage_SalesRule_Model_Rule (already done if working in /code instead of /include)

If you are working in code instead of /include, this is already done. If not, add:

const CART_PERCENT_ACTION = 'cart_percent';

after:

const CART_FIXED_ACTION = 'cart_fixed';

3. Add Discount_Max to Management interface tab in Mage_Adminhtml_Block_Promo_Quote_Edit_Tab_Actions (/app/code/core/Mage/Adminhtml/Block/Promo/Quote/Edit/Tab/Actions.php)

This will need to be added to prepareForm(), which is pretty straightforward. Add this:

$fieldset->addField('discount_max', 'text', array(
        	    'name' => 'discount_max',
	            'label' => Mage::helper('salesrule')->__('Maximum Cart-wide Discount Amount'),
        	));
	        $model->setDiscountMax($model->getDiscountMax()*1);

after:

$model->setDiscountQty($model->getDiscountQty()*1);

And with that, you are done. Flush your cache, add your new SalesRule in the management interface, and enjoy!

Introduction

While building the front page for this site, I found myself stuck on the title graphic.

I had the following requirements:

Initially, I looked at trianglify – but I was already using that for the site backdrop.  After some further searching, I settled on geopattern. There is a demo page here.

Usage

Usage is simple – just include the script and either use the GeoPattern global, or the jQuery plugin.


// Use the global...
var pattern = GeoPattern.generate('GitHub');
$('#geopattern').css('background-image', pattern.toDataUrl());

// ...or the plugin
$('#geopattern').geopattern('GitHub');

There are options for the pattern seed, color, basecolor, and pattern generator:


//Generate yellow octagons
GeoPattern.generate("seed string",{color: '#997700',baseColor:'#997722', generator: "ocatagons"});
//Generate green chevrons
GeoPattern.generate("seed string2",{color: '#22FF44',baseColor:'#000000', generator: "chevrons"});

Applying the resulting image to an object in your DOM is simple with jQuery:


 $('#objectid').css('background-image', pattern.toDataUrl());

Gotchas

This was one of the simplest pieces I’ve worked with yet – it’s really just plug and play. As long as you don’t MUNG the script somehow, you’re good. I didn’t bother with using npm or bower to include it – I just dropped it in my page and started working.

Example

This canvas is patterned with a random seed. Refresh to see a new pattern.